Files
the-other-dude/frontend/src/lib/simpleConfigSchema.ts
Jason Staack b840047e19 feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:30:44 -05:00

336 lines
12 KiB
TypeScript

/**
* Simple Configuration Interface - Declarative Category Schema
*
* Maps 7 simplified configuration categories to RouterOS paths,
* field definitions, and friendly labels. Consumed by all Simple
* mode category panels and the category sidebar.
*/
import type { LucideIcon } from 'lucide-react'
import {
Globe,
Network,
Wifi,
ArrowLeftRight,
Shield,
Server,
Settings,
} from 'lucide-react'
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export interface SimpleCategory {
id: string
label: string
icon: LucideIcon
description: string
sections: SimpleCategorySection[]
}
export interface SimpleCategorySection {
label: string
routerosPath: string
isSingleton: boolean
fields: SimpleFieldDef[]
}
export interface SimpleFieldDef {
key: string
label: string
type: 'text' | 'ip' | 'cidr' | 'number' | 'boolean' | 'select' | 'password'
help?: string
placeholder?: string
required?: boolean
options?: { value: string; label: string }[]
validation?: (value: string) => string | null
minVersion?: number
}
// ---------------------------------------------------------------------------
// Validators
// ---------------------------------------------------------------------------
const IPV4_REGEX = /^(\d{1,3}\.){3}\d{1,3}$/
const IPV6_REGEX = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/
export function isValidIp(value: string): boolean {
if (!value) return false
if (IPV4_REGEX.test(value)) {
return value.split('.').every((octet) => {
const n = parseInt(octet, 10)
return n >= 0 && n <= 255
})
}
return IPV6_REGEX.test(value)
}
export function isValidCidr(value: string): boolean {
if (!value) return false
const parts = value.split('/')
if (parts.length !== 2) return false
const [ip, prefix] = parts
if (!isValidIp(ip)) return false
const prefixNum = parseInt(prefix, 10)
if (isNaN(prefixNum)) return false
// IPv4: 0-32, IPv6: 0-128
const maxPrefix = ip.includes(':') ? 128 : 32
return prefixNum >= 0 && prefixNum <= maxPrefix
}
export function isValidPort(value: string): boolean {
const n = parseInt(value, 10)
return !isNaN(n) && n >= 1 && n <= 65535
}
// ---------------------------------------------------------------------------
// Category Definitions
// ---------------------------------------------------------------------------
export const SIMPLE_CATEGORIES: SimpleCategory[] = [
{
id: 'internet',
label: 'Internet Setup',
icon: Globe,
description: 'Configure how this router connects to the internet',
sections: [
{
label: 'DHCP Client',
routerosPath: '/ip/dhcp-client',
isSingleton: false,
fields: [
{ key: 'interface', label: 'WAN Interface', type: 'select', required: true },
{ key: 'use-peer-dns', label: 'Use ISP DNS', type: 'boolean', help: 'Accept DNS servers from your ISP' },
{ key: 'use-peer-ntp', label: 'Use ISP NTP', type: 'boolean', help: 'Accept time servers from your ISP' },
{ key: 'add-default-route', label: 'Add Default Route', type: 'boolean', help: 'Automatically create a default route via this connection' },
],
},
{
label: 'PPPoE Client',
routerosPath: '/interface/pppoe-client',
isSingleton: false,
fields: [
{ key: 'interface', label: 'Interface', type: 'select', required: true },
{ key: 'user', label: 'PPPoE Username', type: 'text', required: true, placeholder: 'ISP username' },
{ key: 'password', label: 'PPPoE Password', type: 'password', required: true },
{ key: 'service-name', label: 'Service Name', type: 'text', placeholder: 'Optional' },
{ key: 'use-peer-dns', label: 'Use ISP DNS', type: 'boolean' },
],
},
{
label: 'Static IP',
routerosPath: '/ip/address',
isSingleton: false,
fields: [
{ key: 'address', label: 'IP Address / Mask', type: 'cidr', required: true, placeholder: '192.168.1.100/24' },
{ key: 'interface', label: 'WAN Interface', type: 'select', required: true },
],
},
],
},
{
id: 'lan',
label: 'LAN & DHCP',
icon: Network,
description: 'Local network addresses, DHCP server, and IP pools',
sections: [
{
label: 'LAN Address',
routerosPath: '/ip/address',
isSingleton: false,
fields: [
{ key: 'address', label: 'IP Address / Mask', type: 'cidr', required: true, placeholder: '192.168.88.1/24', help: 'The IP address of this router on the local network' },
{ key: 'interface', label: 'Interface', type: 'text' },
],
},
{
label: 'DHCP Server',
routerosPath: '/ip/dhcp-server',
isSingleton: false,
fields: [
{ key: 'disabled', label: 'Enabled', type: 'boolean', help: 'Enable or disable the DHCP server' },
{ key: 'address-pool', label: 'Address Pool', type: 'text' },
],
},
{
label: 'DHCP Network',
routerosPath: '/ip/dhcp-server/network',
isSingleton: false,
fields: [
{ key: 'gateway', label: 'Gateway', type: 'ip', placeholder: '192.168.88.1' },
{ key: 'dns-server', label: 'DNS Servers', type: 'text', placeholder: '192.168.88.1', help: 'DNS servers provided to DHCP clients' },
{ key: 'lease-time', label: 'Lease Time', type: 'text', placeholder: '10m', help: 'How long a DHCP lease is valid' },
],
},
{
label: 'Address Pool',
routerosPath: '/ip/pool',
isSingleton: false,
fields: [
{ key: 'ranges', label: 'IP Range', type: 'text', placeholder: '192.168.88.10-192.168.88.254', help: 'Range of IP addresses for DHCP clients' },
],
},
],
},
{
id: 'wifi',
label: 'WiFi',
icon: Wifi,
description: 'Wireless network names, passwords, and bands',
sections: [
{
label: 'Wireless Interface',
routerosPath: '/interface/wifi',
isSingleton: false,
fields: [
{ key: 'ssid', label: 'Network Name (SSID)', type: 'text', required: true, placeholder: 'MyNetwork' },
{ key: 'security.passphrase', label: 'Password', type: 'password', required: true, help: 'WPA2/WPA3 passphrase (min 8 characters)' },
{ key: 'configuration.band', label: 'Band', type: 'select', options: [
{ value: '2ghz-ax', label: '2.4 GHz' },
{ value: '5ghz-ax', label: '5 GHz' },
] },
{ key: 'disabled', label: 'Enabled', type: 'boolean' },
],
},
],
},
{
id: 'port-forwarding',
label: 'Port Forwarding',
icon: ArrowLeftRight,
description: 'Forward external ports to internal servers',
sections: [
{
label: 'NAT Rules',
routerosPath: '/ip/firewall/nat',
isSingleton: false,
fields: [
{ key: 'dst-port', label: 'External Port', type: 'number', required: true, placeholder: '80', validation: (v) => isValidPort(v) ? null : 'Port must be 1-65535' },
{ key: 'protocol', label: 'Protocol', type: 'select', required: true, options: [
{ value: 'tcp', label: 'TCP' },
{ value: 'udp', label: 'UDP' },
{ value: '6', label: 'TCP + UDP' },
] },
{ key: 'to-addresses', label: 'Internal IP Address', type: 'ip', required: true, placeholder: '192.168.88.100' },
{ key: 'to-ports', label: 'Internal Port', type: 'number', required: true, placeholder: '80', help: 'Leave same as external port if unchanged' },
{ key: 'comment', label: 'Description', type: 'text', placeholder: 'e.g., Web Server' },
],
},
],
},
{
id: 'firewall',
label: 'Firewall',
icon: Shield,
description: 'Basic firewall rules and address lists',
sections: [
{
label: 'Filter Rules',
routerosPath: '/ip/firewall/filter',
isSingleton: false,
fields: [
{ key: 'chain', label: 'Chain', type: 'select', required: true, options: [
{ value: 'input', label: 'Input' },
{ value: 'forward', label: 'Forward' },
] },
{ key: 'action', label: 'Action', type: 'select', required: true, options: [
{ value: 'accept', label: 'Accept' },
{ value: 'drop', label: 'Drop' },
{ value: 'reject', label: 'Reject' },
] },
{ key: 'protocol', label: 'Protocol', type: 'select', options: [
{ value: 'tcp', label: 'TCP' },
{ value: 'udp', label: 'UDP' },
{ value: 'icmp', label: 'ICMP' },
] },
{ key: 'dst-port', label: 'Destination Port', type: 'number', placeholder: '22' },
{ key: 'src-address', label: 'Source Address', type: 'text', placeholder: '192.168.88.0/24', help: 'Leave blank to match any source' },
{ key: 'comment', label: 'Comment', type: 'text', placeholder: 'e.g., Allow SSH from LAN' },
],
},
{
label: 'Address Lists',
routerosPath: '/ip/firewall/address-list',
isSingleton: false,
fields: [
{ key: 'list', label: 'List Name', type: 'text', required: true },
{ key: 'address', label: 'Address', type: 'text', required: true, placeholder: '192.168.88.0/24' },
{ key: 'comment', label: 'Comment', type: 'text' },
],
},
],
},
{
id: 'dns',
label: 'DNS',
icon: Server,
description: 'DNS servers and local name resolution',
sections: [
{
label: 'DNS Settings',
routerosPath: '/ip/dns',
isSingleton: true,
fields: [
{ key: 'servers', label: 'Upstream Servers', type: 'text', required: true, placeholder: '8.8.8.8,8.8.4.4', help: 'Comma-separated list of DNS server IPs used for name resolution' },
{ key: 'allow-remote-requests', label: 'Allow Remote Requests', type: 'boolean', help: 'Allow devices on your network to use this router as their DNS server' },
{ key: 'cache-size', label: 'Cache Size (KiB)', type: 'number', placeholder: '2048', help: 'DNS cache size in KiB' },
],
},
{
label: 'Static Entries',
routerosPath: '/ip/dns/static',
isSingleton: false,
fields: [
{ key: 'name', label: 'Name', type: 'text', required: true, placeholder: 'myserver.local' },
{ key: 'address', label: 'Address', type: 'ip', required: true, placeholder: '192.168.88.100' },
{ key: 'type', label: 'Type', type: 'select', options: [
{ value: 'A', label: 'A' },
{ value: 'AAAA', label: 'AAAA' },
{ value: 'CNAME', label: 'CNAME' },
] },
],
},
],
},
{
id: 'system',
label: 'System',
icon: Settings,
description: 'Device identity, time, passwords, and maintenance',
sections: [
{
label: 'Identity',
routerosPath: '/system/identity',
isSingleton: true,
fields: [
{ key: 'name', label: 'Hostname', type: 'text', required: true, placeholder: 'e.g., Office-Router-1', help: 'A friendly name for this router, visible in the fleet dashboard' },
],
},
{
label: 'Clock',
routerosPath: '/system/clock',
isSingleton: true,
fields: [
{ key: 'time-zone-name', label: 'Timezone', type: 'text', placeholder: 'America/New_York', help: 'IANA timezone identifier (e.g., America/New_York, Europe/London)' },
],
},
{
label: 'NTP Client',
routerosPath: '/system/ntp/client',
isSingleton: true,
fields: [
{ key: 'enabled', label: 'NTP Enabled', type: 'boolean' },
{ key: 'server-dns-names', label: 'NTP Servers', type: 'text', placeholder: 'pool.ntp.org', help: 'Comma-separated NTP server hostnames' },
],
},
{
label: 'System Resource',
routerosPath: '/system/resource',
isSingleton: true,
fields: [],
},
],
},
]