ProductionGame/backend/main.py

113 lines
3.8 KiB
Python

import json
import sqlite3
from fastapi import FastAPI, HTTPException, status, Depends
from fastapi.middleware.cors import CORSMiddleware
from database import init_db, get_db
from models import RegisterRequest, LoginRequest, AuthResponse, SaveGameRequest
from auth import hash_password, verify_password, create_access_token, get_current_user_id
app = FastAPI(title="El Puro API", version="1.0.0")
# Enable CORS for local and proxy flexibility
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.on_event("startup")
def startup_event():
init_db()
@app.get("/api/health")
def health_check():
return {"status": "ok", "app": "El Puro", "version": "1.0.0"}
@app.post("/api/auth/register", response_model=AuthResponse)
def register(req: RegisterRequest):
# HONEYPOT TRAP: If website_hp is populated, a bot filled it!
if req.website_hp and req.website_hp.strip():
# Reject silently with generic bad request
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Registration verification failed."
)
clean_username = req.username.strip()
if not clean_username:
raise HTTPException(status_code=400, detail="Username cannot be blank.")
hashed = hash_password(req.password)
try:
with get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"INSERT INTO users (username, password_hash) VALUES (?, ?)",
(clean_username, hashed)
)
user_id = cursor.lastrowid
conn.commit()
except sqlite3.IntegrityError:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Username already taken."
)
token = create_access_token({"sub": str(user_id), "username": clean_username})
return AuthResponse(token=token, username=clean_username)
@app.post("/api/auth/login", response_model=AuthResponse)
def login(req: LoginRequest):
clean_username = req.username.strip()
with get_db() as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT id, username, password_hash FROM users WHERE username = ?",
(clean_username,)
)
row = cursor.fetchone()
if not row or not verify_password(req.password, row["password_hash"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid username or password."
)
user_id = row["id"]
token = create_access_token({"sub": str(user_id), "username": clean_username})
return AuthResponse(token=token, username=clean_username)
@app.get("/api/game/state")
def get_game_state(user_id: int = Depends(get_current_user_id)):
with get_db() as conn:
cursor = conn.cursor()
cursor.execute("SELECT save_data, updated_at FROM saves WHERE user_id = ?", (user_id,))
row = cursor.fetchone()
if not row:
return {"has_save": False, "save_data": None}
try:
data = json.loads(row["save_data"])
return {"has_save": True, "save_data": data, "updated_at": row["updated_at"]}
except Exception:
return {"has_save": False, "save_data": None}
@app.post("/api/game/save")
def save_game_state(req: SaveGameRequest, user_id: int = Depends(get_current_user_id)):
json_data = json.dumps(req.save_data)
with get_db() as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO saves (user_id, save_data, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(user_id) DO UPDATE SET
save_data = excluded.save_data,
updated_at = CURRENT_TIMESTAMP
""", (user_id, json_data))
conn.commit()
return {"status": "saved"}