# 🔧 Admin OOM Crash Fix & User Relations Repair — Code Mode Hand-off **Author:** Architect Mode **Date:** 2026-07-26 **Target Mode:** `code` (Fast Coder) --- ## 📊 Diagnosztikai ÖsszefoglalĂł ### Issue 1: Admin Frontend OOM (`admin.servicefinder.hu` → 500) **Jelenlegi ĂĄllapot:** - A [`sf_admin_frontend`](docker-compose.yml:281) kontĂ©ner jelenleg FUT (742MiB RAM, 0.42%), az OOM korĂĄbban törtĂ©nt. - A [`Dockerfile.dev`](frontend_admin/Dockerfile.dev:1) `node:20-slim`-et hasznĂĄl, a CMD `npm run dev` (Nuxt dev mode SSR + HMR). - A [`docker-compose.yml`](docker-compose.yml:289) szekciĂłban **nincs** `NODE_OPTIONS` környezeti vĂĄltozĂł Ă©s **nincs** `deploy.resources.limits.memory` beĂĄllĂ­tva. - A bot-scanner forgalom (`.env` probing) csak Vue Router warningokat generĂĄl, nem ez okozza az OOM-t. **Root Cause:** A Nuxt 3 dev mĂłdban az SSR (Server-Side Rendering) + HMR (Hot Module Replacement) + a dashboard oldal API hĂ­vĂĄsa (`/api/v1/admin/users/stats`) egyĂŒtt kĂ©pes kimerĂ­teni a Node.js default heap limitjĂ©t (~2GB). A `node:20-slim` image-nek nincs explicit `--max-old-space-size` beĂĄllĂ­tĂĄsa. ### Issue 2: User 86 Wallet Status & Missing User Relations **User 86 (`zs.gyongyossy@gmail.com`) stĂĄtusza:** - Wallet: **RENDBEN** — `identity.wallets` id=12, earned=0, purchased=0, service_coins=0, currency=HUF, org_id=45 - Subscription: **RENDBEN** — `finance.user_subscriptions` id=9, tier_id=13 (`private_free_v1`), is_active=true **6 aktĂ­v user-nek NINCS walletje Ă©s subscription-je:** | ID | Email | Wallet | Subscription | |----|-------|--------|-------------| | **1** | `superadmin@profibot.hu` | ❌ MISSING | ❌ MISSING | | 136 | `no_perm_1782770270_8c9970@test.com` | ❌ MISSING | ❌ MISSING | | 138 | `no_perm_1782778366_243628@test.com` | ❌ MISSING | ❌ MISSING | | 153 | `commission_test_gen2@test.com` | ❌ MISSING | ❌ MISSING | | 154 | `commission_test_gen1@test.com` | ❌ MISSING | ❌ MISSING | | 155 | `commission_test_buyer@test.com` | ❌ MISSING | ❌ MISSING | **VĂ©gpont viselkedĂ©s:** [`get_wallet_summary`](backend/app/services/billing_engine.py:598) `ValueError`-t dob ha nincs wallet → a vĂ©gpont 404-et ad. Ez helyes. --- ## đŸ› ïž JavĂ­tĂĄsi Terv ### FIX-1: Admin Frontend Memory Limit **FĂĄjl:** [`docker-compose.yml`](docker-compose.yml:281-299) **MĂłdosĂ­tĂĄs — a `sf_admin_frontend` szekciĂłban:** 1. Az `environment` blokkhoz add hozzĂĄ: ```yaml - NODE_OPTIONS=--max-old-space-size=4096 ``` 2. Adj hozzĂĄ egy `deploy` blokkot: ```yaml deploy: resources: limits: memory: 8G ``` **A mĂłdosĂ­tott szekciĂł:** ```yaml sf_admin_frontend: build: context: ./frontend_admin dockerfile: Dockerfile.dev container_name: sf_admin_frontend env_file: .env ports: - "8502:8502" environment: - NUXT_PORT=8502 - NUXT_HOST=0.0.0.0 - NUXT_PUBLIC_API_BASE_URL=https://dev.servicefinder.hu - NODE_OPTIONS=--max-old-space-size=4096 # ← NEW volumes: - ./frontend_admin:/app - /app/node_modules networks: - sf_net - shared_db_net restart: unless-stopped deploy: # ← NEW resources: limits: memory: 8G ``` **FuttatĂĄs:** ```bash docker compose up -d --force-recreate sf_admin_frontend docker compose logs --tail=20 sf_admin_frontend ``` --- ### FIX-2: User Relations Repair Script **FĂĄjl lĂ©trehozĂĄsa:** [`backend/scripts/repair_user_relations.py`](backend/scripts/repair_user_relations.py) **CĂ©l:** Minden `is_active = true` user szĂĄmĂĄra biztosĂ­tja: 1. Personal Organization (`fleet.organizations`) — ha mĂ©g nincs 2. Wallet (`identity.wallets`) — ha mĂ©g nincs 3. User Subscription (`finance.user_subscriptions`, tier=private_free_v1) — ha mĂ©g nincs **AdatbĂĄzis sĂ©ma referencia:** | TĂĄbla | SĂ©ma | Kulcs mezƑk | |-------|------|-------------| | `wallets` | `identity` | id (serial PK), user_id (FK), earned_credits, purchased_credits, service_coins, currency, organization_id | | `user_subscriptions` | `finance` | id (serial PK), user_id (FK), tier_id (FK→system.subscription_tiers), valid_from, is_active | | `organizations` | `fleet` | id (serial PK), name, full_name, display_name, folder_slug, owner_id (FK), org_type, status, is_active, ... | **AlapĂ©rtelmezett Ă©rtĂ©kek:** - **Wallet:** `earned_credits=0`, `purchased_credits=0`, `service_coins=0`, `currency='HUF'` - **Subscription:** `tier_id=13` (`private_free_v1`), `valid_from=now()`, `is_active=true` - **Organization:** `name="{user.email}'s Garage"`, `org_type='individual'`, `status='active'`, `owner_id=user.id` **Script pszeudokĂłd:** ```python #!/usr/bin/env python3 """ User Relations Repair Script — Idempotens: csak hiĂĄnyzĂł rekordokat hoz lĂ©tre. FuttatĂĄs: docker compose exec sf_api python3 /app/backend/scripts/repair_user_relations.py """ import asyncio, sys sys.path.insert(0, '/app') sys.path.insert(0, '/app/backend') from app.core.database import async_session_factory from sqlalchemy import select from app.models.identity import User, Wallet from app.models.finance import UserSubscription from app.models.fleet import Organization from datetime import datetime, timezone from decimal import Decimal DEFAULT_TIER_ID = 13 DEFAULT_CURRENCY = "HUF" async def ensure_organization(db, user): result = await db.execute(select(Organization).where(Organization.owner_id == user.id)) org = result.scalar_one_or_none() if org: return org.id folder_slug = f"user-{user.id}-{int(datetime.now(timezone.utc).timestamp())}" org = Organization( name=f"{user.email}'s Garage", full_name=f"{user.email}'s Personal Garage", display_name=f"{user.email}'s Garage", folder_slug=folder_slug, owner_id=user.id, org_type="individual", status="active", is_active=True, is_deleted=False, subscription_plan="FREE", base_asset_limit=5, purchased_extra_slots=0, notification_settings={}, external_integration_config={}, is_verified=False, first_registered_at=datetime.now(timezone.utc), current_lifecycle_started_at=datetime.now(timezone.utc), lifecycle_index=1, is_anonymized=False, default_currency="HUF", country_code="HU", language="hu", is_ownership_transferable=False, visual_settings={}, is_affiliate_partner=False, aliases=[], tags=[], settings={}, custom_features={}, ) db.add(org) await db.flush() return org.id async def ensure_wallet(db, user, org_id): result = await db.execute(select(Wallet).where(Wallet.user_id == user.id)) if result.scalar_one_or_none(): return False wallet = Wallet(user_id=user.id, earned_credits=Decimal('0'), purchased_credits=Decimal('0'), service_coins=Decimal('0'), currency=DEFAULT_CURRENCY, organization_id=org_id) db.add(wallet) await db.flush() return True async def ensure_subscription(db, user): result = await db.execute(select(UserSubscription).where(UserSubscription.user_id == user.id)) if result.scalar_one_or_none(): return False sub = UserSubscription(user_id=user.id, tier_id=DEFAULT_TIER_ID, valid_from=datetime.now(timezone.utc), is_active=True) db.add(sub) await db.flush() return True async def repair_all_users(): async with async_session_factory() as db: result = await db.execute(select(User).where(User.is_active == True)) users = result.scalars().all() stats = {"total": len(users), "wallets_created": 0, "subs_created": 0, "errors": []} for user in users: try: org_id = await ensure_organization(db, user) if await ensure_wallet(db, user, org_id): stats["wallets_created"] += 1 if await ensure_subscription(db, user): stats["subs_created"] += 1 except Exception as e: stats["errors"].append(f"User {user.id}: {e}") await db.commit() print(f"=== REPAIR: total={stats['total']} wallets_new={stats['wallets_created']} subs_new={stats['subs_created']} errors={len(stats['errors'])}") for e in stats["errors"]: print(f" ERR: {e}") if __name__ == "__main__": asyncio.run(repair_all_users()) ``` **FuttatĂĄs:** ```bash docker compose exec sf_api python3 /app/backend/scripts/repair_user_relations.py ``` **EllenƑrzĂ©s:** ```sql SELECT u.id, u.email, w.id as wallet_id, s.id as sub_id FROM identity.users u LEFT JOIN identity.wallets w ON u.id = w.user_id LEFT JOIN finance.user_subscriptions s ON u.id = s.user_id WHERE u.id IN (1, 136, 138, 153, 154, 155); ``` --- ### FIX-3 (OPTIONAL): get_wallet_summary Auto-Create **Architect döntĂ©s:** **NEM mĂłdosĂ­tjuk.** A 404 helyes vĂĄlasz. A repair script utĂĄn a problĂ©ma megszƱnik. A wallet auto-create a regisztrĂĄciĂłs flow-ban garantĂĄlandĂł — kĂŒlön feladat. --- ## 📋 VĂ©grehajtĂĄsi Sorrend (Code Mode) | # | Feladat | FĂĄjl | Parancs | |---|---------|------|---------| | 1 | `NODE_OPTIONS` + memory limit | [`docker-compose.yml`](docker-compose.yml:289) | SzerkesztĂ©s | | 2 | Admin restart | — | `docker compose up -d --force-recreate sf_admin_frontend` | | 3 | Repair script lĂ©trehozĂĄsa | [`backend/scripts/repair_user_relations.py`](backend/scripts/repair_user_relations.py) | FĂĄjl lĂ©trehozĂĄsa | | 4 | Repair script futtatĂĄsa | — | `docker compose exec sf_api python3 /app/backend/scripts/repair_user_relations.py` | | 5 | SQL ellenƑrzĂ©s | — | MCP Postgres query | | 6 | BöngĂ©szƑ ellenƑrzĂ©s | — | `https://admin.servicefinder.hu/` | --- ## ⚠ KockĂĄzatok 1. **superadmin (ID=1):** Lehet hogy szĂĄndĂ©kosan nincs walletje. Ha nem kĂ­vĂĄnatos, skip-elni kell ID=1-et a scriptben. 2. **Organization folder_slug:** Egyedi kell legyen. A `user-{id}-{timestamp}` minta garantĂĄlja. 3. **Tier ID=13:** EllenƑrizve — lĂ©tezik `system.subscription_tiers`-ben, `tier_level=0`. 4. **Docker restart:** ~30-60 mĂĄsodperc downtime a Nuxt ĂșjraĂ©pĂ­tĂ©se miatt. 5. **A repair script IDEMPOTENS:** Többször futtathatĂł, nem duplikĂĄl.