mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
f0e4679887
Players sign in to the website, and admins to the admin panel, with an OpenID Connect provider (DorkNet:SignIn:*). The first sign-in links the provider identity (issuer + subject) to a DorkNet account with that account's password; after that the provider sign-in is enough. - Cookie sessions per host, keys persisted in the database so redeploys keep people signed in; unlink, deletion or a ban ends the session. - Link attempts limited to 5 failures per 15 minutes per identity and per username. - Admin panel: "Sign in with ..." exchanges the session for the usual admin tokens via GET /api/admin/v1/sso (IsAdmin still required). - Downloads: optional DorkNet:Downloads:RequireSignIn serves the manifest and files only to signed-in players; the Downloads and Quest pages offer sign-in instead. - Website: sign-in in the header, /link and /account pages. The game client's login is unchanged.
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
// Lightweight fetch wrapper for the public-facing site. Talks to the
|
|
// apex-hosted PublicSiteController at /api/site/v1/*. No Authorization
|
|
// header or token plumbing: the optional sign-in (lib/account.ts) is an
|
|
// HttpOnly cookie that same-origin fetches send on their own.
|
|
|
|
const BASE = '/api/site/v1';
|
|
|
|
export class ApiError extends Error {
|
|
constructor(public status: number, message: string) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
}
|
|
}
|
|
|
|
export async function get<T = unknown>(path: string): Promise<T> {
|
|
const res = await fetch(BASE + path);
|
|
if (!res.ok) {
|
|
let msg = `${res.status} ${res.statusText}`;
|
|
try {
|
|
const body = await res.json();
|
|
if (body?.error) msg = body.error;
|
|
else if (body?.message) msg = body.message;
|
|
} catch { /* keep status text */ }
|
|
throw new ApiError(res.status, msg);
|
|
}
|
|
if (res.status === 204) return undefined as T;
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
// POST with a JSON body. Used by the /join signup-code flow — the only
|
|
// write path the otherwise read-only public site has. On a non-2xx the
|
|
// server's { error } string (e.g. "code_expired") is surfaced as the
|
|
// thrown ApiError message so the page can map it to friendly copy.
|
|
export async function post<T = unknown>(path: string, body: unknown): Promise<T> {
|
|
const res = await fetch(BASE + path, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
});
|
|
if (!res.ok) {
|
|
let msg = `${res.status} ${res.statusText}`;
|
|
try {
|
|
const b = await res.json();
|
|
if (b?.error) msg = b.error;
|
|
else if (b?.message) msg = b.message;
|
|
} catch { /* keep status text */ }
|
|
throw new ApiError(res.status, msg);
|
|
}
|
|
if (res.status === 204) return undefined as T;
|
|
return res.json() as Promise<T>;
|
|
}
|