Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: set once the Vercel deploy is connected, e.g. https://clouddocs.vercel.app -->

**Live demo:** _coming soon (Vercel)._ · **API:** `https://ngm5oizp91.execute-api.sa-east-1.amazonaws.com/v1/health`

## Why this exists

Expand Down Expand Up @@ -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://<project>.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.
8 changes: 7 additions & 1 deletion apps/web/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions apps/web/src/environments/environment.prod.ts
Original file line number Diff line number Diff line change
@@ -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',
};
9 changes: 9 additions & 0 deletions infra/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface InfraConfig {
readonly env: Required<Environment>;
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;
Expand Down Expand Up @@ -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 },
Expand Down
6 changes: 3 additions & 3 deletions infra/lib/stacks/api-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -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" }]
}
Loading