Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation. Made-with: Cursor
79 lines
1.9 KiB
TypeScript
79 lines
1.9 KiB
TypeScript
export type ExchangeListing = {
|
|
id: string;
|
|
kind: "wts" | "wtb";
|
|
title: string;
|
|
body: string;
|
|
price: string;
|
|
currency: "BTC" | "XMR" | "USD";
|
|
author: string;
|
|
ts: number;
|
|
};
|
|
|
|
const KEY = "cyberlux-exchange-v1";
|
|
|
|
function uid(): string {
|
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
return `e_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
}
|
|
|
|
const seed: ExchangeListing[] = [
|
|
{
|
|
id: "es-1",
|
|
kind: "wts",
|
|
title: "Hardware entropy module (parody listing)",
|
|
body: "Open-box. Local pickup on this onion only. Meet at public node.",
|
|
price: "0.015",
|
|
currency: "BTC",
|
|
author: "EntropyCorp",
|
|
ts: Date.now() - 3600000 * 20,
|
|
},
|
|
{
|
|
id: "es-2",
|
|
kind: "wtb",
|
|
title: "Looking for vintage cipher manuals (replica)",
|
|
body: "Budget flexible for the right scan quality.",
|
|
price: "120",
|
|
currency: "USD",
|
|
author: "Collector42",
|
|
ts: Date.now() - 3600000 * 8,
|
|
},
|
|
];
|
|
|
|
export function loadExchange(): ExchangeListing[] {
|
|
if (typeof window === "undefined") return seed;
|
|
try {
|
|
const raw = localStorage.getItem(KEY);
|
|
if (!raw) {
|
|
saveExchange(seed);
|
|
return seed;
|
|
}
|
|
const v = JSON.parse(raw) as ExchangeListing[];
|
|
return Array.isArray(v) && v.length ? v : seed;
|
|
} catch {
|
|
return seed;
|
|
}
|
|
}
|
|
|
|
export function saveExchange(list: ExchangeListing[]): void {
|
|
if (typeof window === "undefined") return;
|
|
localStorage.setItem(KEY, JSON.stringify(list));
|
|
}
|
|
|
|
export function addListing(
|
|
L: Omit<ExchangeListing, "id" | "ts">,
|
|
): ExchangeListing {
|
|
const row: ExchangeListing = {
|
|
id: uid(),
|
|
ts: Date.now(),
|
|
kind: L.kind,
|
|
title: L.title.trim(),
|
|
body: L.body.trim(),
|
|
price: L.price.trim(),
|
|
currency: L.currency,
|
|
author: L.author.trim() || "Anonymous",
|
|
};
|
|
const all = [row, ...loadExchange()];
|
|
saveExchange(all);
|
|
return row;
|
|
}
|