Age: a Didit provider behind the vendor-neutral age interface. A person verifies once (ID document, passive liveness, face match) and all their linked IMVU accounts count as adult. Webhooks are checked against Didit's signatures; only "over 18" is kept. A later answer replaces an earlier one (an approval taken back is revoked). Checks are limited: one unfinished check per person, 3 in 30 days, none once verified, and a monthly cap (DIDIT_MONTHLY_LIMIT, default 30: Didit's free plan is $10 of credit a month and a check costs $0.30). The Account page gets an Age Verification card. Registration: open or closed, an admin's setting in the console (default from REGISTRATION). Closed: new accounts can only sign in until a moderator approves them (or declines, deleting the account); the website shows them a waiting page and the API treats them as signed out. Co-Authored-By: Claude Opus 5.5 <[email protected]>
78 lines
3.0 KiB
TypeScript
78 lines
3.0 KiB
TypeScript
"use server";
|
|
import { revalidatePath } from "next/cache";
|
|
import { redirect } from "next/navigation";
|
|
import { api, unwrap } from "@/lib/api";
|
|
import { attempt, field, type ActionState } from "@/lib/actions";
|
|
|
|
const roles = ["user", "moderator", "admin"] as const;
|
|
|
|
/** Runs a change to a person from the console, then shows their page again. */
|
|
async function onPerson(form: FormData, change: (id: string) => Promise<string | void>): Promise<ActionState> {
|
|
const id = field(form, "id");
|
|
return attempt(async () => {
|
|
const notice = await change(id);
|
|
revalidatePath(`/console/people/${id}`);
|
|
return notice;
|
|
});
|
|
}
|
|
|
|
export async function setLinkCap(_: ActionState, form: FormData): Promise<ActionState> {
|
|
return onPerson(form, async (id) => {
|
|
const linkCap = Number(field(form, "linkCap"));
|
|
const { person } = unwrap(await api.console.people({ id })["link-cap"].post({ linkCap }));
|
|
return `Link cap set to ${person.linkCap}.`;
|
|
});
|
|
}
|
|
|
|
export async function ban(_: ActionState, form: FormData): Promise<ActionState> {
|
|
return onPerson(form, async (id) => {
|
|
unwrap(await api.console.people({ id }).ban.post({ reason: field(form, "reason") }));
|
|
});
|
|
}
|
|
|
|
export async function unban(_: ActionState, form: FormData): Promise<ActionState> {
|
|
return onPerson(form, async (id) => {
|
|
unwrap(await api.console.people({ id }).unban.post());
|
|
});
|
|
}
|
|
|
|
export async function setRole(_: ActionState, form: FormData): Promise<ActionState> {
|
|
return onPerson(form, async (id) => {
|
|
const role = roles.find((r) => r === field(form, "role")) ?? "user";
|
|
const { person } = unwrap(await api.console.people({ id }).role.post({ role }));
|
|
return `Role set to ${person.role}.`;
|
|
});
|
|
}
|
|
|
|
export async function decideCatalog(_: ActionState, form: FormData): Promise<ActionState> {
|
|
const id = field(form, "id");
|
|
const decision = field(form, "decision") === "approve" ? "approve" : "reject";
|
|
return attempt(async () => {
|
|
unwrap(await api.console.catalogs({ id })({ decision }).post());
|
|
revalidatePath("/console/catalogs");
|
|
revalidatePath("/catalogs");
|
|
});
|
|
}
|
|
|
|
/** An account waiting for approval: approve it, or decline it (deleting it). */
|
|
export async function decideAccount(_: ActionState, form: FormData): Promise<ActionState> {
|
|
const id = field(form, "id");
|
|
const decline = field(form, "decision") === "decline";
|
|
const state = await attempt(async () => {
|
|
if (decline) unwrap(await api.console.people({ id }).decline.post());
|
|
else unwrap(await api.console.people({ id }).approve.post());
|
|
revalidatePath("/console", "layout");
|
|
});
|
|
if (decline && !state?.error) redirect("/console/people?waiting=true");
|
|
return state;
|
|
}
|
|
|
|
export async function setRegistration(_: ActionState, form: FormData): Promise<ActionState> {
|
|
return attempt(async () => {
|
|
const registration = field(form, "registration") === "closed" ? "closed" : "open";
|
|
unwrap(await api.console.settings.put({ registration }));
|
|
revalidatePath("/console");
|
|
return registration === "closed" ? "Registration closed: new accounts wait for approval." : "Registration open.";
|
|
});
|
|
}
|