10 KiB
🔧 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_frontendkonténer jelenleg FUT (742MiB RAM, 0.42%), az OOM korábban történt. - A
Dockerfile.devnode:20-slim-et használ, a CMDnpm run dev(Nuxt dev mode SSR + HMR). - A
docker-compose.ymlszekcióban nincsNODE_OPTIONSkörnyezeti változó és nincsdeploy.resources.limits.memorybeállítva. - A bot-scanner forgalom (
.envprobing) 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.walletsid=12, earned=0, purchased=0, service_coins=0, currency=HUF, org_id=45 - Subscription: RENDBEN —
finance.user_subscriptionsid=9, tier_id=13 (private_free_v1), is_active=true
6 aktív user-nek NINCS walletje és subscription-je:
| ID | 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 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
Módosítás — a sf_admin_frontend szekcióban:
-
Az
environmentblokkhoz add hozzá:- NODE_OPTIONS=--max-old-space-size=4096 -
Adj hozzá egy
deployblokkot:deploy: resources: limits: memory: 8G
A módosított szekció:
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:
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
Cél: Minden is_active = true user számára biztosítja:
- Personal Organization (
fleet.organizations) — ha még nincs - Wallet (
identity.wallets) — ha még nincs - 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:
#!/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:
docker compose exec sf_api python3 /app/backend/scripts/repair_user_relations.py
Ellenőrzés:
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 |
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 |
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
- superadmin (ID=1): Lehet hogy szándékosan nincs walletje. Ha nem kívánatos, skip-elni kell ID=1-et a scriptben.
- Organization folder_slug: Egyedi kell legyen. A
user-{id}-{timestamp}minta garantálja. - Tier ID=13: Ellenőrizve — létezik
system.subscription_tiers-ben,tier_level=0. - Docker restart: ~30-60 másodperc downtime a Nuxt újraépítése miatt.
- A repair script IDEMPOTENS: Többször futtatható, nem duplikál.