Add session-based login page and auth for dashboard

This commit is contained in:
2026-04-13 01:19:56 +00:00
parent 90cf0992b8
commit 73ea08003e
3 changed files with 239 additions and 6 deletions
+69 -6
View File
@@ -1,11 +1,15 @@
from fastapi import FastAPI, Depends, HTTPException
from fastapi import FastAPI, Depends, HTTPException, Request, Response, Cookie
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from fastapi.responses import FileResponse, RedirectResponse, JSONResponse
from sqlalchemy.orm import Session
from pydantic import BaseModel
from datetime import datetime, timezone
from typing import Optional
import hashlib
import hmac
import json
import os
import secrets
from database import get_db, init_db
from models import Agent, Run
@@ -13,6 +17,62 @@ from models import Agent, Run
app = FastAPI(title="Agent Command Center", version="1.0.0")
# --- Auth ---
AUTH_USER = os.environ.get("AUTH_USER", "eric")
AUTH_PASS = os.environ.get("AUTH_PASS", "Kj8#mPx2vQ!nR4wL")
SESSION_SECRET = os.environ.get("SESSION_SECRET", secrets.token_hex(32))
# In-memory session store
_sessions: dict[str, str] = {}
def create_session(username: str) -> str:
token = secrets.token_urlsafe(32)
_sessions[token] = username
return token
def get_current_user(session: Optional[str] = Cookie(None)) -> Optional[str]:
if session and session in _sessions:
return _sessions[session]
return None
def require_auth(session: Optional[str] = Cookie(None)):
user = get_current_user(session)
if not user:
raise HTTPException(status_code=401, detail="Not authenticated")
return user
class LoginRequest(BaseModel):
username: str
password: str
@app.post("/api/login")
def login(creds: LoginRequest, response: Response):
if creds.username == AUTH_USER and creds.password == AUTH_PASS:
token = create_session(creds.username)
response.set_cookie("session", token, httponly=True, samesite="lax", max_age=86400 * 7)
return {"status": "ok", "user": creds.username}
raise HTTPException(status_code=401, detail="Invalid credentials")
@app.post("/api/logout")
def logout(response: Response, session: Optional[str] = Cookie(None)):
if session and session in _sessions:
del _sessions[session]
response.delete_cookie("session")
return {"status": "ok"}
@app.get("/login")
def login_page():
return FileResponse("static/login.html")
# --- Pydantic schemas ---
class AgentCreate(BaseModel):
@@ -49,7 +109,7 @@ def health():
@app.get("/api/agents")
def list_agents(db: Session = Depends(get_db)):
def list_agents(user: str = Depends(require_auth), db: Session = Depends(get_db)):
agents = db.query(Agent).all()
result = []
for a in agents:
@@ -96,7 +156,7 @@ def create_agent(agent: AgentCreate, db: Session = Depends(get_db)):
@app.get("/api/agents/{agent_id}")
def get_agent(agent_id: str, db: Session = Depends(get_db)):
def get_agent(agent_id: str, user: str = Depends(require_auth), db: Session = Depends(get_db)):
agent = db.query(Agent).filter(Agent.id == agent_id).first()
if not agent:
raise HTTPException(status_code=404, detail="Agent not found")
@@ -172,7 +232,7 @@ def update_run(run_id: int, update: RunUpdate, db: Session = Depends(get_db)):
@app.get("/api/runs")
def list_runs(limit: int = 50, db: Session = Depends(get_db)):
def list_runs(limit: int = 50, user: str = Depends(require_auth), db: Session = Depends(get_db)):
runs = db.query(Run).order_by(Run.started_at.desc()).limit(limit).all()
return [{
"id": r.id,
@@ -191,7 +251,10 @@ def list_runs(limit: int = 50, db: Session = Depends(get_db)):
app.mount("/static", StaticFiles(directory="static"), name="static")
@app.get("/")
def root():
def root(session: Optional[str] = Cookie(None)):
user = get_current_user(session)
if not user:
return RedirectResponse("/login", status_code=302)
return FileResponse("static/index.html")