mitai-jinkendo/.claude/docs/technical/FEATURE_ENFORCEMENT_MAPPING.md
Lars 7940dc7560 docs: Struktur .claude/docs versionieren, working/, Gitea-Index, Regeln
- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren
- DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues
- .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08)
- Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben
- docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md
- CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht

Made-with: Cursor
2026-04-08 13:01:49 +02:00

9.5 KiB

Feature Enforcement Mapping

Version: v9c Phase 2 Status: Planning Datum: 20. März 2026


Übersicht

Dieses Dokument definiert, welche API-Endpoints welche Features prüfen müssen.


Feature-Katalog (nach Cleanup)

Data Features (count, never)

  1. weight_entries - Gewichtseinträge
  2. circumference_entries - Umfangsmessungen
  3. caliper_entries - Hautfaltenmessungen
  4. nutrition_entries - Ernährungseinträge
  5. activity_entries - Trainingseinträge
  6. photos - Progress-Fotos

AI Features

  1. ai_calls - KI-Einzelanalysen (count, monthly)
  2. ai_pipeline - KI-Pipeline-Analyse (boolean, never)

Export/Import Features

  1. data_export - Daten exportieren (count, monthly)
  2. data_import - Daten importieren (count, monthly)

Endpoint → Feature Mapping

Weight Router (/api/weight)

Endpoint Method Feature Action
/api/weight POST weight_entries Check before create, increment after
/api/weight GET - No check (reading is always allowed)
/api/weight/{id} PUT - No check (editing existing is allowed)
/api/weight/{id} DELETE - No check (deleting is allowed)

Rationale: Limit bezieht sich auf Gesamtanzahl Einträge (COUNT), nicht auf API-Calls.


Circumference Router (/api/circumference)

Endpoint Method Feature Action
/api/circumference POST circumference_entries Check before, increment after
/api/circumference GET - No check
/api/circumference/{id} PUT - No check
/api/circumference/{id} DELETE - No check

Caliper Router (/api/caliper)

Endpoint Method Feature Action
/api/caliper POST caliper_entries Check before, increment after
/api/caliper GET - No check
/api/caliper/{id} PUT - No check
/api/caliper/{id} DELETE - No check

Nutrition Router (/api/nutrition)

Endpoint Method Feature Action
/api/nutrition POST nutrition_entries Check before, increment after
/api/nutrition GET - No check
/api/nutrition/{id} PUT - No check
/api/nutrition/{id} DELETE - No check

Activity Router (/api/activity)

Endpoint Method Feature Action
/api/activity POST activity_entries Check before, increment after
/api/activity GET - No check
/api/activity/{id} PUT - No check
/api/activity/{id} DELETE - No check

Photos Router (/api/photos)

Endpoint Method Feature Action
/api/photos/upload POST photos Check before, increment after
/api/photos GET - No check
/api/photos/{id} DELETE - No check (deleting is allowed)

Insights Router (/api/insights)

Endpoint Method Feature Action
/api/insights/run/{slug} POST ai_calls Check before, increment after
/api/insights/pipeline POST ai_pipeline (boolean) Check before (no increment for boolean)
/api/insights GET - No check
/api/insights/{id} GET - No check

Rationale:

  • ai_calls = count-based, monthly reset
  • ai_pipeline = boolean (enabled/disabled), no usage tracking

Export Router (/api/export)

Endpoint Method Feature Action
/api/export/csv GET data_export Check before, increment after
/api/export/json GET data_export Check before, increment after
/api/export/zip GET data_export Check before, increment after

Rationale: Ein Feature für alle 3 Export-Typen (konsolidiert).


Import Router (/api/import)

Endpoint Method Feature Action
/api/nutrition/import/fddb POST data_import Check before, increment after
/api/activity/import/csv POST data_import Check before, increment after
/api/import/zip POST data_import Check before, increment after

Rationale: Ein Feature für alle Import-Typen.


Implementation Pattern (Phase 2: Non-Blocking Logging)

Pattern für count-based Features

from auth import require_auth, check_feature_access, increment_feature_usage
import logging

logger = logging.getLogger(__name__)

@router.post("/api/weight")
def create_weight(data: dict, session: dict = Depends(require_auth)):
    profile_id = session['profile_id']

    # Phase 2: Check access (log only, don't block)
    access = check_feature_access(profile_id, 'weight_entries')
    if not access['allowed']:
        logger.warning(
            f"[FEATURE-LIMIT] User {profile_id} would be blocked: "
            f"weight_entries limit_exceeded ({access['used']}/{access['limit']})"
        )
        # NOTE: Phase 2 does NOT raise HTTPException - just logs!

    # Actual logic
    # ... create weight entry ...

    # Phase 2: Increment usage (even if limit would be exceeded)
    increment_feature_usage(profile_id, 'weight_entries')

    return {"ok": True, "id": entry_id}

