proto(woo): per-browser Woo creds via X-Woo-* request headers
Prototype routing so 2–3 owners can share one store URL with different WP application passwords. Device creds live in localStorage; server uses request override → Settings fallback and never writes overrides into the Settings row. Inference stays on shared Settings. See PROTOTYPE.md for UAT with two browsers and known LAN forge risk.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user