Skip to content

Architecture

The repository is an npm workspaces monorepo (Node.js 22 or newer).

  • package.json root scripts (dev, build, start, test, lint, teams:package)
  • Directorypackages/
    • Directoryshared/ @pdfeditor/shared: brand, plans and prices, roles, activity actions, field types, e-sign consent text
      • …
  • Directoryapps/
    • Directoryapi/ @pdfeditor/api: Express API, Postgres access, migrations, scripts
      • Directorymigrations/
        • Directorycore/ application tables
          • …
        • Directoryaudit/ the write-only activity log
          • …
      • sql/audit-roles.sql insert-only and read-only roles for the audit log
      • Directoryscripts/ dev-db, migrate, create-super-admin, stripe-setup
        • …
    • Directoryweb/ @pdfeditor/web: the React app (editor, signing, admin, platform), built with Vite
      • …
    • Directorydocs/ @pdfeditor/docs: this site (Astro Starlight)
      • …
    • Directorymarketing/ @pdfeditor/marketing: the marketing website (Astro)
      • …
  • Directoryteams/
    • build-package.mjs builds the Microsoft Teams app package
Browser / Microsoft Teams
│ HTTPS (one origin)
▼
API (Node.js, Express) ── serves apps/web/dist and /api/*
│ │ │ │
│ │ │ └─ Mailgun (platform email)
│ │ └─ Stripe (checkout, portal, webhooks)
│ └─ Microsoft Entra ID + Microsoft Graph (on behalf of the user)
▼
PostgreSQL ── core data, documents sent for signature
└── audit.events (optionally a separate database)
  • One origin. In production the API serves the built web app (apps/web/dist) as well as /api. Same-origin means no CORS and no third-party cookies, which matters inside the Teams iframe. Hashed files under /assets/ are cached for a year; everything else is served with no-cache. Any non-API path returns index.html so client-side routes work.
  • Sessions are tokens sent in the Authorization header (not cookies), stored in the browser’s local storage outside Teams and in memory inside Teams.
  • Microsoft access is delegated: the browser sends the user’s Entra token for this API, and the API exchanges it for a Microsoft Graph token (OAuth 2.0 on-behalf-of) for each call. See Security model.
  • PostgreSQL holds organizations, users, sessions, signing requests and their PDFs. The activity log lives in the audit schema, in the same database or a separate one. See Database and audit log.
  • A background job in the API process runs hourly: it expires overdue signing requests and sends automatic reminders.
  • The docs and marketing sites are static builds (apps/docs/dist, apps/marketing/dist). Where you host them is your choice.
Script What it does
npm run dev API (--watch, port 3001) and web app (Vite, port 5173) together
npm run dev:db Local embedded Postgres for development
npm run build Builds the web app, the docs and the marketing site
npm start Starts the API (which serves the built web app)
npm test Tests for @pdfeditor/shared and the API (the API tests start their own embedded Postgres)
npm run lint Lints the web app and the API
npm run teams:package Builds the Teams app package

API workspace scripts (npm run <script> -w @pdfeditor/api): dev, start, db:dev, migrate, create-super-admin, stripe:setup, test, lint.