Files
SlabUploader/frontend/src/api/wooDeviceCreds.ts
T
grokbot_ty 2f9b8ebd02 proto(woo): blocking setup gate; require X-Woo-* on Woo HTTP routes
Full-screen WooSetupGate on App mount when this browser lacks device WP
user + app password. Save + Test Woo dismisses only after a successful
test. Publish/test-woo auth failures reopen the gate.

Server: client_for_request requires X-Woo-* on publish, taxonomy sync,
and test-woo (401 woo_device_creds_required); health probe keeps
Settings. Optional SLAB_WOO_ALLOW_SETTINGS_FALLBACK for legacy tests.

See PROTOTYPE.md.
2026-09-26 19:20:16 -07:00

85 lines
2.4 KiB
TypeScript

/**
* Browser-local Woo credentials (prototype per-user routing).
*
* Stored in localStorage on this device only — never written to server Settings
* via the device-creds path. Attached as X-Woo-Username / X-Woo-App-Password on
* Woo-touching API calls. See PROTOTYPE.md.
*/
const LS_USER = 'slabuploader.woo.device.username';
const LS_PASS = 'slabuploader.woo.device.appPassword';
export const HEADER_WOO_USERNAME = 'X-Woo-Username';
export const HEADER_WOO_APP_PASSWORD = 'X-Woo-App-Password';
export interface DeviceWooCreds {
username: string;
appPassword: string;
}
function safeGet(key: string): string {
try {
return (localStorage.getItem(key) || '').trim();
} catch {
return '';
}
}
function safeSet(key: string, value: string): void {
try {
if (value) localStorage.setItem(key, value);
else localStorage.removeItem(key);
} catch {
/* private mode / quota — ignore */
}
}
export function getDeviceWooCreds(): DeviceWooCreds {
return {
username: safeGet(LS_USER),
appPassword: safeGet(LS_PASS),
};
}
/** Persist this browser's Woo login. Empty strings clear the slot. */
export function setDeviceWooCreds(creds: DeviceWooCreds): void {
safeSet(LS_USER, (creds.username || '').trim());
safeSet(LS_PASS, (creds.appPassword || '').trim());
}
export function clearDeviceWooCreds(): void {
safeSet(LS_USER, '');
safeSet(LS_PASS, '');
}
/** Headers to merge into Woo-backed fetch calls. Empty object when unset. */
export function wooOverrideHeaders(): Record<string, string> {
const { username, appPassword } = getDeviceWooCreds();
if (!username || !appPassword) return {};
return {
[HEADER_WOO_USERNAME]: username,
[HEADER_WOO_APP_PASSWORD]: appPassword,
};
}
/** Merge Content-Type / caller headers with device Woo override when present. */
export function withWooHeaders(extra?: HeadersInit): HeadersInit {
const woo = wooOverrideHeaders();
if (!extra) return woo;
if (extra instanceof Headers) {
const h = new Headers(extra);
for (const [k, v] of Object.entries(woo)) h.set(k, v);
return h;
}
if (Array.isArray(extra)) {
return [...extra, ...Object.entries(woo)];
}
return { ...extra, ...woo };
}
/** True when this browser has both device Woo username and app password. */
export function hasDeviceWooCreds(): boolean {
const { username, appPassword } = getDeviceWooCreds();
return Boolean(username && appPassword);
}