admin felület fejlesztése garázs, előfizeztési csomagok

This commit is contained in:
Roo
2026-06-25 01:58:04 +00:00
parent 80a5d67f79
commit 52011606ff
334 changed files with 46636 additions and 1606 deletions

View File

@@ -5,17 +5,20 @@ Subscription Public API Endpoints.
Provides:
- GET /public — Public package catalog with dynamic pricing resolution
based on the user's organization country (or fallback to DEFAULT zone).
- GET /my — Current user's/org's subscription details with feature flags
- GET /feature-flags — All feature flags resolved for the current user
"""
from typing import List, Optional
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.api import deps
from app.db.session import get_db
from app.models.core_logic import SubscriptionTier
from app.models.core_logic import SubscriptionTier, OrganizationSubscription, UserSubscription
from app.models.identity import User
from app.models.marketplace.organization import Organization
from app.schemas.subscription import (
@@ -23,6 +26,7 @@ from app.schemas.subscription import (
SubscriptionTierResponse,
PricingZoneModel,
)
from app.services.subscription_service import SubscriptionService, SUBSCRIPTION_FEATURES
router = APIRouter()
@@ -174,3 +178,179 @@ async def get_public_subscriptions(
)
return response
# ── P0 Feature Flag Endpoints ─────────────────────────────────────────────────
@router.get(
"/my",
summary="Saját előfizetés részletei",
description=(
"Visszaadja a bejelentkezett felhasználó aktuális előfizetési adatait. "
"Először a szervezeti előfizetést (OrganizationSubscription) ellenőrzi, "
"ha nincs, akkor a felhasználói előfizetést (UserSubscription). "
"Tartalmazza a tier adatokat, allowances, pricing, feature_capabilities mezőket."
),
)
async def get_my_subscription(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(deps.get_current_user),
):
"""
Visszaadja a felhasználó aktuális előfizetési adatait.
**Feloldási sorrend:**
1. Aktív szervezet OrganizationSubscription (ha van active_organization_id)
2. UserSubscription (ha nincs org subscription)
3. Fallback: alapértelmezett 'free' adatok
**Response struktúra:**
```json
{
"subscription": {
"tier_id": 16,
"tier_name": "corp_premium_v1",
"display_name": "Céges Prémium",
"valid_from": "...",
"valid_until": "...",
"is_active": true,
"allowances": {"max_vehicles": 20, ...},
"pricing": {"monthly_price": 29.99, ...},
"feature_capabilities": {"can_export_data": true, ...}
},
"tier": "premium",
"features": {
"export_csv": true,
"analytics_tco": true,
...
},
"source": "organization" | "user" | "default"
}
```
"""
user_id = current_user.id
active_org_id = getattr(current_user, "active_organization_id", None)
subscription_data = None
source = "default"
# 1. Try org subscription first
if active_org_id:
org_sub = await SubscriptionService.get_org_subscription_details(db, active_org_id)
if org_sub:
subscription_data = org_sub
source = "organization"
# 2. Fallback to user subscription
if not subscription_data:
user_sub = await SubscriptionService.get_user_subscription_details(db, user_id)
if user_sub:
subscription_data = user_sub
source = "user"
# 3. Resolve tier and feature flags
user_tier = await SubscriptionService.get_user_tier(db, user_id)
feature_flags = await SubscriptionService.get_user_feature_flags(db, user_id)
return {
"subscription": subscription_data,
"tier": user_tier,
"features": feature_flags.get("features", {}),
"expires_at": feature_flags.get("expires_at"),
"source": source,
}
@router.get(
"/feature-flags",
summary="Feature flag-ek listázása",
description=(
"Visszaadja az összes feature flag állapotát a felhasználó "
"előfizetési szintje alapján. A frontend useFeatureFlag() composable "
"ezt a végpontot használja a funkciók engedélyezéséhez/letiltásához."
),
)
async def get_feature_flags(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(deps.get_current_user),
):
"""
Visszaadja az összes feature flag állapotát.
**Response:**
```json
{
"tier": "premium",
"features": {
"export_csv": true,
"analytics_tco": true,
"analytics_detailed": false,
"multi_vehicle": true,
"unlimited_vehicles": true,
"subscription_management": true,
"vehicle_tracking": true,
"cost_tracking": true,
"service_booking": true,
"advanced_reports": true,
"team_management": true,
"priority_support": true,
"white_label": false,
"api_webhooks": false,
"custom_integrations": false
},
"expires_at": "2026-07-24T12:00:00"
}
```
"""
feature_flags = await SubscriptionService.get_user_feature_flags(
db, current_user.id
)
return feature_flags
@router.get(
"/check/{feature_key}",
summary="Egy adott feature flag ellenőrzése",
description=(
"Ellenőrzi, hogy a felhasználó hozzáfér-e egy adott funkcióhoz. "
"Használható a frontend által komponens szintű guard-okhoz."
),
)
async def check_feature_access(
feature_key: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(deps.get_current_user),
):
"""
Ellenőrzi egy adott feature elérhetőségét.
**Response:**
```json
{
"feature_key": "analytics_tco",
"granted": true,
"tier": "premium",
"required_tier": "premium"
}
```
Hibák:
- 404: Ismeretlen feature_key
"""
if feature_key not in SUBSCRIPTION_FEATURES:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Unknown feature key: {feature_key}",
)
user_tier = await SubscriptionService.get_user_tier(db, current_user.id)
granted = SubscriptionService.can_access_feature(user_tier, feature_key)
required_tier = SUBSCRIPTION_FEATURES[feature_key]
return {
"feature_key": feature_key,
"granted": granted,
"tier": user_tier,
"required_tier": required_tier,
}