import json
import os
import secrets
import sqlite3
import uuid
from datetime import datetime, timezone
import bcrypt
from app.crypto import (
decrypt_entity,
decrypt_user_value,
encrypt_entity,
encrypt_user_submission,
encrypt_user_value,
encrypt_value,
hash_value,
is_encrypted,
key_is_configured,
try_decrypt,
try_decrypt_entity,
try_decrypt_user_submission,
try_decrypt_user_value,
)
from app.db import DB_PATH, get_db
# ─── Tier configuration ──────────────────────────────────────────────────────
# SendGrid-style tiers. Override via env vars.
TIERS = {
"free": {
"name": "Free",
"max_sites": int(os.environ.get("TIER_FREE_SITES", "2")),
"max_submissions": int(os.environ.get("TIER_FREE_SUBS", "50")),
"price": 0,
"analytics": "basic",
"max_campaigns": 0,
"max_recipients_per_campaign": 0,
"max_custom_templates": 0,
"max_api_email_sends": 0,
"max_chains": 0,
"max_documents": 0,
"max_agents": 0,
},
"starter": {
"name": "Starter",
"max_sites": int(os.environ.get("TIER_STARTER_SITES", "5")),
"max_submissions": int(os.environ.get("TIER_STARTER_SUBS", "500")),
"price": 9,
"analytics": "standard",
"max_campaigns": 5,
"max_recipients_per_campaign": 100,
"max_custom_templates": 3,
"max_api_email_sends": 100,
"max_chains": 1,
"max_documents": 10,
"max_agents": 2,
},
"pro": {
"name": "Pro",
"max_sites": int(os.environ.get("TIER_PRO_SITES", "0")),
"max_submissions": int(os.environ.get("TIER_PRO_SUBS", "5000")),
"price": 29,
"analytics": "advanced",
"max_campaigns": 20,
"max_recipients_per_campaign": 1000,
"max_custom_templates": 10,
"max_api_email_sends": 500,
"max_chains": 3,
"max_documents": 100,
"max_agents": 10,
},
"teams": {
"name": "Teams",
"max_sites": 0,
"max_submissions": int(os.environ.get("TIER_TEAMS_SUBS", "25000")),
"max_members": 5,
"price": 49,
"analytics": "advanced",
"max_campaigns": 50,
"max_recipients_per_campaign": 5000,
"max_custom_templates": 25,
"max_api_email_sends": 2000,
"max_chains": 10,
"max_documents": 500,
"max_agents": 50,
},
"teams_plus": {
"name": "Teams Plus",
"max_sites": 0,
"max_submissions": int(os.environ.get("TIER_TEAMS_PLUS_SUBS", "50000")),
"max_members": 0,
"price": 99,
"analytics": "advanced",
"max_campaigns": -1,
"max_recipients_per_campaign": -1,
"max_custom_templates": -1, # unlimited
"max_api_email_sends": -1, # unlimited
"max_chains": -1, # unlimited
"max_documents": -1, # unlimited
"max_agents": -1, # unlimited
},
}
# Stripe price IDs — override via env vars for test/live
STRIPE_PRICE_IDS = {
"starter": os.environ.get("STRIPE_STARTER_PRICE_ID") or os.environ.get("STRIPE_PRICE_STARTER", ""),
"pro": os.environ.get("STRIPE_PRO_PRICE_ID") or os.environ.get("STRIPE_PRICE_PRO", ""),
"teams": os.environ.get("STRIPE_TEAMS_PRICE_ID") or os.environ.get("STRIPE_PRICE_TEAMS", ""),
"teams_plus": os.environ.get("STRIPE_TEAMS_PLUS_PRICE_ID") or os.environ.get("STRIPE_PRICE_TEAMS_PLUS", ""),
}
STRIPE_WEBHOOK_SECRET = os.environ.get("STRIPE_WEBHOOK_SECRET", "")
API_KEY_PREFIX = "afk_live_"
# ── Re-exports from split modules (models.py → domain modules) ──────────────
from app.analytics import (
ab_test_stats,
dismiss_spam,
get_admin_stats,
get_site_analytics,
get_site_analytics_summary,
get_spam_submissions,
get_team_analytics,
get_user_analytics,
site_device_stats,
site_field_dropoff,
site_geo_stats,
site_hourly_trends,
site_impression_stats,
site_session_stats,
)
from app.helpers import (
_SPAM_KEYWORDS,
_SPAM_URL_PATTERNS,
_check_spam_content,
_current_month,
_decrypt_invoice_schedule,
_decrypt_submission_fields,
_decrypt_team_member,
_decrypt_user_row,
_decrypt_webhook_dest,
_decrypt_webhook_log,
_encrypt_submission_field,
_get_site_owner_password_hash,
_is_encrypted_value,
_table_exists,
parse_user_agent,
resolve_geo,
)
from app.migrations import (
migrate_add_abuse_protection,
migrate_add_agents,
migrate_add_ai_builder,
migrate_add_analytics,
migrate_add_api_keys,
migrate_add_branding,
migrate_add_document_templates,
migrate_add_email_campaigns,
migrate_add_email_phase3,
migrate_add_email_phase4,
migrate_add_email_tracking,
migrate_add_email_verification,
migrate_add_field_config,
migrate_add_form_actions,
migrate_add_form_analytics,
migrate_add_form_sessions,
migrate_add_form_templates,
migrate_add_form_variants,
migrate_add_form_versions,
migrate_add_indexes,
migrate_add_invite_expiration,
migrate_add_rate_limit,
migrate_add_submission_data,
migrate_add_teams,
migrate_add_usage,
migrate_add_user_id_to_doc_templates,
migrate_add_user_id_to_invoice_schedules,
migrate_add_user_id_to_sites,
migrate_add_user_referral_cols,
migrate_add_webhook_destinations,
migrate_add_webhook_retry_locking,
migrate_encrypt_existing_data,
migrate_encrypt_user_pii,
migrate_normalize_field_configs,
migrate_rescope_to_user_keys,
migrate_rotate_encryption_keys,
)
from app.model_documents import (
advance_schedule_next_run,
count_user_documents,
count_user_documents_by_type,
create_document_template,
create_invoice_schedule,
delete_document_template,
delete_invoice_schedule,
generate_email_html,
generate_invoice_number,
generate_qr_code,
get_auto_generate_template,
get_document,
get_document_template,
get_due_schedules,
get_invoice_schedule,
increment_document_download,
list_document_templates,
list_documents,
list_documents_by_email,
list_invoice_schedules,
list_user_documents,
store_document,
update_document_due_date,
update_document_generation_error,
update_document_generation_status,
update_document_pdf_path,
update_document_status,
update_document_stripe_payment,
update_document_template,
update_invoice_schedule,
update_next_invoice_number,
update_site_branding,
)
from app.model_email_campaigns import (
add_campaign_recipients,
assign_recipients_to_variants,
cancel_reminder,
check_pending_ab_tests,
check_send_quota,
cleanup_old_send_log,
create_ab_test,
create_campaign,
create_custom_template,
create_reminder,
declare_ab_winner,
delete_campaign,
delete_custom_template,
get_ab_test_info,
get_ab_variant,
get_ab_variants,
get_campaign,
get_campaign_analytics,
get_campaign_by_id,
get_campaign_recipients,
get_campaign_recipients_by_email,
get_custom_template,
get_custom_templates,
get_email_settings,
get_pending_recipients,
get_pending_reminders,
get_recipient_by_message_id,
get_recipient_by_token,
get_reminder,
get_reminder_eligible_recipients,
get_reminders,
list_campaigns,
mark_reminder_sent,
record_bounce,
record_click,
record_open,
record_send,
schedule_reminders_for_campaign,
send_ab_winner_to_remaining,
update_campaign_status,
update_custom_template,
update_recipient_status,
upsert_email_settings,
)
from app.models_initdb import init_db
from app.models_site import (
add_site,
add_waitlist,
assign_variant,
create_action,
create_or_update_session,
create_variant,
deactivate_variant,
delete_action,
delete_site,
find_site_by_domain,
find_site_by_token,
get_action,
get_actions_by_site,
get_site,
get_site_version,
get_site_versions,
get_user_sites,
get_variant,
get_variants,
get_version_limit,
list_sites,
parse_site_fields,
prune_old_versions,
rollback_site_version,
save_site_version,
toggle_action,
update_action,
update_site_fields,
update_variant,
waitlist_count,
)
from app.models_submission import (
accept_submission,
add_form_impression,
add_submission,
get_submission,
list_submissions,
list_submissions_raw,
)
from app.models_team import (
accept_invite,
change_member_role,
create_team,
delete_team,
get_team,
get_team_members,
get_user_team_role,
get_user_teams,
invite_to_team,
remove_team_member,
)
from app.models_template import (
create_template,
delete_template,
get_template_by_slug,
list_templates,
seed_templates,
update_template,
)
from app.models_usage import (
accept_site_creation,
can_accept_submission,
can_create_site,
get_monthly_submission_count,
get_user_usage,
increment_submission_count,
seed_monthly_usage,
)
# ── Re-exports from domain modules (C2 refactor) ──────────────────────────
# These functions have been extracted to separate modules for better
# organization. New code should import directly from the domain modules.
from app.models_user import (
authenticate_user,
change_user_email,
change_user_password,
check_api_permission,
check_chain_quota,
check_document_quota,
check_rate_limit,
delete_user,
export_user_data,
find_user_by_email,
generate_api_key,
generate_magic_login_token,
generate_password_reset_token,
generate_referral_code,
generate_verification_token,
get_referral_stats,
get_site_owner_tier,
get_user,
get_user_chain_count,
get_user_document_count,
get_user_site_count,
get_user_tier,
is_magic_login_enabled,
list_all_users,
list_api_keys,
record_api_usage,
register_user,
renew_api_key,
reset_password_with_token,
revoke_api_key,
try_decrypt_user_value,
update_stripe_customer,
update_user_magic_login_pref,
update_user_profile,
update_user_tier,
validate_api_key,
validate_magic_login_token,
verify_email_token,
)
from app.models_webhook import (
claim_webhook_retries,
create_destination,
delete_destination,
get_destination,
get_destination_delivery_stats,
get_pending_webhook_retries,
get_permanently_failed_webhooks,
get_site_destinations,
get_site_webhook,
log_webhook_attempt,
mark_webhook_delivered,
release_webhook_retry,
update_destination,
update_destination_stats,
)