Membership-System und Bug Fixing (inkl. Nutrition) #8
194
backend/auth.py
194
backend/auth.py
|
|
@ -200,115 +200,115 @@ def _check_impl(profile_id: str, feature_id: str, conn) -> dict:
|
||||||
"""Internal implementation of check_feature_access."""
|
"""Internal implementation of check_feature_access."""
|
||||||
cur = get_cursor(conn)
|
cur = get_cursor(conn)
|
||||||
|
|
||||||
# Get feature info
|
# Get feature info
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
SELECT limit_type, reset_period, default_limit
|
SELECT limit_type, reset_period, default_limit
|
||||||
FROM features
|
FROM features
|
||||||
WHERE id = %s AND active = true
|
WHERE id = %s AND active = true
|
||||||
""", (feature_id,))
|
""", (feature_id,))
|
||||||
feature = cur.fetchone()
|
feature = cur.fetchone()
|
||||||
|
|
||||||
if not feature:
|
if not feature:
|
||||||
return {
|
return {
|
||||||
'allowed': False,
|
'allowed': False,
|
||||||
'limit': None,
|
'limit': None,
|
||||||
'used': 0,
|
'used': 0,
|
||||||
'remaining': None,
|
'remaining': None,
|
||||||
'reason': 'feature_not_found'
|
'reason': 'feature_not_found'
|
||||||
}
|
}
|
||||||
|
|
||||||
# Priority 1: Check user-specific restriction
|
# Priority 1: Check user-specific restriction
|
||||||
|
cur.execute("""
|
||||||
|
SELECT limit_value
|
||||||
|
FROM user_feature_restrictions
|
||||||
|
WHERE profile_id = %s AND feature_id = %s
|
||||||
|
""", (profile_id, feature_id))
|
||||||
|
restriction = cur.fetchone()
|
||||||
|
|
||||||
|
if restriction is not None:
|
||||||
|
limit = restriction['limit_value']
|
||||||
|
else:
|
||||||
|
# Priority 2: Check tier limit
|
||||||
|
tier_id = get_effective_tier(profile_id, conn)
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
SELECT limit_value
|
SELECT limit_value
|
||||||
FROM user_feature_restrictions
|
FROM tier_limits
|
||||||
WHERE profile_id = %s AND feature_id = %s
|
WHERE tier_id = %s AND feature_id = %s
|
||||||
""", (profile_id, feature_id))
|
""", (tier_id, feature_id))
|
||||||
restriction = cur.fetchone()
|
tier_limit = cur.fetchone()
|
||||||
|
|
||||||
if restriction is not None:
|
if tier_limit is not None:
|
||||||
limit = restriction['limit_value']
|
limit = tier_limit['limit_value']
|
||||||
else:
|
else:
|
||||||
# Priority 2: Check tier limit
|
# Priority 3: Feature default
|
||||||
tier_id = get_effective_tier(profile_id, conn)
|
limit = feature['default_limit']
|
||||||
cur.execute("""
|
|
||||||
SELECT limit_value
|
|
||||||
FROM tier_limits
|
|
||||||
WHERE tier_id = %s AND feature_id = %s
|
|
||||||
""", (tier_id, feature_id))
|
|
||||||
tier_limit = cur.fetchone()
|
|
||||||
|
|
||||||
if tier_limit is not None:
|
|
||||||
limit = tier_limit['limit_value']
|
|
||||||
else:
|
|
||||||
# Priority 3: Feature default
|
|
||||||
limit = feature['default_limit']
|
|
||||||
|
|
||||||
# For boolean features (limit 0 = disabled, 1 = enabled)
|
|
||||||
if feature['limit_type'] == 'boolean':
|
|
||||||
allowed = limit == 1
|
|
||||||
return {
|
|
||||||
'allowed': allowed,
|
|
||||||
'limit': limit,
|
|
||||||
'used': 0,
|
|
||||||
'remaining': None,
|
|
||||||
'reason': 'enabled' if allowed else 'feature_disabled'
|
|
||||||
}
|
|
||||||
|
|
||||||
# For count-based features
|
|
||||||
# Check current usage
|
|
||||||
cur.execute("""
|
|
||||||
SELECT usage_count, reset_at
|
|
||||||
FROM user_feature_usage
|
|
||||||
WHERE profile_id = %s AND feature_id = %s
|
|
||||||
""", (profile_id, feature_id))
|
|
||||||
usage = cur.fetchone()
|
|
||||||
|
|
||||||
used = usage['usage_count'] if usage else 0
|
|
||||||
|
|
||||||
# Check if reset is needed
|
|
||||||
if usage and usage['reset_at'] and datetime.now() > usage['reset_at']:
|
|
||||||
# Reset usage
|
|
||||||
used = 0
|
|
||||||
next_reset = _calculate_next_reset(feature['reset_period'])
|
|
||||||
cur.execute("""
|
|
||||||
UPDATE user_feature_usage
|
|
||||||
SET usage_count = 0, reset_at = %s, updated = CURRENT_TIMESTAMP
|
|
||||||
WHERE profile_id = %s AND feature_id = %s
|
|
||||||
""", (next_reset, profile_id, feature_id))
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
# NULL limit = unlimited
|
|
||||||
if limit is None:
|
|
||||||
return {
|
|
||||||
'allowed': True,
|
|
||||||
'limit': None,
|
|
||||||
'used': used,
|
|
||||||
'remaining': None,
|
|
||||||
'reason': 'unlimited'
|
|
||||||
}
|
|
||||||
|
|
||||||
# 0 limit = disabled
|
|
||||||
if limit == 0:
|
|
||||||
return {
|
|
||||||
'allowed': False,
|
|
||||||
'limit': 0,
|
|
||||||
'used': used,
|
|
||||||
'remaining': 0,
|
|
||||||
'reason': 'feature_disabled'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if within limit
|
|
||||||
allowed = used < limit
|
|
||||||
remaining = limit - used if limit else None
|
|
||||||
|
|
||||||
|
# For boolean features (limit 0 = disabled, 1 = enabled)
|
||||||
|
if feature['limit_type'] == 'boolean':
|
||||||
|
allowed = limit == 1
|
||||||
return {
|
return {
|
||||||
'allowed': allowed,
|
'allowed': allowed,
|
||||||
'limit': limit,
|
'limit': limit,
|
||||||
'used': used,
|
'used': 0,
|
||||||
'remaining': remaining,
|
'remaining': None,
|
||||||
'reason': 'within_limit' if allowed else 'limit_exceeded'
|
'reason': 'enabled' if allowed else 'feature_disabled'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# For count-based features
|
||||||
|
# Check current usage
|
||||||
|
cur.execute("""
|
||||||
|
SELECT usage_count, reset_at
|
||||||
|
FROM user_feature_usage
|
||||||
|
WHERE profile_id = %s AND feature_id = %s
|
||||||
|
""", (profile_id, feature_id))
|
||||||
|
usage = cur.fetchone()
|
||||||
|
|
||||||
|
used = usage['usage_count'] if usage else 0
|
||||||
|
|
||||||
|
# Check if reset is needed
|
||||||
|
if usage and usage['reset_at'] and datetime.now() > usage['reset_at']:
|
||||||
|
# Reset usage
|
||||||
|
used = 0
|
||||||
|
next_reset = _calculate_next_reset(feature['reset_period'])
|
||||||
|
cur.execute("""
|
||||||
|
UPDATE user_feature_usage
|
||||||
|
SET usage_count = 0, reset_at = %s, updated = CURRENT_TIMESTAMP
|
||||||
|
WHERE profile_id = %s AND feature_id = %s
|
||||||
|
""", (next_reset, profile_id, feature_id))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# NULL limit = unlimited
|
||||||
|
if limit is None:
|
||||||
|
return {
|
||||||
|
'allowed': True,
|
||||||
|
'limit': None,
|
||||||
|
'used': used,
|
||||||
|
'remaining': None,
|
||||||
|
'reason': 'unlimited'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 0 limit = disabled
|
||||||
|
if limit == 0:
|
||||||
|
return {
|
||||||
|
'allowed': False,
|
||||||
|
'limit': 0,
|
||||||
|
'used': used,
|
||||||
|
'remaining': 0,
|
||||||
|
'reason': 'feature_disabled'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if within limit
|
||||||
|
allowed = used < limit
|
||||||
|
remaining = limit - used if limit else None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'allowed': allowed,
|
||||||
|
'limit': limit,
|
||||||
|
'used': used,
|
||||||
|
'remaining': remaining,
|
||||||
|
'reason': 'within_limit' if allowed else 'limit_exceeded'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def increment_feature_usage(profile_id: str, feature_id: str) -> None:
|
def increment_feature_usage(profile_id: str, feature_id: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user