mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-09-30 16:54:26 +10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47abce351b | ||
|
|
8f6c65b7fd | ||
|
|
5a88ac12bf | ||
|
|
118004b3d3 | ||
|
|
6a8dd480cb | ||
|
|
5d8126d4b0 | ||
|
|
8566a9fd0f | ||
|
|
0d0bc61582 | ||
|
|
e3af637100 | ||
|
|
f752145c57 | ||
|
|
b87f200767 | ||
|
|
73ec8b2ffb | ||
|
|
107537999f | ||
|
|
e1bccbcc93 | ||
|
|
54e256be09 | ||
|
|
b26d510294 | ||
|
|
623ca5c675 | ||
|
|
76e00ae019 | ||
|
|
ff0ef12ecc | ||
|
|
1bcb2e2af6 | ||
|
|
796dc2ca41 | ||
|
|
80bf41254b | ||
|
|
bbc38d2f09 | ||
|
|
775f625f6e | ||
|
|
2203933fac | ||
|
|
907e32a731 | ||
|
|
d6919e340b | ||
|
|
e05ae42fbb | ||
|
|
e99f39d139 | ||
|
|
a4e7d6680d | ||
|
|
77ad14b359 | ||
|
|
9b3916d43e | ||
|
|
29f1aaf0da | ||
|
|
73cef02a4f | ||
|
|
2cb1897922 | ||
|
|
d0af9f4b4f | ||
|
|
08e9c80037 | ||
|
|
a42dbcd452 | ||
|
|
d3102565e4 | ||
|
|
c0387298c5 | ||
|
|
9e1e593dc4 | ||
|
|
849aad6497 | ||
|
|
847d69b621 | ||
|
|
6ff754d125 | ||
|
|
ff3d4b1337 | ||
|
|
2d2d5cd505 | ||
|
|
c6e8df0a00 | ||
|
|
7df9b1e4b5 | ||
|
|
0b4bac62a7 | ||
|
|
82cb6e7590 | ||
|
|
46dac6d3b1 | ||
|
|
b0b0d27c1f | ||
|
|
b12d1184bc | ||
|
|
d6e0d9ac61 | ||
|
|
c19b9746c8 | ||
|
|
4ed6177aee | ||
|
|
5900de101c | ||
|
|
61b3324941 | ||
|
|
0e858c5967 | ||
|
|
8c968e92f4 | ||
|
|
bea8ff1beb | ||
|
|
85e0b0a96d | ||
|
|
1b266ba7ac | ||
|
|
1810dc8b07 | ||
|
|
06b9da39ed | ||
|
|
11373763fd | ||
|
|
b3224ce59b | ||
|
|
145b17de0f | ||
|
|
623c5f6f81 | ||
|
|
bcbe70d231 | ||
|
|
8b52b9d8fc | ||
|
|
4ecc66d081 | ||
|
|
10d58175f1 | ||
|
|
923f5f6173 | ||
|
|
9332e1e3ff | ||
|
|
77f31fa127 | ||
|
|
4fd43657dc | ||
|
|
d9a24448e8 | ||
|
|
69ff78b656 | ||
|
|
0794b5c955 | ||
|
|
5c986cc68d | ||
|
|
688c002822 | ||
|
|
ced49d5314 | ||
|
|
3653baad9b | ||
|
|
b4aaf9712f | ||
|
|
e3274d7c95 | ||
|
|
a7a3d53dbd | ||
|
|
f8d776106f | ||
|
|
e34f648455 | ||
|
|
f4e2d05f25 | ||
|
|
bbc17b8995 | ||
|
|
a66bc13f4f | ||
|
|
7858efbd2b | ||
|
|
7da5e2c0c2 | ||
|
|
177c550a0c |
+14
-1
@@ -10,7 +10,7 @@ APP_URL="http://localhost:3000"
|
||||
PRINTER_APP_URL="http://host.docker.internal:3000"
|
||||
|
||||
# --- Printer ---
|
||||
PRINTER_ENDPOINT="ws://localhost:4000?token=1234567890"
|
||||
PRINTER_ENDPOINT="ws://localhost:4000?token=change-me"
|
||||
|
||||
# --- Database (PostgreSQL) ---
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
|
||||
@@ -30,12 +30,21 @@ GOOGLE_CLIENT_SECRET=""
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_CLIENT_SECRET=""
|
||||
|
||||
# Social Auth (LinkedIn, optional)
|
||||
LINKEDIN_CLIENT_ID=""
|
||||
LINKEDIN_CLIENT_SECRET=""
|
||||
|
||||
# Custom OAuth Provider (optional)
|
||||
OAUTH_PROVIDER_NAME=""
|
||||
OAUTH_CLIENT_ID=""
|
||||
OAUTH_CLIENT_SECRET=""
|
||||
OAUTH_DISCOVERY_URL=""
|
||||
OAUTH_AUTHORIZATION_URL=""
|
||||
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=""
|
||||
|
||||
# AI provider base URL allowlist (optional, comma-separated hosts/origins)
|
||||
# Example: api.openai.com,https://gateway.ai.vercel.com
|
||||
AI_ALLOWED_BASE_URLS=""
|
||||
|
||||
# --- Email (optional) ---
|
||||
# If all keys are disabled, the app logs the email to be sent to the console instead.
|
||||
@@ -78,3 +87,7 @@ FLAG_DISABLE_IMAGE_PROCESSING="false"
|
||||
# This is not used within Reactive Resume, but in src/scripts/fonts/generate.ts to generate a list of fonts served by Google Fonts.
|
||||
# Note: Make sure "Google Fonts Developer API" is unrestricted.
|
||||
GOOGLE_CLOUD_API_KEY=""
|
||||
|
||||
# --- Crowdin ---
|
||||
CROWDIN_PROJECT_ID=""
|
||||
CROWDIN_API_TOKEN=""
|
||||
@@ -8,21 +8,31 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
autofix:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- uses: voidzero-dev/setup-vp@v1
|
||||
- name: Setup Vite+
|
||||
uses: voidzero-dev/setup-vp@v1
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: true
|
||||
|
||||
- run: vp install
|
||||
- name: Install Dependencies
|
||||
run: vp install --frozen-lockfile
|
||||
|
||||
- run: vp fmt
|
||||
- run: vp lint --fix
|
||||
- name: Run Tests
|
||||
run: vp test
|
||||
|
||||
- uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8
|
||||
- name: Run Lint and Format
|
||||
run: vp check --fix
|
||||
|
||||
- name: Autofix
|
||||
uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Sync Translations from Crowdin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
crowdin-sync:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Sync Translations from Crowdin
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
download_translations: true
|
||||
localization_branch_name: "l10n"
|
||||
commit_message: "[skip ci] chore(i18n): sync translations from crowdin"
|
||||
pull_request_title: "Sync Translations from Crowdin"
|
||||
pull_request_body: "This is an automated pull request to sync translations from [Crowdin](https://crowdin.com/project/reactive-resume)."
|
||||
pull_request_labels: "l10n"
|
||||
pull_request_reviewers: "amruthpillai"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
@@ -9,6 +9,7 @@ concurrency:
|
||||
|
||||
env:
|
||||
IMAGE: ${{ github.repository }}
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -41,16 +42,16 @@ jobs:
|
||||
run: echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -58,7 +59,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ env.IMAGE }}
|
||||
@@ -68,7 +69,7 @@ jobs:
|
||||
|
||||
- name: Build and Push by Digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
sbom: true
|
||||
@@ -88,7 +89,7 @@ jobs:
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: digests-${{ matrix.arch }}
|
||||
path: /tmp/digests/*
|
||||
@@ -118,23 +119,23 @@ jobs:
|
||||
run: echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -152,7 +153,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ env.IMAGE }}
|
||||
|
||||
@@ -2,9 +2,11 @@ dist
|
||||
.env*
|
||||
/data
|
||||
.nitro
|
||||
.agents
|
||||
.output
|
||||
.vercel
|
||||
.cursor
|
||||
.claude
|
||||
TODO.md
|
||||
coverage
|
||||
.netlify
|
||||
|
||||
Vendored
+4
-5
@@ -1,11 +1,8 @@
|
||||
{
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode"
|
||||
},
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.oxc": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file",
|
||||
"files.associations": {
|
||||
@@ -25,6 +22,7 @@
|
||||
"i18n-ally.sourceLanguage": "en-US",
|
||||
"oxc.enable.oxfmt": true,
|
||||
"oxc.enable.oxlint": true,
|
||||
"oxc.configPath": "./vite.config.ts",
|
||||
"oxc.fmt.configPath": "./vite.config.ts",
|
||||
"search.exclude": {
|
||||
"**/routeTree.gen.ts": true,
|
||||
@@ -38,5 +36,6 @@
|
||||
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||
],
|
||||
"tailwindCSS.experimental.configFile": "src/styles/globals.css",
|
||||
"typescript.experimental.useTsgo": true
|
||||
"typescript.experimental.useTsgo": true,
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
||||
|
||||
@@ -1,12 +1,69 @@
|
||||
# AGENTS.md
|
||||
## Overview
|
||||
|
||||
## Cursor Cloud specific instructions
|
||||
Reactive Resume is a single-package full-stack TypeScript app (not a monorepo) built with [TanStack Start](https://tanstack.com/start/latest/docs/framework/react/overview) (React, Vite, Nitro). It serves both frontend and API on port 3000.
|
||||
|
||||
### Overview
|
||||
This project uses [Vite+](https://vite.dev/blog/announcing-viteplus), a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. All modules should be imported from the `vite-plus` dependency (e.g., `import { defineConfig } from 'vite-plus'` or `import { expect, test, vi } from 'vite-plus/test'`).
|
||||
|
||||
Reactive Resume is a single-package full-stack TypeScript app (not a monorepo) built with TanStack Start (React 19, Vite, Nitro). It serves both frontend and API on port 3000.
|
||||
## Key Libraries
|
||||
|
||||
### Infrastructure services
|
||||
| Area | Library | Docs |
|
||||
| -------------------- | ------------------------------------------------------------------------ | ---------------------------------- |
|
||||
| Frontend framework | React | https://react.dev |
|
||||
| Full-stack framework | TanStack Start | https://tanstack.com/start/latest |
|
||||
| Router | TanStack React Router | https://tanstack.com/router/latest |
|
||||
| Server state | TanStack React Query | https://tanstack.com/query/latest |
|
||||
| Client state | Zustand (+ Zundo for undo/redo, Immer for immutable updates) | https://zustand.docs.pmnd.rs |
|
||||
| Type-safe API | oRPC | https://orpc.unnoq.com |
|
||||
| Database ORM | Drizzle ORM (PostgreSQL) | https://orm.drizzle.team |
|
||||
| Authentication | Better Auth (+ Drizzle adapter, OAuth provider, API keys, 2FA, Passkeys) | https://www.better-auth.com |
|
||||
| Styling | Tailwind CSS | https://tailwindcss.com |
|
||||
| UI Components | shadcn/ui (built on Base UI) | https://ui.shadcn.com |
|
||||
| Icons | Phosphor Icons | https://phosphoricons.com |
|
||||
| Forms | React Hook Form (+ Zod resolvers) | https://react-hook-form.com |
|
||||
| Rich text editor | Tiptap | https://tiptap.dev |
|
||||
| Validation | Zod | https://zod.dev |
|
||||
| AI | Vercel AI SDK (OpenAI, Anthropic, Google, Ollama providers) | https://ai-sdk.dev |
|
||||
| MCP | Model Context Protocol SDK | https://modelcontextprotocol.io |
|
||||
| i18n | Lingui | https://lingui.dev |
|
||||
| Animations | Motion (Framer Motion) | https://motion.dev |
|
||||
| PDF export | Puppeteer Core (via Browserless) | https://pptr.dev |
|
||||
| Drag and drop | dnd-kit | https://dndkit.com |
|
||||
| Server engine | Nitro | https://nitro.build |
|
||||
| PWA | Vite PWA Plugin | https://vite-pwa-org.netlify.app |
|
||||
| Unused deps | Knip | https://knip.dev |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
components/ UI, resume, layout, animation, theme, locale components
|
||||
routes/ File-based routing (TanStack React Router)
|
||||
integrations/ Feature modules (auth, drizzle, orpc, ai, email, jobs, mcp, storage)
|
||||
schema/ Zod schemas for resume data validation
|
||||
utils/ Utility functions (locale, theme, env, resume processing)
|
||||
dialogs/ Modal/dialog components
|
||||
hooks/ Custom React hooks
|
||||
styles/ CSS and Tailwind configuration
|
||||
stores/ Zustand stores (resume, AI, dialog, command palette)
|
||||
migrations/ Drizzle database migrations
|
||||
locales/ Lingui i18n message catalogs (47+ locales)
|
||||
```
|
||||
|
||||
### Key Config Files
|
||||
|
||||
- `vite.config.ts` — Vite + Nitro + TanStack Start + PWA + Tailwind + Lingui
|
||||
- `drizzle.config.ts` — PostgreSQL dialect, schema at `./src/integrations/drizzle/schema.ts`
|
||||
- `tsconfig.json` — ES2022, strict mode, path alias `@/*` → `./src/*`
|
||||
- `lingui.config.ts` — i18n extraction and locale configuration
|
||||
- `components.json` — shadcn CLI configuration
|
||||
|
||||
### API Architecture
|
||||
|
||||
- **oRPC API** (`/api/rpc/*`) — Type-safe RPC with routers for: `ai`, `auth`, `resume`, `storage`, `printer`, `jobs`, `statistics`, `flags`. Three procedure types: `publicProcedure`, `protectedProcedure`, `serverOnlyProcedure`.
|
||||
- **Better Auth API** (`/api/auth/*`) — OAuth, session management, social provider callbacks.
|
||||
- **MCP Server** (`/mcp/`) — Model Context Protocol with OAuth Bearer tokens and API key auth. Exposes resumes as resources and tools for resume CRUD.
|
||||
|
||||
## Infrastructure Services
|
||||
|
||||
Before running the dev server, Docker must be running with at least PostgreSQL. Start services via `compose.dev.yml`:
|
||||
|
||||
@@ -18,7 +75,7 @@ sudo docker compose -f compose.dev.yml up -d postgres browserless
|
||||
- **PostgreSQL** (port 5432) — required. The app auto-runs Drizzle migrations on startup via a Nitro plugin.
|
||||
- **Browserless** (port 4000) — required for PDF export. Maps container port 3000 to host port 4000.
|
||||
|
||||
### Environment variables
|
||||
## Environment Variables
|
||||
|
||||
Copy `.env.example` to `.env` if not present. Key notes for local dev:
|
||||
|
||||
@@ -28,99 +85,47 @@ Copy `.env.example` to `.env` if not present. Key notes for local dev:
|
||||
- `DATABASE_URL` — PostgreSQL connection using `postgres:postgres` credentials on localhost:5432.
|
||||
- S3/Storage and SMTP vars can be left empty — the app falls back to local filesystem and console-logged emails.
|
||||
|
||||
### Common commands
|
||||
## Common Commands
|
||||
|
||||
See `scripts` in `package.json`. Key ones:
|
||||
`vp` is the global CLI for Vite+. Do not use pnpm/npm/yarn directly — Vite+ wraps the underlying package manager.
|
||||
|
||||
| Task | Command |
|
||||
| -------------- | --------------------------------------------------------------- |
|
||||
| Dev server | `pnpm dev` (port 3000) |
|
||||
| Lint (Oxlint) | `pnpm lint` |
|
||||
| Format (Oxfmt) | `pnpm fmt` |
|
||||
| Typecheck | `pnpm typecheck` |
|
||||
| DB migrations | `pnpm db:generate` / `pnpm db:migrate` (auto-runs on dev start) |
|
||||
| Task | Command |
|
||||
| ----------------------------- | --------------------------------------------------------------- |
|
||||
| Install dependencies | `vp install` |
|
||||
| Dev server (port 3000) | `vp dev` |
|
||||
| Lint (Oxlint, type-aware) | `vp lint --type-aware` |
|
||||
| Format (Oxfmt) | `vp fmt` |
|
||||
| Check (lint + format + types) | `vp check` |
|
||||
| Typecheck | `pnpm typecheck` (uses tsgo) |
|
||||
| Run tests | `vp test` |
|
||||
| DB migrations | `pnpm db:generate` / `pnpm db:migrate` (auto-runs on dev start) |
|
||||
| DB studio | `pnpm db:studio` |
|
||||
| i18n extraction | `pnpm lingui:extract` |
|
||||
| Add a dependency | `vp add <package>` |
|
||||
| Remove a dependency | `vp remove <package>` |
|
||||
| One-off binary | `vp dlx <package>` |
|
||||
| Build for production | `vp build` |
|
||||
| Preview production build | `vp preview` |
|
||||
| Start production server | `pnpm start` |
|
||||
|
||||
### Gotchas
|
||||
## Vite+ Pitfalls
|
||||
|
||||
- **Do not use pnpm/npm/yarn directly** for package operations — use `vp add`, `vp remove`, `vp install`, etc.
|
||||
- **Do not run `vp vitest` or `vp oxlint`** — they don't exist. Use `vp test` and `vp lint`.
|
||||
- **Do not install Vitest, Oxlint, Oxfmt, or tsdown directly** — Vite+ bundles them.
|
||||
- **Import from `vite-plus`**, not from `vite` or `vitest` directly (e.g., `import { defineConfig } from 'vite-plus'`).
|
||||
- **Vite+ commands take precedence** over `package.json` scripts. If there's a naming conflict, use `vp run <script>`.
|
||||
- **Use `vp dlx`** instead of `npx` or `pnpm dlx`.
|
||||
- **Type-aware linting** works out of the box with `vp lint --type-aware` — no need to install `oxlint-tsgolint`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- The Docker daemon needs `fuse-overlayfs` storage driver and `iptables-legacy` in the cloud VM (nested container environment).
|
||||
- `pnpm.onlyBuiltDependencies` in `package.json` controls which packages are allowed to run install scripts — no interactive `pnpm approve-builds` needed.
|
||||
- Email verification is optional in dev — after signup, click "Continue" to skip.
|
||||
- Vite 8 is beta (`^8.0.0-beta.15`); Nitro uses a nightly build. Occasional upstream issues may occur.
|
||||
|
||||
<!--VITE PLUS START-->
|
||||
|
||||
# Using Vite+, the Unified Toolchain for the Web
|
||||
|
||||
This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`.
|
||||
|
||||
## Vite+ Workflow
|
||||
|
||||
`vp` is a global binary that handles the full development lifecycle. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.
|
||||
|
||||
### Start
|
||||
|
||||
- create - Create a new project from a template
|
||||
- migrate - Migrate an existing project to Vite+
|
||||
- config - Configure hooks and agent integration
|
||||
- staged - Run linters on staged files
|
||||
- install (`i`) - Install dependencies
|
||||
- env - Manage Node.js versions
|
||||
|
||||
### Develop
|
||||
|
||||
- dev - Run the development server
|
||||
- check - Run format, lint, and TypeScript type checks
|
||||
- lint - Lint code
|
||||
- fmt - Format code
|
||||
- test - Run tests
|
||||
|
||||
### Execute
|
||||
|
||||
- run - Run monorepo tasks
|
||||
- exec - Execute a command from local `node_modules/.bin`
|
||||
- dlx - Execute a package binary without installing it as a dependency
|
||||
- cache - Manage the task cache
|
||||
|
||||
### Build
|
||||
|
||||
- build - Build for production
|
||||
- pack - Build libraries
|
||||
- preview - Preview production build
|
||||
|
||||
### Manage Dependencies
|
||||
|
||||
Vite+ automatically detects and wraps the underlying package manager such as pnpm, npm, or Yarn through the `packageManager` field in `package.json` or package manager-specific lockfiles.
|
||||
|
||||
- add - Add packages to dependencies
|
||||
- remove (`rm`, `un`, `uninstall`) - Remove packages from dependencies
|
||||
- update (`up`) - Update packages to latest versions
|
||||
- dedupe - Deduplicate dependencies
|
||||
- outdated - Check for outdated packages
|
||||
- list (`ls`) - List installed packages
|
||||
- why (`explain`) - Show why a package is installed
|
||||
- info (`view`, `show`) - View package information from the registry
|
||||
- link (`ln`) / unlink - Manage local package links
|
||||
- pm - Forward a command to the package manager
|
||||
|
||||
### Maintain
|
||||
|
||||
- upgrade - Update `vp` itself to the latest version
|
||||
|
||||
These commands map to their corresponding tools. For example, `vp dev --port 3000` runs Vite's dev server and works the same as Vite. `vp test` runs JavaScript tests through the bundled Vitest. The version of all tools can be checked using `vp --version`. This is useful when researching documentation, features, and bugs.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Using the package manager directly:** Do not use pnpm, npm, or Yarn directly. Vite+ can handle all package manager operations.
|
||||
- **Always use Vite commands to run tools:** Don't attempt to run `vp vitest` or `vp oxlint`. They do not exist. Use `vp test` and `vp lint` instead.
|
||||
- **Running scripts:** Vite+ commands take precedence over `package.json` scripts. If there is a `test` script defined in `scripts` that conflicts with the built-in `vp test` command, run it using `vp run test`.
|
||||
- **Do not install Vitest, Oxlint, Oxfmt, or tsdown directly:** Vite+ wraps these tools. They must not be installed directly. You cannot upgrade these tools by installing their latest versions. Always use Vite+ commands.
|
||||
- **Use Vite+ wrappers for one-off binaries:** Use `vp dlx` instead of package-manager-specific `dlx`/`npx` commands.
|
||||
- **Import JavaScript modules from `vite-plus`:** Instead of importing from `vite` or `vitest`, all modules should be imported from the project's `vite-plus` dependency. For example, `import { defineConfig } from 'vite-plus';` or `import { expect, test, vi } from 'vite-plus/test';`. You must not install `vitest` to import test utilities.
|
||||
- **Type-Aware Linting:** There is no need to install `oxlint-tsgolint`, `vp lint --type-aware` works out of the box.
|
||||
- Vite and Nitro use beta/nightly builds. Occasional upstream issues may occur.
|
||||
|
||||
## Review Checklist for Agents
|
||||
|
||||
- [ ] Run `vp install` after pulling remote changes and before getting started.
|
||||
- [ ] Run `vp check` and `vp test` to validate changes.
|
||||
|
||||
<!--VITE PLUS END-->
|
||||
- [ ] Run `pnpm lint:fix`, `pnpm format:fix`, `pnpm typecheck` and `vp test` to validate changes.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# ---------- Development Layer ----------
|
||||
FROM node:24-slim AS development
|
||||
|
||||
ENV NODE_ENV="development"
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["pnpm", "run", "dev"]
|
||||
@@ -126,6 +126,10 @@ Built with privacy as a core principle, Reactive Resume gives you complete owner
|
||||
<img src="public/templates/jpg/ditgar.jpg" alt="Ditgar" width="150" />
|
||||
<br /><sub><b>Ditgar</b></sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="public/templates/jpg/meowth.jpg" alt="Meowth" width="150" />
|
||||
<br /><sub><b>Meowth</b></sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Reactive Resume provides security updates for version `5.0.x` and newer.
|
||||
Earlier major versions are no longer supported.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| >= 5.0 | :white_check_mark: |
|
||||
| 4.x | :x: |
|
||||
| < 4.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you believe you have found a security vulnerability, please report it
|
||||
privately instead of opening a public issue.
|
||||
|
||||
Preferred reporting channels:
|
||||
|
||||
- Use GitHub's private vulnerability reporting for this repository.
|
||||
- If that is unavailable, email `hello@amruthpillai.com` with the subject
|
||||
`Security Vulnerability: Reactive Resume`.
|
||||
|
||||
Please include as much detail as possible, including:
|
||||
|
||||
- A description of the vulnerability and its impact.
|
||||
- Steps to reproduce or a proof of concept.
|
||||
- Affected versions, configuration, or deployment details.
|
||||
- Any relevant logs, screenshots, or links.
|
||||
|
||||
You can expect an initial response within 7 days. If the report is accepted,
|
||||
we will coordinate remediation and disclosure timing with you before publishing
|
||||
details publicly.
|
||||
@@ -19,8 +19,5 @@
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/utils",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {
|
||||
"@animate-ui": "https://animate-ui.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
|
||||
+65
-12
@@ -20,32 +20,34 @@ services:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql
|
||||
ports:
|
||||
- "5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"]
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
browserless:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
image: ${BROWSERLESS_IMAGE:-ghcr.io/browserless/chromium:latest}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4000:3000"
|
||||
environment:
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
- TOKEN=1234567890
|
||||
QUEUED: 10
|
||||
HEALTH: "true"
|
||||
CONCURRENT: 5
|
||||
TOKEN: ${BROWSERLESS_TOKEN:-change-me}
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
test: ["CMD-SHELL", 'curl -f "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN:-change-me}"']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
@@ -57,14 +59,16 @@ services:
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9222:9222"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
restart: unless-stopped
|
||||
command: server -s3 -filer -dir=/data -ip=0.0.0.0
|
||||
environment:
|
||||
- AWS_ACCESS_KEY_ID=seaweedfs
|
||||
- AWS_SECRET_ACCESS_KEY=seaweedfs
|
||||
AWS_ACCESS_KEY_ID: seaweedfs
|
||||
AWS_SECRET_ACCESS_KEY: seaweedfs
|
||||
volumes:
|
||||
- seaweedfs_data:/data
|
||||
ports:
|
||||
@@ -106,7 +110,56 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
web_app:
|
||||
profiles: ["app"]
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
PRINTER_APP_URL: http://web_app:3000
|
||||
PRINTER_ENDPOINT: ws://browserless:3000?token=${BROWSERLESS_TOKEN:-change-me}
|
||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
|
||||
SMTP_HOST: mailpit
|
||||
S3_ENDPOINT: http://seaweedfs:8333
|
||||
CHOKIDAR_INTERVAL: 100
|
||||
WATCHPACK_POLLING: "true"
|
||||
CHOKIDAR_USEPOLLING: "true"
|
||||
volumes:
|
||||
- ./:/app
|
||||
- web_app_data:/app/data
|
||||
- web_app_pnpm_store:/pnpm/store
|
||||
- web_app_nitro_cache:/app/.nitro
|
||||
- web_app_tanstack_cache:/app/.tanstack
|
||||
- web_app_node_modules:/app/node_modules
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
browserless:
|
||||
condition: service_healthy
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
seaweedfs_create_bucket:
|
||||
condition: service_completed_successfully
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
mailpit_data:
|
||||
postgres_data:
|
||||
seaweedfs_data:
|
||||
web_app_data:
|
||||
web_app_pnpm_store:
|
||||
web_app_nitro_cache:
|
||||
web_app_node_modules:
|
||||
web_app_tanstack_cache:
|
||||
|
||||
+32
-8
@@ -4,6 +4,8 @@ services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- data_network
|
||||
environment:
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
@@ -18,15 +20,17 @@ services:
|
||||
retries: 3
|
||||
|
||||
browserless:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
image: ${BROWSERLESS_IMAGE:-ghcr.io/browserless/chromium:latest}
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- printer_network
|
||||
environment:
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
- TOKEN=1234567890
|
||||
- TOKEN=${BROWSERLESS_TOKEN:-change-me}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
test: ["CMD-SHELL", 'curl -f "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN:-change-me}"']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
@@ -36,6 +40,8 @@ services:
|
||||
# chrome:
|
||||
# image: chromedp/headless-shell:latest
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - printer_network
|
||||
# ports:
|
||||
# - "9222:9222"
|
||||
|
||||
@@ -43,6 +49,8 @@ services:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
restart: unless-stopped
|
||||
command: server -s3 -filer -dir=/data -ip=0.0.0.0
|
||||
networks:
|
||||
- storage_network
|
||||
environment:
|
||||
- AWS_ACCESS_KEY_ID=seaweedfs
|
||||
- AWS_SECRET_ACCESS_KEY=seaweedfs
|
||||
@@ -65,21 +73,32 @@ services:
|
||||
mc mb seaweedfs/reactive-resume;
|
||||
exit 0;
|
||||
"
|
||||
networks:
|
||||
- storage_network
|
||||
depends_on:
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
# image: amruthpillai/reactive-resume:latest
|
||||
# image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
networks:
|
||||
- data_network
|
||||
- printer_network
|
||||
- storage_network
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
# Server
|
||||
- TZ=Etc/UTC
|
||||
- NODE_ENV=production
|
||||
- APP_URL=http://localhost:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- PRINTER_APP_URL=http://host.docker.internal:3000
|
||||
# Printer
|
||||
- PRINTER_ENDPOINT=ws://browserless:3000?token=1234567890
|
||||
- PRINTER_ENDPOINT=ws://browserless:3000?token=${BROWSERLESS_TOKEN:-change-me}
|
||||
# - PRINTER_ENDPOINT=http://chrome:9222 # Or, if you're using chromedp/headless-shell
|
||||
# Database
|
||||
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres
|
||||
@@ -93,8 +112,6 @@ services:
|
||||
- S3_FORCE_PATH_STYLE=true
|
||||
volumes:
|
||||
- reactive_resume_data:/app/data
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
@@ -102,6 +119,8 @@ services:
|
||||
condition: service_healthy
|
||||
seaweedfs_create_bucket:
|
||||
condition: service_completed_successfully
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
start_period: 10s
|
||||
@@ -113,3 +132,8 @@ volumes:
|
||||
postgres_data:
|
||||
seaweedfs_data:
|
||||
reactive_resume_data:
|
||||
|
||||
networks:
|
||||
data_network:
|
||||
printer_network:
|
||||
storage_network:
|
||||
|
||||
+8
-5
@@ -1,10 +1,13 @@
|
||||
preserve_hierarchy: true
|
||||
commit_message: "[ci skip]"
|
||||
|
||||
project_id_env: CROWDIN_PROJECT_ID
|
||||
api_token_env: CROWDIN_PERSONAL_TOKEN
|
||||
commit_message: "[skip ci] chore(i18n): sync translations from crowdin"
|
||||
pull_request_title: "Sync Translations from Crowdin"
|
||||
pull_request_labels: ["l10n"]
|
||||
pull_request_reviewers: ["amruthpillai"]
|
||||
|
||||
pull_request_body: "This is an automated pull request to sync translations from [Crowdin](https://crowdin.com/project/reactive-resume)."
|
||||
pull_request_labels:
|
||||
- l10n
|
||||
pull_request_reviewers:
|
||||
- amruthpillai
|
||||
files:
|
||||
- source: /locales/en-US.po
|
||||
translation: /locales/%locale%.%file_extension%
|
||||
|
||||
+309
-149
@@ -4,198 +4,358 @@ description: "List of all notable changes and updates to Reactive Resume"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.0.20" description="27th April 2026">
|
||||
## Features & Improvements
|
||||
- Added the new **Meowth** resume template (compact ATS-friendly layout with an inline three-column entry header), thanks to [@JamesGoslings](https://github.com/JamesGoslings). [#2923](https://github.com/amruthpillai/reactive-resume/pull/2923)
|
||||
- Added support for custom icon colors on section items, thanks to [@Ruzenie](https://github.com/Ruzenie). [#2928](https://github.com/amruthpillai/reactive-resume/pull/2928)
|
||||
- Improved account-related email UX with better templates for password reset and email verification. [b87f2007](https://github.com/amruthpillai/reactive-resume/commit/b87f2007)
|
||||
- Consolidated separate settings pages into a single **Integrations** page for a more streamlined configuration flow. [5d8126d4](https://github.com/amruthpillai/reactive-resume/commit/5d8126d4)
|
||||
|
||||
## Fixes
|
||||
- Added a targeted Chinese localization fix for the `Skills` section title (`zh-CN`). [#2931](https://github.com/amruthpillai/reactive-resume/pull/2931)
|
||||
- Allowed MCP localhost loopback only for local MCP clients, improving local integration behavior while keeping external access restrictions intact. [623ca5c6](https://github.com/amruthpillai/reactive-resume/commit/623ca5c6)
|
||||
|
||||
## Maintenance & Localization
|
||||
- Updated dependencies and lockfile, including cleanup of unused exports (Knip). [e1bccbcc](https://github.com/amruthpillai/reactive-resume/commit/e1bccbcc)
|
||||
- Synced translation catalogs from Crowdin. [#2922](https://github.com/amruthpillai/reactive-resume/pull/2922), [#2925](https://github.com/amruthpillai/reactive-resume/pull/2925), [#2926](https://github.com/amruthpillai/reactive-resume/pull/2926), [#2932](https://github.com/amruthpillai/reactive-resume/pull/2932), [#2934](https://github.com/amruthpillai/reactive-resume/pull/2934), [#2935](https://github.com/amruthpillai/reactive-resume/pull/2935)
|
||||
- Updated docs to list **Meowth** alongside existing templates. [#2929](https://github.com/amruthpillai/reactive-resume/pull/2929)
|
||||
|
||||
**Full Changelog**: [v5.0.19...v5.0.20](https://github.com/amruthpillai/reactive-resume/compare/v5.0.19...v5.0.20)
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.19" description="25th April 2026">
|
||||
## Highlights
|
||||
- Added **OpenRouter** as an AI provider, thanks to [@iagodemacedo](https://github.com/iagodemacedo). [#2906](https://github.com/amruthpillai/reactive-resume/pull/2906)
|
||||
- Added Chinese font options, thanks to [@Platinum1154](https://github.com/Platinum1154). [#2905](https://github.com/amruthpillai/reactive-resume/pull/2905)
|
||||
- Added text color support to the rich text editor, including DOCX export handling for colored text, thanks to [@Platinum1154](https://github.com/Platinum1154). [#2903](https://github.com/amruthpillai/reactive-resume/pull/2903)
|
||||
- Fixed MCP tool names for Claude Desktop compatibility. [#2885](https://github.com/amruthpillai/reactive-resume/pull/2885)
|
||||
- Improved Docker-based local development support and refreshed self-hosting setup guidance. [849aad64](https://github.com/amruthpillai/reactive-resume/commit/849aad64), [c0387298](https://github.com/amruthpillai/reactive-resume/commit/c0387298)
|
||||
|
||||
## Fixes & Security
|
||||
- Thank you to [@shaxbozaka](https://github.com/shaxbozaka) for running a security audit on Reactive Resume and helping make the app more secure.
|
||||
- Hardened auth, OAuth, AI URL validation, printer/export endpoints, resume access controls, Browserless configuration, and public route exposure. [a42dbcd4](https://github.com/amruthpillai/reactive-resume/commit/a42dbcd4)
|
||||
- Removed the public RPC surface for the printer-only resume fetch endpoint by moving it behind a server function, further shrinking the attack surface for `getByIdForPrinter`, thanks to [@shaxbozaka](https://github.com/shaxbozaka).
|
||||
- Corrected Computer Modern Sans italic font file mappings, thanks to [@russellbrenner](https://github.com/russellbrenner). [#2881](https://github.com/amruthpillai/reactive-resume/pull/2881)
|
||||
- Improved email handling and user lookup in OAuth configuration, thanks to [@VedantBhawsar](https://github.com/VedantBhawsar). [#2874](https://github.com/amruthpillai/reactive-resume/pull/2874)
|
||||
- Aligned role period text in experience items, thanks to [@JamesGoslings](https://github.com/JamesGoslings). [#2908](https://github.com/amruthpillai/reactive-resume/pull/2908)
|
||||
- Reduced resume preview wheel zoom sensitivity, thanks to [@GoldenSection0618](https://github.com/GoldenSection0618). [#2911](https://github.com/amruthpillai/reactive-resume/pull/2911)
|
||||
- Prevented orphaned section headings at page breaks, thanks to [@5queezer](https://github.com/5queezer). [#2851](https://github.com/amruthpillai/reactive-resume/pull/2851)
|
||||
- Added top margin handling for subsequent PDF pages, based on feedback from [@trigger-xyz](https://github.com/trigger-xyz). [bbc38d2f](https://github.com/amruthpillai/reactive-resume/commit/bbc38d2f)
|
||||
|
||||
## Maintenance & Localization
|
||||
- Updated dependencies and lockfile, including security-focused package pins and Vite+ version pins. [c6e8df0a](https://github.com/amruthpillai/reactive-resume/commit/c6e8df0a), [847d69b6](https://github.com/amruthpillai/reactive-resume/commit/847d69b6), [9b3916d4](https://github.com/amruthpillai/reactive-resume/commit/9b3916d4)
|
||||
- Added `SECURITY.md`. [9e1e593d](https://github.com/amruthpillai/reactive-resume/commit/9e1e593d)
|
||||
- Added focused tests for auth, printer/export, URL validation, form edge cases, rich text DOCX output, font handling, and CSS sanitization hardening. [d0af9f4b](https://github.com/amruthpillai/reactive-resume/commit/d0af9f4b)
|
||||
- Synced translation catalogs from Crowdin and moved syncing to GitHub Actions. [#2886](https://github.com/amruthpillai/reactive-resume/pull/2886), [#2918](https://github.com/amruthpillai/reactive-resume/pull/2918)
|
||||
|
||||
## New Contributors
|
||||
- [@russellbrenner](https://github.com/russellbrenner), [@VedantBhawsar](https://github.com/VedantBhawsar), [@JamesGoslings](https://github.com/JamesGoslings), [@GoldenSection0618](https://github.com/GoldenSection0618), [@iagodemacedo](https://github.com/iagodemacedo), and [@Platinum1154](https://github.com/Platinum1154)
|
||||
|
||||
**Full Changelog**: [v5.0.17...v5.0.19](https://github.com/amruthpillai/reactive-resume/compare/v5.0.17...v5.0.19)
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.18" description="14th April 2026">
|
||||
## Features & Improvements
|
||||
- Re-enabled **Passkeys** across authentication flows, including passkey sign-in on the login page and passkey management in account settings.
|
||||
|
||||
## Maintenance
|
||||
- Updated dependencies and lockfile.
|
||||
- Synced translation catalogs from Crowdin.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.17" description="9th April 2026">
|
||||
## Features & Improvements
|
||||
- Added **Resume Analysis** in the resume builder right sidebar: run an on-demand AI review for an overall score (0 to 100), a dimension scorecard, strengths, and prioritized suggestions. Results are saved per resume and reload after refresh.
|
||||
- When AI features are disabled, the analysis section shows a short notice with a link to **AI Settings** so you can enable your provider and model first.
|
||||
- Improved the **MCP server** experience: updated tools, resources, and prompts; added an MCP server card at `/.well-known/mcp/server-card.json` for compatible clients; refreshed the **Using the MCP server** guide.
|
||||
|
||||
## Fixes
|
||||
- Fixed [#2865](https://github.com/amruthpillai/reactive-resume/issues/2865), incorrect spacing between resume section items in shared layouts.
|
||||
- Fixed [#2868](https://github.com/amruthpillai/reactive-resume/issues/2868) affecting resume item presentation and linked titles in shared item components.
|
||||
|
||||
## Maintenance & CI
|
||||
- Downgraded the Cosign installer action to v3 in the Docker build workflow for more reliable image signing.
|
||||
- Synced translation catalogs from Crowdin. [#2864](https://github.com/amruthpillai/reactive-resume/pull/2864)
|
||||
- Dependency and lockfile updates (including changes bundled with MCP and analysis work).
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.16" description="4th April 2026">
|
||||
## Features & Improvements
|
||||
- Updated shared resume item layouts (Experience, Education, Awards, Certifications, Publications, References, Volunteer) for better alignment and spacing consistency.
|
||||
- Generated printer pages now set a meaningful document title from the resume owner's name (`<Name> - Resume`) for cleaner PDF metadata and tab titles. [#2863](https://github.com/amruthpillai/reactive-resume/pull/2863)
|
||||
|
||||
## Fixes
|
||||
- Fixed [#2733](https://github.com/amruthpillai/reactive-resume/issues/2733), where date ranges could appear on a separate line unexpectedly in resume items. [#2862](https://github.com/amruthpillai/reactive-resume/pull/2862)
|
||||
- Adjusted Experience section form field ordering/labels so `Location`, `Position`, and `Period` are mapped and displayed correctly.
|
||||
|
||||
## Maintenance & Localization
|
||||
- Synced translation catalogs across all supported locales.
|
||||
- Updated dependencies and lockfile, including AI SDK, TipTap, TanStack Query, and Vite+ package aliases.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.15" description="1st April 2026">
|
||||
## Features & Improvements
|
||||
- Added **LinkedIn sign-in** support for self-hosted instances, including sign-in on the auth page and account linking in settings.
|
||||
- Improved the sign-in experience by showing loading placeholders while social login providers are being fetched.
|
||||
- Resume builder panel sizes are now persisted more reliably, making the layout feel more consistent between sessions.
|
||||
- Added clearer labels for resume sorting and filtering controls on the dashboard.
|
||||
|
||||
## Fixes
|
||||
- Improved autosave reliability in the resume builder:
|
||||
- Unsaved edits are flushed before page unload.
|
||||
- You now get a clear persistent warning if changes fail to save (for example, due to network issues).
|
||||
- Strengthened authentication defaults by increasing the minimum password length requirement to 8 characters.
|
||||
- Improved reliability around resume deletion and server-side error handling for create/update operations.
|
||||
- Fixed a rare language mix-up during printing/export when multiple print jobs run at the same time, so each PDF/screenshot now reliably uses the correct locale.
|
||||
- Improved print stability under heavy usage by reusing in-progress PDF and screenshot generation requests for the same resume instead of starting duplicate jobs.
|
||||
- Hardened printer token signing and token/session/API-key verification paths for better security.
|
||||
- Improved resilience in AI tailoring output parsing and JSON Resume import normalization.
|
||||
- Printer service now provides descriptive error messages ("Failed to generate PDF" / "Failed to capture screenshot") instead of generic internal server errors.
|
||||
- Job descriptions in the job detail sheet now render HTML formatting (headings, lists, bold text) instead of showing raw tags.
|
||||
- Fixed the "Tailor Resume" button being incorrectly disabled when AI is not configured — the dialog already supports plain resume duplication as a fallback.
|
||||
- Fixed the tailor resume flow to open the resume builder in a new tab while keeping the job listing open for reference, instead of navigating away and auto-opening the application page.
|
||||
- Apply option links in the job detail sheet are now validated before rendering, preventing malformed or potentially unsafe URLs.
|
||||
- Improved job card loading skeletons to match the actual card layout (logo, title, badges) for a smoother loading experience.
|
||||
- Centered the "Configure Job Search" empty state on the page.
|
||||
- Extracted the `JobCard` component into its own file for better code organization.
|
||||
|
||||
## Docs & Localization
|
||||
- Updated self-hosting and environment variable documentation to include LinkedIn OAuth configuration.
|
||||
- Synced translations across locales for the latest authentication and save-status messaging.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.14" description="24th March 2026">
|
||||
## Features
|
||||
- Implemented OAuth 2.1 support for MCP authentication, thanks to @5queezer. [#2829](https://github.com/amruthpillai/reactive-resume/pull/2829)
|
||||
- Migrated from using Biome to Oxlint/Oxfmt for linting and formatting, with a new autofix CI workflow. [#2822](https://github.com/amruthpillai/reactive-resume/pull/2822)
|
||||
|
||||
## Fixes
|
||||
- Fixed an issue with the slider component for "Sidebar Width" in the resume builder. [#2830](https://github.com/amruthpillai/reactive-resume/issues/2830)
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.13" description="19th March 2026">
|
||||
- **Features & Improvements**
|
||||
- Added **Job Listings with AI-Powered Resume Tailoring**, a major new feature that integrates job search (via JSearch API) directly into the dashboard with AI-driven resume tailoring to match job descriptions. [#2788](https://github.com/amruthpillai/reactive-resume/pull/2788)
|
||||
- Search for jobs with filters for location (city, state, country), employment type, remote status, and more.
|
||||
- View detailed job listings in a slide-out panel with apply links and tags.
|
||||
- Tailor your resume to a specific job posting using AI — rewrites skills, experience, and references for optimal fit.
|
||||
- Per-user API quota management with usage tracking in settings.
|
||||
- Pagination with 30 results per page.
|
||||
- Added **DOCX resume export** — generate and download tailored resumes as Word documents. [#2788](https://github.com/amruthpillai/reactive-resume/pull/2788)
|
||||
- Migrated the entire project toolchain to **Vite+** (`vp` CLI), unifying Vite, Vitest, Oxlint, and Oxfmt under a single developer experience.
|
||||
- Migrated from **Biome to Oxlint/Oxfmt** for linting and formatting, with a new autofix CI workflow. [#2822](https://github.com/amruthpillai/reactive-resume/pull/2822)
|
||||
- Added **Slovenian** locale support.
|
||||
- Added a new `Progress` UI component.
|
||||
## Features & Improvements
|
||||
- Added **Job Listings with AI-Powered Resume Tailoring**, a major new feature that integrates job search (via JSearch API) directly into the dashboard with AI-driven resume tailoring to match job descriptions. [#2788](https://github.com/amruthpillai/reactive-resume/pull/2788)
|
||||
- Search for jobs with filters for location (city, state, country), employment type, remote status, and more.
|
||||
- View detailed job listings in a slide-out panel with apply links and tags.
|
||||
- Tailor your resume to a specific job posting using AI — rewrites skills, experience, and references for optimal fit.
|
||||
- Per-user API quota management with usage tracking in settings.
|
||||
- Pagination with 30 results per page.
|
||||
- Added **DOCX resume export** — generate and download tailored resumes as Word documents. [#2788](https://github.com/amruthpillai/reactive-resume/pull/2788)
|
||||
- Migrated the entire project toolchain to **Vite+** (`vp` CLI), unifying Vite, Vitest, Oxlint, and Oxfmt under a single developer experience.
|
||||
- Migrated from **Biome to Oxlint/Oxfmt** for linting and formatting, with a new autofix CI workflow. [#2822](https://github.com/amruthpillai/reactive-resume/pull/2822)
|
||||
- Added **Slovenian** locale support.
|
||||
- Added a new `Progress` UI component.
|
||||
|
||||
- **Fixes**
|
||||
- Fixed type issues in auth client and configuration.
|
||||
- Used RTL-friendly CSS classes across the app for improved bidirectional layout support.
|
||||
- Removed dead code and unused imports.
|
||||
- Updated route tree generation and added `routeTree.gen.ts` to `.gitignore`.
|
||||
- Improved animation performance with `willChange` styles on animation components.
|
||||
## Fixes
|
||||
- Fixed type issues in auth client and configuration.
|
||||
- Used RTL-friendly CSS classes across the app for improved bidirectional layout support.
|
||||
- Removed dead code and unused imports.
|
||||
- Updated route tree generation and added `routeTree.gen.ts` to `.gitignore`.
|
||||
- Improved animation performance with `willChange` styles on animation components.
|
||||
|
||||
- **Maintenance & Dependencies**
|
||||
- Updated dependencies across the board, including `@typescript/native-preview`, `vite-plus`, and others.
|
||||
- Added database migrations for missing indexes.
|
||||
- Synced translations from Crowdin. [#2820](https://github.com/amruthpillai/reactive-resume/pull/2820), [#2824](https://github.com/amruthpillai/reactive-resume/pull/2824)
|
||||
- Updated French translation for 'Volunteer' to 'Bénévolat'. [#2823](https://github.com/amruthpillai/reactive-resume/pull/2823)
|
||||
- Multiple translation and language updates across all locales.
|
||||
## Maintenance & Dependencies
|
||||
- Updated dependencies across the board, including `@typescript/native-preview`, `vite-plus`, and others.
|
||||
- Added database migrations for missing indexes.
|
||||
- Synced translations from Crowdin. [#2820](https://github.com/amruthpillai/reactive-resume/pull/2820), [#2824](https://github.com/amruthpillai/reactive-resume/pull/2824)
|
||||
- Updated French translation for 'Volunteer' to 'Bénévolat'. [#2823](https://github.com/amruthpillai/reactive-resume/pull/2823)
|
||||
- Multiple translation and language updates across all locales.
|
||||
|
||||
- **New Contributors** - @lukadfagundes - @Hephaisto-dev
|
||||
</Update>
|
||||
## New Contributors
|
||||
- @lukadfagundes
|
||||
- @Hephaisto-dev
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.12" description="17th March 2026">
|
||||
- **Features & Improvements**
|
||||
- Refactored the component system to `@base-ui/react` across the app for standardized UI behavior (dialogs, menus, accordions, comboboxes, switches, etc.).
|
||||
- Improved auth infrastructure with Better Auth dashboard/sentinel integration and expanded IP/header handling for proxied deployments.
|
||||
- Added a dedicated `src/server.ts` server entry using `FastResponse` for better runtime compatibility.
|
||||
- Sidebar button list now scrolls internally on overflow.
|
||||
## Features & Improvements
|
||||
- Refactored the component system to `@base-ui/react` across the app for standardized UI behavior (dialogs, menus, accordions, comboboxes, switches, etc.).
|
||||
- Improved auth infrastructure with Better Auth dashboard/sentinel integration and expanded IP/header handling for proxied deployments.
|
||||
- Added a dedicated `src/server.ts` server entry using `FastResponse` for better runtime compatibility.
|
||||
- Sidebar button list now scrolls internally on overflow.
|
||||
|
||||
- **Fixes**
|
||||
- Printer endpoint now resolves to IP when using chromedp, improving compatibility with proxied deployments.
|
||||
- Switched OpenAI integration to `chat/completions` for resume import and AI features.
|
||||
- AI PDF/DOCX resume parsing is now more robust via direct file send (multimodal input).
|
||||
- Resume import normalization hardened: section defaults merged, required fields enforced, missing IDs/flags autogenerated.
|
||||
- Fixed UI migration regressions (accordion behavior, button interaction, right sidebar scrollbar).
|
||||
- Fixed multi-page PDF crashes and Gemini API ingestion errors.
|
||||
- Fixed role title styling, role spacing, and issues with skills item rendering and template headings.
|
||||
## Fixes
|
||||
- Printer endpoint now resolves to IP when using chromedp, improving compatibility with proxied deployments.
|
||||
- Switched OpenAI integration to `chat/completions` for resume import and AI features.
|
||||
- AI PDF/DOCX resume parsing is now more robust via direct file send (multimodal input).
|
||||
- Resume import normalization hardened: section defaults merged, required fields enforced, missing IDs/flags autogenerated.
|
||||
- Fixed UI migration regressions (accordion behavior, button interaction, right sidebar scrollbar).
|
||||
- Fixed multi-page PDF crashes and Gemini API ingestion errors.
|
||||
- Fixed role title styling, role spacing, and issues with skills item rendering and template headings.
|
||||
|
||||
- **Maintenance & Dependencies**
|
||||
- Updated dependencies and lockfile.
|
||||
- Synced translation strings across locales, including multiple contributions from Crowdin.
|
||||
- General dependency and code style improvements, plus removal of redundant indexes and plugins.
|
||||
## Maintenance & Dependencies
|
||||
- Updated dependencies and lockfile.
|
||||
- Synced translation strings across locales, including multiple contributions from Crowdin.
|
||||
- General dependency and code style improvements, plus removal of redundant indexes and plugins.
|
||||
|
||||
- **Community & Contributors** - Users can now view other public resumes while logged in. - New Contributors: @yang-summer, @FrancocDev, @obitton, @jondycz. - Full changelog: https://docs.rxresu.me/changelog
|
||||
</Update>
|
||||
## New Contributors
|
||||
- @yang-summer
|
||||
- @FrancocDev
|
||||
- @obitton
|
||||
- @jondycz
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.10" description="24th February 2026">
|
||||
- **Fixes**
|
||||
- Show section titles for summary-type custom sections in the resume builder. [#2744](https://github.com/amruthpillai/reactive-resume/pull/2744)
|
||||
- Prevent browser password managers and Edge autofill/save prompts from appearing on AI settings API key fields. [#2719](https://github.com/amruthpillai/reactive-resume/pull/2719)
|
||||
- Replace deprecated Tailwind CSS classes: use `inset-s-*`/`inset-e-*` instead of `start-*`/`end-*`.
|
||||
- Fix PDF downloader to work correctly in offline mode. [#2743](https://github.com/amruthpillai/reactive-resume/pull/2743)
|
||||
- Make bold formatting visible for `<strong>` in the resume rich text editor (uses plain `font-weight: bold` fallback if the CSS variable is unset). Fixes [#2730](https://github.com/amruthpillai/reactive-resume/issues/2730)
|
||||
- Prevent credentials sign-in from dropping `session_token` Set-Cookie, improves login reliability. [#2718](https://github.com/amruthpillai/reactive-resume/pull/2718)
|
||||
- Remove redundant `resume-` prefix from download filename; add spacing between pages in shared view. [#2709](https://github.com/amruthpillai/reactive-resume/pull/2709)
|
||||
- Normalize autocomplete tokens for login and register forms. [#2714](https://github.com/amruthpillai/reactive-resume/pull/2714)
|
||||
- Fix improper chips reordering and update dependency/translations. [#2711](https://github.com/amruthpillai/reactive-resume/issues/2711)
|
||||
- Fix issue with clipping of heading in Lapras resume template.
|
||||
- Remove error-causing plugins in oRPC integration.
|
||||
- Remove duplicate database indexes; add index for `created_at` on user and resume tables.
|
||||
## Fixes
|
||||
- Show section titles for summary-type custom sections in the resume builder. [#2744](https://github.com/amruthpillai/reactive-resume/pull/2744)
|
||||
- Prevent browser password managers and Edge autofill/save prompts from appearing on AI settings API key fields. [#2719](https://github.com/amruthpillai/reactive-resume/pull/2719)
|
||||
- Replace deprecated Tailwind CSS classes: use `inset-s-*`/`inset-e-*` instead of `start-*`/`end-*`.
|
||||
- Fix PDF downloader to work correctly in offline mode. [#2743](https://github.com/amruthpillai/reactive-resume/pull/2743)
|
||||
- Make bold formatting visible for `<strong>` in the resume rich text editor (uses plain `font-weight: bold` fallback if the CSS variable is unset). Fixes [#2730](https://github.com/amruthpillai/reactive-resume/issues/2730)
|
||||
- Prevent credentials sign-in from dropping `session_token` Set-Cookie, improves login reliability. [#2718](https://github.com/amruthpillai/reactive-resume/pull/2718)
|
||||
- Remove redundant `resume-` prefix from download filename; add spacing between pages in shared view. [#2709](https://github.com/amruthpillai/reactive-resume/pull/2709)
|
||||
- Normalize autocomplete tokens for login and register forms. [#2714](https://github.com/amruthpillai/reactive-resume/pull/2714)
|
||||
- Fix improper chips reordering and update dependency/translations. [#2711](https://github.com/amruthpillai/reactive-resume/issues/2711)
|
||||
- Fix clipping of heading in Lapras resume template.
|
||||
- Remove error-causing plugins in oRPC integration.
|
||||
- Remove duplicate database indexes; add index for `created_at` on user and resume tables.
|
||||
|
||||
- **Features & Improvements**
|
||||
- Add new feature flag: `FLAG_DISABLE_IMAGE_PROCESSING` (allows disabling of image processing site-wide).
|
||||
- Update Discord invite link in the app and documentation.
|
||||
- Add comprehensive codebase and architecture documentation in `CLAUDE.md`.
|
||||
- Sync latest translations from Crowdin (updated and added multiple languages).
|
||||
## Features & Improvements
|
||||
- Added feature flag: `FLAG_DISABLE_IMAGE_PROCESSING` (allows disabling image processing site-wide).
|
||||
- Updated Discord invite link in the app and documentation.
|
||||
- Added comprehensive codebase and architecture documentation in `CLAUDE.md`.
|
||||
- Synced latest translations from Crowdin (multiple language updates and additions).
|
||||
|
||||
- **Other** - Refactor: rename auth utility from `originWith` to `withHostname`, preserve localhost/127.0.0.1 sibling trust. - General dependency updates and code style improvements. - Documentation and README updates.
|
||||
</Update>
|
||||
## Maintenance & Other
|
||||
- Refactored auth utility from `originWith` to `withHostname` to preserve localhost/127.0.0.1 sibling trust.
|
||||
- Updated dependencies and improved code style.
|
||||
- Updated documentation and README files.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.9" description="9th February 2026">
|
||||
- Add Computer Modern web fonts to the font selector, allowing the user to choose from a variety of "Computer Modern"
|
||||
(LaTeX) fonts. - This lets you create a resume that looks just like a LaTeX document, with the same fonts and styles.
|
||||
- Update dependencies to the latest versions.
|
||||
## Features & Improvements
|
||||
- Added Computer Modern web fonts to the font selector, allowing users to choose from LaTeX-style "Computer Modern" fonts for a classic scientific look.
|
||||
- Updated dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.8" description="9th February 2026">
|
||||
- Remove Passkey support from the authentication system, as it was causing issues with the authentication provider. -
|
||||
Update dependencies to the latest versions.
|
||||
## Fixes
|
||||
- Removed Passkey support from the authentication system due to provider compatibility issues.
|
||||
- Updated dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.7" description="9th February 2026">
|
||||
- Introduce a new **MCP (Model Context Protocol) server** that lets you manage and edit resumes from any
|
||||
MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. Supports listing, reading, creating, deleting,
|
||||
locking/unlocking, and patching resumes via natural language. [(guide)](/guides/using-the-mcp-server) - Add an **AI
|
||||
Chat** panel to the resume builder, allowing you to modify your resume through conversational AI directly within the
|
||||
editor. The chat uses tool-calling to apply JSON Patch operations to your resume in real-time, with visual feedback
|
||||
for each change. - Add a system prompt and `patch_resume` tool for the AI chat, enabling structured, minimal-diff
|
||||
resume edits following RFC 6902 JSON Patch operations. - Chat history is now persisted per resume in localStorage, so
|
||||
conversations are preserved across sessions. - Fix rendering issues in the Lapras and Onyx resume templates. -
|
||||
Improvements to the Combobox and ScrollArea UI components. - Fix an issue with skills item rendering in the shared
|
||||
resume components. - Update authentication configuration and auth route handling. - Update the JSON Schema to reflect
|
||||
the latest resume data model. - Update dependencies to the latest versions.
|
||||
## Features & Improvements
|
||||
- Introduced a new **MCP (Model Context Protocol) server** for managing and editing resumes from MCP-compatible AI tools (Claude Desktop, Cursor, Codex, and others). Supports listing, reading, creating, deleting, locking/unlocking, and patching resumes via natural language. [(guide)](/guides/using-the-mcp-server)
|
||||
- Added an **AI Chat** panel to the resume builder, allowing conversational resume modifications through tool-calling (JSON Patch operations applied to the resume in real time with visual feedback).
|
||||
- Added a system prompt and `patch_resume` tool for structured, minimal-diff resume edits via RFC 6902 JSON Patch.
|
||||
- Chat history is now persisted per resume in localStorage.
|
||||
|
||||
## Fixes
|
||||
- Fixed rendering issues in the Lapras and Onyx resume templates.
|
||||
- Improved Combobox and ScrollArea UI components.
|
||||
- Fixed skills item rendering in shared resume components.
|
||||
- Updated authentication configuration and route handling.
|
||||
- Updated JSON Schema to match the latest resume data model.
|
||||
|
||||
## Maintenance
|
||||
- Updated dependencies to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.6" description="8th February 2026">
|
||||
- Implement Atomic Resume Patching API for fine-grained resume updates, allowing partial, atomic updates to resumes
|
||||
via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692) - The API endpoint `PUT
|
||||
/resume/{id}` now returns the updated resume object instead of void. Also, resume routing/services now use explicit
|
||||
DTOs for input and output schemas. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688) - Add error
|
||||
logging for API server errors (server-side only) to improve debugging and reliability. - Refactor and clean up
|
||||
imports/exports for clarity and maintainability. - Added `.devcontainer` configuration for improved contributor
|
||||
development environment. - Update dependencies to the latest versions. - Add build status badge and documentation link
|
||||
to README. - Sync latest translations from Crowdin (notably: French, other languages). - Other minor fixes and
|
||||
improvements.
|
||||
## Features & Improvements
|
||||
- Implemented Atomic Resume Patching API for fine-grained, partial updates to resumes via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692)
|
||||
- API endpoint `PUT /resume/{id}` now returns the updated resume object rather than void, with explicit DTOs for input/output. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688)
|
||||
- Added error logging for API server errors (server-side only) to improve debugging and reliability.
|
||||
- Refactored and cleaned up imports/exports for clarity and maintainability.
|
||||
- Added `.devcontainer` configuration for improved contributor development environment.
|
||||
- Added build status badge and documentation link to README.
|
||||
|
||||
## Fixes
|
||||
- General bug fixes and minor improvements.
|
||||
- Synced latest translations from Crowdin (notably: French, other languages).
|
||||
- Updated dependencies to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.5" description="31st January 2026">
|
||||
- Implement Cover Letter functionality in the resume builder, allowing the user to create cover letters as custom
|
||||
sections. [(link)](/guides/adding-a-cover-letter) - Implement full-screen mode for the rich text editor in the resume
|
||||
builder, allowing the user to write in a more focused environment. - Implement a new custom section type: `summary`,
|
||||
which allows the user to add another "summary" like section to the resume. - Implement a `useFormBlocker` hook to
|
||||
prevent the user from closing a dialog while the form has unsaved changes. - Fix an issue where keywords spacing was
|
||||
not consistent in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631) - Fix an
|
||||
issue where the AI connection test was not working correctly, also return appropriate error messages for AI provider
|
||||
issues.
|
||||
## Features & Improvements
|
||||
- Implemented Cover Letter functionality as a custom section in the resume builder. [(link)](/guides/adding-a-cover-letter)
|
||||
- Added full-screen mode to the rich text editor for distraction-free writing.
|
||||
- Introduced a new custom section type: `summary`, enabling users to add extra summary sections.
|
||||
- Added `useFormBlocker` hook to prevent dialogs from being closed with unsaved form changes.
|
||||
|
||||
## Fixes
|
||||
- Fixed keyword spacing issue in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631)
|
||||
- Fixed AI connection test and improved error reporting for AI provider issues.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.4" description="28th January 2026">
|
||||
- Bring back Undo/Redo functionality in the resume builder for improved editing experience. - Arrange the sidebar
|
||||
builder dynamically based on the section type in each template.
|
||||
[#2564](https://github.com/amruthpillai/reactive-resume/pull/2603) - Remove extra spacing when proficiency is empty.
|
||||
[#2607](https://github.com/amruthpillai/reactive-resume/pull/2626) - Fix rendering in Pikachu template: conditionally
|
||||
render header and page picture using `isFirstPage`, and respect the `fullWidth` property for page layout. - Fixes to
|
||||
templates to improve layout and rendering consistency. - Fix GitHub OAuth login for users migrated from previous
|
||||
versions. - Improve communication with the printer service and reduce resource usage for better PDF generation
|
||||
reliability. - Update and sync translations from Crowdin (Afrikaans, Persian, Portuguese/Brazilian, and other
|
||||
languages). - Update translation sources and configuration. - Fix "empty" Git merge remnants in codebase. - Update
|
||||
package dependencies and fix self-hosting guide links in README. - Remove dead code, update screenshots, and add PWA
|
||||
(Progressive Web App) support. - Update links to PDF example files in documentation. - Other bug fixes and minor
|
||||
improvements (#2542, #2573, #2598).
|
||||
## Features & Improvements
|
||||
- Brought back Undo/Redo functionality in the resume builder for an improved editing experience.
|
||||
- Sidebar builder now arranges dynamically based on the section type in each template. [#2564](https://github.com/amruthpillai/reactive-resume/pull/2603)
|
||||
- Added PWA (Progressive Web App) support and updated PDF example file links in documentation.
|
||||
|
||||
## Fixes
|
||||
- Removed extra spacing when proficiency is empty. [#2607](https://github.com/amruthpillai/reactive-resume/pull/2626)
|
||||
- Fixed Pikachu template: header/page picture rendering, and respect for `fullWidth` property.
|
||||
- General template improvements for layout and rendering consistency.
|
||||
- Fixed GitHub OAuth login for users migrated from previous versions.
|
||||
- Improved printer service communication and reduced resource usage for more reliable PDF generation.
|
||||
- Fixed "empty" Git merge remnants in codebase.
|
||||
|
||||
## Maintenance
|
||||
- Synced translations from Crowdin (Afrikaans, Persian, Portuguese/Brazilian, others).
|
||||
- Updated translation sources and configuration.
|
||||
- Updated package dependencies and fixed self-hosting guide links in README.
|
||||
- Removed dead code and updated screenshots.
|
||||
- Other bug fixes and minor improvements (#2542, #2573, #2598).
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.3" description="25th January 2026">
|
||||
- Implement the ability to print Free-Form PDFs which do not have a fixed page height, allowing the user to fit the
|
||||
content on a page as they see fit. [(link)](/guides/selecting-page-format) - Allow the user to override the default
|
||||
endpoint for all AI providers, not just Ollama. Also display the default endpoint for each provider in the AI settings
|
||||
page. - Updated the chip input component, to allow the user to add, edit, remove or reorder keywords for skills and
|
||||
other sections. - Improved RTL support across the app, thanks to @obreo for the contribution.
|
||||
[(link)](https://github.com/amruthpillai/reactive-resume/pull/2583) - Updates to the translation configuration to
|
||||
remove line numbers from the translation files, as this was causing unnecessary diffs. - Updated the video on the
|
||||
homepage to be lighter and faster to load, while still maintaining the same quality. - Increased the screenshot TTL
|
||||
for resumes, to avoid regenerating screenshots unnecessarily unless the resume has been updated recently (in the last
|
||||
hour). - Update dependencies and translations to the latest versions.
|
||||
## Features & Improvements
|
||||
- Added support for printing Free-Form PDFs (no fixed page height), allowing for custom-fit page content. [(link)](/guides/selecting-page-format)
|
||||
- Allow overriding the default endpoint for all AI providers, not just Ollama; default endpoint now visible in AI settings.
|
||||
- Updated the chip input component: supports adding, editing, removing, and reordering keywords for skills and other sections.
|
||||
- Improved RTL support across the app, thanks to @obreo. [(link)](https://github.com/amruthpillai/reactive-resume/pull/2583)
|
||||
|
||||
## Fixes
|
||||
- Updated translation configuration to remove line numbers, reducing unnecessary diffs.
|
||||
- Homepage video optimized for faster loading without quality loss.
|
||||
- Increased screenshot TTL for resumes to reduce unnecessary regenerations.
|
||||
- Updated dependencies and translations to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.2" description="24th January 2026">
|
||||
- Added an agent skill `skills/resume-builder` for agentic AI assistants to build resumes for Reactive Resume through
|
||||
conversational AI. - Added a new guide on how to fit content on a page, to avoid issues when exporting to PDF.
|
||||
[(link)](/guides/fitting-content-on-a-page) - Display an alert when the content is too tall for a page, to help the
|
||||
user fit the content on a page. - Fix an issue with the Ditgar template, where the page was not respecting the
|
||||
`fullWidth` setting. - Updated the JSON Schema to conform to a proper format. - Updated the Discord Server invite link
|
||||
to a new one. - Updated dependencies to the latest versions.
|
||||
## Features & Improvements
|
||||
- Added an agent skill `skills/resume-builder` for agentic AI assistants, enabling resume construction through conversational AI.
|
||||
- Added a guide for how to fit content on a page when exporting to PDF. [(link)](/guides/fitting-content-on-a-page)
|
||||
- Alert user when content is too tall for a page for easier export troubleshooting.
|
||||
|
||||
## Fixes
|
||||
- Ditgar template now respects `fullWidth` setting.
|
||||
- Updated JSON Schema for proper format.
|
||||
- Updated Discord server invite link.
|
||||
- Updated dependencies to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.1" description="23th January 2026">
|
||||
- Updated translations from Crowdin. - Added a Community Spotlight section to the documentation. - Remove `-r
|
||||
require-metadata` from the Dockerfile as it was not needed. - Fixed inconsistencies in the docker compose examples in
|
||||
the documentation. - Fixed an issue with usernames not allowing hyphens in them. - Fixed issues with the printer
|
||||
service, when using the `getResumeScreenshot` or `printResumeAsPDF` endpoints.
|
||||
## Maintenance & Fixes
|
||||
- Updated translations from Crowdin.
|
||||
- Added Community Spotlight section to the documentation.
|
||||
- Removed `-r require-metadata` from the Dockerfile (was not needed).
|
||||
- Fixed inconsistencies in Docker compose documentation examples.
|
||||
- Fixed username validation to allow hyphens.
|
||||
- Fixed issues with the printer service when using `getResumeScreenshot` or `printResumeAsPDF` endpoints.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.0" description="22th January 2026">
|
||||
This has been a major overhaul from the previous version of Reactive Resume. The app has been completely redesigned and rebuilt from scratch, to be more intuitive and user-friendly.
|
||||
This release is a major overhaul of Reactive Resume. The app has been completely redesigned and rebuilt from scratch for a more intuitive, user-friendly experience.
|
||||
|
||||
**Here are some of the key changes from the previous version:**
|
||||
|
||||
- 2 new templates: _Ditgar_ and _Lapras_
|
||||
- Authentication via Passkeys
|
||||
- New user interface and refreshed design
|
||||
- API Access and Reference Documentation
|
||||
**Highlights:**
|
||||
- 2 new resume templates: _Ditgar_ and _Lapras_
|
||||
- Passkeys for authentication
|
||||
- Entirely new user interface and refreshed design
|
||||
- API access and reference docs
|
||||
- AI Integration with OpenAI, Google, Anthropic, and Ollama
|
||||
- A better font selector, with real-time preview of the font
|
||||
- More comprehensive documentation, with guides for specific features
|
||||
- An improved templates gallery, with clear overview of each template
|
||||
- Ability to import resumes from PDF or DOCX (requires AI Integration)
|
||||
- Reliable server infrastructure, with improved performance and scalability
|
||||
- Ability to move items between sections and pages, for better organization
|
||||
- A more powerful CSS Editor, with better autocomplete and syntax highlighting
|
||||
- Extended Custom Sections to include a type, for better customization and organization
|
||||
- Ability to choose the kind of icons to display for level indicators (stars, circles, custom icons, etc.)
|
||||
- Ability to resize how wide/narrow the sidebar should be on the resume, or convert a page to be full width
|
||||
|
||||
There's still a lot more that I'm forgetting, but I'm sure you'll find out soon enough as you explore the new version. I hope you enjoy building your resume now, better than ever. If you have any feedback, please feel free to [contact me](https://amruthpillai.com/#contact) or [open an issue](https://github.com/amruthpillai/reactive-resume/issues) on GitHub.
|
||||
- Improved font selector with real-time preview
|
||||
- Expanded and improved documentation, with more how-to guides
|
||||
- Redesigned template gallery with detailed views
|
||||
- Resume import from PDF/DOCX (requires AI Integration)
|
||||
- Stronger infrastructure for better reliability and scalability
|
||||
- Drag-and-drop for moving items between sections and pages
|
||||
- Powerful new CSS Editor with autocompletion and highlighting
|
||||
- Custom sections now support typed extension for finer control
|
||||
- Icon style selector for indicators (stars, circles, custom, etc.)
|
||||
- Sidebar width adjustment and full-width page support
|
||||
|
||||
And much more! Explore the new version and share your feedback via [contact](https://amruthpillai.com/#contact) or [GitHub issues](https://github.com/amruthpillai/reactive-resume/issues).
|
||||
</Update>
|
||||
|
||||
@@ -126,7 +126,7 @@ Here are the most commonly used scripts during development:
|
||||
| `pnpm run build` | Build the application for production |
|
||||
| `pnpm run start` | Start the production server |
|
||||
| `pnpm run lint` | Run Oxlint linter and formatter |
|
||||
| `pnpm run fmt` | Run Oxfmt formatter |
|
||||
| `pnpm run format` | Run Oxfmt formatter |
|
||||
| `pnpm run typecheck` | Run TypeScript type checking |
|
||||
|
||||
### Database
|
||||
@@ -252,8 +252,8 @@ Uses [Oxlint](https://oxlint.dev/) for linting and [Oxfmt](https://oxfmt.dev/) f
|
||||
|
||||
```bash
|
||||
# Check and auto-fix issues
|
||||
pnpm run lint
|
||||
pnpm run fmt
|
||||
pnpm run lint:fix
|
||||
pnpm run format:fix
|
||||
```
|
||||
|
||||
### Type Checking
|
||||
|
||||
@@ -39,8 +39,8 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Multiple Templates" icon="grid-2">
|
||||
Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditto, Gengar,
|
||||
Glalie, Kakuna, Lapras, Leafish, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
|
||||
Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditgar, Ditto,
|
||||
Gengar, Glalie, Kakuna, Lapras, Leafish, Meowth, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rich Text Editor" icon="text">
|
||||
|
||||
@@ -67,28 +67,60 @@ Before you begin, ensure you have the following installed:
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone the Repository">
|
||||
```bash git clone https://github.com/amruthpillai/reactive-resume.git cd Reactive-Resume ```
|
||||
```bash
|
||||
git clone https://github.com/amruthpillai/reactive-resume.git
|
||||
cd Reactive-Resume
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Environment Variables">
|
||||
Create a `.env` file in the root directory with the following variables: ```bash # Application
|
||||
APP_URL=http://localhost:3000 # Database DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres #
|
||||
Authentication (generate a secure secret) AUTH_SECRET=your-secure-secret-key-here # Storage (S3-compatible)
|
||||
S3_ACCESS_KEY_ID=seaweedfs S3_SECRET_ACCESS_KEY=seaweedfs S3_ENDPOINT=http://seaweedfs:8333
|
||||
S3_BUCKET=reactive-resume S3_FORCE_PATH_STYLE=true ```
|
||||
Create a `.env` file in the root directory with the following variables:
|
||||
|
||||
```bash .env
|
||||
# Application
|
||||
APP_URL=http://localhost:3000
|
||||
|
||||
# Printer (Browserless)
|
||||
BROWSERLESS_TOKEN=change-me
|
||||
PRINTER_ENDPOINT=ws://browserless:3000?token=change-me
|
||||
|
||||
# Database
|
||||
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres
|
||||
|
||||
# Authentication (generate a secure secret)
|
||||
AUTH_SECRET=your-secure-secret-key-here
|
||||
|
||||
# Storage (S3-compatible via SeaweedFS)
|
||||
S3_ACCESS_KEY_ID=seaweedfs
|
||||
S3_SECRET_ACCESS_KEY=seaweedfs
|
||||
S3_ENDPOINT=http://seaweedfs:8333
|
||||
S3_BUCKET=reactive-resume
|
||||
S3_FORCE_PATH_STYLE=true
|
||||
```
|
||||
|
||||
<Warning>
|
||||
For production deployments, always use strong, unique values for `AUTH_SECRET` and database credentials.
|
||||
For production deployments, always use strong, unique values for `AUTH_SECRET`, `BROWSERLESS_TOKEN`, and database credentials.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Start the Services">
|
||||
```bash docker compose up -d ``` This will start: - **PostgreSQL** — Database for storing user data and resumes -
|
||||
**SeaweedFS** — S3-compatible storage for file uploads - **Printer** — PDF and screenshot generation service
|
||||
(browserless/chromium) - **Reactive Resume** — The main application
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This starts:
|
||||
- **PostgreSQL** — Database for storing user data and resumes
|
||||
- **SeaweedFS** — S3-compatible storage for file uploads
|
||||
- **Browserless** — Headless Chromium service for PDF/screenshot generation
|
||||
- **Reactive Resume** — The main application
|
||||
</Step>
|
||||
|
||||
<Step title="Access Your Instance">
|
||||
Once all services are running, access your Reactive Resume instance at: ``` http://localhost:3000 ```
|
||||
Once all services are running, access your Reactive Resume instance at:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
@@ -97,12 +129,12 @@ Before you begin, ensure you have the following installed:
|
||||
|
||||
Here's what each service in the stack does:
|
||||
|
||||
| Service | Port | Description |
|
||||
| ----------- | ---- | ----------------------------------------------------------- |
|
||||
| `postgres` | 5432 | PostgreSQL database for storing all application data |
|
||||
| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
|
||||
| `printer` | 4000 | Headless Chromium service for PDF and screenshot generation |
|
||||
| `app` | 3000 | The main Reactive Resume application |
|
||||
| Service | Port | Description |
|
||||
| ----------------- | ---------------------------------- | ----------------------------------------------------------- |
|
||||
| `postgres` | 5432 | PostgreSQL database for storing all application data |
|
||||
| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
|
||||
| `browserless` | 3000 (internal) / 4000 (published) | Headless Chromium service for PDF and screenshot generation |
|
||||
| `reactive_resume` | 3000 | The main Reactive Resume application |
|
||||
|
||||
### Health Checks
|
||||
|
||||
@@ -122,49 +154,58 @@ Here's a complete list of environment variables you can configure:
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description | Example |
|
||||
| ------------------ | ------------------------------ | --------------------------------------- |
|
||||
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
|
||||
| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -base64 32` |
|
||||
| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
|
||||
| `PRINTER_ENDPOINT` | URL of the printer service | `http://printer:3000` |
|
||||
| Variable | Description | Example |
|
||||
| ------------------ | ------------------------------ | ------------------------------------- |
|
||||
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
|
||||
| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -hex 32` |
|
||||
| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
|
||||
| `PRINTER_ENDPOINT` | URL of the printer service | `ws://browserless:3000?token=...` |
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------- | ----------------------------------------------------------- | ---------------------- |
|
||||
| `PRINTER_APP_URL` | Public URL for the printer to access the Application | — |
|
||||
| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — |
|
||||
| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — |
|
||||
| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — |
|
||||
| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — |
|
||||
| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — |
|
||||
| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — |
|
||||
| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — |
|
||||
| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — |
|
||||
| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
|
||||
| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
|
||||
| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
|
||||
| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
|
||||
| `SMTP_HOST` | SMTP Server Host (for email features) | — |
|
||||
| `SMTP_PORT` | SMTP Server Port | `587` |
|
||||
| `SMTP_USER` | SMTP Username | — |
|
||||
| `SMTP_PASS` | SMTP Password | — |
|
||||
| `SMTP_FROM` | Default FROM address for emails | — |
|
||||
| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` |
|
||||
| `S3_ACCESS_KEY_ID` | S3 Access Key | — |
|
||||
| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — |
|
||||
| `S3_REGION` | S3 Region | `us-east-1` |
|
||||
| `S3_ENDPOINT` | S3-compatible Endpoint URL | — |
|
||||
| `S3_BUCKET` | S3 Bucket Name | — |
|
||||
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
|
||||
| `FLAG_DEBUG_PRINTER` | Used for debugging the printer route | `false` |
|
||||
| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` |
|
||||
| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` |
|
||||
| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` |
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------- | ----------------------------------------------------------- | ---------------------- |
|
||||
| `PRINTER_APP_URL` | Public URL for the printer to access the Application | — |
|
||||
| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — |
|
||||
| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — |
|
||||
| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — |
|
||||
| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — |
|
||||
| `LINKEDIN_CLIENT_ID` | LinkedIn OAuth Client ID | — |
|
||||
| `LINKEDIN_CLIENT_SECRET` | LinkedIn OAuth Client Secret | — |
|
||||
| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — |
|
||||
| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — |
|
||||
| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — |
|
||||
| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — |
|
||||
| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
|
||||
| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
|
||||
| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
|
||||
| `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` | Trusted HTTPS hosts/origins for dynamic OAuth redirects | — |
|
||||
| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
|
||||
| `BETTER_AUTH_API_KEY` | Better Auth dashboard API key | — |
|
||||
| `BETTER_AUTH_URL` | Better Auth base URL override (advanced) | `APP_URL` |
|
||||
| `BETTER_AUTH_SECRET` | Better Auth secret override (advanced) | `AUTH_SECRET` |
|
||||
| `AI_ALLOWED_BASE_URLS` | Allowlist for custom AI provider base URLs | — |
|
||||
| `SMTP_HOST` | SMTP Server Host (for email features) | — |
|
||||
| `SMTP_PORT` | SMTP Server Port | `587` |
|
||||
| `SMTP_USER` | SMTP Username | — |
|
||||
| `SMTP_PASS` | SMTP Password | — |
|
||||
| `SMTP_FROM` | Default FROM address for emails | — |
|
||||
| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` |
|
||||
| `S3_ACCESS_KEY_ID` | S3 Access Key | — |
|
||||
| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — |
|
||||
| `S3_REGION` | S3 Region | `us-east-1` |
|
||||
| `S3_ENDPOINT` | S3-compatible Endpoint URL | — |
|
||||
| `S3_BUCKET` | S3 Bucket Name | — |
|
||||
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
|
||||
| `FLAG_DEBUG_PRINTER` | Used for debugging the printer route | `false` |
|
||||
| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` |
|
||||
| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` |
|
||||
| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` |
|
||||
|
||||
> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused.
|
||||
|
||||
> **Health check behavior:** `/api/health` reports status for database, printer, and storage. A failure in any dependency returns HTTP `503`.
|
||||
|
||||
<Note>
|
||||
**Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while
|
||||
the printer service is running inside Docker. In this scenario, the printer needs to reach your local app to render
|
||||
|
||||
@@ -132,6 +132,10 @@ Reactive Resume includes a variety of professionally designed templates. Each te
|
||||
<img src="/images/templates/leafish.webp" alt="Leafish template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Meowth">
|
||||
<img src="/images/templates/meowth.webp" alt="Meowth template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Onyx">
|
||||
<img src="/images/templates/onyx.webp" alt="Onyx template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
@@ -127,7 +127,7 @@ The exported JSON follows the Reactive Resume schema. Here's a high-level overvi
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"picture": {
|
||||
@@ -937,6 +937,7 @@ The exported JSON follows the Reactive Resume schema. Here's a high-level overvi
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
|
||||
@@ -24,7 +24,7 @@ The Reactive Resume JSON Schema is publicly available at:
|
||||
|
||||
<Info>[https://rxresu.me/schema.json](https://rxresu.me/schema.json)</Info>
|
||||
|
||||
This endpoint returns a complete JSON Schema (draft 2020-12 compatible) that describes the structure of a Reactive Resume document. It includes:
|
||||
This endpoint returns a complete JSON Schema (draft 07 compatible) that describes the structure of a Reactive Resume document. It includes:
|
||||
|
||||
- **All resume sections**: basics, summary, experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer work, and references
|
||||
- **Custom sections**: user-defined sections with arbitrary content
|
||||
@@ -149,7 +149,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)."
|
||||
"description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload."
|
||||
},
|
||||
"size": {
|
||||
"type": "number",
|
||||
@@ -221,17 +221,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"description": "The headline of the author of the resume."
|
||||
},
|
||||
"email": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"const": ""
|
||||
}
|
||||
],
|
||||
"type": "string",
|
||||
"description": "The email address of the author of the resume."
|
||||
},
|
||||
"phone": {
|
||||
@@ -277,16 +267,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
},
|
||||
"link": {
|
||||
"default": "",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uri"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"const": ""
|
||||
}
|
||||
],
|
||||
"type": "string",
|
||||
"description": "If the custom field should be a link, the URL to link to."
|
||||
}
|
||||
},
|
||||
@@ -355,10 +336,31 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
|
||||
},
|
||||
"iconColor": {
|
||||
"default": "",
|
||||
"description": "Optional custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.",
|
||||
"type": "string"
|
||||
},
|
||||
"network": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -385,7 +387,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"description": "The link to the profile of the author on the network or platform, if any."
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "icon", "network", "username", "website"],
|
||||
"required": ["id", "hidden", "icon", "iconColor", "network", "username", "website"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "The items to display in the profiles section."
|
||||
@@ -423,6 +425,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"company": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -430,7 +448,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
},
|
||||
"position": {
|
||||
"type": "string",
|
||||
"description": "The position held at the company or organization."
|
||||
"description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank."
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
@@ -438,7 +456,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
},
|
||||
"period": {
|
||||
"type": "string",
|
||||
"description": "The period of time the author was employed at the company or organization."
|
||||
"description": "The overall period of time at the company. When multiple roles are used, this should reflect the total tenure."
|
||||
},
|
||||
"website": {
|
||||
"type": "object",
|
||||
@@ -459,9 +477,47 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of the experience. This should be a HTML-formatted string."
|
||||
},
|
||||
"roles": {
|
||||
"default": [],
|
||||
"description": "Optional list of individual roles held at this company to show career progression.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the role. Usually generated as a UUID."
|
||||
},
|
||||
"position": {
|
||||
"type": "string",
|
||||
"description": "The position or job title for this role."
|
||||
},
|
||||
"period": {
|
||||
"type": "string",
|
||||
"description": "The period of time this role was held."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of this specific role. This should be a HTML-formatted string."
|
||||
}
|
||||
},
|
||||
"required": ["id", "position", "period", "description"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "company", "position", "location", "period", "website", "description"],
|
||||
"required": [
|
||||
"id",
|
||||
"hidden",
|
||||
"company",
|
||||
"position",
|
||||
"location",
|
||||
"period",
|
||||
"website",
|
||||
"description",
|
||||
"roles"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "The items to display in the experience section."
|
||||
@@ -499,6 +555,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"school": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -594,6 +666,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -662,10 +750,31 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
|
||||
},
|
||||
"iconColor": {
|
||||
"default": "",
|
||||
"description": "Optional custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -691,7 +800,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"],
|
||||
"required": ["id", "hidden", "icon", "iconColor", "name", "proficiency", "level", "keywords"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "The items to display in the skills section."
|
||||
@@ -729,6 +838,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -784,10 +909,31 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
|
||||
},
|
||||
"iconColor": {
|
||||
"default": "",
|
||||
"description": "Optional custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -802,7 +948,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "icon", "name", "keywords"],
|
||||
"required": ["id", "hidden", "icon", "iconColor", "name", "keywords"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "The items to display in the interests section."
|
||||
@@ -840,6 +986,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -912,6 +1074,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -984,6 +1162,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1056,6 +1250,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"organization": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1128,6 +1338,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1214,6 +1440,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"summary",
|
||||
"profiles",
|
||||
"experience",
|
||||
"education",
|
||||
@@ -1225,7 +1452,8 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"certifications",
|
||||
"publications",
|
||||
"volunteer",
|
||||
"references"
|
||||
"references",
|
||||
"cover-letter"
|
||||
],
|
||||
"description": "The type of items this custom section contains. Determines which item schema and form fields to use."
|
||||
},
|
||||
@@ -1244,10 +1472,105 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string",
|
||||
"description": "The recipient's address block as HTML (name, title, company, address, email)."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)."
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "recipient", "content"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the item. Usually generated as a UUID."
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The rich text content of the summary item. This should be a HTML-formatted string."
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "content"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the item. Usually generated as a UUID."
|
||||
},
|
||||
"hidden": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
|
||||
},
|
||||
"iconColor": {
|
||||
"default": "",
|
||||
"description": "Optional custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.",
|
||||
"type": "string"
|
||||
},
|
||||
"network": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1274,7 +1597,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"description": "The link to the profile of the author on the network or platform, if any."
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "icon", "network", "username", "website"],
|
||||
"required": ["id", "hidden", "icon", "iconColor", "network", "username", "website"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
@@ -1288,6 +1611,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"company": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1295,7 +1634,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
},
|
||||
"position": {
|
||||
"type": "string",
|
||||
"description": "The position held at the company or organization."
|
||||
"description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank."
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
@@ -1303,7 +1642,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
},
|
||||
"period": {
|
||||
"type": "string",
|
||||
"description": "The period of time the author was employed at the company or organization."
|
||||
"description": "The overall period of time at the company. When multiple roles are used, this should reflect the total tenure."
|
||||
},
|
||||
"website": {
|
||||
"type": "object",
|
||||
@@ -1324,9 +1663,47 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of the experience. This should be a HTML-formatted string."
|
||||
},
|
||||
"roles": {
|
||||
"default": [],
|
||||
"description": "Optional list of individual roles held at this company to show career progression.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The unique identifier for the role. Usually generated as a UUID."
|
||||
},
|
||||
"position": {
|
||||
"type": "string",
|
||||
"description": "The position or job title for this role."
|
||||
},
|
||||
"period": {
|
||||
"type": "string",
|
||||
"description": "The period of time this role was held."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "The description of this specific role. This should be a HTML-formatted string."
|
||||
}
|
||||
},
|
||||
"required": ["id", "position", "period", "description"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "company", "position", "location", "period", "website", "description"],
|
||||
"required": [
|
||||
"id",
|
||||
"hidden",
|
||||
"company",
|
||||
"position",
|
||||
"location",
|
||||
"period",
|
||||
"website",
|
||||
"description",
|
||||
"roles"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
@@ -1340,6 +1717,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"school": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1411,6 +1804,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1455,10 +1864,31 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
|
||||
},
|
||||
"iconColor": {
|
||||
"default": "",
|
||||
"description": "Optional custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1484,7 +1914,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"],
|
||||
"required": ["id", "hidden", "icon", "iconColor", "name", "proficiency", "level", "keywords"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
@@ -1498,6 +1928,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"language": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1529,10 +1975,31 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available."
|
||||
},
|
||||
"iconColor": {
|
||||
"default": "",
|
||||
"description": "Optional custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1547,7 +2014,7 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["id", "hidden", "icon", "name", "keywords"],
|
||||
"required": ["id", "hidden", "icon", "iconColor", "name", "keywords"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
@@ -1561,6 +2028,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1609,6 +2092,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1657,6 +2156,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1705,6 +2220,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"organization": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1753,6 +2284,22 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"type": "boolean",
|
||||
"description": "Whether to hide the item from the resume."
|
||||
},
|
||||
"options": {
|
||||
"description": "Display options for this item.",
|
||||
"default": {
|
||||
"showLinkInTitle": false
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"showLinkInTitle": {
|
||||
"default": false,
|
||||
"description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["showLinkInTitle"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -1811,13 +2358,14 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"azurill",
|
||||
"bronzor",
|
||||
"chikorita",
|
||||
"ditto",
|
||||
"ditgar",
|
||||
"ditto",
|
||||
"gengar",
|
||||
"glalie",
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -1907,9 +2455,10 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la
|
||||
"description": "The vertical margin of the page, defined in points (pt)."
|
||||
},
|
||||
"format": {
|
||||
"default": "a4",
|
||||
"type": "string",
|
||||
"enum": ["a4", "letter", "free-form"],
|
||||
"description": "The format of the page. Can be 'a4', 'letter', or 'free-form'."
|
||||
"description": "The format of the page. Can be 'a4', 'letter' or 'free-form'."
|
||||
},
|
||||
"locale": {
|
||||
"default": "en-US",
|
||||
|
||||
@@ -3,15 +3,6 @@ title: "Setting up passkeys"
|
||||
description: "Learn how to register passkeys (WebAuthn) to sign in securely using biometrics or your device PIN"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
<strong>Passkeys Temporarily Disabled:</strong> Passkey registration and sign-in are currently unavailable in Reactive Resume due to an upstream issue with our authentication provider. We are closely monitoring the situation and will re-enable passkeys once it is resolved.
|
||||
|
||||
For more details and technical updates, see the <a href="https://github.com/better-auth/better-auth/issues/7463" target="_blank" rel="noopener">GitHub issue #7463</a>.
|
||||
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
@@ -27,8 +18,7 @@ For more details and technical updates, see the <a href="https://github.com/bett
|
||||
</Step>
|
||||
|
||||
<Step title="Name your passkey">
|
||||
You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for
|
||||
example, “MacBook Touch ID” or “iPhone Face ID”).
|
||||
Enter a descriptive name so you can recognize it later (for example, "MacBook Touch ID" or "iPhone Face ID").
|
||||
</Step>
|
||||
|
||||
<Step title="Complete the passkey prompt">
|
||||
@@ -41,11 +31,15 @@ For more details and technical updates, see the <a href="https://github.com/bett
|
||||
</Step>
|
||||
|
||||
<Step title="Manage passkeys (optional)">
|
||||
After registering, your passkey will show up in the list. You can delete a passkey using the trash icon next to it.
|
||||
After registering, your passkey will show up in the list. You can rename it or delete it from the same section.
|
||||
|
||||
<Warning>
|
||||
Deleting a passkey cannot be undone. After deletion, you won't be able to sign in using that passkey anymore.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Sign in with a passkey">
|
||||
On the login page, click <Badge>Sign in with Passkey</Badge> to authenticate without entering your password.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -12,7 +12,17 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standar
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an API key">
|
||||
<Step title="Choose your authentication method">
|
||||
Reactive Resume MCP supports two authentication methods:
|
||||
|
||||
- **OAuth2 (recommended):** best user experience for clients that support MCP OAuth.
|
||||
- **API key (fallback):** works in all clients that can send custom headers.
|
||||
|
||||
Use OAuth2 whenever your MCP client supports it. Use API key only when OAuth is unavailable in that client.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="If using API key, create one">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) (or your self-hosted instance), sign in, and navigate to **Settings → API Keys**. Click **Create a new API key**, give it a name, and copy the secret — it's only shown once.
|
||||
|
||||
For the full walkthrough, see [Using the API](/guides/using-the-api).
|
||||
@@ -22,11 +32,31 @@ The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standar
|
||||
|
||||
## Configuration
|
||||
|
||||
There are two ways to connect, depending on whether your MCP client supports the Streamable HTTP transport natively.
|
||||
There are two transport options, and each can use either OAuth2 or API key depending on your client capabilities.
|
||||
|
||||
### Method 1: Streamable HTTP (recommended)
|
||||
|
||||
If your client supports the `url` field (e.g. **Cursor**), use this — no extra dependencies required:
|
||||
If your client supports the `url` field (e.g. **Cursor**, **Codex**, Claude custom connectors), use this.
|
||||
|
||||
#### Option A: OAuth2 (recommended)
|
||||
|
||||
Most OAuth-capable clients only need the MCP URL:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then connect/sign in from the client UI (or with the client's OAuth login command).
|
||||
|
||||
#### Option B: API key (fallback)
|
||||
|
||||
If OAuth is not supported in your client, send `x-api-key`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -43,7 +73,9 @@ If your client supports the `url` field (e.g. **Cursor**), use this — no extra
|
||||
|
||||
### Method 2: mcp-remote
|
||||
|
||||
If your client only supports `command` / `args` (e.g. **Claude Desktop**), use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a bridge. This requires [Node.js](https://nodejs.org) **20 or later**.
|
||||
If your client only supports `command` / `args` (for example, local-only Claude Desktop config), use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a bridge. This requires [Node.js](https://nodejs.org) **20 or later**.
|
||||
|
||||
`mcp-remote` is most commonly used with API keys:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -63,9 +95,103 @@ If your client only supports `command` / `args` (e.g. **Claude Desktop**), use [
|
||||
| Client | Config file |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| Cursor | `.cursor/mcp.json` in your project or home directory |
|
||||
| Claude Desktop | `claude_desktop_config.json` ([docs](https://modelcontextprotocol.io/docs/tools/claude-desktop)) |
|
||||
| Claude Desktop | `claude_desktop_config.json` ([docs](https://modelcontextprotocol.io/quickstart/user)) |
|
||||
| Codex | `~/.codex/config.toml` or `.codex/config.toml` ([docs](https://developers.openai.com/codex/mcp)) |
|
||||
| Other MCP clients | Refer to the client's documentation |
|
||||
|
||||
## Authentication Details (How Reactive Resume MCP Works)
|
||||
|
||||
Reactive Resume MCP accepts authentication in this order:
|
||||
|
||||
1. **Bearer token (OAuth2 access token)** via `Authorization: Bearer <token>`
|
||||
2. **API key fallback** via `x-api-key: <key>`
|
||||
|
||||
If neither is valid, the MCP endpoint responds with `401` and advertises OAuth metadata using:
|
||||
|
||||
- `WWW-Authenticate: Bearer resource_metadata="<instance>/.well-known/oauth-protected-resource"`
|
||||
|
||||
This lets OAuth-capable MCP clients discover and complete the OAuth flow automatically.
|
||||
|
||||
### OAuth2 flow used by this server
|
||||
|
||||
Reactive Resume is configured as an OAuth authorization server for MCP clients:
|
||||
|
||||
- The MCP endpoint is `https://rxresu.me/mcp`.
|
||||
- OAuth discovery metadata is exposed under `/.well-known/*` endpoints.
|
||||
- The login/authorization route is `/auth/oauth`.
|
||||
- If the user is not signed in, `/auth/oauth` redirects to `/auth/login`, then resumes OAuth.
|
||||
- If the user is signed in, `/auth/oauth` validates `client_id` and `redirect_uri`, issues an authorization code, and redirects back to the client.
|
||||
- PKCE parameters (`code_challenge`, `code_challenge_method`) are preserved in the authorization flow.
|
||||
|
||||
## Popular Client Setup
|
||||
|
||||
### Cursor
|
||||
|
||||
**OAuth2 (recommended):**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**API key fallback:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp",
|
||||
"headers": {
|
||||
"x-api-key": "your-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Codex (CLI / IDE extension)
|
||||
|
||||
Add server:
|
||||
|
||||
```bash
|
||||
codex mcp add reactive-resume --url https://rxresu.me/mcp
|
||||
```
|
||||
|
||||
Then log in with OAuth:
|
||||
|
||||
```bash
|
||||
codex mcp login reactive-resume
|
||||
```
|
||||
|
||||
API key fallback (`config.toml`):
|
||||
|
||||
```toml
|
||||
[mcp_servers."reactive-resume"]
|
||||
url = "https://rxresu.me/mcp"
|
||||
http_headers = { "x-api-key" = "your-api-key" }
|
||||
```
|
||||
|
||||
### Claude (web app custom connector)
|
||||
|
||||
Add `https://rxresu.me/mcp` as a custom remote MCP connector, then connect with OAuth in Claude's connector UI.
|
||||
|
||||
### Claude Desktop (local config file)
|
||||
|
||||
Use `mcp-remote` bridge with API key (example shown above in **Method 2**).
|
||||
|
||||
## External References
|
||||
|
||||
- [Cursor MCP docs](https://cursor.sh/docs/mcp)
|
||||
- [MCP quickstart for users (Claude Desktop example)](https://modelcontextprotocol.io/quickstart/user)
|
||||
- [OpenAI Codex MCP docs](https://developers.openai.com/codex/mcp)
|
||||
- [Claude custom connectors (remote MCP)](https://claude.com/docs/connectors/custom/remote-mcp)
|
||||
- [MCP Authorization spec](https://modelcontextprotocol.io/specification/latest/basic/authorization)
|
||||
|
||||
## Self-Hosting
|
||||
|
||||
If you're running a self-hosted Reactive Resume instance, replace `https://rxresu.me/mcp` with your instance URL:
|
||||
@@ -81,32 +207,56 @@ If you're running a self-hosted Reactive Resume instance, replace `https://rxres
|
||||
|
||||
## Available Tools
|
||||
|
||||
The MCP server exposes the following tools:
|
||||
Tool names use a `reactive_resume_` prefix so they stay distinct when multiple MCP servers are enabled in the same client.
|
||||
|
||||
| Tool | Description |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
|
||||
| `get_resume` | Get the full data of a specific resume by ID |
|
||||
| `create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
|
||||
| `duplicate_resume` | Create a copy of an existing resume with a new name and slug |
|
||||
| `patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
|
||||
| `delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
|
||||
| `lock_resume` | Lock a resume to prevent edits, patches, and deletion |
|
||||
| `unlock_resume` | Unlock a previously locked resume to re-enable editing |
|
||||
| `export_resume_pdf` | Generate a PDF from the resume and return a download URL |
|
||||
| `get_resume_screenshot` | Get a visual preview of the resume's first page as a WebP image URL |
|
||||
| `get_resume_statistics` | Get view and download statistics for a resume |
|
||||
| Tool | Description |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `reactive_resume_list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
|
||||
| `reactive_resume_list_resume_tags` | List every distinct tag in use across your resumes (sorted) |
|
||||
| `reactive_resume_get_resume` | Get the full data of a specific resume by ID |
|
||||
| `reactive_resume_get_resume_analysis` | Get the latest saved AI analysis for a resume (from the web app), if any |
|
||||
| `reactive_resume_create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
|
||||
| `reactive_resume_import_resume` | Create a resume from a full ResumeData JSON export (random name/slug). Large files may exceed client limits |
|
||||
| `reactive_resume_duplicate_resume` | Create a copy of an existing resume with a new name and slug |
|
||||
| `reactive_resume_patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
|
||||
| `reactive_resume_update_resume` | Update metadata only: name, slug, tags, `isPublic`. Returns canonical share URL; passwords are not managed via MCP |
|
||||
| `reactive_resume_delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
|
||||
| `reactive_resume_lock_resume` | Lock a resume to prevent edits, patches, and deletion |
|
||||
| `reactive_resume_unlock_resume` | Unlock a previously locked resume to re-enable editing |
|
||||
| `reactive_resume_export_resume_pdf` | Generate a PDF from the resume and return a download URL |
|
||||
| `reactive_resume_get_resume_screenshot` | Get a visual preview of the resume's first page as a WebP image URL |
|
||||
| `reactive_resume_get_resume_statistics` | Get view and download statistics for a resume |
|
||||
|
||||
### Breaking change (tool names)
|
||||
|
||||
Older clients may refer to unprefixed names (`list_resumes`, `get_resume`, …) or dot-separated names (`reactive_resume.list_resumes`, …). Those names are no longer used; update automations and saved prompts to the `reactive_resume_*` names above.
|
||||
|
||||
## Available Resources
|
||||
|
||||
| Resource | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `resume://{id}` | The full resume data as a readable JSON resource. Lists all resumes and supports reading individual ones by ID |
|
||||
| `resume://schema` | The ResumeData JSON Schema — reference this to understand valid paths and value types for JSON Patch operations |
|
||||
Resources follow MCP conventions: **static** items appear in `resources/list`; **parameterized** access is declared in `resources/templates/list` and read via `resources/read` once you know the ID.
|
||||
|
||||
| Discovery | What you get |
|
||||
| ------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `resources/list` | Static resources only — currently **`resume://_meta/schema`** (ResumeData JSON Schema) |
|
||||
| `resources/templates/list` | **`resume://{id}`** — template for reading full resume JSON by ID (not enumerated per resume) |
|
||||
| `reactive_resume_list_resumes` (tool) | **Primary way to discover resume IDs** — resumes are not listed as separate MCP resources |
|
||||
|
||||
| URI | Description |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| `resume://_meta/schema` | ResumeData JSON Schema — use for valid JSON Patch paths and value types |
|
||||
| `resume://{id}` | Full resume data as JSON — use an ID from `reactive_resume_list_resumes` |
|
||||
|
||||
### Breaking change (schema URI)
|
||||
|
||||
The schema resource was previously `resume://schema`. It is now **`resume://_meta/schema`**. Update any saved prompts, automations, or client configs that referenced the old URI.
|
||||
|
||||
### Static server card (`/.well-known/mcp/server-card.json`)
|
||||
|
||||
`GET /.well-known/mcp/server-card.json` returns a JSON document ([SEP-1649](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1649)) with `serverInfo`, optional authentication metadata, and summaries of tools, resources, resource templates, and prompts. It is generated to match the live MCP server and can be used for discovery when a client cannot run a full capability scan against `/mcp/`.
|
||||
|
||||
## Available Prompts
|
||||
|
||||
Prompts are pre-built workflows that provide the AI with structured instructions and context. Each prompt embeds the resume data and schema automatically.
|
||||
Prompts are pre-built workflows that provide the AI with structured instructions and context. Each prompt embeds the resume data and the schema resource (`resume://_meta/schema`) automatically.
|
||||
|
||||
| Prompt | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
@@ -129,7 +279,10 @@ Once your MCP client is connected, you can use natural language to interact with
|
||||
### Creating & Managing
|
||||
|
||||
- "Create a new resume called 'Frontend Engineer 2026'"
|
||||
- "Import this exported ResumeData JSON as a new resume"
|
||||
- "What tags do I use across my resumes?"
|
||||
- "Duplicate my 'Software Engineer' resume for a product manager role"
|
||||
- "Make my resume public and give me the share link"
|
||||
- "Lock my finalized resume so it can't be accidentally edited"
|
||||
- "Delete my old draft resume"
|
||||
|
||||
@@ -160,16 +313,19 @@ Once your MCP client is connected, you can use natural language to interact with
|
||||
- "Tailor my resume for this job description: ..." (uses `tailor_resume`)
|
||||
|
||||
<Tip>
|
||||
The AI will use `get_resume` to inspect your current resume before making changes with `patch_resume`. This ensures
|
||||
the correct JSON paths are used.
|
||||
The AI will use `reactive_resume_get_resume` to inspect your current resume before making changes with
|
||||
`reactive_resume_patch_resume`. This ensures the correct JSON paths are used. Use `reactive_resume_update_resume` for
|
||||
name, slug, tags, and public visibility (not for section content).
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| "Unauthorized" with no login prompt | Your client may not support MCP OAuth discovery. Use API key mode (`x-api-key`) |
|
||||
| OAuth login opens but fails redirect/callback | Confirm your client's MCP OAuth callback settings and retry the connection |
|
||||
| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → API Keys** |
|
||||
| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs |
|
||||
| "API error (404)" | The resume ID doesn't exist. Use `reactive_resume_list_resumes` to find valid IDs |
|
||||
| "API error (403)" | The resume is locked. Unlock it in the Reactive Resume dashboard |
|
||||
| Connection refused | Check that the URL is correct and the instance is running |
|
||||
| "ReferenceError: File is not defined" when using `mcp-remote` | You're running Node.js 18. `mcp-remote` requires **Node.js 20 or later** — upgrade with `nvm use 20` or `nvm alias default 20` |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
@@ -54,9 +54,9 @@ APP_URL="http://localhost:3000"
|
||||
PRINTER_APP_URL="http://host.docker.internal:3000"
|
||||
|
||||
# --- Printer ---
|
||||
# If using browserless with token authentication, include the token as a query parameter:
|
||||
# PRINTER_ENDPOINT="ws://printer:3000?token=your-secret-token"
|
||||
PRINTER_ENDPOINT="ws://printer:3000"
|
||||
# Keep this token in sync with the Browserless TOKEN value.
|
||||
BROWSERLESS_TOKEN="change-me"
|
||||
PRINTER_ENDPOINT="ws://printer:3000?token=change-me"
|
||||
|
||||
# --- Database (PostgreSQL) ---
|
||||
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
|
||||
@@ -64,6 +64,8 @@ DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
|
||||
# --- Authentication ---
|
||||
# Generated using `openssl rand -hex 32`
|
||||
AUTH_SECRET=""
|
||||
# Better Auth dashboard API key (optional)
|
||||
BETTER_AUTH_API_KEY=""
|
||||
|
||||
# Social Auth (Google, optional)
|
||||
GOOGLE_CLIENT_ID=""
|
||||
@@ -73,6 +75,10 @@ GOOGLE_CLIENT_SECRET=""
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_CLIENT_SECRET=""
|
||||
|
||||
# Social Auth (LinkedIn, optional)
|
||||
LINKEDIN_CLIENT_ID=""
|
||||
LINKEDIN_CLIENT_SECRET=""
|
||||
|
||||
# Custom OAuth Provider
|
||||
OAUTH_PROVIDER_NAME=""
|
||||
OAUTH_CLIENT_ID=""
|
||||
@@ -83,20 +89,30 @@ OAUTH_DISCOVERY_URL=""
|
||||
OAUTH_AUTHORIZATION_URL=""
|
||||
OAUTH_TOKEN_URL=""
|
||||
OAUTH_USER_INFO_URL=""
|
||||
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=""
|
||||
# Custom scopes (space-separated, defaults to "openid profile email")
|
||||
OAUTH_SCOPES=""
|
||||
|
||||
# Optional Better Auth runtime overrides for advanced deployments:
|
||||
# BETTER_AUTH_URL="https://auth.example.com"
|
||||
# BETTER_AUTH_SECRET=""
|
||||
|
||||
# --- AI (optional) ---
|
||||
# Comma-separated hostnames/origins for custom AI base URLs
|
||||
# Example: api.openai.com,https://gateway.ai.vercel.com
|
||||
AI_ALLOWED_BASE_URLS=""
|
||||
|
||||
# --- Email (optional) ---
|
||||
# If all keys are disabled, the app logs the email to be sent to the console instead.
|
||||
SMTP_HOST=""
|
||||
SMTP_PORT="465"
|
||||
SMTP_PORT="587"
|
||||
SMTP_USER=""
|
||||
SMTP_PASS=""
|
||||
SMTP_FROM="Reactive Resume <noreply@rxresu.me>"
|
||||
SMTP_SECURE="false"
|
||||
|
||||
# --- Storage (optional) ---
|
||||
# If all keys are disabled, the app uses local filesystem (/data) to store uploads instead.
|
||||
# If all keys are disabled, the app uses local filesystem (usually /app/data) to store uploads instead.
|
||||
# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity.
|
||||
S3_ACCESS_KEY_ID=""
|
||||
S3_SECRET_ACCESS_KEY=""
|
||||
@@ -111,6 +127,7 @@ S3_FORCE_PATH_STYLE="false"
|
||||
FLAG_DEBUG_PRINTER="false"
|
||||
FLAG_DISABLE_SIGNUPS="false"
|
||||
FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
FLAG_DISABLE_IMAGE_PROCESSING="false"
|
||||
```
|
||||
|
||||
</Step>
|
||||
@@ -130,9 +147,6 @@ FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
[byte[]]$bytes = New-Object byte[] 32; (New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes); $bytes | ForEach-Object { "{0:x2}" -f $_ } | Out-String -Stream | ForEach-Object { $_.Trim() } | Write-Host -NoNewline
|
||||
```
|
||||
|
||||
```cmd Windows (alternative)
|
||||
certutil -generateSRS 32 | findstr /r /v "^$" | findstr /v ":" | findstr /v " " | findstr /v "-" | findstr /v "certutil"
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Step>
|
||||
@@ -168,10 +182,9 @@ services:
|
||||
- HEALTH=true
|
||||
- CONCURRENT=20
|
||||
- QUEUED=10
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
- TOKEN=${BROWSERLESS_TOKEN}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
|
||||
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN}" > /dev/null']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
@@ -278,11 +291,15 @@ docker compose logs -f reactive-resume
|
||||
SMTP (<code>SMTP_*</code>)
|
||||
</li>
|
||||
<li>
|
||||
Social auth (<code>GOOGLE_*</code>, <code>GITHUB_*</code>)
|
||||
Social auth (<code>GOOGLE_*</code>, <code>GITHUB_*</code>, <code>LINKEDIN_*</code>,{" "}
|
||||
<code>OAUTH_*</code>)
|
||||
</li>
|
||||
<li>
|
||||
S3 storage (<code>S3_*</code>)
|
||||
</li>
|
||||
<li>
|
||||
AI URL allowlist (<code>AI_ALLOWED_BASE_URLS</code>)
|
||||
</li>
|
||||
<li>
|
||||
Feature flags (<code>FLAG_*</code>)
|
||||
</li>
|
||||
@@ -298,7 +315,9 @@ docker compose logs -f reactive-resume
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Printer">
|
||||
- **`PRINTER_ENDPOINT`**: Base URL where Reactive Resume reaches the printer service. In Compose: `http://printer:3000`. If using browserless with token authentication, include the token as a query parameter: `ws://printer:3000?token=your-secret-token`.
|
||||
- **`PRINTER_ENDPOINT`**: Endpoint where Reactive Resume connects to the printer browser.
|
||||
- **Recommended (Browserless)**: `ws://printer:3000?token=...` and keep the token value in sync with Browserless `TOKEN`.
|
||||
- **Also supported**: `http://chrome:9222` for Chrome DevTools Protocol endpoints.
|
||||
|
||||
<Note>
|
||||
**Alternative to browserless**: You can use a lightweight headless Chrome Docker image like `chromedp/headless-shell`:
|
||||
@@ -318,7 +337,9 @@ docker compose logs -f reactive-resume
|
||||
|
||||
<Accordion title="Database (PostgreSQL)">
|
||||
- **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`. - In
|
||||
Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`.
|
||||
Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`. - If your password
|
||||
contains special characters (`@`, `#`, `:`), URL-encode it. - For managed Postgres, add provider-specific params (for
|
||||
example `?sslmode=require`) when needed.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Authentication">
|
||||
@@ -338,9 +359,18 @@ openssl rand -hex 32
|
||||
|
||||
**`GITHUB_CLIENT_ID`** / **`GITHUB_CLIENT_SECRET`** (optional): Enables GitHub sign-in.
|
||||
|
||||
**`LINKEDIN_CLIENT_ID`** / **`LINKEDIN_CLIENT_SECRET`** (optional): Enables LinkedIn sign-in.
|
||||
|
||||
**`BETTER_AUTH_API_KEY`** (optional): Enables Better Auth dashboard integrations.
|
||||
|
||||
**`BETTER_AUTH_URL`** (optional, advanced): Overrides auth base URL if it must differ from `APP_URL` (for split-host deployments).
|
||||
|
||||
**`BETTER_AUTH_SECRET`** (optional, advanced): Overrides `AUTH_SECRET` for Better Auth internals.
|
||||
|
||||
**Custom OAuth provider** (optional):
|
||||
- **`OAUTH_PROVIDER_NAME`**: Display name in the UI
|
||||
- **`OAUTH_CLIENT_ID`** / **`OAUTH_CLIENT_SECRET`**: Required for any custom OAuth provider
|
||||
- **`OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`**: Comma-separated allowlist for extra dynamic OAuth redirect hosts/origins (HTTPS only, non-private hosts).
|
||||
- **`OAUTH_SCOPES`**: Space-separated scopes (defaults to `openid profile email`)
|
||||
|
||||
Configure endpoints using **one** of these methods:
|
||||
@@ -352,8 +382,9 @@ openssl rand -hex 32
|
||||
<Accordion title="Email (SMTP, optional)">
|
||||
If SMTP is not configured, the app logs emails to the server console instead of sending them.
|
||||
|
||||
- Email delivery is enabled only when **all** of `SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM` are set.
|
||||
- **`SMTP_HOST`**: SMTP host (if empty, email sending is disabled).
|
||||
- **`SMTP_PORT`**: Usually `465` (implicit TLS) or `587` (STARTTLS).
|
||||
- **`SMTP_PORT`**: Defaults to `587` in the app.
|
||||
- **`SMTP_USER`** / **`SMTP_PASS`**: SMTP credentials.
|
||||
- **`SMTP_FROM`**: Default from address (for example, `Reactive Resume <noreply@rxresu.me>`).
|
||||
- **`SMTP_SECURE`**: `"true"` or `"false"` (string). Match your provider settings.
|
||||
@@ -361,21 +392,23 @@ openssl rand -hex 32
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Storage (S3 or local)">
|
||||
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent
|
||||
storage (for example `./data:/app/data`) or uploads can be lost on container recreation. - **S3/S3-compatible**:
|
||||
Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.): -
|
||||
**`S3_ACCESS_KEY_ID`** - **`S3_SECRET_ACCESS_KEY`** - **`S3_REGION`** - **`S3_ENDPOINT`** (for S3-compatible
|
||||
providers; may be blank for AWS depending on your setup) - **`S3_BUCKET`** - **`S3_FORCE_PATH_STYLE`**: Controls how
|
||||
the bucket is addressed in URLs. Defaults to `"false"`. - Set to `"true"` for **path-style** URLs
|
||||
(`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services.
|
||||
- Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**,
|
||||
**Cloudflare R2**, and most cloud providers.
|
||||
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `<cwd>/data` (usually `/app/data` in the official image).
|
||||
- Mount local uploads to persistent storage (for example `./data:/app/data`) or uploads can be lost on container recreation.
|
||||
- **S3/S3-compatible**: Configure `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_REGION`, `S3_ENDPOINT`, and `S3_BUCKET`.
|
||||
- **`S3_FORCE_PATH_STYLE`** controls bucket addressing (defaults to `"false"`):
|
||||
- `"true"` for path-style URLs (`https://endpoint/bucket`) common with MinIO/SeaweedFS.
|
||||
- `"false"` for virtual-hosted-style URLs (`https://bucket.endpoint`) common with AWS S3 / Cloudflare R2.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="AI (optional)">
|
||||
- **`AI_ALLOWED_BASE_URLS`**: Comma-separated hosts or origins allowed as custom AI API base URLs. - Use this when
|
||||
routing AI requests through your own gateway/proxy. - Example: `api.openai.com,https://gateway.ai.vercel.com`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Feature Flags">
|
||||
- **`FLAG_DEBUG_PRINTER`**: Bypasses the printer-only access restriction (useful when debugging `/printer/{resumeId}`). Recommended: keep `"false"` in production.
|
||||
- **`FLAG_DISABLE_SIGNUPS`**: Disables new signups (web app and server). Useful for private instances.
|
||||
- **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required.
|
||||
- **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/LinkedIn/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required.
|
||||
- **`FLAG_DISABLE_IMAGE_PROCESSING`**: Disables image processing. This is useful if you are using a machine with limited resources, like a Raspberry Pi.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -384,24 +417,32 @@ openssl rand -hex 32
|
||||
|
||||
To update your Reactive Resume installation to the latest available version, follow these steps:
|
||||
|
||||
1. **Pull the latest images** for all services defined in your Docker Compose file.
|
||||
1. **Back up your database and uploads first** (highly recommended before every update).
|
||||
|
||||
2. **Pull the latest images** for all services defined in your Docker Compose file.
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
2. **Restart the containers** to run the new images.
|
||||
3. **Restart the containers** to run the new images.
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. **(Optional) Remove old, unused Docker images** to free up disk space.
|
||||
4. **Check migration/startup logs** after deploy.
|
||||
|
||||
```bash
|
||||
docker compose logs -f reactive-resume
|
||||
```
|
||||
|
||||
5. **(Optional) Remove old, unused Docker images** to free up disk space.
|
||||
```bash
|
||||
docker image prune -f
|
||||
```
|
||||
|
||||
This process ensures your app, database, and printer are all up-to-date while keeping your data and configuration intact.
|
||||
This process updates app services and automatically runs DB migrations on startup. If migration fails, restore from backup and fix configuration before retrying.
|
||||
|
||||
## Backups (recommended)
|
||||
|
||||
@@ -419,7 +460,7 @@ If you're using S3-compatible storage, consider enabling versioning on your buck
|
||||
|
||||
## Health Checks
|
||||
|
||||
Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies are functioning correctly. If any critical service (such as the database connection) fails, the health check will return an unhealthy status.
|
||||
Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies. It checks **database**, **printer**, and **storage**; if any one is unhealthy, the endpoint returns HTTP `503`.
|
||||
|
||||
### How it works
|
||||
|
||||
@@ -442,7 +483,7 @@ Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream
|
||||
- **Healthy containers** receive traffic as normal
|
||||
- **Unhealthy containers** are automatically removed from the load balancer pool
|
||||
|
||||
This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses its database connection), the reverse proxy will stop routing traffic to it until it recovers.
|
||||
This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses database, printer, or storage connectivity), the reverse proxy will stop routing traffic to it until it recovers.
|
||||
|
||||
<Tip>
|
||||
If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy
|
||||
@@ -461,40 +502,51 @@ curl -f http://localhost:3000/api/health
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
A healthy response returns HTTP 200. Any other response (or a connection failure) indicates a problem that should be investigated in the container logs.
|
||||
A healthy response returns HTTP 200. Any other response (or a connection failure) indicates a problem that should be investigated in the JSON response body and container logs.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="The app container exits immediately">
|
||||
- **Common cause**: database migrations failed (often a bad `DATABASE_URL`).
|
||||
- **What to do**:
|
||||
When the app container exits right away, you'll want to check the logs for more information about the error. Run the following command to view real-time logs from the Reactive Resume container:
|
||||
- **What to do**:
|
||||
Check logs for migration errors and database connectivity details:
|
||||
```bash
|
||||
docker compose logs -f reactive-resume
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can't sign in / redirects loop / cookies don't stick">
|
||||
- **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy). -
|
||||
**Fix**: set `APP_URL` to the public URL (preferably HTTPS) and restart the container.
|
||||
- **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy), or
|
||||
you're serving HTTPS while `APP_URL` is `http://...`. - **Fix**: set `APP_URL` to your canonical public HTTPS URL and
|
||||
restart the container.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="PDF export fails / printing is stuck">
|
||||
- **Common cause**: Reactive Resume can't reach the printer or the printer can't reach your app. - **Checks**: -
|
||||
`PRINTER_ENDPOINT` should usually be `http://printer:3000` in Compose. - If you use
|
||||
`PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for the printer
|
||||
service.
|
||||
- **Common cause**: Reactive Resume can't reach the printer, token mismatch, or the printer can't reach your app. -
|
||||
**Checks**: - `PRINTER_ENDPOINT` should usually be `ws://printer:3000?token=...` in Compose. - Browserless `TOKEN` and
|
||||
the token in `PRINTER_ENDPOINT` must match. - If you use `PRINTER_APP_URL="http://host.docker.internal:3000"` on
|
||||
Linux, set `extra_hosts: ["host.docker.internal:host-gateway"]` for the printer service.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="/api/health returns 503 even though Postgres is up">
|
||||
- **Common cause**: printer or storage health failed (not only database). - **Fix**: inspect the endpoint response
|
||||
payload and check `printer` / `storage` fields: ```bash curl -s http://localhost:3000/api/health ```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Uploads disappear after restart">
|
||||
- **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3). - **Fix**: add a volume mount
|
||||
like `./data:/app/data` and redeploy.
|
||||
- **Cause**: local upload storage wasn't mounted to a persistent volume. - **Fix**: add a volume mount like
|
||||
`./data:/app/data` and redeploy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Emails aren't being delivered">
|
||||
- **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead. - **Fix**: configure SMTP
|
||||
and verify your provider's TLS/port settings.
|
||||
- **Expected behavior**: if SMTP isn't fully configured, the app logs emails to the console. - **Fix**: set
|
||||
`SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM`, then verify `SMTP_PORT` and `SMTP_SECURE`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dynamic OAuth redirect URI is rejected">
|
||||
- **Common cause**: redirect host is not trusted for dynamic client registration. - **Fix**: add trusted HTTPS
|
||||
hosts/origins to `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3 storage error: ENOTFOUND bucket.endpoint">
|
||||
|
||||
@@ -85,12 +85,11 @@ services:
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
- TOKEN=${BROWSERLESS_TOKEN}
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
|
||||
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN}" > /dev/null']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -102,7 +101,7 @@ services:
|
||||
- APP_URL=https://resume.${DOMAIN}
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
|
||||
- PRINTER_ENDPOINT=http://printer:3000
|
||||
- PRINTER_ENDPOINT=ws://printer:3000?token=${BROWSERLESS_TOKEN}
|
||||
- AUTH_SECRET=${AUTH_SECRET}
|
||||
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
|
||||
volumes:
|
||||
@@ -143,6 +142,7 @@ DOMAIN="example.com"
|
||||
ACME_EMAIL="admin@example.com"
|
||||
POSTGRES_PASSWORD="your-secure-postgres-password"
|
||||
AUTH_SECRET="your-auth-secret-from-openssl-rand-hex-32"
|
||||
BROWSERLESS_TOKEN="your-browserless-token"
|
||||
# Optional: Traefik dashboard auth (generate with: htpasswd -nb admin password)
|
||||
TRAEFIK_DASHBOARD_AUTH="admin:$$apr1$$..."
|
||||
```
|
||||
@@ -191,10 +191,14 @@ services:
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
- TOKEN=${BROWSERLESS_TOKEN}
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN}" > /dev/null']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
@@ -203,7 +207,7 @@ services:
|
||||
- APP_URL=https://resume.${DOMAIN}
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
|
||||
- PRINTER_ENDPOINT=http://printer:3000
|
||||
- PRINTER_ENDPOINT=ws://printer:3000?token=${BROWSERLESS_TOKEN}
|
||||
- AUTH_SECRET=${AUTH_SECRET}
|
||||
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
|
||||
volumes:
|
||||
@@ -339,10 +343,9 @@ services:
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
- TOKEN=$BROWSERLESS_TOKEN
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
|
||||
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=$BROWSERLESS_TOKEN" > /dev/null']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -405,6 +408,8 @@ services:
|
||||
- GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET
|
||||
- GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
|
||||
- GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
|
||||
- LINKEDIN_CLIENT_ID=$LINKEDIN_CLIENT_ID
|
||||
- LINKEDIN_CLIENT_SECRET=$LINKEDIN_CLIENT_SECRET
|
||||
- SMTP_HOST=$SMTP_HOST
|
||||
- SMTP_PORT=$SMTP_PORT
|
||||
- SMTP_USER=$SMTP_USER
|
||||
@@ -467,10 +472,10 @@ docker stack deploy -c compose-swarm.yml reactive_resume
|
||||
docker stack services reactive_resume
|
||||
|
||||
# View logs for the app
|
||||
docker service logs -f reactive_resume_app
|
||||
docker service logs -f reactive_resume_reactive_resume
|
||||
|
||||
# Scale the app
|
||||
docker service scale reactive_resume_app=3
|
||||
docker service scale reactive_resume_reactive_resume=3
|
||||
|
||||
# Remove the stack
|
||||
docker stack rm reactive_resume
|
||||
|
||||
@@ -7,6 +7,11 @@ description: "A step-by-step guide to migrate your Reactive Resume instance from
|
||||
|
||||
This guide walks you through migrating your Reactive Resume installation from **v4 to v5**. The migration process involves setting up a new v5 instance alongside your existing v4 instance, then transferring your users and resumes to the new system.
|
||||
|
||||
<Info>
|
||||
This page is for **v4 → v5 data migration** only. For normal v5 upgrades, use the [Self-Hosting with
|
||||
Docker](/self-hosting/docker) guide. v5 schema migrations run automatically on app startup.
|
||||
</Info>
|
||||
|
||||
<Warning>
|
||||
**Keep your v4 instance running** until you have successfully migrated all data to v5 and verified everything works
|
||||
correctly. This ensures you have a fallback in case anything goes wrong during the migration.
|
||||
@@ -86,7 +91,7 @@ To run the migration scripts, you need the following installed on your host mach
|
||||
</Card>
|
||||
<Card title="Reactive Resume Source Code">
|
||||
Clone the Reactive Resume repository to access the migration scripts: ```bash git clone
|
||||
https://github.com/amruthpillai/reactive-resume.git cd reactive-resume pnpm install ```
|
||||
https://github.com/amruthpillai/reactive-resume.git cd reactive-resume vp install ```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -107,6 +112,8 @@ PRODUCTION_DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resu
|
||||
`PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss.
|
||||
</Warning>
|
||||
|
||||
<Warning>`PRODUCTION_DATABASE_URL` is used only by these migration scripts. It is not a runtime app variable.</Warning>
|
||||
|
||||
### Step 1: Migrate Users
|
||||
|
||||
The user migration script transfers all user accounts, authentication data, and two-factor settings from v4 to v5.
|
||||
|
||||
@@ -62,10 +62,13 @@ You must configure endpoints using **one** of these two methods:
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --------------------- | ---------------------------------------- | ---------------------- |
|
||||
| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
|
||||
| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
|
||||
| Variable | Description | Default |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- |
|
||||
| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
|
||||
| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
|
||||
| `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` | Comma-separated allowlist for dynamic OAuth client redirect hosts/origins (HTTPS only) | _empty_ |
|
||||
| `BETTER_AUTH_URL` | Optional auth base URL override for split-host setups | `APP_URL` |
|
||||
| `BETTER_AUTH_SECRET` | Optional Better Auth secret override | `AUTH_SECRET` |
|
||||
|
||||
## Callback URL
|
||||
|
||||
@@ -86,6 +89,18 @@ https://resume.example.com/api/auth/oauth2/callback/custom
|
||||
authentication to fail.
|
||||
</Warning>
|
||||
|
||||
<Info>
|
||||
Built-in providers (Google, GitHub, LinkedIn) use callback URLs in this format: `{APP_URL}/api/auth/callback/
|
||||
{provider}` (for example `.../google`, `.../github`, `.../linkedin`).
|
||||
</Info>
|
||||
|
||||
## URL and Proxy Requirements
|
||||
|
||||
- Set `APP_URL` to the exact public URL users access (prefer HTTPS in production).
|
||||
- If auth metadata/JWKS must be served from a different public host, set `BETTER_AUTH_URL`.
|
||||
- Behind a reverse proxy, forward `Host` and `X-Forwarded-Proto` correctly, or cookie/session behavior may break.
|
||||
- `trustedOrigins` are derived from `APP_URL`, so alternate domains are not automatically trusted.
|
||||
|
||||
## Profile Mapping
|
||||
|
||||
Reactive Resume automatically maps user profile data from the OAuth provider. The following fields are used:
|
||||
@@ -253,6 +268,8 @@ AUTH_SECRET="your-32-byte-hex-secret"
|
||||
# GOOGLE_CLIENT_SECRET=""
|
||||
# GITHUB_CLIENT_ID=""
|
||||
# GITHUB_CLIENT_SECRET=""
|
||||
# LINKEDIN_CLIENT_ID=""
|
||||
# LINKEDIN_CLIENT_SECRET=""
|
||||
|
||||
# Custom OAuth Provider (e.g., Authentik)
|
||||
OAUTH_PROVIDER_NAME="Company SSO"
|
||||
@@ -278,6 +295,11 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
number differences - Path case sensitivity
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Session/cookie issues after successful OAuth login">
|
||||
Common cause: `APP_URL` does not match the real HTTPS public origin (for example, app is behind TLS but `APP_URL` is
|
||||
`http://...`). Fix: set `APP_URL` to the canonical HTTPS URL and restart the app.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Custom OAuth button not appearing">
|
||||
The custom OAuth option only appears if both `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set, **and** either:
|
||||
- `OAUTH_DISCOVERY_URL` is set, **or**
|
||||
@@ -293,6 +315,11 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
domain
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dynamic client redirect URI is rejected">
|
||||
Dynamic OAuth client registration only allows HTTPS redirect URIs on trusted hosts. Add allowed hosts/origins to
|
||||
`OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` (comma-separated).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="User profile data is missing or incorrect">
|
||||
The profile mapping depends on your provider returning standard claims:
|
||||
- `email` (required)
|
||||
@@ -317,6 +344,9 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
<Card title="Verify redirect URIs" icon="shield-check">
|
||||
Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
|
||||
</Card>
|
||||
<Card title="Protect auth internals" icon="shield">
|
||||
Keep `BETTER_AUTH_SECRET` and `BETTER_AUTH_API_KEY` private. Rotating these values may invalidate active sessions.
|
||||
</Card>
|
||||
<Card title="Review scopes" icon="list-check">
|
||||
Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
|
||||
</Card>
|
||||
|
||||
+13793
-187
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@5/schema.json",
|
||||
"entry": ["src/server.ts", "src/router.tsx"],
|
||||
"entry": ["src/server.ts", "src/router.tsx", "lingui.config.ts"],
|
||||
"tailwind": { "entry": ["src/styles.css"] },
|
||||
"project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"],
|
||||
"project": ["src/**/*.{js,jsx,ts,tsx,css}"],
|
||||
"ignoreBinaries": ["mint"],
|
||||
"ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"],
|
||||
"ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}"]
|
||||
"ignore": ["src/schema/**/*.ts", "src/components/ui/**/*.{ts,tsx}"]
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
import { defineConfig } from "@lingui/cli";
|
||||
import { formatter } from "@lingui/format-po";
|
||||
|
||||
export default defineConfig({
|
||||
sourceLocale: "en-US",
|
||||
@@ -64,9 +65,9 @@ export default defineConfig({
|
||||
"zu-ZA": "en-US",
|
||||
default: "en-US",
|
||||
},
|
||||
formatOptions: {
|
||||
format: formatter({
|
||||
lineNumbers: false,
|
||||
},
|
||||
}),
|
||||
catalogs: [
|
||||
{
|
||||
path: "<rootDir>/locales/{locale}",
|
||||
|
||||
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+820
-145
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+907
-231
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+907
-231
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+909
-233
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+908
-232
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+904
-228
File diff suppressed because it is too large
Load Diff
Generated
+906
-230
File diff suppressed because it is too large
Load Diff
Generated
+905
-229
File diff suppressed because it is too large
Load Diff
Generated
+814
-139
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
CREATE TABLE "jwks" (
|
||||
"id" uuid PRIMARY KEY,
|
||||
"public_key" text NOT NULL,
|
||||
"private_key" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"expires_at" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "oauth_access_token" (
|
||||
"id" uuid PRIMARY KEY,
|
||||
"token" text NOT NULL UNIQUE,
|
||||
"client_id" text NOT NULL,
|
||||
"session_id" uuid,
|
||||
"user_id" uuid,
|
||||
"reference_id" text,
|
||||
"refresh_id" uuid,
|
||||
"expires_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"scopes" text[] NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "oauth_client" (
|
||||
"id" uuid PRIMARY KEY,
|
||||
"client_id" text NOT NULL UNIQUE,
|
||||
"client_secret" text,
|
||||
"disabled" boolean DEFAULT false,
|
||||
"skip_consent" boolean,
|
||||
"enable_end_session" boolean,
|
||||
"subject_type" text,
|
||||
"scopes" text[],
|
||||
"user_id" uuid,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now(),
|
||||
"name" text,
|
||||
"uri" text,
|
||||
"icon" text,
|
||||
"contacts" text[],
|
||||
"tos" text,
|
||||
"policy" text,
|
||||
"software_id" text,
|
||||
"software_version" text,
|
||||
"software_statement" text,
|
||||
"redirect_uris" text[] NOT NULL,
|
||||
"post_logout_redirect_uris" text[],
|
||||
"token_endpoint_auth_method" text,
|
||||
"grant_types" text[],
|
||||
"response_types" text[],
|
||||
"public" boolean,
|
||||
"type" text,
|
||||
"require_pkce" boolean,
|
||||
"reference_id" text,
|
||||
"metadata" jsonb
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "oauth_consent" (
|
||||
"id" uuid PRIMARY KEY,
|
||||
"client_id" text NOT NULL,
|
||||
"user_id" uuid,
|
||||
"reference_id" text,
|
||||
"scopes" text[] NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now()
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "oauth_refresh_token" (
|
||||
"id" uuid PRIMARY KEY,
|
||||
"token" text NOT NULL,
|
||||
"client_id" text NOT NULL,
|
||||
"session_id" uuid,
|
||||
"user_id" uuid NOT NULL,
|
||||
"reference_id" text,
|
||||
"expires_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"revoked" timestamp with time zone,
|
||||
"auth_time" timestamp with time zone,
|
||||
"scopes" text[] NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX "oauth_access_token_token_index" ON "oauth_access_token" ("token");--> statement-breakpoint
|
||||
CREATE INDEX "oauth_client_client_id_index" ON "oauth_client" ("client_id");--> statement-breakpoint
|
||||
CREATE INDEX "oauth_consent_user_id_client_id_index" ON "oauth_consent" ("user_id","client_id");--> statement-breakpoint
|
||||
CREATE INDEX "oauth_refresh_token_token_index" ON "oauth_refresh_token" ("token");--> statement-breakpoint
|
||||
ALTER TABLE "oauth_access_token" ADD CONSTRAINT "oauth_access_token_client_id_oauth_client_client_id_fkey" FOREIGN KEY ("client_id") REFERENCES "oauth_client"("client_id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_access_token" ADD CONSTRAINT "oauth_access_token_session_id_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "session"("id") ON DELETE SET NULL;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_access_token" ADD CONSTRAINT "oauth_access_token_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_access_token" ADD CONSTRAINT "oauth_access_token_refresh_id_oauth_refresh_token_id_fkey" FOREIGN KEY ("refresh_id") REFERENCES "oauth_refresh_token"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_client" ADD CONSTRAINT "oauth_client_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_consent" ADD CONSTRAINT "oauth_consent_client_id_oauth_client_client_id_fkey" FOREIGN KEY ("client_id") REFERENCES "oauth_client"("client_id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_consent" ADD CONSTRAINT "oauth_consent_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_refresh_token" ADD CONSTRAINT "oauth_refresh_token_client_id_oauth_client_client_id_fkey" FOREIGN KEY ("client_id") REFERENCES "oauth_client"("client_id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_refresh_token" ADD CONSTRAINT "oauth_refresh_token_session_id_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "session"("id") ON DELETE SET NULL;--> statement-breakpoint
|
||||
ALTER TABLE "oauth_refresh_token" ADD CONSTRAINT "oauth_refresh_token_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE "session" ADD COLUMN "impersonated_by" text;--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "role" text DEFAULT 'user';--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "banned" boolean DEFAULT false;--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "ban_reason" text;--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp(6) with time zone;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE "resume_analysis" (
|
||||
"id" uuid PRIMARY KEY,
|
||||
"analysis" jsonb NOT NULL,
|
||||
"resume_id" uuid NOT NULL UNIQUE,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX "resume_analysis_resume_id_index" ON "resume_analysis" ("resume_id");--> statement-breakpoint
|
||||
ALTER TABLE "resume_analysis" ADD CONSTRAINT "resume_analysis_resume_id_resume_id_fkey" FOREIGN KEY ("resume_id") REFERENCES "resume"("id") ON DELETE CASCADE;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
CREATE UNIQUE INDEX "user_email_lower_unique_idx" ON "user" (lower("email"));
|
||||
File diff suppressed because it is too large
Load Diff
+101
-92
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "reactive-resume",
|
||||
"version": "5.0.13",
|
||||
"version": "5.0.20",
|
||||
"description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
@@ -22,103 +22,109 @@
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"dev": "vp dev",
|
||||
"docs:dev": "cd docs && npx mint dev",
|
||||
"fmt": "vp fmt --check",
|
||||
"fmt:fix": "vp fmt",
|
||||
"email:dev": "email dev --dir src/integrations/email/templates --port 3001",
|
||||
"format": "vp fmt --check",
|
||||
"format:fix": "vp fmt",
|
||||
"knip": "knip",
|
||||
"lingui:extract": "lingui extract --clean --overwrite",
|
||||
"lint": "vp lint --type-aware",
|
||||
"lint:fix": "vp lint --type-aware --fix",
|
||||
"prepare": "vp config",
|
||||
"preview": "vp preview",
|
||||
"start": "node .output/server/index.mjs",
|
||||
"test": "vp test",
|
||||
"test:coverage": "vp test --coverage --run",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^3.0.58",
|
||||
"@ai-sdk/google": "^3.0.43",
|
||||
"@ai-sdk/openai": "^3.0.41",
|
||||
"@ai-sdk/react": "^3.0.118",
|
||||
"@aws-sdk/client-s3": "^3.1012.0",
|
||||
"@base-ui/react": "^1.3.0",
|
||||
"@better-auth/api-key": "^1.5.5",
|
||||
"@better-auth/drizzle-adapter": "^1.5.5",
|
||||
"@better-auth/infra": "^0.1.12",
|
||||
"@ai-sdk/anthropic": "^3.0.71",
|
||||
"@ai-sdk/google": "^3.0.64",
|
||||
"@ai-sdk/openai": "^3.0.53",
|
||||
"@ai-sdk/openai-compatible": "^2.0.41",
|
||||
"@aws-sdk/client-s3": "^3.1037.0",
|
||||
"@base-ui/react": "^1.4.1",
|
||||
"@better-auth/api-key": "^1.6.9",
|
||||
"@better-auth/drizzle-adapter": "^1.6.9",
|
||||
"@better-auth/infra": "^0.2.5",
|
||||
"@better-auth/oauth-provider": "^1.6.9",
|
||||
"@better-auth/passkey": "^1.6.9",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@lingui/core": "^5.9.3",
|
||||
"@lingui/react": "^5.9.3",
|
||||
"@modelcontextprotocol/sdk": "^1.27.1",
|
||||
"@lingui/core": "^6.0.0",
|
||||
"@lingui/react": "^6.0.0",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@monaco-editor/react": "4.8.0-rc.3",
|
||||
"@orpc/client": "^1.13.8",
|
||||
"@orpc/json-schema": "^1.13.8",
|
||||
"@orpc/openapi": "^1.13.8",
|
||||
"@orpc/server": "^1.13.8",
|
||||
"@orpc/tanstack-query": "^1.13.8",
|
||||
"@orpc/zod": "^1.13.8",
|
||||
"@orpc/client": "^1.14.0",
|
||||
"@orpc/experimental-ratelimit": "^1.14.0",
|
||||
"@orpc/json-schema": "^1.14.0",
|
||||
"@orpc/openapi": "^1.14.0",
|
||||
"@orpc/server": "^1.14.0",
|
||||
"@orpc/tanstack-query": "^1.14.0",
|
||||
"@orpc/zod": "^1.14.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@phosphor-icons/web": "^2.1.2",
|
||||
"@sindresorhus/slugify": "^3.0.0",
|
||||
"@t3-oss/env-core": "^0.13.10",
|
||||
"@tanstack/react-query": "^5.91.0",
|
||||
"@tanstack/react-router": "^1.167.5",
|
||||
"@tanstack/react-router-ssr-query": "^1.166.9",
|
||||
"@tanstack/react-start": "^1.166.17",
|
||||
"@t3-oss/env-core": "^0.13.11",
|
||||
"@tanstack/react-query": "^5.100.5",
|
||||
"@tanstack/react-router": "^1.168.25",
|
||||
"@tanstack/react-router-ssr-query": "^1.166.12",
|
||||
"@tanstack/react-start": "^1.167.50",
|
||||
"@tanstack/zod-adapter": "^1.166.9",
|
||||
"@tiptap/extension-highlight": "^3.20.4",
|
||||
"@tiptap/extension-table": "^3.20.4",
|
||||
"@tiptap/extension-text-align": "^3.20.4",
|
||||
"@tiptap/pm": "^3.20.4",
|
||||
"@tiptap/react": "^3.20.4",
|
||||
"@tiptap/starter-kit": "^3.20.4",
|
||||
"@uiw/color-convert": "^2.9.6",
|
||||
"@uiw/react-color-colorful": "^2.9.6",
|
||||
"ai": "^6.0.116",
|
||||
"ai-sdk-ollama": "^3.8.1",
|
||||
"@tiptap/extension-color": "^3.22.4",
|
||||
"@tiptap/extension-highlight": "^3.22.4",
|
||||
"@tiptap/extension-table": "^3.22.4",
|
||||
"@tiptap/extension-text-align": "^3.22.4",
|
||||
"@tiptap/extension-text-style": "^3.22.4",
|
||||
"@tiptap/pm": "^3.22.4",
|
||||
"@tiptap/react": "^3.22.4",
|
||||
"@tiptap/starter-kit": "^3.22.4",
|
||||
"@uiw/color-convert": "^2.10.1",
|
||||
"@uiw/react-color-colorful": "^2.10.1",
|
||||
"ai": "^6.0.168",
|
||||
"bcrypt": "^6.0.0",
|
||||
"better-auth": "^1.5.5",
|
||||
"better-auth": "^1.6.9",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"docx": "^9.6.1",
|
||||
"dompurify": "^3.3.3",
|
||||
"drizzle-orm": "1.0.0-beta.18-7eb39f0",
|
||||
"drizzle-zod": "^1.0.0-beta.14-a36c63d",
|
||||
"es-toolkit": "^1.45.1",
|
||||
"dompurify": "^3.4.1",
|
||||
"drizzle-orm": "1.0.0-beta.22",
|
||||
"drizzle-zod": "1.0.0-beta.14-a36c63d",
|
||||
"es-toolkit": "^1.46.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-json-patch": "^3.1.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"fuse.js": "^7.3.0",
|
||||
"immer": "^11.1.4",
|
||||
"input-otp": "^1.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsonrepair": "^3.13.3",
|
||||
"jsonrepair": "^3.14.0",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"motion": "^12.38.0",
|
||||
"nodemailer": "^8.0.3",
|
||||
"nodemailer": "^8.0.7",
|
||||
"ollama-ai-provider-v2": "^3.5.0",
|
||||
"pg": "^8.20.0",
|
||||
"puppeteer-core": "^24.39.1",
|
||||
"puppeteer-core": "^24.42.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-hook-form": "^7.71.2",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"react-email": "^6.0.0",
|
||||
"react-hook-form": "^7.74.0",
|
||||
"react-hotkeys-hook": "^5.2.4",
|
||||
"react-resizable-panels": "^4.7.3",
|
||||
"react-resizable-panels": "^4.10.0",
|
||||
"react-window": "^2.2.7",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"shadcn": "^4.0.8",
|
||||
"react-zoom-pan-pinch": "^4.0.3",
|
||||
"shadcn": "^4.5.0",
|
||||
"sharp": "^0.34.5",
|
||||
"sonner": "^2.0.7",
|
||||
"srvx": "^0.11.12",
|
||||
"srvx": "^0.11.15",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"tailwindcss": "^4.2.4",
|
||||
"ts-pattern": "^5.9.0",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"usehooks-ts": "^3.1.1",
|
||||
"uuid": "^13.0.0",
|
||||
"uuid": "^14.0.0",
|
||||
"workbox-window": "^7.4.0",
|
||||
"zod": "^4.3.6",
|
||||
"zundo": "^2.3.0",
|
||||
@@ -126,55 +132,58 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
"@lingui/babel-plugin-lingui-macro": "^5.9.3",
|
||||
"@lingui/cli": "^5.9.3",
|
||||
"@lingui/vite-plugin": "^5.9.3",
|
||||
"@rolldown/plugin-babel": "^0.2.2",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"@lingui/babel-plugin-lingui-macro": "^6.0.0",
|
||||
"@lingui/cli": "^6.0.0",
|
||||
"@lingui/format-po": "^6.0.0",
|
||||
"@lingui/vite-plugin": "^6.0.0",
|
||||
"@react-email/ui": "^6.0.0",
|
||||
"@rolldown/plugin-babel": "^0.2.3",
|
||||
"@tailwindcss/vite": "^4.2.4",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/nodemailer": "^7.0.11",
|
||||
"@types/pg": "^8.18.0",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/nodemailer": "^8.0.0",
|
||||
"@types/pg": "^8.20.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260319.1",
|
||||
"@typescript/native-preview": "7.0.0-dev.20260427.1",
|
||||
"@vitejs/plugin-react": "^6.0.1",
|
||||
"@vitest/coverage-v8": "^4.1.5",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"drizzle-kit": "1.0.0-beta.18-7eb39f0",
|
||||
"jsdom": "^29.0.0",
|
||||
"knip": "^5.88.0",
|
||||
"nitro": "3.0.260311-beta",
|
||||
"node-addon-api": "^8.6.0",
|
||||
"node-gyp": "^12.2.0",
|
||||
"npm-check-updates": "^19.6.5",
|
||||
"tsx": "^4.21.0",
|
||||
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
||||
"drizzle-kit": "1.0.0-beta.22",
|
||||
"happy-dom": "^20.9.0",
|
||||
"jose": "^6.2.2",
|
||||
"knip": "^6.7.0",
|
||||
"nitro": "3.0.260415-beta",
|
||||
"node-addon-api": "^8.7.0",
|
||||
"node-gyp": "^12.3.0",
|
||||
"npm-check-updates": "^22.0.1",
|
||||
"vite": "npm:@voidzero-dev/vite-plus-core@^0.1.19",
|
||||
"vite-plugin-pwa": "^1.2.0",
|
||||
"vite-plus": "latest",
|
||||
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
||||
"vite-plus": "^0.1.19",
|
||||
"vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.19"
|
||||
},
|
||||
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
|
||||
"packageManager": "pnpm@10.33.2",
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@isaacs/brace-expansion": "^5.0.1",
|
||||
"ajv": "^8.18.0",
|
||||
"basic-ftp": "^5.2.0",
|
||||
"fast-xml-parser": "^5.3.8",
|
||||
"hono": "^4.11.10",
|
||||
"lodash": "^4.17.23",
|
||||
"markdown-it": "^14.1.1",
|
||||
"minimatch": "^10.2.3",
|
||||
"qs": "^6.14.2",
|
||||
"serialize-javascript": "^7.0.4",
|
||||
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
||||
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
||||
},
|
||||
"onlyBuiltDependencies": [
|
||||
"bcrypt",
|
||||
"esbuild",
|
||||
"msw",
|
||||
"sharp"
|
||||
]
|
||||
],
|
||||
"overrides": {
|
||||
"dompurify@<=3.3.1": ">=3.3.2",
|
||||
"dompurify@<=3.3.3": ">=3.4.0",
|
||||
"dompurify@<3.3.2": ">=3.3.2",
|
||||
"dompurify@<3.4.0": ">=3.4.0",
|
||||
"dompurify@>=1.0.10 <3.4.0": ">=3.4.0",
|
||||
"dompurify@>=3.0.1 <3.4.0": ">=3.4.0",
|
||||
"dompurify@>=3.1.3 <=3.3.1": ">=3.3.2",
|
||||
"postcss@<8.5.10": ">=8.5.10",
|
||||
"serialize-javascript@<=7.0.2": ">=7.0.3",
|
||||
"serialize-javascript@<7.0.5": ">=7.0.5",
|
||||
"uuid@<14.0.0": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { definePlugin } from "nitro";
|
||||
import { Pool } from "pg";
|
||||
|
||||
async function migrateDatabase() {
|
||||
console.log("⌛ Running database migrations...");
|
||||
console.info("Running database migrations...");
|
||||
|
||||
const connectionString = process.env.DATABASE_URL;
|
||||
|
||||
@@ -17,9 +17,9 @@ async function migrateDatabase() {
|
||||
|
||||
try {
|
||||
await migrate(db, { migrationsFolder: "./migrations" });
|
||||
console.log("✅ Database migrations completed");
|
||||
console.info("Database migrations completed");
|
||||
} catch (error) {
|
||||
console.error("🚨 Database migrations failed:", error);
|
||||
console.error({ err: error }, "Database migrations failed");
|
||||
throw error;
|
||||
} finally {
|
||||
await pool.end();
|
||||
|
||||
Generated
+4983
-4706
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"name": "Reactive Resume",
|
||||
"short_name": "Reactive Resume",
|
||||
"description": "A free and open-source resume builder.",
|
||||
"start_url": "/?source=pwa",
|
||||
"display": "standalone",
|
||||
"background_color": "#09090B",
|
||||
"theme_color": "#09090B",
|
||||
"lang": "en",
|
||||
"scope": "/",
|
||||
"id": "/?source=pwa",
|
||||
"orientation": "portrait",
|
||||
"icons": [
|
||||
{ "src": "favicon.ico", "sizes": "128x128", "type": "image/x-icon" },
|
||||
{ "src": "pwa-64x64.png", "sizes": "64x64", "type": "image/png" },
|
||||
{ "src": "pwa-192x192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "pwa-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
|
||||
{ "src": "maskable-icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "screenshots/web/1-landing-page.webp",
|
||||
"sizes": "1920x1080 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "wide",
|
||||
"label": "Landing Page"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/web/2-resume-dashboard.webp",
|
||||
"sizes": "1920x1080 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "wide",
|
||||
"label": "Resume Dashboard"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/web/3-builder-screen.webp",
|
||||
"sizes": "1920x1080 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "wide",
|
||||
"label": "Builder Screen"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/web/4-template-gallery.webp",
|
||||
"sizes": "1920x1080 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "wide",
|
||||
"label": "Template Gallery"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/mobile/1-landing-page.webp",
|
||||
"sizes": "1284x2778 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "narrow",
|
||||
"label": "Landing Page"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/mobile/2-resume-dashboard.webp",
|
||||
"sizes": "1284x2778 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "narrow",
|
||||
"label": "Resume Dashboard"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/mobile/3-builder-screen.webp",
|
||||
"sizes": "1284x2778 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "narrow",
|
||||
"label": "Builder Screen"
|
||||
},
|
||||
{
|
||||
"src": "screenshots/mobile/4-template-gallery.webp",
|
||||
"sizes": "1284x2778 any",
|
||||
"type": "image/webp",
|
||||
"form_factor": "narrow",
|
||||
"label": "Template Gallery"
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
"ai",
|
||||
"builder",
|
||||
"business",
|
||||
"career",
|
||||
"cv",
|
||||
"editor",
|
||||
"free",
|
||||
"generator",
|
||||
"job-search",
|
||||
"multilingual",
|
||||
"open-source",
|
||||
"privacy",
|
||||
"productivity",
|
||||
"resume",
|
||||
"self-hosted",
|
||||
"templates",
|
||||
"utilities",
|
||||
"writing"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 954 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user