Atstatymo žetonų saugykla
Penki failai kaip skirtukai, priklausomybių tvarka: 00003_password_reset_tokens.sql → password_resets.sql → users.sql → sessions.sql → token.go (lentelė, jos užklausos ir du esami užklausų failai, kurie auga). Tada sqlc generate.
1. db/migrations/00003_password_reset_tokens.sql — žetonų saugykla, atkartojanti sessions:
token_hash TEXT NOT NULL UNIQUE, -- maiša, niekada ne žetonas
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
expires_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ -- NULL kol nepanaudotas → vienkartinis
used_at — vienkartinio vėliava: NULL reiškia šviežias, laiko žyma — panaudotas.
2. internal/db/queries/password_resets.sql — keturios užklausos: CreatePasswordReset, GetValidPasswordReset (ta, kuri užtikrina taisykles — used_at IS NULL AND expires_at > now()), MarkPasswordResetUsed ir InvalidateUserPasswordResets (sunaikinti visus vartotojo likusius).
3. internal/db/queries/users.sql (modify) — viena nauja užklausa, UpdateUserPassword. Grynas papildymas.
4. internal/db/queries/sessions.sql (modify) — viena nauja užklausa, DeleteSessionsForUser — „atjungti visus įrenginius" užklausa, kurią kvies užbaigtas atstatymas. Taip pat grynas papildymas.
5. internal/auth/token.go (naujas) — tas pats generatorius, kurį naudoja sesijos, dabar eksportuotas perpanaudojimui:
func GenerateToken() (raw, hash string, err error) { // raw → nuoroda; hash → DB
b := make([]byte, 32); rand.Read(b)
raw = base64.RawURLEncoding.EncodeToString(b)
return raw, HashToken(raw), nil
}
func HashToken(raw string) string { /* SHA-256, hex */ }
GenerateToken grąžina du dalykus: raw žetoną (keliauja į el. laiško nuorodą) ir jo hash (keliauja į duomenų bazę). Serveris niekada nesaugo raw žetono — tad, lygiai kaip sesijos ir slaptažodžiai, pavogta kopija neatskleidžia jokių veikiančių atstatymo nuorodų.