Files
service-finder/code-server-config/data/User/History/-3487e1e/0MBT.py

180 lines
7.9 KiB
Python
Executable File

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import FileResponse
from pydantic import BaseModel, validator
from typing import Optional, List
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy import text
import os
from datetime import date
import uuid
from dotenv import load_dotenv
load_dotenv()
# DB Config
raw_url = os.getenv("DATABASE_URL")
if not raw_url:
raw_url = "postgresql://admin:PASSWORD_111@postgres-db:5432/service_finder"
fixed_url = raw_url.replace("postgresql://", "postgresql+asyncpg://").replace("/service_finder_db", "/service_finder")
engine = create_async_engine(fixed_url)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
app = FastAPI(title="Service Finder API")
# --- MODELLEK ---
class VehicleRegister(BaseModel):
model_id: int
vin: str
plate: str
mileage: int
purchase_date: date
role: str = "OWNER"
@validator('vin')
def clean_vin(cls, v): return v.upper().replace("-", "").replace(" ", "")
@validator('plate')
def clean_plate(cls, v): return v.upper().replace("-", "").replace(" ", "")
@validator('mileage')
def positive_mileage(cls, v): return v if v >= 0 else 0
class InviteRequest(BaseModel):
email: str
role: str
access_level: str
class IssueReport(BaseModel):
vehicle_id: int
description: str
is_critical: bool
# --- SEGÉDFÜGGVÉNY: AUDIT LOG ÍRÁSA ---
async def create_audit_log(session, user_id, event, target_id, details, old_val=None, new_val=None):
await session.execute(text("""
INSERT INTO data.audit_logs (user_id, event_type, target_id, details, old_value, new_value)
VALUES (:uid, :evt, :tid, :det, :old, :new)
"""), {
"uid": user_id, "evt": event, "tid": target_id,
"det": details, "old": old_val, "new": new_val
})
# --- VÉGPONTOK ---
@app.get("/api/my_vehicles")
async def get_my_garage():
user_id = 1
async with AsyncSessionLocal() as session:
# Most már lekérjük a státuszt is!
query = text("""
SELECT v.id as vehicle_id, v.vin, v.current_plate, m.name as brand,
mo.model_name, mo.category, vh.start_mileage, vh.role,
v.status, v.current_issue
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 AND vh.end_date IS NULL
ORDER BY vh.start_date DESC
""")
result = await session.execute(query, {"uid": user_id})
return [{
"vehicle_id": r.vehicle_id, "brand": r.brand, "model": r.model_name,
"plate": r.current_plate, "category": r.category, "role": r.role,
"status": r.status, "current_issue": r.current_issue # <--- ÚJ MEZŐK
} for r in result.fetchall()]
@app.get("/api/vehicle/{vehicle_id}")
async def get_vehicle_details(vehicle_id: int):
user_id = 1
async with AsyncSessionLocal() as session:
q = text("""
SELECT v.id, v.vin, v.current_plate, v.production_year,
m.name as brand, mo.model_name, mo.category,
vh.role, vh.start_date, vh.start_mileage, u.default_currency,
v.status, v.current_issue -- <--- ÚJ
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
JOIN data.users u ON vh.user_id = u.id
WHERE v.id = :vid AND vh.user_id = :uid AND vh.end_date IS NULL
""")
res = await session.execute(q, {"vid": vehicle_id, "uid": user_id})
car = res.fetchone()
if not car: raise HTTPException(status_code=404, detail="Nincs adat")
return {
"id": car.id, "brand": car.brand, "model": car.model_name,
"plate": car.current_plate, "vin": car.vin, "role": car.role,
"start_date": car.start_date, "mileage": car.start_mileage,
"currency": car.default_currency,
"status": car.status, "current_issue": car.current_issue
}
# --- ÚJ: HIBA BEJELENTÉS (LOGOLÁSSAL) ---
@app.post("/api/report_issue")
async def report_issue(data: IssueReport):
user_id = 1 # Demo User
async with AsyncSessionLocal() as session:
async with session.begin():
# 1. Frissítjük a járművet
new_status = 'CRITICAL' if data.is_critical else 'WARNING'
await session.execute(text("""
UPDATE data.vehicles
SET status = :st, current_issue = :desc
WHERE id = :vid
"""), {"st": new_status, "desc": data.description, "vid": data.vehicle_id})
# 2. ÍRUNK A NAPLÓBA (AUDIT LOG)
await create_audit_log(
session, user_id, "ISSUE_REPORT", data.vehicle_id,
details=f"Hiba: {data.description}",
old_val="OK", new_val=new_status
)
return {"status": "success", "message": "Hiba naplózva és rögzítve."}
# --- MARADÉK (REGISZTER, FLOTTA, MEGHÍVÁS) ---
# (Ezeket most rövidítve hagyom, de a fájlban benne kell lenniük a korábbi verzió szerint)
@app.get("/api/vehicles")
async def get_vehicles():
async with AsyncSessionLocal() as session:
result = await session.execute(text("SELECT vm.id, m.name as brand, vm.model_name, vm.category FROM ref.vehicle_models vm JOIN ref.vehicle_makes m ON vm.make_id = m.id ORDER BY m.name"))
return [{"id": r.id, "brand": r.brand, "model": r.model_name, "category": r.category} for r in result.fetchall()]
@app.post("/api/register")
async def register_vehicle(data: VehicleRegister):
async with AsyncSessionLocal() as session:
async with session.begin():
res = await session.execute(text("SELECT id FROM data.vehicles WHERE vin = :vin"), {"vin": data.vin})
vid = res.scalar()
if not vid:
ins = text("INSERT INTO data.vehicles (model_id, vin, current_plate) VALUES (:mid, :vin, :plt) RETURNING id")
r = await session.execute(ins, {"mid": data.model_id, "vin": data.vin, "plt": data.plate})
vid = r.scalar()
hist = text("INSERT INTO data.vehicle_history (vehicle_id, user_id, role, start_date, start_mileage) VALUES (:vid, 1, :role, :sd, :sm)")
await session.execute(hist, {"vid": vid, "role": data.role, "sd": data.purchase_date, "sm": data.mileage})
# LOGOLÁS ITT IS!
await create_audit_log(session, 1, "REGISTER_VEHICLE", vid, "Jármű regisztrálva")
return {"status": "success"}
@app.get("/api/fleet/members")
async def get_team_members():
async with AsyncSessionLocal() as session:
res = await session.execute(text("SELECT u.email, fm.role, fm.joined_at, u.country FROM data.fleet_members fm JOIN data.users u ON fm.user_id = u.id WHERE fm.owner_id = 1"))
return [{"email": r.email, "role": r.role, "joined_at": r.joined_at, "country": r.country} for r in res.fetchall()]
@app.post("/api/fleet/invite")
async def invite_member(data: InviteRequest):
token = str(uuid.uuid4())
async with AsyncSessionLocal() as session:
async with session.begin():
await session.execute(text("INSERT INTO data.invitations (email, inviter_id, role, access_level, token, expires_at) VALUES (:e, 1, :r, :l, :t, NOW() + INTERVAL '7 days')"), {"e": data.email, "r": data.role, "l": data.access_level, "t": token})
return {"status": "success", "message": "Meghívó elküldve!", "debug_token": token}
@app.get("/")
async def serve_frontend():
if os.path.exists("/app/frontend/index.html"):
return FileResponse("/app/frontend/index.html")
return {"error": "Frontend hiányzik"}