diff --git a/docker/router-dash/Dockerfile b/docker/routlin-dash/Dockerfile similarity index 100% rename from docker/router-dash/Dockerfile rename to docker/routlin-dash/Dockerfile diff --git a/docker/router-dash/app/action_add_account.py b/docker/routlin-dash/app/action_add_account.py similarity index 100% rename from docker/router-dash/app/action_add_account.py rename to docker/routlin-dash/app/action_add_account.py diff --git a/docker/router-dash/app/action_apply_banned_ips.py b/docker/routlin-dash/app/action_apply_banned_ips.py similarity index 100% rename from docker/router-dash/app/action_apply_banned_ips.py rename to docker/routlin-dash/app/action_apply_banned_ips.py diff --git a/docker/router-dash/app/action_apply_blocklists.py b/docker/routlin-dash/app/action_apply_blocklists.py similarity index 100% rename from docker/router-dash/app/action_apply_blocklists.py rename to docker/routlin-dash/app/action_apply_blocklists.py diff --git a/docker/router-dash/app/action_apply_ddns_providers.py b/docker/routlin-dash/app/action_apply_ddns_providers.py similarity index 100% rename from docker/router-dash/app/action_apply_ddns_providers.py rename to docker/routlin-dash/app/action_apply_ddns_providers.py diff --git a/docker/router-dash/app/action_apply_dhcp_reservations.py b/docker/routlin-dash/app/action_apply_dhcp_reservations.py similarity index 100% rename from docker/router-dash/app/action_apply_dhcp_reservations.py rename to docker/routlin-dash/app/action_apply_dhcp_reservations.py diff --git a/docker/router-dash/app/action_apply_general.py b/docker/routlin-dash/app/action_apply_general.py similarity index 100% rename from docker/router-dash/app/action_apply_general.py rename to docker/routlin-dash/app/action_apply_general.py diff --git a/docker/router-dash/app/action_apply_host_overrides.py b/docker/routlin-dash/app/action_apply_host_overrides.py similarity index 100% rename from docker/router-dash/app/action_apply_host_overrides.py rename to docker/routlin-dash/app/action_apply_host_overrides.py diff --git a/docker/router-dash/app/action_apply_iface_config.py b/docker/routlin-dash/app/action_apply_iface_config.py similarity index 100% rename from docker/router-dash/app/action_apply_iface_config.py rename to docker/routlin-dash/app/action_apply_iface_config.py diff --git a/docker/router-dash/app/action_apply_inter_vlan.py b/docker/routlin-dash/app/action_apply_inter_vlan.py similarity index 100% rename from docker/router-dash/app/action_apply_inter_vlan.py rename to docker/routlin-dash/app/action_apply_inter_vlan.py diff --git a/docker/router-dash/app/action_apply_interface.py b/docker/routlin-dash/app/action_apply_interface.py similarity index 100% rename from docker/router-dash/app/action_apply_interface.py rename to docker/routlin-dash/app/action_apply_interface.py diff --git a/docker/router-dash/app/action_apply_mdns.py b/docker/routlin-dash/app/action_apply_mdns.py similarity index 100% rename from docker/router-dash/app/action_apply_mdns.py rename to docker/routlin-dash/app/action_apply_mdns.py diff --git a/docker/router-dash/app/action_apply_port_forwarding.py b/docker/routlin-dash/app/action_apply_port_forwarding.py similarity index 100% rename from docker/router-dash/app/action_apply_port_forwarding.py rename to docker/routlin-dash/app/action_apply_port_forwarding.py diff --git a/docker/router-dash/app/action_apply_upstream_dns.py b/docker/routlin-dash/app/action_apply_upstream_dns.py similarity index 100% rename from docker/router-dash/app/action_apply_upstream_dns.py rename to docker/routlin-dash/app/action_apply_upstream_dns.py diff --git a/docker/router-dash/app/action_apply_vlans.py b/docker/routlin-dash/app/action_apply_vlans.py similarity index 100% rename from docker/router-dash/app/action_apply_vlans.py rename to docker/routlin-dash/app/action_apply_vlans.py diff --git a/docker/router-dash/app/action_apply_vpn.py b/docker/routlin-dash/app/action_apply_vpn.py similarity index 98% rename from docker/router-dash/app/action_apply_vpn.py rename to docker/routlin-dash/app/action_apply_vpn.py index 7df9ca2..674e17b 100644 --- a/docker/router-dash/app/action_apply_vpn.py +++ b/docker/routlin-dash/app/action_apply_vpn.py @@ -4,7 +4,7 @@ import re from flask import Blueprint, make_response, redirect, flash, request from auth import require_level -from config_utils import load_core, save_core, verify_core_hash, queued_msg, CONFIGS_DIR +from config_utils import load_core, save_core, verify_core_hash, queued_msg, CONFIGS_DIR, PRODUCT_DISPLAY_NAME import sanitize import validation as validate @@ -103,7 +103,7 @@ def _build_client_conf(vlan, peer_name, peer_ip, private_key, server_pubkey): allowed_ips = f'{subnet}/{prefix}' if split_tunnel else '0.0.0.0/0' lines = [ - '# Generated by router-dash', + f'# Generated by {PRODUCT_DISPLAY_NAME}', '', '[Interface]', f'PrivateKey = {private_key}', @@ -125,7 +125,7 @@ def _conf_response(vlan, peer_name, peer_ip, private_key): iface = _wg_iface(vlan, core) server_pub = _server_pubkey(iface) if not server_pub: - flash('Peer saved. Run sudo python3 ~/router/core.py --apply to generate the server ' + flash('Peer saved. Run sudo python3 ~/routlin/core.py --apply to generate the server ' 'public key, then regenerate this peer to download the client config.', 'warning') return redirect(_VIEW) conf = _build_client_conf(vlan, peer_name, peer_ip, private_key, server_pub) diff --git a/docker/router-dash/app/action_change_password.py b/docker/routlin-dash/app/action_change_password.py similarity index 100% rename from docker/router-dash/app/action_change_password.py rename to docker/routlin-dash/app/action_change_password.py diff --git a/docker/router-dash/app/action_clear_ddns_log.py b/docker/routlin-dash/app/action_clear_ddns_log.py similarity index 100% rename from docker/router-dash/app/action_clear_ddns_log.py rename to docker/routlin-dash/app/action_clear_ddns_log.py diff --git a/docker/router-dash/app/action_create_account.py b/docker/routlin-dash/app/action_create_account.py similarity index 96% rename from docker/router-dash/app/action_create_account.py rename to docker/routlin-dash/app/action_create_account.py index ae5af99..1989eb6 100644 --- a/docker/router-dash/app/action_create_account.py +++ b/docker/routlin-dash/app/action_create_account.py @@ -3,6 +3,7 @@ import json, os, bcrypt, secrets, smtplib from datetime import datetime, timezone, timedelta from email.message import EmailMessage from auth import require_level +from config_utils import PRODUCT_DISPLAY_NAME import sanitize bp = Blueprint('action_create_account', __name__) @@ -31,7 +32,7 @@ def _send_verification_email(to_address, code): raise RuntimeError('SMTP_HOST is not configured.') msg = EmailMessage() - msg['Subject'] = 'Router Dashboard - Email Verification' + msg['Subject'] = f'{PRODUCT_DISPLAY_NAME} - Email Verification' msg['From'] = from_addr msg['To'] = to_address msg.set_content( diff --git a/docker/router-dash/app/action_delete_account.py b/docker/routlin-dash/app/action_delete_account.py similarity index 100% rename from docker/router-dash/app/action_delete_account.py rename to docker/routlin-dash/app/action_delete_account.py diff --git a/docker/router-dash/app/action_log_in.py b/docker/routlin-dash/app/action_log_in.py similarity index 100% rename from docker/router-dash/app/action_log_in.py rename to docker/routlin-dash/app/action_log_in.py diff --git a/docker/router-dash/app/action_log_out.py b/docker/routlin-dash/app/action_log_out.py similarity index 100% rename from docker/router-dash/app/action_log_out.py rename to docker/routlin-dash/app/action_log_out.py diff --git a/docker/router-dash/app/action_save_preferences.py b/docker/routlin-dash/app/action_save_preferences.py similarity index 100% rename from docker/router-dash/app/action_save_preferences.py rename to docker/routlin-dash/app/action_save_preferences.py diff --git a/docker/router-dash/app/action_verify_email.py b/docker/routlin-dash/app/action_verify_email.py similarity index 100% rename from docker/router-dash/app/action_verify_email.py rename to docker/routlin-dash/app/action_verify_email.py diff --git a/docker/router-dash/app/api_apply_status.py b/docker/routlin-dash/app/api_apply_status.py similarity index 100% rename from docker/router-dash/app/api_apply_status.py rename to docker/routlin-dash/app/api_apply_status.py diff --git a/docker/router-dash/app/auth.py b/docker/routlin-dash/app/auth.py similarity index 100% rename from docker/router-dash/app/auth.py rename to docker/routlin-dash/app/auth.py diff --git a/docker/router-dash/app/config_utils.py b/docker/routlin-dash/app/config_utils.py similarity index 97% rename from docker/router-dash/app/config_utils.py rename to docker/routlin-dash/app/config_utils.py index a790864..1395126 100644 --- a/docker/router-dash/app/config_utils.py +++ b/docker/routlin-dash/app/config_utils.py @@ -8,7 +8,8 @@ DASHBOARD_QUEUE = f'{CONFIGS_DIR}/.dashboard-queue' DASHBOARD_DONE = f'{CONFIGS_DIR}/.dashboard-done' DASHBOARD_LAST_RUN = f'{CONFIGS_DIR}/.dashboard-last-run' DASHBOARD_LOCK = f'{CONFIGS_DIR}/.dashboard-lock' -DASHB_TIMER_NAME = 'router-dashboard-queue' +DASHB_TIMER_NAME = 'routlin-dashboard-queue' +PRODUCT_DISPLAY_NAME = os.environ.get('PRODUCT_DISPLAY_NAME', 'Routlin Dashboard') DASHB_INTERVAL_SECS = 60 QUEUE_MAX_LINES = 50 diff --git a/docker/router-dash/app/main.py b/docker/routlin-dash/app/main.py similarity index 100% rename from docker/router-dash/app/main.py rename to docker/routlin-dash/app/main.py diff --git a/docker/router-dash/app/sanitize.py b/docker/routlin-dash/app/sanitize.py similarity index 100% rename from docker/router-dash/app/sanitize.py rename to docker/routlin-dash/app/sanitize.py diff --git a/docker/router-dash/app/view_page.py b/docker/routlin-dash/app/view_page.py similarity index 99% rename from docker/router-dash/app/view_page.py rename to docker/routlin-dash/app/view_page.py index c2c17ce..dd134ce 100644 --- a/docker/router-dash/app/view_page.py +++ b/docker/routlin-dash/app/view_page.py @@ -4,7 +4,7 @@ import json, re, subprocess, os, sys, html as html_mod import sanitize import validation as validate from datetime import datetime, timezone -from config_utils import core_hash, get_pending_entries, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime +from config_utils import core_hash, get_pending_entries, _seconds_until_next_run, _format_timing, _is_locked, _lock_mtime, PRODUCT_DISPLAY_NAME bp = Blueprint('view_page', __name__) @@ -1330,9 +1330,9 @@ def _load_datasource(spec): def render_layout(view_id, content_html, tokens): css = _load_css() level = _client_level() - titlebar_html = '
Router Dashboard
' + titlebar_html = f'
{PRODUCT_DISPLAY_NAME}
' navbar_html = _render_navbar(view_id, level, tokens) - footer_html = '' + footer_html = f'' page_hash = core_hash() lan_iface = e(tokens.get('GENERAL_LAN_INTERFACE', '')) @@ -1367,7 +1367,7 @@ def render_layout(view_id, content_html, tokens): return (f'\n\n\n' f' \n' f' \n' - f' Router Dashboard\n' + f' {PRODUCT_DISPLAY_NAME}\n' f' \n' f'\n\n' f'{titlebar_html}\n' diff --git a/docker/router-dash/data/authorized_accounts.json b/docker/routlin-dash/data/authorized_accounts.json similarity index 100% rename from docker/router-dash/data/authorized_accounts.json rename to docker/routlin-dash/data/authorized_accounts.json diff --git a/docker/router-dash/data/navbar_content.json b/docker/routlin-dash/data/navbar_content.json similarity index 100% rename from docker/router-dash/data/navbar_content.json rename to docker/routlin-dash/data/navbar_content.json diff --git a/docker/router-dash/data/page_content.json b/docker/routlin-dash/data/page_content.json similarity index 99% rename from docker/router-dash/data/page_content.json rename to docker/routlin-dash/data/page_content.json index bc4783b..c5e6ddf 100644 --- a/docker/router-dash/data/page_content.json +++ b/docker/routlin-dash/data/page_content.json @@ -13,7 +13,7 @@ "items": [ { "type": "h1", - "text": "Router Dashboard" + "text": "Routlin Dashboard" }, { "type": "p", diff --git a/docker/router-dash/docker-compose.yml b/docker/routlin-dash/docker-compose.yml similarity index 76% rename from docker/router-dash/docker-compose.yml rename to docker/routlin-dash/docker-compose.yml index abebb05..722abfb 100644 --- a/docker/router-dash/docker-compose.yml +++ b/docker/routlin-dash/docker-compose.yml @@ -1,18 +1,19 @@ -name: router-dash +name: routlin-dash services: flask-app: - container_name: router-dash + container_name: routlin-dash build: . ports: - "25327:25327" volumes: - ./data:/data - - $HOME/router:/configs - - $HOME/router/validation.py:/app/validation.py + - $HOME/routlin:/configs + - $HOME/routlin/validation.py:/app/validation.py - /sys/class/net:/sys/class/net:ro - /sys/devices:/sys/devices:ro environment: + - PRODUCT_DISPLAY_NAME=Routlin Dashboard - INITIAL_MANAGER_EMAIL=mgrotke@gmail.com - SECRET_KEY=ey8hSQCCYE5kQXV8nOg1CB44LSd3AoUet2ZBc3aZlFrwBbazE7aHcxXWyuT97eAObet5jmOL0CjMg0rB1hE4d2SBVYHPfl8De55EiFv307r1QP3Mf5XgOSSCxD3TuD - SMTP_HOST=smtp.gmail.com diff --git a/docker/router-dash/requirements.txt b/docker/routlin-dash/requirements.txt similarity index 100% rename from docker/router-dash/requirements.txt rename to docker/routlin-dash/requirements.txt diff --git a/routlin/DESCRIPTION.txt b/routlin/DESCRIPTION.txt new file mode 100644 index 0000000..5e697ef --- /dev/null +++ b/routlin/DESCRIPTION.txt @@ -0,0 +1 @@ +Turn any Linux machine with 2 NICs into an enterprise-grade router and firewall. Ditch vendor gated appliances and opaque firmware while keeping your machine fully multipurpose and under your control. Easily manage VLANs, NAT, DNS, DHCP, VPNs, RADIUS, mDNS, and content filtering through a modern interface built on battle-tested Linux tools like dnsmasq, nftables, systemd-networkd, FreeRADIUS, and WireGuard. Designed to integrate seamlessly with existing enterprise and prosumer networking hardware. diff --git a/router/README.md b/routlin/README.md similarity index 100% rename from router/README.md rename to routlin/README.md diff --git a/router/core.json b/routlin/core.json similarity index 100% rename from router/core.json rename to routlin/core.json diff --git a/router/core.py b/routlin/core.py similarity index 98% rename from router/core.py rename to routlin/core.py index fe88ac1..3489010 100644 --- a/router/core.py +++ b/routlin/core.py @@ -16,8 +16,8 @@ VLAN's dnsmasq instance loads the merged file for its specific combination, giving true per-VLAN DNS filtering. Blocked domains and all their subdomains return NXDOMAIN via dnsmasq's local=/ syntax. -nftables rules are applied atomically into dedicated tables (router-nat, -router-filter) that do not touch Docker-managed tables. A systemd boot +nftables rules are applied atomically into dedicated tables (routlin-nat, +routlin-filter) that do not touch Docker-managed tables. A systemd boot service (core-nat.service) re-applies the rules on every boot. File layout: @@ -25,17 +25,17 @@ File layout: -- raw downloaded blocklist files merged-.conf -- merged file per unique blocklist combo - /etc/dnsmasq-router/ + /etc/dnsmasq-routlin/ .conf -- per-VLAN dnsmasq config /etc/systemd/system/ - dnsmasq-router-.service -- per-VLAN dnsmasq service unit - dns-blocklists-update.timer -- daily blocklist refresh timer - dns-blocklists-update.service -- timer service unit + dnsmasq-routlin-.service -- per-VLAN dnsmasq service unit + routlin-dns-blocklist-update.timer -- daily blocklist refresh timer + routlin-dns-blocklist-update.service -- timer service unit core-nat.service -- boot service to re-apply nftables rules /var/lib/misc/ - dnsmasq-router-.leases -- per-VLAN DHCP lease files + dnsmasq-routlin-.leases -- per-VLAN DHCP lease files .dns-metrics -- cumulative lifetime DNS metrics @@ -107,19 +107,21 @@ from validation import ( resolve_vlan_derived_fields, validate_config, ) +PRODUCT_NAME = "routlin" + SCRIPT_DIR = Path(__file__).parent CONFIG_FILE = SCRIPT_DIR / "core.json" BLOCKLIST_DIR = SCRIPT_DIR / "blocklists" LOG_FILE = SCRIPT_DIR / "core.log" METRICS_FILE = SCRIPT_DIR / ".dns-metrics" -DNSMASQ_CONF_DIR = Path("/etc/dnsmasq-router") +DNSMASQ_CONF_DIR = Path(f"/etc/dnsmasq-{PRODUCT_NAME}") LEASES_DIR = Path("/var/lib/misc") NETWORKD_DIR = Path("/etc/systemd/network") SYSTEMD_DIR = Path("/etc/systemd/system") -BLIST_TIMER_NAME = "dns-blocklists-update" +BLIST_TIMER_NAME = f"{PRODUCT_NAME}-dns-blocklist-update" BLIST_TIMER_FILE = SYSTEMD_DIR / f"{BLIST_TIMER_NAME}.timer" BLIST_TIMER_SVC_FILE = SYSTEMD_DIR / f"{BLIST_TIMER_NAME}.service" -DASHB_TIMER_NAME = "router-dashboard-queue" +DASHB_TIMER_NAME = f"{PRODUCT_NAME}-dashboard-queue" DASHB_TIMER_FILE = SYSTEMD_DIR / f"{DASHB_TIMER_NAME}.timer" DASHB_TIMER_SVC_FILE = SYSTEMD_DIR / f"{DASHB_TIMER_NAME}.service" DASHB_TIMER_INTERVAL_SEC = 60 @@ -253,12 +255,12 @@ def is_physical(vlan): return vlan["vlan_id"] == 1 def networkd_stem(vlan): - return f"10-router-{vlan['name']}" + return f"10-{PRODUCT_NAME}-{vlan['name']}" def vlan_service_name(vlan): if is_wg(vlan): - return f"dnsmasq-router-{vlan['name']}-{vlan['interface']}" - return f"dnsmasq-router-{vlan['name']}" + return f"dnsmasq-{PRODUCT_NAME}-{vlan['name']}-{vlan['interface']}" + return f"dnsmasq-{PRODUCT_NAME}-{vlan['name']}" def vlan_service_file(vlan): return SYSTEMD_DIR / f"{vlan_service_name(vlan)}.service" @@ -267,10 +269,10 @@ def vlan_conf_file(vlan): return DNSMASQ_CONF_DIR / f"{vlan['name']}.conf" def vlan_leases_file(vlan): - return LEASES_DIR / f"dnsmasq-router-{vlan['name']}.leases" + return LEASES_DIR / f"dnsmasq-{PRODUCT_NAME}-{vlan['name']}.leases" def vlan_pid_file(vlan): - return Path("/run") / f"dnsmasq-router-{vlan['name']}.pid" + return Path("/run") / f"dnsmasq-{PRODUCT_NAME}-{vlan['name']}.pid" # nftables rule list helpers def rule_enabled(rules): @@ -352,7 +354,7 @@ def find_legacy_files(managed_interfaces): to_remove = [] for pattern in ("*.network", "*.netdev"): for f in NETWORKD_DIR.glob(pattern): - if f.name.startswith("10-router-"): + if f.name.startswith(f"10-{PRODUCT_NAME}-"): continue try: content = f.read_text() @@ -1153,12 +1155,12 @@ def apply_dnsmasq_instances(data, dry_run=False, start_if_needed=True): subprocess.run(["systemctl", "daemon-reload"], capture_output=True, text=True) # Remove stale service units (VLANs removed from config) - for f in SYSTEMD_DIR.glob("dnsmasq-router-*.service"): + for f in SYSTEMD_DIR.glob(f"dnsmasq-{PRODUCT_NAME}-*.service"): if f.stem not in active_service_stems: subprocess.run(["systemctl", "disable", "--now", f.stem], capture_output=True, text=True) f.unlink() - n = f.stem.removeprefix("dnsmasq-router-") + n = f.stem.removeprefix(f"dnsmasq-{PRODUCT_NAME}-") stale_conf = DNSMASQ_CONF_DIR / f"{n}.conf" if stale_conf.exists(): stale_conf.unlink() @@ -1530,10 +1532,10 @@ def build_nft_config(data, dry_run=False): line() # ========================================================================== - # router-nat table + # {PRODUCT_NAME}-nat table # ========================================================================== - line("table ip router-nat {") + line(f"table ip {PRODUCT_NAME}-nat {{") line() line(" chain prerouting {") line(" type nat hook prerouting priority dstnat - 10; policy accept;") @@ -1574,10 +1576,10 @@ def build_nft_config(data, dry_run=False): line() # ========================================================================== - # router-filter table + # {PRODUCT_NAME}-filter table # ========================================================================== - line("table ip router-filter {") + line(f"table ip {PRODUCT_NAME}-filter {{") line() if banned_v4: @@ -1732,7 +1734,7 @@ def build_nft_config(data, dry_run=False): if banned_v6: line() - line("table ip6 router-ban {") + line(f"table ip6 {PRODUCT_NAME}-ban {{") line() line(" set banned_ipv6 {") line(" type ipv6_addr") @@ -1767,7 +1769,7 @@ def table_exists(family, name): return result.returncode == 0 def delete_our_tables(): - for family, table in [("ip", "router-nat"), ("ip", "router-filter"), ("ip6", "router-ban")]: + for family, table in [("ip", f"{PRODUCT_NAME}-nat"), ("ip", f"{PRODUCT_NAME}-filter"), ("ip6", f"{PRODUCT_NAME}-ban")]: if table_exists(family, table): result = subprocess.run( ["nft", "delete", "table", family, table], @@ -1872,7 +1874,7 @@ def apply_nftables(data, dry_run=False): print(f" [{r['protocol'].upper():<4}] {src} -> {dst_str} ({r['description']})") def show_rules(): - for table in ("router-nat", "router-filter"): + for table in (f"{PRODUCT_NAME}-nat", f"{PRODUCT_NAME}-filter"): result = subprocess.run( ["nft", "list", "table", "ip", table], capture_output=True, text=True @@ -1890,7 +1892,7 @@ def install_nat_service(): script_path = Path(__file__).resolve() service_content = f"""[Unit] -Description=Apply router NAT and firewall rules +Description=Apply {PRODUCT_NAME} NAT and firewall rules After=network-online.target docker.service Wants=network-online.target docker.service @@ -2599,7 +2601,7 @@ def disable_all(data): def _write_client_network(iface, dhcp, static_cidr=None): """Remove all router networkd files and write a plain client .network file.""" - for pattern in ("10-router-*.network", "10-router-*.netdev"): + for pattern in (f"10-{PRODUCT_NAME}-*.network", f"10-{PRODUCT_NAME}-*.netdev"): for f in NETWORKD_DIR.glob(pattern): f.unlink() print(f"Removed: {f}") @@ -2793,7 +2795,7 @@ def _dry_run_disable(data, iface, use_dhcp, static_cidr, resolv_ok, dns_choice, print("[DRY RUN] Based on your selections, --disable would perform the following:") print() - print("-- Stopping router services (dry-run) --------------------------------") + print(f"-- Stopping {PRODUCT_NAME} services (dry-run) --------------------------------") print(f" Would disable and stop: {BLIST_TIMER_NAME}.timer") for vlan in data["vlans"]: svc = vlan_service_name(vlan) @@ -2805,7 +2807,7 @@ def _dry_run_disable(data, iface, use_dhcp, static_cidr, resolv_ok, dns_choice, if svc_f.exists(): print(f" Would remove: {svc_f}") print(f" Would reload: systemd daemon") - for table in ("router-nat", "router-filter"): + for table in (f"{PRODUCT_NAME}-nat", f"{PRODUCT_NAME}-filter"): r = subprocess.run(["nft", "list", "table", "ip", table], capture_output=True, text=True) if r.returncode == 0: @@ -2833,8 +2835,8 @@ def _dry_run_disable(data, iface, use_dhcp, static_cidr, resolv_ok, dns_choice, print() print("-- Network interface (dry-run) ----------------------------------------") - router_net = list(NETWORKD_DIR.glob("10-router-*.network")) - router_dev = list(NETWORKD_DIR.glob("10-router-*.netdev")) + router_net = list(NETWORKD_DIR.glob(f"10-{PRODUCT_NAME}-*.network")) + router_dev = list(NETWORKD_DIR.glob(f"10-{PRODUCT_NAME}-*.netdev")) client_file = NETWORKD_DIR / f"10-client-{iface}.network" for f in router_net + router_dev: print(f" Would remove: {f}") @@ -3020,7 +3022,7 @@ def cmd_disable(data, dry_run=False): _dry_run_disable(data, iface, use_dhcp, static_cidr, resolv_ok, dns_choice, static_nameserver) return - print("-- Stopping router services ------------------------------------------") + print(f"-- Stopping {PRODUCT_NAME} services ------------------------------------------") disable_all(data) print() diff --git a/router/create_vpn_peer.py b/routlin/create_vpn_peer.py similarity index 100% rename from router/create_vpn_peer.py rename to routlin/create_vpn_peer.py diff --git a/router/ddns.json b/routlin/ddns.json similarity index 100% rename from router/ddns.json rename to routlin/ddns.json diff --git a/router/ddns.py b/routlin/ddns.py similarity index 100% rename from router/ddns.py rename to routlin/ddns.py diff --git a/router/validation.py b/routlin/validation.py similarity index 99% rename from router/validation.py rename to routlin/validation.py index 23d57b6..c30a700 100644 --- a/router/validation.py +++ b/routlin/validation.py @@ -1,8 +1,8 @@ """ validation.py -- Shared structural validators for core.json fields. -Lives alongside core.py in ~/router/ and is volume-mounted into the -router-dash container at /app/validation.py. Importable by both +Lives alongside core.py in ~/routlin/ and is volume-mounted into the +routlin-dash container at /app/validation.py. Importable by both core.py (router host) and the Flask app directly. Convention: primitive validators accept a raw string and return the