Rewrites the internal/db/queries comments to rule 32 and regenerates dbq, whose doc comments and embedded SQL carry them. The bugs behind the tiered mixes, the md5 order seed, the score bands and the round-robin arms are restated as the constraint each one enforces; counts from one library and stale cross-references are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
212 lines
6.2 KiB
Go
212 lines
6.2 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: sessions.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const deleteOtherSessionsForUser = `-- name: DeleteOtherSessionsForUser :execrows
|
|
DELETE FROM sessions WHERE user_id = $1 AND id <> $2
|
|
`
|
|
|
|
type DeleteOtherSessionsForUserParams struct {
|
|
UserID pgtype.UUID
|
|
ID pgtype.UUID
|
|
}
|
|
|
|
// "Log out everywhere else." Excludes the caller's own session so the action
|
|
// doesn't log them out of the page they just used to invoke it.
|
|
func (q *Queries) DeleteOtherSessionsForUser(ctx context.Context, arg DeleteOtherSessionsForUserParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, deleteOtherSessionsForUser, arg.UserID, arg.ID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const deleteSession = `-- name: DeleteSession :exec
|
|
DELETE FROM sessions WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) DeleteSession(ctx context.Context, id pgtype.UUID) error {
|
|
_, err := q.db.Exec(ctx, deleteSession, id)
|
|
return err
|
|
}
|
|
|
|
const deleteSessionByTokenHash = `-- name: DeleteSessionByTokenHash :exec
|
|
DELETE FROM sessions WHERE token_hash = $1
|
|
`
|
|
|
|
func (q *Queries) DeleteSessionByTokenHash(ctx context.Context, tokenHash []byte) error {
|
|
_, err := q.db.Exec(ctx, deleteSessionByTokenHash, tokenHash)
|
|
return err
|
|
}
|
|
|
|
const deleteSessionForUser = `-- name: DeleteSessionForUser :execrows
|
|
DELETE FROM sessions WHERE id = $1 AND user_id = $2
|
|
`
|
|
|
|
type DeleteSessionForUserParams struct {
|
|
ID pgtype.UUID
|
|
UserID pgtype.UUID
|
|
}
|
|
|
|
// Scoped by user_id, not just id: household data is per-user. Keyed on the
|
|
// id alone, any
|
|
// household member could revoke another member's session by guessing a uuid.
|
|
// execrows lets the handler answer 404 rather than a false 204 when the row
|
|
// isn't theirs.
|
|
func (q *Queries) DeleteSessionForUser(ctx context.Context, arg DeleteSessionForUserParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, deleteSessionForUser, arg.ID, arg.UserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const deleteSessionsForUser = `-- name: DeleteSessionsForUser :execrows
|
|
DELETE FROM sessions WHERE user_id = $1
|
|
`
|
|
|
|
// Every session the user has, the caller's included. A password reset uses
|
|
// it: whoever forgot the password is not signed in anywhere they trust, and
|
|
// whoever may have learned it must be signed out everywhere.
|
|
func (q *Queries) DeleteSessionsForUser(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
|
result, err := q.db.Exec(ctx, deleteSessionsForUser, userID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const getSessionByTokenHash = `-- name: GetSessionByTokenHash :one
|
|
SELECT id, user_id, token_hash, user_agent, created_at, last_seen_at, created_ip, last_ip FROM sessions
|
|
WHERE token_hash = $1
|
|
AND last_seen_at > now() - interval '30 days'
|
|
AND created_at > now() - interval '365 days'
|
|
`
|
|
|
|
// Expired sessions are invisible here, so they fail auth the moment they
|
|
// lapse rather than whenever the GC sweep next runs. Two limits:
|
|
//
|
|
// idle 30 days — a token nobody has used in a month is abandoned, and
|
|
// matches the web cookie's lifetime;
|
|
// absolute 1 year — even a token in daily use is re-issued yearly, so a
|
|
// stolen one that is being used quietly does not live
|
|
// forever.
|
|
//
|
|
// Keep in step with ListSessionsForUser and GcDeleteExpiredSessions.
|
|
func (q *Queries) GetSessionByTokenHash(ctx context.Context, tokenHash []byte) (Session, error) {
|
|
row := q.db.QueryRow(ctx, getSessionByTokenHash, tokenHash)
|
|
var i Session
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.TokenHash,
|
|
&i.UserAgent,
|
|
&i.CreatedAt,
|
|
&i.LastSeenAt,
|
|
&i.CreatedIp,
|
|
&i.LastIp,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const insertSession = `-- name: InsertSession :one
|
|
INSERT INTO sessions (user_id, token_hash, user_agent, created_ip, last_ip)
|
|
VALUES ($1, $2, $3, $4, $4)
|
|
RETURNING id, user_id, token_hash, user_agent, created_at, last_seen_at, created_ip, last_ip
|
|
`
|
|
|
|
type InsertSessionParams struct {
|
|
UserID pgtype.UUID
|
|
TokenHash []byte
|
|
UserAgent string
|
|
Ip string
|
|
}
|
|
|
|
// created_ip and last_ip start equal: at issue time the origin IS the current
|
|
// location. They diverge as the session is used from elsewhere, which is what
|
|
// makes a stolen token visible in the active-sessions surface.
|
|
func (q *Queries) InsertSession(ctx context.Context, arg InsertSessionParams) (Session, error) {
|
|
row := q.db.QueryRow(ctx, insertSession,
|
|
arg.UserID,
|
|
arg.TokenHash,
|
|
arg.UserAgent,
|
|
arg.Ip,
|
|
)
|
|
var i Session
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.TokenHash,
|
|
&i.UserAgent,
|
|
&i.CreatedAt,
|
|
&i.LastSeenAt,
|
|
&i.CreatedIp,
|
|
&i.LastIp,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listSessionsForUser = `-- name: ListSessionsForUser :many
|
|
SELECT id, user_id, token_hash, user_agent, created_at, last_seen_at, created_ip, last_ip FROM sessions
|
|
WHERE user_id = $1
|
|
AND last_seen_at > now() - interval '30 days'
|
|
AND created_at > now() - interval '365 days'
|
|
ORDER BY last_seen_at DESC
|
|
`
|
|
|
|
// Most-recently-active first: the row a user is most likely to act on is the
|
|
// one that moved last, and an unfamiliar entry at the top is the alarm.
|
|
// Expired rows are left out: they no longer authenticate, so listing them
|
|
// as active would be wrong. Same limits as GetSessionByTokenHash.
|
|
func (q *Queries) ListSessionsForUser(ctx context.Context, userID pgtype.UUID) ([]Session, error) {
|
|
rows, err := q.db.Query(ctx, listSessionsForUser, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Session
|
|
for rows.Next() {
|
|
var i Session
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.TokenHash,
|
|
&i.UserAgent,
|
|
&i.CreatedAt,
|
|
&i.LastSeenAt,
|
|
&i.CreatedIp,
|
|
&i.LastIp,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const touchSessionLastSeen = `-- name: TouchSessionLastSeen :exec
|
|
UPDATE sessions SET last_seen_at = now(), last_ip = $2 WHERE id = $1
|
|
`
|
|
|
|
type TouchSessionLastSeenParams struct {
|
|
ID pgtype.UUID
|
|
LastIp string
|
|
}
|
|
|
|
func (q *Queries) TouchSessionLastSeen(ctx context.Context, arg TouchSessionLastSeenParams) error {
|
|
_, err := q.db.Exec(ctx, touchSessionLastSeen, arg.ID, arg.LastIp)
|
|
return err
|
|
}
|