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>
182 lines
6.3 KiB
SQL
182 lines
6.3 KiB
SQL
-- name: CreateUser :one
|
|
INSERT INTO users (username, password_hash, api_token_hash, is_admin, display_name)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING *;
|
|
|
|
-- name: CreateUserFirstAdminRace :one
|
|
-- Inserts a new user; sets is_admin=true ONLY when no users currently
|
|
-- exist. The (SELECT NOT EXISTS ...) subquery is evaluated at INSERT
|
|
-- time within the same statement, so the result reflects committed
|
|
-- state at that moment.
|
|
--
|
|
-- Race semantics: two concurrent calls in an empty-users state may
|
|
-- both observe "no users" and both insert with is_admin=true. That's
|
|
-- benign — having two admins from the gate is fine; what matters is
|
|
-- that there's AT LEAST one. If the two calls happened to share the
|
|
-- same username, the unique constraint on users.username arbitrates
|
|
-- and the second caller's INSERT fails with a unique violation. The
|
|
-- caller (registration handler) can retry as a regular non-admin in
|
|
-- that case (or surface a "username taken" error to the user).
|
|
INSERT INTO users (username, password_hash, api_token_hash, is_admin, display_name)
|
|
VALUES (
|
|
$1, $2, $3,
|
|
(SELECT NOT EXISTS (SELECT 1 FROM users)),
|
|
$4
|
|
)
|
|
RETURNING *;
|
|
|
|
-- name: GetUserByUsername :one
|
|
SELECT * FROM users WHERE username = $1;
|
|
|
|
-- name: GetUserByAPITokenHash :one
|
|
SELECT * FROM users WHERE api_token_hash = $1;
|
|
|
|
-- name: CountUsers :one
|
|
SELECT count(*) FROM users;
|
|
|
|
-- name: SetSubsonicPassword :exec
|
|
-- Stores (or clears with NULL) the per-user Subsonic legacy credential used
|
|
-- for t/s and p auth on /rest/*. Must be plaintext; see migration 0003. Only
|
|
-- ever a server-generated value, never the login password.
|
|
UPDATE users SET subsonic_password = $2 WHERE id = $1;
|
|
|
|
-- name: GetUserByID :one
|
|
SELECT * FROM users WHERE id = $1;
|
|
|
|
-- name: ListUsernamesByIDs :many
|
|
-- Names a list's people in one round trip, e.g. who made each request on
|
|
-- /admin/requests.
|
|
SELECT id, username FROM users WHERE id = ANY($1::uuid[]);
|
|
|
|
-- name: SetListenBrainzToken :exec
|
|
UPDATE users
|
|
SET listenbrainz_token = $2,
|
|
listenbrainz_enabled = CASE WHEN COALESCE($2, '') = '' THEN FALSE ELSE listenbrainz_enabled END
|
|
WHERE id = $1;
|
|
|
|
-- name: SetListenBrainzEnabled :exec
|
|
UPDATE users
|
|
SET listenbrainz_enabled = $2
|
|
WHERE id = $1;
|
|
|
|
-- name: ListUsers :many
|
|
-- Admin user-management list. Sort newest-first.
|
|
SELECT id, username, display_name, is_admin, auto_approve_requests,
|
|
debug_mode_enabled, created_at
|
|
FROM users
|
|
ORDER BY created_at DESC;
|
|
|
|
-- name: CountAdmins :one
|
|
SELECT count(*) FROM users WHERE is_admin = true;
|
|
|
|
-- name: UpdateUserAdmin :one
|
|
-- Sets is_admin to the given value. Returns the updated row so the
|
|
-- handler can echo it back to the caller.
|
|
UPDATE users
|
|
SET is_admin = $2
|
|
WHERE id = $1
|
|
RETURNING *;
|
|
|
|
-- name: GetListenBrainzConfig :one
|
|
-- Returns the user's LB token + enabled flag and the most recent
|
|
-- play_events.scrobbled_at for last-scrobbled-at status.
|
|
SELECT
|
|
u.listenbrainz_token,
|
|
u.listenbrainz_enabled,
|
|
(SELECT MAX(pe.scrobbled_at)::timestamptz
|
|
FROM play_events pe
|
|
WHERE pe.user_id = u.id) AS last_scrobbled_at
|
|
FROM users u
|
|
WHERE u.id = $1;
|
|
|
|
-- name: CreateUserAdmin :one
|
|
-- Admin-driven user creation. Distinct from CreateUser/CreateUserFirstAdminRace:
|
|
-- the caller (an admin) supplies all five fields explicitly including is_admin,
|
|
-- so an admin can promote on creation. Used by POST /api/admin/users.
|
|
INSERT INTO users (username, password_hash, api_token_hash, is_admin, display_name)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING *;
|
|
|
|
-- name: DeleteUser :exec
|
|
-- Hard delete with cascade. The schema's ON DELETE CASCADE foreign keys
|
|
-- handle plays/likes/sessions/etc. Caller must enforce the last-admin
|
|
-- guard at the HTTP layer (count admins, refuse if target is the only
|
|
-- admin). The quarantine, general_likes*, play_events,
|
|
-- sessions, and other user-FK tables all have ON DELETE CASCADE so
|
|
-- this is a clean drop.
|
|
DELETE FROM users WHERE id = $1;
|
|
|
|
-- name: ResetUserPassword :exec
|
|
-- Admin-driven password reset: caller writes a new hashed password to
|
|
-- the target user's row. No knowledge of the old password is required
|
|
-- (this is the admin path; self-service "knows current password" is
|
|
-- a separate U3 query).
|
|
UPDATE users
|
|
SET password_hash = $2
|
|
WHERE id = $1;
|
|
|
|
-- name: UpdateUserAutoApprove :one
|
|
-- Toggle the per-user auto_approve_requests flag.
|
|
UPDATE users
|
|
SET auto_approve_requests = $2
|
|
WHERE id = $1
|
|
RETURNING *;
|
|
|
|
-- name: SetDebugMode :one
|
|
-- Toggle the per-account diagnostics/debug-reporting opt-in. Admin-driven
|
|
-- from /admin (flip remotely while a bug is live) or self-driven OFF from
|
|
-- the client. Returns the updated row so the handler can echo it.
|
|
UPDATE users
|
|
SET debug_mode_enabled = $2
|
|
WHERE id = $1
|
|
RETURNING *;
|
|
|
|
-- name: ChangeUserPassword :exec
|
|
-- Self-service password change. Caller (HTTP handler) verifies the
|
|
-- current password before calling this. Distinct from
|
|
-- ResetUserPassword which is admin-driven (no current-password
|
|
-- check).
|
|
UPDATE users SET password_hash = $2 WHERE id = $1;
|
|
|
|
-- name: UpdateUserProfile :one
|
|
-- Self-service: set display name and/or email. Both fields are
|
|
-- nullable in the DB; pass NULL ptr to clear, non-NULL ptr to set.
|
|
UPDATE users
|
|
SET display_name = $2,
|
|
email = $3
|
|
WHERE id = $1
|
|
RETURNING *;
|
|
|
|
-- name: RegenerateApiToken :exec
|
|
-- Self-service: caller wants a new API token. Used by the /settings
|
|
-- API Token card's "Regenerate" button. Only the hash is stored; the
|
|
-- handler returns the raw key once.
|
|
UPDATE users SET api_token_hash = $2 WHERE id = $1;
|
|
|
|
-- name: GetUserByEmail :one
|
|
-- Used by forgot-password lookup. Lowercase comparison both sides
|
|
-- to keep the unique index happy and to make the lookup
|
|
-- case-insensitive.
|
|
SELECT * FROM users WHERE lower(email) = lower($1);
|
|
|
|
-- name: UpdateUserTimezone :exec
|
|
-- Sets the user's IANA timezone and bumps timezone_updated_at. The
|
|
-- handler validates the timezone string via time.LoadLocation before
|
|
-- calling this; the DB does not re-validate.
|
|
UPDATE users
|
|
SET timezone = $2,
|
|
timezone_updated_at = now()
|
|
WHERE id = $1;
|
|
|
|
-- name: ListActiveUsersWithTimezones :many
|
|
-- Returns (id, timezone) for every user with a play in the last 7
|
|
-- days. The system-playlist scheduler iterates this list at startup
|
|
-- and during its hourly reconciliation to discover newly-active /
|
|
-- no-longer-active users.
|
|
SELECT u.id, u.timezone FROM users u
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM play_events pe
|
|
WHERE pe.user_id = u.id
|
|
AND pe.started_at > now() - INTERVAL '7 days'
|
|
);
|