tlv84yb7n: Build the sign-in screen and signed-in placeholder shell

This commit is contained in:
2026-10-19 13:21:27 -06:00
parent 6e4cc3494b
commit 22aa454b81
8 changed files with 226 additions and 10 deletions
+51
View File
@@ -0,0 +1,51 @@
// Browser-side calls to this app's own /api/session. No token, password or
// session id is stored or logged here; the session lives in an httpOnly cookie.
export type Person = { name: string; email: string; pictureUrl: string | null };
export const WRONG_CREDENTIALS = "Email or password is incorrect.";
export const GENERIC_FAILURE = "Could not sign you in. Try again.";
type Fetch = typeof fetch;
function toPerson(body: unknown): Person | null {
if (!body || typeof body !== "object") return null;
const r = body as Record<string, unknown>;
if (typeof r.email !== "string") return null;
return {
name: typeof r.name === "string" ? r.name : "",
email: r.email,
pictureUrl: typeof r.pictureUrl === "string" ? r.pictureUrl : null,
};
}
/** GET /api/session: the person, or null when signed out or unreachable. */
export async function loadSession(f: Fetch = fetch): Promise<Person | null> {
try {
const res = await f("/api/session", { credentials: "same-origin", cache: "no-store" });
if (!res.ok) return null;
return toPerson(await res.json());
} catch {
return null;
}
}
export type SignInResult = { ok: true; person: Person } | { ok: false; message: string };
/** POST /api/session. A 401 is always the one wrong-credentials message. */
export async function signIn(email: string, password: string, f: Fetch = fetch): Promise<SignInResult> {
try {
const res = await f("/api/session", {
method: "POST",
credentials: "same-origin",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email, password }),
});
if (res.status === 401) return { ok: false, message: WRONG_CREDENTIALS };
if (!res.ok) return { ok: false, message: GENERIC_FAILURE };
const person = toPerson(await res.json());
return person ? { ok: true, person } : { ok: false, message: GENERIC_FAILURE };
} catch {
return { ok: false, message: GENERIC_FAILURE };
}
}