mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-08 01:16:45 +08:00
Adds the backend for the operations console: platform_admin roles (super_admin / operator / viewer), an admin_audit_log table, and user.disabled_at so disabling an account is a stored state instead of a hard-coded denylist. The in-process disabled set is now refreshed from the database every 30 seconds, and the old emergency denylist entries are migrated into it. /api/admin exposes overview counts, user and workspace listings with detail, disable/enable, token revocation, the audit log, and platform role management. Every write requires a reason and is recorded with the actor, IP and user agent. PLATFORM_ADMIN_EMAILS bootstraps the first super_admin. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01FrVjAFWeckYV69HS51xUhy
692 lines
19 KiB
Go
692 lines
19 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: admin.sql
|
|
|
|
package db
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const adminCountUsers = `-- name: AdminCountUsers :one
|
|
SELECT count(*) FROM "user" u
|
|
WHERE ($1::text IS NULL
|
|
OR u.email ILIKE '%' || $1::text || '%'
|
|
OR u.name ILIKE '%' || $1::text || '%')
|
|
AND (NOT $2::boolean OR u.disabled_at IS NOT NULL)
|
|
`
|
|
|
|
type AdminCountUsersParams struct {
|
|
Q pgtype.Text `json:"q"`
|
|
DisabledOnly bool `json:"disabled_only"`
|
|
}
|
|
|
|
func (q *Queries) AdminCountUsers(ctx context.Context, arg AdminCountUsersParams) (int64, error) {
|
|
row := q.db.QueryRow(ctx, adminCountUsers, arg.Q, arg.DisabledOnly)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const adminCountWorkspaces = `-- name: AdminCountWorkspaces :one
|
|
SELECT count(*) FROM workspace w
|
|
WHERE ($1::text IS NULL
|
|
OR w.name ILIKE '%' || $1::text || '%'
|
|
OR w.slug ILIKE '%' || $1::text || '%')
|
|
`
|
|
|
|
func (q *Queries) AdminCountWorkspaces(ctx context.Context, q_ pgtype.Text) (int64, error) {
|
|
row := q.db.QueryRow(ctx, adminCountWorkspaces, q_)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const adminDisableUser = `-- name: AdminDisableUser :execrows
|
|
UPDATE "user" SET disabled_at = now(), disabled_reason = $2, updated_at = now()
|
|
WHERE id = $1 AND disabled_at IS NULL
|
|
`
|
|
|
|
type AdminDisableUserParams struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
DisabledReason string `json:"disabled_reason"`
|
|
}
|
|
|
|
func (q *Queries) AdminDisableUser(ctx context.Context, arg AdminDisableUserParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, adminDisableUser, arg.ID, arg.DisabledReason)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const adminEnableUser = `-- name: AdminEnableUser :execrows
|
|
UPDATE "user" SET disabled_at = NULL, disabled_reason = '', updated_at = now()
|
|
WHERE id = $1 AND disabled_at IS NOT NULL
|
|
`
|
|
|
|
func (q *Queries) AdminEnableUser(ctx context.Context, id pgtype.UUID) (int64, error) {
|
|
result, err := q.db.Exec(ctx, adminEnableUser, id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const adminGetUser = `-- name: AdminGetUser :one
|
|
SELECT u.id, u.name, u.email, u.avatar_url, u.created_at, u.disabled_at, u.disabled_reason,
|
|
(SELECT count(*) FROM member m WHERE m.user_id = u.id) AS workspace_count
|
|
FROM "user" u
|
|
WHERE u.id = $1
|
|
`
|
|
|
|
type AdminGetUserRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
AvatarUrl pgtype.Text `json:"avatar_url"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
DisabledAt pgtype.Timestamptz `json:"disabled_at"`
|
|
DisabledReason string `json:"disabled_reason"`
|
|
WorkspaceCount int64 `json:"workspace_count"`
|
|
}
|
|
|
|
func (q *Queries) AdminGetUser(ctx context.Context, id pgtype.UUID) (AdminGetUserRow, error) {
|
|
row := q.db.QueryRow(ctx, adminGetUser, id)
|
|
var i AdminGetUserRow
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.Email,
|
|
&i.AvatarUrl,
|
|
&i.CreatedAt,
|
|
&i.DisabledAt,
|
|
&i.DisabledReason,
|
|
&i.WorkspaceCount,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const adminGetWorkspace = `-- name: AdminGetWorkspace :one
|
|
SELECT w.id, w.name, w.slug, w.created_at,
|
|
(SELECT count(*) FROM member m WHERE m.workspace_id = w.id) AS member_count,
|
|
(SELECT count(*) FROM agent a WHERE a.workspace_id = w.id) AS agent_count
|
|
FROM workspace w
|
|
WHERE w.id = $1
|
|
`
|
|
|
|
type AdminGetWorkspaceRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
Slug string `json:"slug"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
MemberCount int64 `json:"member_count"`
|
|
AgentCount int64 `json:"agent_count"`
|
|
}
|
|
|
|
func (q *Queries) AdminGetWorkspace(ctx context.Context, id pgtype.UUID) (AdminGetWorkspaceRow, error) {
|
|
row := q.db.QueryRow(ctx, adminGetWorkspace, id)
|
|
var i AdminGetWorkspaceRow
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.Slug,
|
|
&i.CreatedAt,
|
|
&i.MemberCount,
|
|
&i.AgentCount,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const adminListUserWorkspaces = `-- name: AdminListUserWorkspaces :many
|
|
SELECT w.id, w.name, w.slug, m.role, m.created_at AS joined_at
|
|
FROM member m
|
|
JOIN workspace w ON w.id = m.workspace_id
|
|
WHERE m.user_id = $1
|
|
ORDER BY m.created_at ASC
|
|
`
|
|
|
|
type AdminListUserWorkspacesRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
Slug string `json:"slug"`
|
|
Role string `json:"role"`
|
|
JoinedAt pgtype.Timestamptz `json:"joined_at"`
|
|
}
|
|
|
|
func (q *Queries) AdminListUserWorkspaces(ctx context.Context, userID pgtype.UUID) ([]AdminListUserWorkspacesRow, error) {
|
|
rows, err := q.db.Query(ctx, adminListUserWorkspaces, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []AdminListUserWorkspacesRow{}
|
|
for rows.Next() {
|
|
var i AdminListUserWorkspacesRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.Slug,
|
|
&i.Role,
|
|
&i.JoinedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const adminListUsers = `-- name: AdminListUsers :many
|
|
SELECT u.id, u.name, u.email, u.avatar_url, u.created_at, u.disabled_at, u.disabled_reason,
|
|
(SELECT count(*) FROM member m WHERE m.user_id = u.id) AS workspace_count
|
|
FROM "user" u
|
|
WHERE ($1::text IS NULL
|
|
OR u.email ILIKE '%' || $1::text || '%'
|
|
OR u.name ILIKE '%' || $1::text || '%')
|
|
AND (NOT $2::boolean OR u.disabled_at IS NOT NULL)
|
|
ORDER BY u.created_at DESC, u.id DESC
|
|
LIMIT $4 OFFSET $3
|
|
`
|
|
|
|
type AdminListUsersParams struct {
|
|
Q pgtype.Text `json:"q"`
|
|
DisabledOnly bool `json:"disabled_only"`
|
|
PageOffset int32 `json:"page_offset"`
|
|
PageLimit int32 `json:"page_limit"`
|
|
}
|
|
|
|
type AdminListUsersRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
AvatarUrl pgtype.Text `json:"avatar_url"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
DisabledAt pgtype.Timestamptz `json:"disabled_at"`
|
|
DisabledReason string `json:"disabled_reason"`
|
|
WorkspaceCount int64 `json:"workspace_count"`
|
|
}
|
|
|
|
func (q *Queries) AdminListUsers(ctx context.Context, arg AdminListUsersParams) ([]AdminListUsersRow, error) {
|
|
rows, err := q.db.Query(ctx, adminListUsers,
|
|
arg.Q,
|
|
arg.DisabledOnly,
|
|
arg.PageOffset,
|
|
arg.PageLimit,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []AdminListUsersRow{}
|
|
for rows.Next() {
|
|
var i AdminListUsersRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.Email,
|
|
&i.AvatarUrl,
|
|
&i.CreatedAt,
|
|
&i.DisabledAt,
|
|
&i.DisabledReason,
|
|
&i.WorkspaceCount,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const adminListWorkspaceMembers = `-- name: AdminListWorkspaceMembers :many
|
|
SELECT u.id AS user_id, u.name, u.email, m.role, m.created_at AS joined_at, u.disabled_at
|
|
FROM member m
|
|
JOIN "user" u ON u.id = m.user_id
|
|
WHERE m.workspace_id = $1
|
|
ORDER BY m.created_at ASC
|
|
`
|
|
|
|
type AdminListWorkspaceMembersRow struct {
|
|
UserID pgtype.UUID `json:"user_id"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Role string `json:"role"`
|
|
JoinedAt pgtype.Timestamptz `json:"joined_at"`
|
|
DisabledAt pgtype.Timestamptz `json:"disabled_at"`
|
|
}
|
|
|
|
func (q *Queries) AdminListWorkspaceMembers(ctx context.Context, workspaceID pgtype.UUID) ([]AdminListWorkspaceMembersRow, error) {
|
|
rows, err := q.db.Query(ctx, adminListWorkspaceMembers, workspaceID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []AdminListWorkspaceMembersRow{}
|
|
for rows.Next() {
|
|
var i AdminListWorkspaceMembersRow
|
|
if err := rows.Scan(
|
|
&i.UserID,
|
|
&i.Name,
|
|
&i.Email,
|
|
&i.Role,
|
|
&i.JoinedAt,
|
|
&i.DisabledAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const adminListWorkspaces = `-- name: AdminListWorkspaces :many
|
|
SELECT w.id, w.name, w.slug, w.created_at,
|
|
(SELECT count(*) FROM member m WHERE m.workspace_id = w.id) AS member_count,
|
|
(SELECT count(*) FROM agent a WHERE a.workspace_id = w.id) AS agent_count
|
|
FROM workspace w
|
|
WHERE ($1::text IS NULL
|
|
OR w.name ILIKE '%' || $1::text || '%'
|
|
OR w.slug ILIKE '%' || $1::text || '%')
|
|
ORDER BY w.created_at DESC, w.id DESC
|
|
LIMIT $3 OFFSET $2
|
|
`
|
|
|
|
type AdminListWorkspacesParams struct {
|
|
Q pgtype.Text `json:"q"`
|
|
PageOffset int32 `json:"page_offset"`
|
|
PageLimit int32 `json:"page_limit"`
|
|
}
|
|
|
|
type AdminListWorkspacesRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
Slug string `json:"slug"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
MemberCount int64 `json:"member_count"`
|
|
AgentCount int64 `json:"agent_count"`
|
|
}
|
|
|
|
func (q *Queries) AdminListWorkspaces(ctx context.Context, arg AdminListWorkspacesParams) ([]AdminListWorkspacesRow, error) {
|
|
rows, err := q.db.Query(ctx, adminListWorkspaces, arg.Q, arg.PageOffset, arg.PageLimit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []AdminListWorkspacesRow{}
|
|
for rows.Next() {
|
|
var i AdminListWorkspacesRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.Slug,
|
|
&i.CreatedAt,
|
|
&i.MemberCount,
|
|
&i.AgentCount,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const adminOverviewCounts = `-- name: AdminOverviewCounts :one
|
|
SELECT
|
|
(SELECT count(*) FROM "user")::bigint AS users_total,
|
|
(SELECT count(*) FROM "user" WHERE created_at > now() - interval '7 days')::bigint AS users_new_7d,
|
|
(SELECT count(*) FROM "user" WHERE disabled_at IS NOT NULL)::bigint AS users_disabled,
|
|
(SELECT count(*) FROM workspace)::bigint AS workspaces_total,
|
|
(SELECT count(*) FROM agent_runtime)::bigint AS runtimes_total,
|
|
(SELECT count(*) FROM agent_runtime WHERE status = 'online')::bigint AS runtimes_online
|
|
`
|
|
|
|
type AdminOverviewCountsRow struct {
|
|
UsersTotal int64 `json:"users_total"`
|
|
UsersNew7d int64 `json:"users_new_7d"`
|
|
UsersDisabled int64 `json:"users_disabled"`
|
|
WorkspacesTotal int64 `json:"workspaces_total"`
|
|
RuntimesTotal int64 `json:"runtimes_total"`
|
|
RuntimesOnline int64 `json:"runtimes_online"`
|
|
}
|
|
|
|
func (q *Queries) AdminOverviewCounts(ctx context.Context) (AdminOverviewCountsRow, error) {
|
|
row := q.db.QueryRow(ctx, adminOverviewCounts)
|
|
var i AdminOverviewCountsRow
|
|
err := row.Scan(
|
|
&i.UsersTotal,
|
|
&i.UsersNew7d,
|
|
&i.UsersDisabled,
|
|
&i.WorkspacesTotal,
|
|
&i.RuntimesTotal,
|
|
&i.RuntimesOnline,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const adminTaskCounts24h = `-- name: AdminTaskCounts24h :many
|
|
SELECT status, count(*)::bigint AS total
|
|
FROM agent_task_queue
|
|
WHERE created_at > now() - interval '24 hours'
|
|
GROUP BY status
|
|
`
|
|
|
|
type AdminTaskCounts24hRow struct {
|
|
Status string `json:"status"`
|
|
Total int64 `json:"total"`
|
|
}
|
|
|
|
func (q *Queries) AdminTaskCounts24h(ctx context.Context) ([]AdminTaskCounts24hRow, error) {
|
|
rows, err := q.db.Query(ctx, adminTaskCounts24h)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []AdminTaskCounts24hRow{}
|
|
for rows.Next() {
|
|
var i AdminTaskCounts24hRow
|
|
if err := rows.Scan(&i.Status, &i.Total); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const countAdminAuditLog = `-- name: CountAdminAuditLog :one
|
|
SELECT count(*) FROM admin_audit_log l
|
|
WHERE ($1::uuid IS NULL OR l.actor_user_id = $1)
|
|
AND ($2::text IS NULL OR l.action = $2)
|
|
AND ($3::text IS NULL OR l.target_id = $3)
|
|
`
|
|
|
|
type CountAdminAuditLogParams struct {
|
|
ActorUserID pgtype.UUID `json:"actor_user_id"`
|
|
Action pgtype.Text `json:"action"`
|
|
TargetID pgtype.Text `json:"target_id"`
|
|
}
|
|
|
|
func (q *Queries) CountAdminAuditLog(ctx context.Context, arg CountAdminAuditLogParams) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countAdminAuditLog, arg.ActorUserID, arg.Action, arg.TargetID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countPlatformSuperAdmins = `-- name: CountPlatformSuperAdmins :one
|
|
SELECT count(*) FROM platform_admin WHERE role = 'super_admin'
|
|
`
|
|
|
|
func (q *Queries) CountPlatformSuperAdmins(ctx context.Context) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countPlatformSuperAdmins)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const deletePlatformAdmin = `-- name: DeletePlatformAdmin :execrows
|
|
DELETE FROM platform_admin WHERE user_id = $1
|
|
`
|
|
|
|
func (q *Queries) DeletePlatformAdmin(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
|
result, err := q.db.Exec(ctx, deletePlatformAdmin, userID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const getPlatformAdmin = `-- name: GetPlatformAdmin :one
|
|
|
|
SELECT user_id, role, granted_by, created_at FROM platform_admin
|
|
WHERE user_id = $1
|
|
`
|
|
|
|
// Platform admin console. Everything here is platform-level (not workspace
|
|
// scoped) and is only reachable through /api/admin/*.
|
|
func (q *Queries) GetPlatformAdmin(ctx context.Context, userID pgtype.UUID) (PlatformAdmin, error) {
|
|
row := q.db.QueryRow(ctx, getPlatformAdmin, userID)
|
|
var i PlatformAdmin
|
|
err := row.Scan(
|
|
&i.UserID,
|
|
&i.Role,
|
|
&i.GrantedBy,
|
|
&i.CreatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getUserDisabledAt = `-- name: GetUserDisabledAt :one
|
|
SELECT disabled_at FROM "user" WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetUserDisabledAt(ctx context.Context, id pgtype.UUID) (pgtype.Timestamptz, error) {
|
|
row := q.db.QueryRow(ctx, getUserDisabledAt, id)
|
|
var disabled_at pgtype.Timestamptz
|
|
err := row.Scan(&disabled_at)
|
|
return disabled_at, err
|
|
}
|
|
|
|
const insertAdminAuditLog = `-- name: InsertAdminAuditLog :one
|
|
INSERT INTO admin_audit_log (actor_user_id, action, target_type, target_id, reason, payload, ip, user_agent)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
RETURNING id, created_at
|
|
`
|
|
|
|
type InsertAdminAuditLogParams struct {
|
|
ActorUserID pgtype.UUID `json:"actor_user_id"`
|
|
Action string `json:"action"`
|
|
TargetType string `json:"target_type"`
|
|
TargetID string `json:"target_id"`
|
|
Reason string `json:"reason"`
|
|
Payload []byte `json:"payload"`
|
|
Ip string `json:"ip"`
|
|
UserAgent string `json:"user_agent"`
|
|
}
|
|
|
|
type InsertAdminAuditLogRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
}
|
|
|
|
func (q *Queries) InsertAdminAuditLog(ctx context.Context, arg InsertAdminAuditLogParams) (InsertAdminAuditLogRow, error) {
|
|
row := q.db.QueryRow(ctx, insertAdminAuditLog,
|
|
arg.ActorUserID,
|
|
arg.Action,
|
|
arg.TargetType,
|
|
arg.TargetID,
|
|
arg.Reason,
|
|
arg.Payload,
|
|
arg.Ip,
|
|
arg.UserAgent,
|
|
)
|
|
var i InsertAdminAuditLogRow
|
|
err := row.Scan(&i.ID, &i.CreatedAt)
|
|
return i, err
|
|
}
|
|
|
|
const listAdminAuditLog = `-- name: ListAdminAuditLog :many
|
|
SELECT l.id, l.actor_user_id, l.action, l.target_type, l.target_id, l.reason,
|
|
l.payload, l.ip, l.created_at,
|
|
COALESCE(u.email, '')::text AS actor_email
|
|
FROM admin_audit_log l
|
|
LEFT JOIN "user" u ON u.id = l.actor_user_id
|
|
WHERE ($1::uuid IS NULL OR l.actor_user_id = $1)
|
|
AND ($2::text IS NULL OR l.action = $2)
|
|
AND ($3::text IS NULL OR l.target_id = $3)
|
|
ORDER BY l.created_at DESC, l.id DESC
|
|
LIMIT $5 OFFSET $4
|
|
`
|
|
|
|
type ListAdminAuditLogParams struct {
|
|
ActorUserID pgtype.UUID `json:"actor_user_id"`
|
|
Action pgtype.Text `json:"action"`
|
|
TargetID pgtype.Text `json:"target_id"`
|
|
PageOffset int32 `json:"page_offset"`
|
|
PageLimit int32 `json:"page_limit"`
|
|
}
|
|
|
|
type ListAdminAuditLogRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
ActorUserID pgtype.UUID `json:"actor_user_id"`
|
|
Action string `json:"action"`
|
|
TargetType string `json:"target_type"`
|
|
TargetID string `json:"target_id"`
|
|
Reason string `json:"reason"`
|
|
Payload []byte `json:"payload"`
|
|
Ip string `json:"ip"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
ActorEmail string `json:"actor_email"`
|
|
}
|
|
|
|
func (q *Queries) ListAdminAuditLog(ctx context.Context, arg ListAdminAuditLogParams) ([]ListAdminAuditLogRow, error) {
|
|
rows, err := q.db.Query(ctx, listAdminAuditLog,
|
|
arg.ActorUserID,
|
|
arg.Action,
|
|
arg.TargetID,
|
|
arg.PageOffset,
|
|
arg.PageLimit,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListAdminAuditLogRow{}
|
|
for rows.Next() {
|
|
var i ListAdminAuditLogRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.ActorUserID,
|
|
&i.Action,
|
|
&i.TargetType,
|
|
&i.TargetID,
|
|
&i.Reason,
|
|
&i.Payload,
|
|
&i.Ip,
|
|
&i.CreatedAt,
|
|
&i.ActorEmail,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listDisabledUsers = `-- name: ListDisabledUsers :many
|
|
SELECT id, email FROM "user" WHERE disabled_at IS NOT NULL
|
|
`
|
|
|
|
type ListDisabledUsersRow struct {
|
|
ID pgtype.UUID `json:"id"`
|
|
Email string `json:"email"`
|
|
}
|
|
|
|
func (q *Queries) ListDisabledUsers(ctx context.Context) ([]ListDisabledUsersRow, error) {
|
|
rows, err := q.db.Query(ctx, listDisabledUsers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListDisabledUsersRow{}
|
|
for rows.Next() {
|
|
var i ListDisabledUsersRow
|
|
if err := rows.Scan(&i.ID, &i.Email); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listPlatformAdmins = `-- name: ListPlatformAdmins :many
|
|
SELECT pa.user_id, pa.role, pa.granted_by, pa.created_at, u.name, u.email
|
|
FROM platform_admin pa
|
|
JOIN "user" u ON u.id = pa.user_id
|
|
ORDER BY pa.created_at ASC, pa.user_id ASC
|
|
`
|
|
|
|
type ListPlatformAdminsRow struct {
|
|
UserID pgtype.UUID `json:"user_id"`
|
|
Role string `json:"role"`
|
|
GrantedBy pgtype.UUID `json:"granted_by"`
|
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
}
|
|
|
|
func (q *Queries) ListPlatformAdmins(ctx context.Context) ([]ListPlatformAdminsRow, error) {
|
|
rows, err := q.db.Query(ctx, listPlatformAdmins)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
items := []ListPlatformAdminsRow{}
|
|
for rows.Next() {
|
|
var i ListPlatformAdminsRow
|
|
if err := rows.Scan(
|
|
&i.UserID,
|
|
&i.Role,
|
|
&i.GrantedBy,
|
|
&i.CreatedAt,
|
|
&i.Name,
|
|
&i.Email,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const upsertPlatformAdmin = `-- name: UpsertPlatformAdmin :one
|
|
INSERT INTO platform_admin (user_id, role, granted_by)
|
|
VALUES ($1, $2, $3)
|
|
ON CONFLICT (user_id) DO UPDATE SET role = EXCLUDED.role
|
|
RETURNING user_id, role, granted_by, created_at
|
|
`
|
|
|
|
type UpsertPlatformAdminParams struct {
|
|
UserID pgtype.UUID `json:"user_id"`
|
|
Role string `json:"role"`
|
|
GrantedBy pgtype.UUID `json:"granted_by"`
|
|
}
|
|
|
|
func (q *Queries) UpsertPlatformAdmin(ctx context.Context, arg UpsertPlatformAdminParams) (PlatformAdmin, error) {
|
|
row := q.db.QueryRow(ctx, upsertPlatformAdmin, arg.UserID, arg.Role, arg.GrantedBy)
|
|
var i PlatformAdmin
|
|
err := row.Scan(
|
|
&i.UserID,
|
|
&i.Role,
|
|
&i.GrantedBy,
|
|
&i.CreatedAt,
|
|
)
|
|
return i, err
|
|
}
|