-- name: KeyByHash :one -- The authentication hot path. Joins owners so one round trip settles both the -- key's status and the account's. -- -- No filter on status here: the middleware needs to distinguish "revoked" from -- "expired" from "unknown" to give a useful error, and it cannot do that if the -- query has already hidden the row. SELECT k.id, k.owner_id, k.scopes, k.quota_tier, k.status, k.expires_at, o.status AS owner_status FROM api.keys k JOIN api.owners o ON o.id = k.owner_id WHERE k.key_hash = $1; -- name: TouchKeyLastUsed :exec -- Debounced by the caller: writing on every request would be one UPDATE per -- request for a field nobody reads in real time. UPDATE api.keys SET last_used_at = now() WHERE id = $1 AND (last_used_at IS NULL OR last_used_at < now() - interval '5 minutes'); -- name: CreateOwner :one INSERT INTO api.owners (email, callsign) VALUES ($1, $2) RETURNING id, email, callsign, status, created_at; -- name: OwnerByEmail :one SELECT id, email, email_verified_at, callsign, callsign_verified_at, status, created_at FROM api.owners WHERE email = $1; -- name: CreateKey :one INSERT INTO api.keys (owner_id, key_hash, key_prefix, label, scopes, quota_tier, expires_at) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id, key_prefix, scopes, quota_tier, status, created_at, expires_at; -- name: ListKeysForOwner :many SELECT id, key_prefix, label, scopes, quota_tier, status, created_at, expires_at, last_used_at, revoked_at FROM api.keys WHERE owner_id = $1 ORDER BY created_at DESC; -- name: RevokeKey :exec -- The keys_revoked_consistent CHECK requires status and revoked_at to move -- together, so both are set here. UPDATE api.keys SET status = 'revoked', revoked_at = now(), revoked_reason = $2 WHERE id = $1 AND status <> 'revoked';