felhasználói felületre pü
This commit is contained in:
251
plans/admin_crash_and_user_repair_blueprint.md
Normal file
251
plans/admin_crash_and_user_repair_blueprint.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# 🔧 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.
|
||||
Reference in New Issue
Block a user