feat(coderd/database): add AI Gateway key auth lookup and last-used queries (#26505)

Adds DB methods`GetAIGatewayKeyIDByHashedSecret` and `UpdateAIGatewayKeyLastUsedAt`.
`GetAIGatewayKeyIDByHashedSecret` - returns AI Gateway key ID by hashed secret value.
`UpdateAIGatewayKeyLastUsedAt` - updates last used timestamp for given AI Gateway key. 
Used by standalone AI Gateway for authentication and keeping track of currently used keys.
This commit is contained in:
Paweł Banaszewski
2026-06-26 18:16:01 +02:00
committed by GitHub
parent e71d4ca69b
commit 0f1e792f3f
24 changed files with 271 additions and 7 deletions
+40
View File
@@ -137,6 +137,29 @@ func (q *sqlQuerier) DeleteAIGatewayKey(ctx context.Context, id uuid.UUID) (Dele
return i, err
}
const getAIGatewayKeyByHashedSecret = `-- name: GetAIGatewayKeyByHashedSecret :one
SELECT id, created_at, name, secret_prefix, hashed_secret, last_used_at
FROM ai_gateway_keys
WHERE hashed_secret = $1
`
// Authenticates a standalone AI Gateway replica by its hashed key secret,
// returning the matched key. The lookup is an exact match on a unique index,
// so a returned row is itself proof the secret is valid.
func (q *sqlQuerier) GetAIGatewayKeyByHashedSecret(ctx context.Context, hashedSecret []byte) (AIGatewayKey, error) {
row := q.db.QueryRowContext(ctx, getAIGatewayKeyByHashedSecret, hashedSecret)
var i AIGatewayKey
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.Name,
&i.SecretPrefix,
&i.HashedSecret,
&i.LastUsedAt,
)
return i, err
}
const insertAIGatewayKey = `-- name: InsertAIGatewayKey :one
INSERT INTO ai_gateway_keys (id, name, secret_prefix, hashed_secret, created_at)
VALUES ($1, $4, $2, $3, NOW())
@@ -217,6 +240,23 @@ func (q *sqlQuerier) ListAIGatewayKeys(ctx context.Context) ([]ListAIGatewayKeys
return items, nil
}
const updateAIGatewayKeyLastUsedAt = `-- name: UpdateAIGatewayKeyLastUsedAt :execrows
UPDATE ai_gateway_keys
SET last_used_at = NOW()
WHERE id = $1
`
// Records liveness for an active Gateway DRPC session. The database sets the
// timestamp so it stays consistent regardless of clock drift between API
// replicas.
func (q *sqlQuerier) UpdateAIGatewayKeyLastUsedAt(ctx context.Context, id uuid.UUID) (int64, error) {
result, err := q.db.ExecContext(ctx, updateAIGatewayKeyLastUsedAt, id)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const deleteAIProviderKey = `-- name: DeleteAIProviderKey :exec
DELETE FROM
ai_provider_keys