From 7469a61f3eebc7021ad486bf5b7f129a26af476e Mon Sep 17 00:00:00 2001 From: Giorgio Ravera Date: Mon, 5 Jan 2026 20:58:55 +0100 Subject: [PATCH] created host router --- backend/main.py | 78 ++------------------------------ backend/routes/health.py | 5 +++ backend/routes/hosts.py | 96 ++++++++++++++++++++++++++++++++++++++++ backend/routes/login.py | 5 ++- backend/security.py | 18 +++++++- frontend/app.js | 2 +- 6 files changed, 126 insertions(+), 78 deletions(-) create mode 100644 backend/routes/hosts.py diff --git a/backend/main.py b/backend/main.py index 28ce840..4f9f5f8 100644 --- a/backend/main.py +++ b/backend/main.py @@ -2,13 +2,12 @@ # import standard modules from fastapi import FastAPI -from fastapi import Request, Response, HTTPException +from fastapi import Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, RedirectResponse import os -import ipaddress # Import local modules -from backend.security import is_logged_in, require_login +from backend.security import is_logged_in, require_login, html_protected from backend.db.hosts import ( get_hosts, get_host, @@ -18,6 +17,7 @@ from backend.db.hosts import ( ) from backend.routes.health import router as health_router from backend.routes.login import router as login_router +from backend.routes.hosts import router as hosts_router # Import config variables from backend.config import FRONTEND_DIR, HTTP_PORT @@ -25,6 +25,7 @@ from backend.config import FRONTEND_DIR, HTTP_PORT app = FastAPI() app.include_router(health_router) app.include_router(login_router) +app.include_router(hosts_router) # Allow frontend JS to call the API app.add_middleware( @@ -52,78 +53,7 @@ def html_protected(request: Request, filename: str): def home(request: Request): return html_protected(request, "hosts.html") -# Hosts page -@app.get("/hosts") -def hosts(request: Request): - return html_protected(request, "hosts.html") - -# Serve hosts.css -@app.get("/css/hosts.css") -def css_hosts(): - return FileResponse(os.path.join(FRONTEND_DIR, "css/hosts.css")) - # Serve app.js @app.get("/app.js") def js(): return FileResponse(os.path.join(FRONTEND_DIR, "app.js")) - -# --------------------------------------------------------- -# API ENDPOINTS -# --------------------------------------------------------- - -@app.get("/api/hosts") -def api_get_hosts(request: Request): - require_login(request) - return get_hosts() - -@app.post("/api/hosts") -def api_add_host(request: Request, data: dict): - require_login(request) - name = data.get("name", "").strip() - ipv4 = data.get("ipv4") - ipv6 = data.get("ipv6") - if not name: - return {"error": "Name is required"} - if ipv4: - try: - ipaddress.IPv4Address(ipv4) - except: - return {"error": "Invalid IPv4 format"} - if ipv6: - try: - ipaddress.IPv6Address(ipv6) - except: - return {"error": "Invalid IPv6 format"} - return {"id": add_host(data)} - -@app.get("/api/hosts/{host_id}") -def api_get_host(request: Request, host_id: int): - require_login(request) - return get_host(host_id) or {} - -@app.put("/api/hosts/{host_id}") -def api_update_host(request: Request, data: dict, host_id: int): - require_login(request) - name = data.get("name", "").strip() - ipv4 = data.get("ipv4") - ipv6 = data.get("ipv6") - if not name: - return {"error": "Name is required"} - if ipv4: - try: - ipaddress.IPv4Address(ipv4) - except: - return {"error": "Invalid IPv4 format"} - if ipv6: - try: - ipaddress.IPv6Address(ipv6) - except: - return {"error": "Invalid IPv6 format"} - update_host(host_id, data) - return {"status": "ok"} - -@app.delete("/api/hosts/{host_id}") -def api_delete_host(request: Request, host_id: int): - require_login(request) - delete_host(host_id) - return {"status": "ok"} diff --git a/backend/routes/health.py b/backend/routes/health.py index 98af0bc..2a68d12 100644 --- a/backend/routes/health.py +++ b/backend/routes/health.py @@ -2,8 +2,13 @@ from fastapi import APIRouter +# Create Router router = APIRouter() +# --------------------------------------------------------- +# API ENDPOINTS +# --------------------------------------------------------- + @router.get("/health", tags=["health"]) def health_check(): return {"status": "ok"} diff --git a/backend/routes/hosts.py b/backend/routes/hosts.py new file mode 100644 index 0000000..0376d2d --- /dev/null +++ b/backend/routes/hosts.py @@ -0,0 +1,96 @@ +# backend/routes/hosts.py + +# import standard modules +from fastapi import APIRouter, Request, Response +from fastapi.responses import FileResponse, RedirectResponse +import os +import ipaddress +# Import local modules +from backend.security import is_logged_in, require_login, html_protected +from backend.db.hosts import ( + get_hosts, + get_host, + add_host, + update_host, + delete_host +) +# Import config variables +from backend.config import FRONTEND_DIR + +# Create Router +router = APIRouter() + +# --------------------------------------------------------- +# FRONTEND PATHS (absolute paths inside Docker) +# --------------------------------------------------------- + +# Hosts page +@router.get("/hosts") +def hosts(request: Request): + return html_protected(request, "hosts.html") + +# Serve hosts.css +@router.get("/css/hosts.css") +def css_hosts(): + return FileResponse(os.path.join(FRONTEND_DIR, "css/hosts.css")) + +# --------------------------------------------------------- +# API ENDPOINTS +# --------------------------------------------------------- + +@router.get("/api/hosts") +def api_get_hosts(request: Request): + require_login(request) + return get_hosts() + +@router.post("/api/hosts") +def api_add_host(request: Request, data: dict): + require_login(request) + name = data.get("name", "").strip() + ipv4 = data.get("ipv4") + ipv6 = data.get("ipv6") + if not name: + return {"error": "Name is required"} + if ipv4: + try: + ipaddress.IPv4Address(ipv4) + except: + return {"error": "Invalid IPv4 format"} + if ipv6: + try: + ipaddress.IPv6Address(ipv6) + except: + return {"error": "Invalid IPv6 format"} + return {"id": add_host(data)} + +@router.get("/api/hosts/{host_id}") +def api_get_host(request: Request, host_id: int): + require_login(request) + return get_host(host_id) or {} + +@router.put("/api/hosts/{host_id}") +def api_update_host(request: Request, data: dict, host_id: int): + require_login(request) + name = data.get("name", "").strip() + ipv4 = data.get("ipv4") + ipv6 = data.get("ipv6") + if not name: + return {"error": "Name is required"} + if ipv4: + try: + ipaddress.IPv4Address(ipv4) + except: + return {"error": "Invalid IPv4 format"} + if ipv6: + try: + ipaddress.IPv6Address(ipv6) + except: + return {"error": "Invalid IPv6 format"} + update_host(host_id, data) + return {"status": "ok"} + +@router.delete("/api/hosts/{host_id}") +def api_delete_host(request: Request, host_id: int): + require_login(request) + delete_host(host_id) + return {"status": "ok"} diff --git a/backend/routes/login.py b/backend/routes/login.py index 7918ea1..88b358c 100644 --- a/backend/routes/login.py +++ b/backend/routes/login.py @@ -1,7 +1,7 @@ # backend/routes/login.py # import standard modules -from fastapi import APIRouter, Request, Response +from fastapi import APIRouter, Request, Response, HTTPException from fastapi.responses import FileResponse, RedirectResponse import os import time @@ -11,6 +11,7 @@ from backend.db.users import verify_login # Import config variables from backend.config import FRONTEND_DIR, LOGIN_MAX_ATTEMPTS, LOGIN_WINDOW_SECONDS +# Create Router router = APIRouter() # IP -> lista timestamp tentativi @@ -73,7 +74,7 @@ def api_login(request: Request, data: dict, response: Response): ) return {"status": "ok"} - return {"error": "Invalid credentials"} + return {"error": "Wrong credentials"} @router.post("/api/logout") def api_logout(response: Response): diff --git a/backend/security.py b/backend/security.py index 803e2cf..b31f092 100644 --- a/backend/security.py +++ b/backend/security.py @@ -1,13 +1,18 @@ # backend/security.py # import standard modules +import os from fastapi import Request, HTTPException +from fastapi.responses import FileResponse, RedirectResponse from itsdangerous import TimestampSigner # Import config variables -from backend.config import SECRET_KEY +from backend.config import FRONTEND_DIR, SECRET_KEY signer = TimestampSigner(SECRET_KEY) +# ----------------------------- +# login check +# ----------------------------- def is_logged_in(request: Request) -> bool: token = request.cookies.get("session") if not token: @@ -18,6 +23,17 @@ def is_logged_in(request: Request) -> bool: except: return False +# ----------------------------- +# login check (for api) +# ----------------------------- def require_login(request: Request): if not is_logged_in(request): raise HTTPException(status_code=401, detail="Not authenticated") + +# ----------------------------- +# login check (for html) +# ----------------------------- +def html_protected(request: Request, filename: str): + if not is_logged_in(request): + return RedirectResponse("/login") + return FileResponse(os.path.join(FRONTEND_DIR, filename)) \ No newline at end of file diff --git a/frontend/app.js b/frontend/app.js index 5c873e6..f8984fa 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -290,7 +290,7 @@ async function handleLogin(e) { if (data.status === "ok") { window.location.href = "/hosts"; } else { - document.getElementById("loginError").textContent = "Wrong credentials"; + document.getElementById("loginError").textContent = data.error; } } -- 2.47.3