76 lines
3.6 KiB
Python
Executable File
76 lines
3.6 KiB
Python
Executable File
from fastapi import FastAPI, HTTPException, UploadFile, File, Form, Depends
|
|
from fastapi.responses import FileResponse, JSONResponse
|
|
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
|
from sqlalchemy.orm import sessionmaker
|
|
from sqlalchemy import text
|
|
from datetime import datetime, timedelta
|
|
from jose import JWTError, jwt
|
|
from passlib.context import CryptContext
|
|
import os, uuid, shutil
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
SECRET_KEY = "SZUPER_TITKOS_KULCS_2026"
|
|
ALGORITHM = "HS256"
|
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login")
|
|
|
|
DATABASE_URL = os.getenv("DATABASE_URL", "").replace("postgresql://", "postgresql+asyncpg://")
|
|
engine = create_async_engine(DATABASE_URL, pool_pre_ping=True)
|
|
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
app = FastAPI()
|
|
|
|
def get_password_hash(password): return pwd_context.hash(password)
|
|
def verify_password(plain, hashed): return pwd_context.verify(plain, hashed)
|
|
|
|
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
|
try:
|
|
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
|
user_id: str = payload.get("sub")
|
|
if user_id is None: raise HTTPException(status_code=401)
|
|
return int(user_id)
|
|
except Exception: raise HTTPException(status_code=401)
|
|
|
|
@app.post("/api/auth/register")
|
|
async def register(email: str = Form(...), password: str = Form(...)):
|
|
async with AsyncSessionLocal() as session:
|
|
async with session.begin():
|
|
hashed = get_password_hash(password)
|
|
await session.execute(text("INSERT INTO data.users (email, password_hash) VALUES (:e, :p)"), {"e": email, "p": hashed})
|
|
return {"status": "success"}
|
|
|
|
@app.post("/api/auth/login")
|
|
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
async with AsyncSessionLocal() as session:
|
|
res = await session.execute(text("SELECT id, password_hash FROM data.users WHERE email = :e"), {"e": form_data.username})
|
|
user = res.fetchone()
|
|
if not user or not verify_password(form_data.password, user.password_hash):
|
|
raise HTTPException(status_code=401, detail="Hibás adatok")
|
|
token = jwt.encode({"sub": str(user.id), "exp": datetime.utcnow() + timedelta(days=1)}, SECRET_KEY, algorithm=ALGORITHM)
|
|
return {"access_token": token, "token_type": "bearer"}
|
|
|
|
@app.get("/api/my_vehicles")
|
|
async def my_vehicles(user_id: int = Depends(get_current_user)):
|
|
async with AsyncSessionLocal() as session:
|
|
q = text("SELECT v.id as vehicle_id, v.current_plate as plate, m.name as brand, mo.model_name as model, v.status FROM data.vehicle_history vh JOIN data.vehicles v ON vh.vehicle_id = v.id JOIN ref.vehicle_models mo ON v.model_id = mo.id JOIN ref.vehicle_makes m ON mo.make_id = m.id WHERE vh.user_id = :uid")
|
|
res = await session.execute(q, {"uid": user_id})
|
|
return [dict(r._mapping) for r in res.fetchall()]
|
|
|
|
@app.get("/api/ref/cost_types")
|
|
async def cost_types():
|
|
async with AsyncSessionLocal() as session:
|
|
res = await session.execute(text("SELECT code, name, parent_code FROM ref.cost_types"))
|
|
rows = res.fetchall()
|
|
tree = {}
|
|
for r in rows:
|
|
if not r.parent_code: tree[r.code] = {"label": r.name, "subs": {}}
|
|
for r in rows:
|
|
if r.parent_code and r.parent_code in tree: tree[r.parent_code]["subs"][r.code] = r.name
|
|
return tree
|
|
|
|
@app.get("/")
|
|
async def index(): return FileResponse("/app/frontend/index.html") |