Add session-based login page and auth for dashboard
This commit is contained in:
+69
-6
@@ -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")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user