overrides
This commit is contained in:
+219
-10
@@ -11,15 +11,102 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const getKeyByHash = `-- name: GetKeyByHash :one
|
||||
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 AND k.status = 'active'
|
||||
const createKey = `-- 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
|
||||
`
|
||||
|
||||
type GetKeyByHashRow struct {
|
||||
type CreateKeyParams struct {
|
||||
OwnerID int64
|
||||
KeyHash []byte
|
||||
KeyPrefix string
|
||||
Label *string
|
||||
Scopes []string
|
||||
QuotaTier string
|
||||
ExpiresAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type CreateKeyRow struct {
|
||||
ID int64
|
||||
KeyPrefix string
|
||||
Scopes []string
|
||||
QuotaTier string
|
||||
Status string
|
||||
CreatedAt pgtype.Timestamptz
|
||||
ExpiresAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) CreateKey(ctx context.Context, arg CreateKeyParams) (CreateKeyRow, error) {
|
||||
row := q.db.QueryRow(ctx, createKey,
|
||||
arg.OwnerID,
|
||||
arg.KeyHash,
|
||||
arg.KeyPrefix,
|
||||
arg.Label,
|
||||
arg.Scopes,
|
||||
arg.QuotaTier,
|
||||
arg.ExpiresAt,
|
||||
)
|
||||
var i CreateKeyRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.KeyPrefix,
|
||||
&i.Scopes,
|
||||
&i.QuotaTier,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createOwner = `-- name: CreateOwner :one
|
||||
INSERT INTO api.owners (email, callsign)
|
||||
VALUES ($1, $2)
|
||||
RETURNING id, email, callsign, status, created_at
|
||||
`
|
||||
|
||||
type CreateOwnerParams struct {
|
||||
Email string
|
||||
Callsign interface{}
|
||||
}
|
||||
|
||||
type CreateOwnerRow struct {
|
||||
ID int64
|
||||
Email string
|
||||
Callsign interface{}
|
||||
Status string
|
||||
CreatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) CreateOwner(ctx context.Context, arg CreateOwnerParams) (CreateOwnerRow, error) {
|
||||
row := q.db.QueryRow(ctx, createOwner, arg.Email, arg.Callsign)
|
||||
var i CreateOwnerRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.Callsign,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const keyByHash = `-- name: KeyByHash :one
|
||||
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
|
||||
`
|
||||
|
||||
type KeyByHashRow struct {
|
||||
ID int64
|
||||
OwnerID int64
|
||||
Scopes []string
|
||||
@@ -29,9 +116,15 @@ type GetKeyByHashRow struct {
|
||||
OwnerStatus string
|
||||
}
|
||||
|
||||
func (q *Queries) GetKeyByHash(ctx context.Context, keyHash []byte) (GetKeyByHashRow, error) {
|
||||
row := q.db.QueryRow(ctx, getKeyByHash, keyHash)
|
||||
var i GetKeyByHashRow
|
||||
// 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.
|
||||
func (q *Queries) KeyByHash(ctx context.Context, keyHash []byte) (KeyByHashRow, error) {
|
||||
row := q.db.QueryRow(ctx, keyByHash, keyHash)
|
||||
var i KeyByHashRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.OwnerID,
|
||||
@@ -43,3 +136,119 @@ func (q *Queries) GetKeyByHash(ctx context.Context, keyHash []byte) (GetKeyByHas
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listKeysForOwner = `-- 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
|
||||
`
|
||||
|
||||
type ListKeysForOwnerRow struct {
|
||||
ID int64
|
||||
KeyPrefix string
|
||||
Label *string
|
||||
Scopes []string
|
||||
QuotaTier string
|
||||
Status string
|
||||
CreatedAt pgtype.Timestamptz
|
||||
ExpiresAt pgtype.Timestamptz
|
||||
LastUsedAt pgtype.Timestamptz
|
||||
RevokedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) ListKeysForOwner(ctx context.Context, ownerID int64) ([]ListKeysForOwnerRow, error) {
|
||||
rows, err := q.db.Query(ctx, listKeysForOwner, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []ListKeysForOwnerRow{}
|
||||
for rows.Next() {
|
||||
var i ListKeysForOwnerRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.KeyPrefix,
|
||||
&i.Label,
|
||||
&i.Scopes,
|
||||
&i.QuotaTier,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.LastUsedAt,
|
||||
&i.RevokedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const ownerByEmail = `-- name: OwnerByEmail :one
|
||||
SELECT id, email, email_verified_at, callsign, callsign_verified_at, status, created_at
|
||||
FROM api.owners
|
||||
WHERE email = $1
|
||||
`
|
||||
|
||||
type OwnerByEmailRow struct {
|
||||
ID int64
|
||||
Email string
|
||||
EmailVerifiedAt pgtype.Timestamptz
|
||||
Callsign interface{}
|
||||
CallsignVerifiedAt pgtype.Timestamptz
|
||||
Status string
|
||||
CreatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) OwnerByEmail(ctx context.Context, email string) (OwnerByEmailRow, error) {
|
||||
row := q.db.QueryRow(ctx, ownerByEmail, email)
|
||||
var i OwnerByEmailRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.EmailVerifiedAt,
|
||||
&i.Callsign,
|
||||
&i.CallsignVerifiedAt,
|
||||
&i.Status,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const revokeKey = `-- name: RevokeKey :exec
|
||||
UPDATE api.keys
|
||||
SET status = 'revoked', revoked_at = now(), revoked_reason = $2
|
||||
WHERE id = $1
|
||||
AND status <> 'revoked'
|
||||
`
|
||||
|
||||
type RevokeKeyParams struct {
|
||||
ID int64
|
||||
RevokedReason *string
|
||||
}
|
||||
|
||||
// The keys_revoked_consistent CHECK requires status and revoked_at to move
|
||||
// together, so both are set here.
|
||||
func (q *Queries) RevokeKey(ctx context.Context, arg RevokeKeyParams) error {
|
||||
_, err := q.db.Exec(ctx, revokeKey, arg.ID, arg.RevokedReason)
|
||||
return err
|
||||
}
|
||||
|
||||
const touchKeyLastUsed = `-- name: TouchKeyLastUsed :exec
|
||||
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')
|
||||
`
|
||||
|
||||
// Debounced by the caller: writing on every request would be one UPDATE per
|
||||
// request for a field nobody reads in real time.
|
||||
func (q *Queries) TouchKeyLastUsed(ctx context.Context, id int64) error {
|
||||
_, err := q.db.Exec(ctx, touchKeyLastUsed, id)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user