All display labels in MARKETS/ENTITY_TYPES/FIELDS/ENRICHMENTS/FREQUENCIES/DELIVERY_FORMATS + SERVICES/FLOW_LABEL are now {en,sv} with stable IDs/values (pricing logic, EntityType/Frequency unions, config keys unchanged). Added entityLabel()/FREQUENCY_LABEL/FORMAT_LABEL. Updated all render sites (configurator, tjanster, beskriv, katalog/[id]) to resolve via useT(). Also fixed 2 pre-existing tsc nits (auth BufferSource cast, orders-store sql.json param type). tsc 0, build green. 0pt is now fully English-by-default with SV toggle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
// Lightweight signed-session scaffold (HMAC-SHA256 via Web Crypto, no deps).
|
|
// Production auth = Auth.js + Postgres adapter + OAuth (docs/02) — wired once the
|
|
// database is provisioned. This provides the session shape + a guarded dev login.
|
|
const enc = new TextEncoder();
|
|
export const SESSION_COOKIE = "0pt_session";
|
|
|
|
function b64url(buf: ArrayBuffer | Uint8Array): string {
|
|
const b = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
|
|
let s = "";
|
|
for (const x of b) s += String.fromCharCode(x);
|
|
return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
}
|
|
function fromB64url(s: string): Uint8Array {
|
|
s = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
s += "=".repeat((4 - (s.length % 4)) % 4);
|
|
const bin = atob(s);
|
|
const u = new Uint8Array(bin.length);
|
|
for (let i = 0; i < bin.length; i++) u[i] = bin.charCodeAt(i);
|
|
return u;
|
|
}
|
|
async function hmacKey(): Promise<CryptoKey> {
|
|
return crypto.subtle.importKey("raw", enc.encode(process.env.AUTH_SECRET ?? ""), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]);
|
|
}
|
|
|
|
export async function signSession(payload: Record<string, unknown>): Promise<string> {
|
|
const body = b64url(enc.encode(JSON.stringify(payload)));
|
|
const sig = await crypto.subtle.sign("HMAC", await hmacKey(), enc.encode(body));
|
|
return `${body}.${b64url(sig)}`;
|
|
}
|
|
|
|
export async function verifySession(token: string | undefined | null): Promise<Record<string, unknown> | null> {
|
|
if (!token || !process.env.AUTH_SECRET) return null;
|
|
const [body, sig] = token.split(".");
|
|
if (!body || !sig) return null;
|
|
const ok = await crypto.subtle.verify("HMAC", await hmacKey(), fromB64url(sig) as BufferSource, enc.encode(body));
|
|
if (!ok) return null;
|
|
try {
|
|
return JSON.parse(new TextDecoder().decode(fromB64url(body)));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|