diff --git a/README.md b/README.md index 84516fa..81f7fb7 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,17 @@ costs cents per month at idle. --- -> **Status — Phase 2 (Auth + multi-tenant orgs) complete.** -> CDK stacks are deployed to AWS (`sa-east-1`), the JWT auth API is live -> (register / login / refresh / logout / me) on Neon Postgres, and the Angular -> SPA ships login, register, route guards, HTTP interceptors and a session -> shell wired to the live API. Uploads, the AI pipeline and billing land next. +> **Status — Phase 5 complete; Phase 6 (production deploy) in progress.** +> The full MVP is live on AWS (`sa-east-1`): JWT auth + multi-tenant orgs, +> document upload to S3 via presigned URLs, an async AI pipeline (extract → +> summarize → classify with `gpt-4o-mini`), full-text search and a metrics +> dashboard. The Angular SPA covers auth, upload, document detail with live AI +> results, search/filters and the dashboard. Phase 6 wires the frontend to +> Vercel for a public demo URL. + + + +**Live demo:** _coming soon (Vercel)._ · **API:** `https://ngm5oizp91.execute-api.sa-east-1.amazonaws.com/v1/health` ## Why this exists @@ -124,20 +130,26 @@ runs Prettier + ESLint on staged files via Husky pre-commit hooks. The work is sequenced into publishable phases. Each phase produces something demoable so the project never sits half-finished. -| Phase | Theme | Status | -| ----- | ---------------------------------------------- | ------- | -| 0 | Workspace, tooling, libs, healthcheck handler | ✅ Done | -| 1 | CDK stacks (S3, API GW, observability) | ✅ Done | -| 2 | Auth + multi-tenant orgs (API + frontend UI) | ✅ Done | -| 3 | Upload + S3 storage | 🟡 Next | -| 4 | AI pipeline (extract → summarize → classify) | ⏳ | -| 5 | Dashboard + full-text search | ⏳ | -| 6 | Polish + production deploy | ⏳ | -| 7+ | Embeddings + RAG chat + folders + Stripe + OCR | ⏳ | +| Phase | Theme | Status | +| ----- | ---------------------------------------------- | -------------- | +| 0 | Workspace, tooling, libs, healthcheck handler | ✅ Done | +| 1 | CDK stacks (S3, API GW, observability) | ✅ Done | +| 2 | Auth + multi-tenant orgs (API + frontend UI) | ✅ Done | +| 3 | Upload + S3 storage | ✅ Done | +| 4 | AI pipeline (extract → summarize → classify) | ✅ Done | +| 5 | Dashboard + full-text search | ✅ Done | +| 6 | Polish + production deploy (frontend → Vercel) | 🟡 In progress | +| 7+ | Embeddings + RAG chat + folders + Stripe + OCR | ⏳ | See [`docs/plan.md`](docs/plan.md) for the detailed architecture, data model, security model and AWS free-tier strategy. +## Deployment + +- **Backend (AWS CDK → sa-east-1):** `cd infra && AWS_PROFILE=clouddocs-dev AWS_REGION=sa-east-1 ALERT_EMAIL=… pnpm exec cdk deploy --all`. App stacks live in `sa-east-1`; the billing/observability stack is pinned to `us-east-1`. +- **Frontend (Vercel):** connect the GitHub repo in Vercel — `vercel.json` sets the build (`nx build web --configuration=production`), output (`dist/apps/web/browser`) and SPA rewrites. After the first deploy, allow the resulting origin on the API by redeploying with `WEB_ORIGIN=https://.vercel.app` (the CORS allowlist reads it; credentials require an exact origin). +- **Secrets:** `clouddocs/dev/api` in Secrets Manager holds `DATABASE_URL`, the JWT keys and `OPENAI_API_KEY`; populate with `pnpm secrets:put:dev` from `.env.local`. + ## License MIT. diff --git a/apps/web/project.json b/apps/web/project.json index f1e4156..2858cb6 100644 --- a/apps/web/project.json +++ b/apps/web/project.json @@ -36,7 +36,13 @@ "maximumError": "8kb" } ], - "outputHashing": "all" + "outputHashing": "all", + "fileReplacements": [ + { + "replace": "apps/web/src/environments/environment.ts", + "with": "apps/web/src/environments/environment.prod.ts" + } + ] }, "development": { "optimization": false, diff --git a/apps/web/src/environments/environment.prod.ts b/apps/web/src/environments/environment.prod.ts new file mode 100644 index 0000000..98449c7 --- /dev/null +++ b/apps/web/src/environments/environment.prod.ts @@ -0,0 +1,10 @@ +/** + * Production environment, swapped in for `environment.ts` by the `production` + * build configuration (see project.json `fileReplacements`). Same API endpoint + * as dev for now (single sa-east-1 backend); `production: true` enables prod + * behaviour and is the hook for any future prod-only config. + */ +export const environment = { + production: true, + apiBaseUrl: 'https://ngm5oizp91.execute-api.sa-east-1.amazonaws.com', +}; diff --git a/infra/lib/config.ts b/infra/lib/config.ts index eb57996..0af2a4b 100644 --- a/infra/lib/config.ts +++ b/infra/lib/config.ts @@ -7,6 +7,8 @@ export interface InfraConfig { readonly env: Required; readonly resourcePrefix: string; readonly alertEmail: string; + /** Browser origins allowed by the API CORS policy (credentials require exact origins). */ + readonly webOrigins: string[]; readonly billingAlarmUsd: number; readonly uploadsLifecycle: { readonly transitionToIaDays: number; @@ -56,11 +58,18 @@ export function loadConfig(stageInput?: string): InfraConfig { const stageDefaults = STAGE_DEFAULTS[stage]; + // Dev server origin is always allowed; the deployed frontend origin (Vercel) + // is supplied via WEB_ORIGIN at deploy time so we never hardcode it. + const webOrigins = ['http://localhost:4200']; + const webOrigin = process.env.WEB_ORIGIN?.trim(); + if (webOrigin && !webOrigins.includes(webOrigin)) webOrigins.push(webOrigin); + return { stage, env: { account, region }, resourcePrefix: `clouddocs-${stage}`, alertEmail, + webOrigins, billingAlarmUsd: stageDefaults.billingAlarmUsd, uploadsLifecycle: stageDefaults.uploadsLifecycle, customDomain: { enabled: false }, diff --git a/infra/lib/stacks/api-stack.ts b/infra/lib/stacks/api-stack.ts index fee4812..1871484 100644 --- a/infra/lib/stacks/api-stack.ts +++ b/infra/lib/stacks/api-stack.ts @@ -159,9 +159,9 @@ export class ApiStack extends cdk.Stack { description: 'CloudDocs public HTTP API (v2).', corsPreflight: { // allowCredentials=true (needed for the refresh cookie) is incompatible - // with allowOrigins '*'. Enumerate explicit origins per stage. - // TODO Phase 6: add the prod Vercel + custom domain origins here. - allowOrigins: ['http://localhost:4200'], + // with allowOrigins '*'. Origins come from config.webOrigins: localhost + // always + the deployed frontend origin via the WEB_ORIGIN env var. + allowOrigins: config.webOrigins, allowMethods: [ apigwv2.CorsHttpMethod.GET, apigwv2.CorsHttpMethod.POST, diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..b699425 --- /dev/null +++ b/vercel.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "buildCommand": "pnpm nx build web --configuration=production", + "installCommand": "pnpm install --frozen-lockfile", + "outputDirectory": "dist/apps/web/browser", + "framework": null, + "github": { "silent": true }, + "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] +}