Initial commit: VoIdeaAI - voice-first AI idea assistant

This commit is contained in:
2026-05-13 12:51:42 +03:00
commit 688d043dad
421 changed files with 47915 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
"""Feature gate module for VoIdea tariff-based access control.
Usage:
features = await get_user_features(user, db)
if not features.get("has_voice"):
raise HTTPException(403, "Feature not available on your plan")
"""
from typing import Any
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import get_settings
from app.models.tariff import TariffPlan, UserSubscription
from app.models.user import User
settings = get_settings()
DEFAULT_FREE_FEATURES: dict[str, Any] = {
"limit_ideas": None, # None = unlimited
"limit_sessions_per_day": None,
"limit_agents": None,
"limit_team_members": None,
"limit_storage_mb": 100,
"has_voice": True,
"has_voice_commands": True,
"has_drive": True,
"has_advanced_analytics": True,
"has_api_access": True,
"has_priority_support": True,
"has_custom_branding": True,
"_promo": True,
}
async def get_user_features(
user: User, db: AsyncSession
) -> dict[str, Any]:
"""Get feature flags and limits for a user based on their tariff plan.
When tariffs_enabled=False (promo mode), returns all features as available.
"""
if not settings.tariffs_enabled:
return dict(DEFAULT_FREE_FEATURES)
result = await db.execute(
select(UserSubscription).where(UserSubscription.user_id == user.id)
)
sub = result.scalar_one_or_none()
if sub is None or sub.status != "active":
return _get_plan_features(db, settings.tariffs_free_code)
result = await db.execute(
select(TariffPlan).where(TariffPlan.id == sub.plan_id)
)
plan = result.scalar_one_or_none()
if plan is None or not plan.is_active:
return _get_plan_features(db, settings.tariffs_free_code)
return plan.features or dict(DEFAULT_FREE_FEATURES)
async def _get_plan_features(
db: AsyncSession, code: str
) -> dict[str, Any]:
"""Get features dict for a plan by code, falling back to defaults."""
result = await db.execute(
select(TariffPlan).where(
TariffPlan.code == code, TariffPlan.is_active.is_(True)
)
)
plan = result.scalar_one_or_none()
if plan and plan.features:
return plan.features
return dict(DEFAULT_FREE_FEATURES)
def check_feature(features: dict[str, Any], key: str) -> bool:
"""Check if a boolean feature is enabled."""
return bool(features.get(key, False))
def check_limit(
features: dict[str, Any], key: str, current: int = 0
) -> bool:
"""Check if a numeric limit is not exceeded (None = unlimited)."""
limit = features.get(key)
if limit is None:
return True
return current < int(limit)