Skip to content

Latest commit

 

History

History
164 lines (119 loc) · 7.58 KB

File metadata and controls

164 lines (119 loc) · 7.58 KB

Impactis Architecture (Next.js + NestJS + Better Auth + Postgres + R2)

Last verified against code on March 10, 2026.

High-level topology

  • Frontend: Next.js App Router (impactis-client/src/app/**)

    • Uses Better Auth for auth/session instead of Supabase JS.
    • Uses NestJS REST API for business operations.
    • Uses Cloudflare R2 for profile avatars, organization logos, and startup data-room uploads via signed PUT URLs from the backend.
  • Backend: NestJS (impactis-server/)

    • Exposes versioned REST (/api/v1/*).
    • Uses SQL executor service (PrismaService) backed by pg.
    • Owns permission checks, capability-based feature gates, and business rules.
  • Database

    • Local dev: standard Postgres running in Docker, using the SQL schema from supabase/migrations/, mirrored in impactis-server/prisma/schema.prisma.
    • Remote: Postgres (Supabase-hosted in legacy environments, or a managed Postgres service), with Supabase Auth now treated as legacy and Better Auth as the active auth layer.
  • Cloudflare R2

    • Single bucket (e.g. impactis-files) with logical prefixes such as:
      • profile-avatars/<userId>/...
      • organization-logos/<orgId>/...
      • startup-data-room-assets/<orgId>/...
    • Public access via R2_PUBLIC_BASE_URL (e.g. https://pub-<id>.r2.dev).

Active backend API areas

Current REST controllers in server code:

  • GET /api/v1/health
  • /api/v1/organizations/*
  • /api/v1/profiles/*
  • /api/v1/workspace/*
  • /api/v1/startups/*
  • /api/v1/files/startups/*
  • /api/v1/billing/*
  • /api/v1/billing/stripe/*
  • /api/v1/sessions/*
  • /api/v1/admin/readiness

Notes:

  • Engagement flows (/api/v1/engagements/*) are not implemented yet; both backend controllers and any new frontend modules will be added together in a future milestone.
  • admin/readiness is active and protected by Supabase JWT + ADMIN_USER_EMAILS allow-list.

Request flows

Authentication

Current (Better Auth + Turnstile + NestJS guard):

  1. Next.js uses Better Auth to handle signup, login, and session management backed by the same Postgres database that NestJS uses.
  2. Auth pages (/auth/login, /auth/signup, password reset) render a Cloudflare Turnstile widget; the client sends the resulting token in an x-captcha-response header on Better Auth auth requests.
  3. Better Auth is configured with its captcha plugin using the TURNSTILE_SECRET_KEY and validates the Turnstile token server-side before proceeding with the auth operation.
  4. On success, Better Auth issues signed tokens / session cookies containing user id, email, and session metadata (user agent, IP, expiry).
  5. Client attaches the Better Auth access token (Authorization header) to calls to the NestJS API.
  6. BetterAuthJwtGuard validates Better Auth tokens against JWKS (BETTER_AUTH_JWKS_URL) and issuer (BETTER_AUTH_ISSUER) and attaches the authenticated user to the request context.

Business operations

  1. Client repository/server action calls apiRequest() with bearer token.
  2. NestJS guard attaches authenticated user context.
  3. Controller/service validates role and executes SQL.
  4. API returns focused payload (success, message, data payloads).

Billing (manual + Stripe-hosted)

  1. Billing settings action checks Stripe redirect feature flag:
    • BILLING_STRIPE_REDIRECTS_ENABLED
    • fallback: NEXT_PUBLIC_BILLING_STRIPE_REDIRECTS_ENABLED
  2. If redirects are disabled, action calls PATCH /billing/subscription directly.
  3. If redirects are enabled for paid plans, action requests Stripe checkout session (/billing/stripe/checkout-session) and redirects user.
  4. Stripe returns user to /workspace/settings?section=settings-billing&stripe=success|cancel.
  5. Backend webhook (/billing/stripe/webhook) verifies signature with Stripe SDK and synchronizes subscription state into org_subscriptions.

File flows

Startup data room + readiness documents

  1. Client validates file constraints in workspace settings.
  2. Client calls NestJS Files module to request a signed PUT URL for a given org and document type.
  3. Client uploads bytes directly to Cloudflare R2 using the signed URL.
  4. Client persists metadata through startups APIs (/startups/data-room/documents), which write into startup_data_room_documents.
  5. DB source of truth is startup_data_room_documents (with document_type, storage_bucket, storage_object_path, and canonical file_url pointing at R2).

Startup model boundary:

  • startup_profiles: structured profile metadata + narrative summaries.
  • startup_data_room_documents: actual uploaded evidence and diligence files.

Compatibility note:

  • API responses (/startups/profile, workspace snapshot) still expose pitch/financial/legal file pointer fields for compatibility, but those values are now derived from startup_data_room_documents, not stored in startup_profiles.

Organization logos and profile avatars

  1. Client server actions call NestJS Files module for a signed PUT URL (profile avatar or organization logo).
  2. Client uploads bytes directly to R2 with the signed URL.
  3. NestJS returns the final public URL, which the client persists into profiles.avatar_url or organizations.logo_url.

Database highlights

  • Canonical startup readiness view is public.startup_readiness (non-versioned).
  • Admin aggregate readiness view is public.organization_readiness_summary_v1.
  • Legacy table startup_readiness_profiles has been removed.
  • Legacy document-pointer columns were dropped from startup_profiles in phase-5 migrations.
  • startup_data_room_documents now supports canonical storage references:
    • storage_bucket
    • storage_object_path

Environment configuration

Next.js (impactis-client/.env)

At minimum:

  • NEXT_PUBLIC_SITE_URL
  • NEXT_PUBLIC_IMPACTIS_API_URL (for example http://localhost:4000/api/v1)
  • Better Auth public envs required by your auth configuration (see impactis-client/src/lib/auth.ts).

Optional:

  • NEXT_PUBLIC_TURNSTILE_SITE_KEY (Cloudflare Turnstile site key used by auth pages).
  • NEXT_PUBLIC_BILLING_STRIPE_REDIRECTS_ENABLED

NestJS (impactis-server/.env)

Core:

  • PORT
  • WEB_ORIGIN
  • DATABASE_URL
  • BETTER_AUTH_JWKS_URL
  • BETTER_AUTH_ISSUER

Optional features:

  • Stripe env vars (STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET)
  • BILLING_STRIPE_REDIRECTS_ENABLED
  • ADMIN_USER_EMAILS (for /admin/readiness)
  • Upstash cache env vars (UPSTASH_*, CACHE_*) – wired but effectively disabled by code (cache is a no-op).

Cloudflare R2 (impactis-server/.env)

  • R2_ACCOUNT_ID
  • R2_ACCESS_KEY_ID
  • R2_SECRET_ACCESS_KEY
  • R2_BUCKET_NAME
  • R2_PUBLIC_BASE_URL (e.g. https://pub-<id>.r2.dev)

Current implementation status

  • Implemented and active

    • Workspace, startups, billing, sessions, organizations/profiles, health APIs.
    • Stripe hosted checkout + billing portal + webhook synchronization.
    • Startup readiness computed from startup_profiles + startup_data_room_documents.
    • Startup data-room document lifecycle (upsert/delete/list) with plan-gated limits.
  • Implemented but not primary path

    • FilesModule R2 signed URL endpoints exist, but workspace upload actions currently use Supabase Storage direct upload.
  • Known gaps

    • Engagement / intro workflows are not wired up yet; there is no /engagements controller in the backend and previous frontend hooks have been removed.
    • Capability-based feature gates are designed and documented in docs/FEATURE_GATES_IMPLEMENTATION_PLAN.md but are not yet backed by dedicated DB tables or NestJS guards; current checks are membership/role + readiness + implicit plan limits.