docs: add Feature-Registry Pattern architecture for v9c
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s

Replaced hardcoded tier limits with flexible Feature-Registry Pattern:
- features table: All limitable features (weight, AI, photos, export, etc.)
- tier_limits: Tier x Feature matrix (admin-configurable)
- user_feature_restrictions: Individual user overrides
- user_feature_usage: Usage tracking with configurable reset periods

Key capabilities:
- Add new limitable features without schema changes
- Admin UI for matrix-based limit configuration
- User-level overrides for specific restrictions
- Access hierarchy: User restriction > Tier limit > Feature default

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-19 12:40:25 +01:00
parent 95c57de8d0
commit 26f8bcf86d

217
CLAUDE.md
View File

@ -151,18 +151,70 @@ Beispiele:
- gift_coupons_per_month: 3
```
#### **tiers** - Tier-Konfiguration (Admin-editierbar)
#### **tiers** - Tier-Konfiguration (vereinfacht)
```sql
id, slug, name, description, sort_order
max_weight_entries, max_ai_calls_month, max_photos (NULL = unbegrenzt)
allow_export, allow_activity_import, allow_nutrition_import
allow_fitness_connectors, allow_gift_coupons
id, slug, name, description, sort_order, active
Initial Tiers:
- free: 30 Einträge, 0 KI, 5 Fotos, kein Export/Import
- basic: unbegrenzt, 10 KI/Monat, 50 Fotos, Export/Import
- premium: unbegrenzt, unbegrenzt KI, unbegrenzt Fotos, alle Features
- selfhosted: unbegrenzt alles (für Lars)
- free, basic, premium, selfhosted
Limits sind jetzt in tier_limits Tabelle (siehe unten)!
```
#### **features** - Feature-Registry (alle limitierbaren Features)
```sql
id, slug, name, category, description, unit
default_limit (NULL = unbegrenzt)
reset_period ('monthly' | 'daily' | 'never')
visible_in_admin, sort_order, active
Initial Features:
- weight_entries: Gewichtseinträge, default: 30, never
- circumference_entries: Umfangsmessungen, default: 30, never
- caliper_entries: Caliper-Messungen, default: 30, never
- nutrition_entries: Ernährungseinträge, default: 30, never
- activity_entries: Aktivitäten, default: 30, never
- photos: Progress-Fotos, default: 5, never
- ai_calls: KI-Analysen, default: 0, monthly
- ai_pipeline: KI-Pipeline, default: 0, monthly
- csv_import: CSV-Importe, default: 0, monthly
- data_export: Daten-Exporte, default: 0, monthly
- fitness_connectors: Fitness-Connectoren, default: 0, never
Neue Features einfach per INSERT hinzufügen - kein Schema-Change!
```
#### **tier_limits** - Limits pro Tier + Feature
```sql
id, tier_slug, feature_slug, limit_value, enabled
Beispiel Free Tier:
- ('free', 'weight_entries', 30, true)
- ('free', 'ai_calls', 0, false) -- KI deaktiviert
- ('free', 'data_export', 0, false)
Beispiel Premium:
- ('premium', 'weight_entries', NULL, true) -- unbegrenzt
- ('premium', 'ai_calls', NULL, true) -- unbegrenzt
Admin kann in UI Matrix bearbeiten: Tier x Feature
```
#### **user_feature_restrictions** - Individuelle User-Limits
```sql
id, profile_id, feature_slug, limit_value, enabled
reason, set_by (admin_id)
Überschreibt Tier-Limits für spezifische User.
Admin kann jeden User individuell einschränken oder erweitern.
```
#### **user_feature_usage** - Nutzungs-Tracking
```sql
id, profile_id, feature_slug, period_start, usage_count, last_used
Für Features mit reset_period (z.B. ai_calls monthly).
Wird automatisch zurückgesetzt am Monatsanfang.
```
#### **coupons** - Coupon-Verwaltung
@ -214,15 +266,6 @@ total_weight_entries, total_ai_analyses, total_exports
bonus_points (später), gift_coupons_available (später)
```
#### **user_restrictions** - Individuelle Einschränkungen
```sql
profile_id, max_ai_calls_month, max_weight_entries, max_photos
block_export, block_ai, block_import
reason, set_by (admin_id)
Überschreibt Tier-Limits für spezifische User
```
#### **profiles** - Erweiterte Spalten
```sql
tier, tier_locked (Admin kann Tier festnageln)
@ -238,18 +281,38 @@ stripe_customer_id (vorbereitet für v9d)
#### Neue Router (v9c):
```
routers/tiers.py - Tier-Verwaltung (List, Edit, Create)
routers/features.py - Feature-Registry (List, Add, Edit, Delete) ⭐ NEU
routers/tier_limits.py - Tier-Limits-Matrix (Admin bearbeitet Tier x Feature) ⭐ NEU
routers/coupons.py - Coupon-System (Redeem, Admin CRUD)
routers/access_grants.py - Zugriffs-Verwaltung (Current, Grant, Revoke)
routers/user_admin.py - Erweiterte User-Verwaltung (Activity, Stats, Restrictions)
routers/user_admin.py - Erweiterte User-Verwaltung (Activity, Stats, Feature-Restrictions)
routers/settings.py - App-Einstellungen (Admin)
routers/registration.py - Registrierung + E-Mail-Verifizierung
```
#### Neue Middleware:
```python
require_tier(min_tier) - Feature-Gate basierend auf Tier
check_feature_limit(feature, action) - Limit-Prüfung (z.B. max_weight_entries)
log_activity(type, details) - Activity-Logging
check_feature_access(profile_id, feature_slug, action='use')
"""
Zentrale Feature-Access-Prüfung.
Hierarchie:
1. User-Restriction (höchste Priorität)
2. Tier-Limit
3. Feature-Default
Returns: {'allowed': bool, 'limit': int, 'used': int, 'remaining': int, 'reason': str}
"""
increment_feature_usage(profile_id, feature_slug)
"""
Inkrementiert Nutzungszähler.
Berücksichtigt reset_period (monthly, daily, never).
"""
log_activity(profile_id, activity_type, details=None)
"""
Loggt User-Aktivitäten in user_activity_log.
"""
```
#### Hintergrund-Tasks (Cron):
@ -302,22 +365,27 @@ def create_weight_entry():
#### Neue Seiten:
```
RegisterPage.jsx - Registrierung (Name, E-Mail, Passwort)
VerifyEmailPage.jsx - E-Mail-Verifizierung (Token aus URL)
RedeemCouponPage.jsx - Coupon-Eingabe (oder Modal)
AdminCouponsPage.jsx - Coupon-Verwaltung (Admin)
AdminTiersPage.jsx - Tier-Konfiguration (Admin)
AdminSettingsPage.jsx - App-Einstellungen (Admin)
RegisterPage.jsx - Registrierung (Name, E-Mail, Passwort)
VerifyEmailPage.jsx - E-Mail-Verifizierung (Token aus URL)
RedeemCouponPage.jsx - Coupon-Eingabe (oder Modal)
AdminCouponsPage.jsx - Coupon-Verwaltung (Admin)
AdminTiersPage.jsx - Tier-Verwaltung (CRUD) (Admin)
AdminFeaturesPage.jsx - Feature-Registry (List, Add, Edit) ⭐ NEU
AdminTierLimitsPage.jsx - Tier x Feature Matrix (bearbeiten) ⭐ NEU
AdminUserRestrictionsPage.jsx - User-spezifische Limits (bearbeiten) ⭐ NEU
AdminSettingsPage.jsx - App-Einstellungen (Admin)
```
#### Neue Komponenten:
```jsx
<TierBadge tier="premium" /> // Tier-Anzeige mit Icon
<FeatureGate tier="basic">...</> // Feature nur für Basic+ zeigen
<AccessStatus /> // "Trial endet in 5 Tagen" Banner
<CouponInput onRedeem={...} /> // Coupon-Eingabefeld
<ActivityTimeline activities={...} /> // User-Activity-Log
<StreakCounter days={7} /> // Login-Streak Anzeige (später)
<TierBadge tier="premium" /> // Tier-Anzeige mit Icon
<FeatureGate feature="ai_calls">...</> // Feature-basierte Sichtbarkeit ⭐ GEÄNDERT
<AccessStatus /> // "Trial endet in 5 Tagen" Banner
<CouponInput onRedeem={...} /> // Coupon-Eingabefeld
<ActivityTimeline activities={...} /> // User-Activity-Log
<FeatureLimitBadge feature="ai_calls" /> // "5/10 verwendet" Anzeige ⭐ NEU
<TierLimitsMatrix tiers={...} features={...}/> // Matrix-Editor ⭐ NEU
<StreakCounter days={7} /> // Login-Streak (später)
```
#### Erweiterte Admin-Seiten:
@ -326,8 +394,87 @@ AdminUsersPage.jsx erweitert um:
- Activity-Log Button → zeigt user_activity_log
- Stats Button → zeigt user_stats
- Access-Grants Button → zeigt aktive/abgelaufene Zugriffe
- Restrictions Button → individuelle Limits setzen
- Feature-Restrictions Button → individuelle Feature-Limits setzen ⭐ GEÄNDERT
- Grant Access Button → manuell Tier-Zugriff gewähren
- Usage-Overview → zeigt user_feature_usage für alle Features ⭐ NEU
```
#### Admin-Interface-Details:
**AdminFeaturesPage.jsx** - Feature-Registry verwalten
```jsx
// Alle Features auflisten + neue hinzufügen
<FeatureList>
{features.map(f => (
<FeatureRow>
<Name>{f.name}</Name>
<Category>{f.category}</Category>
<Unit>{f.unit}</Unit>
<ResetPeriod>{f.reset_period}</ResetPeriod>
<DefaultLimit>{f.default_limit ?? '∞'}</DefaultLimit>
<Actions>
<EditButton />
<DeleteButton />
</Actions>
</FeatureRow>
))}
</FeatureList>
<AddFeatureButton />
```
**AdminTierLimitsPage.jsx** - Matrix-Editor
```jsx
// Matrix-View: Tiers (Spalten) x Features (Zeilen)
<TierLimitsMatrix>
<thead>
<tr>
<th>Feature</th>
<th>Free</th>
<th>Basic</th>
<th>Premium</th>
<th>Selfhosted</th>
</tr>
</thead>
<tbody>
<tr>
<td>Gewichtseinträge</td>
<td><Input value="30" /></td>
<td><Input value="" placeholder="∞" /></td>
<td><Input value="" placeholder="∞" /></td>
<td><Input value="" placeholder="∞" /></td>
</tr>
<tr>
<td>KI-Analysen/Monat</td>
<td><Checkbox disabled /> 0</td>
<td><Checkbox checked /> <Input value="10" /></td>
<td><Checkbox checked /></td>
<td><Checkbox checked /></td>
</tr>
</tbody>
</TierLimitsMatrix>
```
**AdminUserRestrictionsPage.jsx** - Individuelle User-Limits
```jsx
<UserSelect onChange={loadUser} />
<CurrentTier badge={user.tier} />
<CurrentUsage>
{features.map(f => (
<FeatureUsageRow key={f.slug}>
<Name>{f.name}</Name>
<TierLimit>{getTierLimit(user.tier, f.slug)}</TierLimit>
<CurrentUsage>{getUsage(user.id, f.slug)}</CurrentUsage>
<Override>
<Input
value={getUserRestriction(user.id, f.slug)}
placeholder="Tier-Standard"
/>
</Override>
<SaveButton />
</FeatureUsageRow>
))}
</CurrentUsage>
```
---