Pattern für boolean Features

@router.post("/api/insights/pipeline")
def run_pipeline(session: dict = Depends(require_auth)):
    profile_id = session['profile_id']

    # Phase 2: Check access (log only)
    access = check_feature_access(profile_id, 'ai_pipeline')
    if not access['allowed']:
        logger.warning(
            f"[FEATURE-LIMIT] User {profile_id} would be blocked: "
            f"ai_pipeline disabled"
        )
        # NOTE: Phase 2 does NOT raise HTTPException!

    # Actual logic
    # ... run pipeline ...

    # No increment for boolean features

    return {"ok": True}

Phase 3: Frontend Display (ohne Gates)

Usage-Counter anzeigen

// Example: WeightPage.jsx
import { useEffect, useState } from 'react'
import api from '../utils/api'

function WeightPage() {
  const [usage, setUsage] = useState(null)

  useEffect(() => {
    // Fetch usage info
    api.get('/api/features/weight_entries/check-access')
      .then(res => setUsage(res))
  }, [])

  return (
    <div>
      <h1>Gewicht</h1>

      {/* Phase 3: Display usage (non-blocking) */}
      {usage && usage.limit !== null && (
        <div className="usage-badge">
          {usage.used} / {usage.limit} Einträge
          {usage.remaining !== null && usage.remaining < 5 && (
            <span className="warning">
              Nur noch {usage.remaining} Einträge verfügbar
            </span>
          )}
        </div>
      )}

      {/* Button is NOT disabled in Phase 3 */}
      <button onClick={createEntry}>
        Gewicht hinzufügen
      </button>
    </div>
  )
}

Phase 4: Enforcement aktivieren (opt-in)

Feature-Flag System

# In app_settings table
INSERT INTO app_settings (key, value, description)
VALUES ('feature_enforcement_enabled', 'false', 'Enable/disable feature limit enforcement');

Modified Pattern (mit Enforcement)

def create_weight(data: dict, session: dict = Depends(require_auth)):
    profile_id = session['profile_id']

    # Check if enforcement is enabled
    enforcement_enabled = get_app_setting('feature_enforcement_enabled', False)

    # Check access
    access = check_feature_access(profile_id, 'weight_entries')
    if not access['allowed']:
        if enforcement_enabled:
            # Phase 4: BLOCK
            raise HTTPException(
                status_code=429,
                detail=f"Limit erreicht: {access['used']}/{access['limit']} Gewichtseinträge. Upgrade für mehr."
            )
        else:
            # Phase 2/3: LOG ONLY
            logger.warning(
                f"[FEATURE-LIMIT] User {profile_id} would be blocked: "
                f"weight_entries limit_exceeded ({access['used']}/{access['limit']})"
            )

    # Actual logic
    # ...

Rollout-Strategie

Phase 2: Log-Only (1-2 Wochen)

  • Alle Checks implementiert
  • Nur Logging, keine Blocks
  • Monitoring: Wie oft würde blockiert?
  • Analyse: Gibt es falsche Limits?

Phase 3: Display-Only (1 Woche)

  • Frontend zeigt Usage an
  • Buttons NICHT disabled
  • User-Feedback: Ist Usage-Anzeige klar?
  • Testing: Funktioniert Counter korrekt?

Phase 4: Enforcement (schrittweise)

  1. Admin-Account testen (enforcement=true nur für Admin)
  2. Test-User (1-2 Accounts)
  3. Rollout an alle (feature_enforcement_enabled=true)

Rollback-Plan

  • UPDATE app_settings SET value='false' WHERE key='feature_enforcement_enabled'
  • Sofortiger Rollback ohne Code-Deploy

Testing-Checklist

Unit-Tests (Backend)

  • check_feature_access() mit allen Hierarchien
  • increment_feature_usage() mit Reset-Logik
  • Count-based Features (limit erreicht)
  • Boolean Features (enabled/disabled)
  • Monthly reset funktioniert

Integration-Tests

  • POST weight-entry bis Limit erreicht
  • Limit wird korrekt in Response angezeigt
  • Reset nach Monatswechsel
  • User-Override überschreibt Tier-Limit
  • Access-Grant überschreibt Base-Tier

Frontend-Tests

  • Usage-Counter aktualisiert nach Create
  • Warning bei < 5 remaining
  • Unlimited zeigt "∞"
  • Disabled-Features zeigen Upgrade-Hinweis

Letzte Aktualisierung: 20. März 2026 Autor: Lars Stommer + Claude Opus 4.6