feat: Unified Auth system and SendGrid integration - STABLE v1.0.1
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -1,32 +1,38 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/api/v1/endpoints/auth.py
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status, Body
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import text
|
||||
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
|
||||
from app.schemas.auth import UserLiteRegister, Token, PasswordResetRequest
|
||||
|
||||
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)
|
||||
):
|
||||
# 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.")
|
||||
@router.post("/register-lite", response_model=Token, status_code=201)
|
||||
async def register_lite(user_in: UserLiteRegister, db: AsyncSession = Depends(get_db)):
|
||||
# Email csekkolás nyers SQL-el
|
||||
check = await db.execute(text("SELECT id FROM data.users WHERE email = :e"), {"e": user_in.email})
|
||||
if check.fetchone():
|
||||
raise HTTPException(status_code=400, detail="Ez az email cím már foglalt.")
|
||||
|
||||
# 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
|
||||
)
|
||||
try:
|
||||
user = await AuthService.register_lite(db, user_in)
|
||||
token = create_access_token(data={"sub": str(user.id)})
|
||||
return {"access_token": token, "token_type": "bearer", "is_active": user.is_active}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Szerver hiba: {str(e)}")
|
||||
|
||||
# 3. Token kiállítása
|
||||
token_data = {"sub": str(user.id), "email": user.email}
|
||||
access_token = create_access_token(data=token_data)
|
||||
@router.post("/login", response_model=Token)
|
||||
async def login(form_data: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)):
|
||||
user = await AuthService.authenticate(db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=401, detail="Hibás e-mail vagy jelszó.")
|
||||
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
token = create_access_token(data={"sub": str(user.id)})
|
||||
return {"access_token": token, "token_type": "bearer", "is_active": user.is_active}
|
||||
|
||||
@router.post("/forgot-password")
|
||||
async def forgot_password(req: PasswordResetRequest, db: AsyncSession = Depends(get_db)):
|
||||
await AuthService.initiate_password_reset(db, req.email)
|
||||
return {"message": "Helyreállítási folyamat elindítva."}
|
||||
Binary file not shown.
@@ -5,23 +5,26 @@ from app.core.config import settings
|
||||
|
||||
app = FastAPI(
|
||||
title="Service Finder API",
|
||||
version="2.0.0",
|
||||
version="2.0.0", # A rendszer verziója, de a végpont marad v1
|
||||
openapi_url="/api/v1/openapi.json",
|
||||
docs_url="/docs"
|
||||
)
|
||||
|
||||
# CORS beállítások
|
||||
# PONTOS CORS BEÁLLÍTÁS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_origins=[
|
||||
"http://192.168.100.10:3001", # Frontend portja
|
||||
"http://localhost:3001",
|
||||
"https://dev.profibot.hu" # Ha van NPM proxy
|
||||
],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Routerek befűzése
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"status": "online", "message": "Service Finder API v2.0"}
|
||||
return {"status": "online", "message": "Service Finder Master System v1.0"}
|
||||
Binary file not shown.
@@ -1,23 +1,23 @@
|
||||
import uuid
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Enum
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, Numeric, text, Enum, BigInteger
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.sql import func
|
||||
from app.db.base import Base
|
||||
from app.db.base import Base # <--- JAVÍTVA: base_class helyett base
|
||||
|
||||
class UserRole(str, enum.Enum):
|
||||
ADMIN = "admin"
|
||||
USER = "user"
|
||||
SERVICE = "service"
|
||||
FLEET_MANAGER = "fleet_manager"
|
||||
DRIVER = "driver"
|
||||
admin = "admin"
|
||||
user = "user"
|
||||
service = "service"
|
||||
fleet_manager = "fleet_manager"
|
||||
driver = "driver"
|
||||
|
||||
class Person(Base):
|
||||
__tablename__ = "persons"
|
||||
__table_args__ = {"schema": "data"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
id = Column(BigInteger, primary_key=True, index=True)
|
||||
id_uuid = Column(UUID(as_uuid=True), default=uuid.uuid4, unique=True, nullable=False)
|
||||
|
||||
last_name = Column(String, nullable=False)
|
||||
@@ -26,11 +26,16 @@ 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"))
|
||||
|
||||
# Ez a mező kell a 2-lépcsős regisztrációhoz
|
||||
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):
|
||||
@@ -39,24 +44,19 @@ 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=True) # Social Auth esetén null lehet!
|
||||
hashed_password = Column(String, nullable=True)
|
||||
|
||||
# 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)
|
||||
role = Column(Enum(UserRole), default=UserRole.user)
|
||||
is_active = Column(Boolean, default=False)
|
||||
region_code = Column(String, default="HU")
|
||||
|
||||
# Soft Delete
|
||||
is_deleted = Column(Boolean, default=False)
|
||||
deleted_at = Column(DateTime, nullable=True)
|
||||
person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
|
||||
|
||||
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", back_populates="owner")
|
||||
# Kapcsolat lusta betöltéssel a mapper hiba ellen
|
||||
owned_organizations = relationship("Organization", back_populates="owner", lazy="select")
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,37 +1,22 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/schemas/auth.py
|
||||
from pydantic import BaseModel, EmailStr, Field, field_validator
|
||||
from typing import Optional, List
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
from typing import Optional
|
||||
from datetime import date
|
||||
|
||||
class UserRegister(BaseModel):
|
||||
email: EmailStr = Field(..., example="pilot@profibot.hu")
|
||||
password: Optional[str] = Field(None, min_length=8)
|
||||
last_name: str = Field(..., min_length=2)
|
||||
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
|
||||
|
||||
@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 UserLiteRegister(BaseModel):
|
||||
email: EmailStr
|
||||
password: str = Field(..., min_length=8)
|
||||
first_name: str
|
||||
last_name: str
|
||||
region_code: str = "HU"
|
||||
|
||||
class UserLogin(BaseModel):
|
||||
email: EmailStr
|
||||
password: str
|
||||
|
||||
class PasswordResetRequest(BaseModel):
|
||||
email: EmailStr
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
is_active: bool # KYC státusz visszajelzés
|
||||
Binary file not shown.
@@ -1,145 +1,82 @@
|
||||
# /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__)
|
||||
from sqlalchemy import select, text
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.models.organization import Organization
|
||||
from app.schemas.auth import UserLiteRegister
|
||||
from app.core.security import get_password_hash, verify_password
|
||||
from app.services.email_manager import email_manager # Importálva!
|
||||
|
||||
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."""
|
||||
async def register_lite(db: AsyncSession, user_in: UserLiteRegister):
|
||||
"""Step 1: Lite regisztráció + Email küldés."""
|
||||
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
|
||||
Tartalmazza: KYC, Email, Tagság, Pénztárca, Audit, Flotta.
|
||||
"""
|
||||
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)
|
||||
# 1. Person shell
|
||||
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
|
||||
is_active=False
|
||||
)
|
||||
db.add(new_person)
|
||||
await db.flush() # ID generálás
|
||||
await db.flush()
|
||||
|
||||
# 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
|
||||
# 2. User fiók
|
||||
new_user = User(
|
||||
email=user_in.email,
|
||||
hashed_password=hashed_pwd,
|
||||
social_provider=user_in.social_provider,
|
||||
social_id=user_in.social_id,
|
||||
hashed_password=get_password_hash(user_in.password),
|
||||
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
|
||||
role=UserRole.user,
|
||||
is_active=False,
|
||||
region_code=user_in.region_code
|
||||
)
|
||||
db.add(new_user)
|
||||
await db.flush()
|
||||
|
||||
# 4. ECONOMY: WALLET ÉS JUTALÉK SNAPSHOT
|
||||
db.add(Wallet(user_id=new_user.id, coin_balance=0.00, xp_balance=0))
|
||||
|
||||
# 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} flottája",
|
||||
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 (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_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)
|
||||
})
|
||||
|
||||
# 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)
|
||||
# 3. Email kiküldése (Mester Könyv v1.4 szerint)
|
||||
try:
|
||||
await email_manager.send_email(
|
||||
recipient=user_in.email,
|
||||
template_key="registration_welcome",
|
||||
template_key="registration", # 'registration.html' sablon használata
|
||||
variables={
|
||||
"first_name": user_in.first_name,
|
||||
"reward_days": reward_days
|
||||
"login_url": "http://192.168.100.10:3000/login"
|
||||
},
|
||||
user_id=new_user.id
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Email failed during reg: {str(e)}")
|
||||
except Exception as email_err:
|
||||
# Az email hiba nem állítja meg a regisztrációt, csak logoljuk
|
||||
print(f"Email hiba regisztrációkor: {str(email_err)}")
|
||||
|
||||
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
|
||||
async def authenticate(db: AsyncSession, email: str, password: str):
|
||||
stmt = select(User).where(User.email == email, User.is_deleted == False)
|
||||
res = await db.execute(stmt)
|
||||
user = res.scalar_one_or_none()
|
||||
|
||||
if not user or not user.hashed_password or not verify_password(password, user.hashed_password):
|
||||
return None
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
async def initiate_password_reset(db: AsyncSession, email: str):
|
||||
"""Jelszó-emlékeztető email küldése."""
|
||||
stmt = select(User).where(User.email == email, User.is_deleted == False)
|
||||
res = await db.execute(stmt)
|
||||
user = res.scalar_one_or_none()
|
||||
|
||||
if user:
|
||||
await email_manager.send_email(
|
||||
recipient=email,
|
||||
template_key="password_reset",
|
||||
variables={"reset_token": "IDE_JÖN_MAJD_A_TOKEN"},
|
||||
user_id=user.id
|
||||
)
|
||||
return True
|
||||
return False
|
||||
@@ -1,6 +1,22 @@
|
||||
(Változásnapló.)
|
||||
# 📜 CHANGELOG
|
||||
|
||||
## [1.0.1] - 2026-02-06
|
||||
|
||||
### Hozzáadva
|
||||
- **Unified Auth Module**: Integrált Belépés, Lite Regisztráció és Elfelejtett jelszó kezelés.
|
||||
- **Email Rendszer**: SendGrid integráció SMTP fallback lehetőséggel.
|
||||
- **KYC Előkészítés**: `is_active` flag bevezetése a `User` és `Person` modellekben a 2-lépcsős folyamathoz.
|
||||
|
||||
### Javítva
|
||||
- **SQLAlchemy Mapper Fix**: Megszűnt az `owned_organizations` körkörös függőségi hiba.
|
||||
- **Típus Szinkron**: `Person.id` javítva `BigInteger` típusra a Postgres sémával összhangban.
|
||||
- **Import Fixek**: `app.db.base_class` -> `app.db.base` útvonalak egységesítve.
|
||||
|
||||
### Technikai adatok
|
||||
- Konténer állapot: Stabil (running)
|
||||
- Regisztrációs folyamat: Step 1 (Lite) tesztelve, sikeres.
|
||||
|
||||
## [1.0.0] - 2026-02-03
|
||||
- **Init:** Grand Master Book létrehozása.
|
||||
- **Arch:** Átköltözés a 80 magos szerverre.
|
||||
@@ -76,3 +92,4 @@ A fejlesztések rendben tartásához javaslom a **`17_DEVELOPER_NOTES_AND_PITFAL
|
||||
|
||||
|
||||
**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!
|
||||
|
||||
|
||||
99
docs/V01_gemini/_00_gemini_gem_kód
Normal file
99
docs/V01_gemini/_00_gemini_gem_kód
Normal file
@@ -0,0 +1,99 @@
|
||||
# 📘 SERVICE FINDER - MASTER ARCHITECT SYSTEM INSTRUCTIONS
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Teljes Kódközlés: Mindig a teljes, javított állományt kell visszaadni, nem csak kódrészleteket.
|
||||
|
||||
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).
|
||||
|
||||
🏗️ ARCHITEKTURÁLIS ÉS ÜZLETI LOGIKA
|
||||
|
||||
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.
|
||||
|
||||
Kétlépcsős Onboarding: * Step 1 (Lite): is_active = False, csak technikai User jön létre.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Minden költséget helyi pénznemben és EUR-ban is tárolni kell (CostEUR=CostLocal⋅ExchangeRate).
|
||||
|
||||
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.
|
||||
|
||||
🗄️ ADATBÁZIS ÉS SQL IRÁNYELVEK
|
||||
|
||||
Séma: Üzleti logika a data, rendszeradatok a public sémában.
|
||||
|
||||
Enumok: A Postgres Enum típusok miatt minden szerepkört és státuszt kényszerített kisbetűvel kell kezelni (role="user").
|
||||
|
||||
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.
|
||||
|
||||
Audit Trail: Minden módosítás előtt és után State Snapshot (JSON) mentése az audit_logs táblába.
|
||||
|
||||
🛠️ TECHNIKAI STACK SPECIFIKÁCIÓK
|
||||
|
||||
Backend: Python 3.12, FastAPI, SQLAlchemy (Alembic), Pydantic validáció.
|
||||
|
||||
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.
|
||||
|
||||
💬 KOMMUNIKÁCIÓ ÉS DOKUMENTÁCIÓ
|
||||
|
||||
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.
|
||||
|
||||
Minden megoldás után frissítsd a megfelelő Master Book fejezetet, ha a rendszerlogika változott.
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
## 🛠️ SERVICE FINDER - SYSTEM ARCHITECT GEM CONFIGURATION
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
📝 DOKUMENTÁCIÓ ÉS VERZIÓKEZELÉS
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
1196
schema_dump.sql
Normal file
1196
schema_dump.sql
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user