mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-09-30 00:34:28 +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 |
+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 test
|
||||
- run: vp check --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,6 +2,7 @@ dist
|
||||
.env*
|
||||
/data
|
||||
.nitro
|
||||
.agents
|
||||
.output
|
||||
.vercel
|
||||
.cursor
|
||||
|
||||
Vendored
+3
-6
@@ -1,12 +1,8 @@
|
||||
{
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode"
|
||||
},
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit",
|
||||
"source.fixAll.oxc": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file",
|
||||
"files.associations": {
|
||||
@@ -40,5 +36,6 @@
|
||||
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||
],
|
||||
"tailwindCSS.experimental.configFile": "src/styles/globals.css",
|
||||
"typescript.experimental.useTsgo": true
|
||||
"typescript.experimental.useTsgo": true,
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Overview
|
||||
|
||||
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.
|
||||
@@ -91,24 +89,24 @@ Copy `.env.example` to `.env` if not present. Key notes for local dev:
|
||||
|
||||
`vp` is the global CLI for Vite+. Do not use pnpm/npm/yarn directly — Vite+ wraps the underlying package manager.
|
||||
|
||||
| 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 + fmt + 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` |
|
||||
| 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` |
|
||||
|
||||
## Vite+ Pitfalls
|
||||
|
||||
@@ -130,4 +128,4 @@ Copy `.env.example` to `.env` if not present. Key notes for local dev:
|
||||
## Review Checklist for Agents
|
||||
|
||||
- [ ] Run `vp install` after pulling remote changes and before getting started.
|
||||
- [ ] Run `vp check` and `vp test` to validate changes.
|
||||
- [ ] 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%
|
||||
|
||||
@@ -4,6 +4,126 @@ 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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
+647
-165
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
@@ -207,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 |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
@@ -255,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"
|
||||
|
||||
@@ -286,8 +313,9 @@ 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
|
||||
@@ -297,7 +325,7 @@ Once your MCP client is connected, you can use natural language to interact with
|
||||
| "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>
|
||||
|
||||
@@ -1686,6 +1686,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -5050,6 +5051,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -7222,6 +7224,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -9428,6 +9431,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -11741,6 +11745,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -14020,6 +14025,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
@@ -16189,6 +16195,7 @@
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"$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/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
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+905
-197
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+820
-113
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+824
-116
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+828
-120
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+905
-197
File diff suppressed because it is too large
Load Diff
Generated
+905
-197
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+828
-120
File diff suppressed because it is too large
Load Diff
Generated
+905
-197
File diff suppressed because it is too large
Load Diff
Generated
+905
-197
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+830
-122
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+830
-122
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+825
-117
File diff suppressed because it is too large
Load Diff
Generated
+827
-119
File diff suppressed because it is too large
Load Diff
Generated
+826
-118
File diff suppressed because it is too large
Load Diff
Generated
+814
-107
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
+98
-88
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "reactive-resume",
|
||||
"version": "5.0.14",
|
||||
"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,8 +22,9 @@
|
||||
"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",
|
||||
@@ -31,92 +32,99 @@
|
||||
"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.64",
|
||||
"@ai-sdk/google": "^3.0.53",
|
||||
"@ai-sdk/openai": "^3.0.48",
|
||||
"@aws-sdk/client-s3": "^3.1015.0",
|
||||
"@base-ui/react": "^1.3.0",
|
||||
"@better-auth/api-key": "^1.5.6",
|
||||
"@better-auth/drizzle-adapter": "^1.5.6",
|
||||
"@better-auth/infra": "^0.1.13",
|
||||
"@better-auth/oauth-provider": "^1.5.6",
|
||||
"@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.9",
|
||||
"@orpc/json-schema": "^1.13.9",
|
||||
"@orpc/openapi": "^1.13.9",
|
||||
"@orpc/server": "^1.13.9",
|
||||
"@orpc/tanstack-query": "^1.13.9",
|
||||
"@orpc/zod": "^1.13.9",
|
||||
"@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.11",
|
||||
"@tanstack/react-query": "^5.95.2",
|
||||
"@tanstack/react-router": "^1.168.3",
|
||||
"@tanstack/react-router-ssr-query": "^1.166.10",
|
||||
"@tanstack/react-start": "^1.167.5",
|
||||
"@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.137",
|
||||
"ai-sdk-ollama": "^3.8.2",
|
||||
"@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.6",
|
||||
"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.19",
|
||||
"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",
|
||||
"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.40.0",
|
||||
"puppeteer-core": "^24.42.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-hook-form": "^7.72.0",
|
||||
"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.5",
|
||||
"react-resizable-panels": "^4.10.0",
|
||||
"react-window": "^2.2.7",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"shadcn": "^4.1.0",
|
||||
"react-zoom-pan-pinch": "^4.0.3",
|
||||
"shadcn": "^4.5.0",
|
||||
"sharp": "^0.34.5",
|
||||
"sonner": "^2.0.7",
|
||||
"srvx": "^0.11.13",
|
||||
"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",
|
||||
@@ -124,56 +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/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.20260324.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.19",
|
||||
"drizzle-kit": "1.0.0-beta.22",
|
||||
"happy-dom": "^20.9.0",
|
||||
"jose": "^6.2.2",
|
||||
"jsdom": "^29.0.1",
|
||||
"knip": "^6.0.4",
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+4808
-4723
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 |
Binary file not shown.
@@ -99,9 +99,9 @@ function getComputerModernWebFonts(): WebFont[] {
|
||||
preview: `${CDN}/Sans/cmunss.woff`,
|
||||
files: {
|
||||
"400": `${CDN}/Sans/cmunss.woff`,
|
||||
"400italic": `${CDN}/Sans/cmunsl.woff`,
|
||||
"400italic": `${CDN}/Sans/cmunsi.woff`,
|
||||
"700": `${CDN}/Sans/cmunsx.woff`,
|
||||
"700italic": `${CDN}/Sans/cmunsi.woff`,
|
||||
"700italic": `${CDN}/Sans/cmunso.woff`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -192,8 +192,7 @@ export async function migrateResumes() {
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on("SIGINT", handleShutdown);
|
||||
process.on("SIGTERM", handleShutdown);
|
||||
process.on("exit", handleShutdown);
|
||||
|
||||
// Initialize the importer
|
||||
const importer = new ReactiveResumeV4JSONImporter();
|
||||
@@ -465,8 +464,7 @@ export async function migrateResumes() {
|
||||
}
|
||||
|
||||
// Remove signal handlers
|
||||
process.off("SIGINT", handleShutdown);
|
||||
process.off("SIGTERM", handleShutdown);
|
||||
process.off("exit", handleShutdown);
|
||||
|
||||
const migrationEnd = performance.now();
|
||||
const migrationDurationMs = migrationEnd - migrationStart;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user