feat: Phase 4 Batch 1 - enable enforcement for data entries
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

- Weight, Circumference, Caliper now BLOCK on limit exceeded
- Raise HTTPException(403) with user-friendly message
- Show used/limit and suggest contacting admin
- Phase 2 → Phase 4 transition

Phase 4: Enforcement (Batch 1/3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-21 06:57:05 +01:00
parent baad096ead
commit cbcb6a2a34
3 changed files with 25 additions and 11 deletions

View File

@ -7,7 +7,7 @@ import uuid
import logging
from typing import Optional
from fastapi import APIRouter, Header, Depends
from fastapi import APIRouter, Header, Depends, HTTPException
from db import get_db, get_cursor, r2d
from auth import require_auth, check_feature_access, increment_feature_usage
@ -35,15 +35,20 @@ def upsert_caliper(e: CaliperEntry, x_profile_id: Optional[str]=Header(default=N
"""Create or update caliper entry (upsert by date)."""
pid = get_pid(x_profile_id)
# Phase 2: Check feature access (non-blocking, log only)
# Phase 4: Check feature access and ENFORCE
access = check_feature_access(pid, 'caliper_entries')
log_feature_usage(pid, 'caliper_entries', access, 'create')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {pid} would be blocked: "
f"[FEATURE-LIMIT] User {pid} blocked: "
f"caliper_entries {access['reason']} (used: {access['used']}, limit: {access['limit']})"
)
raise HTTPException(
status_code=403,
detail=f"Limit erreicht: Du hast das Kontingent für Caliper-Einträge überschritten ({access['used']}/{access['limit']}). "
f"Bitte kontaktiere den Admin oder warte bis zum nächsten Reset."
)
with get_db() as conn:
cur = get_cursor(conn)

View File

@ -7,7 +7,7 @@ import uuid
import logging
from typing import Optional
from fastapi import APIRouter, Header, Depends
from fastapi import APIRouter, Header, Depends, HTTPException
from db import get_db, get_cursor, r2d
from auth import require_auth, check_feature_access, increment_feature_usage
@ -35,15 +35,20 @@ def upsert_circ(e: CircumferenceEntry, x_profile_id: Optional[str]=Header(defaul
"""Create or update circumference entry (upsert by date)."""
pid = get_pid(x_profile_id)
# Phase 2: Check feature access (non-blocking, log only)
# Phase 4: Check feature access and ENFORCE
access = check_feature_access(pid, 'circumference_entries')
log_feature_usage(pid, 'circumference_entries', access, 'create')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {pid} would be blocked: "
f"[FEATURE-LIMIT] User {pid} blocked: "
f"circumference_entries {access['reason']} (used: {access['used']}, limit: {access['limit']})"
)
raise HTTPException(
status_code=403,
detail=f"Limit erreicht: Du hast das Kontingent für Umfangs-Einträge überschritten ({access['used']}/{access['limit']}). "
f"Bitte kontaktiere den Admin oder warte bis zum nächsten Reset."
)
with get_db() as conn:
cur = get_cursor(conn)

View File

@ -7,7 +7,7 @@ import uuid
import logging
from typing import Optional
from fastapi import APIRouter, Header, Depends
from fastapi import APIRouter, Header, Depends, HTTPException
from db import get_db, get_cursor, r2d
from auth import require_auth, check_feature_access, increment_feature_usage
@ -35,19 +35,23 @@ def upsert_weight(e: WeightEntry, x_profile_id: Optional[str]=Header(default=Non
"""Create or update weight entry (upsert by date)."""
pid = get_pid(x_profile_id)
# Phase 2: Check feature access (non-blocking, log only)
# Phase 4: Check feature access and ENFORCE
access = check_feature_access(pid, 'weight_entries')
# Structured logging (always)
log_feature_usage(pid, 'weight_entries', access, 'create')
# Warning if limit exceeded (legacy)
# BLOCK if limit exceeded
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {pid} would be blocked: "
f"[FEATURE-LIMIT] User {pid} blocked: "
f"weight_entries {access['reason']} (used: {access['used']}, limit: {access['limit']})"
)
# NOTE: Phase 2 does NOT block - just logs!
raise HTTPException(
status_code=403,
detail=f"Limit erreicht: Du hast das Kontingent für Gewichtseinträge überschritten ({access['used']}/{access['limit']}). "
f"Bitte kontaktiere den Admin oder warte bis zum nächsten Reset."
)
with get_db() as conn:
cur = get_cursor(conn)