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

170 lines
6.8 KiB
Python
Executable File

from fastapi import FastAPI, HTTPException
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):
if len(v) < 5: raise ValueError('Túl rövid alvázszám')
return v.upper().replace("-", "").replace(" ", "")
@validator('plate')
def clean_plate(cls, v):
return v.upper().replace("-", "").replace(" ", "")
@validator('mileage')
def positive_mileage(cls, v):
if v < 0: raise ValueError('Negatív km nem lehet')
return v
class InviteRequest(BaseModel):
email: str
role: str
access_level: str
# --- VÉGPONTOK ---
@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, vm.model_name
"""))
return [{"id": r.id, "brand": r.brand, "model": r.model_name, "category": r.category} for r in result.fetchall()]
@app.get("/api/my_vehicles")
async def get_my_garage():
user_id = 1
async with AsyncSessionLocal() as session:
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
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} for r in result.fetchall()]
# ÚJ: RÉSZLETES ADATLAP LEKÉRÉSE
@app.get("/api/vehicle/{vehicle_id}")
async def get_vehicle_details(vehicle_id: int):
user_id = 1
async with AsyncSessionLocal() as session:
# 1. Alapadatok
q_basic = 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 -- A user pénzneme
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_basic = await session.execute(q_basic, {"vid": vehicle_id, "uid": user_id})
car = res_basic.fetchone()
if not car:
raise HTTPException(status_code=404, detail="Jármű nem található vagy nincs hozzáférése")
# 2. Utolsó ismert km óraállás (a history-ból vagy költségekből)
# Egyelőre visszaadjuk a start_mileage-t, később itt számolunk
current_km = car.start_mileage
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": current_km,
"currency": car.default_currency
}
@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})
return {"status": "success"}
@app.get("/api/fleet/members")
async def get_team_members():
user_id = 1
async with AsyncSessionLocal() as session:
query = 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 = :uid
""")
result = await session.execute(query, {"uid": user_id})
return [{"email": r.email, "role": r.role, "joined_at": r.joined_at, "country": r.country} for r in result.fetchall()]
@app.post("/api/fleet/invite")
async def invite_member(data: InviteRequest):
user_id = 1
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 (:email, :uid, :role, :lvl, :token, NOW() + INTERVAL '7 days')
"""), {
"email": data.email, "uid": user_id, "role": data.role,
"lvl": data.access_level, "token": token
})
print(f"📧 EMAIL KÜLDÉSE: {data.email} -> Link: /invite/{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"}