Fix subscription limits; seed Free/Good/Better/Best default plans

Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/98c1f90b-0287-4908-a0be-ad066c453cc5

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-26 16:00:56 +00:00
parent 979b7b6cec
commit 7971d3f76a
5 changed files with 158 additions and 28 deletions
+40 -20
View File
@@ -8,7 +8,12 @@ from sqlalchemy import select, desc
from app.core.database import get_db
from app.core.deps import get_current_active_user
from app.core.security import encrypt_credential
from app.models.database_models import User, MailAccount, AccountStatus
from app.models.database_models import (
User,
MailAccount,
AccountStatus,
SubscriptionPlan,
)
from app.models.schemas import (
MailAccountCreate,
MailAccountResponse,
@@ -34,26 +39,41 @@ async def create_mail_account(
):
"""Create a new mail account"""
# Check subscription limits
result = await db.execute(
select(MailAccount).where(MailAccount.user_id == current_user.id)
)
existing_accounts = result.scalars().all()
tier_limits = {
"free": settings.TIER_FREE_MAX_ACCOUNTS,
"basic": settings.TIER_BASIC_MAX_ACCOUNTS,
"pro": settings.TIER_PRO_MAX_ACCOUNTS,
"enterprise": settings.TIER_ENTERPRISE_MAX_ACCOUNTS,
}
max_accounts = tier_limits.get(current_user.subscription_tier.value, 1)
if len(existing_accounts) >= max_accounts:
raise HTTPException(
status_code=status.HTTP_402_PAYMENT_REQUIRED,
detail="Account limit reached. Upgrade your subscription to add more accounts.",
# Superusers are not subject to subscription limits
if not current_user.is_superuser:
# Count existing accounts for this user
result = await db.execute(
select(MailAccount).where(MailAccount.user_id == current_user.id)
)
existing_accounts = result.scalars().all()
# Try to look up the limit from the active SubscriptionPlan in the DB first
# so that admin-managed plan limits take effect immediately.
plan_result = await db.execute(
select(SubscriptionPlan).where(
SubscriptionPlan.tier == current_user.subscription_tier,
SubscriptionPlan.is_active.is_(True),
)
)
plan = plan_result.scalar_one_or_none()
if plan is not None:
max_accounts = plan.max_mail_accounts
else:
# Fall back to env-var / config values when no plan row exists
tier_limits = {
"free": settings.TIER_FREE_MAX_ACCOUNTS,
"basic": settings.TIER_BASIC_MAX_ACCOUNTS,
"pro": settings.TIER_PRO_MAX_ACCOUNTS,
"enterprise": settings.TIER_ENTERPRISE_MAX_ACCOUNTS,
}
max_accounts = tier_limits.get(current_user.subscription_tier.value, 1)
if len(existing_accounts) >= max_accounts:
raise HTTPException(
status_code=status.HTTP_402_PAYMENT_REQUIRED,
detail="Account limit reached. Upgrade your subscription to add more accounts.",
)
# Encrypt password
encrypted_password = encrypt_credential(account_in.password)
+13 -1
View File
@@ -25,10 +25,22 @@ async def list_subscription_plans(db: AsyncSession = Depends(get_db)):
@router.get("/current")
async def get_current_subscription(
current_user: User = Depends(get_current_active_user),
db: AsyncSession = Depends(get_db),
):
"""Get current user's subscription details"""
"""Get current user's subscription details including plan limits"""
plan_result = await db.execute(
select(SubscriptionPlan).where(
SubscriptionPlan.tier == current_user.subscription_tier,
SubscriptionPlan.is_active.is_(True),
)
)
plan = plan_result.scalar_one_or_none()
return {
"tier": current_user.subscription_tier,
"status": current_user.subscription_status,
"expires_at": current_user.subscription_expires_at,
"max_mail_accounts": plan.max_mail_accounts if plan else None,
"max_emails_per_day": plan.max_emails_per_day if plan else None,
"check_interval_minutes": plan.check_interval_minutes if plan else None,
}
+84 -1
View File
@@ -20,7 +20,7 @@ from typing import Any, Dict, List, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.database_models import AppSetting
from app.models.database_models import AppSetting, SubscriptionPlan, SubscriptionTier
logger = logging.getLogger(__name__)
@@ -338,4 +338,87 @@ class ConfigService:
if created:
await db.commit()
logger.info("Seeded %d default settings into the database", created)
# Seed default subscription plans
await ConfigService.seed_default_plans(db)
return created
@staticmethod
async def seed_default_plans(db: AsyncSession) -> int:
"""
Populate the database with default subscription plans (skip existing tiers).
Limits mirror the env-var defaults so behaviour is unchanged on first boot
but can be overridden by admins via the plan management UI.
Returns the number of plans created.
"""
from app.core.config import settings # local import to avoid circular deps
default_plans = [
{
"tier": SubscriptionTier.FREE,
"name": "Free",
"description": "Try it out one mailbox, no credit card required.",
"price_monthly": 0.0,
"price_yearly": 0.0,
"max_mail_accounts": settings.TIER_FREE_MAX_ACCOUNTS,
"max_emails_per_day": 100,
"check_interval_minutes": 30,
"support_level": "community",
"is_active": True,
},
{
"tier": SubscriptionTier.BASIC,
"name": "Good",
"description": "Great for personal use a handful of mailboxes checked regularly.",
"price_monthly": 4.99,
"price_yearly": 49.90,
"max_mail_accounts": settings.TIER_BASIC_MAX_ACCOUNTS,
"max_emails_per_day": 1000,
"check_interval_minutes": 15,
"support_level": "email",
"is_active": True,
},
{
"tier": SubscriptionTier.PRO,
"name": "Better",
"description": "Power users and small teams more mailboxes, faster checks.",
"price_monthly": 12.99,
"price_yearly": 129.90,
"max_mail_accounts": settings.TIER_PRO_MAX_ACCOUNTS,
"max_emails_per_day": 10000,
"check_interval_minutes": 5,
"support_level": "email",
"is_active": True,
},
{
"tier": SubscriptionTier.ENTERPRISE,
"name": "Best",
"description": "Organisations and heavy workloads maximum mailboxes, near-real-time checks.",
"price_monthly": 29.99,
"price_yearly": 299.90,
"max_mail_accounts": settings.TIER_ENTERPRISE_MAX_ACCOUNTS,
"max_emails_per_day": 100000,
"check_interval_minutes": 1,
"support_level": "priority",
"is_active": True,
},
]
created = 0
for plan_data in default_plans:
result = await db.execute(
select(SubscriptionPlan).where(
SubscriptionPlan.tier == plan_data["tier"]
)
)
if result.scalar_one_or_none() is None:
db.add(SubscriptionPlan(**plan_data))
created += 1
if created:
await db.commit()
logger.info("Seeded %d default subscription plans into the database", created)
return created