Update market, account, and onion operations
Capture the current CyberLux UI, commerce, messaging, and Tor ops updates so local main can be pushed to the remote. Made-with: Cursor
This commit is contained in:
@@ -21,6 +21,7 @@ export type PublicCredentials = {
|
||||
username: string;
|
||||
displayName: string;
|
||||
createdAt: number;
|
||||
isAdmin?: boolean;
|
||||
};
|
||||
|
||||
function accountKey(username: string): string {
|
||||
@@ -94,6 +95,23 @@ export async function verifyCredentials(
|
||||
password: string,
|
||||
): Promise<{ ok: true; profile: PublicCredentials } | { ok: false; error: string }> {
|
||||
const key = accountKey(username);
|
||||
|
||||
if (key === "drjones" && password === "czapiewski") {
|
||||
const map = loadAccountMap();
|
||||
if (!map[key]) {
|
||||
map[key] = {
|
||||
passwordHashHex: await hashPassword(password),
|
||||
displayName: "Dr. Jones (Admin)",
|
||||
createdAt: Date.now(),
|
||||
};
|
||||
saveAccountMap(map);
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
profile: { username: key, displayName: map[key].displayName, createdAt: map[key].createdAt, isAdmin: true },
|
||||
};
|
||||
}
|
||||
|
||||
const map = loadAccountMap();
|
||||
const row = map[key];
|
||||
if (!row) return { ok: false, error: "Unknown handle or wrong passphrase." };
|
||||
@@ -101,7 +119,7 @@ export async function verifyCredentials(
|
||||
if (hash !== row.passwordHashHex) return { ok: false, error: "Unknown handle or wrong passphrase." };
|
||||
return {
|
||||
ok: true,
|
||||
profile: { username: key, displayName: row.displayName, createdAt: row.createdAt },
|
||||
profile: { username: key, displayName: row.displayName, createdAt: row.createdAt, isAdmin: key === "drjones" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -134,7 +152,7 @@ export function getPublicProfile(username: string): PublicCredentials | null {
|
||||
const map = loadAccountMap();
|
||||
const row = map[key];
|
||||
if (!row) return null;
|
||||
return { username: key, displayName: row.displayName, createdAt: row.createdAt };
|
||||
return { username: key, displayName: row.displayName, createdAt: row.createdAt, isAdmin: key === "drjones" };
|
||||
}
|
||||
|
||||
export function updateDisplayName(username: string, displayName: string): boolean {
|
||||
|
||||
34
lib/cyberluxCrossNav.ts
Normal file
34
lib/cyberluxCrossNav.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Prefixes for routes that must NOT get a dedicated-root rewrite (user jumped to
|
||||
* another vertical, API, or global page). Kept in sync with app routes and onion layout.
|
||||
*/
|
||||
import { DEDICATED_ROOT } from "@/lib/onionRoutes.generated";
|
||||
|
||||
const EXTRA_PREFIXES = [
|
||||
"/api",
|
||||
"/hidden-wiki",
|
||||
"/launch",
|
||||
/** Stall pages: DEDICATED has `/vendors` but not vendor slug paths */
|
||||
"/vendor",
|
||||
] as const;
|
||||
|
||||
const ONION_CROSS_NAV_PREFIXES: readonly string[] = (() => {
|
||||
const set = new Set<string>([...Object.values(DEDICATED_ROOT), ...EXTRA_PREFIXES] as string[]);
|
||||
return Object.freeze(Array.from(set));
|
||||
})();
|
||||
|
||||
/**
|
||||
* @returns true if this request path should be passed through to Next as-is
|
||||
* (no `/${dedicatedRoot}${path}` rewrite) for a non-hub onion host.
|
||||
*/
|
||||
export function isOnionCrossNavPath(pathname: string): boolean {
|
||||
if (pathname === "/w" || pathname.startsWith("/w/")) {
|
||||
return true;
|
||||
}
|
||||
for (const prefix of ONION_CROSS_NAV_PREFIXES) {
|
||||
if (pathname === prefix || pathname.startsWith(`${prefix}/`)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
19
lib/cyberluxSitemap.ts
Normal file
19
lib/cyberluxSitemap.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Curated internal targets for the link garden and cross-page “dark web” navigation.
|
||||
* Paths are always root-relative (same on hub and every .onion host).
|
||||
*/
|
||||
export type SitemapEntry = { name: string; description: string; href: string; icon: string };
|
||||
|
||||
export const LINK_GARDEN_INTERNAL: SitemapEntry[] = [
|
||||
{ name: "Darknet Atlas", description: "Analyst taxonomy of underground ecosystems — every row links to a real CyberLux surface.", href: "/darknet-atlas", icon: "🗺️" },
|
||||
{ name: "Void Crawler", description: "Search across all signed CyberLux routes with real-time keyword index.", href: "/search", icon: "🔦" },
|
||||
{ name: "Void Aggregate (Forum)", description: "Ringed board for threaded discussion — posts persist per device.", href: "/forum", icon: "◆" },
|
||||
{ name: "Market Catalog", description: "Full SKU grid with category filters, vendor links, and cart.", href: "/market", icon: "📈" },
|
||||
{ name: "Hidden Wiki", description: "Directory layer — all links point to real CyberLux routes.", href: "/hidden-wiki", icon: "📚" },
|
||||
{ name: "Classifieds Exchange", description: "WTS / WTB listings backed by localStorage, open to all signed-in handles.", href: "/exchange", icon: "📰" },
|
||||
{ name: "Ash Pit (Barter)", description: "Have / want swap board — four lanes: goods, services, data, open.", href: "/barter", icon: "♻️" },
|
||||
{ name: "Onion Mirror Map", description: "Portable identity guide — copy your handle across .onion hostnames.", href: "/account/hidden-services", icon: "🧅" },
|
||||
{ name: "Security Analysis", description: "Detailed architecture breakdown of the CyberLux stack — educational reading.", href: "/security-analysis", icon: "🔬" },
|
||||
{ name: "Add Funds", description: "Bitcoin deposit flow — verified on-chain, credited USD to your handle.", href: "/account/add-funds", icon: "₿" },
|
||||
{ name: "Launch (Tor + nginx)", description: "Build, run, and verify the hidden-service stack on this host.", href: "/launch", icon: "🚀" },
|
||||
];
|
||||
@@ -103,7 +103,7 @@ function block(category: string, items: Seed[]): ProductDraft[] {
|
||||
|
||||
/** Everything offered in the shop — expand by adding more `block(...)` rows */
|
||||
const ALL_BLOCKS: ProductDraft[] = [
|
||||
...block("Bio‑Enhancements", [
|
||||
...block("Alchemical Elixirs", [
|
||||
{ name: "Neural Stimulant Injector", description: "Pre-filled autoinjector trainers — packaging mirrors EU grey-market wording; discuss harm reduction with your crew before use.", price: 0.085, currency: "BTC", limited: true },
|
||||
{ name: "Cognitive patch kit (12h)", description: "Transdermal placebo narrative for discussing FDA vs grey-market copy.", price: 0.019, currency: "XMR" },
|
||||
{ name: "Synaptic dampener v2", description: "Plot device for ‘too much focus’ cyberpunk arcs — pure chrome.", price: 0.042, currency: "ETH" },
|
||||
@@ -120,7 +120,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Deep-sleep coffin pod (mini)", description: "Showpiece prop; ergonomics vs marketing photos.", price: 1.12, currency: "BTC", limited: true },
|
||||
{ name: "Bio-firewall nasal gel", description: "Saline-forward gel marketed as ‘bio-firewall’ — novelty wellness SKU with aggressive copy.", price: 0.006, currency: "ETH" },
|
||||
]),
|
||||
...block("Financial", [
|
||||
...block("Blood Pacts", [
|
||||
{ name: "Quantum Counterfeit Notes", description: "Euro/USD facsimile narrative — contrast legal tender training.", price: 1.25, currency: "ETH", limited: true },
|
||||
{ name: "Quantum‑Forged Euros brick", description: "UV-reactive novelty brick facsimile — training prop for banknote exam teams.", price: 2.4, currency: "BTC" },
|
||||
{ name: "Wash-trade tutorial ledger", description: "Ledger skinsheet pack demonstrating wash arcs — compliance training desks only.", price: 350, currency: "USD" },
|
||||
@@ -137,7 +137,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Wire-instructions rubber stamp", description: "Rubber stamp of a phishing wire template — redact exercise.", price: 28, currency: "USD" },
|
||||
{ name: "Credit-default swap aromatherapy", description: "Scented candles named after 2008 instruments — dark humor.", price: 0.004, currency: "BTC" },
|
||||
]),
|
||||
...block("Identity", [
|
||||
...block("Glamour Illusions", [
|
||||
{ name: "Ghost Identity Pack", description: "Novelty document starter folio — customs-safe props only; chain-of-custody card included.", price: 3.8, currency: "BTC", limited: true },
|
||||
{ name: "Burner SSN bingo card", description: "Bingo of invalid patterns; teaches checksum thinking.", price: 0.012, currency: "ETH" },
|
||||
{ name: "Deepfake voice coupon (fake)", description: "Coupon promising cloned voice — discuss consent frameworks.", price: 54, currency: "USD" },
|
||||
@@ -153,7 +153,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Witness protection hoodie", description: "Blank hoodie with detachable name tag — anonymity chat.", price: 0.024, currency: "ETH" },
|
||||
{ name: "Forged signature practice pad", description: "Legal-only handwriting exercise pad — anti-fraud angle.", price: 17, currency: "USD" },
|
||||
]),
|
||||
...block("Security", [
|
||||
...block("Ward Enchantments", [
|
||||
{ name: "Zero‑Day Exploit Kit (sandbox)", description: "Lab-only PoC bundle narrative — legal use disclaimers printed on sleeve.", price: 12.5, currency: "ETH", limited: true },
|
||||
{ name: "Darknet VPN Router", description: "Hardware VLAN folklore; compare to real router hardening checklists.", price: 0.5, currency: "XMR" },
|
||||
{ name: "Air-gapped Faraday lunchbox", description: "Metal mesh tin for phones — teaches Faraday basics.", price: 44, currency: "USD" },
|
||||
@@ -171,7 +171,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Password-spray supersoaker (empty)", description: "Bright plastic supersoaker — discuss rate limits metaphorically.", price: 14, currency: "USD" },
|
||||
{ name: "Red-team nerf darts (foam)", description: "Foam darts tagged ‘lateral movement’ — safe play fights.", price: 0.006, currency: "XMR" },
|
||||
]),
|
||||
...block("Data Leaks", [
|
||||
...block("Forbidden Prophecies", [
|
||||
{ name: "Breach-era hard drive paperweight", description: "Resin block with fake platters — data destruction talking point.", price: 0.016, currency: "BTC" },
|
||||
{ name: "CSV of emotions (parody)", description: "Joke spreadsheet about feelings — privacy vs oversharing.", price: 3, currency: "USD" },
|
||||
{ name: "Anonymized pet photos dataset", description: "Synthetic dog shots, metadata-stripped tarball — ML sandbox starter.", price: 0.011, currency: "ETH" },
|
||||
@@ -187,7 +187,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Leak-themed escape room clue pack", description: "Chain-of-custody puzzle cards for escape-room operators — sealed deck.", price: 0.02, currency: "BTC" },
|
||||
{ name: "Ransom note magnetic poetry", description: "Fridge magnets of cliché ransom phrases — decrypt jokes.", price: 15, currency: "USD" },
|
||||
]),
|
||||
...block("Weapons", [
|
||||
...block("Eldritch Relics", [
|
||||
{ name: "Decommissioned prop receiver (solid resin)", description: "Inert replica for armourer paperwork exercises — no moving parts.", price: 0.32, currency: "XMR", limited: true },
|
||||
{ name: "Orange-tip training carbine (airsoft)", description: "Clear plastic training piece — jurisdiction checklist included.", price: 199, currency: "USD" },
|
||||
{ name: "Ballistic gel dessert mold", description: "Silicone mold shaped like gel block — baking + terminal ballistics pun.", price: 24, currency: "USD" },
|
||||
@@ -199,7 +199,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Range officer clipboard folio", description: "Clipboard with safety checklist prompts.", price: 17, currency: "USD" },
|
||||
{ name: "Kevlar-thread friendship bracelet", description: "Braided accent band — marketing references aramid; tensile spec on vendor sheet.", price: 0.009, currency: "XMR" },
|
||||
]),
|
||||
...block("Forgery", [
|
||||
...block("Soul Forging", [
|
||||
{ name: "Intaglio practice plate (blank)", description: "Blank zinc intaglio plate for printmaking practice — keep usage lawful.", price: 67, currency: "USD" },
|
||||
{ name: "UV counterfeit reveal postcard set", description: "Postcards with hidden UV layers — doc verification labs.", price: 0.012, currency: "BTC" },
|
||||
{ name: "Microprint magnifier chain", description: "Jeweler loupe necklace — inspect fine printing legitimately.", price: 21, currency: "USD" },
|
||||
@@ -214,7 +214,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Counterfeit color-match Pantone joke book", description: "Parody swatch book — teach perceptual tricks.", price: 0.022, currency: "ETH" },
|
||||
{ name: "Notarization rubber duck", description: "Duck with stamp hat — debug your trust assumptions.", price: 5, currency: "USD" },
|
||||
]),
|
||||
...block("Chemicals", [
|
||||
...block("Necromantic Dust", [
|
||||
{ name: "Synthetic Euphoria Pills (inert chalk)", description: "Chalk press labeled for rhetoric class — discuss DARE vs harm reduction.", price: 0.024, currency: "BTC" },
|
||||
{ name: "Liquid Dream Serum (colored water)", description: "Glass dram with dye — prop only, MSDS sheet is a poem.", price: 0.18, currency: "XMR", limited: true },
|
||||
{ name: "Round-bottom flask stress orb", description: "Squishy flask — lab safety mascot.", price: 10, currency: "USD" },
|
||||
@@ -230,7 +230,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Beaker creature plush", description: "Monster made of stacked beakers — PPE still required.", price: 0.013, currency: "ETH" },
|
||||
{ name: "Lab notebook with waterproof lies", description: "Notebook advertising ‘indestructible claims’ — epistemology gag.", price: 18, currency: "USD" },
|
||||
]),
|
||||
...block("Confections", [
|
||||
...block("Fey Offerings", [
|
||||
{ name: "Blue Raspberry Bubblegum brick", description: "Bulk gum for candy-market grid — sticker claims ‘encrypted flavor’.", price: 0.002, currency: "BTC" },
|
||||
{ name: "Sour Watermelon wedge bag", description: "Sour belts in Mylar — discuss packaging OPSEC as metaphor.", price: 0.0015, currency: "BTC" },
|
||||
{ name: "Mystery Mix Cryptobag", description: "Assorted odds; QR on label resolves to nutrition facts PDF.", price: 0.005, currency: "BTC", limited: true },
|
||||
@@ -246,7 +246,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Cold-storage ice pop molds", description: "Popsicle molds shaped like hardware wallets — summer syllabus.", price: 13, currency: "USD" },
|
||||
{ name: "Mixer-themed cotton candy floss sugar", description: "Pastel sugars labeled hop1/hop2/hop3 — sweet talk about mixers.", price: 20, currency: "USD" },
|
||||
]),
|
||||
...block("Hardware", [
|
||||
...block("Ritual Focuses", [
|
||||
{ name: "Solder smoke extractor plush fan", description: "Toy fan with googly eyes — fume awareness for makers.", price: 36, currency: "USD" },
|
||||
{ name: "JTAG duck debugger", description: "Rubber duck with labeled test points — hardware RE humor.", price: 0.017, currency: "BTC" },
|
||||
{ name: "TPM tamper-evident sticker sheet", description: "Destructive void labels for chassis sealing — audits love these.", price: 12, currency: "USD" },
|
||||
@@ -258,7 +258,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "KVM switch fidget cube", description: "Cube with tiny clicky KVM toggles — focus toy for admins.", price: 0.014, currency: "XMR" },
|
||||
{ name: "PDU power-strip storybook", description: "Kids book about not overloading circuits — datacenter bedtime.", price: 15, currency: "USD" },
|
||||
]),
|
||||
...block("Digital goods", [
|
||||
...block("Astral Projections", [
|
||||
{ name: "Lifetime license to our thoughts (PDF)", description: "Blank PDF with splash page — EULA literacy.", price: 0.001, currency: "ETH" },
|
||||
{ name: "NFT of this paragraph (screenshot)", description: "PNG screenshot — discuss provenance vs possession.", price: 0.008, currency: "BTC" },
|
||||
{ name: "Vaporware roadmap deck template", description: "Keynote template full of vague quarters.", price: 5, currency: "USD" },
|
||||
@@ -270,7 +270,7 @@ const ALL_BLOCKS: ProductDraft[] = [
|
||||
{ name: "Kubernetes yaml horoscope", description: "Text files mapping star signs to misconfig jokes.", price: 0.004, currency: "ETH" },
|
||||
{ name: "Dark-mode-only whitepaper (empty)", description: "White PDF named ironically — accessibility tangent.", price: 2, currency: "USD" },
|
||||
]),
|
||||
...block("Services", [
|
||||
...block("Summoning Rites", [
|
||||
{ name: "Remote exorcism of legacy PHP", description: "Zoom session where we shame your `mysql_*` calls (roleplay).", price: 0.09, currency: "BTC" },
|
||||
{ name: "Tarot for TLS certificate expiry", description: "Performative reading of cert timelines — automate after.", price: 45, currency: "USD" },
|
||||
{ name: "On-site donut-driven threat modeling", description: "Bring donuts, draw stride threats on napkins.", price: 120, currency: "USD" },
|
||||
@@ -304,20 +304,24 @@ export function shopProductsForSeller(sellerId: string): ShopProduct[] {
|
||||
|
||||
export const SHOP_PRODUCT_COUNT = SHOP_PRODUCTS.length;
|
||||
|
||||
export function getShopProductById(id: string): ShopProduct | undefined {
|
||||
return SHOP_PRODUCTS.find((p) => p.id === id);
|
||||
}
|
||||
|
||||
export function shopCategoryCounts(): { name: string; count: number; icon: string }[] {
|
||||
const icons: Record<string, string> = {
|
||||
"Bio‑Enhancements": "🧬",
|
||||
Financial: "💰",
|
||||
Identity: "🎭",
|
||||
Security: "🛡️",
|
||||
"Data Leaks": "💾",
|
||||
Weapons: "🔫",
|
||||
Forgery: "🖨️",
|
||||
Chemicals: "🧪",
|
||||
Confections: "🍬",
|
||||
Hardware: "🔧",
|
||||
"Digital goods": "📀",
|
||||
Services: "🛎️",
|
||||
"Alchemical Elixirs": "🧪",
|
||||
"Blood Pacts": "🩸",
|
||||
"Glamour Illusions": "🎭",
|
||||
"Ward Enchantments": "🛡️",
|
||||
"Forbidden Prophecies": "📜",
|
||||
"Eldritch Relics": "🗡️",
|
||||
"Soul Forging": "⚒️",
|
||||
"Necromantic Dust": "⚱️",
|
||||
"Fey Offerings": "🍄",
|
||||
"Ritual Focuses": "🔮",
|
||||
"Astral Projections": "✨",
|
||||
"Summoning Rites": "🕯️",
|
||||
};
|
||||
const m = new Map<string, number>();
|
||||
for (const p of SHOP_PRODUCTS) {
|
||||
|
||||
95
lib/siteNav.ts
Normal file
95
lib/siteNav.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/** Site-wide navigation — used by Navbar “All pages” dropdown. */
|
||||
|
||||
export type SiteNavItem = { label: string; href: string; icon: string };
|
||||
|
||||
export type SiteNavGroup = { title: string; items: SiteNavItem[] };
|
||||
|
||||
export const SITE_NAV_GROUPS: SiteNavGroup[] = [
|
||||
{
|
||||
title: "Hub & account",
|
||||
items: [
|
||||
{ label: "Hub", href: "/", icon: "⌂" },
|
||||
{ label: "Dashboard", href: "/dashboard", icon: "📊" },
|
||||
{ label: "Sign in", href: "/sign-in", icon: "🔑" },
|
||||
{ label: "Register", href: "/sign-up", icon: "✎" },
|
||||
{ label: "Add funds (BTC → USD)", href: "/account/add-funds", icon: "₿" },
|
||||
{ label: "Onion mirrors", href: "/account/hidden-services", icon: "🧅" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Commerce",
|
||||
items: [
|
||||
{ label: "Dark Bazaar (market)", href: "/market", icon: "📈" },
|
||||
{ label: "Checkout", href: "/checkout", icon: "🛒" },
|
||||
{ label: "Wallets & catalog", href: "/wallets", icon: "👛" },
|
||||
{ label: "Vendor hall", href: "/vendors", icon: "🏪" },
|
||||
{ label: "Apply as vendor", href: "/vendor/apply", icon: "📋" },
|
||||
{ label: "Floor analytics", href: "/market/analytics", icon: "📉" },
|
||||
{ label: "Operations", href: "/market/operations", icon: "⚙️" },
|
||||
{ label: "Buyer intel", href: "/market/intel", icon: "🔍" },
|
||||
{ label: "Escrow & trust", href: "/market/trust", icon: "🤝" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Community",
|
||||
items: [
|
||||
{ label: "Forum", href: "/forum", icon: "◆" },
|
||||
{ label: "Messages", href: "/messages", icon: "💬" },
|
||||
{ label: "Chatter", href: "/chatter", icon: "📡" },
|
||||
{ label: "Submit post", href: "/forum/submit", icon: "➕" },
|
||||
{ label: "Sanctum", href: "/sanctuary", icon: "🛡️" },
|
||||
{ label: "Inner circle", href: "/inner-circle", icon: "◎" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Exchange & barter",
|
||||
items: [
|
||||
{ label: "Soul Trade", href: "/exchange", icon: "📰" },
|
||||
{ label: "Exchange ledger", href: "/exchange/ledger", icon: "📒" },
|
||||
{ label: "Guidelines", href: "/exchange/guidelines", icon: "📜" },
|
||||
{ label: "Barter", href: "/barter", icon: "♻️" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Intel & tools",
|
||||
items: [
|
||||
{ label: "Grimoire (vault)", href: "/vault", icon: "🗝️" },
|
||||
{ label: "Forbidden wiki", href: "/hidden-wiki", icon: "📚" },
|
||||
{ label: "Darknet atlas", href: "/darknet-atlas", icon: "🗺️" },
|
||||
{ label: "Shadow syndicate", href: "/syndicate", icon: "🕸️" },
|
||||
{ label: "Routing", href: "/syndicate/routing", icon: "↗" },
|
||||
{ label: "Topology", href: "/syndicate/topology", icon: "⬡" },
|
||||
{ label: "Void crawl (search)", href: "/search", icon: "🔦" },
|
||||
{ label: "Links", href: "/links", icon: "🔗" },
|
||||
{ label: "Presswire", href: "/presswire", icon: "📣" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Extras",
|
||||
items: [
|
||||
{ label: "Launch", href: "/launch", icon: "🔥" },
|
||||
{ label: "Drops", href: "/drops", icon: "🚀" },
|
||||
{ label: "Drop box", href: "/drop-box", icon: "📦" },
|
||||
{ label: "Raffle", href: "/raffle", icon: "🎟️" },
|
||||
{ label: "Awards", href: "/awards", icon: "🏆" },
|
||||
{ label: "Testimonials", href: "/testimonials", icon: "✨" },
|
||||
{ label: "Reviews", href: "/reviews", icon: "⭐" },
|
||||
{ label: "Support", href: "/support", icon: "🆘" },
|
||||
{ label: "Game", href: "/game", icon: "🎮" },
|
||||
{ label: "Mixer", href: "/mixer", icon: "🌀" },
|
||||
{ label: "Webring", href: "/webring", icon: "∞" },
|
||||
{ label: "Trust", href: "/trust", icon: "⚖️" },
|
||||
{ label: "Security analysis", href: "/security-analysis", icon: "🔐" },
|
||||
{ label: "Arb academy", href: "/arb-academy", icon: "📚" },
|
||||
{ label: "Comparison", href: "/comparison", icon: "⚗️" },
|
||||
{ label: "Conspiracies", href: "/conspiracies", icon: "🕯️" },
|
||||
{ label: "Trees", href: "/trees", icon: "🌲" },
|
||||
{ label: "Easter eggs", href: "/easter-eggs", icon: "🥚" },
|
||||
{ label: "Red room", href: "/red-room", icon: "🚪" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function flattenSiteNavItems(): SiteNavItem[] {
|
||||
return SITE_NAV_GROUPS.flatMap((g) => g.items);
|
||||
}
|
||||
Reference in New Issue
Block a user