feat: add notification center and reliable client presence
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"rmm-openwrt/server/internal/authn"
|
||||
)
|
||||
|
||||
func VerificationCodeHash(code string) (string, error) {
|
||||
return authn.HashPassword("verification:" + strings.TrimSpace(code))
|
||||
}
|
||||
|
||||
func (s *Store) BeginContactVerification(ctx context.Context, userID, channel, destination, codeHash string, expiresAt time.Time) error {
|
||||
_, err := s.db.ExecContext(ctx, `
|
||||
INSERT INTO contact_verifications (user_id, channel, destination, code_hash, expires_at, verified_at, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, NULL, ?)
|
||||
ON CONFLICT(user_id, channel) DO UPDATE SET
|
||||
destination = excluded.destination, code_hash = excluded.code_hash,
|
||||
expires_at = excluded.expires_at, verified_at = NULL, created_at = excluded.created_at
|
||||
`, userID, channel, destination, codeHash, notificationTimeText(expiresAt), nowText())
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Store) ConfirmContactVerification(ctx context.Context, userID, channel, code string) (string, bool, error) {
|
||||
var destination, expected, expires string
|
||||
err := s.db.QueryRowContext(ctx, `
|
||||
SELECT destination, code_hash, expires_at FROM contact_verifications
|
||||
WHERE user_id = ? AND channel = ? AND verified_at IS NULL
|
||||
`, userID, channel).Scan(&destination, &expected, &expires)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if time.Now().UTC().After(parseTime(expires)) ||
|
||||
!authn.VerifyPassword(expected, "verification:"+strings.TrimSpace(code)) {
|
||||
return "", false, nil
|
||||
}
|
||||
_, err = s.db.ExecContext(ctx, `
|
||||
UPDATE contact_verifications SET verified_at = ? WHERE user_id = ? AND channel = ?
|
||||
`, nowText(), userID, channel)
|
||||
return destination, err == nil, err
|
||||
}
|
||||
|
||||
func (s *Store) ContactVerified(ctx context.Context, userID, channel, destination string) (bool, error) {
|
||||
var verified bool
|
||||
err := s.db.QueryRowContext(ctx, `
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM contact_verifications
|
||||
WHERE user_id = ? AND channel = ? AND destination = ? AND verified_at IS NOT NULL
|
||||
)
|
||||
`, userID, channel, strings.TrimSpace(destination)).Scan(&verified)
|
||||
return verified, err
|
||||
}
|
||||
Reference in New Issue
Block a user