Files
resolutionflow/backend/app/api/router.py
Michael Chihlas 694279f89e feat(sales): add POST /sales-leads public endpoint
Phase 2 Task 29 — public Talk-to-Sales submission endpoint.

- New POST /api/v1/sales-leads (public, no auth, rate-limited 5/hour per IP).
- Inserts a sales_leads row, fires best-effort notification email and
  PostHog server-side capture; failures are logged but never fail the
  request.
- New EmailService.send_sales_lead_notification static method.
- New SALES_LEAD_RECIPIENT_EMAIL setting (defaults to sales@resolutionflow.com).
- Schemas: SalesLeadCreate / SalesLeadCreateResponse with literal source enum.
- Tests: happy path (row + email), email-failure resilience, and rate-limit
  enforcement (re-enables the slowapi limiter for the rate-limit assertion
  since DEBUG=true disables it by default in tests).

PostHog server-side instrumentation point is wired in but no-ops gracefully
until app.core.analytics.posthog exists — turning it on is a one-line
change when the backend SDK is configured.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 20:12:03 -04:00

180 lines
7.9 KiB
Python

from fastapi import APIRouter, Depends
from app.api.deps import (
require_tenant_context,
require_active_subscription,
require_verified_email_after_grace,
)
from app.api.endpoints import (
admin,
admin_audit,
admin_categories,
admin_dashboard,
admin_feature_flags,
admin_gallery,
admin_plan_limits,
admin_settings,
admin_survey,
ai_builder,
ai_chat,
ai_fix,
ai_sessions,
ai_suggestions,
analytics,
assistant_chat,
auth,
billing,
beta_feedback,
beta_signup,
sales_leads,
branding,
categories,
copilot,
device_types,
draft_templates,
feedback,
flow_proposals,
flowpilot_analytics,
folders,
integrations,
invite,
kb_accelerator,
maintenance_schedules,
network_diagrams,
notifications,
oauth as oauth_endpoints,
onboarding,
public_templates,
ratings,
scripts,
script_builder,
session_branches,
session_facts,
session_handoffs,
session_resolutions,
session_suggested_fixes,
sessions,
shared,
shares,
sidebar,
step_categories,
steps,
supporting_data,
survey,
tags,
target_lists,
tree_markdown,
tree_transfer,
trees,
uploads,
webhooks,
accounts,
)
api_router = APIRouter()
# ---------------------------------------------------------------------------
# Public / unauthenticated endpoints — no tenant context
#
# Note: auth.router contains both public endpoints (register, login,
# forgot-password, reset-password, email/verify) and authenticated endpoints
# (GET/PATCH /me, logout, change-password, email/send-verification).
# The authenticated auth endpoints only query the `users` table, which is
# excluded from Phase 1 RLS. They work correctly without tenant context
# in Phase 1. This will need revisiting in Phase 2 when `users` gets RLS.
# ---------------------------------------------------------------------------
api_router.include_router(auth.router)
api_router.include_router(oauth_endpoints.router)
api_router.include_router(billing.router) # Reachable when subscription locked
api_router.include_router(shared.router) # Public share links (no auth)
api_router.include_router(shares.public_router) # Public session share links (optional auth)
api_router.include_router(beta_signup.router)
api_router.include_router(sales_leads.router) # Talk-to-Sales (no auth, rate-limited)
api_router.include_router(webhooks.router) # Stripe webhook receiver
api_router.include_router(public_templates.router) # Public gallery (no auth, rate-limited)
api_router.include_router(survey.router) # Public survey flow (no auth, rate-limited)
# ---------------------------------------------------------------------------
# Admin endpoints — super_admin only
# admin_categories, admin_gallery, admin_dashboard, admin query Phase 1 RLS
# tables and MUST use get_admin_db (migrated in Task 8). The remaining admin
# endpoints (admin_audit, admin_plan_limits, admin_feature_flags,
# admin_settings, admin_survey) are safe until Phase 2 extends RLS.
# ---------------------------------------------------------------------------
api_router.include_router(admin.router)
api_router.include_router(admin_dashboard.router)
api_router.include_router(admin_audit.router)
api_router.include_router(admin_plan_limits.router)
api_router.include_router(admin_feature_flags.router)
api_router.include_router(admin_settings.router)
api_router.include_router(admin_categories.router)
api_router.include_router(admin_survey.router)
api_router.include_router(admin_gallery.router)
# ---------------------------------------------------------------------------
# User-facing endpoints — tenant context required
#
# _tenant_deps: routers that only require an authenticated user inside a
# tenant (auth/account/admin/non-Pro feature surfaces).
# _pro_deps: routers gated behind an active Pro subscription. Adds
# require_active_subscription which raises 402 unless the
# account's Subscription is active/complimentary/past_due or
# trialing-with-time-remaining. Allowlisted paths in deps.py
# bypass the gate for billing/account admin/auth flows.
# ---------------------------------------------------------------------------
_tenant_deps = [Depends(require_tenant_context)]
_pro_deps = [
Depends(require_tenant_context),
Depends(require_active_subscription),
Depends(require_verified_email_after_grace),
]
api_router.include_router(trees.router, dependencies=_pro_deps)
api_router.include_router(sidebar.router, dependencies=_tenant_deps)
api_router.include_router(sessions.router, dependencies=_pro_deps)
api_router.include_router(invite.router, dependencies=_tenant_deps)
api_router.include_router(categories.router, dependencies=_tenant_deps)
api_router.include_router(tags.router, dependencies=_tenant_deps)
api_router.include_router(folders.router, dependencies=_tenant_deps)
api_router.include_router(step_categories.router, dependencies=_pro_deps)
api_router.include_router(steps.router, dependencies=_pro_deps)
api_router.include_router(accounts.router, dependencies=_tenant_deps)
api_router.include_router(shares.router, dependencies=_tenant_deps)
api_router.include_router(tree_markdown.router, dependencies=_tenant_deps)
api_router.include_router(ratings.router, dependencies=_tenant_deps)
api_router.include_router(analytics.router, dependencies=_pro_deps)
api_router.include_router(target_lists.router, dependencies=_tenant_deps)
api_router.include_router(maintenance_schedules.router, dependencies=_tenant_deps)
api_router.include_router(feedback.router, dependencies=_tenant_deps)
api_router.include_router(ai_builder.router, dependencies=_tenant_deps)
api_router.include_router(ai_fix.router, dependencies=_tenant_deps)
api_router.include_router(ai_chat.router, dependencies=_tenant_deps)
api_router.include_router(copilot.router, dependencies=_tenant_deps)
api_router.include_router(assistant_chat.router, dependencies=_pro_deps)
api_router.include_router(tree_transfer.router, dependencies=_tenant_deps)
api_router.include_router(ai_suggestions.router, dependencies=_tenant_deps)
api_router.include_router(kb_accelerator.router, dependencies=_tenant_deps)
api_router.include_router(scripts.router, dependencies=_pro_deps)
api_router.include_router(integrations.router, dependencies=_pro_deps)
api_router.include_router(onboarding.router, dependencies=_tenant_deps)
api_router.include_router(branding.router, dependencies=_tenant_deps)
api_router.include_router(supporting_data.router, dependencies=_tenant_deps)
api_router.include_router(network_diagrams.router, dependencies=_tenant_deps)
# session_handoffs queue router must come before ai_sessions to avoid conflict
api_router.include_router(session_handoffs.queue_router, dependencies=_pro_deps)
api_router.include_router(session_resolutions.router, dependencies=_pro_deps)
# session_facts mounts under /ai-sessions/{id}/facts — register before ai_sessions
# so the {session_id}/facts subpaths take precedence over any future generic catchalls.
api_router.include_router(session_facts.router, dependencies=_pro_deps)
api_router.include_router(session_suggested_fixes.router, dependencies=_pro_deps)
api_router.include_router(draft_templates.router, dependencies=_tenant_deps)
api_router.include_router(ai_sessions.router, dependencies=_pro_deps)
api_router.include_router(flow_proposals.router, dependencies=_pro_deps)
api_router.include_router(flowpilot_analytics.router, dependencies=_pro_deps)
api_router.include_router(notifications.router, dependencies=_tenant_deps)
api_router.include_router(uploads.router, dependencies=_tenant_deps)
api_router.include_router(script_builder.router, dependencies=_pro_deps)
api_router.include_router(beta_feedback.router, dependencies=_tenant_deps)
api_router.include_router(session_branches.router, dependencies=_pro_deps)
api_router.include_router(session_handoffs.router, dependencies=_pro_deps)
api_router.include_router(device_types.router, dependencies=_tenant_deps)