Membership-System und Bug Fixing (inkl. Nutrition) #8

Merged
Lars merged 56 commits from develop into main 2026-03-21 08:48:57 +01:00
Showing only changes of commit d10f605d66 - Show all commits

View File

@ -2,11 +2,16 @@
Feature Management Endpoints for Mitai Jinkendo
Admin-only CRUD for features registry.
User endpoint for feature usage overview (Phase 3).
"""
from fastapi import APIRouter, HTTPException, Depends
from typing import Optional
from datetime import datetime
from fastapi import APIRouter, HTTPException, Header, Depends
from db import get_db, get_cursor, r2d
from auth import require_admin, require_auth, check_feature_access
from routers.profiles import get_pid
router = APIRouter(prefix="/api/features", tags=["features"])
@ -136,3 +141,82 @@ def check_access(feature_id: str, session: dict = Depends(require_auth)):
profile_id = session['profile_id']
result = check_feature_access(profile_id, feature_id)
return result
@router.get("/usage")
def get_feature_usage(x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""
User: Get usage overview for all active features (Phase 3: Frontend Display).
Returns list of all features with current usage, limits, and reset info.
Automatically includes new features from database - no code changes needed.
Response:
[
{
"feature_id": "weight_entries",
"name": "Gewichtseinträge",
"description": "Anzahl der Gewichtseinträge",
"category": "data",
"limit_type": "count",
"reset_period": "never",
"used": 5,
"limit": 10,
"remaining": 5,
"allowed": true,
"reset_at": null
},
...
]
"""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
# Get all active features (dynamic - picks up new features automatically)
cur.execute("""
SELECT id, name, description, category, limit_type, reset_period
FROM features
WHERE active = true
ORDER BY category, name
""")
features = [r2d(r) for r in cur.fetchall()]
result = []
for feature in features:
# Use existing check_feature_access to get usage and limits
# This respects user overrides, tier limits, and feature defaults
access = check_feature_access(pid, feature['id'])
# Get reset date from user_feature_usage
cur.execute("""
SELECT reset_at
FROM user_feature_usage
WHERE profile_id = %s AND feature_id = %s
""", (pid, feature['id']))
usage_row = cur.fetchone()
# Format reset_at as ISO string
reset_at = None
if usage_row and usage_row['reset_at']:
if isinstance(usage_row['reset_at'], datetime):
reset_at = usage_row['reset_at'].isoformat()
else:
reset_at = str(usage_row['reset_at'])
result.append({
'feature_id': feature['id'],
'name': feature['name'],
'description': feature.get('description'),
'category': feature.get('category'),
'limit_type': feature['limit_type'],
'reset_period': feature['reset_period'],
'used': access['used'],
'limit': access['limit'],
'remaining': access['remaining'],
'allowed': access['allowed'],
'reset_at': reset_at
})
return result