Website: Next.js app for accounts, linking, who's online, device sign-in and the console
apps/web: Next.js 16 (App Router), Tailwind 4 and shadcn/ui, light and dark from the system. Pages render on the server and call the API with Eden Treaty (typed by @t5de/api), forwarding the visitor's cookie; forms use server actions; sign-up, sign-in and sign-out use Better Auth's React client. /api is rewritten to the API so its cookies are first-party. Who's online follows the API's event stream (Next's gzip is off: it held the stream back). Standalone output with a Dockerfile; deploys to Vercel as is. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
7afada1c63
commit
64c552330b
57 files changed
+3834
No files matched your search
@@ -0,0 +1,60 @@
|
||||
"use server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { api, unwrap } from "@/lib/api";
|
||||
import { attempt, field, type ActionState } from "@/lib/actions";
|
||||
|
||||
const done = () => revalidatePath("/account");
|
||||
|
||||
/** Starts linking an IMVU account: the API makes a code to put in its tagline. */
|
||||
export async function linkAccount(_: ActionState, form: FormData): Promise<ActionState> {
|
||||
return attempt(async () => {
|
||||
unwrap(await api.account.links.post({ username: field(form, "username") }));
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
/** A pending link: check the tagline for the code, or cancel. */
|
||||
export async function challengeAction(_: ActionState, form: FormData): Promise<ActionState> {
|
||||
const imvu = field(form, "imvu");
|
||||
return attempt(async () => {
|
||||
if (field(form, "intent") === "cancel") {
|
||||
unwrap(await api.account.challenges({ imvu }).delete());
|
||||
} else {
|
||||
unwrap(await api.account.links({ imvu }).check.post());
|
||||
}
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
/** A linked account: check its age status with IMVU again, or unlink it. */
|
||||
export async function linkAction(_: ActionState, form: FormData): Promise<ActionState> {
|
||||
const imvu = field(form, "imvu");
|
||||
return attempt(async () => {
|
||||
if (field(form, "intent") === "unlink") {
|
||||
unwrap(await api.account.links({ imvu }).delete());
|
||||
done();
|
||||
return;
|
||||
}
|
||||
unwrap(await api.account.links({ imvu }).recheck.post());
|
||||
done();
|
||||
return "Checked with IMVU just now.";
|
||||
});
|
||||
}
|
||||
|
||||
export async function saveSettings(
|
||||
imvu: number,
|
||||
settings: { showBadge?: boolean; showPresence?: boolean },
|
||||
): Promise<ActionState> {
|
||||
return attempt(async () => {
|
||||
unwrap(await api.account.links({ imvu }).patch(settings));
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
export async function signOutComputers(_: ActionState, _form: FormData): Promise<ActionState> {
|
||||
return attempt(async () => {
|
||||
unwrap(await api.account["signout-computers"].post());
|
||||
done();
|
||||
return "Signed out everywhere.";
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user