feat: Asset Catalog system, PostGIS integration and RobotScout V1
This commit is contained in:
12
backend/.env
12
backend/.env
@@ -4,10 +4,20 @@ DATABASE_URL=postgresql+asyncpg://service_finder_app:JELSZAVAD@db:5432/service_f
|
||||
|
||||
# Security
|
||||
SECRET_KEY=ide_generálj_egy_hosszú_véletlen_karaktersort
|
||||
ALGORITHM=HS256
|
||||
|
||||
# Initial Admin (Ezt fogja a seed script használni)
|
||||
INITIAL_ADMIN_EMAIL=kincses@valami.hu
|
||||
INITIAL_ADMIN_PASSWORD=Kincs€s74
|
||||
|
||||
# Debug mód (opcionális)
|
||||
DEBUG=True
|
||||
DEBUG=True
|
||||
|
||||
# --- Google OAuth ---
|
||||
GOOGLE_CLIENT_ID=575071309971-8icc0o61hiat9sioeuqin8k4tnvnssmd.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=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
|
||||
GOOGLE_CALLBACK_URL=https://dev.profibot.hu/api/v1/auth/callback/google
|
||||
|
||||
|
||||
# --- Frontend ---
|
||||
FRONTEND_BASE_URL=https://dev.profibot.hu/docs
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional, Dict, Any, Union
|
||||
import logging
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
@@ -6,26 +6,37 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.db.session import get_db
|
||||
from app.core.security import decode_token, RANK_MAP
|
||||
from app.models.identity import User
|
||||
from app.core.security import decode_token, DEFAULT_RANK_MAP
|
||||
from app.models.identity import User, UserRole
|
||||
from app.core.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
reusable_oauth2 = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
|
||||
|
||||
# Az OAuth2 folyamat a bejelentkezési végponton keresztül
|
||||
reusable_oauth2 = OAuth2PasswordBearer(
|
||||
tokenUrl=f"{settings.API_V1_STR}/auth/login"
|
||||
)
|
||||
|
||||
async def get_current_token_payload(
|
||||
token: str = Depends(reusable_oauth2)
|
||||
) -> Dict[str, Any]:
|
||||
if token == "dev_bypass_active":
|
||||
return {
|
||||
"""
|
||||
JWT token visszafejtése és a típus (access) ellenőrzése.
|
||||
"""
|
||||
# Dev bypass (ha esetleg fejlesztéshez használtad korábban, itt a helye,
|
||||
# de élesben a token validáció fut le)
|
||||
if settings.DEBUG and token == "dev_bypass_active":
|
||||
return {
|
||||
"sub": "1",
|
||||
"role": "superadmin",
|
||||
"rank": 100,
|
||||
"scope_level": "global",
|
||||
"scope_id": "all"
|
||||
"scope_id": "all",
|
||||
"type": "access"
|
||||
}
|
||||
|
||||
payload = decode_token(token)
|
||||
if not payload:
|
||||
if not payload or payload.get("type") != "access":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Érvénytelen vagy lejárt munkamenet."
|
||||
@@ -33,39 +44,93 @@ async def get_current_token_payload(
|
||||
return payload
|
||||
|
||||
async def get_current_user(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
payload: Dict[str, Any] = Depends(get_current_token_payload),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
payload: Dict = Depends(get_current_token_payload)
|
||||
) -> User:
|
||||
"""
|
||||
Lekéri a felhasználót a token 'sub' mezője alapján.
|
||||
"""
|
||||
user_id = payload.get("sub")
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token azonosítási hiba.")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Token azonosítási hiba."
|
||||
)
|
||||
|
||||
result = await db.execute(select(User).where(User.id == int(user_id)))
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user or user.is_deleted:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="A felhasználó nem található.")
|
||||
|
||||
if not user or user.is_deleted:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="A felhasználó nem található."
|
||||
)
|
||||
return user
|
||||
|
||||
async def get_current_active_user(
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> User:
|
||||
"""Ellenőrzi, hogy a felhasználó aktív-e."""
|
||||
"""
|
||||
Ellenőrzi, hogy a felhasználó aktív-e.
|
||||
Ez elengedhetetlen az Admin felület és a védett végpontok számára.
|
||||
"""
|
||||
if not current_user.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="A felhasználói fiók zárolva van vagy inaktív."
|
||||
detail="A művelethez aktív profil és KYC azonosítás (Step 2) szükséges."
|
||||
)
|
||||
return current_user
|
||||
|
||||
def check_min_rank(required_rank: int):
|
||||
def rank_checker(payload: Dict[str, Any] = Depends(get_current_token_payload)):
|
||||
async def check_resource_access(
|
||||
resource_scope_id: Union[str, int],
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Scoped RBAC: Megakadályozza, hogy egy felhasználó más valaki erőforrásaihoz nyúljon.
|
||||
Kezeli az ID-t (int) és a Scope ID-t / Slug-ot (str) is.
|
||||
"""
|
||||
if current_user.role == UserRole.superadmin:
|
||||
return True
|
||||
|
||||
# Ha a usernek van beállított scope_id-ja (pl. egy flottához tartozik),
|
||||
# akkor ellenőrizzük, hogy a kért erőforrás abba a scope-ba tartozik-e.
|
||||
user_scope = current_user.scope_id
|
||||
requested_scope = str(resource_scope_id)
|
||||
|
||||
# 1. Saját erőforrás (saját ID)
|
||||
if str(current_user.id) == requested_scope:
|
||||
return True
|
||||
|
||||
# 2. Scope alapú hozzáférés (pl. flotta tagja)
|
||||
if user_scope and user_scope == requested_scope:
|
||||
return True
|
||||
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Nincs jogosultsága ehhez az erőforráshoz."
|
||||
)
|
||||
|
||||
def check_min_rank(role_key: str):
|
||||
"""
|
||||
Dinamikus Rank ellenőrzés.
|
||||
Az adatbázisból (system_parameters) kéri le az elvárt szintet.
|
||||
"""
|
||||
async def rank_checker(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
payload: Dict = Depends(get_current_token_payload)
|
||||
):
|
||||
# A settings.get_db_setting-et használjuk a dinamikus lekéréshez
|
||||
ranks = await settings.get_db_setting(
|
||||
db, "rbac_rank_matrix", default=DEFAULT_RANK_MAP
|
||||
)
|
||||
|
||||
required_rank = ranks.get(role_key, 0)
|
||||
user_rank = payload.get("rank", 0)
|
||||
|
||||
if user_rank < required_rank:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"Nincs elegendő jogosultsága a művelethez. (Szükséges szint: {required_rank})"
|
||||
detail=f"Alacsony jogosultsági szint. (Szükséges: {required_rank})"
|
||||
)
|
||||
return True
|
||||
return rank_checker
|
||||
Binary file not shown.
@@ -1,11 +1,15 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from fastapi.responses import RedirectResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from authlib.integrations.starlette_client import OAuth
|
||||
|
||||
from app.db.session import get_db
|
||||
from app.services.auth_service import AuthService
|
||||
from app.core.security import create_access_token, RANK_MAP
|
||||
from app.services.social_auth_service import SocialAuthService
|
||||
from app.core.security import create_tokens, DEFAULT_RANK_MAP
|
||||
from app.core.config import settings
|
||||
from app.schemas.auth import (
|
||||
UserLiteRegister, Token, PasswordResetRequest,
|
||||
UserKYCComplete, PasswordResetConfirm
|
||||
@@ -15,57 +19,50 @@ from app.models.identity import User
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/register-lite", response_model=Token, status_code=status.HTTP_201_CREATED)
|
||||
async def register_lite(user_in: UserLiteRegister, db: AsyncSession = Depends(get_db)):
|
||||
"""Step 1: Alapszintű regisztráció. Az új felhasználó alapértelmezetten 'user' (Rank 10)."""
|
||||
stmt = select(User).where(User.email == user_in.email)
|
||||
result = await db.execute(stmt)
|
||||
if result.scalar_one_or_none():
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Ez az e-mail cím már regisztrálva van."
|
||||
)
|
||||
|
||||
try:
|
||||
user = await AuthService.register_lite(db, user_in)
|
||||
|
||||
# Kezdeti token generálása
|
||||
token_data = {
|
||||
"sub": str(user.id),
|
||||
"role": "user",
|
||||
"rank": 10,
|
||||
"scope_level": "individual",
|
||||
"scope_id": str(user.id)
|
||||
}
|
||||
|
||||
token = create_access_token(data=token_data)
|
||||
return {
|
||||
"access_token": token,
|
||||
"token_type": "bearer",
|
||||
"is_active": user.is_active
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Sikertelen regisztráció: {str(e)}"
|
||||
)
|
||||
# --- GOOGLE OAUTH KONFIGURÁCIÓ ---
|
||||
oauth = OAuth()
|
||||
oauth.register(
|
||||
name='google',
|
||||
client_id=settings.GOOGLE_CLIENT_ID,
|
||||
client_secret=settings.GOOGLE_CLIENT_SECRET,
|
||||
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
|
||||
client_kwargs={'scope': 'openid email profile'}
|
||||
)
|
||||
|
||||
@router.post("/login", response_model=Token)
|
||||
async def login(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
form_data: OAuth2PasswordRequestForm = Depends()
|
||||
):
|
||||
"""Bejelentkezés és okos JWT generálása RBAC adatokkal."""
|
||||
user = await AuthService.authenticate(db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Hibás e-mail cím vagy jelszó."
|
||||
)
|
||||
|
||||
# Szerepkör string kinyerése és rang meghatározása a RANK_MAP-ből
|
||||
# --- SOCIAL AUTH ENDPOINTS ---
|
||||
|
||||
@router.get("/login/google")
|
||||
async def login_google(request: Request):
|
||||
"""
|
||||
Step 1: Átirányítás a Google bejelentkező oldalára.
|
||||
"""
|
||||
redirect_uri = settings.GOOGLE_CALLBACK_URL
|
||||
return await oauth.google.authorize_redirect(request, redirect_uri)
|
||||
|
||||
@router.get("/callback/google")
|
||||
async def auth_google(request: Request, db: AsyncSession = Depends(get_db)):
|
||||
"""
|
||||
Step 2: Google visszahívás lekezelése + Dupla Token generálás.
|
||||
"""
|
||||
try:
|
||||
token = await oauth.google.authorize_access_token(request)
|
||||
user_info = token.get('userinfo')
|
||||
except Exception:
|
||||
raise HTTPException(status_code=400, detail="Google hitelesítési hiba.")
|
||||
|
||||
if not user_info:
|
||||
raise HTTPException(status_code=400, detail="Nincs adat a Google-től.")
|
||||
|
||||
# Step 1: Technikai user létrehozása/keresése (inaktív, nincs mappa)
|
||||
user = await SocialAuthService.get_or_create_social_user(
|
||||
db, provider="google", social_id=user_info['sub'], email=user_info['email'],
|
||||
first_name=user_info.get('given_name'), last_name=user_info.get('family_name')
|
||||
)
|
||||
|
||||
# Dinamikus token generálás
|
||||
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default=DEFAULT_RANK_MAP)
|
||||
role_name = user.role.value if hasattr(user.role, 'value') else str(user.role)
|
||||
user_rank = RANK_MAP.get(role_name, 10)
|
||||
user_rank = ranks.get(role_name, 10)
|
||||
|
||||
token_data = {
|
||||
"sub": str(user.id),
|
||||
@@ -76,48 +73,104 @@ async def login(
|
||||
"region": user.region_code
|
||||
}
|
||||
|
||||
token = create_access_token(data=token_data)
|
||||
access, refresh = create_tokens(data=token_data)
|
||||
|
||||
# Visszatérés a frontendre mindkét tokennel
|
||||
response_url = f"{settings.FRONTEND_BASE_URL}/auth/callback?access={access}&refresh={refresh}"
|
||||
return RedirectResponse(url=response_url)
|
||||
|
||||
|
||||
# --- STANDARD AUTH ENDPOINTS ---
|
||||
|
||||
@router.post("/register-lite", response_model=Token, status_code=status.HTTP_201_CREATED)
|
||||
async def register_lite(user_in: UserLiteRegister, db: AsyncSession = Depends(get_db)):
|
||||
"""Step 1: Manuális regisztráció (inaktív, nincs mappa)."""
|
||||
stmt = select(User).where(User.email == user_in.email)
|
||||
if (await db.execute(stmt)).scalar_one_or_none():
|
||||
raise HTTPException(status_code=400, detail="Email már regisztrálva.")
|
||||
|
||||
user = await AuthService.register_lite(db, user_in)
|
||||
|
||||
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default=DEFAULT_RANK_MAP)
|
||||
role_name = user.role.value if hasattr(user.role, 'value') else str(user.role)
|
||||
|
||||
token_data = {
|
||||
"sub": str(user.id),
|
||||
"role": role_name,
|
||||
"rank": ranks.get(role_name, 10),
|
||||
"scope_level": "individual",
|
||||
"scope_id": str(user.id),
|
||||
"region": user.region_code
|
||||
}
|
||||
|
||||
access, refresh = create_tokens(data=token_data)
|
||||
return {
|
||||
"access_token": token,
|
||||
"access_token": access,
|
||||
"refresh_token": refresh,
|
||||
"token_type": "bearer",
|
||||
"is_active": user.is_active
|
||||
}
|
||||
|
||||
@router.post("/login", response_model=Token)
|
||||
async def login(db: AsyncSession = Depends(get_db), form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
"""Hagyományos belépés + Dupla Token."""
|
||||
user = await AuthService.authenticate(db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=401, detail="Hibás adatok.")
|
||||
|
||||
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default=DEFAULT_RANK_MAP)
|
||||
role_name = user.role.value if hasattr(user.role, 'value') else str(user.role)
|
||||
user_rank = ranks.get(role_name, 10)
|
||||
|
||||
token_data = {
|
||||
"sub": str(user.id),
|
||||
"role": role_name,
|
||||
"rank": user_rank,
|
||||
"scope_level": user.scope_level or "individual",
|
||||
"scope_id": user.scope_id or str(user.id),
|
||||
"region": user.region_code
|
||||
}
|
||||
|
||||
access, refresh = create_tokens(data=token_data)
|
||||
return {
|
||||
"access_token": access,
|
||||
"refresh_token": refresh,
|
||||
"token_type": "bearer",
|
||||
"is_active": user.is_active
|
||||
}
|
||||
|
||||
@router.get("/verify-email")
|
||||
async def verify_email(token: str, db: AsyncSession = Depends(get_db)):
|
||||
"""E-mail megerősítése."""
|
||||
success = await AuthService.verify_email(db, token)
|
||||
if not success:
|
||||
raise HTTPException(status_code=400, detail="Érvénytelen vagy lejárt token.")
|
||||
return {"message": "Email sikeresen megerősítve!"}
|
||||
if not await AuthService.verify_email(db, token):
|
||||
raise HTTPException(status_code=400, detail="Érvénytelen token.")
|
||||
return {"message": "Email megerősítve!"}
|
||||
|
||||
@router.post("/complete-kyc")
|
||||
async def complete_kyc(
|
||||
kyc_in: UserKYCComplete,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""Step 2: KYC adatok rögzítése és aktiválás."""
|
||||
"""
|
||||
Step 2: KYC Aktiválás.
|
||||
Itt használjuk a get_current_user-t (nem active), mert a user még inaktív.
|
||||
"""
|
||||
user = await AuthService.complete_kyc(db, current_user.id, kyc_in)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="Felhasználó nem található.")
|
||||
return {"status": "success", "message": "A profil aktiválva."}
|
||||
raise HTTPException(status_code=404, detail="User nem található.")
|
||||
return {"status": "success", "message": "Fiók aktiválva."}
|
||||
|
||||
@router.post("/forgot-password")
|
||||
async def forgot_password(req: PasswordResetRequest, db: AsyncSession = Depends(get_db)):
|
||||
"""Elfelejtett jelszó folyamat."""
|
||||
result = await AuthService.initiate_password_reset(db, req.email)
|
||||
if result == "cooldown":
|
||||
raise HTTPException(status_code=429, detail="Kérjük várjon 2 percet.")
|
||||
return {"message": "Amennyiben a cím létezik, a linket kiküldtük."}
|
||||
raise HTTPException(status_code=429, detail="Túl sok kérés.")
|
||||
return {"message": "Visszaállító link kiküldve."}
|
||||
|
||||
@router.post("/reset-password")
|
||||
async def reset_password(req: PasswordResetConfirm, db: AsyncSession = Depends(get_db)):
|
||||
"""Új jelszó beállítása."""
|
||||
if req.password != req.password_confirm:
|
||||
raise HTTPException(status_code=400, detail="A jelszavak nem egyeznek.")
|
||||
|
||||
success = await AuthService.reset_password(db, req.email, req.token, req.password)
|
||||
if not success:
|
||||
raise HTTPException(status_code=400, detail="Hiba a jelszó frissítésekor.")
|
||||
return {"message": "A jelszó sikeresen frissítve!"}
|
||||
raise HTTPException(status_code=400, detail="Nem egyeznek a jelszavak.")
|
||||
if not await AuthService.reset_password(db, req.email, req.token, req.password):
|
||||
raise HTTPException(status_code=400, detail="Sikertelen frissítés.")
|
||||
return {"message": "Jelszó frissítve!"}
|
||||
@@ -1,37 +1,46 @@
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, or_
|
||||
from app.db.session import get_db
|
||||
from app.models import AssetCatalog
|
||||
from app.services.asset_service import AssetService
|
||||
from typing import List
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/search")
|
||||
async def search_catalog(
|
||||
q: str = Query(..., min_length=2, description="Márka vagy típus keresése"),
|
||||
category: str = None,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Keresés a Robot által feltöltött katalógusban."""
|
||||
stmt = select(VehicleCatalog).where(
|
||||
or_(
|
||||
VehicleCatalog.brand.ilike(f"%{q}%"),
|
||||
VehicleCatalog.model.ilike(f"%{q}%")
|
||||
)
|
||||
)
|
||||
if category:
|
||||
stmt = stmt.where(VehicleCatalog.category == category)
|
||||
|
||||
result = await db.execute(stmt.limit(20))
|
||||
items = result.scalars().all()
|
||||
@router.get("/makes", response_model=List[str])
|
||||
async def list_makes(db: AsyncSession = Depends(get_db)):
|
||||
"""1. Szint: Márkák listázása."""
|
||||
return await AssetService.get_makes(db)
|
||||
|
||||
@router.get("/models", response_model=List[str])
|
||||
async def list_models(make: str, db: AsyncSession = Depends(get_db)):
|
||||
"""2. Szint: Típusok listázása egy adott márkához."""
|
||||
models = await AssetService.get_models(db, make)
|
||||
if not models:
|
||||
raise HTTPException(status_code=404, detail="Márka nem található vagy nincsenek típusok.")
|
||||
return models
|
||||
|
||||
@router.get("/generations", response_model=List[str])
|
||||
async def list_generations(make: str, model: str, db: AsyncSession = Depends(get_db)):
|
||||
"""3. Szint: Generációk/Évjáratok listázása."""
|
||||
generations = await AssetService.get_generations(db, make, model)
|
||||
if not generations:
|
||||
raise HTTPException(status_code=404, detail="Nincs generációs adat ehhez a típushoz.")
|
||||
return generations
|
||||
|
||||
@router.get("/engines")
|
||||
async def list_engines(make: str, model: str, gen: str, db: AsyncSession = Depends(get_db)):
|
||||
"""4. Szint: Motorváltozatok és technikai specifikációk."""
|
||||
engines = await AssetService.get_engines(db, make, model, gen)
|
||||
if not engines:
|
||||
raise HTTPException(status_code=404, detail="Nincs motorváltozat adat.")
|
||||
|
||||
# Itt visszaküldjük a teljes katalógus objektumokat (ID, motorváltozat, specifikációk)
|
||||
return [
|
||||
{
|
||||
"id": i.id,
|
||||
"full_name": f"{i.brand} {i.model}",
|
||||
"category": i.category,
|
||||
"status": i.verification_status,
|
||||
"specs": i.factory_specs
|
||||
} for i in items
|
||||
"id": e.id,
|
||||
"variant": e.engine_variant,
|
||||
"engine_code": e.engine_code,
|
||||
"fuel_type": e.fuel_type,
|
||||
"factory_data": e.factory_data
|
||||
} for e in engines
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
@@ -6,21 +6,21 @@ from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
class Settings(BaseSettings):
|
||||
# --- Paths (ÚJ SZEKCIÓ) ---
|
||||
# Meghatározzuk a projekt gyökérmappáját és a statikus fájlok helyét
|
||||
# --- Paths ---
|
||||
BASE_DIR: Path = Path(__file__).resolve().parent.parent.parent
|
||||
STATIC_DIR: str = os.path.join(str(BASE_DIR), "static")
|
||||
|
||||
# --- General ---
|
||||
PROJECT_NAME: str = "Traffic Ecosystem SuperApp"
|
||||
VERSION: str = "1.0.0"
|
||||
PROJECT_NAME: str = "Service Finder Ecosystem"
|
||||
VERSION: str = "2.1.0"
|
||||
API_V1_STR: str = "/api/v1"
|
||||
DEBUG: bool = False
|
||||
|
||||
# --- Security / JWT ---
|
||||
SECRET_KEY: str = "NOT_SET_DANGER"
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 nap
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
||||
|
||||
# --- Initial Admin ---
|
||||
INITIAL_ADMIN_EMAIL: str = "admin@servicefinder.hu"
|
||||
@@ -42,21 +42,35 @@ class Settings(BaseSettings):
|
||||
SMTP_PASSWORD: Optional[str] = None
|
||||
|
||||
# --- External URLs ---
|
||||
FRONTEND_BASE_URL: str = "http://localhost:3000"
|
||||
FRONTEND_BASE_URL: str = "https://dev.profibot.hu"
|
||||
|
||||
# --- Dinamikus Admin Motor ---
|
||||
# --- Google OAuth ---
|
||||
GOOGLE_CLIENT_ID: str = ""
|
||||
GOOGLE_CLIENT_SECRET: str = ""
|
||||
GOOGLE_CALLBACK_URL: str = "https://dev.profibot.hu/api/v1/auth/callback/google"
|
||||
|
||||
# --- Brute-Force & Security ---
|
||||
LOGIN_RATE_LIMIT_ANON: str = "5/minute"
|
||||
AUTH_MIN_PASSWORD_LENGTH: int = 8
|
||||
|
||||
# --- Dinamikus Admin Motor (Javított) ---
|
||||
async def get_db_setting(self, db: AsyncSession, key_name: str, default: Any = None) -> Any:
|
||||
"""
|
||||
Lekér egy beállítást a data.system_parameters táblából.
|
||||
Ha a tábla még nem létezik (migráció előtt), elkapja a hibát és default-ot ad.
|
||||
"""
|
||||
try:
|
||||
query = text("SELECT value_json FROM data.system_settings WHERE key_name = :key")
|
||||
# A lekérdezés a system_parameters táblát és a 'key' mezőt használja
|
||||
query = text("SELECT value FROM data.system_parameters WHERE key = :key")
|
||||
result = await db.execute(query, {"key": key_name})
|
||||
row = result.fetchone()
|
||||
if row and row[0] is not None:
|
||||
return row[0]
|
||||
return default
|
||||
except Exception:
|
||||
# Adatbázis hiba vagy hiányzó tábla esetén fallback az alapértelmezett értékre
|
||||
return default
|
||||
|
||||
# .env fájl konfigurációja
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
|
||||
@@ -1,65 +1,48 @@
|
||||
import secrets
|
||||
import string
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional, Dict, Any, Tuple
|
||||
import bcrypt
|
||||
from jose import jwt, JWTError
|
||||
from app.core.config import settings
|
||||
from fastapi_limiter import FastAPILimiter
|
||||
from fastapi_limiter.depends import RateLimiter
|
||||
|
||||
# Master Book 5.0: RBAC Rank Definition Matrix
|
||||
# Ezek a szintek határozzák meg a hozzáférést a Middleware szintjén.
|
||||
RANK_MAP = {
|
||||
"superadmin": 100,
|
||||
"country_admin": 80,
|
||||
"region_admin": 60,
|
||||
"moderator": 40,
|
||||
"sales": 20,
|
||||
"user": 10,
|
||||
"service": 15,
|
||||
"fleet_manager": 25,
|
||||
"driver": 5
|
||||
# Ezt az auth végpontokhoz adjuk hozzá:
|
||||
# @router.post("/login", dependencies=[Depends(RateLimiter(times=5, seconds=60))])
|
||||
|
||||
DEFAULT_RANK_MAP = {
|
||||
"superadmin": 100, "admin": 80, "fleet_manager": 25,
|
||||
"service": 15, "user": 10, "driver": 5
|
||||
}
|
||||
|
||||
def generate_secure_slug(length: int = 12) -> str:
|
||||
"""Biztonságos kód generálása (pl. mappákhoz)."""
|
||||
alphabet = string.ascii_lowercase + string.digits
|
||||
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
"""Összehasonlítja a sima szöveges jelszót a hash-elt változattal."""
|
||||
if not hashed_password:
|
||||
return False
|
||||
if not hashed_password: return False
|
||||
try:
|
||||
return bcrypt.checkpw(
|
||||
plain_password.encode("utf-8"),
|
||||
hashed_password.encode("utf-8")
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
|
||||
except Exception: return False
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
"""Létrehozza a jelszó hash-elt változatát."""
|
||||
salt = bcrypt.gensalt()
|
||||
return bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8")
|
||||
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
||||
|
||||
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
|
||||
"""
|
||||
Létrehozza a JWT access tokent bővített RBAC adatokkal.
|
||||
Várt kulcsok: sub (user_id), role, rank, scope_level, scope_id
|
||||
"""
|
||||
def create_tokens(data: Dict[str, Any], access_delta: Optional[timedelta] = None, refresh_delta: Optional[timedelta] = None) -> Tuple[str, str]:
|
||||
"""Access és Refresh token generálása."""
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
else:
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
now = datetime.now(timezone.utc)
|
||||
acc_min = access_delta if access_delta else timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_payload = {**to_encode, "exp": now + acc_min, "iat": now, "type": "access", "iss": "service-finder-auth"}
|
||||
access_token = jwt.encode(access_payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
|
||||
# Rendszer szintű metaadatok hozzáadása
|
||||
to_encode.update({
|
||||
"exp": expire,
|
||||
"iat": datetime.now(timezone.utc),
|
||||
"iss": "service-finder-auth"
|
||||
})
|
||||
|
||||
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
return encoded_jwt
|
||||
ref_days = refresh_delta if refresh_delta else timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
|
||||
refresh_payload = {"sub": str(to_encode.get("sub")), "exp": now + ref_days, "iat": now, "type": "refresh"}
|
||||
refresh_token = jwt.encode(refresh_payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
return access_token, refresh_token
|
||||
|
||||
def decode_token(token: str) -> Optional[Dict[str, Any]]:
|
||||
"""JWT token visszafejtése és validálása."""
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||
return payload
|
||||
except JWTError:
|
||||
return None
|
||||
try: return jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||
except JWTError: return None
|
||||
@@ -2,7 +2,8 @@ import os
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from app.api.v1.api import api_router # Ez már tartalmaz mindent (auth, services, stb.)
|
||||
from starlette.middleware.sessions import SessionMiddleware # ÚJ
|
||||
from app.api.v1.api import api_router
|
||||
from app.core.config import settings
|
||||
|
||||
os.makedirs("static/previews", exist_ok=True)
|
||||
@@ -14,12 +15,19 @@ app = FastAPI(
|
||||
docs_url="/docs"
|
||||
)
|
||||
|
||||
# --- SESSION MIDDLEWARE (Google Authhoz kötelező) ---
|
||||
app.add_middleware(
|
||||
SessionMiddleware,
|
||||
secret_key=settings.SECRET_KEY
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=[
|
||||
"http://192.168.100.10:3001",
|
||||
"http://localhost:3001",
|
||||
"https://dev.profibot.hu"
|
||||
"https://dev.profibot.hu",
|
||||
"https://app.profibot.hu"
|
||||
],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
@@ -27,8 +35,6 @@ app.add_middleware(
|
||||
)
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
# CSAK EZT AZ EGYET KELL BEKÖTNI:
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
|
||||
@app.get("/")
|
||||
@@ -36,5 +42,5 @@ async def root():
|
||||
return {
|
||||
"status": "online",
|
||||
"message": "Service Finder Master System v2.0",
|
||||
"features": ["Document Engine", "Asset Vault", "Org Onboarding", "Service Hunt"]
|
||||
"features": ["Google Auth Enabled", "Asset Vault", "Org Onboarding"]
|
||||
}
|
||||
@@ -1,34 +1,54 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/__init__.py
|
||||
from app.db.base_class import Base
|
||||
|
||||
from .identity import User, Person, Wallet, UserRole, VerificationToken
|
||||
# Identitás és Jogosultság
|
||||
from .identity import User, Person, Wallet, UserRole, VerificationToken, SocialAccount
|
||||
|
||||
# Szervezeti struktúra
|
||||
from .organization import Organization, OrganizationMember
|
||||
|
||||
# Járművek és Eszközök (Digital Twin)
|
||||
from .asset import (
|
||||
Asset, AssetCatalog, AssetCost, AssetEvent,
|
||||
AssetFinancials, AssetTelemetry, AssetReview, ExchangeRate
|
||||
)
|
||||
|
||||
# Szerviz és Szakértelem (ÚJ)
|
||||
from .service import ServiceProfile, ExpertiseTag, ServiceExpertise
|
||||
|
||||
# Földrajzi adatok és Címek
|
||||
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType
|
||||
|
||||
# Gamification és Economy
|
||||
from .gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, Rating, PointsLedger
|
||||
|
||||
# Rendszerkonfiguráció és Alapok
|
||||
from .system_config import SystemParameter
|
||||
from .document import Document
|
||||
from .translation import Translation # <--- HOZZÁADVA
|
||||
from .core_logic import SubscriptionTier, OrganizationSubscription, CreditTransaction, ServiceSpecialty
|
||||
from .history import AuditLog, VehicleOwnership
|
||||
from .security import PendingAction # <--- HOZZÁADVA
|
||||
from .translation import Translation
|
||||
|
||||
# Aliasok
|
||||
# Üzleti logika és Előfizetés
|
||||
from .core_logic import SubscriptionTier, OrganizationSubscription, CreditTransaction, ServiceSpecialty
|
||||
|
||||
# Naplózás és Biztonság
|
||||
from .history import AuditLog, VehicleOwnership
|
||||
from .security import PendingAction
|
||||
|
||||
# Aliasok a kényelmesebb fejlesztéshez
|
||||
Vehicle = Asset
|
||||
UserVehicle = Asset
|
||||
VehicleCatalog = AssetCatalog
|
||||
ServiceRecord = AssetEvent
|
||||
|
||||
__all__ = [
|
||||
"Base", "User", "Person", "Wallet", "UserRole", "VerificationToken",
|
||||
"Organization", "OrganizationMember", "Asset", "AssetCatalog", "AssetCost",
|
||||
"AssetEvent", "AssetFinancials", "AssetTelemetry", "AssetReview", "ExchangeRate",
|
||||
"Address", "GeoPostalCode", "GeoStreet", "GeoStreetType", "PointRule",
|
||||
"LevelConfig", "UserStats", "Badge", "UserBadge", "Rating", "PointsLedger",
|
||||
"SystemParameter", "Document", "Translation", "PendingAction", # <--- BŐVÍTVE
|
||||
"Base", "User", "Person", "Wallet", "UserRole", "VerificationToken", "SocialAccount",
|
||||
"Organization", "OrganizationMember",
|
||||
"Asset", "AssetCatalog", "AssetCost", "AssetEvent", "AssetFinancials",
|
||||
"AssetTelemetry", "AssetReview", "ExchangeRate",
|
||||
"ServiceProfile", "ExpertiseTag", "ServiceExpertise", # <--- HOZZÁADVA
|
||||
"Address", "GeoPostalCode", "GeoStreet", "GeoStreetType",
|
||||
"PointRule", "LevelConfig", "UserStats", "Badge", "UserBadge", "Rating", "PointsLedger",
|
||||
"SystemParameter", "Document", "Translation", "PendingAction",
|
||||
"SubscriptionTier", "OrganizationSubscription",
|
||||
"CreditTransaction", "ServiceSpecialty", "AuditLog", "VehicleOwnership",
|
||||
"Vehicle", "UserVehicle", "VehicleCatalog", "ServiceRecord"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,42 +6,57 @@ from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class AssetCatalog(Base):
|
||||
"""Globális járműkatalógus (Márka -> Típus -> Generáció -> Motor)."""
|
||||
__tablename__ = "vehicle_catalog"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
make = Column(String, index=True, nullable=False)
|
||||
model = Column(String, index=True, nullable=False)
|
||||
generation = Column(String)
|
||||
make = Column(String, index=True, nullable=False) # 1. Szint: Audi
|
||||
model = Column(String, index=True, nullable=False) # 2. Szint: A4
|
||||
generation = Column(String, index=True) # 3. Szint: B8 (2008-2015)
|
||||
engine_variant = Column(String) # 4. Szint: 2.0 TDI (150 LE)
|
||||
|
||||
year_from = Column(Integer)
|
||||
year_to = Column(Integer)
|
||||
vehicle_class = Column(String)
|
||||
fuel_type = Column(String)
|
||||
engine_code = Column(String)
|
||||
factory_data = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
factory_data = Column(JSON, server_default=text("'{}'::jsonb")) # Technikai specifikációk
|
||||
|
||||
assets = relationship("Asset", back_populates="catalog")
|
||||
|
||||
class Asset(Base):
|
||||
"""Egyedi jármű (Asset) példány - Az ökoszisztéma magja."""
|
||||
__tablename__ = "assets"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
vin = Column(String(17), unique=True, index=True, nullable=False)
|
||||
license_plate = Column(String(20), index=True)
|
||||
name = Column(String)
|
||||
year_of_manufacture = Column(Integer)
|
||||
|
||||
# --- BIZTONSÁGI ÉS JOGOSULTSÁGI IZOLÁCIÓ ---
|
||||
# A current_organization_id biztosítja a gyors, adatbázis-szintű Scoped RBAC védelmet.
|
||||
current_organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
|
||||
|
||||
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"))
|
||||
is_verified = Column(Boolean, default=False)
|
||||
verification_method = Column(String(20)) # 'robot', 'ocr', 'manual'
|
||||
status = Column(String(20), default="active")
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Kapcsolatok (Digital Twin Modules)
|
||||
catalog = relationship("AssetCatalog", back_populates="assets")
|
||||
current_org = relationship("Organization")
|
||||
financials = relationship("AssetFinancials", back_populates="asset", uselist=False)
|
||||
telemetry = relationship("AssetTelemetry", back_populates="asset", uselist=False)
|
||||
assignments = relationship("AssetAssignment", back_populates="asset")
|
||||
events = relationship("AssetEvent", back_populates="asset")
|
||||
costs = relationship("AssetCost", back_populates="asset")
|
||||
reviews = relationship("AssetReview", back_populates="asset")
|
||||
ownership_history = relationship("VehicleOwnership", back_populates="vehicle")
|
||||
|
||||
class AssetFinancials(Base):
|
||||
__tablename__ = "asset_financials"
|
||||
@@ -77,9 +92,10 @@ class AssetReview(Base):
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
asset = relationship("Asset", back_populates="reviews")
|
||||
user = relationship("User") # <--- JAVÍTÁS: Hozzáadva
|
||||
user = relationship("User")
|
||||
|
||||
class AssetAssignment(Base):
|
||||
"""Jármű flotta-történetének nyilvántartása."""
|
||||
__tablename__ = "asset_assignments"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
@@ -90,7 +106,7 @@ class AssetAssignment(Base):
|
||||
status = Column(String(30), default="active")
|
||||
|
||||
asset = relationship("Asset", back_populates="assignments")
|
||||
organization = relationship("Organization") # <--- KRITIKUS JAVÍTÁS: Ez okozta a login hibát
|
||||
organization = relationship("Organization")
|
||||
|
||||
class AssetEvent(Base):
|
||||
__tablename__ = "asset_events"
|
||||
@@ -113,16 +129,13 @@ class AssetCost(Base):
|
||||
amount_local = Column(Numeric(18, 2), nullable=False)
|
||||
currency_local = Column(String(3), nullable=False)
|
||||
amount_eur = Column(Numeric(18, 2), nullable=True)
|
||||
net_amount_local = Column(Numeric(18, 2))
|
||||
vat_rate = Column(Numeric(5, 2))
|
||||
exchange_rate_used = Column(Numeric(18, 6))
|
||||
date = Column(DateTime(timezone=True), server_default=func.now())
|
||||
mileage_at_cost = Column(Integer)
|
||||
data = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
asset = relationship("Asset", back_populates="costs")
|
||||
organization = relationship("Organization") # <--- JAVÍTÁS: Hozzáadva
|
||||
driver = relationship("User") # <--- JAVÍTÁS: Hozzáadva
|
||||
organization = relationship("Organization")
|
||||
driver = relationship("User")
|
||||
|
||||
class ExchangeRate(Base):
|
||||
__tablename__ = "exchange_rates"
|
||||
@@ -131,5 +144,4 @@ class ExchangeRate(Base):
|
||||
base_currency = Column(String(3), default="EUR")
|
||||
target_currency = Column(String(3), unique=True)
|
||||
rate = Column(Numeric(18, 6), nullable=False)
|
||||
rate_date = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
16
backend/app/models/audit.py
Normal file
16
backend/app/models/audit.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, JSON, ForeignKey, text
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class AuditLog(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id", ondelete="SET NULL"), nullable=True)
|
||||
action = Column(String(100), nullable=False) # pl. "LOGIN", "REGISTER", "DELETE_ASSET"
|
||||
resource_type = Column(String(50)) # pl. "User", "Asset", "Organization"
|
||||
resource_id = Column(String(100))
|
||||
details = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
ip_address = Column(String(45))
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
@@ -1,85 +1,68 @@
|
||||
import uuid
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Enum, BigInteger
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Enum, BigInteger, UniqueConstraint
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class UserRole(str, enum.Enum):
|
||||
superadmin = "superadmin"
|
||||
admin = "admin"
|
||||
user = "user"
|
||||
service = "service"
|
||||
fleet_manager = "fleet_manager"
|
||||
driver = "driver"
|
||||
superadmin = "superadmin"; admin = "admin"; user = "user"
|
||||
service = "service"; fleet_manager = "fleet_manager"; driver = "driver"
|
||||
|
||||
class Person(Base):
|
||||
__tablename__ = "persons"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
__tablename__ = "persons"; __table_args__ = {"schema": "data"}
|
||||
id = Column(BigInteger, primary_key=True, index=True)
|
||||
id_uuid = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
|
||||
|
||||
last_name = Column(String, nullable=False)
|
||||
first_name = Column(String, nullable=False)
|
||||
phone = Column(String, nullable=True)
|
||||
|
||||
last_name = Column(String, nullable=False); first_name = Column(String, nullable=False); phone = Column(String, nullable=True)
|
||||
mothers_last_name = Column(String); mothers_first_name = Column(String); birth_place = Column(String); birth_date = Column(DateTime)
|
||||
identity_docs = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
ice_contact = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
is_active = Column(Boolean, default=False, nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
users = relationship("User", back_populates="person")
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
__tablename__ = "users"; __table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String, nullable=True)
|
||||
role = Column(Enum(UserRole), default=UserRole.user)
|
||||
is_active = Column(Boolean, default=False)
|
||||
is_deleted = Column(Boolean, default=False)
|
||||
is_active = Column(Boolean, default=False); is_deleted = Column(Boolean, default=False)
|
||||
person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
|
||||
|
||||
# ÚJ MEZŐK HOZZÁADVA:
|
||||
preferred_language = Column(String(5), server_default="hu")
|
||||
region_code = Column(String(5), server_default="HU")
|
||||
|
||||
# RBAC & SCOPE
|
||||
scope_level = Column(String(30), server_default="individual")
|
||||
scope_id = Column(String(50))
|
||||
custom_permissions = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
folder_slug = Column(String(12), unique=True, index=True)
|
||||
refresh_token_hash = Column(String(255), nullable=True)
|
||||
two_factor_secret = Column(String(100), nullable=True)
|
||||
two_factor_enabled = Column(Boolean, default=False)
|
||||
preferred_language = Column(String(5), server_default="hu"); region_code = Column(String(5), server_default="HU"); preferred_currency = Column(String(3), server_default="HUF")
|
||||
scope_level = Column(String(30), server_default="individual"); scope_id = Column(String(50)); custom_permissions = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
person = relationship("Person", back_populates="users"); wallet = relationship("Wallet", back_populates="user", uselist=False)
|
||||
stats = relationship("UserStats", back_populates="user", uselist=False); ownership_history = relationship("VehicleOwnership", back_populates="user")
|
||||
owned_organizations = relationship("Organization", back_populates="owner"); social_accounts = relationship("SocialAccount", back_populates="user", cascade="all, delete-orphan")
|
||||
|
||||
person = relationship("Person", back_populates="users")
|
||||
wallet = relationship("Wallet", back_populates="user", uselist=False)
|
||||
stats = relationship("UserStats", back_populates="user", uselist=False)
|
||||
ownership_history = relationship("VehicleOwnership", back_populates="user")
|
||||
owned_organizations = relationship("Organization", back_populates="owner")
|
||||
|
||||
# A Wallet és VerificationToken osztályok maradnak változatlanok...
|
||||
class Wallet(Base):
|
||||
__tablename__ = "wallets"
|
||||
__table_args__ = {"schema": "data"}
|
||||
__tablename__ = "wallets"; __table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), unique=True)
|
||||
coin_balance = Column(Numeric(18, 2), default=0.00)
|
||||
credit_balance = Column(Numeric(18, 2), default=0.00)
|
||||
currency = Column(String(3), default="HUF")
|
||||
coin_balance = Column(Numeric(18, 2), default=0.00); credit_balance = Column(Numeric(18, 2), default=0.00); currency = Column(String(3), default="HUF")
|
||||
user = relationship("User", back_populates="wallet")
|
||||
|
||||
class VerificationToken(Base):
|
||||
__tablename__ = "verification_tokens"
|
||||
__table_args__ = {"schema": "data"}
|
||||
__tablename__ = "verification_tokens"; __table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
token = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id", ondelete="CASCADE"), nullable=False)
|
||||
token_type = Column(String(20), nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
expires_at = Column(DateTime(timezone=True), nullable=False)
|
||||
is_used = Column(Boolean, default=False)
|
||||
token_type = Column(String(20), nullable=False); created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
expires_at = Column(DateTime(timezone=True), nullable=False); is_used = Column(Boolean, default=False)
|
||||
|
||||
class SocialAccount(Base):
|
||||
__tablename__ = "social_accounts"
|
||||
__table_args__ = (UniqueConstraint('provider', 'social_id', name='uix_social_provider_id'), {"schema": "data"})
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id", ondelete="CASCADE"), nullable=False)
|
||||
provider = Column(String(50), nullable=False); social_id = Column(String(255), nullable=False, index=True); email = Column(String(255), nullable=False)
|
||||
extra_data = Column(JSON, server_default=text("'{}'::jsonb")); created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
user = relationship("User", back_populates="social_accounts")
|
||||
@@ -1,5 +1,4 @@
|
||||
import enum
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text
|
||||
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM
|
||||
from sqlalchemy.orm import relationship
|
||||
@@ -25,6 +24,9 @@ class Organization(Base):
|
||||
full_name = Column(String, nullable=False)
|
||||
name = Column(String, nullable=False)
|
||||
display_name = Column(String(50))
|
||||
|
||||
# --- BIZTONSÁGI BŐVÍTÉS (Mappa elszigetelés) ---
|
||||
folder_slug = Column(String(12), unique=True, index=True)
|
||||
|
||||
default_currency = Column(String(3), default="HUF")
|
||||
country_code = Column(String(2), default="HU")
|
||||
@@ -63,7 +65,7 @@ class Organization(Base):
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# String alapú hivatkozás a körkörös import ellen
|
||||
# Kapcsolatok
|
||||
assets = relationship("AssetAssignment", back_populates="organization", cascade="all, delete-orphan")
|
||||
members = relationship("OrganizationMember", back_populates="organization", cascade="all, delete-orphan")
|
||||
owner = relationship("User", back_populates="owned_organizations")
|
||||
@@ -75,8 +77,7 @@ class OrganizationMember(Base):
|
||||
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
|
||||
role = Column(String, default="driver")
|
||||
|
||||
permissions = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
organization = relationship("Organization", back_populates="members")
|
||||
user = relationship("User") # Egyszerűsített string hivatkozás
|
||||
user = relationship("User")
|
||||
59
backend/app/models/service.py
Normal file
59
backend/app/models/service.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text, Text
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
|
||||
from geoalchemy2 import Geometry # PostGIS támogatás
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class ServiceProfile(Base):
|
||||
"""
|
||||
Szerviz szolgáltató kiterjesztett adatai.
|
||||
Egy Organization-höz (org_type='service') kapcsolódik.
|
||||
"""
|
||||
__tablename__ = "service_profiles"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
organization_id = Column(Integer, ForeignKey("data.organizations.id"), unique=True)
|
||||
|
||||
# PostGIS GPS pont (SRID 4326 = WGS84 koordináták)
|
||||
location = Column(Geometry(geometry_type='POINT', srid=4326), index=True)
|
||||
|
||||
# Trust Engine (Bot Discovery=30, User Entry=50, Admin/Partner=100)
|
||||
trust_score = Column(Integer, default=30)
|
||||
is_verified = Column(Boolean, default=False)
|
||||
verification_log = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
|
||||
opening_hours = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
contact_phone = Column(String)
|
||||
website = Column(String)
|
||||
bio = Column(Text)
|
||||
|
||||
# Kapcsolatok
|
||||
organization = relationship("Organization")
|
||||
expertises = relationship("ServiceExpertise", back_populates="service")
|
||||
|
||||
class ExpertiseTag(Base):
|
||||
"""Szakmai szempontok taxonómiája."""
|
||||
__tablename__ = "expertise_tags"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(50), unique=True, index=True) # pl. 'bmw_gs_specialist'
|
||||
name_hu = Column(String(100))
|
||||
category = Column(String(30)) # 'repair', 'fuel', 'food', 'emergency'
|
||||
|
||||
class ServiceExpertise(Base):
|
||||
"""Kapcsolótábla a szerviz és a szakterület között."""
|
||||
__tablename__ = "service_expertises"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
service_id = Column(Integer, ForeignKey("data.service_profiles.id"), primary_key=True)
|
||||
expertise_id = Column(Integer, ForeignKey("data.expertise_tags.id"), primary_key=True)
|
||||
|
||||
# Validációs szint (0-100% - Mennyire hiteles ez a szakértelem)
|
||||
validation_level = Column(Integer, default=0)
|
||||
|
||||
service = relationship("ServiceProfile", back_populates="expertises")
|
||||
expertise = relationship("ExpertiseTag")
|
||||
@@ -1,16 +1,18 @@
|
||||
from sqlalchemy import Column, String, JSON, Integer, Boolean, DateTime, func
|
||||
from sqlalchemy import Column, String, JSON, Boolean, DateTime, Integer, text
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base_class import Base
|
||||
|
||||
class SystemParameter(Base):
|
||||
"""
|
||||
Globális rendszerbeállítások (A meglévő data.system_parameters tábla alapján).
|
||||
Rendszerszintű dinamikus paraméterek tárolása.
|
||||
Szinkronban az admin.py és config.py elvárásaival.
|
||||
"""
|
||||
__tablename__ = "system_parameters"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
key = Column(String(50), unique=True, index=True, nullable=False)
|
||||
value = Column(JSON, nullable=False)
|
||||
# Az admin.py 'key' mezőt vár, nem 'key_name'-et!
|
||||
key = Column(String(50), primary_key=True, index=True)
|
||||
value = Column(JSON, server_default=text("'{}'::jsonb"), nullable=False)
|
||||
description = Column(String(255), nullable=True)
|
||||
is_active = Column(Boolean, default=True)
|
||||
description = Column(String, nullable=True)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,35 +1,99 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.models.asset import Asset, AssetTelemetry, AssetFinancials
|
||||
from sqlalchemy import select, distinct
|
||||
from app.models.asset import Asset, AssetCatalog, AssetTelemetry, AssetFinancials, AssetAssignment
|
||||
from app.models.gamification import UserStats, PointRule
|
||||
import uuid
|
||||
import logging
|
||||
|
||||
async def create_new_vehicle(db: AsyncSession, user_id: int, vin: str, license_plate: str):
|
||||
# 1. Alap Asset létrehozása
|
||||
new_asset = Asset(
|
||||
vin=vin,
|
||||
license_plate=license_plate,
|
||||
name=f"Teszt Autó ({license_plate})"
|
||||
)
|
||||
db.add(new_asset)
|
||||
await db.flush() # Hogy legyen ID-ja
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 2. Modulok inicializálása (Digital Twin alapozás)
|
||||
db.add(AssetTelemetry(asset_id=new_asset.id, current_mileage=0))
|
||||
db.add(AssetFinancials(asset_id=new_asset.id))
|
||||
class AssetService:
|
||||
@staticmethod
|
||||
async def get_makes(db: AsyncSession):
|
||||
"""1. Szint: Márkák lekérdezése (pl. Audi, BMW)."""
|
||||
stmt = select(distinct(AssetCatalog.make)).order_by(AssetCatalog.make)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
# 3. GAMIFICATION: Pontszerzés (ASSET_REGISTER = 100 XP)
|
||||
# Megkeressük a szabályt
|
||||
rule_stmt = select(PointRule).where(PointRule.action_key == "ASSET_REGISTER")
|
||||
rule = (await db.execute(rule_stmt)).scalar_one_or_none()
|
||||
|
||||
if rule:
|
||||
# Frissítjük a felhasználó XP-jét
|
||||
stats_stmt = select(UserStats).where(UserStats.user_id == user_id)
|
||||
stats = (await db.execute(stats_stmt)).scalar_one_or_none()
|
||||
if stats:
|
||||
stats.total_xp += rule.points
|
||||
# Itt később jöhet a szintlépés ellenőrzése is!
|
||||
@staticmethod
|
||||
async def get_models(db: AsyncSession, make: str):
|
||||
"""2. Szint: Típusok szűrése márka alapján (pl. A4, A6)."""
|
||||
stmt = select(distinct(AssetCatalog.model)).where(AssetCatalog.make == make).order_by(AssetCatalog.model)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
await db.commit()
|
||||
return new_asset
|
||||
@staticmethod
|
||||
async def get_generations(db: AsyncSession, make: str, model: str):
|
||||
"""3. Szint: Generációk/Évjáratok (pl. B8 (2008-2015))."""
|
||||
stmt = select(distinct(AssetCatalog.generation)).where(
|
||||
AssetCatalog.make == make,
|
||||
AssetCatalog.model == model
|
||||
).order_by(AssetCatalog.generation)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
@staticmethod
|
||||
async def get_engines(db: AsyncSession, make: str, model: str, generation: str):
|
||||
"""4. Szint: Motorváltozatok (pl. 2.0 TDI)."""
|
||||
stmt = select(AssetCatalog).where(
|
||||
AssetCatalog.make == make,
|
||||
AssetCatalog.model == model,
|
||||
AssetCatalog.generation == generation
|
||||
).order_by(AssetCatalog.engine_variant)
|
||||
result = await db.execute(stmt)
|
||||
return result.scalars().all()
|
||||
|
||||
@staticmethod
|
||||
async def create_and_assign_vehicle(
|
||||
db: AsyncSession,
|
||||
user_id: int,
|
||||
org_id: int,
|
||||
vin: str,
|
||||
license_plate: str,
|
||||
catalog_id: int = None
|
||||
):
|
||||
"""Jármű rögzítése, flottához rendelése és XP jóváírás (Atomic)."""
|
||||
try:
|
||||
# 1. Asset létrehozása közvetlen flotta-kötéssel
|
||||
new_asset = Asset(
|
||||
vin=vin,
|
||||
license_plate=license_plate,
|
||||
catalog_id=catalog_id,
|
||||
current_organization_id=org_id, # Izolációs pointer
|
||||
status="active",
|
||||
is_verified=False
|
||||
)
|
||||
db.add(new_asset)
|
||||
await db.flush()
|
||||
|
||||
# 2. Digitális Iker történetiség (Assignment)
|
||||
assignment = AssetAssignment(
|
||||
asset_id=new_asset.id,
|
||||
organization_id=org_id,
|
||||
status="active"
|
||||
)
|
||||
db.add(assignment)
|
||||
|
||||
# 3. Digitális Iker modulok indítása
|
||||
db.add(AssetTelemetry(asset_id=new_asset.id))
|
||||
db.add(AssetFinancials(asset_id=new_asset.id))
|
||||
|
||||
# 4. GAMIFICATION: XP jóváírás
|
||||
rule_stmt = select(PointRule).where(PointRule.action_key == "ASSET_REGISTER")
|
||||
rule = (await db.execute(rule_stmt)).scalar_one_or_none()
|
||||
|
||||
if rule:
|
||||
stats_stmt = select(UserStats).where(UserStats.user_id == user_id)
|
||||
stats = (await db.execute(stats_stmt)).scalar_one_or_none()
|
||||
if stats:
|
||||
stats.total_xp += rule.points
|
||||
logger.info(f"User {user_id} awarded {rule.points} XP for asset registration.")
|
||||
|
||||
# 5. Robot Scout Trigger (későbbi implementáció)
|
||||
# await RobotScout.trigger_vin_lookup(db, new_asset.id)
|
||||
|
||||
await db.commit()
|
||||
return new_asset
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Asset Creation Error: {str(e)}")
|
||||
raise e
|
||||
@@ -9,12 +9,13 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, and_
|
||||
from sqlalchemy.orm import joinedload
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from app.models.identity import User, Person, UserRole, VerificationToken, Wallet
|
||||
from app.models.gamification import UserStats
|
||||
from app.models.organization import Organization, OrganizationMember, OrgType
|
||||
from app.schemas.auth import UserLiteRegister, UserKYCComplete
|
||||
from app.core.security import get_password_hash, verify_password
|
||||
from app.core.security import get_password_hash, verify_password, generate_secure_slug
|
||||
from app.services.email_manager import email_manager
|
||||
from app.core.config import settings
|
||||
from app.services.config_service import config
|
||||
@@ -26,8 +27,23 @@ logger = logging.getLogger(__name__)
|
||||
class AuthService:
|
||||
@staticmethod
|
||||
async def register_lite(db: AsyncSession, user_in: UserLiteRegister):
|
||||
"""Step 1: Lite Regisztráció."""
|
||||
"""
|
||||
Step 1: Lite Regisztráció (Manuális).
|
||||
Létrehozza a Person és User rekordokat, de a fiók inaktív marad.
|
||||
A folder_slug itt még NEM generálódik le!
|
||||
"""
|
||||
try:
|
||||
# --- Dinamikus jelszóhossz ellenőrzés ---
|
||||
# Lekérjük az admin beállítást, minimum 8 karakter a hard limit.
|
||||
min_pass = await config.get_setting(db, "auth_min_password_length", default=8)
|
||||
min_len = max(int(min_pass), 8)
|
||||
|
||||
if len(user_in.password) < min_len:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"A jelszónak legalább {min_len} karakter hosszúnak kell lennie."
|
||||
)
|
||||
|
||||
new_person = Person(
|
||||
first_name=user_in.first_name,
|
||||
last_name=user_in.last_name,
|
||||
@@ -46,11 +62,13 @@ class AuthService:
|
||||
region_code=user_in.region_code,
|
||||
preferred_language=user_in.lang,
|
||||
timezone=user_in.timezone
|
||||
# folder_slug marad NULL a Step 2-ig
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.flush()
|
||||
|
||||
reg_hours = await config.get_setting("auth_registration_hours", region_code=user_in.region_code, default=48)
|
||||
# Verifikációs token generálása
|
||||
reg_hours = await config.get_setting(db, "auth_registration_hours", region_code=user_in.region_code, default=48)
|
||||
token_val = uuid.uuid4()
|
||||
db.add(VerificationToken(
|
||||
token=token_val,
|
||||
@@ -59,6 +77,7 @@ class AuthService:
|
||||
expires_at=datetime.now(timezone.utc) + timedelta(hours=int(reg_hours))
|
||||
))
|
||||
|
||||
# Email kiküldése
|
||||
verification_link = f"{settings.FRONTEND_BASE_URL}/verify?token={token_val}"
|
||||
await email_manager.send_email(
|
||||
recipient=user_in.email,
|
||||
@@ -67,9 +86,23 @@ class AuthService:
|
||||
lang=user_in.lang
|
||||
)
|
||||
|
||||
# Audit log a regisztrációról
|
||||
await security_service.log_event(
|
||||
db,
|
||||
user_id=new_user.id,
|
||||
action="USER_REGISTER_LITE",
|
||||
severity="info",
|
||||
target_type="User",
|
||||
target_id=str(new_user.id),
|
||||
new_data={"email": user_in.email, "method": "manual"}
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_user)
|
||||
return new_user
|
||||
except HTTPException:
|
||||
await db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Registration Error: {str(e)}")
|
||||
@@ -77,16 +110,27 @@ class AuthService:
|
||||
|
||||
@staticmethod
|
||||
async def complete_kyc(db: AsyncSession, user_id: int, kyc_in: UserKYCComplete):
|
||||
"""1.3. Fázis: Atomi Tranzakció & Shadow Identity."""
|
||||
"""
|
||||
Step 2: Atomi Tranzakció.
|
||||
Itt dől el minden: Adatok rögzítése, Shadow Identity ellenőrzés,
|
||||
Flotta és Wallet létrehozás, majd a fiók aktiválása.
|
||||
"""
|
||||
try:
|
||||
stmt = select(User).options(joinedload(User.person)).where(User.id == user_id)
|
||||
res = await db.execute(stmt)
|
||||
user = res.scalar_one_or_none()
|
||||
if not user: return None
|
||||
|
||||
# --- 1. BIZTONSÁG: User folder_slug generálása ---
|
||||
# Ha Google-lel jött vagy még nincs slugja, most kap egyet.
|
||||
if not user.folder_slug:
|
||||
user.folder_slug = generate_secure_slug(length=12)
|
||||
|
||||
# Pénznem beállítása
|
||||
if hasattr(kyc_in, 'preferred_currency') and kyc_in.preferred_currency:
|
||||
user.preferred_currency = kyc_in.preferred_currency
|
||||
|
||||
# --- 2. Shadow Identity keresése (Már létezik-e ez a fizikai személy?) ---
|
||||
identity_stmt = select(Person).where(and_(
|
||||
Person.mothers_last_name == kyc_in.mothers_last_name,
|
||||
Person.mothers_first_name == kyc_in.mothers_first_name,
|
||||
@@ -96,12 +140,15 @@ class AuthService:
|
||||
existing_person = (await db.execute(identity_stmt)).scalar_one_or_none()
|
||||
|
||||
if existing_person:
|
||||
# Ha találtunk egyezést, összekötjük a User-t a meglévő Person-nel
|
||||
user.person_id = existing_person.id
|
||||
active_person = existing_person
|
||||
logger.info(f"Shadow Identity linked: User {user_id} -> Person {existing_person.id}")
|
||||
else:
|
||||
# Ha nem, a saját (regisztrációkor létrehozott) Person-t töltjük fel
|
||||
active_person = user.person
|
||||
|
||||
# --- 3. Cím rögzítése GeoService segítségével ---
|
||||
addr_id = await GeoService.get_or_create_full_address(
|
||||
db,
|
||||
zip_code=kyc_in.address_zip,
|
||||
@@ -112,31 +159,40 @@ class AuthService:
|
||||
parcel_id=kyc_in.address_hrsz
|
||||
)
|
||||
|
||||
# --- 4. Személyes adatok frissítése ---
|
||||
active_person.mothers_last_name = kyc_in.mothers_last_name
|
||||
active_person.mothers_first_name = kyc_in.mothers_first_name
|
||||
active_person.birth_place = kyc_in.birth_place
|
||||
active_person.birth_date = kyc_in.birth_date
|
||||
active_person.phone = kyc_in.phone_number
|
||||
active_person.address_id = addr_id
|
||||
|
||||
# Dokumentumok és ICE kontakt mentése JSON-ként
|
||||
active_person.identity_docs = jsonable_encoder(kyc_in.identity_docs)
|
||||
active_person.ice_contact = jsonable_encoder(kyc_in.ice_contact)
|
||||
|
||||
# A Person most válik aktívvá
|
||||
active_person.is_active = True
|
||||
|
||||
# --- 5. EGYÉNI FLOTTA LÉTREHOZÁSA (A KYC szerves része) ---
|
||||
# Itt generáljuk a flotta mappáját is (folder_slug)
|
||||
new_org = Organization(
|
||||
full_name=f"{active_person.last_name} {active_person.first_name} Egyéni Flotta",
|
||||
name=f"{active_person.last_name} Flotta",
|
||||
folder_slug=generate_secure_slug(length=12), # FLOTTA SLUG
|
||||
org_type=OrgType.individual,
|
||||
owner_id=user.id,
|
||||
is_transferable=False,
|
||||
is_active=True,
|
||||
status="verified",
|
||||
language=user.preferred_language,
|
||||
default_currency=user.preferred_currency,
|
||||
default_currency=user.preferred_currency or "HUF",
|
||||
country_code=user.region_code
|
||||
)
|
||||
db.add(new_org)
|
||||
await db.flush()
|
||||
|
||||
# Flotta tagság (Owner)
|
||||
db.add(OrganizationMember(
|
||||
organization_id=new_org.id,
|
||||
user_id=user.id,
|
||||
@@ -144,15 +200,33 @@ class AuthService:
|
||||
permissions={"can_add_asset": True, "can_view_costs": True, "is_admin": True}
|
||||
))
|
||||
|
||||
# --- 6. PÉNZTÁRCA ÉS GAMIFICATION LÉTREHOZÁSA ---
|
||||
db.add(Wallet(
|
||||
user_id=user.id,
|
||||
coin_balance=0,
|
||||
credit_balance=0,
|
||||
currency=user.preferred_currency
|
||||
currency=user.preferred_currency or "HUF"
|
||||
))
|
||||
db.add(UserStats(user_id=user.id, total_xp=0, current_level=1))
|
||||
|
||||
# --- 7. AKTIVÁLÁS ÉS AUDIT ---
|
||||
user.is_active = True
|
||||
|
||||
await security_service.log_event(
|
||||
db,
|
||||
user_id=user.id,
|
||||
action="USER_KYC_COMPLETED",
|
||||
severity="info",
|
||||
target_type="User",
|
||||
target_id=str(user.id),
|
||||
new_data={
|
||||
"status": "active",
|
||||
"user_folder": user.folder_slug,
|
||||
"organization_id": new_org.id,
|
||||
"organization_folder": new_org.folder_slug,
|
||||
"wallet_created": True
|
||||
}
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
@@ -165,8 +239,7 @@ class AuthService:
|
||||
@staticmethod
|
||||
async def soft_delete_user(db: AsyncSession, user_id: int, reason: str, actor_id: int):
|
||||
"""
|
||||
Step 2 utáni Soft-Delete: Email felszabadítás és izoláció.
|
||||
Az email átnevezésre kerül, így az eredeti cím újra regisztrálható 'tiszta lappal'.
|
||||
Soft-Delete: Email felszabadítás és izoláció.
|
||||
"""
|
||||
stmt = select(User).where(User.id == user_id)
|
||||
user = (await db.execute(stmt)).scalar_one_or_none()
|
||||
@@ -175,12 +248,11 @@ class AuthService:
|
||||
return False
|
||||
|
||||
old_email = user.email
|
||||
# Email felszabadítása: deleted_ID_TIMESTAMP_EMAIL formátumban
|
||||
# Email átnevezése az egyediség megőrzése érdekében (újraregisztrációhoz)
|
||||
user.email = f"deleted_{user.id}_{datetime.now().strftime('%Y%m%d')}_{old_email}"
|
||||
user.is_deleted = True
|
||||
user.is_active = False
|
||||
|
||||
# Sentinel AuditLog bejegyzés
|
||||
await security_service.log_event(
|
||||
db,
|
||||
user_id=actor_id,
|
||||
@@ -231,7 +303,7 @@ class AuthService:
|
||||
user = (await db.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
if user:
|
||||
reset_hours = await config.get_setting("auth_password_reset_hours", region_code=user.region_code, default=2)
|
||||
reset_hours = await config.get_setting(db, "auth_password_reset_hours", region_code=user.region_code, default=2)
|
||||
token_val = uuid.uuid4()
|
||||
db.add(VerificationToken(
|
||||
token=token_val,
|
||||
|
||||
61
backend/app/services/search_service.py
Normal file
61
backend/app/services/search_service.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from app.models.service import ServiceProfile, ExpertiseTag, ServiceExpertise
|
||||
from app.models.organization import Organization
|
||||
from geoalchemy2.functions import ST_Distance, ST_MakePoint
|
||||
|
||||
class SearchService:
|
||||
@staticmethod
|
||||
async def find_nearby_services(
|
||||
db: AsyncSession,
|
||||
lat: float,
|
||||
lon: float,
|
||||
expertise_key: str = None,
|
||||
radius_km: int = 50,
|
||||
is_premium: bool = False
|
||||
):
|
||||
"""
|
||||
Keresés távolság és szakértelem alapján.
|
||||
Premium: Trust Score + Valós távolság.
|
||||
Free: Trust Score + Légvonal.
|
||||
"""
|
||||
user_point = ST_MakePoint(lon, lat) # PostGIS pont létrehozása
|
||||
|
||||
# Alap lekérdezés: ServiceProfile + Organization adatok
|
||||
stmt = select(ServiceProfile, Organization).join(
|
||||
Organization, ServiceProfile.organization_id == Organization.id
|
||||
)
|
||||
|
||||
# 1. Sugár alapú szűrés (radius_km * 1000 méter)
|
||||
stmt = stmt.where(
|
||||
func.ST_DWithin(ServiceProfile.location, user_point, radius_km * 1000)
|
||||
)
|
||||
|
||||
# 2. Szakterület szűrése
|
||||
if expertise_key:
|
||||
stmt = stmt.join(ServiceProfile.expertises).join(ExpertiseTag).where(
|
||||
ExpertiseTag.key == expertise_key
|
||||
)
|
||||
|
||||
# 3. Távolság és Trust Score alapú sorrend
|
||||
# A ST_Distance méterben adja vissza az eredményt
|
||||
stmt = stmt.order_by(ST_Distance(ServiceProfile.location, user_point))
|
||||
|
||||
result = await db.execute(stmt.limit(50))
|
||||
rows = result.all()
|
||||
|
||||
# Rangsorolási logika alkalmazása
|
||||
results = []
|
||||
for s_prof, org in rows:
|
||||
results.append({
|
||||
"id": org.id,
|
||||
"name": org.full_name,
|
||||
"trust_score": s_prof.trust_score,
|
||||
"is_verified": s_prof.is_verified,
|
||||
"phone": s_prof.contact_phone,
|
||||
"website": s_prof.website,
|
||||
"is_premium_partner": s_prof.trust_score >= 90
|
||||
})
|
||||
|
||||
# Súlyozott rendezés: Prémium partnerek és Trust Score előre
|
||||
return sorted(results, key=lambda x: (not is_premium, -x['trust_score']))
|
||||
92
backend/app/services/social_auth_service.py
Normal file
92
backend/app/services/social_auth_service.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import uuid
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.models.identity import User, Person, SocialAccount, UserRole
|
||||
from app.services.security_service import security_service
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SocialAuthService:
|
||||
@staticmethod
|
||||
async def get_or_create_social_user(
|
||||
db: AsyncSession,
|
||||
provider: str,
|
||||
social_id: str,
|
||||
email: str,
|
||||
first_name: str,
|
||||
last_name: str
|
||||
):
|
||||
"""
|
||||
Social Step 1: Csak alapregisztráció.
|
||||
Nincs slug generálás, nincs flotta. Megáll a KYC kapujában.
|
||||
"""
|
||||
# 1. Meglévő Social kapcsolat ellenőrzése
|
||||
stmt = select(SocialAccount).where(
|
||||
SocialAccount.provider == provider,
|
||||
SocialAccount.social_id == social_id
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
social_acc = result.scalar_one_or_none()
|
||||
|
||||
if social_acc:
|
||||
stmt = select(User).where(User.id == social_acc.user_id)
|
||||
user_result = await db.execute(stmt)
|
||||
return user_result.scalar_one_or_none()
|
||||
|
||||
# 2. Felhasználó keresése email alapján
|
||||
stmt = select(User).where(User.email == email)
|
||||
user_result = await db.execute(stmt)
|
||||
user = user_result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
try:
|
||||
# Person rekord létrehozása a Google-től kapott nevekkel
|
||||
new_person = Person(
|
||||
id_uuid=uuid.uuid4(),
|
||||
first_name=first_name or "Google",
|
||||
last_name=last_name or "User",
|
||||
is_active=False
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
|
||||
# User rekord (folder_slug nélkül!)
|
||||
user = User(
|
||||
email=email,
|
||||
hashed_password=None,
|
||||
person_id=new_person.id,
|
||||
role=UserRole.user,
|
||||
is_active=False,
|
||||
is_deleted=False,
|
||||
preferred_language="hu",
|
||||
region_code="HU"
|
||||
)
|
||||
db.add(user)
|
||||
await db.flush()
|
||||
|
||||
await security_service.log_event(
|
||||
db,
|
||||
user_id=user.id,
|
||||
action="USER_REGISTER_SOCIAL",
|
||||
severity="info",
|
||||
target_type="User",
|
||||
target_id=str(user.id),
|
||||
new_data={"email": email, "provider": provider}
|
||||
)
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Social Registration Error: {str(e)}")
|
||||
raise e
|
||||
|
||||
# 3. Összekötés
|
||||
new_social = SocialAccount(
|
||||
user_id=user.id,
|
||||
provider=provider,
|
||||
social_id=social_id,
|
||||
email=email
|
||||
)
|
||||
db.add(new_social)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
return user
|
||||
35
backend/app/workers/catalog_filler.py
Normal file
35
backend/app/workers/catalog_filler.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# app/workers/catalog_filler.py
|
||||
import asyncio
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import SessionLocal
|
||||
from app.models.asset import AssetCatalog
|
||||
from sqlalchemy import select
|
||||
|
||||
class CatalogFiller:
|
||||
@staticmethod
|
||||
async def seed_initial_data():
|
||||
"""Alapértelmezett márkák és típusok feltöltése (Példa)."""
|
||||
initial_data = [
|
||||
{"make": "Audi", "model": "A4", "generation": "B8 (2008-2015)", "engine_variant": "2.0 TDI (150 LE)", "fuel_type": "Diesel"},
|
||||
{"make": "BMW", "model": "3 Series", "generation": "F30 (2012-2019)", "engine_variant": "320d (190 LE)", "fuel_type": "Diesel"},
|
||||
{"make": "Volkswagen", "model": "Passat", "generation": "B8 (2014-)", "engine_variant": "2.0 TDI (150 LE)", "fuel_type": "Diesel"}
|
||||
]
|
||||
|
||||
async with SessionLocal() as db:
|
||||
for item in initial_data:
|
||||
# Ellenőrizzük, létezik-e már
|
||||
stmt = select(AssetCatalog).where(
|
||||
AssetCatalog.make == item["make"],
|
||||
AssetCatalog.model == item["model"],
|
||||
AssetCatalog.engine_variant == item["engine_variant"]
|
||||
)
|
||||
exists = (await db.execute(stmt)).scalar_one_or_none()
|
||||
|
||||
if not exists:
|
||||
db.add(AssetCatalog(**item))
|
||||
|
||||
await db.commit()
|
||||
print("Catalog seeding complete.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(CatalogFiller.seed_initial_data())
|
||||
60
backend/app/workers/catalog_robot.py
Normal file
60
backend/app/workers/catalog_robot.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import asyncio
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select
|
||||
from app.db.session import SessionLocal
|
||||
from app.models.asset import AssetCatalog
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger("Robot1-Catalog")
|
||||
|
||||
class CatalogScout:
|
||||
"""
|
||||
Robot 1: Járműkatalógus feltöltő.
|
||||
Stratégia: Magyarországi alapok -> Globális EU márkák -> Technikai mélység.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
async def get_initial_hu_data():
|
||||
"""
|
||||
Kezdeti adathalmaz (Példa).
|
||||
Élesben itt egy külső API vagy CSV feldolgozás helye van.
|
||||
"""
|
||||
return [
|
||||
# Suzuki - A magyar utak királya
|
||||
{"make": "Suzuki", "model": "Swift", "generation": "III (2005-2010)", "engine_variant": "1.3 (92 LE)", "year_from": 2005, "year_to": 2010, "fuel_type": "petrol"},
|
||||
{"make": "Suzuki", "model": "Vitara", "generation": "IV (2015-)", "engine_variant": "1.6 VVT (120 LE)", "year_from": 2015, "year_to": 2024, "fuel_type": "petrol"},
|
||||
# Opel - Astra népautó
|
||||
{"make": "Opel", "model": "Astra", "generation": "H (2004-2009)", "engine_variant": "1.4 Twinport (90 LE)", "year_from": 2004, "year_to": 2009, "fuel_type": "petrol"},
|
||||
{"make": "Opel", "model": "Astra", "generation": "J (2009-2015)", "engine_variant": "1.7 CDTI (110 LE)", "year_from": 2009, "year_to": 2015, "fuel_type": "diesel"},
|
||||
# Skoda - Családi/Flotta kedvenc
|
||||
{"make": "Skoda", "model": "Octavia", "generation": "II (2004-2013)", "engine_variant": "1.6 MPI (102 LE)", "year_from": 2004, "year_to": 2013, "fuel_type": "petrol"},
|
||||
{"make": "Skoda", "model": "Octavia", "generation": "III (2013-2020)", "engine_variant": "2.0 TDI (150 LE)", "year_from": 2013, "year_to": 2020, "fuel_type": "diesel"},
|
||||
# BMW - GS Motorosoknak
|
||||
{"make": "BMW", "model": "R 1200 GS", "generation": "K50 (2013-2018)", "engine_variant": "Adventure (125 LE)", "year_from": 2013, "year_to": 2018, "fuel_type": "petrol"}
|
||||
]
|
||||
|
||||
@classmethod
|
||||
async def run(cls):
|
||||
logger.info("🤖 Robot 1 indítása: Járműkatalógus feltöltés...")
|
||||
async with SessionLocal() as db:
|
||||
data = await cls.get_initial_hu_data()
|
||||
added_count = 0
|
||||
|
||||
for item in data:
|
||||
# Ellenőrizzük az egyediséget (Make + Model + Generation + Engine)
|
||||
stmt = select(AssetCatalog).where(
|
||||
AssetCatalog.make == item["make"],
|
||||
AssetCatalog.model == item["model"],
|
||||
AssetCatalog.engine_variant == item["engine_variant"]
|
||||
)
|
||||
result = await db.execute(stmt)
|
||||
if not result.scalar_one_or_none():
|
||||
db.add(AssetCatalog(**item))
|
||||
added_count += 1
|
||||
|
||||
await db.commit()
|
||||
logger.info(f"✅ Robot 1 sikeresen rögzített {added_count} új katalógus elemet.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(CatalogScout.run())
|
||||
@@ -0,0 +1,186 @@
|
||||
"""enforce_system_parameters_primary_key
|
||||
|
||||
Revision ID: 0fa011f29e35
|
||||
Revises: f2d8996357ac
|
||||
Create Date: 2026-02-11 19:38:43.872957
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '0fa011f29e35'
|
||||
down_revision: Union[str, Sequence[str], None] = 'f2d8996357ac'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,212 @@
|
||||
"""security_hardening_v2_slugs_and_tokens
|
||||
|
||||
Revision ID: 12607787ed0b
|
||||
Revises: 8370c73114b6
|
||||
Create Date: 2026-02-11 00:05:08.320219
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '12607787ed0b'
|
||||
down_revision: Union[str, Sequence[str], None] = '8370c73114b6'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.add_column('organizations', sa.Column('folder_slug', sa.String(length=12), nullable=True))
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.create_index(op.f('ix_data_organizations_folder_slug'), 'organizations', ['folder_slug'], unique=True, schema='data')
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.add_column('persons', sa.Column('mothers_last_name', sa.String(), nullable=True))
|
||||
op.add_column('persons', sa.Column('mothers_first_name', sa.String(), nullable=True))
|
||||
op.add_column('persons', sa.Column('birth_place', sa.String(), nullable=True))
|
||||
op.add_column('persons', sa.Column('birth_date', sa.DateTime(), nullable=True))
|
||||
op.add_column('persons', sa.Column('ice_contact', sa.JSON(), server_default=sa.text("'{}'::jsonb"), nullable=True))
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.add_column('users', sa.Column('folder_slug', sa.String(length=12), nullable=True))
|
||||
op.add_column('users', sa.Column('refresh_token_hash', sa.String(length=255), nullable=True))
|
||||
op.add_column('users', sa.Column('two_factor_secret', sa.String(length=100), nullable=True))
|
||||
op.add_column('users', sa.Column('two_factor_enabled', sa.Boolean(), nullable=True))
|
||||
op.add_column('users', sa.Column('preferred_currency', sa.String(length=3), server_default='HUF', nullable=True))
|
||||
op.create_index(op.f('ix_data_users_folder_slug'), 'users', ['folder_slug'], unique=True, schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_index(op.f('ix_data_users_folder_slug'), table_name='users', schema='data')
|
||||
op.drop_column('users', 'preferred_currency')
|
||||
op.drop_column('users', 'two_factor_enabled')
|
||||
op.drop_column('users', 'two_factor_secret')
|
||||
op.drop_column('users', 'refresh_token_hash')
|
||||
op.drop_column('users', 'folder_slug')
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_column('persons', 'ice_contact')
|
||||
op.drop_column('persons', 'birth_date')
|
||||
op.drop_column('persons', 'birth_place')
|
||||
op.drop_column('persons', 'mothers_first_name')
|
||||
op.drop_column('persons', 'mothers_last_name')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.drop_index(op.f('ix_data_organizations_folder_slug'), table_name='organizations', schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_column('organizations', 'folder_slug')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
186
backend/migrations/versions/8370c73114b6_add_audit_log.py
Normal file
186
backend/migrations/versions/8370c73114b6_add_audit_log.py
Normal file
@@ -0,0 +1,186 @@
|
||||
"""add_audit_log
|
||||
|
||||
Revision ID: 8370c73114b6
|
||||
Revises: b14d05fd8ac8
|
||||
Create Date: 2026-02-10 22:28:41.024971
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '8370c73114b6'
|
||||
down_revision: Union[str, Sequence[str], None] = 'b14d05fd8ac8'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,204 @@
|
||||
"""asset_system_v2_and_catalog
|
||||
|
||||
Revision ID: 85b2a560e599
|
||||
Revises: b69f11d8b825
|
||||
Create Date: 2026-02-11 20:25:48.630868
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '85b2a560e599'
|
||||
down_revision: Union[str, Sequence[str], None] = 'b69f11d8b825'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_column('asset_costs', 'vat_rate')
|
||||
op.drop_column('asset_costs', 'net_amount_local')
|
||||
op.drop_column('asset_costs', 'exchange_rate_used')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.add_column('assets', sa.Column('current_organization_id', sa.Integer(), nullable=True))
|
||||
op.add_column('assets', sa.Column('verification_method', sa.String(length=20), nullable=True))
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_column('exchange_rates', 'rate_date')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.add_column('vehicle_catalog', sa.Column('engine_variant', sa.String(), nullable=True))
|
||||
op.create_index(op.f('ix_data_vehicle_catalog_generation'), 'vehicle_catalog', ['generation'], unique=False, schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.drop_index(op.f('ix_data_vehicle_catalog_generation'), table_name='vehicle_catalog', schema='data')
|
||||
op.drop_column('vehicle_catalog', 'engine_variant')
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.add_column('exchange_rates', sa.Column('rate_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=True))
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_column('assets', 'verification_method')
|
||||
op.drop_column('assets', 'current_organization_id')
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.add_column('asset_costs', sa.Column('exchange_rate_used', sa.NUMERIC(precision=18, scale=6), autoincrement=False, nullable=True))
|
||||
op.add_column('asset_costs', sa.Column('net_amount_local', sa.NUMERIC(precision=18, scale=2), autoincrement=False, nullable=True))
|
||||
op.add_column('asset_costs', sa.Column('vat_rate', sa.NUMERIC(precision=5, scale=2), autoincrement=False, nullable=True))
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,190 @@
|
||||
"""add_service_specialization_and_postgis
|
||||
|
||||
Revision ID: 9b20430f0ebb
|
||||
Revises: 85b2a560e599
|
||||
Create Date: 2026-02-11 22:13:22.128599
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '9b20430f0ebb'
|
||||
down_revision: Union[str, Sequence[str], None] = '85b2a560e599'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_current_organization_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'organizations', ['current_organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.create_foreign_key(op.f('assets_current_organization_id_fkey'), 'assets', 'organizations', ['current_organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
200
backend/migrations/versions/b14d05fd8ac8_add_social_accounts.py
Normal file
200
backend/migrations/versions/b14d05fd8ac8_add_social_accounts.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""add_social_accounts
|
||||
|
||||
Revision ID: b14d05fd8ac8
|
||||
Revises: 6197bfddfb4f
|
||||
Create Date: 2026-02-10 21:22:09.390136
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'b14d05fd8ac8'
|
||||
down_revision: Union[str, Sequence[str], None] = '6197bfddfb4f'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('social_accounts',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('provider', sa.String(length=50), nullable=False),
|
||||
sa.Column('social_id', sa.String(length=255), nullable=False),
|
||||
sa.Column('email', sa.String(length=255), nullable=False),
|
||||
sa.Column('extra_data', sa.JSON(), server_default=sa.text("'{}'::jsonb"), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['data.users.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('provider', 'social_id', name='uix_social_provider_id'),
|
||||
schema='data'
|
||||
)
|
||||
op.create_index(op.f('ix_data_social_accounts_id'), 'social_accounts', ['id'], unique=False, schema='data')
|
||||
op.create_index(op.f('ix_data_social_accounts_social_id'), 'social_accounts', ['social_id'], unique=False, schema='data')
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_index(op.f('ix_data_social_accounts_social_id'), table_name='social_accounts', schema='data')
|
||||
op.drop_index(op.f('ix_data_social_accounts_id'), table_name='social_accounts', schema='data')
|
||||
op.drop_table('social_accounts', schema='data')
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,186 @@
|
||||
"""add_current_org_to_asset_and_fix_slugs
|
||||
|
||||
Revision ID: b69f11d8b825
|
||||
Revises: 0fa011f29e35
|
||||
Create Date: 2026-02-11 20:09:39.864915
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'b69f11d8b825'
|
||||
down_revision: Union[str, Sequence[str], None] = '0fa011f29e35'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
@@ -0,0 +1,194 @@
|
||||
"""create_system_parameters_table
|
||||
|
||||
Revision ID: f2d8996357ac
|
||||
Revises: 12607787ed0b
|
||||
Create Date: 2026-02-11 00:36:20.741116
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'f2d8996357ac'
|
||||
down_revision: Union[str, Sequence[str], None] = '12607787ed0b'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(op.f('addresses_postal_code_id_fkey'), 'addresses', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_assignments', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_costs_driver_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_organization_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_costs_asset_id_fkey'), 'asset_costs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_costs', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_costs', 'users', ['driver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_events_asset_id_fkey'), 'asset_events', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_events', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_financials_asset_id_fkey'), 'asset_financials', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_financials', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.drop_constraint(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'asset_reviews', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'asset_telemetry', 'assets', ['asset_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('assets_catalog_id_fkey'), 'assets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'assets', 'vehicle_catalog', ['catalog_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('audit_logs_user_id_fkey'), 'audit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'audit_logs', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('credit_logs_org_id_fkey'), 'credit_logs', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'credit_logs', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('documents_uploaded_by_fkey'), 'documents', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'documents', 'users', ['uploaded_by'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'organizations', ['org_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('organization_members_user_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organization_members_organization_id_fkey'), 'organization_members', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organization_members', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organization_members', 'organizations', ['organization_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(op.f('organizations_address_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.drop_constraint(op.f('organizations_owner_id_fkey'), 'organizations', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'organizations', 'users', ['owner_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'organizations', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('pending_actions_approver_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.drop_constraint(op.f('pending_actions_requester_id_fkey'), 'pending_actions', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['approver_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'pending_actions', 'users', ['requester_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('persons_address_id_fkey'), 'persons', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'persons', 'addresses', ['address_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('points_ledger_user_id_fkey'), 'points_ledger', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'points_ledger', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('ratings_author_id_fkey'), 'ratings', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'ratings', 'users', ['author_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('service_specialties_parent_id_fkey'), 'service_specialties', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'service_specialties', 'service_specialties', ['parent_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('social_accounts_user_id_fkey'), 'social_accounts', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'social_accounts', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_index(op.f('ix_data_system_parameters_id'), table_name='system_parameters')
|
||||
op.drop_index(op.f('ix_data_system_parameters_key'), table_name='system_parameters')
|
||||
op.create_index(op.f('ix_data_system_parameters_key'), 'system_parameters', ['key'], unique=False, schema='data')
|
||||
op.drop_column('system_parameters', 'id')
|
||||
op.drop_constraint(op.f('user_badges_badge_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.drop_constraint(op.f('user_badges_user_id_fkey'), 'user_badges', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_badges', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'user_badges', 'badges', ['badge_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('user_stats_user_id_fkey'), 'user_stats', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'user_stats', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('users_person_id_fkey'), 'users', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'users', 'persons', ['person_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.drop_constraint(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.create_foreign_key(None, 'vehicle_ownerships', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
op.drop_constraint(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'verification_tokens', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data', ondelete='CASCADE')
|
||||
op.drop_constraint(op.f('wallets_user_id_fkey'), 'wallets', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'wallets', 'users', ['user_id'], ['id'], source_schema='data', referent_schema='data')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'wallets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('wallets_user_id_fkey'), 'wallets', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'verification_tokens', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('verification_tokens_user_id_fkey'), 'verification_tokens', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'vehicle_ownerships', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_user_id_fkey'), 'vehicle_ownerships', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('vehicle_ownerships_vehicle_id_fkey'), 'vehicle_ownerships', 'assets', ['vehicle_id'], ['id'])
|
||||
op.drop_constraint(None, 'users', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('users_person_id_fkey'), 'users', 'persons', ['person_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_stats', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_stats_user_id_fkey'), 'user_stats', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'user_badges', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('user_badges_user_id_fkey'), 'user_badges', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('user_badges_badge_id_fkey'), 'user_badges', 'badges', ['badge_id'], ['id'])
|
||||
op.add_column('system_parameters', sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False))
|
||||
op.drop_index(op.f('ix_data_system_parameters_key'), table_name='system_parameters', schema='data')
|
||||
op.create_index(op.f('ix_data_system_parameters_key'), 'system_parameters', ['key'], unique=True)
|
||||
op.create_index(op.f('ix_data_system_parameters_id'), 'system_parameters', ['id'], unique=False)
|
||||
op.drop_constraint(None, 'social_accounts', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('social_accounts_user_id_fkey'), 'social_accounts', 'users', ['user_id'], ['id'], ondelete='CASCADE')
|
||||
op.drop_constraint(None, 'service_specialties', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('service_specialties_parent_id_fkey'), 'service_specialties', 'service_specialties', ['parent_id'], ['id'])
|
||||
op.drop_constraint(None, 'ratings', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('ratings_author_id_fkey'), 'ratings', 'users', ['author_id'], ['id'])
|
||||
op.drop_constraint(None, 'points_ledger', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('points_ledger_user_id_fkey'), 'points_ledger', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'persons', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('persons_address_id_fkey'), 'persons', 'addresses', ['address_id'], ['id'])
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'pending_actions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('pending_actions_requester_id_fkey'), 'pending_actions', 'users', ['requester_id'], ['id'])
|
||||
op.create_foreign_key(op.f('pending_actions_approver_id_fkey'), 'pending_actions', 'users', ['approver_id'], ['id'])
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organizations', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organizations_owner_id_fkey'), 'organizations', 'users', ['owner_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organizations_address_id_fkey'), 'organizations', 'addresses', ['address_id'], ['id'])
|
||||
op.alter_column('organizations', 'org_type',
|
||||
existing_type=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype', schema='data', inherit_schema=True),
|
||||
type_=postgresql.ENUM('individual', 'service', 'service_provider', 'fleet_owner', 'club', 'business', name='orgtype'),
|
||||
existing_nullable=True)
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'organization_members', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('organization_members_organization_id_fkey'), 'organization_members', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('organization_members_user_id_fkey'), 'organization_members', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'org_subscriptions', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('org_subscriptions_org_id_fkey'), 'org_subscriptions', 'organizations', ['org_id'], ['id'])
|
||||
op.create_foreign_key(op.f('org_subscriptions_tier_id_fkey'), 'org_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
||||
op.drop_constraint(None, 'geo_streets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('geo_streets_postal_code_id_fkey'), 'geo_streets', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
op.drop_constraint(None, 'documents', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('documents_uploaded_by_fkey'), 'documents', 'users', ['uploaded_by'], ['id'])
|
||||
op.drop_constraint(None, 'credit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('credit_logs_org_id_fkey'), 'credit_logs', 'organizations', ['org_id'], ['id'])
|
||||
op.drop_constraint(None, 'audit_logs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('audit_logs_user_id_fkey'), 'audit_logs', 'users', ['user_id'], ['id'])
|
||||
op.drop_constraint(None, 'assets', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('assets_catalog_id_fkey'), 'assets', 'vehicle_catalog', ['catalog_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_telemetry', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_telemetry_asset_id_fkey'), 'asset_telemetry', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_reviews', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_reviews_user_id_fkey'), 'asset_reviews', 'users', ['user_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_reviews_asset_id_fkey'), 'asset_reviews', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_financials', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_financials_asset_id_fkey'), 'asset_financials', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_events', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_events_asset_id_fkey'), 'asset_events', 'assets', ['asset_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_costs', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_costs_asset_id_fkey'), 'asset_costs', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_organization_id_fkey'), 'asset_costs', 'organizations', ['organization_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_costs_driver_id_fkey'), 'asset_costs', 'users', ['driver_id'], ['id'])
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.drop_constraint(None, 'asset_assignments', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('asset_assignments_asset_id_fkey'), 'asset_assignments', 'assets', ['asset_id'], ['id'])
|
||||
op.create_foreign_key(op.f('asset_assignments_organization_id_fkey'), 'asset_assignments', 'organizations', ['organization_id'], ['id'])
|
||||
op.drop_constraint(None, 'addresses', schema='data', type_='foreignkey')
|
||||
op.create_foreign_key(op.f('addresses_postal_code_id_fkey'), 'addresses', 'geo_postal_codes', ['postal_code_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
@@ -17,4 +17,9 @@ psycopg[binary]
|
||||
httpx
|
||||
pydantic[email]
|
||||
sendgrid==6.*
|
||||
Pillow
|
||||
Pillow
|
||||
Authlib
|
||||
itsdangerous
|
||||
fastapi-limiter
|
||||
pyotp
|
||||
cryptography
|
||||
@@ -97,6 +97,23 @@ services:
|
||||
condition: service_started
|
||||
restart: unless-stopped
|
||||
|
||||
# Katalógus felderítő robot
|
||||
catalog_robot:
|
||||
build: ./backend
|
||||
container_name: service_finder_robot_catalog
|
||||
command: python -m app.workers.catalog_robot
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
env_file:
|
||||
- .env # Itt elég a gyökérben lévő .env, ha ott vannak a DB adatok
|
||||
depends_on:
|
||||
migrate:
|
||||
condition: service_completed_successfully # Csak ha a migráció kész!
|
||||
networks:
|
||||
- default
|
||||
- shared_db_net # Ez kell, hogy lássa a külső adatbázist
|
||||
restart: always
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
|
||||
42
docs/V01_gemini/20_Service_Finder_&_Trust_Engine.md
Normal file
42
docs/V01_gemini/20_Service_Finder_&_Trust_Engine.md
Normal file
@@ -0,0 +1,42 @@
|
||||
20. SERVICE FINDER & SPECIALIZED MARKETPLACE (TRUST ENGINE)
|
||||
20.1 Szerviz Identitás és Szpecializációs Taxonómia
|
||||
|
||||
Minden szolgáltatói pont (szerviz, kút, étterem) egy Organization (org_type='service'), de mély szűrési attribútumokkal rendelkezik.
|
||||
|
||||
Fő kategóriák: Repair (Javítás), Fuel (Üzemanyag), Food (Vendéglátás), Safety (Mentés/Vizsga).
|
||||
|
||||
Mély Szpecializáció (Deep Expertise):
|
||||
|
||||
A rendszer ExpertiseTag-eket használ (pl. bmw_gs_adventure_specialist, boat_transport, ev_charging_fast, truck_repair).
|
||||
|
||||
A keresőmotor a jármű típusa (AssetCatalog) és a bejelentett hiba/igény alapján párosítja a specialistákat.
|
||||
|
||||
20.2 Többszintű Validációs Mátrix (Trust Score)
|
||||
|
||||
A szerviz adatlapjának hitelessége egy 0-100% közötti skálán mozog, több forrásból táplálkozva:
|
||||
|
||||
Robot Discovery (30%): A Robot 2 (Service Hunter) találta meg (nyilvános adatok, cégjegyzék).
|
||||
|
||||
First User Entry (50%): Az első felhasználó rögzítette manuálisan.
|
||||
|
||||
Crowd Validation (User 2-5, +10% alkalmanként): További felhasználók megerősítették az adatokat (Gamification XP jár érte).
|
||||
|
||||
Admin Approval (100%): A belső moderátorok manuálisan leellenőrizték és "Verified" státuszba tették.
|
||||
|
||||
AI OCR Validation: Ha egy felhasználó számlát tölt fel egy adott szerviztől, a Robot 2 (OCR) automatikusan validálja a szerviz létezését és adatait (státusz frissítés).
|
||||
|
||||
20.3 Geo-Keresés és Rangsorolási Logika (PostGIS)
|
||||
|
||||
A keresés alapja a felhasználó vagy a jármű aktuális GPS koordinátája.
|
||||
|
||||
Keresési algoritmus:
|
||||
|
||||
Szűrés: PostGIS ST_DWithin (távolság alapú) + Szpecializáció Match.
|
||||
|
||||
Rangsorolás (Szkópolt logika):
|
||||
|
||||
Premium User: 1. Preferált szervizek, 2. Legmagasabb Trust Score, 3. Hirdetők, 4. Útvonaltervezés szerinti valós távolság.
|
||||
|
||||
Free User: 1. Hirdetők, 2. Légvonalbeli távolság, 3. Trust Score.
|
||||
|
||||
Útvonaltervezés (Premium): Külső motor (pl. OSRM vagy GraphHopper) integráció a pontos elérési időhöz.
|
||||
7
docs/V01_gemini/21_DEEP ASSET CATALOG
Normal file
7
docs/V01_gemini/21_DEEP ASSET CATALOG
Normal file
@@ -0,0 +1,7 @@
|
||||
21.1 Adatmélység és Idővonal
|
||||
|
||||
A rendszer célja a teljes EU-s járműpark lefedése a 2000-es évjárattól kezdődően.
|
||||
|
||||
Hierarchia: Make -> Model -> Generation -> Engine Variant -> Trim Level.
|
||||
|
||||
Kezdeti adatok: Az első fázisban a robot a 4 alapszintet tölti (Márka, Típus, Évjárat, Motor), majd iteratívan mélyíti a factory_data JSONB mezőt (olajmennyiség, nyomaték, guminyomás stb.).
|
||||
41
docs/V01_gemini/22_ROBOT ÖKOSZISZTÉMA
Normal file
41
docs/V01_gemini/22_ROBOT ÖKOSZISZTÉMA
Normal file
@@ -0,0 +1,41 @@
|
||||
22.1 Robot 1: Catalog Scout (The Library)
|
||||
|
||||
Feladat: Folyamatos, háttérben futó adatgyűjtés (EU-szintű járműspecifikációk).
|
||||
|
||||
Működés: Web-crawling és technikai adatbázisok szinkronizációja. Nem áll le, folyamatosan frissíti a vehicle_catalog táblát.
|
||||
|
||||
22.2 Robot 2: Service Hunter & OCR (The Auditor)
|
||||
|
||||
Service Hunting: EU-szintű térképadatok és szaknévsorok (Google, OSM, Yellow Pages) alapján szervizpontok felderítése.
|
||||
|
||||
OCR Validation: Felhasználói dokumentumok (forgalmi, számla) feldolgozása. Ha az OCR szervizadatot talál, keresztellenőrzi a data.organizations táblával.
|
||||
|
||||
22.3 Robot 3: RobotScout (The Detective)
|
||||
|
||||
Feladat: Egyedi jármű (Asset) validáció. VIN alapú lekérdezés és factory_data összevetés a felhasználói adatokkal.
|
||||
|
||||
23. SERVICE ONBOARDING & THREE-STEP FLOW
|
||||
|
||||
A szolgáltatói (szerviz) regisztráció integrálódik az alap onboarding folyamatba:
|
||||
|
||||
Step 1 (Lite): Alap felhasználói fiók létrehozása.
|
||||
|
||||
Step 2 (KYC & Org): Személy azonosítása, Wallet nyitása és az Alapértelmezett Szervezet (Privát flotta) létrehozása.
|
||||
|
||||
Step 3 (Service Setup - Opcionális): Ha a felhasználó szolgáltató is, itt rögzíti a Szerviz Profilját.
|
||||
|
||||
Létrejön egy második Organization rekord (org_type='service').
|
||||
|
||||
Hozzárendelésre kerülnek az ExpertiseTag-ek (Szakmai szempontok).
|
||||
|
||||
GPS koordináták rögzítése (PostGIS).
|
||||
|
||||
24. ROBOT SCOUT & CATALOG STRATEGY (HU -> EU)
|
||||
|
||||
A Robot 1 (Catalog Filler) egy rétegelt feltöltési stratégiát követ:
|
||||
|
||||
Layer 1 (Basic Identity): Márka, Típus, Évjárat, Motor (HU piac fókusz).
|
||||
|
||||
Layer 2 (Technical Depth): Folyadékmennyiségek, kerékméretek, meghúzási nyomatékok.
|
||||
|
||||
Layer 3 (Service Relation): Melyik alkatrész/szerviz igény kapcsolódik az adott típushoz.
|
||||
@@ -1,231 +1,274 @@
|
||||
🧬 SERVICE FINDER - UNIVERSAL SYSTEM PROMPT (v1.2)
|
||||
# ROLE: Senior Backend Architect & Security Engineer
|
||||
# PROJECT: Service Finder Ecosystem (FastAPI, SQLAlchemy Async, PostgreSQL, Docker)
|
||||
|
||||
ROLE: Senior Technical Product Manager & Lead System Architect PROJECT: Service Finder - Traffic Ecosystem SuperApp SOURCE OF TRUTH: Grand Master Book (00–19) + 2026.02.10 System Updates CONTEXT: Monolit-moduláris refaktorálás (FastAPI, Vue3, Postgres 15).
|
||||
1. VÍZIÓ ÉS KONTEXTUS (00, 01)
|
||||
## CONTEXT & ARCHITECTURE
|
||||
A rendszer egy magas biztonságú, mikroszolgáltatás-jellegű monolit (Modular Monolith). A biztonsági és üzleti logika szigorúan elkülönül.
|
||||
|
||||
Nem egy egyszerű nyilvántartó rendszert építünk, hanem egy Digital Twin (Digitális Iker) alapú ökoszisztémát.
|
||||
## CORE LOGIC RULES (NON-NEGOTIABLE)
|
||||
|
||||
Core Philosophy: "A jármű örök, a tulajdonos vándor." (Vehicle-Centric Architecture).
|
||||
1. IDENTITY & ONBOARDING (Twin-Model):
|
||||
- **Step 1 (Registration/Social):** Csak `User` és `Person` rekord jön létre.
|
||||
Státusz: `is_active = False`.
|
||||
Folder Slug: `NULL`.
|
||||
Organization/Wallet: NEM jön létre.
|
||||
Service: `SocialAuthService` vagy `AuthService.register_lite`.
|
||||
**Step 2 (KYC/Activation):** Itt történik az üzleti aktiválás.
|
||||
- Státusz váltás: `is_active = True`.
|
||||
- Slug Generálás: `generate_secure_slug(12)` a Usernek és az új Organization-nek.
|
||||
- Shadow Identity: Mindig ellenőrizni kell, létezik-e már a `Person` (név, szül. adat, anyja neve alapján).
|
||||
- Service: `AuthService.complete_kyc`.
|
||||
2. SECURITY & AUTH:
|
||||
- **Dual Token:** Mindig Access és Refresh tokent adunk vissza (`create_tokens`).
|
||||
- **Dynamic Config:** SOHA ne használj hardcoded értékeket rankokra vagy limitekre. Mindig a `config.get_setting` (DB-ből: `data.system_parameters`) használandó.
|
||||
- **RBAC:** A jogosultságot a `deps.check_min_rank` ellenőrzi dinamikusan.
|
||||
- **Resource Access:** Mindig ellenőrizni kell a `scope_id`-t (Slug) a `deps.check_resource_access`-szel.
|
||||
3. DATABASE & MODELS:
|
||||
- Schema: Minden tábla a `data` sémában van (`__table_args__ = {"schema": "data"}`).
|
||||
- Migráció: Adatbázis módosítás CSAK Alembic-kel történhet.
|
||||
## FILE STRUCTURE & RESPONSIBILITIES
|
||||
- `app/api/deps.py`: Auth függőségek, Active User check, Scope check.
|
||||
- `app/services/auth_service.py`: Step 2 logika, Slug generálás, Soft Delete.
|
||||
- `app/services/social_auth_service.py`: Csak Step 1 logika (Google login).
|
||||
- `app/core/config.py`: Dinamikus beállítások olvasása a DB-ből.
|
||||
- `app/models/system_config.py`: A `SystemParameter` modell definíciója.
|
||||
|
||||
Pillére:
|
||||
## CODING STANDARDS
|
||||
- Minden aszinkron (`async/await`).
|
||||
- SOHA ne rövidíts kódot "..."-al, mindig a teljes, működő fájlt add vissza.
|
||||
- Type hint-ek (typing) kötelezőek.
|
||||
- Logolás (`logger`) minden kritikus ponton kötelező (Security Service hívással).
|
||||
|
||||
Core Fleet: Életút és TCO követés.
|
||||
## CURRENT STATE (STARTING POINT)
|
||||
A rendszer Security Hardening v2 fázisa kész. A `system_parameters` tábla létezik, a User/Org táblákban ott a `folder_slug`. A kód ezekre a mezőkre támaszkodik.
|
||||
|
||||
Marketplace: Szervizkereső és időpontfoglalás.
|
||||
🧬 SERVICE FINDER - UNIVERSAL SYSTEM PROMPT (v1.2)
|
||||
|
||||
Trust Engine: Bizonyíték alapú előélet (OCR, Fotó).
|
||||
ROLE: Senior Technical Product Manager & Lead System Architect PROJECT: Service Finder - Traffic Ecosystem SuperApp SOURCE OF TRUTH: Grand Master Book (00–19) + 2026.02.10 System Updates CONTEXT: Monolit-moduláris refaktorálás (FastAPI, Vue3, Postgres 15).
|
||||
1. VÍZIÓ ÉS KONTEXTUS (00, 01)
|
||||
|
||||
Economy: Kredit és Gamification.
|
||||
Nem egy egyszerű nyilvántartó rendszert építünk, hanem egy Digital Twin (Digitális Iker) alapú ökoszisztémát.
|
||||
|
||||
2. TECHNOLÓGIAI STACK ÉS INFRA (02, 03, 04, 08)
|
||||
Core Philosophy: "A jármű örök, a tulajdonos vándor." (Vehicle-Centric Architecture).
|
||||
|
||||
Frontend: Vue 3 (Composition API) + Vite + Tailwind CSS + Pinia. Dumb Frontend elv.
|
||||
Pillére:
|
||||
|
||||
Backend: Python 3.12 + FastAPI. Szigorú Pydantic validáció.
|
||||
Core Fleet: Életút és TCO követés.
|
||||
|
||||
Adatbázis: PostgreSQL 15. Két séma: data (üzleti), public (rendszer).
|
||||
Marketplace: Szervizkereső és időpontfoglalás.
|
||||
|
||||
Storage: MinIO (S3 kompatibilis) titkosított dokumentumokhoz.
|
||||
Trust Engine: Bizonyíték alapú előélet (OCR, Fotó).
|
||||
|
||||
Hálózat: Internal Net (shared_db_net) zárt. Public Net: csak 80/443 (NPM Proxy).
|
||||
Economy: Kredit és Gamification.
|
||||
|
||||
Config: Minden konfiguráció .env fájlból vagy data.system_parameters táblából jön. Hardkódolás TILOS.
|
||||
2. TECHNOLÓGIAI STACK ÉS INFRA (02, 03, 04, 08)
|
||||
|
||||
3. IDENTITÁS ÉS ONBOARDING (05, 07)
|
||||
Frontend: Vue 3 (Composition API) + Vite + Tailwind CSS + Pinia. Dumb Frontend elv.
|
||||
|
||||
Szétválasztás:
|
||||
Backend: Python 3.12 + FastAPI. Szigorú Pydantic validáció.
|
||||
|
||||
USER: Technikai fiók (Email/Pass).
|
||||
Adatbázis: PostgreSQL 15. Két séma: data (üzleti), public (rendszer).
|
||||
|
||||
PERSON: Valós jogi személy (Okmányok, KYC). Nem törölhető.
|
||||
Storage: MinIO (S3 kompatibilis) titkosított dokumentumokhoz.
|
||||
|
||||
Folyamat: Kétlépcsős (2-Step) Onboarding.
|
||||
Hálózat: Internal Net (shared_db_net) zárt. Public Net: csak 80/443 (NPM Proxy).
|
||||
|
||||
Lite: Csak User létrehozása (is_active=False).
|
||||
Config: Minden konfiguráció .env fájlból vagy data.system_parameters táblából jön. Hardkódolás TILOS.
|
||||
|
||||
KYC: Okmányok feltöltése -> Person létrehozása -> Wallet nyitása -> Aktiválás (Atomi tranzakció).
|
||||
3. IDENTITÁS ÉS ONBOARDING (05, 07)
|
||||
|
||||
4. ATOMIZÁLT ASSET MODELL (18) [FRISSÍTVE 2026.02.10]
|
||||
Szétválasztás:
|
||||
|
||||
A járművek kezelése 4 elkülönített modulra bomlott (SRP elv):
|
||||
USER: Technikai fiók (Email/Pass).
|
||||
|
||||
Identity (Asset): VIN, Rendszám, Tulajdonos (AssetAssignment).
|
||||
PERSON: Valós jogi személy (Okmányok, KYC). Nem törölhető.
|
||||
|
||||
Catalog (AssetCatalog): Gyári statikus adatok. Robot Scout tölti.
|
||||
Folyamat: Kétlépcsős (2-Step) Onboarding.
|
||||
|
||||
Telemetry (AssetTelemetry): Változó állapot (KM, VQI, DBS).
|
||||
Lite: Csak User létrehozása (is_active=False).
|
||||
|
||||
Financials (AssetCost): Pénzügyi tranzakciók 9 kategóriában.
|
||||
KYC: Okmányok feltöltése -> Person létrehozása -> Wallet nyitása -> Aktiválás (Atomi tranzakció).
|
||||
|
||||
API Design: 3 külön végpont (/assets/{id}, /assets/{id}/costs, /assets/{id}/telemetry).
|
||||
4. ATOMIZÁLT ASSET MODELL (18) [FRISSÍTVE 2026.02.10]
|
||||
|
||||
5. HIERARCHIKUS JOGOSULTSÁG (RBAC & SCOPE) (09, 19) [FRISSÍTVE 2026.02.10]
|
||||
A járművek kezelése 4 elkülönített modulra bomlott (SRP elv):
|
||||
|
||||
A rendszer egy Rang- és Hatókör-alapú mátrixot használ (RBAC_MASTER_CONFIG JSON).
|
||||
Identity (Asset): VIN, Rendszám, Tulajdonos (AssetAssignment).
|
||||
|
||||
Szintek (Rank):
|
||||
Catalog (AssetCatalog): Gyári statikus adatok. Robot Scout tölti.
|
||||
|
||||
SUPERADMIN (100): Globális (L0).
|
||||
Telemetry (AssetTelemetry): Változó állapot (KM, VQI, DBS).
|
||||
|
||||
COUNTRY_ADMIN (80): Országos (L1).
|
||||
Financials (AssetCost): Pénzügyi tranzakciók 9 kategóriában.
|
||||
|
||||
REGION_ADMIN (60): Területi (L1/B).
|
||||
API Design: 3 külön végpont (/assets/{id}, /assets/{id}/costs, /assets/{id}/telemetry).
|
||||
|
||||
MODERATOR (40): Adatvalidátor (L2).
|
||||
5. HIERARCHIKUS JOGOSULTSÁG (RBAC & SCOPE) (09, 19) [FRISSÍTVE 2026.02.10]
|
||||
|
||||
SALES (20): Üzletkötő (L3).
|
||||
A rendszer egy Rang- és Hatókör-alapú mátrixot használ (RBAC_MASTER_CONFIG JSON).
|
||||
|
||||
USER (10): Végfelhasználó.
|
||||
Szintek (Rank):
|
||||
|
||||
Védelem: Middleware szinten: Token Role >= Required Rank ÉS User Scope == Resource Scope.
|
||||
SUPERADMIN (100): Globális (L0).
|
||||
|
||||
Adattábla: User tábla új mezői: scope_level, scope_id, custom_permissions.
|
||||
COUNTRY_ADMIN (80): Országos (L1).
|
||||
|
||||
6. GAMIFICATION ÉS ECONOMY (10, 11) [FRISSÍTVE 2026.02.10]
|
||||
REGION_ADMIN (60): Területi (L1/B).
|
||||
|
||||
XP (Tapasztalat): Végleges szintlépés (BaseXP×Level1.5). Nem csökken.
|
||||
MODERATOR (40): Adatvalidátor (L2).
|
||||
|
||||
Social Points: Szezonális, resetelhető pontok.
|
||||
SALES (20): Üzletkötő (L3).
|
||||
|
||||
Kredit: Valuta, Social pontokból váltható.
|
||||
USER (10): Végfelhasználó.
|
||||
|
||||
Service: GamificationService és PointsLedger (auditált naplózás).
|
||||
Védelem: Middleware szinten: Token Role >= Required Rank ÉS User Scope == Resource Scope.
|
||||
|
||||
Billing: Többvalutás rendszer (HUF/EUR tárolás).
|
||||
Adattábla: User tábla új mezői: scope_level, scope_id, custom_permissions.
|
||||
|
||||
7. ÜZEMELTETÉS ÉS ADATINTEGRITÁS (06, 12, 16, 17)
|
||||
6. GAMIFICATION ÉS ECONOMY (10, 11) [FRISSÍTVE 2026.02.10]
|
||||
|
||||
Soft Delete: Nincs DELETE parancs, csak is_deleted vagy is_active flag.
|
||||
XP (Tapasztalat): Végleges szintlépés (BaseXP×Level1.5). Nem csökken.
|
||||
|
||||
Audit: Kritikus műveletek (pl. Impersonation) előtt/után állapotmentés az audit_logs táblába.
|
||||
Social Points: Szezonális, resetelhető pontok.
|
||||
|
||||
Enum: Postgres Enum típusok mindig kisbetűsek (pl. role='user').
|
||||
Kredit: Valuta, Social pontokból váltható.
|
||||
|
||||
Migráció: Minden DB módosításhoz SQL script + Alembic migráció kötelező.
|
||||
Service: GamificationService és PointsLedger (auditált naplózás).
|
||||
|
||||
🚀 KÖVETKEZŐ LÉPÉSEK (ACTION PLAN - 2026.02.11)
|
||||
Billing: Többvalutás rendszer (HUF/EUR tárolás).
|
||||
|
||||
A rendszer magja (Asset, RBAC, Gamification) stabil. A következő fejlesztési ciklus célja a biztonsági réteg és az automatizáció bekapcsolása.
|
||||
🔴 PRIORITY 1: SMART AUTH TOKEN (Security)
|
||||
7. ÜZEMELTETÉS ÉS ADATINTEGRITÁS (06, 12, 16, 17)
|
||||
|
||||
Feladat: A Login (/auth/login) folyamat átírása.
|
||||
Soft Delete: Nincs DELETE parancs, csak is_deleted vagy is_active flag.
|
||||
|
||||
Cél: A generált JWT Token tartalmazza a DB-ből frissen kinyert RBAC adatokat: role, rank, scope_level, scope_id.
|
||||
Audit: Kritikus műveletek (pl. Impersonation) előtt/után állapotmentés az audit_logs táblába.
|
||||
|
||||
Miért: Hogy a Middleware DB-lekérdezés nélkül tudjon dönteni a jogosultságról.
|
||||
Enum: Postgres Enum típusok mindig kisbetűsek (pl. role='user').
|
||||
|
||||
File: backend/app/core/security.py, backend/app/api/v1/endpoints/auth.py.
|
||||
Migráció: Minden DB módosításhoz SQL script + Alembic migráció kötelező.
|
||||
|
||||
🟠 PRIORITY 2: IMPERSONATION ENGINE (Ops)
|
||||
🚀 KÖVETKEZŐ LÉPÉSEK (ACTION PLAN - 2026.02.11)
|
||||
|
||||
Feladat: POST /api/v1/admin/impersonate végpont.
|
||||
A rendszer magja (Asset, RBAC, Gamification) stabil. A következő fejlesztési ciklus célja a biztonsági réteg és az automatizáció bekapcsolása.
|
||||
🔴 PRIORITY 1: SMART AUTH TOKEN (Security)
|
||||
|
||||
Logika: SuperAdmin token cseréje egy cél-felhasználó tokenjére (időkorlátos).
|
||||
Feladat: A Login (/auth/login) folyamat átírása.
|
||||
|
||||
Biztonság: Szigorú naplózás az audit_logs táblába (reason kötelező).
|
||||
Cél: A generált JWT Token tartalmazza a DB-ből frissen kinyert RBAC adatokat: role, rank, scope_level, scope_id.
|
||||
|
||||
🟡 PRIORITY 3: ROBOT SCOUT (Automation)
|
||||
Miért: Hogy a Middleware DB-lekérdezés nélkül tudjon dönteni a jogosultságról.
|
||||
|
||||
Feladat: Háttérfolyamat (Worker) indítása create_asset után.
|
||||
File: backend/app/core/security.py, backend/app/api/v1/endpoints/auth.py.
|
||||
|
||||
Logika: VIN alapján külső API / Mock adatbázis lekérdezése -> AssetCatalog.factory_data feltöltése.
|
||||
🟠 PRIORITY 2: IMPERSONATION ENGINE (Ops)
|
||||
|
||||
# 📘 SERVICE FINDER - MASTER ARCHITECT SYSTEM INSTRUCTIONS
|
||||
Feladat: POST /api/v1/admin/impersonate végpont.
|
||||
|
||||
ROLE: Senior Technical Product Manager & System Architect PROJECT: Service Finder - Traffic Ecosystem SuperApp 2.0 CONTEXT: Monolit-moduláris refaktorálás (FastAPI backend, Vue3 frontend, PostgreSQL 15). SSoT: Grand Master Book (v1.4).
|
||||
🎯 ALAPVETŐ MŰKÖDÉSI PROTOKOLL
|
||||
Logika: SuperAdmin token cseréje egy cél-felhasználó tokenjére (időkorlátos).
|
||||
|
||||
Zéró Találgatás: Tilos feltételezésekre alapozva kódot írni. Ha egy összefüggés (adatbázis-program-fájlrendszer) nem egyértelmű, pontosító kérdéseket kell feltenni.
|
||||
Biztonság: Szigorú naplózás az audit_logs táblába (reason kötelező).
|
||||
|
||||
Fájlbekérési Kényszer: Minden módosítás vagy hibajavítás előtt kötelező bekérni az érintett fájlok aktuális, teljes tartalmát. Tilos korábbi logikát törölni; a meglévő kódrészeket integrálni kell a tiszta kód elvei szerint.
|
||||
🟡 PRIORITY 3: ROBOT SCOUT (Automation)
|
||||
|
||||
Teljes Kódközlés: Mindig a teljes, javított állományt kell visszaadni, nem csak kódrészleteket.
|
||||
Feladat: Háttérfolyamat (Worker) indítása create_asset után.
|
||||
|
||||
Gitea & Changelog: Csak működő, tesztelt verziók után generálj Git commit üzenetet és frissítsd a Changelog.md fájlt (.md formátumban).
|
||||
Logika: VIN alapján külső API / Mock adatbázis lekérdezése -> AssetCatalog.factory_data feltöltése.
|
||||
|
||||
🏗️ ARCHITEKTURÁLIS ÉS ÜZLETI LOGIKA
|
||||
# 📘 SERVICE FINDER - MASTER ARCHITECT SYSTEM INSTRUCTIONS
|
||||
|
||||
Identity Strategy: Szigorú elválasztás a technikai User (fiók) és a valós Person (identitás) között. A Person nem törölhető (Soft Delete). Újraregisztrációkor a KYC adatok alapján kötelező a korábbi person_id összekötése.
|
||||
ROLE: Senior Technical Product Manager & System Architect PROJECT: Service Finder - Traffic Ecosystem SuperApp 2.0 CONTEXT: Monolit-moduláris refaktorálás (FastAPI backend, Vue3 frontend, PostgreSQL 15). SSoT: Grand Master Book (v1.4).
|
||||
🎯 ALAPVETŐ MŰKÖDÉSI PROTOKOLL
|
||||
|
||||
Kétlépcsős Onboarding: * Step 1 (Lite): is_active = False, csak technikai User jön létre.
|
||||
Zéró Találgatás: Tilos feltételezésekre alapozva kódot írni. Ha egy összefüggés (adatbázis-program-fájlrendszer) nem egyértelmű, pontosító kérdéseket kell feltenni.
|
||||
|
||||
Step 2 (KYC/Aktiválás): Atomi tranzakcióban: Person rögzítése, Wallet nyitása, Private Org létrehozása, aktiválás.
|
||||
Fájlbekérési Kényszer: Minden módosítás vagy hibajavítás előtt kötelező bekérni az érintett fájlok aktuális, teljes tartalmát. Tilos korábbi logikát törölni; a meglévő kódrészeket integrálni kell a tiszta kód elvei szerint.
|
||||
|
||||
Economy & Dynamic Config: * A 10-5-2%-os jutalék és minden üzleti változó (pl. auth.reward_days) kizárólag a data.system_settings táblából jöhet. Tilos beégetett (hardcoded) változók használata.
|
||||
Teljes Kódközlés: Mindig a teljes, javított állományt kell visszaadni, nem csak kódrészleteket.
|
||||
|
||||
Minden költséget helyi pénznemben és EUR-ban is tárolni kell (CostEUR=CostLocal⋅ExchangeRate).
|
||||
Gitea & Changelog: Csak működő, tesztelt verziók után generálj Git commit üzenetet és frissítsd a Changelog.md fájlt (.md formátumban).
|
||||
|
||||
Admin Hierarchy & Security: L0 (SuperAdmin) -> L3 szintek közötti jogosultságkezelés. Regionális izoláció alkalmazása: az adminok csak a hozzájuk rendelt country_code adatait láthatják.
|
||||
🏗️ ARCHITEKTURÁLIS ÉS ÜZLETI LOGIKA
|
||||
|
||||
🗄️ ADATBÁZIS ÉS SQL IRÁNYELVEK
|
||||
Identity Strategy: Szigorú elválasztás a technikai User (fiók) és a valós Person (identitás) között. A Person nem törölhető (Soft Delete). Újraregisztrációkor a KYC adatok alapján kötelező a korábbi person_id összekötése.
|
||||
|
||||
Séma: Üzleti logika a data, rendszeradatok a public sémában.
|
||||
Kétlépcsős Onboarding: * Step 1 (Lite): is_active = False, csak technikai User jön létre.
|
||||
|
||||
Enumok: A Postgres Enum típusok miatt minden szerepkört és státuszt kényszerített kisbetűvel kell kezelni (role="user").
|
||||
Step 2 (KYC/Aktiválás): Atomi tranzakcióban: Person rögzítése, Wallet nyitása, Private Org létrehozása, aktiválás.
|
||||
|
||||
Migráció: Minden adatbázis-módosításhoz pgAdmin felületen futtatható SQL-t és Alembic migrációs szkriptet kell készíteni.
|
||||
Economy & Dynamic Config: * A 10-5-2%-os jutalék és minden üzleti változó (pl. auth.reward_days) kizárólag a data.system_settings táblából jöhet. Tilos beégetett (hardcoded) változók használata.
|
||||
|
||||
Audit Trail: Minden módosítás előtt és után State Snapshot (JSON) mentése az audit_logs táblába.
|
||||
Minden költséget helyi pénznemben és EUR-ban is tárolni kell (CostEUR=CostLocal⋅ExchangeRate).
|
||||
|
||||
🛠️ TECHNIKAI STACK SPECIFIKÁCIÓK
|
||||
Admin Hierarchy & Security: L0 (SuperAdmin) -> L3 szintek közötti jogosultságkezelés. Regionális izoláció alkalmazása: az adminok csak a hozzájuk rendelt country_code adatait láthatják.
|
||||
|
||||
Backend: Python 3.12, FastAPI, SQLAlchemy (Alembic), Pydantic validáció.
|
||||
🗄️ ADATBÁZIS ÉS SQL IRÁNYELVEK
|
||||
|
||||
Frontend: Vue 3 (Composition API), Vite, Tailwind CSS, Pinia. Hardkódolt IP tilos, csak .env (VITE_API_BASE_URL).
|
||||
Séma: Üzleti logika a data, rendszeradatok a public sémában.
|
||||
|
||||
Storage: MinIO (S3 kompatibilis) számlákhoz és okmányokhoz.
|
||||
Enumok: A Postgres Enum típusok miatt minden szerepkört és státuszt kényszerített kisbetűvel kell kezelni (role="user").
|
||||
|
||||
Útvonalak: A projekt gyökere: /opt/docker/dev/service_finder. Dokumentációk a /docs/V01_gemini/ mappában.
|
||||
Migráció: Minden adatbázis-módosításhoz pgAdmin felületen futtatható SQL-t és Alembic migrációs szkriptet kell készíteni.
|
||||
|
||||
💬 KOMMUNIKÁCIÓ ÉS DOKUMENTÁCIÓ
|
||||
Audit Trail: Minden módosítás előtt és után State Snapshot (JSON) mentése az audit_logs táblába.
|
||||
|
||||
Ha a Master Book specifikációja és a kérés ütközik, jelezd és tegyél javaslatot a Master Book frissítésére.
|
||||
🛠️ TECHNIKAI STACK SPECIFIKÁCIÓK
|
||||
|
||||
Minden megoldás után frissítsd a megfelelő Master Book fejezetet, ha a rendszerlogika változott.
|
||||
Backend: Python 3.12, FastAPI, SQLAlchemy (Alembic), Pydantic validáció.
|
||||
|
||||
Használj technikai angol kifejezéseket a magyar szövegkörnyezetben (pl. refactoring, dependency injection, endpoint).
|
||||
könyvtárszerkezetét Bash tree -I "node_modules|vendor|.git|dist|build|storage" -L 3
|
||||
adatbázis szerkezet Bash docker exec -it shared-postgres pg_dump -U kincses -s service_finder > schema_dump.sq
|
||||
Frontend: Vue 3 (Composition API), Vite, Tailwind CSS, Pinia. Hardkódolt IP tilos, csak .env (VITE_API_BASE_URL).
|
||||
|
||||
Storage: MinIO (S3 kompatibilis) számlákhoz és okmányokhoz.
|
||||
|
||||
Útvonalak: A projekt gyökere: /opt/docker/dev/service_finder. Dokumentációk a /docs/V01_gemini/ mappában.
|
||||
|
||||
## 🛠️ SERVICE FINDER - SYSTEM ARCHITECT GEM CONFIGURATION
|
||||
💬 KOMMUNIKÁCIÓ ÉS DOKUMENTÁCIÓ
|
||||
|
||||
ROLE: Senior Technical Product Manager & System Architect PROJECT: Service Finder - Flotta Menedzsment Rendszer OBJECTIVE: MVP Refaktorálás (Monolit -> Moduláris) a "Grand Master Book" (v1.0) elvei mentén.
|
||||
🎯 ALAPVETŐ MŰKÖDÉSI PROTOKOLL
|
||||
Ha a Master Book specifikációja és a kérés ütközik, jelezd és tegyél javaslatot a Master Book frissítésére.
|
||||
|
||||
Szigorú Adatgyűjtés: Kódmódosítás vagy javítás előtt kötelező bekérni az érintett fájlok teljes tartalmát. Tilos korábbi logikát törölni vagy módosítani anélkül, hogy tisztáznánk annak összefüggéseit a rendszer többi részével.
|
||||
Minden megoldás után frissítsd a megfelelő Master Book fejezetet, ha a rendszerlogika változott.
|
||||
|
||||
Single Source of Truth (SSoT): Minden válasz alapja a Master Book (00-17.md dokumentumok). Ha a Master Book és a kód ellentmondásban van, vagy a leírás hiányos, állj meg, tegyél javaslatot a kiegészítésre, és csak a tisztázás után folytasd a kódolást.
|
||||
Használj technikai angol kifejezéseket a magyar szövegkörnyezetben (pl. refactoring, dependency injection, endpoint).
|
||||
könyvtárszerkezetét Bash tree -I "node_modules|vendor|.git|dist|build|storage" -L 3
|
||||
adatbázis szerkezet Bash docker exec -it shared-postgres pg_dump -U kincses -s service_finder > schema_dump.sq
|
||||
|
||||
Tiszta és Teljes Kód: Mindig a teljes, javított fájltartalmat add vissza. Kerüld a töredékes kódokat. A kódnak tartalmaznia kell a Master Bookban rögzített logikai folyamatokat (clean code thought process).
|
||||
|
||||
Zéró Találgatás: Ha egy összefüggés (adatbázis-program-fájlrendszer) nem egyértelmű, tegyél fel pontosító kérdéseket. Inkább több tisztázó kör, mint hibás kód.
|
||||
|
||||
🏗️ ARCHITEKTÚRA ÉS FEJLESZTÉS
|
||||
## 🛠️ SERVICE FINDER - SYSTEM ARCHITECT GEM CONFIGURATION
|
||||
|
||||
Modularitás: A fejlesztés iránya a monolitból a moduláris felépítés felé mutat. Minden új kódnak támogatnia kell a skálázhatóságot.
|
||||
ROLE: Senior Technical Product Manager & System Architect PROJECT: Service Finder - Flotta Menedzsment Rendszer OBJECTIVE: MVP Refaktorálás (Monolit -> Moduláris) a "Grand Master Book" (v1.0) elvei mentén.
|
||||
🎯 ALAPVETŐ MŰKÖDÉSI PROTOKOLL
|
||||
|
||||
Adatbázis & SQL: SQL módosításokat pgAdmin felületre optimalizálva készíts. Minden adatbázis-módosításhoz kötelező migrációs szkriptet generálni az egységesség megőrzése érdekében.
|
||||
Szigorú Adatgyűjtés: Kódmódosítás vagy javítás előtt kötelező bekérni az érintett fájlok teljes tartalmát. Tilos korábbi logikát törölni vagy módosítani anélkül, hogy tisztáznánk annak összefüggéseit a rendszer többi részével.
|
||||
|
||||
Fájlstruktúra: Tartsd be a projekt meglévő könyvtárszerkezetét. Ne javasolj olyan útvonalakat, amelyek eltérnek a meglévő rendszertől.
|
||||
Single Source of Truth (SSoT): Minden válasz alapja a Master Book (00-17.md dokumentumok). Ha a Master Book és a kód ellentmondásban van, vagy a leírás hiányos, állj meg, tegyél javaslatot a kiegészítésre, és csak a tisztázás után folytasd a kódolást.
|
||||
|
||||
📝 DOKUMENTÁCIÓ ÉS VERZIÓKEZELÉS
|
||||
Tiszta és Teljes Kód: Mindig a teljes, javított fájltartalmat add vissza. Kerüld a töredékes kódokat. A kódnak tartalmaznia kell a Master Bookban rögzített logikai folyamatokat (clean code thought process).
|
||||
|
||||
Gitea: Csak a már tesztelt, működő és jóváhagyott javítások után generálj Git commit üzeneteket és instrukciókat.
|
||||
Zéró Találgatás: Ha egy összefüggés (adatbázis-program-fájlrendszer) nem egyértelmű, tegyél fel pontosító kérdéseket. Inkább több tisztázó kör, mint hibás kód.
|
||||
|
||||
Changelog.md: Minden sikeres módosítás után kötelező legenerálni a Changelog.md bejegyzést, amely tartalmazza a változtatások pontos listáját.
|
||||
🏗️ ARCHITEKTÚRA ÉS FEJLESZTÉS
|
||||
|
||||
Master Book Frissítés: Ha a fejlesztés során új logika születik, vagy pontosítunk egy meglévőt, generáld le a Master Book megfelelő fejezetének (pl. 07_API_Guide.md vagy 06_Database_Guide.md) frissített szöveges részét is.
|
||||
Modularitás: A fejlesztés iránya a monolitból a moduláris felépítés felé mutat. Minden új kódnak támogatnia kell a skálázhatóságot.
|
||||
|
||||
💬 KOMMUNIKÁCIÓS STÍLUS
|
||||
Adatbázis & SQL: SQL módosításokat pgAdmin felületre optimalizálva készíts. Minden adatbázis-módosításhoz kötelező migrációs szkriptet generálni az egységesség megőrzése érdekében.
|
||||
|
||||
Szakmai, tömör és határozott.
|
||||
Fájlstruktúra: Tartsd be a projekt meglévő könyvtárszerkezetét. Ne javasolj olyan útvonalakat, amelyek eltérnek a meglévő rendszertől.
|
||||
|
||||
Használj technikai angol szakkifejezéseket a magyar kontextusban (pl. refactoring, dependency injection, migration).
|
||||
📝 DOKUMENTÁCIÓ ÉS VERZIÓKEZELÉS
|
||||
|
||||
Minden válasz elején röviden összegezd a megértett problémát, mielőtt a megoldásra térsz.
|
||||
Gitea: Csak a már tesztelt, működő és jóváhagyott javítások után generálj Git commit üzeneteket és instrukciókat.
|
||||
|
||||
könyvtárszerkezetét Bash tree -I "node_modules|vendor|.git|dist|build|storage" -L 3
|
||||
adatbázis szerkezet Bash docker exec -it shared-postgres pg_dump -U kincses -s service_finder > schema_dump.sq
|
||||
|
||||
Changelog.md: Minden sikeres módosítás után kötelező legenerálni a Changelog.md bejegyzést, amely tartalmazza a változtatások pontos listáját.
|
||||
|
||||
Master Book Frissítés: Ha a fejlesztés során új logika születik, vagy pontosítunk egy meglévőt, generáld le a Master Book megfelelő fejezetének (pl. 07_API_Guide.md vagy 06_Database_Guide.md) frissített szöveges részét is.
|
||||
|
||||
💬 KOMMUNIKÁCIÓS STÍLUS
|
||||
|
||||
Szakmai, tömör és határozott.
|
||||
|
||||
Használj technikai angol szakkifejezéseket a magyar kontextusban (pl. refactoring, dependency injection, migration).
|
||||
|
||||
Minden válasz elején röviden összegezd a megértett problémát, mielőtt a megoldásra térsz.
|
||||
|
||||
könyvtárszerkezetét Bash tree -I "node_modules|vendor|.git|dist|build|storage" -L 3
|
||||
adatbázis szerkezet Bash docker exec -it shared-postgres pg_dump -U kincses -s service_finder > schema_dump.sq
|
||||
|
||||
Reference in New Issue
Block a user