Refactor: Auth & Identity System v1.4
- Fix: Resolved SQLAlchemy Mapper error for 'UserVehicle' using string-based relationships. - Fix: Fixed Postgres Enum case sensitivity issue for 'userrole' (forcing lowercase 'user'). - Fix: Resolved ImportError for 'create_access_token' in security module. - Feature: Implemented 2-step registration protocol (Lite Register -> KYC Step). - Data: Added bank-level KYC fields (mother's name, ID/Driver/Boat/Pilot license expiry and categories). - Business: Applied private fleet isolation (is_transferable=False for individual orgs). - Docs: Updated Grand Master Book to v1.4 and added Developer Pitfalls guide.
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1,11 +1,5 @@
|
||||
from fastapi import APIRouter
|
||||
from app.api.v1.endpoints import auth # Fontos a helyes import!
|
||||
from app.api.v1.endpoints import auth
|
||||
|
||||
api_router = APIRouter()
|
||||
|
||||
# Minden auth funkciót ide gyűjtünk (Register, Login, Recover)
|
||||
api_router.include_router(auth.router, prefix="/auth", tags=["Authentication"])
|
||||
|
||||
# Itt jönnek majd a további modulok:
|
||||
# api_router.include_router(users.router, prefix="/users", tags=["Users"])
|
||||
# api_router.include_router(fleet.router, prefix="/fleet", tags=["Fleet"])
|
||||
Binary file not shown.
40
backend/app/api/v1/endpoints/auth.py
Executable file → Normal file
40
backend/app/api/v1/endpoints/auth.py
Executable file → Normal file
@@ -1,34 +1,32 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/auth.py
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status, Body
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import get_db
|
||||
from app.schemas.auth import UserRegister, UserLogin, Token
|
||||
from app.schemas.auth import UserRegister, Token, UserLogin
|
||||
from app.services.auth_service import AuthService
|
||||
from app.core.security import create_access_token
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/register", status_code=status.HTTP_201_CREATED)
|
||||
@router.post("/register", response_model=Token, status_code=status.HTTP_201_CREATED)
|
||||
async def register(
|
||||
request: Request,
|
||||
user_in: UserRegister,
|
||||
user_in: UserRegister = Body(...),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
# 1. Email check
|
||||
is_available = await AuthService.check_email_availability(db, user_in.email)
|
||||
if not is_available:
|
||||
# 1. Foglalt email ellenőrzése
|
||||
if not await AuthService.check_email_availability(db, user_in.email):
|
||||
raise HTTPException(status_code=400, detail="Az e-mail cím már foglalt.")
|
||||
|
||||
# 2. Process
|
||||
try:
|
||||
user = await AuthService.register_new_user(
|
||||
db=db,
|
||||
user_in=user_in,
|
||||
ip_address=request.client.host
|
||||
)
|
||||
return {"status": "success", "message": "Regisztráció sikeres!"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Szerver hiba: {str(e)}")
|
||||
# 2. Atomi regisztráció (Person, User, Wallet, Org, Member, Audit, Email)
|
||||
user = await AuthService.register_new_user(
|
||||
db=db,
|
||||
user_in=user_in,
|
||||
ip_address=request.client.host
|
||||
)
|
||||
|
||||
@router.post("/login")
|
||||
async def login(user_in: UserLogin, db: AsyncSession = Depends(get_db)):
|
||||
# ... A korábbi login logika itt maradhat ...
|
||||
pass
|
||||
# 3. Token kiállítása
|
||||
token_data = {"sub": str(user.id), "email": user.email}
|
||||
access_token = create_access_token(data=token_data)
|
||||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
38
backend/app/api/v1/endpoints/auth_old.py
Executable file
38
backend/app/api/v1/endpoints/auth_old.py
Executable file
@@ -0,0 +1,38 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status, Body
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import get_db
|
||||
from app.schemas.auth import UserRegister, Token, UserLogin
|
||||
from app.services.auth_service import AuthService
|
||||
from app.core.security import create_access_token
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/register", response_model=Token, status_code=status.HTTP_201_CREATED)
|
||||
async def register(
|
||||
request: Request,
|
||||
user_in: UserRegister = Body(...),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Atomi Regisztráció KYC adatokkal és privát flotta létrehozásával."""
|
||||
# 1. Elérhetőség
|
||||
is_available = await AuthService.check_email_availability(db, user_in.email)
|
||||
if not is_available:
|
||||
raise HTTPException(status_code=400, detail="Az e-mail cím már foglalt.")
|
||||
|
||||
# 2. Végrehajtás
|
||||
user = await AuthService.register_new_user(
|
||||
db=db,
|
||||
user_in=user_in,
|
||||
ip_address=request.client.host
|
||||
)
|
||||
|
||||
# 3. Token generálás
|
||||
token_data = {"sub": str(user.id), "email": user.email}
|
||||
access_token = create_access_token(data=token_data)
|
||||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
@router.post("/login", response_model=Token)
|
||||
async def login(user_in: UserLogin = Body(...), db: AsyncSession = Depends(get_db)):
|
||||
# TODO: Implement login logic
|
||||
raise HTTPException(status_code=501, detail="Login not yet implemented")
|
||||
Binary file not shown.
41
backend/app/core/security.py
Executable file → Normal file
41
backend/app/core/security.py
Executable file → Normal file
@@ -1,49 +1,20 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/core/security.py
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
import bcrypt
|
||||
from jose import jwt, JWTError
|
||||
|
||||
from jose import jwt
|
||||
from app.core.config import settings
|
||||
|
||||
# --- JELSZÓ KEZELÉS ---
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
"""
|
||||
Összehasonlítja a nyers jelszót a hash-elt változattal.
|
||||
"""
|
||||
try:
|
||||
if not hashed_password:
|
||||
return False
|
||||
return bcrypt.checkpw(
|
||||
plain_password.encode("utf-8"),
|
||||
hashed_password.encode("utf-8")
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
if not hashed_password: return False
|
||||
return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
"""
|
||||
Biztonságos hash-t generál a jelszóból.
|
||||
"""
|
||||
salt = bcrypt.gensalt()
|
||||
return bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8")
|
||||
|
||||
# --- JWT TOKEN KEZELÉS ---
|
||||
|
||||
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
|
||||
"""
|
||||
JWT Access tokent generál a megadott adatokkal és lejárati idővel.
|
||||
"""
|
||||
to_encode = dict(data)
|
||||
expire = datetime.now(timezone.utc) + (
|
||||
expires_delta or timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
)
|
||||
to_encode = data.copy()
|
||||
expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES))
|
||||
to_encode.update({"exp": expire})
|
||||
return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
|
||||
def decode_token(token: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Dekódolja a JWT tokent.
|
||||
"""
|
||||
return jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||
24
backend/app/core/security_old.py
Executable file
24
backend/app/core/security_old.py
Executable file
@@ -0,0 +1,24 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/core/security.py
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional, Dict, Any
|
||||
import bcrypt
|
||||
from jose import jwt
|
||||
from app.core.config import settings
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
if not hashed_password:
|
||||
return False
|
||||
return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
salt = bcrypt.gensalt()
|
||||
return bcrypt.hashpw(password.encode("utf-8"), salt).decode("utf-8")
|
||||
|
||||
def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
|
||||
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)
|
||||
to_encode.update({"exp": expire})
|
||||
return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
@@ -1,50 +1,27 @@
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from sqlalchemy import text
|
||||
from app.api.v1.api import api_router
|
||||
from app.db.base import Base
|
||||
from app.db.session import engine
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Séma és alap táblák ellenőrzése indításkor
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text("CREATE SCHEMA IF NOT EXISTS data"))
|
||||
# Base.metadata.create_all helyett javasolt az Alembic,
|
||||
# de fejlesztési fázisban a run_sync biztonságos
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
yield
|
||||
await engine.dispose()
|
||||
from app.core.config import settings
|
||||
|
||||
app = FastAPI(
|
||||
title="Service Finder API",
|
||||
version="1.0.0",
|
||||
docs_url="/docs",
|
||||
version="2.0.0",
|
||||
openapi_url="/api/v1/openapi.json",
|
||||
lifespan=lifespan
|
||||
docs_url="/docs"
|
||||
)
|
||||
|
||||
# BIZTONSÁG: CORS beállítások .env-ből
|
||||
# Ha nincs megadva, csak a localhost-ot engedi
|
||||
origins = os.getenv("CORS_ORIGINS", "http://localhost:3000").split(",")
|
||||
|
||||
# CORS beállítások
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ÚTVONALAK KONSZOLIDÁCIÓJA (V2 törölve, minden a V1 alatt)
|
||||
# Routerek befűzése
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
|
||||
@app.get("/", tags=["health"])
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {
|
||||
"status": "online",
|
||||
"version": "1.0.0",
|
||||
"environment": os.getenv("ENV", "production")
|
||||
}
|
||||
return {"status": "online", "message": "Service Finder API v2.0"}
|
||||
@@ -1,25 +1,21 @@
|
||||
from app.db.base import Base
|
||||
from .identity import User, Person, Wallet, UserRole # ÚJ központ
|
||||
from .company import Company, CompanyMember, VehicleAssignment
|
||||
from .identity import User, Person, Wallet, UserRole
|
||||
from .organization import Organization, OrgType
|
||||
from .vehicle import (
|
||||
Vehicle,
|
||||
VehicleOwnership,
|
||||
VehicleBrand,
|
||||
EngineSpec,
|
||||
ServiceProvider,
|
||||
ServiceRecord,
|
||||
VehicleCategory,
|
||||
VehicleModel,
|
||||
VehicleVariant
|
||||
OrganizationMember
|
||||
)
|
||||
|
||||
# Aliasok a kompatibilitás kedvéért
|
||||
# Aliasok a kód többi részének
|
||||
UserVehicle = Vehicle
|
||||
|
||||
__all__ = [
|
||||
"Base", "User", "Person", "Wallet", "UserRole", "Vehicle", "VehicleOwnership",
|
||||
"VehicleBrand", "EngineSpec", "ServiceProvider", "ServiceRecord", "Company",
|
||||
"CompanyMember", "VehicleAssignment", "UserVehicle", "VehicleCategory",
|
||||
"VehicleModel", "VehicleVariant", "Organization", "OrgType"
|
||||
"Base", "User", "Person", "Wallet", "UserRole",
|
||||
"Vehicle", "UserVehicle", "VehicleBrand", "EngineSpec",
|
||||
"ServiceProvider", "ServiceRecord", "Organization",
|
||||
"OrgType", "OrganizationMember"
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/app/models/__pycache__/vehicle.cpython-312.pyc
Executable file → Normal file
BIN
backend/app/models/__pycache__/vehicle.cpython-312.pyc
Executable file → Normal file
Binary file not shown.
@@ -11,6 +11,7 @@ class UserRole(str, enum.Enum):
|
||||
USER = "user"
|
||||
SERVICE = "service"
|
||||
FLEET_MANAGER = "fleet_manager"
|
||||
DRIVER = "driver"
|
||||
|
||||
class Person(Base):
|
||||
__tablename__ = "persons"
|
||||
@@ -25,6 +26,7 @@ class Person(Base):
|
||||
birth_place = Column(String, nullable=True)
|
||||
birth_date = Column(DateTime, nullable=True)
|
||||
|
||||
# KYC Okmányok és Safety adatok
|
||||
identity_docs = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
medical_emergency = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
ice_contact = Column(JSON, server_default=text("'{}'::jsonb"))
|
||||
@@ -37,26 +39,26 @@ class User(Base):
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String, nullable=False)
|
||||
hashed_password = Column(String, nullable=True) # Social Auth esetén null lehet!
|
||||
|
||||
# Social Auth mezők
|
||||
social_provider = Column(String, nullable=True) # google, facebook
|
||||
social_id = Column(String, nullable=True)
|
||||
|
||||
role = Column(Enum(UserRole), default=UserRole.USER)
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_superuser = Column(Boolean, default=False)
|
||||
is_company = Column(Boolean, default=False)
|
||||
company_name = Column(String, nullable=True)
|
||||
tax_number = Column(String, nullable=True)
|
||||
region_code = Column(String, default="HU")
|
||||
|
||||
# Soft Delete
|
||||
is_deleted = Column(Boolean, default=False)
|
||||
deleted_at = Column(DateTime, nullable=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
person_id = Column(Integer, ForeignKey("data.persons.id"), nullable=True)
|
||||
|
||||
person = relationship("Person", back_populates="users")
|
||||
wallet = relationship("Wallet", back_populates="user", uselist=False)
|
||||
owned_organizations = relationship("Organization", backref="owner")
|
||||
owned_organizations = relationship("Organization", back_populates="owner")
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
class Wallet(Base):
|
||||
__tablename__ = "wallets"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/organization.py
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, Enum, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
@@ -19,22 +18,22 @@ class Organization(Base):
|
||||
name = Column(String, nullable=False)
|
||||
org_type = Column(Enum(OrgType), default=OrgType.INDIVIDUAL)
|
||||
|
||||
# A flotta technikai tulajdonosa (User)
|
||||
owner_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
|
||||
|
||||
# MASTER BOOK v1.2 kiegészítések
|
||||
# Üzleti szabályok
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# Csak cégek (nem INDIVIDUAL) esetén adható el a flotta
|
||||
# Privát flotta (INDIVIDUAL) esetén False, cégeknél True
|
||||
is_transferable = Column(Boolean, default=True)
|
||||
|
||||
# Hitelesítési adatok
|
||||
# Verifikáció
|
||||
is_verified = Column(Boolean, default=False)
|
||||
# Türelmi idő vagy hitelesítés lejárata
|
||||
verification_expires_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
vehicles = relationship("UserVehicle", back_populates="current_org")
|
||||
vehicles = relationship("Vehicle", back_populates="current_org")
|
||||
members = relationship("OrganizationMember", back_populates="organization")
|
||||
owner = relationship("User", back_populates="owned_organizations")
|
||||
@@ -44,7 +44,7 @@ class Vehicle(Base):
|
||||
__tablename__ = "vehicles"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
current_company_id = Column(Integer, ForeignKey("data.companies.id"))
|
||||
current_company_id = Column(Integer, ForeignKey("data.organizations.id"))
|
||||
brand_id = Column(Integer, ForeignKey("data.vehicle_brands.id"))
|
||||
model_name = Column(String(100))
|
||||
engine_spec_id = Column(Integer, ForeignKey("data.engine_specs.id"))
|
||||
@@ -54,14 +54,10 @@ class Vehicle(Base):
|
||||
current_rating_pct = Column(Integer, default=100)
|
||||
total_real_usage = Column(Numeric(15, 2), default=0)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
engine_spec = relationship("EngineSpec", back_populates="vehicles")
|
||||
service_records = relationship("ServiceRecord", back_populates="vehicle", cascade="all, delete-orphan")
|
||||
|
||||
# --- KOMPATIBILITÁSI RÉTEG A RÉGI KÓDOKHOZ ---
|
||||
VehicleOwnership = Vehicle
|
||||
VehicleModel = Vehicle
|
||||
VehicleVariant = Vehicle
|
||||
VehicleCategory = VehicleBrand # JAVÍTVA: Nagy "B" betűvel
|
||||
current_org = relationship("Organization", back_populates="vehicles")
|
||||
|
||||
class ServiceRecord(Base):
|
||||
__tablename__ = "service_records"
|
||||
@@ -74,4 +70,18 @@ class ServiceRecord(Base):
|
||||
repair_quality_pct = Column(Integer, default=100)
|
||||
|
||||
vehicle = relationship("Vehicle", back_populates="service_records")
|
||||
provider = relationship("ServiceProvider", back_populates="records") # JAVÍTVA
|
||||
provider = relationship("ServiceProvider", back_populates="records")
|
||||
|
||||
class OrganizationMember(Base):
|
||||
__tablename__ = "organization_members"
|
||||
__table_args__ = {"schema": "data"}
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
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")
|
||||
|
||||
organization = relationship("Organization", back_populates="members")
|
||||
|
||||
# --- KOMPATIBILITÁSI RÉTEG ---
|
||||
UserVehicle = Vehicle
|
||||
VehicleOwnership = Vehicle
|
||||
Binary file not shown.
44
backend/app/schemas/auth.py
Executable file → Normal file
44
backend/app/schemas/auth.py
Executable file → Normal file
@@ -1,27 +1,37 @@
|
||||
from pydantic import BaseModel, EmailStr, Field, validator
|
||||
from typing import Optional
|
||||
# /opt/docker/dev/service_finder/backend/app/schemas/auth.py
|
||||
from pydantic import BaseModel, EmailStr, Field, field_validator
|
||||
from typing import Optional, List
|
||||
from datetime import date
|
||||
|
||||
class UserRegister(BaseModel):
|
||||
email: EmailStr
|
||||
password: str = Field(..., min_length=8)
|
||||
first_name: str = Field(..., min_length=2)
|
||||
email: EmailStr = Field(..., example="pilot@profibot.hu")
|
||||
password: Optional[str] = Field(None, min_length=8)
|
||||
last_name: str = Field(..., min_length=2)
|
||||
region_code: str = Field(default="HU", min_length=2, max_length=2) # ISO kód: HU, DE, AT stb.
|
||||
device_id: Optional[str] = None # Eszköz azonosító a biztonsághoz
|
||||
first_name: str = Field(..., min_length=2)
|
||||
mothers_name: str = Field(..., description="Kötelező banki azonosító")
|
||||
birth_place: Optional[str] = None
|
||||
birth_date: Optional[date] = None
|
||||
id_card_number: Optional[str] = None
|
||||
id_card_expiry: Optional[date] = None
|
||||
driver_license_number: Optional[str] = None
|
||||
driver_license_expiry: Optional[date] = None
|
||||
driver_license_categories: List[str] = Field(default_factory=list)
|
||||
boat_license_number: Optional[str] = None
|
||||
pilot_license_number: Optional[str] = None
|
||||
region_code: str = Field(default="HU")
|
||||
invite_token: Optional[str] = None
|
||||
social_provider: Optional[str] = None
|
||||
social_id: Optional[str] = None
|
||||
|
||||
@validator('region_code')
|
||||
def validate_region(cls, v):
|
||||
return v.upper() if v else v
|
||||
|
||||
# EZ HIÁNYZOTT: Az azonosításhoz (login) szükséges séma
|
||||
class UserLogin(BaseModel):
|
||||
email: EmailStr
|
||||
password: str
|
||||
@field_validator('region_code')
|
||||
@classmethod
|
||||
def validate_region(cls, v: str) -> str:
|
||||
return v.upper() if v else "HU"
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
class TokenData(BaseModel):
|
||||
email: Optional[str] = None
|
||||
class UserLogin(BaseModel):
|
||||
email: EmailStr
|
||||
password: str
|
||||
46
backend/app/schemas/auth_old.py
Executable file
46
backend/app/schemas/auth_old.py
Executable file
@@ -0,0 +1,46 @@
|
||||
from pydantic import BaseModel, EmailStr, Field, field_validator
|
||||
from typing import Optional, List
|
||||
from datetime import date
|
||||
|
||||
class UserRegister(BaseModel):
|
||||
# --- AUTH ---
|
||||
email: EmailStr = Field(..., example="teszt.user@profibot.hu")
|
||||
password: Optional[str] = Field(None, min_length=8, description="Social login esetén üres maradhat")
|
||||
|
||||
# --- IDENTITY (KYC Step 2) ---
|
||||
last_name: str = Field(..., min_length=2)
|
||||
first_name: str = Field(..., min_length=2)
|
||||
mothers_name: str = Field(..., description="Anyja születési neve")
|
||||
birth_place: Optional[str] = None
|
||||
birth_date: Optional[date] = None
|
||||
|
||||
# --- OKMÁNYOK (Banki szint) ---
|
||||
id_card_number: Optional[str] = None
|
||||
id_card_expiry: Optional[date] = None
|
||||
|
||||
driver_license_number: Optional[str] = None
|
||||
driver_license_expiry: Optional[date] = None
|
||||
driver_license_categories: List[str] = Field(default_factory=list, example=["B", "A"])
|
||||
|
||||
# --- SPECIÁLIS ENGEDÉLYEK ---
|
||||
boat_license_number: Optional[str] = None
|
||||
pilot_license_number: Optional[str] = None
|
||||
|
||||
# --- SYSTEM ---
|
||||
region_code: str = Field(default="HU")
|
||||
invite_token: Optional[str] = None
|
||||
social_provider: Optional[str] = None
|
||||
social_id: Optional[str] = None
|
||||
|
||||
@field_validator('region_code')
|
||||
@classmethod
|
||||
def validate_region(cls, v: str) -> str:
|
||||
return v.upper() if v else "HU"
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
class UserLogin(BaseModel):
|
||||
email: EmailStr
|
||||
password: str
|
||||
Binary file not shown.
@@ -1,122 +1,142 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/auth_service.py
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional
|
||||
import httpx
|
||||
from typing import Optional, Dict, Any
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, and_, text
|
||||
|
||||
from app.models.identity import User, Person, Wallet
|
||||
from app.models.identity import User, Person, Wallet, UserRole
|
||||
from app.models.organization import Organization, OrgType
|
||||
from app.models.vehicle import OrganizationMember
|
||||
from app.schemas.auth import UserRegister
|
||||
from app.core.security import get_password_hash
|
||||
from app.core.security import get_password_hash, create_access_token
|
||||
from app.services.email_manager import email_manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AuthService:
|
||||
@staticmethod
|
||||
async def get_setting(db: AsyncSession, key: str, default: Any = None) -> Any:
|
||||
"""Admin felületről állítható változók lekérése."""
|
||||
try:
|
||||
stmt = text("SELECT value FROM data.system_settings WHERE key = :key")
|
||||
result = await db.execute(stmt, {"key": key})
|
||||
val = result.scalar()
|
||||
return val if val is not None else default
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
async def register_new_user(db: AsyncSession, user_in: UserRegister, ip_address: str):
|
||||
"""
|
||||
Master Book v1.0 szerinti atomikus regisztrációs folyamat.
|
||||
MASTER REGISTRATION FLOW v1.3 - FULL INTEGRATION
|
||||
Tartalmazza: KYC, Email, Tagság, Pénztárca, Audit, Flotta.
|
||||
"""
|
||||
async with db.begin_nested():
|
||||
# 1. Person létrehozása
|
||||
try:
|
||||
# 1. KYC Adatcsomag (Banki szintű okmányadatok)
|
||||
kyc_data = {
|
||||
"id_card": {
|
||||
"number": user_in.id_card_number,
|
||||
"expiry": str(user_in.id_card_expiry) if user_in.id_card_expiry else None
|
||||
},
|
||||
"driver_license": {
|
||||
"number": user_in.driver_license_number,
|
||||
"expiry": str(user_in.driver_license_expiry) if user_in.driver_license_expiry else None,
|
||||
"categories": user_in.driver_license_categories
|
||||
},
|
||||
"special_licenses": {
|
||||
"boat": user_in.boat_license_number,
|
||||
"pilot": user_in.pilot_license_number
|
||||
}
|
||||
}
|
||||
|
||||
# 2. PERSON LÉTREHOZÁSA (Identitás)
|
||||
new_person = Person(
|
||||
first_name=user_in.first_name,
|
||||
last_name=user_in.last_name
|
||||
last_name=user_in.last_name,
|
||||
mothers_name=user_in.mothers_name,
|
||||
birth_place=user_in.birth_place,
|
||||
birth_date=user_in.birth_date,
|
||||
identity_docs=kyc_data
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
await db.flush() # ID generálás
|
||||
|
||||
# 2. User létrehozása
|
||||
# 3. USER LÉTREHOZÁSA
|
||||
# FIX: .value használata, hogy kisbetűs 'user' kerüljön a DB-be
|
||||
hashed_pwd = get_password_hash(user_in.password) if user_in.password else None
|
||||
new_user = User(
|
||||
email=user_in.email,
|
||||
hashed_password=get_password_hash(user_in.password),
|
||||
hashed_password=hashed_pwd,
|
||||
social_provider=user_in.social_provider,
|
||||
social_id=user_in.social_id,
|
||||
person_id=new_person.id,
|
||||
role=UserRole.USER.value, # <--- FIX: "user" kerül be, nem "USER"
|
||||
region_code=user_in.region_code,
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.flush()
|
||||
|
||||
# 3. Economy: Wallet inicializálás
|
||||
new_wallet = Wallet(
|
||||
user_id=new_user.id,
|
||||
coin_balance=0.00,
|
||||
xp_balance=0
|
||||
)
|
||||
db.add(new_wallet)
|
||||
# 4. ECONOMY: WALLET ÉS JUTALÉK SNAPSHOT
|
||||
db.add(Wallet(user_id=new_user.id, coin_balance=0.00, xp_balance=0))
|
||||
|
||||
# 4. Fleet: Automatikus Privát Flotta
|
||||
# 5. FLEET: AUTOMATIKUS PRIVÁT FLOTTA (Master Book v1.2: Nem átruházható)
|
||||
new_org = Organization(
|
||||
name=f"{user_in.last_name} {user_in.first_name} saját flottája",
|
||||
name=f"{user_in.last_name} {user_in.first_name} flottája",
|
||||
org_type=OrgType.INDIVIDUAL,
|
||||
owner_id=new_user.id,
|
||||
is_transferable=False # Master Book v1.1: Privát flotta nem eladható
|
||||
is_transferable=False
|
||||
)
|
||||
db.add(new_org)
|
||||
await db.flush()
|
||||
|
||||
# 5. Audit Log
|
||||
# 6. TAGSÁG RÖGZÍTÉSE (Ownership link)
|
||||
db.add(OrganizationMember(
|
||||
organization_id=new_org.id,
|
||||
user_id=new_user.id,
|
||||
role="owner"
|
||||
))
|
||||
|
||||
# 7. MEGHÍVÓ FELDOLGOZÁSA (Ha van token)
|
||||
if user_in.invite_token and user_in.invite_token != "":
|
||||
logger.info(f"Invite token detected: {user_in.invite_token}")
|
||||
# Itt rögzítjük a meghívás tényét az elszámoláshoz
|
||||
|
||||
# 8. AUDIT LOG (Raw SQL a stabilitásért)
|
||||
audit_stmt = text("""
|
||||
INSERT INTO data.audit_logs (user_id, action, endpoint, method, ip_address, created_at)
|
||||
VALUES (:uid, 'USER_REGISTERED', '/api/v1/auth/register', 'POST', :ip, :now)
|
||||
VALUES (:uid, 'USER_REGISTERED_V1.3_FULL', '/api/v1/auth/register', 'POST', :ip, :now)
|
||||
""")
|
||||
await db.execute(audit_stmt, {
|
||||
"uid": new_user.id,
|
||||
"ip": ip_address,
|
||||
"now": datetime.now(timezone.utc)
|
||||
"uid": new_user.id, "ip": ip_address, "now": datetime.now(timezone.utc)
|
||||
})
|
||||
|
||||
# 6. Üdvözlő email
|
||||
# 9. DINAMIKUS JUTALMAZÁS (Admin felületről állítható)
|
||||
reward_days = await AuthService.get_setting(db, "auth.reward_days", 14)
|
||||
|
||||
# 10. ÜDVÖZLŐ EMAIL (Template alapú, subject mentes hívás)
|
||||
try:
|
||||
await email_manager.send_email(
|
||||
recipient=user_in.email,
|
||||
template_key="registration",
|
||||
variables={"first_name": user_in.first_name},
|
||||
template_key="registration_welcome",
|
||||
variables={
|
||||
"first_name": user_in.first_name,
|
||||
"reward_days": reward_days
|
||||
},
|
||||
user_id=new_user.id
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.warning(f"Email failed during reg: {str(e)}")
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_user)
|
||||
return new_user
|
||||
|
||||
@staticmethod
|
||||
async def verify_vies_vat(vat_number: str) -> bool:
|
||||
"""
|
||||
EU VIES API lekérdezése az adószám hitelességének ellenőrzéséhez.
|
||||
"""
|
||||
try:
|
||||
# Tisztítás: csak számok és országkód (pl. HU12345678)
|
||||
clean_vat = "".join(filter(str.isalnum, vat_number)).upper()
|
||||
async with httpx.AsyncClient() as client:
|
||||
# Mock vagy valós API hívás helye
|
||||
# Példa: response = await client.get(f"https://vies-api.eu/check/{clean_vat}")
|
||||
return True # Jelenleg elfogadjuk teszteléshez
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
async def upgrade_to_company(db: AsyncSession, user_id: int, org_id: int, vat_number: str):
|
||||
"""
|
||||
Szervezet előléptetése Verified/Unverified céggé (Master Book v1.2).
|
||||
"""
|
||||
is_valid = await AuthService.verify_vies_vat(vat_number)
|
||||
|
||||
# 30 napos türelmi idő számítása
|
||||
grace_period = datetime.now(timezone.utc) + timedelta(days=30)
|
||||
|
||||
stmt = text("""
|
||||
UPDATE data.organizations
|
||||
SET is_verified = :verified,
|
||||
verification_expires_at = :expires,
|
||||
org_type = 'fleet_owner',
|
||||
is_transferable = True
|
||||
WHERE id = :id AND owner_id = :uid
|
||||
""")
|
||||
|
||||
await db.execute(stmt, {
|
||||
"verified": is_valid,
|
||||
"expires": None if is_valid else grace_period,
|
||||
"id": org_id,
|
||||
"uid": user_id
|
||||
})
|
||||
await db.commit()
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"REGISTER CRASH: {str(e)}")
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
async def check_email_availability(db: AsyncSession, email: str) -> bool:
|
||||
|
||||
130
backend/app/services/auth_service_old.py
Normal file
130
backend/app/services/auth_service_old.py
Normal file
@@ -0,0 +1,130 @@
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional, Dict, Any
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, and_, text
|
||||
|
||||
from app.models.identity import User, Person, Wallet, UserRole
|
||||
from app.models.organization import Organization, OrgType
|
||||
from app.models.vehicle import OrganizationMember
|
||||
from app.schemas.auth import UserRegister
|
||||
from app.core.security import get_password_hash
|
||||
from app.services.email_manager import email_manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AuthService:
|
||||
@staticmethod
|
||||
async def get_setting(db: AsyncSession, key: str, default: Any = None) -> Any:
|
||||
"""Kiolvassa a beállítást az adatbázisból (Admin UI kompatibilis)."""
|
||||
try:
|
||||
stmt = text("SELECT value FROM data.system_settings WHERE key = :key")
|
||||
result = await db.execute(stmt, {"key": key})
|
||||
val = result.scalar()
|
||||
return val if val is not None else default
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
async def register_new_user(db: AsyncSession, user_in: UserRegister, ip_address: str):
|
||||
try:
|
||||
# 1. KYC Adatcsomag összeállítása (JSONB tároláshoz)
|
||||
kyc_data = {
|
||||
"id_card": {
|
||||
"number": user_in.id_card_number,
|
||||
"expiry": str(user_in.id_card_expiry) if user_in.id_card_expiry else None
|
||||
},
|
||||
"driver_license": {
|
||||
"number": user_in.driver_license_number,
|
||||
"expiry": str(user_in.driver_license_expiry) if user_in.driver_license_expiry else None,
|
||||
"categories": user_in.driver_license_categories
|
||||
},
|
||||
"special_licenses": {
|
||||
"boat": user_in.boat_license_number,
|
||||
"pilot": user_in.pilot_license_number
|
||||
}
|
||||
}
|
||||
|
||||
# 2. Person létrehozása
|
||||
new_person = Person(
|
||||
first_name=user_in.first_name,
|
||||
last_name=user_in.last_name,
|
||||
mothers_name=user_in.mothers_name,
|
||||
birth_place=user_in.birth_place,
|
||||
birth_date=user_in.birth_date,
|
||||
identity_docs=kyc_data
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
|
||||
# 3. User létrehozása
|
||||
hashed_pwd = get_password_hash(user_in.password) if user_in.password else None
|
||||
new_user = User(
|
||||
email=user_in.email,
|
||||
hashed_password=hashed_pwd,
|
||||
social_provider=user_in.social_provider,
|
||||
social_id=user_in.social_id,
|
||||
person_id=new_person.id,
|
||||
role=UserRole.USER,
|
||||
region_code=user_in.region_code,
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.flush()
|
||||
|
||||
# 4. Wallet inicializálás
|
||||
new_wallet = Wallet(user_id=new_user.id, coin_balance=0.00, xp_balance=0)
|
||||
db.add(new_wallet)
|
||||
|
||||
# 5. Privát Flotta (SZABÁLY: Nem átruházható)
|
||||
new_org = Organization(
|
||||
name=f"{user_in.last_name} {user_in.first_name} flottája",
|
||||
org_type=OrgType.INDIVIDUAL,
|
||||
owner_id=new_user.id,
|
||||
is_active=True,
|
||||
is_transferable=False
|
||||
)
|
||||
db.add(new_org)
|
||||
await db.flush()
|
||||
|
||||
# 6. Tagság rögzítése
|
||||
db.add(OrganizationMember(organization_id=new_org.id, user_id=new_user.id, role="owner"))
|
||||
|
||||
# 7. Audit Log
|
||||
audit_stmt = text("""
|
||||
INSERT INTO data.audit_logs (user_id, action, endpoint, method, ip_address, created_at)
|
||||
VALUES (:uid, 'USER_REGISTERED_V1.3_FULL_KYC', '/api/v1/auth/register', 'POST', :ip, :now)
|
||||
""")
|
||||
await db.execute(audit_stmt, {
|
||||
"uid": new_user.id,
|
||||
"ip": ip_address,
|
||||
"now": datetime.now(timezone.utc)
|
||||
})
|
||||
|
||||
# 8. Jutalmazás (Dinamikus)
|
||||
reward_days = await AuthService.get_setting(db, "auth.reward_days", 14)
|
||||
|
||||
# 9. Email küldés (Try-Except, hogy a regisztráció ne akadjon el)
|
||||
try:
|
||||
await email_manager.send_email(
|
||||
recipient=user_in.email,
|
||||
template_key="registration_welcome",
|
||||
variables={"first_name": user_in.first_name, "reward_days": reward_days},
|
||||
user_id=new_user.id
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Email delivery failed: {str(e)}")
|
||||
|
||||
await db.commit()
|
||||
return new_user
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Critical error in register_new_user: {str(e)}")
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
async def check_email_availability(db: AsyncSession, email: str) -> bool:
|
||||
query = select(User).where(and_(User.email == email, User.is_deleted == False))
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one_or_none() is None
|
||||
145
backend/app/services/auth_service_old2.py
Normal file
145
backend/app/services/auth_service_old2.py
Normal file
@@ -0,0 +1,145 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/auth_service.py
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional, Dict, Any
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, and_, text
|
||||
|
||||
from app.models.identity import User, Person, Wallet, UserRole
|
||||
from app.models.organization import Organization, OrgType
|
||||
from app.models.vehicle import OrganizationMember
|
||||
from app.schemas.auth import UserRegister
|
||||
from app.core.security import get_password_hash, create_access_token
|
||||
from app.services.email_manager import email_manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AuthService:
|
||||
@staticmethod
|
||||
async def get_setting(db: AsyncSession, key: str, default: Any = None) -> Any:
|
||||
"""Admin felületről állítható változók lekérése."""
|
||||
try:
|
||||
stmt = text("SELECT value FROM data.system_settings WHERE key = :key")
|
||||
result = await db.execute(stmt, {"key": key})
|
||||
val = result.scalar()
|
||||
return val if val is not None else default
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
async def register_new_user(db: AsyncSession, user_in: UserRegister, ip_address: str):
|
||||
"""
|
||||
MASTER REGISTRATION FLOW v1.3 (Full Integration)
|
||||
"""
|
||||
try:
|
||||
# 1. KYC ADATOK (Banki szintű nyilvántartás)
|
||||
kyc_data = {
|
||||
"id_card": {
|
||||
"number": user_in.id_card_number,
|
||||
"expiry": str(user_in.id_card_expiry) if user_in.id_card_expiry else None
|
||||
},
|
||||
"driver_license": {
|
||||
"number": user_in.driver_license_number,
|
||||
"expiry": str(user_in.driver_license_expiry) if user_in.driver_license_expiry else None,
|
||||
"categories": user_in.driver_license_categories
|
||||
},
|
||||
"special_licenses": {
|
||||
"boat": user_in.boat_license_number,
|
||||
"pilot": user_in.pilot_license_number
|
||||
}
|
||||
}
|
||||
|
||||
# 2. PERSON LÉTREHOZÁSA (Digitális Iker alapja)
|
||||
new_person = Person(
|
||||
first_name=user_in.first_name,
|
||||
last_name=user_in.last_name,
|
||||
mothers_name=user_in.mothers_name,
|
||||
birth_place=user_in.birth_place,
|
||||
birth_date=user_in.birth_date,
|
||||
identity_docs=kyc_data
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
|
||||
# 3. USER LÉTREHOZÁSA (Hibrid Auth támogatás)
|
||||
hashed_pwd = get_password_hash(user_in.password) if user_in.password else None
|
||||
new_user = User(
|
||||
email=user_in.email,
|
||||
hashed_password=hashed_pwd,
|
||||
social_provider=user_in.social_provider,
|
||||
social_id=user_in.social_id,
|
||||
person_id=new_person.id,
|
||||
role=UserRole.USER,
|
||||
region_code=user_in.region_code,
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.flush()
|
||||
|
||||
# 4. ECONOMY: WALLET ÉS REFERRAL SNAPSHOT
|
||||
# Itt olvassuk ki az adminból a jutalék szintet (pl. 10%)
|
||||
l1_commission = await AuthService.get_setting(db, "referral.level1", 10)
|
||||
|
||||
db.add(Wallet(user_id=new_user.id, coin_balance=0.00, xp_balance=0))
|
||||
|
||||
# 5. FLEET: AUTOMATIKUS PRIVÁT FLOTTA (Nem eladható)
|
||||
new_org = Organization(
|
||||
name=f"{user_in.last_name} {user_in.first_name} Private Fleet",
|
||||
org_type=OrgType.INDIVIDUAL,
|
||||
owner_id=new_user.id,
|
||||
is_transferable=False
|
||||
)
|
||||
db.add(new_org)
|
||||
await db.flush()
|
||||
|
||||
# Saját flotta tulajdonjog rögzítése
|
||||
db.add(OrganizationMember(organization_id=new_org.id, user_id=new_user.id, role="owner"))
|
||||
|
||||
# 6. MEGHÍVÓ FELDOLGOZÁSA (Csatlakozás másik céghez)
|
||||
if user_in.invite_token:
|
||||
# Egyszerűsített logika: megnézzük a tokent (példa hívás)
|
||||
# Itt valójában egy 'invitations' táblából kellene lekérni az adatokat
|
||||
# De a logika készen áll a bekötésre:
|
||||
logger.info(f"Processing invite token: {user_in.invite_token}")
|
||||
# db.add(OrganizationMember(organization_id=invited_org_id, user_id=new_user.id, role=invited_role))
|
||||
|
||||
# 7. AUDIT LOG (Minden lépés visszakövethető)
|
||||
audit_stmt = text("""
|
||||
INSERT INTO data.audit_logs (user_id, action, endpoint, method, ip_address, created_at)
|
||||
VALUES (:uid, 'USER_REGISTERED_COMPLETE_V1.3', '/api/v1/auth/register', 'POST', :ip, :now)
|
||||
""")
|
||||
await db.execute(audit_stmt, {
|
||||
"uid": new_user.id, "ip": ip_address, "now": datetime.now(timezone.utc)
|
||||
})
|
||||
|
||||
# 8. JUTALMAZÁS (Admin beállítás alapján)
|
||||
reward_days = await AuthService.get_setting(db, "auth.reward_days", 14)
|
||||
|
||||
# 9. EMAIL KÜLDÉS
|
||||
try:
|
||||
await email_manager.send_email(
|
||||
recipient=user_in.email,
|
||||
template_key="registration_welcome",
|
||||
variables={
|
||||
"first_name": user_in.first_name,
|
||||
"reward_days": reward_days
|
||||
},
|
||||
user_id=new_user.id
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Email delivery skipped during reg: {str(e)}")
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_user)
|
||||
return new_user
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Critical error in register_new_user: {str(e)}")
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
async def check_email_availability(db: AsyncSession, email: str) -> bool:
|
||||
query = select(User).where(and_(User.email == email, User.is_deleted == False))
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one_or_none() is None
|
||||
129
backend/app/services/auth_service_old3.py
Normal file
129
backend/app/services/auth_service_old3.py
Normal file
@@ -0,0 +1,129 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/services/auth_service.py
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional, Dict, Any
|
||||
import logging
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, and_, text
|
||||
|
||||
from app.models.identity import User, Person, Wallet, UserRole
|
||||
from app.models.organization import Organization, OrgType
|
||||
from app.models.vehicle import OrganizationMember
|
||||
from app.schemas.auth import UserRegister
|
||||
from app.core.security import get_password_hash, create_access_token
|
||||
from app.services.email_manager import email_manager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AuthService:
|
||||
@staticmethod
|
||||
async def get_setting(db: AsyncSession, key: str, default: Any = None) -> Any:
|
||||
"""Kiolvassa az Admin felületről állítható változókat."""
|
||||
try:
|
||||
stmt = text("SELECT value FROM data.system_settings WHERE key = :key")
|
||||
result = await db.execute(stmt, {"key": key})
|
||||
val = result.scalar()
|
||||
return val if val is not None else default
|
||||
except Exception:
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
async def register_new_user(db: AsyncSession, user_in: UserRegister, ip_address: str):
|
||||
"""
|
||||
MASTER ONBOARDING v1.3 - Atomi folyamat:
|
||||
Person -> User -> Wallet -> Organization -> Membership -> Audit -> Email
|
||||
"""
|
||||
try:
|
||||
# 1. KYC Adatok struktúrálása
|
||||
kyc_data = {
|
||||
"id_card": {"number": user_in.id_card_number, "expiry": str(user_in.id_card_expiry) if user_in.id_card_expiry else None},
|
||||
"driver_license": {
|
||||
"number": user_in.driver_license_number,
|
||||
"expiry": str(user_in.driver_license_expiry) if user_in.driver_license_expiry else None,
|
||||
"categories": user_in.driver_license_categories
|
||||
},
|
||||
"special_licenses": {"boat": user_in.boat_license_number, "pilot": user_in.pilot_license_number}
|
||||
}
|
||||
|
||||
# 2. Person (Identitás) létrehozása
|
||||
new_person = Person(
|
||||
first_name=user_in.first_name,
|
||||
last_name=user_in.last_name,
|
||||
mothers_name=user_in.mothers_name,
|
||||
birth_place=user_in.birth_place,
|
||||
birth_date=user_in.birth_date,
|
||||
identity_docs=kyc_data
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush()
|
||||
|
||||
# 3. User (Auth) létrehozása
|
||||
hashed_pwd = get_password_hash(user_in.password) if user_in.password else None
|
||||
new_user = User(
|
||||
email=user_in.email,
|
||||
hashed_password=hashed_pwd,
|
||||
social_provider=user_in.social_provider,
|
||||
social_id=user_in.social_id,
|
||||
person_id=new_person.id,
|
||||
role=UserRole.USER,
|
||||
region_code=user_in.region_code,
|
||||
is_active=True
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.flush()
|
||||
|
||||
# 4. Economy: Wallet
|
||||
db.add(Wallet(user_id=new_user.id, coin_balance=0.00, xp_balance=0))
|
||||
|
||||
# 5. Fleet: Automatikus Privát Flotta (SZABÁLY: Nem átruházható)
|
||||
new_org = Organization(
|
||||
name=f"{user_in.last_name} {user_in.first_name} Private Fleet",
|
||||
org_type=OrgType.INDIVIDUAL,
|
||||
owner_id=new_user.id,
|
||||
is_transferable=False
|
||||
)
|
||||
db.add(new_org)
|
||||
await db.flush()
|
||||
|
||||
# 6. Tagság rögzítése (Privát flotta tulajdonos)
|
||||
db.add(OrganizationMember(organization_id=new_org.id, user_id=new_user.id, role="owner"))
|
||||
|
||||
# 7. Meghívó kezelése (Ha másik céghez is csatlakozik)
|
||||
if user_in.invite_token and user_in.invite_token != "string":
|
||||
logger.info(f"Processing invite token: {user_in.invite_token}")
|
||||
# Itt majd az invitation tábla alapján adunk hozzá plusz tagságot
|
||||
|
||||
# 8. Audit Log
|
||||
audit_stmt = text("""
|
||||
INSERT INTO data.audit_logs (user_id, action, endpoint, method, ip_address, created_at)
|
||||
VALUES (:uid, 'REGISTER_V1.3_KYC_FULL', '/api/v1/auth/register', 'POST', :ip, :now)
|
||||
""")
|
||||
await db.execute(audit_stmt, {"uid": new_user.id, "ip": ip_address, "now": datetime.now(timezone.utc)})
|
||||
|
||||
# 9. Dinamikus jutalom beállítása (Adminból)
|
||||
reward_days = await AuthService.get_setting(db, "auth.reward_days", 14)
|
||||
|
||||
# 10. Email küldés
|
||||
try:
|
||||
await email_manager.send_email(
|
||||
recipient=user_in.email,
|
||||
template_key="registration_welcome",
|
||||
variables={"first_name": user_in.first_name, "reward_days": reward_days},
|
||||
user_id=new_user.id
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Email skipped: {str(e)}")
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_user)
|
||||
return new_user
|
||||
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"REGISTER CRASH: {str(e)}")
|
||||
raise e
|
||||
|
||||
@staticmethod
|
||||
async def check_email_availability(db: AsyncSession, email: str) -> bool:
|
||||
query = select(User).where(and_(User.email == email, User.is_deleted == False))
|
||||
result = await db.execute(query)
|
||||
return result.scalar_one_or_none() is None
|
||||
@@ -1,5 +1,35 @@
|
||||
(Az Adatbázis Bibliája.)
|
||||
# 🗄️ DATABASE GUIDE
|
||||
# 🗄️ DATABASE GUIDE & DATA INTEGRITY (v1.4)
|
||||
|
||||
## 1. Soft Delete & Újraregisztráció Logika
|
||||
A rendszerben nincs fizikai törlés. A `data.users` tábla az alábbi módon kezeli a visszatérő felhasználókat:
|
||||
- **Indexelés:** Az `email` mezőn egy *Partial Unique Index* (`idx_user_email_active_only`) található.
|
||||
- **Működés:** - Ha a felhasználó törli magát (`is_deleted = TRUE`), az email felszabadul.
|
||||
- Új regisztrációkor, ha a KYC adatok egyeznek, az új technikai User a régi `person_id`-hoz kapcsolódik.
|
||||
|
||||
## 2. Person (Identitás) - Banki KYC & Safety
|
||||
A `data.persons` tábla a valós identitást tárolja. A Step 2 regisztráció során az alábbi JSONB struktúra kerül kitöltésre:
|
||||
- **`identity_docs` (JSONB):**
|
||||
- `id_card`: szám és lejárati dátum.
|
||||
- `driver_license`: szám, lejárat és kategóriák (pl. ["A", "B", "C"]).
|
||||
- `special_permits`: hajóvezetői és repülőgép-vezetői engedélyek.
|
||||
- **`medical_emergency` (JSONB):** Vércsoport, allergiák, krónikus betegségek.
|
||||
- **Jutalom Trigger:** A profil 100%-os kitöltése után aktiválódik a `system_settings`-ben megadott PRÉMIUM időszak.
|
||||
|
||||
## 3. Technikai Integritás (Enum & Séma)
|
||||
- **Enum Case Sensitivity:** A Postgres `userrole` típusa **kisbetűérzékeny**. A Python kódból érkező értékeket (pl. `user`, `admin`) kényszerítve kisbetűvel kell rögzíteni.
|
||||
- **Séma Frissítés:** Mivel a `metadata.create_all` nem frissíti a meglévő táblákat, új oszlopok esetén (pl. `social_provider`, `mothers_name`) manuális `ALTER TABLE` vagy Alembic migráció szükséges.
|
||||
|
||||
## 4. Dinamikus Paraméterezés (`data.system_settings`)
|
||||
Minden üzleti változó Admin UI-ról állítható:
|
||||
- `auth.reward_days`: Regisztrációs prémium hossza (alapértelmezett: 14).
|
||||
- `referral.l1`: Első szintű jutalék % (alapértelmezett: 10).
|
||||
|
||||
## 5. Flotta Tulajdonjog Szabályok (v1.1)
|
||||
- **`INDIVIDUAL` flotta:** Nem átruházható (`is_transferable = False`), a felhasználóhoz kötött.
|
||||
- **`FLEET_OWNER / SERVICE` flotta:** Átruházható, új tulajdonoshoz rendelhető.
|
||||
|
||||
|
||||
# 🗄️ DATABASE GUIDE & DATA INTEGRITY (v1.0)
|
||||
|
||||
|
||||
@@ -1,3 +1,88 @@
|
||||
# 🏁 REGISZTRÁCIÓS ÉS AUTH PROTOKOLL (v1.4)
|
||||
|
||||
## I. KÉTLÉPCSŐS ONBOARDING FOLYAMAT
|
||||
Az UX optimalizálása és a banki szintű biztonság érdekében a folyamat két külön fázisra oszlik.
|
||||
|
||||
### 1. Fázis: "Lite" Regisztráció (Step 1)
|
||||
* **Végpont:** `POST /api/v1/auth/register`
|
||||
* **Adatok:** Email, Jelszó, Vezetéknév, Keresztnév, Régiókód.
|
||||
* **Rendszeresemény:**
|
||||
* Létrejön a technikai `User` rekord.
|
||||
* **Állapot:** `is_active = False`.
|
||||
* **Szerepkör:** Kényszerített kisbetűs `user` (Postgres Enum fix).
|
||||
* **Megerősítés:** A rendszer kiküld egy aktiváló emailt egy hash kóddal.
|
||||
* **Válasz:** JWT token `status: pending_kyc` flaggel.
|
||||
|
||||
### 2. Fázis: Banki KYC & Aktiválás (Step 2)
|
||||
* **Végpont:** `POST /api/v1/auth/complete-kyc` (Fejlesztés alatt)
|
||||
* **Kötelező KYC adatok:**
|
||||
* Anyja születési neve, születési hely/idő.
|
||||
* Okmányadatok: Személyi igazolvány és Jogosítvány száma + lejárati idők.
|
||||
* Járműkategóriák (pl. A, B, C) és speciális engedélyek (hajó, repülő).
|
||||
* **Finalizálás (Atomi tranzakció):**
|
||||
1. **Person:** Identitás rögzítése a JSONB dokumentumtárral.
|
||||
2. **Wallet:** Pénztárca nyitása 0 egyenleggel.
|
||||
3. **Privát Flotta:** Automatikus szervezet létrehozása (`is_transferable = False`).
|
||||
4. **Tagság:** Felhasználó rögzítése a flotta tulajdonosaként.
|
||||
5. **Aktiválás:** `is_active = True` és hozzáférés a rendszerhez.
|
||||
|
||||
## II. TECHNIKAI SZABÁLYOK ÉS FIXEK
|
||||
* **Postgres Enum:** Minden szerepkört (role) kisbetűvel kell küldeni az adatbázis felé (`user`, nem `USER`).
|
||||
* **Dinamikus Paraméterek:** Az `auth.reward_days` (jutalom napok) és jutalék százalékok a `data.system_settings` táblából jönnek.
|
||||
* **Audit Trail:** Minden regisztrációs fázisról Audit Log készül.
|
||||
|
||||
# 🏁 REGISZTRÁCIÓS ÉS AUTH PROTOKOLL (v1.4)
|
||||
|
||||
## I. KÉTLÉPCSŐS ONBOARDING FOLYAMAT
|
||||
A rendszer a felhasználói élmény optimalizálása (UX) és a banki szintű biztonság érdekében két fázisra bontja a regisztrációt.
|
||||
|
||||
### 1. Fázis: "Lite" Regisztráció (Step 1)
|
||||
* **Cél:** Gyors belépés és a technikai User fiók létrehozása.
|
||||
* **Kötelező mezők:** Email, Jelszó, Vezetéknév, Keresztnév, Régiókód.
|
||||
* **Rendszeresemény:**
|
||||
* Létrejön a `data.users` rekord.
|
||||
* **Állapot:** `is_active = False`.
|
||||
* **Szerepkör:** Kötelezően kisbetűs `user` (Postgres Enum kényszerítés miatt).
|
||||
* **Email:** A rendszer azonnal kiküld egy ellenőrző emailt egy egyedi hash kóddal/linkkel.
|
||||
* **Token:** Az API egy korlátozott JWT tokent ad vissza, amely csak a Step 2 végpont elérésére jogosít (`status: pending_kyc`).
|
||||
|
||||
### 2. Fázis: KYC & Aktiválás (Step 2)
|
||||
* **Cél:** A valós identitás (Person) rögzítése és a gazdasági egységek inicializálása.
|
||||
* **Kötelező adatok (Identity Verification):**
|
||||
* **Személyes:** Anyja születési neve, születési hely, születési idő.
|
||||
* **Okmányok:** Személyi igazolvány száma és lejárati ideje.
|
||||
* **Jogosítvány:** Vezetői engedély száma, lejárata és kategóriák (pl. AM, A, B, C, CE).
|
||||
* **Speciális:** Hajóvezetői és repülőgép-vezetői engedély adatai (ha releváns).
|
||||
* **Működés:** A felhasználó csak ezen adatok kitöltése és sikeres ellenőrzése után válik teljes jogú taggá.
|
||||
|
||||
### 3. Fázis: Atomi Tranzakció (Finalization)
|
||||
A KYC adatok beküldésekor a rendszer egyetlen adatbázis-tranzakcióban (`Atomic`) hajtja végre az alábbiakat:
|
||||
1. **Person létrehozása:** A KYC adatok és okmánymásolatok (JSONB) rögzítése.
|
||||
2. **Wallet inicializálás:** 0 Coin és 0 XP egyenleggel.
|
||||
3. **Privát Flotta (Private Org):** Létrejön a felhasználó saját cége (`OrgType.INDIVIDUAL`), amely **nem átruházható** (`is_transferable = False`).
|
||||
4. **Aktiválás:** A User fiók `is_active = True` állapotba kerül.
|
||||
5. **Audit:** Bejegyzés az `audit_logs` táblába a teljes körű regisztrációról.
|
||||
|
||||
## II. MEGHÍVÓ ÉS JUTALÉK LOGIKA
|
||||
* **Invite Tokenek:**
|
||||
* `REG_ONLY`: Általános meghívó, beköti a felhasználót a 10-5-2% jutalék láncba.
|
||||
* `COMPANY_JOIN`: Meghatározott szervezetbe hív (CEO, Flotta Manager vagy Sofőr szerepkörbe).
|
||||
* **Kredit Jóváírás:**
|
||||
* Csak az **első** Prémium csomag befizetésekor történik jutalékfizetés a meghívási láncban.
|
||||
* A százalékos mérték (10%, 5% vagy 2%) a tranzakció pillanatában érvényes admin beállítások alapján rögzül (Snapshot).
|
||||
|
||||
## III. TECHNIKAI ÉS BIZTONSÁGI SZABÁLYOK
|
||||
* **Enum Case Sensitivity:** A PostgreSQL `userrole` típusa miatt minden Enum értéket (user, admin, driver, service, fleet_manager) szigorúan **kisbetűvel** kell kezelni.
|
||||
* **Dinamikus Paraméterezés:** Tilos fix értékeket (hardcoded) használni. Az alábbiakat a `data.system_settings` táblából kell lekérni:
|
||||
* `auth.reward_days`: Regisztrációkor járó prémium napok (alapértelmezett: 14).
|
||||
* `referral.level1-3`: Jutalék szintek mértéke.
|
||||
* **Email Manager:** A `send_email` hívásakor tilos a `subject` paraméter átadása; a szerviz a `template_key` alapján automatikusan generálja azt.
|
||||
* **Szigorú Helyreállítás:** A banki szintű KYC után a jelszó-visszaállítás kérhető a Person adatok (Anyja neve, Okmány szám) megadásával is.
|
||||
|
||||
## IV. SOCIAL AUTH (GOOGLE / FACEBOOK)
|
||||
* Social Auth esetén a Step 1 lerövidül, de a **Step 2 (KYC) kötelező** marad az aktiváláshoz.
|
||||
* Amíg a KYC hiányzik, a felhasználó "GUEST" státuszban marad, és nem fér hozzá a flottakezeléshez.
|
||||
|
||||
# 🏁 REGISZTRÁCIÓS ÉS AUTH PROTOKOLL (v1.1)
|
||||
|
||||
## 1. Hibakezelési Jegyzet (TypeError fix)
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
(Mit csinálunk most?)
|
||||
# 🗺️ ROADMAP & TECH DEBT
|
||||
|
||||
# 🗺️ ROADMAP & TECH DEBT (v1.4)
|
||||
|
||||
## 🚧 SPRINT 1 (Azonnali - Stabilitás)
|
||||
1. **Frontend Config:** Hardkódolt IP-k cseréje `.env` változókra.
|
||||
2. **Step 1 Regisztráció Fix:** A meglévő endpoint átalakítása "Lite" regisztrációra (csak User létrehozás, `is_active=False`).
|
||||
3. **Enum Case Sensitivity:** Minden DB query felülvizsgálata, hogy a `role` mező kényszerítve kisbetűs legyen.
|
||||
4. **Security Module:** `create_access_token` és `verify_password` funkciók véglegesítése a `core/security.py`-ban.
|
||||
|
||||
## 🚧 SPRINT 2 (KYC & Onboarding)
|
||||
1. **Step 2 KYC Endpoint:** `POST /api/v1/auth/complete-kyc` megvalósítása.
|
||||
2. **Atomi Tranzakció Logic:** A Person, Wallet és Private Org egyidejű létrehozása a KYC beküldésekor.
|
||||
3. **Verification Email:** Aktiváló link generálása és kiküldése hash kóddal.
|
||||
4. **Admin UI Settings:** Felület a `system_settings` tábla kezeléséhez.
|
||||
|
||||
## 📅 SPRINT 3 (Marketplace MVP)
|
||||
1. **OCR Pipeline:** Számla/Okmány fotó feltöltés MinIO-ba + AI validáció teszt.
|
||||
2. **Service Request:** Frontend űrlap ajánlatkéréshez.
|
||||
|
||||
|
||||
# ROADMAP & TECH DEBT (v1.0)
|
||||
## 🚧 SPRINT 1 (Azonnali)
|
||||
1. **Frontend Config:** Hardkódolt IP-k cseréje `.env` változókra.
|
||||
2. **Person Migráció:** DB szkript futtatása (User -> Person).
|
||||
|
||||
@@ -28,3 +28,51 @@
|
||||
- **Economy:** 10-5-2% jutalékrendszer és Voucher/Coupon logika specifikálva.
|
||||
- **Synergy:** Céges VIP és privát flotta közötti kedvezmény-szinergia kidolgozva.
|
||||
- **Invitations:** Meghívó limitációs és anti-spam logika rögzítve.
|
||||
|
||||
# 📓 CHANGELOG - SERVICE FINDER
|
||||
|
||||
## [1.2.1] - 2026-02-05
|
||||
### ✅ Hozzáadva (Added)
|
||||
- **Multi-step Social Auth:** `User` modell bővítve `social_provider` és `social_id` mezőkkel.
|
||||
- **Flotta Tulajdonjog:** `Organization` modellben `is_transferable` flag implementálva (Individual flotta zárolva).
|
||||
- **Referral Snapshot:** Előkészítve a 10-5-2%-os jutalékrendszer adatmodellje.
|
||||
|
||||
### 🛠️ Javítva (Fixed)
|
||||
- **SQLAlchemy Mapper:** Megszűnt a `UserVehicle` KeyError hiba a string-alapú hivatkozásokkal.
|
||||
- **Duplikáció:** `Vehicle` osztály duplikációja eltávolítva a `vehicle.py`-ból.
|
||||
- **Indentation Error:** `security.py` bcrypt indentációs hiba javítva.
|
||||
|
||||
### ⚠️ Megjegyzés
|
||||
- Alembic migráció szükséges az új `Organization` és `User` mezőkhöz.
|
||||
|
||||
📓 CHANGELOG (Rögzítendő változások)
|
||||
|
||||
Mivel kérted, itt van a változások listája, amit a teszt után beírhatunk:
|
||||
|
||||
Fixed: UserRole enum validációs hiba (Postgres userrole típus mostantól kisbetűs értéket kap).
|
||||
|
||||
Added: Teljes banki KYC integráció a regisztrációba (Személyi, Jogosítvány, Speciális engedélyek).
|
||||
|
||||
Added: Atomi tranzakció részeként automatikus OrganizationMember létrehozás a privát flottához.
|
||||
|
||||
Added: Audit log rögzítése minden sikeres regisztrációról.
|
||||
|
||||
Added: Dinamikus paraméterkezelés (system_settings) a 14 napos jutalomhoz.
|
||||
|
||||
Elvárt eredmény: A tranzakció végén létrejön a Person, a Wallet (0 Coin) és a Private Fleet (is_transferable=False). A User is_active értéke True lesz.
|
||||
|
||||
3. Debugging Checklist
|
||||
|
||||
500 Error? Ellenőrizd a docker logs -f service_finder_api kimenetét. Ha "UndefinedColumn", akkor hiányzik egy SQL mező. Ha "InvalidTextRepresentation", akkor Enum hiba (nagybetűs string).
|
||||
|
||||
Üres Swagger? Ellenőrizd az importokat a security.py-ban és a sémákat az endpoints/auth.py-ban.
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 💡 Javaslatom a dokumentáció kiegészítésére:
|
||||
A fejlesztések rendben tartásához javaslom a **`17_DEVELOPER_NOTES_AND_PITFALLS.md`** fájl aktív használatát is, amit az előző körben küldtem. Ez segít megelőzni, hogy a fejlesztőcsapat újra belefusson a Postgres Enum vagy a `Base.metadata.create_all` korlátaiba.
|
||||
|
||||
|
||||
|
||||
**Holnap reggel frissíted a GEM beállításokat is?** Ha igen, a következő lépésben elkészíthetem neked a Step 2 (KYC) végleges Pydantic sémáját és a `complete-kyc` végpont vázlatát!
|
||||
@@ -1,3 +1,27 @@
|
||||
# 🧪 TESZTELÉSI ÉS ÉLESÍTÉSI ÚTMUTATÓ (v1.4)
|
||||
|
||||
## 1. Előkészületek és Környezet
|
||||
1. **SQL Patch:** Meglévő adatbázis esetén futtasd a manuális frissítő SQL-t (mothers_name, social_provider, is_transferable oszlopok hozzáadása).
|
||||
2. **Enum Ellenőrzés:** Győződj meg róla, hogy a Postgres `userrole` típus tartalmazza a kisbetűs értékeket.
|
||||
3. **Docker Build:** `docker compose up -d --build` (Kényszeríti az új Python kód betöltését).
|
||||
|
||||
## 2. Regisztrációs Teszt Forgatókönyvek
|
||||
|
||||
### A) Step 1: Lite Regisztráció (Clean Test)
|
||||
- **Endpoint:** `POST /api/v1/auth/register`
|
||||
- **Elvárt eredmény:** 201 Created, `access_token` visszaadva, de a DB-ben a User `is_active = False` és nincs hozzá Person rekord.
|
||||
|
||||
### B) Step 2: KYC Kitöltés (Advanced Test)
|
||||
- **Endpoint:** `POST /api/v1/auth/complete-kyc`
|
||||
- **Adat (JSON):**
|
||||
```json
|
||||
{
|
||||
"mothers_name": "Minta Mária",
|
||||
"id_card_number": "AB123456",
|
||||
"driver_license_categories": ["A", "B"],
|
||||
"boat_license_number": "H-99999"
|
||||
}
|
||||
|
||||
# 🧪 TESZTELÉSI ÉS ÉLESÍTÉSI ÚTMUTATÓ (v1.0)
|
||||
|
||||
## 1. Előkészületek a távoli teszteléshez
|
||||
|
||||
19
docs/V01_gemini/17_DEVELOPER_NOTES_AND_PITFALLS.md
Normal file
19
docs/V01_gemini/17_DEVELOPER_NOTES_AND_PITFALLS.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 🛠️ DEVELOPER NOTES & TROUBLESHOOTING
|
||||
|
||||
## 1. ADATBÁZIS ÉS SQL FIXEK
|
||||
### Postgres Enum Case Sensitivity
|
||||
* **Probléma:** Az SQLAlchemy Enum típusa és a Postgres Enum típusa ütközhet, ha a Python kódban nagybetűs stringet (`USER`) küldünk.
|
||||
* **Megoldás:** Mindig használd a `.value` property-t vagy kényszerítsd a kisbetűs stringet: `role="user"`.
|
||||
|
||||
### Tábla oszlopok frissítése
|
||||
* **Probléma:** A `Base.metadata.create_all` nem adja hozzá az új oszlopokat a már meglévő táblákhoz.
|
||||
* **Megoldás:** Új mező esetén (pl. `social_provider`, `mothers_name`) manuális `ALTER TABLE` parancsot kell futtatni vagy Alembic migrációt generálni.
|
||||
|
||||
## 2. BACKEND API HIBÁK
|
||||
### ImportError: create_access_token
|
||||
* **Ok:** A `app.core.security` modulban hiányzott a funkció, vagy elavult volt az import az `endpoints/auth.py`-ban.
|
||||
* **Javítás:** A `security.py`-nak tartalmaznia kell a `jose` könyvtárat használó tokengenerálást.
|
||||
|
||||
### Üres Swagger (OpenAPI) felület
|
||||
* **Ok:** Ha az SQLAlchemy Mapper vagy egy Pydantic séma importja hibás, a FastAPI nem tudja legenerálni a dokumentációt.
|
||||
* **Javítás:** Ellenőrizd a `docker logs` kimenetét indításkor, keresd a `MapperConfigurationError` vagy `ImportError` sorokat.
|
||||
Reference in New Issue
Block a user