mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-09-29 16:24:22 +10:00
* feat(deploy): support Vercel Hobby alongside Docker * fix(deploy): include PDFKit runtime font assets * docs(deploy): document Vercel and Docker setup * docs(deploy): record storage persistence checks * refactor(deploy): drop scheduled staging cleanup Staging uploads are deleted after finalization and expired ones are swept on each new upload, so the Vercel cron job, its route, and CRON_SECRET are no longer needed. The Deploy with Vercel wizard now asks for two secrets. * docs(deploy): restructure Vercel guides Split the Vercel page into a how-to with its environment reference, move the large RPC staging protocol to an API reference page, and move CI deployment checks to the contributing section. Point Deploy with Vercel buttons at main. * chore: remove agent planning records and fix web app description Delete superpowers plans/specs, ADRs, issue plans, execution briefs, domain context maps, and Europass research. Describe apps/web as a TanStack Router SPA served by apps/server. * refactor(deploy): simplify Vercel support code - Share one Redis client and key namespace through @reactive-resume/db/redis for API and auth instead of a second auth-only client. - Drop the auth seeding retry; the provider already treats concurrent inserts as no-ops and deployment preparation seeds before runtime. - Detect staging support from POST /api/storage/stage (404 on Docker) instead of a separate GET probe. - Read staged bodies directly; the signed upload already caps their size. - Close per-subscription Redis connections with disconnect() alone. - Check Blob health with one list call instead of write/read/delete. - Remove redundant tsdown onlyBundle list, dead namespace fallbacks, and the conditional spread in the health status. * fix(deploy): heal stopped runs with dead owners and keep auth up without Redis - Run owners refresh a Redis heartbeat until they release their claim. Stop requests reap the run immediately when the owner has stopped heartbeating, instead of leaving the thread blocked until the 15-minute TTL reaper. - Auth and oRPC rate limiters fall back to per-instance memory limits when Redis errors, instead of rejecting every login or failing requests. * ci: allow esbuild build for Vercel CLI and register deployment deps with knip pnpm 12 fails dlx installs with ignored build scripts, so allow esbuild explicitly. The server bundle keeps @vercel/blob, ioredis, and jose external, and api/index.mjs is the Vercel Function entry. * fix(web): send buffered RPC bodies instead of teed streams Reading a request clone turned the original body into a stream, which browsers send without inspectable request data and which needs duplex mode. Send the already buffered Blob for direct requests. * fix(web): send direct RPC bodies as bytes Blob request bodies are sent as data pipes, so browser tooling cannot inspect them. Buffer the original request as an ArrayBuffer and send those bytes; this restores the e2e save assertions that match on request data.
144 lines
6.5 KiB
Plaintext
144 lines
6.5 KiB
Plaintext
---
|
|
title: "Project architecture"
|
|
description: "How the Reactive Resume monorepo is laid out, the runtime boundaries between the web and server apps, and the package ownership model."
|
|
---
|
|
|
|
Reactive Resume is a pnpm/Turborepo monorepo. Docker runs one Node.js process. Vercel serves static assets through its CDN and uses a Node.js Function for the same Hono application. Both targets share the web app, API, authentication, renderers, and database schema.
|
|
|
|
Internal packages are source-consumed through their `package.json` export maps. Import package subpaths, not another workspace's private `src` files.
|
|
|
|
---
|
|
|
|
## Runtime shape
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Browser["Browser"] --> WebRoutes["apps/web routes"]
|
|
WebRoutes --> ORPCClient["oRPC client"]
|
|
ORPCClient --> RPC["/api/rpc"]
|
|
|
|
subgraph NodeProcess["Node process"]
|
|
Server["apps/server Hono adapter"]
|
|
API["packages/api feature routers"]
|
|
Auth["packages/auth"]
|
|
MCP["packages/mcp"]
|
|
PDFServer["@reactive-resume/pdf/server"]
|
|
end
|
|
|
|
Server --> RPC
|
|
RPC --> API
|
|
Server --> Auth
|
|
Server --> MCP
|
|
API --> PDFServer
|
|
API --> DB["packages/db"]
|
|
API --> Storage["Local disk, S3, or private Vercel Blob"]
|
|
DB --> Postgres["PostgreSQL"]
|
|
```
|
|
|
|
`apps/web` owns the React SPA with TanStack Router and Vite. `apps/server` owns the Hono application and mounts RPC, auth, OpenAPI, MCP, static uploads, schema JSON, and the built web app.
|
|
|
|
---
|
|
|
|
## Workspace map
|
|
|
|
| Workspace | Ownership |
|
|
| --- | --- |
|
|
| `apps/web` | TanStack Router routes, web features, browser PDF.js preview/viewer code, PWA setup, oRPC browser client |
|
|
| `apps/server` | Hono route composition, production HTTP adapters, MCP transport, OpenAPI/well-known handlers, static file serving, startup checks |
|
|
| `packages/api` | oRPC procedures and feature-owned business behavior under `src/features/*` |
|
|
| `packages/auth` | Better Auth config, auth helpers, and exported auth types |
|
|
| `packages/db` | Drizzle client and schema; root `migrations/` stores generated migrations |
|
|
| `packages/env` | Server environment validation and root `.env` loading |
|
|
| `packages/schema` | Zod schemas and typed resume/page/template models |
|
|
| `packages/resume` | Pure resume-domain helpers, including JSON Patch behavior and network icon mapping |
|
|
| `packages/pdf` | React PDF document, template primitives, templates, font registration, and browser/server generation adapters |
|
|
| `packages/docx` | DOCX export generation |
|
|
| `packages/mcp` | MCP tools, prompts, resources, server card, and tool metadata |
|
|
| `packages/ui` | Shared Base UI/shadcn-style primitives and hooks |
|
|
| `packages/ai` | AI provider types, prompts, resume parsing/sanitization helpers, and model-facing tool contracts |
|
|
| `packages/import` | Resume importers |
|
|
| `packages/fonts` | Font metadata |
|
|
| `packages/email` | Email transport and templates |
|
|
| `packages/utils` | Narrow cross-cutting utilities with explicit export subpaths |
|
|
| `packages/config` | Shared development configuration |
|
|
| `tooling` | Development-only scripts and repo tooling |
|
|
|
|
---
|
|
|
|
## Boundary rules
|
|
|
|
- Use `@reactive-resume/*` package exports for cross-workspace imports.
|
|
- Do not import another workspace through `apps/**`, `packages/**`, `@reactive-resume/*/src/**`, or a TypeScript path alias to another workspace's `src`.
|
|
- Keep browser-only code in web features or explicit browser subpaths.
|
|
- Keep server-only code in server packages or explicit server subpaths.
|
|
- Keep environment-neutral domain packages free of DB, HTTP, DOM, and app imports.
|
|
- Add public package exports deliberately. Wildcard exports are reserved for leaf-style public surfaces such as UI components/hooks and schema resume files.
|
|
|
|
The checks are executable:
|
|
|
|
```bash
|
|
pnpm exec turbo boundaries
|
|
pnpm exec biome check biome.json turbo.json tooling/grit/no-cross-workspace-src-imports.grit apps/web/tsconfig.json apps/*/turbo.json packages/*/turbo.json
|
|
```
|
|
|
|
---
|
|
|
|
## Feature placement
|
|
|
|
When adding code, choose the owner by behavior:
|
|
|
|
| Change | Put it here |
|
|
| --- | --- |
|
|
| Route, loader, route-level server handler, or web workflow | `apps/web/src/routes` plus `apps/web/src/features/<domain>` |
|
|
| API procedure or authenticated business behavior | `packages/api/src/features/<domain>` |
|
|
| Pure resume data logic | `packages/resume` |
|
|
| Resume schema or template list shape | `packages/schema` |
|
|
| React PDF template/rendering behavior | `packages/pdf` |
|
|
| PDF.js canvas/viewer UI | `apps/web/src/features/resume` |
|
|
| DOCX export behavior | `packages/docx` |
|
|
| MCP tool/prompt/resource behavior | `packages/mcp` |
|
|
| Shared UI primitive/hook | `packages/ui` |
|
|
| Cross-cutting helper | Prefer a domain package first; otherwise add an explicit `packages/utils` export |
|
|
|
|
---
|
|
|
|
## Web layout
|
|
|
|
`apps/web/src/routes` stays route-owned. Route files handle URL shape, loaders, redirects, metadata, and SSR flags.
|
|
|
|
Domain UI and browser-heavy implementation code lives under `apps/web/src/features`. Current feature areas include resume preview/export/public pages, command palette, auth, settings, theme, locale, and user menu behavior.
|
|
|
|
Generic app-local components remain in `apps/web/src/components`; shared reusable primitives live in `packages/ui`.
|
|
|
|
Dialog runtime state is centralized in `apps/web/src/dialogs/store.ts`, while dialog schemas and renderers are registered by domain under `apps/web/src/dialogs/{auth,api-key,resume}`.
|
|
|
|
---
|
|
|
|
## API layout
|
|
|
|
`packages/api/src/routers/index.ts` exports the top-level oRPC contract. Feature modules under `packages/api/src/features/*` own their procedure modules, services, helpers, tests, and public package exports.
|
|
|
|
Avoid reintroducing technical-layer folders such as `services/` or `helpers/` at the package root. If a helper is used by one feature, keep it in that feature. If it becomes shared, name the shared capability explicitly and export it intentionally.
|
|
|
|
---
|
|
|
|
## PDF and export boundaries
|
|
|
|
`packages/pdf` owns React PDF generation:
|
|
|
|
- `@reactive-resume/pdf/browser` creates browser PDF blobs.
|
|
- `@reactive-resume/pdf/server` creates server PDF files.
|
|
- Template code stays under `packages/pdf/src/templates`.
|
|
|
|
Localized section-title resolution stays in the caller because it depends on web/server locale context. PDF.js preview and viewer code stays in `apps/web/src/features/resume`, not in `packages/pdf`.
|
|
|
|
DOCX export generation lives in `packages/docx`.
|
|
|
|
---
|
|
|
|
## MCP boundary
|
|
|
|
MCP implementation lives in `packages/mcp`. It exposes canonical unprefixed tool names such as `list_resumes`, `read_resume`, and `apply_resume_patch`.
|
|
|
|
The server process imports MCP from `@reactive-resume/mcp` and injects the in-process oRPC router client. It must not import MCP code from `apps/web/src`.
|