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>
724 lines
21 KiB
Go
724 lines
21 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: users.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const changeUserPassword = `-- name: ChangeUserPassword :exec
|
|
UPDATE users SET password_hash = $2 WHERE id = $1
|
|
`
|
|
|
|
type ChangeUserPasswordParams struct {
|
|
ID pgtype.UUID
|
|
PasswordHash string
|
|
}
|
|
|
|
// 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).
|
|
func (q *Queries) ChangeUserPassword(ctx context.Context, arg ChangeUserPasswordParams) error {
|
|
_, err := q.db.Exec(ctx, changeUserPassword, arg.ID, arg.PasswordHash)
|
|
return err
|
|
}
|
|
|
|
const countAdmins = `-- name: CountAdmins :one
|
|
SELECT count(*) FROM users WHERE is_admin = true
|
|
`
|
|
|
|
func (q *Queries) CountAdmins(ctx context.Context) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countAdmins)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countUsers = `-- name: CountUsers :one
|
|
SELECT count(*) FROM users
|
|
`
|
|
|
|
func (q *Queries) CountUsers(ctx context.Context) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countUsers)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const createUser = `-- name: CreateUser :one
|
|
INSERT INTO users (username, password_hash, api_token_hash, is_admin, display_name)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type CreateUserParams struct {
|
|
Username string
|
|
PasswordHash string
|
|
ApiTokenHash string
|
|
IsAdmin bool
|
|
DisplayName *string
|
|
}
|
|
|
|
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, createUser,
|
|
arg.Username,
|
|
arg.PasswordHash,
|
|
arg.ApiTokenHash,
|
|
arg.IsAdmin,
|
|
arg.DisplayName,
|
|
)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createUserAdmin = `-- name: CreateUserAdmin :one
|
|
INSERT INTO users (username, password_hash, api_token_hash, is_admin, display_name)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type CreateUserAdminParams struct {
|
|
Username string
|
|
PasswordHash string
|
|
ApiTokenHash string
|
|
IsAdmin bool
|
|
DisplayName *string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) CreateUserAdmin(ctx context.Context, arg CreateUserAdminParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, createUserAdmin,
|
|
arg.Username,
|
|
arg.PasswordHash,
|
|
arg.ApiTokenHash,
|
|
arg.IsAdmin,
|
|
arg.DisplayName,
|
|
)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createUserFirstAdminRace = `-- name: CreateUserFirstAdminRace :one
|
|
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 id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type CreateUserFirstAdminRaceParams struct {
|
|
Username string
|
|
PasswordHash string
|
|
ApiTokenHash string
|
|
DisplayName *string
|
|
}
|
|
|
|
// 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).
|
|
func (q *Queries) CreateUserFirstAdminRace(ctx context.Context, arg CreateUserFirstAdminRaceParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, createUserFirstAdminRace,
|
|
arg.Username,
|
|
arg.PasswordHash,
|
|
arg.ApiTokenHash,
|
|
arg.DisplayName,
|
|
)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const deleteUser = `-- name: DeleteUser :exec
|
|
DELETE FROM users WHERE id = $1
|
|
`
|
|
|
|
// 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.
|
|
func (q *Queries) DeleteUser(ctx context.Context, id pgtype.UUID) error {
|
|
_, err := q.db.Exec(ctx, deleteUser, id)
|
|
return err
|
|
}
|
|
|
|
const getListenBrainzConfig = `-- name: GetListenBrainzConfig :one
|
|
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
|
|
`
|
|
|
|
type GetListenBrainzConfigRow struct {
|
|
ListenbrainzToken *string
|
|
ListenbrainzEnabled bool
|
|
LastScrobbledAt pgtype.Timestamptz
|
|
}
|
|
|
|
// Returns the user's LB token + enabled flag and the most recent
|
|
// play_events.scrobbled_at for last-scrobbled-at status.
|
|
func (q *Queries) GetListenBrainzConfig(ctx context.Context, id pgtype.UUID) (GetListenBrainzConfigRow, error) {
|
|
row := q.db.QueryRow(ctx, getListenBrainzConfig, id)
|
|
var i GetListenBrainzConfigRow
|
|
err := row.Scan(&i.ListenbrainzToken, &i.ListenbrainzEnabled, &i.LastScrobbledAt)
|
|
return i, err
|
|
}
|
|
|
|
const getUserByAPITokenHash = `-- name: GetUserByAPITokenHash :one
|
|
SELECT id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash FROM users WHERE api_token_hash = $1
|
|
`
|
|
|
|
func (q *Queries) GetUserByAPITokenHash(ctx context.Context, apiTokenHash string) (User, error) {
|
|
row := q.db.QueryRow(ctx, getUserByAPITokenHash, apiTokenHash)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getUserByEmail = `-- name: GetUserByEmail :one
|
|
SELECT id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash FROM users WHERE lower(email) = lower($1)
|
|
`
|
|
|
|
// Used by forgot-password lookup. Lowercase comparison both sides
|
|
// to keep the unique index happy and to make the lookup
|
|
// case-insensitive.
|
|
func (q *Queries) GetUserByEmail(ctx context.Context, lower string) (User, error) {
|
|
row := q.db.QueryRow(ctx, getUserByEmail, lower)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getUserByID = `-- name: GetUserByID :one
|
|
SELECT id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash FROM users WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetUserByID(ctx context.Context, id pgtype.UUID) (User, error) {
|
|
row := q.db.QueryRow(ctx, getUserByID, id)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getUserByUsername = `-- name: GetUserByUsername :one
|
|
SELECT id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash FROM users WHERE username = $1
|
|
`
|
|
|
|
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) {
|
|
row := q.db.QueryRow(ctx, getUserByUsername, username)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listActiveUsersWithTimezones = `-- name: ListActiveUsersWithTimezones :many
|
|
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'
|
|
)
|
|
`
|
|
|
|
type ListActiveUsersWithTimezonesRow struct {
|
|
ID pgtype.UUID
|
|
Timezone string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) ListActiveUsersWithTimezones(ctx context.Context) ([]ListActiveUsersWithTimezonesRow, error) {
|
|
rows, err := q.db.Query(ctx, listActiveUsersWithTimezones)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListActiveUsersWithTimezonesRow
|
|
for rows.Next() {
|
|
var i ListActiveUsersWithTimezonesRow
|
|
if err := rows.Scan(&i.ID, &i.Timezone); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listUsernamesByIDs = `-- name: ListUsernamesByIDs :many
|
|
SELECT id, username FROM users WHERE id = ANY($1::uuid[])
|
|
`
|
|
|
|
type ListUsernamesByIDsRow struct {
|
|
ID pgtype.UUID
|
|
Username string
|
|
}
|
|
|
|
// Names a list's people in one round trip, e.g. who made each request on
|
|
// /admin/requests.
|
|
func (q *Queries) ListUsernamesByIDs(ctx context.Context, dollar_1 []pgtype.UUID) ([]ListUsernamesByIDsRow, error) {
|
|
rows, err := q.db.Query(ctx, listUsernamesByIDs, dollar_1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListUsernamesByIDsRow
|
|
for rows.Next() {
|
|
var i ListUsernamesByIDsRow
|
|
if err := rows.Scan(&i.ID, &i.Username); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listUsers = `-- name: ListUsers :many
|
|
SELECT id, username, display_name, is_admin, auto_approve_requests,
|
|
debug_mode_enabled, created_at
|
|
FROM users
|
|
ORDER BY created_at DESC
|
|
`
|
|
|
|
type ListUsersRow struct {
|
|
ID pgtype.UUID
|
|
Username string
|
|
DisplayName *string
|
|
IsAdmin bool
|
|
AutoApproveRequests bool
|
|
DebugModeEnabled bool
|
|
CreatedAt pgtype.Timestamptz
|
|
}
|
|
|
|
// Admin user-management list. Sort newest-first.
|
|
func (q *Queries) ListUsers(ctx context.Context) ([]ListUsersRow, error) {
|
|
rows, err := q.db.Query(ctx, listUsers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListUsersRow
|
|
for rows.Next() {
|
|
var i ListUsersRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.DisplayName,
|
|
&i.IsAdmin,
|
|
&i.AutoApproveRequests,
|
|
&i.DebugModeEnabled,
|
|
&i.CreatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const regenerateApiToken = `-- name: RegenerateApiToken :exec
|
|
UPDATE users SET api_token_hash = $2 WHERE id = $1
|
|
`
|
|
|
|
type RegenerateApiTokenParams struct {
|
|
ID pgtype.UUID
|
|
ApiTokenHash string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) RegenerateApiToken(ctx context.Context, arg RegenerateApiTokenParams) error {
|
|
_, err := q.db.Exec(ctx, regenerateApiToken, arg.ID, arg.ApiTokenHash)
|
|
return err
|
|
}
|
|
|
|
const resetUserPassword = `-- name: ResetUserPassword :exec
|
|
UPDATE users
|
|
SET password_hash = $2
|
|
WHERE id = $1
|
|
`
|
|
|
|
type ResetUserPasswordParams struct {
|
|
ID pgtype.UUID
|
|
PasswordHash string
|
|
}
|
|
|
|
// 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).
|
|
func (q *Queries) ResetUserPassword(ctx context.Context, arg ResetUserPasswordParams) error {
|
|
_, err := q.db.Exec(ctx, resetUserPassword, arg.ID, arg.PasswordHash)
|
|
return err
|
|
}
|
|
|
|
const setDebugMode = `-- name: SetDebugMode :one
|
|
UPDATE users
|
|
SET debug_mode_enabled = $2
|
|
WHERE id = $1
|
|
RETURNING id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type SetDebugModeParams struct {
|
|
ID pgtype.UUID
|
|
DebugModeEnabled bool
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) SetDebugMode(ctx context.Context, arg SetDebugModeParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, setDebugMode, arg.ID, arg.DebugModeEnabled)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const setListenBrainzEnabled = `-- name: SetListenBrainzEnabled :exec
|
|
UPDATE users
|
|
SET listenbrainz_enabled = $2
|
|
WHERE id = $1
|
|
`
|
|
|
|
type SetListenBrainzEnabledParams struct {
|
|
ID pgtype.UUID
|
|
ListenbrainzEnabled bool
|
|
}
|
|
|
|
func (q *Queries) SetListenBrainzEnabled(ctx context.Context, arg SetListenBrainzEnabledParams) error {
|
|
_, err := q.db.Exec(ctx, setListenBrainzEnabled, arg.ID, arg.ListenbrainzEnabled)
|
|
return err
|
|
}
|
|
|
|
const setListenBrainzToken = `-- name: SetListenBrainzToken :exec
|
|
UPDATE users
|
|
SET listenbrainz_token = $2,
|
|
listenbrainz_enabled = CASE WHEN COALESCE($2, '') = '' THEN FALSE ELSE listenbrainz_enabled END
|
|
WHERE id = $1
|
|
`
|
|
|
|
type SetListenBrainzTokenParams struct {
|
|
ID pgtype.UUID
|
|
ListenbrainzToken *string
|
|
}
|
|
|
|
func (q *Queries) SetListenBrainzToken(ctx context.Context, arg SetListenBrainzTokenParams) error {
|
|
_, err := q.db.Exec(ctx, setListenBrainzToken, arg.ID, arg.ListenbrainzToken)
|
|
return err
|
|
}
|
|
|
|
const setSubsonicPassword = `-- name: SetSubsonicPassword :exec
|
|
UPDATE users SET subsonic_password = $2 WHERE id = $1
|
|
`
|
|
|
|
type SetSubsonicPasswordParams struct {
|
|
ID pgtype.UUID
|
|
SubsonicPassword *string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) SetSubsonicPassword(ctx context.Context, arg SetSubsonicPasswordParams) error {
|
|
_, err := q.db.Exec(ctx, setSubsonicPassword, arg.ID, arg.SubsonicPassword)
|
|
return err
|
|
}
|
|
|
|
const updateUserAdmin = `-- name: UpdateUserAdmin :one
|
|
UPDATE users
|
|
SET is_admin = $2
|
|
WHERE id = $1
|
|
RETURNING id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type UpdateUserAdminParams struct {
|
|
ID pgtype.UUID
|
|
IsAdmin bool
|
|
}
|
|
|
|
// Sets is_admin to the given value. Returns the updated row so the
|
|
// handler can echo it back to the caller.
|
|
func (q *Queries) UpdateUserAdmin(ctx context.Context, arg UpdateUserAdminParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, updateUserAdmin, arg.ID, arg.IsAdmin)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateUserAutoApprove = `-- name: UpdateUserAutoApprove :one
|
|
UPDATE users
|
|
SET auto_approve_requests = $2
|
|
WHERE id = $1
|
|
RETURNING id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type UpdateUserAutoApproveParams struct {
|
|
ID pgtype.UUID
|
|
AutoApproveRequests bool
|
|
}
|
|
|
|
// Toggle the per-user auto_approve_requests flag.
|
|
func (q *Queries) UpdateUserAutoApprove(ctx context.Context, arg UpdateUserAutoApproveParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, updateUserAutoApprove, arg.ID, arg.AutoApproveRequests)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateUserProfile = `-- name: UpdateUserProfile :one
|
|
UPDATE users
|
|
SET display_name = $2,
|
|
email = $3
|
|
WHERE id = $1
|
|
RETURNING id, username, password_hash, is_admin, created_at, subsonic_password, listenbrainz_token, listenbrainz_enabled, display_name, auto_approve_requests, email, timezone, timezone_updated_at, debug_mode_enabled, api_token_hash
|
|
`
|
|
|
|
type UpdateUserProfileParams struct {
|
|
ID pgtype.UUID
|
|
DisplayName *string
|
|
Email *string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) (User, error) {
|
|
row := q.db.QueryRow(ctx, updateUserProfile, arg.ID, arg.DisplayName, arg.Email)
|
|
var i User
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Username,
|
|
&i.PasswordHash,
|
|
&i.IsAdmin,
|
|
&i.CreatedAt,
|
|
&i.SubsonicPassword,
|
|
&i.ListenbrainzToken,
|
|
&i.ListenbrainzEnabled,
|
|
&i.DisplayName,
|
|
&i.AutoApproveRequests,
|
|
&i.Email,
|
|
&i.Timezone,
|
|
&i.TimezoneUpdatedAt,
|
|
&i.DebugModeEnabled,
|
|
&i.ApiTokenHash,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const updateUserTimezone = `-- name: UpdateUserTimezone :exec
|
|
UPDATE users
|
|
SET timezone = $2,
|
|
timezone_updated_at = now()
|
|
WHERE id = $1
|
|
`
|
|
|
|
type UpdateUserTimezoneParams struct {
|
|
ID pgtype.UUID
|
|
Timezone string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) UpdateUserTimezone(ctx context.Context, arg UpdateUserTimezoneParams) error {
|
|
_, err := q.db.Exec(ctx, updateUserTimezone, arg.ID, arg.Timezone)
|
|
return err
|
|
}
|