Update
CI / test (push) Successful in 4s

This commit is contained in:
2026-03-02 21:21:21 +00:00
parent 184c5cb59f
commit 6c26135cad
15 changed files with 206 additions and 13 deletions
+1
View File
@@ -8,6 +8,7 @@ type Store interface {
CreateChallenge(ch Challenge) error
GetChallenge(nonce string) (Challenge, error)
MarkChallengeUsed(nonce string) error
SaveUserLogin(ul UserLogin)
SaveSession(session Session)
GetSession(token string) (Session, error)
SaveInvitation(inv Invitation) error
+4
View File
@@ -230,3 +230,7 @@ func (s *MemoryStore) PruneExpired(now time.Time) {
}
}
}
func (s *MemoryStore) SaveUserLogin(ul UserLogin) {
// In-memory store: no-op for login history (persistence only in Postgres)
}
+13 -3
View File
@@ -3,7 +3,9 @@ package store
import (
"database/sql"
"embed"
"io/fs"
"log"
"sort"
_ "github.com/jackc/pgx/v5/stdlib"
)
@@ -17,12 +19,20 @@ func Migrate(databaseURL string) error {
return err
}
defer db.Close()
sql, err := migrationsFS.ReadFile("migrations/0001_init.sql")
entries, err := fs.Glob(migrationsFS, "migrations/*.sql")
if err != nil {
return err
}
if _, err := db.Exec(string(sql)); err != nil {
return err
sort.Strings(entries)
for _, name := range entries {
sql, err := migrationsFS.ReadFile(name)
if err != nil {
return err
}
if _, err := db.Exec(string(sql)); err != nil {
return err
}
}
log.Printf("migrations applied")
return nil
@@ -0,0 +1,21 @@
-- Add ip column to challenges (idempotent)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'challenges' AND column_name = 'ip'
) THEN
ALTER TABLE challenges ADD COLUMN ip TEXT;
END IF;
END $$;
-- User login history (ip, created_at per user)
CREATE TABLE IF NOT EXISTS user_logins (
id SERIAL PRIMARY KEY,
public_key TEXT NOT NULL REFERENCES users(public_key) ON DELETE CASCADE,
ip TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_user_logins_public_key ON user_logins(public_key);
CREATE INDEX IF NOT EXISTS idx_user_logins_created_at ON user_logins(created_at);
+11 -4
View File
@@ -54,8 +54,8 @@ func (s *PostgresStore) GetUser(publicKey string) (User, error) {
func (s *PostgresStore) CreateChallenge(ch Challenge) error {
_, err := s.db.Exec(
`INSERT INTO challenges (nonce, public_key, expires_at, used) VALUES ($1, $2, $3, $4)`,
ch.Nonce, ch.PublicKey, ch.ExpiresAt, ch.Used,
`INSERT INTO challenges (nonce, public_key, ip, expires_at, used) VALUES ($1, $2, $3, $4, $5)`,
ch.Nonce, ch.PublicKey, nullStr(ch.IP), ch.ExpiresAt, ch.Used,
)
if err != nil {
if isUniqueViolation(err) {
@@ -69,9 +69,9 @@ func (s *PostgresStore) CreateChallenge(ch Challenge) error {
func (s *PostgresStore) GetChallenge(nonce string) (Challenge, error) {
var ch Challenge
err := s.db.QueryRow(
`SELECT nonce, public_key, expires_at, used FROM challenges WHERE nonce = $1`,
`SELECT nonce, public_key, COALESCE(ip,''), expires_at, used FROM challenges WHERE nonce = $1`,
nonce,
).Scan(&ch.Nonce, &ch.PublicKey, &ch.ExpiresAt, &ch.Used)
).Scan(&ch.Nonce, &ch.PublicKey, &ch.IP, &ch.ExpiresAt, &ch.Used)
if errors.Is(err, sql.ErrNoRows) {
return Challenge{}, ErrNotFound
}
@@ -282,6 +282,13 @@ func (s *PostgresStore) DeleteFeature(featureID string) error {
return nil
}
func (s *PostgresStore) SaveUserLogin(ul UserLogin) {
_, _ = s.db.Exec(
`INSERT INTO user_logins (public_key, ip, created_at) VALUES ($1, $2, $3)`,
ul.PublicKey, nullStr(ul.IP), ul.CreatedAt,
)
}
func (s *PostgresStore) PruneExpired(now time.Time) {
_, _ = s.db.Exec(`DELETE FROM challenges WHERE expires_at < $1`, now)
_, _ = s.db.Exec(`DELETE FROM sessions WHERE expires_at < $1`, now)
+8
View File
@@ -11,10 +11,18 @@ type User struct {
type Challenge struct {
Nonce string
PublicKey string
IP string
ExpiresAt time.Time
Used bool
}
// UserLogin records a successful login for a user (ip, created_at)
type UserLogin struct {
PublicKey string
IP string
CreatedAt time.Time
}
type Session struct {
Token string
PublicKey string