feat: add GET /api/features/usage endpoint (Phase 3)
All checks were successful
Deploy Development / deploy (push) Successful in 36s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s

- Add user-facing usage overview endpoint
- Returns all features with usage, limits, reset info
- Fully dynamic - automatically includes new features
- Phase 3: Frontend Display preparation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-21 06:32:43 +01:00
parent 4e846605e9
commit d10f605d66

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