Add account restrictions, appeals, and admin moderation tools

This commit is contained in:
2026-10-13 12:32:37 +05:00
parent 018be5049e
commit cfe5820802
28 changed files with 1936 additions and 125 deletions

View File

@@ -0,0 +1,65 @@
import { z } from "zod";
export const accountStatuses = [
"active",
"deactivated",
"banned",
"deleting",
] as const;
export type AccountStatus = (typeof accountStatuses)[number];
export const accountReason = z.string().trim().min(10).max(2000);
export const accountActionInput = z
.object({
action: z.enum(["deactivate", "ban", "restore", "delete"]),
reason: accountReason,
confirmation: z.string().uuid().optional(),
})
.strict();
export const accountAppealInput = z.object({ reason: accountReason }).strict();
export const accountAppealReviewInput = z
.object({
decision: z.enum(["approve", "reject"]),
reason: accountReason,
})
.strict();
export function restrictionMessage(status: string) {
return status === "banned"
? "Your account is banned. Visit /ban/appeal to read the reason and appeal."
: status === "deleting"
? "Account deletion is pending. Visit /ban/appeal for account status."
: "Your account is deactivated. Visit /ban/appeal for account status.";
}
/** Restricted accounts retain only the pages needed to understand/appeal access. */
export function restrictionPageAllowed(path: string) {
return (
path === "/ban/appeal" ||
path === "/docs" ||
path === "/log-in" ||
path === "/auth/callback" ||
path === "/auth/reset-password"
);
}
export type ManagedAccount = {
id: string;
display_name: string;
role: string;
account_status: AccountStatus;
account_reason: string | null;
account_changed_at: string | null;
status_version: number;
};
export type AccountAppealRecord = {
ban_reason?: string | null;
id: string;
owner_id: string;
display_name: string;
status_version: number;
reason: string;
state: "open" | "approved" | "rejected";
created_at: string;
review_reason: string | null;
reviewed_at: string | null;
};