import type { LucideIcon } from 'lucide-react'; import { Globe, Cable, Radio, FileX2, Users, ShieldCheck, Plug, Network, KeyRound } from 'lucide-react'; /** * The sections. * * ── NPM's words, not NPM's order ── * * The VOCABULARY is deliberately NPM's. Somebody who has used it should find the same words, and somebody * who has not will end up in NPM's UI eventually anyway — inventing better names only means learning both. * `404 Hosts` reads oddly and is theirs; the hint says what it means, because the label cannot. * * The ORDER is ours, and this file said the opposite until 2026-08-25. NPM's nav order is an accident of * how NPM grew; ours is a claim about what people come here for. Proxy hosts, then certificates, then * streams — that is most of every visit. The rest are real and rarely the reason you opened this. * * **Being able to make that claim is most of why this is a plugin** rather than a link to NPM. A wrapper * that reproduces the original's ordering has given up the one thing it was for. * * ── Two groups ── * * Everything above `domains` is a thing NPM owns and this reads. From `domains` down is OFFICER's own — * what it knows about your domains, the DNS credentials it holds, and where NPM is. Those are not NPM * concepts and should not pretend to be. */ export type SectionKey = | 'proxy-hosts' | 'certificates' | 'streams' | 'redirections' | 'dead-hosts' | 'access-lists' | 'domains' | 'dns-providers' | 'connection'; export type Section = { key: SectionKey; label: string; icon: LucideIcon; /** One line under the label in the nav. What this is, for somebody who does not already know. */ hint: string; /** The plugin route its count comes from, or null when there is nothing to count. */ path: string | null; /** * Agreed and not built. * * Listed rather than hidden on purpose: these two are the shape the plugin is going towards, and a nav * that shows them says what this is FOR to somebody who opens it — including the owner, six months from * now, wondering what was decided. The pane says plainly that it does not exist, which is better than a * feature that looks half-present. */ planned?: boolean; }; export const SECTIONS: Section[] = [ { key: 'proxy-hosts', label: 'Proxy Hosts', icon: Globe, hint: 'A domain, forwarded to something running here', path: '/proxy-manager/hosts', }, { key: 'certificates', label: 'Certificates', icon: ShieldCheck, hint: 'TLS, and how long each one has left', path: '/proxy-manager/certificates', }, { key: 'streams', label: 'Streams', icon: Radio, hint: 'A raw TCP or UDP port, forwarded', path: '/proxy-manager/streams', }, { key: 'redirections', label: 'Redirection Hosts', icon: Cable, hint: 'A domain that sends visitors somewhere else', path: '/proxy-manager/redirections', }, { key: 'dead-hosts', label: '404 Hosts', icon: FileX2, hint: 'A domain served deliberately as nothing', path: '/proxy-manager/dead-hosts', }, { key: 'access-lists', label: 'Access Lists', icon: Users, hint: 'Who is allowed to reach a host', path: '/proxy-manager/access-lists', }, // ── Officer's own, below here ── { key: 'domains', label: 'Domains', icon: Network, hint: 'The domains you manage, and who registers each', path: null, planned: true, }, { key: 'dns-providers', label: 'DNS Providers', icon: KeyRound, hint: 'API keys, so a private hostname can still get TLS', path: null, planned: true, }, { key: 'connection', label: 'Connection', icon: Plug, hint: 'Where NPM is, and the credential — owner only', path: null, }, ]; export const DEFAULT_SECTION: SectionKey = 'proxy-hosts'; export const isSectionKey = (value: string | null): value is SectionKey => !!value && SECTIONS.some((s) => s.key === value);