Files
service-finder/docs/v201/05_Architectural_Audit.md

4.7 KiB

🚨 ROO: MANDATORY ARCHITECTURAL AUDIT & DOCUMENTATION (v201)

Context

This document aligns the system with the "Personal Workspace" vision (Every user has a private organization/garage). It documents the current reality of the system to identify where the logic is broken.


🔍 Task 1: Forensic Logic Analysis

  • Database Schema: In the users table (identity.users), there is NO default_organization_id.
  • Current Linkage: A user is linked to their "Private Garage" (or multiple garages) via two relationships:
    1. Ownership: organizations.owner_id -> users.id
    2. Membership: organization_members.user_id -> users.id
  • Scope Field: The users table has a scope_id field (and scope_level), but it is often None because the registration/KYC flow does not explicitly set it to the newly created Organization ID.

2. Auth Payload

  • Login Response: Looked at backend/app/api/v1/endpoints/auth.py. The /login endpoint creates a JWT token with scope_id: str(user.scope_id) if user.scope_id else str(user.id). Since user.scope_id is often null, the token just duplicates the user.id.
  • /users/me Endpoint: It returns the UserResponse schema (from app.schemas.user), which includes scope_id: Optional[str] = None. It does not return a specific organization_id array or the user's active/default organization ID to the frontend.

3. Frontend Storage

  • Auth Store (frontend/src/stores/authStore.js): Fetches /users/me and stores the raw response in userProfile.value. It decodes the JWT to get the user's role, but it does not store or manage an active organization_id state.
  • Garage Store (frontend/src/stores/garageStore.js): Contains logic to add a vehicle, but the active organization is not retrieved from the user's profile. Instead, it relies on the payload passed from components and falls back to a hardcoded 1.

4. The "Hardcode" Hunt

Every file where organization_id: 1 or any hardcoded ID is used instead of a dynamic value from the store:

  1. frontend/src/components/actions/AddVehicleModal.vue: Explicitly hardcodes organizationId: 1 in its payload.
  2. frontend/src/views/AddVehicle.vue: Contains organization_id: 1 // default organization.
  3. frontend/src/stores/garageStore.js: Inside the addVehicle action, it explicitly defaults to organization_id: vehicle.organizationId || 1 // Default org ID.
  4. backend/app/api/v1/endpoints/assets.py: When creating expenses/maintenance costs, it falls back to organization_id=asset.current_organization_id or asset.owner_org_id or 1.

📝 Task 2: Real State of the System (The Gap)

Database Level

When a new user completes KYC (complete_kyc in AuthService), a new "Personal" Organization is dynamically created (e.g., {last_name} Széfe). The user is assigned as the OWNER in the OrganizationMember table, and the organizations.owner_id is set. However, user.scope_id is never updated to point to this new organization.

API Level

The frontend calls /login and /users/me, but neither endpoint returns the actual ID of the user's newly created Organization. The frontend receives only the generic UserResponse with scope_id: null. The frontend has no knowledge of which organization_id belongs to the logged-in user.

Frontend Level

Because the frontend (AddVehicleModal, AddVehicle view, and garageStore) does not receive the user's organization ID, it just gives up and blindly sends organization_id: 1 on every POST request to create a vehicle.

The Gap (Contradictions)

  1. The Vision: Every user has their own Organization/Garage.
  2. The Code: The Backend creates the Organization but doesn't tell the Frontend what its ID is.
  3. The Result: The Frontend ignores the Backend's logic and assigns every new vehicle to Organization 1 (usually the Superadmin or System's default organization), completely breaking data isolation and the "Private Garage" architecture.

🔬 Task 3: Verification of the "Tester_Pro" account

  • User: ID 28 (tester_pro@profibot.hu)
  • Database Status: Querying the database shows that User 28 has scope_id: None.
  • True Organization: In the organization_members table, User 28 belongs to Org ID: 15 (Name: "Profibot Test Fleet") with the role ADMIN.
  • Why the frontend does NOT use this ID:
    1. The API never provides Org ID: 15 to the frontend during login or profile fetch.
    2. AddVehicleModal.vue and garageStore.js are hardcoded to send 1 if they don't explicitly receive an ID. Since authStore doesn't know about 15, it sends 1. Consequently, Tester_Pro's vehicles are saved into the wrong organization.