diff --git a/credentials.json b/credentials.json new file mode 100644 index 0000000..3fe3de0 --- /dev/null +++ b/credentials.json @@ -0,0 +1,4 @@ +{ + "username": "nehemis", + "password": "password" +} diff --git a/main.py b/main.py index 333072a..d1027c7 100644 --- a/main.py +++ b/main.py @@ -1,46 +1,99 @@ -from fastapi import FastAPI, HTTPException, Query +from fastapi import FastAPI, HTTPException, Query, Form, Depends from fastapi.staticfiles import StaticFiles -from fastapi.responses import HTMLResponse +from fastapi.responses import HTMLResponse, RedirectResponse import logging import os from fastapi import Request, APIRouter -import subprocess import platform +from fastapi.templating import Jinja2Templates # pip install fastapi uvicorn jinja2 python-multipart passlib +from starlette.middleware.sessions import SessionMiddleware +from starlette.exceptions import HTTPException as StarletteHTTPException +from starlette.status import HTTP_302_FOUND +import json +from pathlib import Path + if platform.system() in ['Darwin']: # macOS or Windows from MockCAN import CANBackend else : from classCAN import CANBackend # Your real backend - app = FastAPI() +app.add_middleware(SessionMiddleware, secret_key="your_super_secret_key") router = APIRouter() - +templates = Jinja2Templates(directory="templates") logging.basicConfig(level=logging.INFO) +can_backend = CANBackend() + +# Mount static files +app.mount("/static", StaticFiles(directory="static"), name="static") + + can_backend = CANBackend() # Serve static files (HTML, JS, CSS) app.mount("/static", StaticFiles(directory="static"), name="static") +# Jinja2 templates +templates = Jinja2Templates(directory="templates") -# @router.post("/webhook") -# async def github_webhook(request: Request): -# payload = await request.json() -# print("[WEBHOOK] Received webhook:", payload.get("head_commit", {}).get("message")) +# CREDENTIALS -# try: -# # Call the update script on the HOST using host bash -# subprocess.run( -# ["/usr/bin/bash", "-c", "bash /home/hmi/Desktop/HMI/update_hmi.sh"], -# check=True, -# capture_output=True, -# text=True -# ) -# return {"status": "Update triggered"} -# except subprocess.CalledProcessError as e: -# print(f"[WEBHOOK] Update failed:\n{e.stderr}") -# return {"status": "Update failed", "error": str(e)} +# Load users from JSON file at startup +CREDENTIAL_PATH = Path("credentials.json") +if CREDENTIAL_PATH.exists(): + with CREDENTIAL_PATH.open("r") as f: + CREDENTIALS = json.load(f) +else: + CREDENTIALS = {} + +USERNAME = CREDENTIALS["username"] +PASSWORD = CREDENTIALS["password"] + + +# ======== LOGIN & SESSION HANDLING ======== + +def require_login(request: Request): + user = request.session.get("user") + if user != USERNAME: + # raise 302 to trigger redirection manually (FastAPI doesn't support redirects from Depends directly) + raise StarletteHTTPException(status_code=302, detail="Redirect to login") + return user +@app.get("/", response_class=HTMLResponse) +def login_form(request: Request): + return templates.TemplateResponse("login.html", {"request": request}) + + +@app.post("/login") +def login(request: Request, username: str = Form(...), password: str = Form(...)): + if username == USERNAME and password == PASSWORD: + request.session["user"] = username + return RedirectResponse("/control", status_code=HTTP_302_FOUND) + return templates.TemplateResponse("login.html", {"request": request, "error": "Invalid credentials.json"}) + + +@app.get("/logout") +def logout(request: Request): + request.session.clear() + return RedirectResponse("/", status_code=HTTP_302_FOUND) + + +# ======== PROTECTED INTERFACE ======== + +@app.get("/control", response_class=HTMLResponse) +def control_page(request: Request): + if request.session.get("user") != USERNAME: + return RedirectResponse("/", status_code=HTTP_302_FOUND) + return templates.TemplateResponse("control.html", {"request": request}) + +@app.get("/monitor-page", response_class=HTMLResponse) +def monitor_page(request: Request): + if request.session.get("user") != USERNAME: + return RedirectResponse("/", status_code=HTTP_302_FOUND) + with open("static/monitor.html") as f: + return HTMLResponse(f.read()) + +# ======== CAN + BACKEND ROUTES ======== - @app.post("/connect_toggle") def connect_toggle(): """Toggle CAN connection.""" @@ -147,19 +200,6 @@ def can_status(): """Return current CAN connection status.""" return {"connected": can_backend.connected} -@app.get("/", response_class=HTMLResponse) -async def read_root(): - """Serve main HTML page.""" - with open("static/index.html", "r") as file: - return HTMLResponse(content=file.read(), status_code=200) - - -@app.get("/monitor-page", response_class=HTMLResponse) -async def read_monitor_page(): - """Serve monitor HTML page.""" - with open("static/monitor.html", "r") as file: - return HTMLResponse(content=file.read(), status_code=200) - app.include_router(router) diff --git a/static/index.html b/templates/control.html similarity index 100% rename from static/index.html rename to templates/control.html diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..d8f2a63 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,23 @@ + + +
+