diff --git a/routlin/install.py b/routlin/install.py index 5e9d8cf..93d5bbf 100644 --- a/routlin/install.py +++ b/routlin/install.py @@ -23,9 +23,10 @@ from pathlib import Path AUTO_YES = False SCRIPT_DIR = Path(__file__).parent.resolve() -COMPOSE_FILE = SCRIPT_DIR.parent / "docker" / "routlin-dash" / "docker-compose.yml" -CADDYFILE = Path("/etc/caddy/Caddyfile") -FLASK_PORT = 25327 +COMPOSE_FILE = SCRIPT_DIR.parent / "docker" / "routlin-dash" / "docker-compose.yml" +PORTAL_COMPOSE_FILE = SCRIPT_DIR.parent / "docker" / "routlin-portal" / "docker-compose.yml" +CADDYFILE = Path("/etc/caddy/Caddyfile") +FLASK_PORT = 25327 PRODUCT_NAME = os.environ.get('PRODUCT_NAME', 'routlin') SYSTEMD_DIR = Path("/etc/systemd/system") @@ -38,6 +39,7 @@ DASHB_PENDING_FILE = SCRIPT_DIR / ".dashboard-pending" DASHB_SCRIPT_FILE = SCRIPT_DIR / "do_dashboard_queue.sh" HEALTH_FILE = SCRIPT_DIR / ".health" SNAPSHOTS_DIR = SCRIPT_DIR / ".snapshots" +CAPTIVE_QUEUE_FILE = SCRIPT_DIR / ".captive-queue" # Dashboard systemd timer DASHB_TIMER_NAME = f"{PRODUCT_NAME}-dashboard-queue" @@ -371,12 +373,58 @@ def create_dotfiles(): if not SNAPSHOTS_DIR.exists(): SNAPSHOTS_DIR.mkdir() os.chown(SNAPSHOTS_DIR, uid, gid) - for f in (DASHB_QUEUE_FILE, DASHB_DONE_FILE, DASHB_LAST_RUN_FILE, DASHB_LOCK_FILE, DASHB_PENDING_FILE, HEALTH_FILE): + for f in (DASHB_QUEUE_FILE, DASHB_DONE_FILE, DASHB_LAST_RUN_FILE, DASHB_LOCK_FILE, DASHB_PENDING_FILE, HEALTH_FILE, CAPTIVE_QUEUE_FILE): if not f.exists(): f.touch() os.chown(f, uid, gid) +# =================================================================== +# Captive portal container +# =================================================================== + +def _captive_portal_enabled(): + cfg_path = SCRIPT_DIR / "config.json" + try: + import json as _json + cfg = _json.loads(cfg_path.read_text()) + return any(v.get('restricted_vlan') == 'c' for v in cfg.get('vlans', [])) + except Exception: + return False + + +def setup_portal_container(): + if not PORTAL_COMPOSE_FILE.exists(): + print(f" routlin-portal docker-compose.yml not found at {PORTAL_COMPOSE_FILE}, skipping.") + return + + env = _compose_env() + + if not _captive_portal_enabled(): + print(" No captive portal VLANs configured - stopping portal container if running.") + subprocess.run(["docker", "compose", "down"], cwd=PORTAL_COMPOSE_FILE.parent, + env=env, check=False) + return + + print("\n Building portal image...") + result = subprocess.run( + ["docker", "compose", "build"], + cwd=PORTAL_COMPOSE_FILE.parent, env=env, check=False + ) + if result.returncode != 0: + die("docker compose build failed for routlin-portal. Check the output above.") + + print("\n Starting portal container...") + result = subprocess.run( + ["docker", "compose", "up", "-d"], + cwd=PORTAL_COMPOSE_FILE.parent, env=env, check=False + ) + if result.returncode != 0: + die("docker compose up failed for routlin-portal. Check the output above.") + + print(" Portal container started.") + + # =================================================================== # Dashboard systemd timer # =================================================================== @@ -645,6 +693,10 @@ def main(): setup_docker_compose(reuse_config=reuse_config) create_dotfiles() + # Captive portal container ========================================== + header("Captive Portal Container") + setup_portal_container() + # Dashboard timer =================================================== header("Dashboard Timer") install_dashboard_timer()