Feat: adds a login page
This commit is contained in:
parent
f84095fc20
commit
4d4d0f986f
4
credentials.json
Normal file
4
credentials.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"username": "nehemis",
|
||||
"password": "password"
|
||||
}
|
||||
110
main.py
110
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)
|
||||
|
||||
|
|
|
|||
23
templates/login.html
Normal file
23
templates/login.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Login</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; background-color: #121212; color: white; display: flex; justify-content: center; align-items: center; height: 100vh; }
|
||||
form { background: #1e1e1e; padding: 30px; border-radius: 8px; box-shadow: 0 0 10px black; }
|
||||
input { display: block; margin: 10px 0; padding: 10px; width: 100%; }
|
||||
button { padding: 10px; width: 100%; background: #4285F4; color: white; border: none; border-radius: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post" action="/login">
|
||||
<h2>Login</h2>
|
||||
{% if error %}
|
||||
<p style="color: red;">{{ error }}</p>
|
||||
{% endif %}
|
||||
<input type="text" name="username" placeholder="Username" required />
|
||||
<input type="password" name="password" placeholder="Password" required />
|
||||
<button type="submit">Log In</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user