mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-09-30 00:34:28 +10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9332e1e3ff | ||
|
|
77f31fa127 | ||
|
|
4fd43657dc | ||
|
|
d9a24448e8 | ||
|
|
69ff78b656 | ||
|
|
0794b5c955 | ||
|
|
5c986cc68d | ||
|
|
688c002822 | ||
|
|
ced49d5314 | ||
|
|
3653baad9b | ||
|
|
b4aaf9712f | ||
|
|
e3274d7c95 | ||
|
|
a7a3d53dbd | ||
|
|
f8d776106f | ||
|
|
e34f648455 | ||
|
|
f4e2d05f25 | ||
|
|
bbc17b8995 | ||
|
|
a66bc13f4f | ||
|
|
7858efbd2b | ||
|
|
7da5e2c0c2 | ||
|
|
177c550a0c | ||
|
|
882725cabf | ||
|
|
3e16586d7a | ||
|
|
56a1838315 | ||
|
|
2d76cc5cfb | ||
|
|
192880e416 | ||
|
|
d1dac8aeca | ||
|
|
f13093249d | ||
|
|
99c602e3c7 | ||
|
|
040755bec9 | ||
|
|
1063680774 | ||
|
|
1f8644bb54 | ||
|
|
1d7c4b2615 | ||
|
|
d8ffd00fa6 | ||
|
|
f431c0fd89 | ||
|
|
571a1c1efe | ||
|
|
547afaa18f | ||
|
|
7789c39fe3 | ||
|
|
bfb2e8bb7e | ||
|
|
189290e093 | ||
|
|
7b541f3567 | ||
|
|
5cd16a62d9 | ||
|
|
89beb43ea2 | ||
|
|
55757857bd | ||
|
|
584444c1fc | ||
|
|
fb61bb4a63 | ||
|
|
ea8fd838d9 | ||
|
|
a3c58b0068 | ||
|
|
e0e8879caf | ||
|
|
feab19913b | ||
|
|
afbe56af0d | ||
|
|
d22ab8202c | ||
|
|
f4b6db222c | ||
|
|
44177c0d1b | ||
|
|
1d59ea3bd0 | ||
|
|
5e9ae64b2b | ||
|
|
46392d9dfc | ||
|
|
6734da4bee | ||
|
|
27edc963ce | ||
|
|
4b46bda4e6 | ||
|
|
df81d0342f | ||
|
|
4825eed22c | ||
|
|
b0711351f7 | ||
|
|
b7e4c86f4e | ||
|
|
c25c068d20 | ||
|
|
d217d5dc4f | ||
|
|
1ed80140b5 | ||
|
|
28ae7714c8 | ||
|
|
9d70bc4fd3 | ||
|
|
e5a51013c1 | ||
|
|
eeaac9a86f | ||
|
|
269dbc600f | ||
|
|
9b29a44429 | ||
|
|
e9fa161148 | ||
|
|
299d6d4878 | ||
|
|
ea63ae6e72 | ||
|
|
4d50cd9b71 | ||
|
|
8b3f3bcc35 | ||
|
|
f73a5117a1 | ||
|
|
2d1e13b9b5 | ||
|
|
b6c274eeb6 | ||
|
|
4b78a7c9dd | ||
|
|
87e2f2f391 | ||
|
|
f237c42093 | ||
|
|
a80f86e99e | ||
|
|
eb62ed99ce | ||
|
|
45fc32323f | ||
|
|
f1d69da019 | ||
|
|
9d131fdb99 | ||
|
|
29ef6307e0 | ||
|
|
c3c771002f | ||
|
|
554903b818 | ||
|
|
6b71ecd7c6 | ||
|
|
833b8343ac | ||
|
|
8e060890d3 | ||
|
|
abf0fc74e5 | ||
|
|
6242c8c182 | ||
|
|
15327d74d8 | ||
|
|
90c34ca572 | ||
|
|
2b8fa9c7e8 | ||
|
|
2f7aabcfe9 | ||
|
|
ef6d054598 | ||
|
|
201d707aea | ||
|
|
d8b0902ac9 | ||
|
|
01c75bd796 | ||
|
|
0a609306a6 | ||
|
|
5bdc6de862 | ||
|
|
cc01fb9418 | ||
|
|
5e8e1349bc | ||
|
|
ddbb71fb78 | ||
|
|
3289453448 | ||
|
|
611af8ee72 | ||
|
|
69b96a592d | ||
|
|
3151f6a9cc | ||
|
|
726968288c | ||
|
|
ad086f337a | ||
|
|
66d09820c3 | ||
|
|
5ec73e9234 | ||
|
|
17569d0658 | ||
|
|
14ea464c0a | ||
|
|
da9a3c0b12 | ||
|
|
f5a9ffb776 | ||
|
|
19181b79c8 | ||
|
|
1509678578 | ||
|
|
c4d5dd4a3c | ||
|
|
ca734ca547 | ||
|
|
8d347f5162 | ||
|
|
4d368ab6f6 | ||
|
|
cabacdc44b | ||
|
|
71dac2021d | ||
|
|
a8d8d0e340 | ||
|
|
97274d1c80 | ||
|
|
aa12fcbd36 | ||
|
|
3d1c2d1fb6 | ||
|
|
f6fcbdcad0 | ||
|
|
30e6ba0809 | ||
|
|
3cb0fc9f30 | ||
|
|
5f578c3327 | ||
|
|
e56402058a | ||
|
|
19ae21e797 |
@@ -0,0 +1,10 @@
|
||||
FROM mcr.microsoft.com/devcontainers/typescript-node:24
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
EXPOSE 3000
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "Reactive Resume",
|
||||
"service": "reactive_resume",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"workspaceFolder": "/workspace",
|
||||
|
||||
"forwardPorts": [3000, 4000, 5432, 8333],
|
||||
"portsAttributes": {
|
||||
"3000": { "label": "Reactive Resume", "onAutoForward": "openBrowser" },
|
||||
"4000": { "label": "Browserless (Printer)" },
|
||||
"5432": { "label": "PostgreSQL" },
|
||||
"8333": { "label": "SeaweedFS (S3)" }
|
||||
},
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"oxc.oxc-vscode",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"lokalise.i18n-ally",
|
||||
"typescriptteam.native-preview"
|
||||
],
|
||||
"settings": {
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.oxc": "always"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"postCreateCommand": "corepack enable && pnpm install"
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
services:
|
||||
reactive_resume:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: .devcontainer/Dockerfile
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
command: sleep infinity
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
browserless:
|
||||
condition: service_started
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
seaweedfs_create_bucket:
|
||||
condition: service_completed_successfully
|
||||
environment:
|
||||
TZ: Etc/UTC
|
||||
APP_URL: http://localhost:3000
|
||||
PRINTER_APP_URL: http://reactive_resume:3000
|
||||
PRINTER_ENDPOINT: ws://browserless:3000?token=1234567890
|
||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
|
||||
AUTH_SECRET: change-me-to-a-secure-secret-key-in-production
|
||||
S3_ACCESS_KEY_ID: seaweedfs
|
||||
S3_SECRET_ACCESS_KEY: seaweedfs
|
||||
S3_REGION: us-east-1
|
||||
S3_ENDPOINT: http://seaweedfs:8333
|
||||
S3_BUCKET: reactive-resume
|
||||
S3_FORCE_PATH_STYLE: "true"
|
||||
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
browserless:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
QUEUED: "10"
|
||||
HEALTH: "true"
|
||||
CONCURRENT: "5"
|
||||
TOKEN: "1234567890"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
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
|
||||
volumes:
|
||||
- seaweedfs_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost:8888"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
restart: on-failure
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
until mc alias set seaweedfs http://seaweedfs:8333 seaweedfs seaweedfs; do
|
||||
echo 'Waiting for SeaweedFS...';
|
||||
sleep 2;
|
||||
done;
|
||||
mc mb seaweedfs/reactive-resume --ignore-existing;
|
||||
"
|
||||
depends_on:
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
seaweedfs_data:
|
||||
+16
-1
@@ -19,6 +19,9 @@ DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
|
||||
# Generated using `openssl rand -hex 32`
|
||||
AUTH_SECRET="change-me-to-a-secure-secret-key-in-production"
|
||||
|
||||
# Better Auth Dashboard (optional)
|
||||
BETTER_AUTH_API_KEY=""
|
||||
|
||||
# Social Auth (Google, optional)
|
||||
GOOGLE_CLIENT_ID=""
|
||||
GOOGLE_CLIENT_SECRET=""
|
||||
@@ -27,7 +30,11 @@ GOOGLE_CLIENT_SECRET=""
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_CLIENT_SECRET=""
|
||||
|
||||
# Custom OAuth Provider
|
||||
# Social Auth (LinkedIn, optional)
|
||||
LINKEDIN_CLIENT_ID=""
|
||||
LINKEDIN_CLIENT_SECRET=""
|
||||
|
||||
# Custom OAuth Provider (optional)
|
||||
OAUTH_PROVIDER_NAME=""
|
||||
OAUTH_CLIENT_ID=""
|
||||
OAUTH_CLIENT_SECRET=""
|
||||
@@ -66,8 +73,16 @@ FLAG_DISABLE_SIGNUPS="false"
|
||||
# This flag disables email/password login. 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.
|
||||
FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
|
||||
# This flag disables the image processing.
|
||||
# This is useful if you are using a machine with limited resources, like a Raspberry Pi.
|
||||
FLAG_DISABLE_IMAGE_PROCESSING="false"
|
||||
|
||||
# --- Others ---
|
||||
# Google Cloud API Key (optional)
|
||||
# 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=""
|
||||
@@ -1,68 +1,68 @@
|
||||
{
|
||||
"$schema": "https://fundingjson.org/schema/v1.1.0.json",
|
||||
"version": "v1.1.0",
|
||||
"entity": {
|
||||
"type": "individual",
|
||||
"role": "maintainer",
|
||||
"name": "Amruth Pillai",
|
||||
"email": "im.amruth@gmail.com",
|
||||
"description": "Software Engineer",
|
||||
"webpageUrl": {
|
||||
"url": "https://rxresu.me/funding.json"
|
||||
}
|
||||
},
|
||||
"projects": [
|
||||
{
|
||||
"guid": "reactive-resume",
|
||||
"name": "Reactive Resume",
|
||||
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||
"webpageUrl": {
|
||||
"url": "https://rxresu.me"
|
||||
},
|
||||
"repositoryUrl": {
|
||||
"url": "https://github.com/amruthpillai/reactive-resume",
|
||||
"wellKnown": "https://github.com/amruthpillai/reactive-resume/blob/main/.github/.well-known/funding-manifest-urls"
|
||||
},
|
||||
"licenses": ["spdx:MIT"],
|
||||
"tags": ["data", "design", "productivity", "resume-builder"]
|
||||
}
|
||||
],
|
||||
"funding": {
|
||||
"plans": [
|
||||
{
|
||||
"guid": "sponsor",
|
||||
"status": "active",
|
||||
"name": "Sponsor",
|
||||
"description": "Support the project on a recurring basis by becoming a sponsor.",
|
||||
"amount": 10,
|
||||
"currency": "EUR",
|
||||
"frequency": "monthly",
|
||||
"channels": ["github", "open-collective"]
|
||||
},
|
||||
{
|
||||
"guid": "donation",
|
||||
"status": "active",
|
||||
"name": "Donation",
|
||||
"description": "Show your support for the project by making a one-time donation.",
|
||||
"amount": 0,
|
||||
"currency": "EUR",
|
||||
"frequency": "one-time",
|
||||
"channels": ["github", "open-collective"]
|
||||
}
|
||||
],
|
||||
"channels": [
|
||||
{
|
||||
"guid": "github",
|
||||
"type": "payment-provider",
|
||||
"description": "GitHub Sponsors",
|
||||
"address": "https://github.com/sponsors/AmruthPillai"
|
||||
},
|
||||
{
|
||||
"guid": "open-collective",
|
||||
"type": "payment-provider",
|
||||
"description": "Open Collective",
|
||||
"address": "https://opencollective.com/reactive-resume"
|
||||
}
|
||||
]
|
||||
}
|
||||
"$schema": "https://fundingjson.org/schema/v1.1.0.json",
|
||||
"version": "v1.1.0",
|
||||
"entity": {
|
||||
"type": "individual",
|
||||
"role": "maintainer",
|
||||
"name": "Amruth Pillai",
|
||||
"email": "im.amruth@gmail.com",
|
||||
"description": "Software Engineer",
|
||||
"webpageUrl": {
|
||||
"url": "https://rxresu.me/funding.json"
|
||||
}
|
||||
},
|
||||
"projects": [
|
||||
{
|
||||
"guid": "reactive-resume",
|
||||
"name": "Reactive Resume",
|
||||
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||
"webpageUrl": {
|
||||
"url": "https://rxresu.me"
|
||||
},
|
||||
"repositoryUrl": {
|
||||
"url": "https://github.com/amruthpillai/reactive-resume",
|
||||
"wellKnown": "https://github.com/amruthpillai/reactive-resume/blob/main/.github/.well-known/funding-manifest-urls"
|
||||
},
|
||||
"licenses": ["spdx:MIT"],
|
||||
"tags": ["data", "design", "productivity", "resume-builder"]
|
||||
}
|
||||
],
|
||||
"funding": {
|
||||
"plans": [
|
||||
{
|
||||
"guid": "sponsor",
|
||||
"status": "active",
|
||||
"name": "Sponsor",
|
||||
"description": "Support the project on a recurring basis by becoming a sponsor.",
|
||||
"amount": 10,
|
||||
"currency": "EUR",
|
||||
"frequency": "monthly",
|
||||
"channels": ["github", "open-collective"]
|
||||
},
|
||||
{
|
||||
"guid": "donation",
|
||||
"status": "active",
|
||||
"name": "Donation",
|
||||
"description": "Show your support for the project by making a one-time donation.",
|
||||
"amount": 0,
|
||||
"currency": "EUR",
|
||||
"frequency": "one-time",
|
||||
"channels": ["github", "open-collective"]
|
||||
}
|
||||
],
|
||||
"channels": [
|
||||
{
|
||||
"guid": "github",
|
||||
"type": "payment-provider",
|
||||
"description": "GitHub Sponsors",
|
||||
"address": "https://github.com/sponsors/AmruthPillai"
|
||||
},
|
||||
{
|
||||
"guid": "open-collective",
|
||||
"type": "payment-provider",
|
||||
"description": "Open Collective",
|
||||
"address": "https://opencollective.com/reactive-resume"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
github: AmruthPillai
|
||||
open_collective: reactive-resume
|
||||
open_collective: reactive-resume
|
||||
|
||||
@@ -20,4 +20,4 @@ body:
|
||||
label: Feature Description
|
||||
description: A detailed description of the feature you would like to see in Reactive Resume. Please provide as much detail as possible as it will help me implement the feature faster.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
@@ -1 +1 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: false
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
name: autofix.ci
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
autofix:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: voidzero-dev/setup-vp@v1
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: true
|
||||
|
||||
- run: vp install
|
||||
|
||||
- run: vp test
|
||||
- run: vp check --fix
|
||||
|
||||
- uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8
|
||||
@@ -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 }}
|
||||
@@ -146,13 +147,13 @@ jobs:
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
||||
MINOR=$(echo "$VERSION" | cut -d. -f2)
|
||||
|
||||
|
||||
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
||||
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ env.IMAGE }}
|
||||
@@ -181,7 +182,7 @@ jobs:
|
||||
--annotation "index:org.opencontainers.image.version=${{ steps.version.outputs.version }}" \
|
||||
$(printf 'ghcr.io/${{ env.IMAGE }}@sha256:%s ' *) \
|
||||
$(printf 'docker.io/${{ env.IMAGE }}@sha256:%s ' *)
|
||||
|
||||
|
||||
# Get the digest of the multi-arch manifest
|
||||
GHCR_DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }} --format '{{json .Manifest.Digest}}' | tr -d '"')
|
||||
DOCKER_DIGEST=$(docker buildx imagetools inspect docker.io/${{ env.IMAGE }}:v${{ steps.version.outputs.version }} --format '{{json .Manifest.Digest}}' | tr -d '"')
|
||||
@@ -189,13 +190,13 @@ jobs:
|
||||
echo "docker_digest=$DOCKER_DIGEST" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
uses: sigstore/cosign-installer@v4
|
||||
|
||||
- name: Sign images with Cosign
|
||||
run: |
|
||||
# Sign GHCR image
|
||||
cosign sign --yes ghcr.io/${{ env.IMAGE }}@${{ steps.manifest.outputs.ghcr_digest }}
|
||||
|
||||
|
||||
# Sign Docker Hub image
|
||||
cosign sign --yes docker.io/${{ env.IMAGE }}@${{ steps.manifest.outputs.docker_digest }}
|
||||
|
||||
@@ -212,4 +213,4 @@ jobs:
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
script: |
|
||||
cd docker
|
||||
./manage_stack.sh up reactive_resume
|
||||
./manage_stack.sh up reactive_resume
|
||||
|
||||
+5
-1
@@ -5,6 +5,8 @@ dist
|
||||
.output
|
||||
.vercel
|
||||
.cursor
|
||||
.claude
|
||||
TODO.md
|
||||
coverage
|
||||
.netlify
|
||||
.DS_Store
|
||||
@@ -12,5 +14,7 @@ coverage
|
||||
node_modules
|
||||
!.env.example
|
||||
public/sw.js
|
||||
public/sw.js.map
|
||||
scripts/**/*.json
|
||||
public/workbox-*.js
|
||||
public/workbox-*.js
|
||||
public/workbox-*.js.map
|
||||
+9
-9
@@ -2,16 +2,16 @@
|
||||
|
||||
const nextPackages = ["@monaco-editor/react"];
|
||||
|
||||
const betaPackages = ["drizzle-orm", "drizzle-kit", "@better-auth/core", "@better-auth/passkey", "better-auth"];
|
||||
const betaPackages = ["drizzle-orm", "drizzle-kit", "drizzle-zod"];
|
||||
|
||||
/** @type {import('npm-check-updates').RunOptions} */
|
||||
module.exports = {
|
||||
upgrade: true,
|
||||
install: "always",
|
||||
packageManager: "pnpm",
|
||||
target: (packageName) => {
|
||||
if (nextPackages.includes(packageName)) return "@next";
|
||||
if (betaPackages.includes(packageName)) return "@beta";
|
||||
return "latest";
|
||||
},
|
||||
upgrade: true,
|
||||
install: "always",
|
||||
packageManager: "pnpm",
|
||||
target: (packageName) => {
|
||||
if (nextPackages.includes(packageName)) return "@next";
|
||||
if (betaPackages.includes(packageName)) return "@beta";
|
||||
return "latest";
|
||||
},
|
||||
};
|
||||
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
vp staged
|
||||
Vendored
+7
-1
@@ -1,3 +1,9 @@
|
||||
{
|
||||
"recommendations": ["biomejs.biome", "bradlc.vscode-tailwindcss", "lokalise.i18n-ally"]
|
||||
"recommendations": [
|
||||
"oxc.oxc-vscode",
|
||||
"bradlc.vscode-tailwindcss",
|
||||
"lokalise.i18n-ally",
|
||||
"typescriptteam.native-preview",
|
||||
"VoidZero.vite-plus-extension-pack"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+42
-36
@@ -1,38 +1,44 @@
|
||||
{
|
||||
"biome.enabled": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.biome": "explicit",
|
||||
"source.fixAll.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"files.associations": {
|
||||
"*.css": "tailwindcss"
|
||||
},
|
||||
"files.readonlyInclude": {
|
||||
"locales/*.po": true,
|
||||
"pnpm-lock.yaml": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"locales/*.po": true,
|
||||
"pnpm-lock.yaml": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"i18n-ally.enabledParsers": ["po"],
|
||||
"i18n-ally.localesPaths": ["locales"],
|
||||
"i18n-ally.sourceLanguage": "en-US",
|
||||
"search.exclude": {
|
||||
"locales/*.po": true,
|
||||
"pnpm-lock.yaml": true,
|
||||
"**/routeTree.gen.ts": true
|
||||
},
|
||||
"tailwindCSS.classFunctions": ["cn", "cva"],
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["class:\\s*?[\"'`]([^\"'`]*).*?,"],
|
||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||
],
|
||||
"tailwindCSS.experimental.configFile": "src/styles/globals.css",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"[typescript]": {
|
||||
"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": {
|
||||
"*.css": "tailwindcss"
|
||||
},
|
||||
"files.readonlyInclude": {
|
||||
"**/routeTree.gen.ts": true,
|
||||
"pnpm-lock.yaml": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/routeTree.gen.ts": true,
|
||||
"locales/**.po": true,
|
||||
"pnpm-lock.yaml": true
|
||||
},
|
||||
"i18n-ally.enabledParsers": ["po"],
|
||||
"i18n-ally.localesPaths": ["locales"],
|
||||
"i18n-ally.sourceLanguage": "en-US",
|
||||
"oxc.enable.oxfmt": true,
|
||||
"oxc.enable.oxlint": true,
|
||||
"oxc.configPath": "./vite.config.ts",
|
||||
"oxc.fmt.configPath": "./vite.config.ts",
|
||||
"search.exclude": {
|
||||
"**/routeTree.gen.ts": true,
|
||||
"locales/**.po": true,
|
||||
"pnpm-lock.yaml": true
|
||||
},
|
||||
"tailwindCSS.classFunctions": ["cn", "cva"],
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["class:\\s*?[\"'`]([^\"'`]*).*?,"],
|
||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||
],
|
||||
"tailwindCSS.experimental.configFile": "src/styles/globals.css",
|
||||
"typescript.experimental.useTsgo": true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
## 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.
|
||||
|
||||
This project uses [Vite+](https://vite.dev/blog/announcing-viteplus), a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. All modules should be imported from the `vite-plus` dependency (e.g., `import { defineConfig } from 'vite-plus'` or `import { expect, test, vi } from 'vite-plus/test'`).
|
||||
|
||||
## Key Libraries
|
||||
|
||||
| Area | Library | Docs |
|
||||
| -------------------- | ------------------------------------------------------------------------ | ---------------------------------- |
|
||||
| Frontend framework | React | https://react.dev |
|
||||
| Full-stack framework | TanStack Start | https://tanstack.com/start/latest |
|
||||
| Router | TanStack React Router | https://tanstack.com/router/latest |
|
||||
| Server state | TanStack React Query | https://tanstack.com/query/latest |
|
||||
| Client state | Zustand (+ Zundo for undo/redo, Immer for immutable updates) | https://zustand.docs.pmnd.rs |
|
||||
| Type-safe API | oRPC | https://orpc.unnoq.com |
|
||||
| Database ORM | Drizzle ORM (PostgreSQL) | https://orm.drizzle.team |
|
||||
| Authentication | Better Auth (+ Drizzle adapter, OAuth provider, API keys, 2FA, Passkeys) | https://www.better-auth.com |
|
||||
| Styling | Tailwind CSS | https://tailwindcss.com |
|
||||
| UI Components | shadcn/ui (built on Base UI) | https://ui.shadcn.com |
|
||||
| Icons | Phosphor Icons | https://phosphoricons.com |
|
||||
| Forms | React Hook Form (+ Zod resolvers) | https://react-hook-form.com |
|
||||
| Rich text editor | Tiptap | https://tiptap.dev |
|
||||
| Validation | Zod | https://zod.dev |
|
||||
| AI | Vercel AI SDK (OpenAI, Anthropic, Google, Ollama providers) | https://ai-sdk.dev |
|
||||
| MCP | Model Context Protocol SDK | https://modelcontextprotocol.io |
|
||||
| i18n | Lingui | https://lingui.dev |
|
||||
| Animations | Motion (Framer Motion) | https://motion.dev |
|
||||
| PDF export | Puppeteer Core (via Browserless) | https://pptr.dev |
|
||||
| Drag and drop | dnd-kit | https://dndkit.com |
|
||||
| Server engine | Nitro | https://nitro.build |
|
||||
| PWA | Vite PWA Plugin | https://vite-pwa-org.netlify.app |
|
||||
| Unused deps | Knip | https://knip.dev |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
components/ UI, resume, layout, animation, theme, locale components
|
||||
routes/ File-based routing (TanStack React Router)
|
||||
integrations/ Feature modules (auth, drizzle, orpc, ai, email, jobs, mcp, storage)
|
||||
schema/ Zod schemas for resume data validation
|
||||
utils/ Utility functions (locale, theme, env, resume processing)
|
||||
dialogs/ Modal/dialog components
|
||||
hooks/ Custom React hooks
|
||||
styles/ CSS and Tailwind configuration
|
||||
stores/ Zustand stores (resume, AI, dialog, command palette)
|
||||
migrations/ Drizzle database migrations
|
||||
locales/ Lingui i18n message catalogs (47+ locales)
|
||||
```
|
||||
|
||||
### Key Config Files
|
||||
|
||||
- `vite.config.ts` — Vite + Nitro + TanStack Start + PWA + Tailwind + Lingui
|
||||
- `drizzle.config.ts` — PostgreSQL dialect, schema at `./src/integrations/drizzle/schema.ts`
|
||||
- `tsconfig.json` — ES2022, strict mode, path alias `@/*` → `./src/*`
|
||||
- `lingui.config.ts` — i18n extraction and locale configuration
|
||||
- `components.json` — shadcn CLI configuration
|
||||
|
||||
### API Architecture
|
||||
|
||||
- **oRPC API** (`/api/rpc/*`) — Type-safe RPC with routers for: `ai`, `auth`, `resume`, `storage`, `printer`, `jobs`, `statistics`, `flags`. Three procedure types: `publicProcedure`, `protectedProcedure`, `serverOnlyProcedure`.
|
||||
- **Better Auth API** (`/api/auth/*`) — OAuth, session management, social provider callbacks.
|
||||
- **MCP Server** (`/mcp/`) — Model Context Protocol with OAuth Bearer tokens and API key auth. Exposes resumes as resources and tools for resume CRUD.
|
||||
|
||||
## Infrastructure Services
|
||||
|
||||
Before running the dev server, Docker must be running with at least PostgreSQL. Start services via `compose.dev.yml`:
|
||||
|
||||
```bash
|
||||
sudo dockerd &>/var/log/dockerd.log &
|
||||
sudo docker compose -f compose.dev.yml up -d postgres browserless
|
||||
```
|
||||
|
||||
- **PostgreSQL** (port 5432) — required. The app auto-runs Drizzle migrations on startup via a Nitro plugin.
|
||||
- **Browserless** (port 4000) — required for PDF export. Maps container port 3000 to host port 4000.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Copy `.env.example` to `.env` if not present. Key notes for local dev:
|
||||
|
||||
- `APP_URL` — local dev server origin on port 3000.
|
||||
- `PRINTER_APP_URL` — must use the Docker bridge gateway IP (not localhost) so the Browserless container can reach the app on the host. Get the IP with: `sudo docker network inspect reactive_resume_default --format '{{range .IPAM.Config}}{{.Gateway}}{{end}}'`
|
||||
- `PRINTER_ENDPOINT` — websocket URL to Browserless on host port 4000 with token `1234567890`.
|
||||
- `DATABASE_URL` — PostgreSQL connection using `postgres:postgres` credentials on localhost:5432.
|
||||
- S3/Storage and SMTP vars can be left empty — the app falls back to local filesystem and console-logged emails.
|
||||
|
||||
## Common Commands
|
||||
|
||||
`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` |
|
||||
|
||||
## Vite+ Pitfalls
|
||||
|
||||
- **Do not use pnpm/npm/yarn directly** for package operations — use `vp add`, `vp remove`, `vp install`, etc.
|
||||
- **Do not run `vp vitest` or `vp oxlint`** — they don't exist. Use `vp test` and `vp lint`.
|
||||
- **Do not install Vitest, Oxlint, Oxfmt, or tsdown directly** — Vite+ bundles them.
|
||||
- **Import from `vite-plus`**, not from `vite` or `vitest` directly (e.g., `import { defineConfig } from 'vite-plus'`).
|
||||
- **Vite+ commands take precedence** over `package.json` scripts. If there's a naming conflict, use `vp run <script>`.
|
||||
- **Use `vp dlx`** instead of `npx` or `pnpm dlx`.
|
||||
- **Type-aware linting** works out of the box with `vp lint --type-aware` — no need to install `oxlint-tsgolint`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- The Docker daemon needs `fuse-overlayfs` storage driver and `iptables-legacy` in the cloud VM (nested container environment).
|
||||
- `pnpm.onlyBuiltDependencies` in `package.json` controls which packages are allowed to run install scripts — no interactive `pnpm approve-builds` needed.
|
||||
- Email verification is optional in dev — after signup, click "Continue" to skip.
|
||||
- Vite and Nitro use beta/nightly builds. Occasional upstream issues may occur.
|
||||
|
||||
## Review Checklist for Agents
|
||||
|
||||
- [ ] Run `vp install` after pulling remote changes and before getting started.
|
||||
- [ ] Run `pnpm lint:fix`, `pnpm fmt:fix`, `pnpm typecheck` and `vp test` to validate changes.
|
||||
@@ -1,123 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Reactive Resume is a free, open-source resume builder built with TanStack Start (React 19 + Vite), using ORPC for type-safe RPC APIs, Drizzle ORM with PostgreSQL, and Better Auth for authentication.
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Start development server (runs on port 3000)
|
||||
pnpm dev
|
||||
|
||||
# Build for production
|
||||
pnpm build
|
||||
|
||||
# Start production server
|
||||
pnpm start
|
||||
|
||||
# Linting (uses Biome)
|
||||
pnpm lint
|
||||
|
||||
# Type checking
|
||||
pnpm typecheck
|
||||
|
||||
# Database operations
|
||||
pnpm db:generate # Generate migration files
|
||||
pnpm db:migrate # Run migrations
|
||||
pnpm db:push # Push schema changes directly
|
||||
pnpm db:studio # Open Drizzle Studio
|
||||
|
||||
# Extract i18n strings for translation
|
||||
pnpm lingui:extract
|
||||
|
||||
# Find unused exports
|
||||
pnpm knip
|
||||
```
|
||||
|
||||
## Local Development Setup
|
||||
|
||||
1. Copy `.env.example` to `.env` and configure environment variables
|
||||
2. Start required services: `docker compose -f compose.dev.yml up -d`
|
||||
- PostgreSQL (port 5432)
|
||||
- Browserless/Chromium for PDF generation (port 4000)
|
||||
- SeaweedFS for S3-compatible storage (port 8333)
|
||||
- Mailpit for email testing (ports 1025, 8025)
|
||||
- Adminer for DB management (port 8080)
|
||||
3. Run `pnpm dev`
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
|
||||
- `src/routes/` - TanStack Router file-based routing
|
||||
- `src/integrations/` - External service integrations (auth, database, ORPC, AI, email)
|
||||
- `src/integrations/orpc/router` - oRPC server routers
|
||||
- `src/integrations/orpc/services` - oRPC server services
|
||||
- `src/components/` - React components organized by feature
|
||||
- `src/schema/` - Zod schemas for validation
|
||||
- `plugins/` - Nitro server plugins (eg. auto-migration on startup)
|
||||
- `migrations/` - Drizzle database migrations
|
||||
- `locales/` - i18n translation files (managed by Lingui)
|
||||
|
||||
### Key Integrations (`src/integrations/`)
|
||||
|
||||
- **auth/** - Better Auth configuration and client
|
||||
- **drizzle/** - Database schema and client (PostgreSQL)
|
||||
- **orpc/** - Type-safe RPC router with procedures for ai, auth, flags, printer, resume, statistics, storage
|
||||
- **query/** - TanStack Query client configuration
|
||||
- **ai/** - AI provider integrations (OpenAI, Anthropic, Google Gemini, Ollama)
|
||||
|
||||
### Resume Data Model
|
||||
|
||||
The resume schema is defined in `src/schema/resume/data.ts`. Key concepts:
|
||||
- **ResumeData** - Complete resume data including basics, sections, customSections, metadata
|
||||
- **Sections** - Built-in sections (profiles, experience, education, skills, etc.)
|
||||
- **CustomSections** - User-created sections that follow one of the built-in section types
|
||||
- **Metadata** - Template, layout, typography, design settings, custom CSS
|
||||
|
||||
### Resume Templates
|
||||
|
||||
Templates are React components in `src/components/resume/templates/`. Each template (azurill, bronzor, chikorita, etc.) renders the resume data with different visual styles. Templates use shared components from `src/components/resume/shared/`.
|
||||
|
||||
### Database Schema
|
||||
|
||||
Defined in `src/integrations/drizzle/schema.ts`:
|
||||
- `user`, `session`, `account`, `verification`, `twoFactor`, `passkey`, `apikey` - Better Auth tables
|
||||
- `resume` - Stores Resume Data as JSONB (defined in `src/schema/resume/data.ts`)
|
||||
- `resumeStatistics` - Views/Download for Resume Tracking
|
||||
|
||||
### Routing
|
||||
|
||||
Uses TanStack Router with file-based routing. Key routes:
|
||||
- `/_home/` - Public landing pages
|
||||
- `/auth/` - Authentication flows
|
||||
- `/dashboard/` - User dashboard and resume management
|
||||
- `/builder/$resumeId/` - Resume editor
|
||||
- `/printer/$resumeId/` - PDF rendering endpoint
|
||||
- `/api/` - Public API endpoints
|
||||
|
||||
### State Management
|
||||
|
||||
- **Zustand** - Client-side state (resume editor state in `src/components/resume/store/`)
|
||||
- **TanStack Query** - Server state and caching (configured via ORPC integration)
|
||||
|
||||
## Code Style
|
||||
|
||||
- Uses Biome for linting and formatting
|
||||
- Tab indentation, double quotes, 120 character line width
|
||||
- Path alias: `@/` maps to `src/`
|
||||
- Tailwind CSS v4 with sorted class names (enforced by Biome)
|
||||
- Uses `cn()` utility for conditional class names
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Key variables (see `.env.example` for full list):
|
||||
- `APP_URL` - Application URL
|
||||
- `DATABASE_URL` - PostgreSQL connection string
|
||||
- `AUTH_SECRET` - Secret for authentication
|
||||
- `PRINTER_ENDPOINT` - WebSocket endpoint for PDF printer service
|
||||
- `S3_*` - S3-compatible storage configuration
|
||||
- `FLAG_*` - Feature flags
|
||||
@@ -1,15 +1,3 @@
|
||||
<div align="center" markdown="1">
|
||||
<a href="https://go.warp.dev/Reactive-Resume">
|
||||
<img alt="Warp Sponsorship" width="400" src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-03.png?raw=true" />
|
||||
</a>
|
||||
|
||||
### [Warp, built for coding with multiple AI agents.](https://go.warp.dev/Reactive-Resume)
|
||||
|
||||
[Available for MacOS, Linux, & Windows](https://go.warp.dev/Reactive-Resume)
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<a href="https://rxresu.me">
|
||||
<img src="public/opengraph/banner.jpg" alt="Reactive Resume" />
|
||||
@@ -26,15 +14,15 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<img src="https://img.shields.io/github/stars/amruthpillai/Reactive-Resume?style=flat-square" alt="Stars" />
|
||||
<img src="https://img.shields.io/github/package-json/v/amruthpillai/reactive-resume?style=flat-square" alt="Reactive Resume version">
|
||||
<img src="https://img.shields.io/github/stars/amruthpillai/Reactive-Resume?style=flat-square" alt="GitHub Stars">
|
||||
<img src="https://img.shields.io/github/license/amruthpillai/Reactive-Resume?style=flat-square" alt="License" />
|
||||
<img src="https://img.shields.io/docker/pulls/amruthpillai/reactive-resume?style=flat-square" alt="Docker Pulls" />
|
||||
<a href="https://discord.gg/EE8yFqW4"><img src="https://img.shields.io/discord/1173518977851473940?label=Discord&style=flat-square&logo=discord" alt="Discord" /></a>
|
||||
<a href="https://discord.gg/aSyA5ZSxpb"><img src="https://img.shields.io/discord/1173518977851473940?style=flat-square&label=discord" alt="Discord" /></a>
|
||||
<a href="https://crowdin.com/project/reactive-resume"><img src="https://badges.crowdin.net/reactive-resume/localized.svg?style=flat-square" alt="Crowdin" /></a>
|
||||
<a href="https://opencollective.com/reactive-resume"><img src="https://img.shields.io/opencollective/all/reactive-resume?style=flat-square" alt="Open Collective" /></a>
|
||||
<a href="https://github.com/sponsors/AmruthPillai"><img src="https://img.shields.io/github/sponsors/AmruthPillai?style=flat-square&label=sponsors" alt="Sponsors" /></a>
|
||||
<a href="https://opencollective.com/reactive-resume"><img src="https://img.shields.io/opencollective/backers/reactive-resume?style=flat-square&label=donations" alt="Donations" /></a>
|
||||
</p>
|
||||
|
||||
<a href="https://www.producthunt.com/products/reactive-resume?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-reactive-resume-v5-2" target="_blank" rel="noopener"><img alt="Reactive Resume v5 - A free and open-source resume builder. | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1065182&theme=light&t=1768850745585"></a>
|
||||
</div>
|
||||
|
||||
---
|
||||
@@ -157,33 +145,35 @@ docker compose up -d
|
||||
open http://localhost:3000
|
||||
```
|
||||
|
||||
[](https://app.ona.com/#https://github.com/amruthpillai/reactive-resume)
|
||||
|
||||
For detailed setup instructions, environment configuration, and self-hosting guides, see the [documentation](https://docs.rxresu.me).
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Category | Technology |
|
||||
| ---------------- | ------------------------------------ |
|
||||
| Framework | TanStack Start (React 19, Vite) |
|
||||
| Runtime | Node.js |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL with Drizzle ORM |
|
||||
| API | ORPC (Type-safe RPC) |
|
||||
| Auth | Better Auth |
|
||||
| Styling | Tailwind CSS |
|
||||
| UI Components | Radix UI |
|
||||
| State Management | Zustand + TanStack Query |
|
||||
| Category | Technology |
|
||||
| ---------------- | ------------------------------- |
|
||||
| Framework | TanStack Start (React 19, Vite) |
|
||||
| Runtime | Node.js |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL with Drizzle ORM |
|
||||
| API | ORPC (Type-safe RPC) |
|
||||
| Auth | Better Auth |
|
||||
| Styling | Tailwind CSS |
|
||||
| UI Components | Radix UI |
|
||||
| State Management | Zustand + TanStack Query |
|
||||
|
||||
## Documentation
|
||||
|
||||
Comprehensive guides are available at [docs.rxresu.me](https://docs.rxresu.me):
|
||||
|
||||
| Guide | Description |
|
||||
| --------------------------------------------------------------------------- | --------------------------------- |
|
||||
| [Getting Started](https://docs.rxresu.me/getting-started) | First-time setup and basic usage |
|
||||
| [Self-Hosting](https://docs.rxresu.me/self-hosting/docker) | Deploy on your own server |
|
||||
| [Development Setup](https://docs.rxresu.me/contributing/development) | Local development environment |
|
||||
| [Project Architecture](https://docs.rxresu.me/contributing/architecture) | Codebase structure and patterns |
|
||||
| [Exporting Your Resume](https://docs.rxresu.me/guides/exporting-your-resume)| PDF and JSON export options |
|
||||
| Guide | Description |
|
||||
| ---------------------------------------------------------------------------- | -------------------------------- |
|
||||
| [Getting Started](https://docs.rxresu.me/getting-started) | First-time setup and basic usage |
|
||||
| [Self-Hosting](https://docs.rxresu.me/self-hosting/docker) | Deploy on your own server |
|
||||
| [Development Setup](https://docs.rxresu.me/contributing/development) | Local development environment |
|
||||
| [Project Architecture](https://docs.rxresu.me/contributing/architecture) | Codebase structure and patterns |
|
||||
| [Exporting Your Resume](https://docs.rxresu.me/guides/exporting-your-resume) | PDF and JSON export options |
|
||||
|
||||
## Self-Hosting
|
||||
|
||||
@@ -203,7 +193,7 @@ docker pull amruthpillai/reactive-resume:latest
|
||||
docker pull ghcr.io/amruthpillai/reactive-resume:latest
|
||||
```
|
||||
|
||||
See the [self-hosting guide](https://docs.rxresu.me/guides/self-hosting-with-docker) for complete instructions.
|
||||
See the [self-hosting guide](https://docs.rxresu.me/self-hosting/docker) for complete instructions.
|
||||
|
||||
## Support
|
||||
|
||||
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
{
|
||||
"$schema": "node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "main"
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
"includes": [
|
||||
"**",
|
||||
"!**/webfontlist.json",
|
||||
"!**/schema/schema.json",
|
||||
"!**/docs/spec.json",
|
||||
"!**/src/routeTree.gen.ts"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"lineWidth": 120,
|
||||
"indentStyle": "tab"
|
||||
},
|
||||
"assist": {
|
||||
"actions": {
|
||||
"recommended": true,
|
||||
"source": {
|
||||
"organizeImports": "on",
|
||||
"useSortedProperties": "on"
|
||||
}
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"rules": {
|
||||
"a11y": "off",
|
||||
"recommended": true,
|
||||
"suspicious": {
|
||||
"noArrayIndexKey": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnknownTypeSelector": "off",
|
||||
"noUnusedImports": {
|
||||
"fix": "safe",
|
||||
"level": "error",
|
||||
"options": {}
|
||||
}
|
||||
},
|
||||
"nursery": {
|
||||
"useSortedClasses": {
|
||||
"fix": "safe",
|
||||
"level": "warn",
|
||||
"options": {
|
||||
"functions": ["cn", "cva"],
|
||||
"attributes": ["class", "className"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
},
|
||||
"css": {
|
||||
"parser": {
|
||||
"tailwindDirectives": true
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
-21
@@ -1,23 +1,26 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "radix-vega",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/styles.css",
|
||||
"baseColor": "zinc",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "phosphor",
|
||||
"menuColor": "default",
|
||||
"menuAccent": "subtle",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/utils/style",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/utils",
|
||||
"hooks": "@/hooks"
|
||||
}
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "base-vega",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/styles.css",
|
||||
"baseColor": "zinc",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "phosphor",
|
||||
"menuColor": "default",
|
||||
"menuAccent": "bold",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/utils/style",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/utils",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {
|
||||
"@animate-ui": "https://animate-ui.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
|
||||
+12
-13
@@ -20,9 +20,9 @@ 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:
|
||||
@@ -40,24 +40,23 @@ services:
|
||||
ports:
|
||||
- "4000:3000"
|
||||
environment:
|
||||
- QUEUED=30
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=20
|
||||
- CONCURRENT=5
|
||||
- TOKEN=1234567890
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# As an alternative to browserless, you can also use a lightweight image like chromedp/headless-shell:latest
|
||||
# See https://docs.rxresu.me/self-hosting/docker#alternative-printer-options for more information.
|
||||
# chrome:
|
||||
# image: chromedp/headless-shell:latest
|
||||
# restart: unless-stopped
|
||||
# ports:
|
||||
# - "9222:9222"
|
||||
chrome:
|
||||
image: chromedp/headless-shell:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9222:9222"
|
||||
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
@@ -77,7 +76,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
seaweedfs-create-bucket:
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
restart: on-failure
|
||||
entrypoint: >
|
||||
|
||||
+10
-12
@@ -5,9 +5,9 @@ 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
|
||||
healthcheck:
|
||||
@@ -21,14 +21,12 @@ services:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- QUEUED=30
|
||||
- TIMEOUT=-1
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=20
|
||||
- CONCURRENT=5
|
||||
- TOKEN=1234567890
|
||||
healthcheck:
|
||||
test:
|
||||
["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
@@ -57,7 +55,7 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
seaweedfs-create-bucket:
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
restart: on-failure
|
||||
entrypoint: >
|
||||
@@ -71,7 +69,7 @@ services:
|
||||
seaweedfs:
|
||||
condition: service_healthy
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
# image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
environment:
|
||||
@@ -79,7 +77,7 @@ services:
|
||||
- TZ=Etc/UTC
|
||||
- NODE_ENV=production
|
||||
- APP_URL=http://localhost:3000
|
||||
- PRINTER_APP_URL=http://app:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
# Printer
|
||||
- PRINTER_ENDPOINT=ws://browserless:3000?token=1234567890
|
||||
# - PRINTER_ENDPOINT=http://chrome:9222 # Or, if you're using chromedp/headless-shell
|
||||
@@ -102,7 +100,7 @@ services:
|
||||
condition: service_healthy
|
||||
browserless:
|
||||
condition: service_healthy
|
||||
seaweedfs-create-bucket:
|
||||
seaweedfs_create_bucket:
|
||||
condition: service_completed_successfully
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
|
||||
+7
-5
@@ -1,10 +1,12 @@
|
||||
preserve_hierarchy: true
|
||||
api_token_env: CROWDIN_API_TOKEN
|
||||
project_id_env: CROWDIN_PROJECT_ID
|
||||
commit_message: "[ci skip]"
|
||||
|
||||
pull_request_title: "Sync Translations from Crowdin"
|
||||
pull_request_labels: ["l10n"]
|
||||
pull_request_reviewers: ["amruthpillai"]
|
||||
|
||||
pull_request_title: Sync Translations from Crowdin
|
||||
pull_request_labels:
|
||||
- l10n
|
||||
pull_request_reviewers:
|
||||
- amruthpillai
|
||||
files:
|
||||
- source: /locales/en-US.po
|
||||
translation: /locales/%locale%.%file_extension%
|
||||
|
||||
+254
-40
@@ -4,55 +4,269 @@ description: "List of all notable changes and updates to Reactive Resume"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.0.15" description="1st April 2026">
|
||||
## Features & Improvements
|
||||
- Added **LinkedIn sign-in** support for self-hosted instances, including sign-in on the auth page and account linking in settings.
|
||||
- Improved the sign-in experience by showing loading placeholders while social login providers are being fetched.
|
||||
- Resume builder panel sizes are now persisted more reliably, making the layout feel more consistent between sessions.
|
||||
- Added clearer labels for resume sorting and filtering controls on the dashboard.
|
||||
|
||||
## Fixes
|
||||
- Improved autosave reliability in the resume builder:
|
||||
- Unsaved edits are flushed before page unload.
|
||||
- You now get a clear persistent warning if changes fail to save (for example, due to network issues).
|
||||
- Strengthened authentication defaults by increasing the minimum password length requirement to 8 characters.
|
||||
- Improved reliability around resume deletion and server-side error handling for create/update operations.
|
||||
- Fixed a rare language mix-up during printing/export when multiple print jobs run at the same time, so each PDF/screenshot now reliably uses the correct locale.
|
||||
- Improved print stability under heavy usage by reusing in-progress PDF and screenshot generation requests for the same resume instead of starting duplicate jobs.
|
||||
- Hardened printer token signing and token/session/API-key verification paths for better security.
|
||||
- Improved resilience in AI tailoring output parsing and JSON Resume import normalization.
|
||||
- Printer service now provides descriptive error messages ("Failed to generate PDF" / "Failed to capture screenshot") instead of generic internal server errors.
|
||||
- Job descriptions in the job detail sheet now render HTML formatting (headings, lists, bold text) instead of showing raw tags.
|
||||
- Fixed the "Tailor Resume" button being incorrectly disabled when AI is not configured — the dialog already supports plain resume duplication as a fallback.
|
||||
- Fixed the tailor resume flow to open the resume builder in a new tab while keeping the job listing open for reference, instead of navigating away and auto-opening the application page.
|
||||
- Apply option links in the job detail sheet are now validated before rendering, preventing malformed or potentially unsafe URLs.
|
||||
- Improved job card loading skeletons to match the actual card layout (logo, title, badges) for a smoother loading experience.
|
||||
- Centered the "Configure Job Search" empty state on the page.
|
||||
- Extracted the `JobCard` component into its own file for better code organization.
|
||||
|
||||
## Docs & Localization
|
||||
- Updated self-hosting and environment variable documentation to include LinkedIn OAuth configuration.
|
||||
- Synced translations across locales for the latest authentication and save-status messaging.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.14" description="24th March 2026">
|
||||
## Features
|
||||
- Implemented OAuth 2.1 support for MCP authentication, thanks to @5queezer. [#2829](https://github.com/amruthpillai/reactive-resume/pull/2829)
|
||||
- Migrated from using Biome to Oxlint/Oxfmt for linting and formatting, with a new autofix CI workflow. [#2822](https://github.com/amruthpillai/reactive-resume/pull/2822)
|
||||
|
||||
## Fixes
|
||||
- Fixed an issue with the slider component for "Sidebar Width" in the resume builder. [#2830](https://github.com/amruthpillai/reactive-resume/issues/2830)
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.13" description="19th March 2026">
|
||||
## Features & Improvements
|
||||
- Added **Job Listings with AI-Powered Resume Tailoring**, a major new feature that integrates job search (via JSearch API) directly into the dashboard with AI-driven resume tailoring to match job descriptions. [#2788](https://github.com/amruthpillai/reactive-resume/pull/2788)
|
||||
- Search for jobs with filters for location (city, state, country), employment type, remote status, and more.
|
||||
- View detailed job listings in a slide-out panel with apply links and tags.
|
||||
- Tailor your resume to a specific job posting using AI — rewrites skills, experience, and references for optimal fit.
|
||||
- Per-user API quota management with usage tracking in settings.
|
||||
- Pagination with 30 results per page.
|
||||
- Added **DOCX resume export** — generate and download tailored resumes as Word documents. [#2788](https://github.com/amruthpillai/reactive-resume/pull/2788)
|
||||
- Migrated the entire project toolchain to **Vite+** (`vp` CLI), unifying Vite, Vitest, Oxlint, and Oxfmt under a single developer experience.
|
||||
- Migrated from **Biome to Oxlint/Oxfmt** for linting and formatting, with a new autofix CI workflow. [#2822](https://github.com/amruthpillai/reactive-resume/pull/2822)
|
||||
- Added **Slovenian** locale support.
|
||||
- Added a new `Progress` UI component.
|
||||
|
||||
## Fixes
|
||||
- Fixed type issues in auth client and configuration.
|
||||
- Used RTL-friendly CSS classes across the app for improved bidirectional layout support.
|
||||
- Removed dead code and unused imports.
|
||||
- Updated route tree generation and added `routeTree.gen.ts` to `.gitignore`.
|
||||
- Improved animation performance with `willChange` styles on animation components.
|
||||
|
||||
## Maintenance & Dependencies
|
||||
- Updated dependencies across the board, including `@typescript/native-preview`, `vite-plus`, and others.
|
||||
- Added database migrations for missing indexes.
|
||||
- Synced translations from Crowdin. [#2820](https://github.com/amruthpillai/reactive-resume/pull/2820), [#2824](https://github.com/amruthpillai/reactive-resume/pull/2824)
|
||||
- Updated French translation for 'Volunteer' to 'Bénévolat'. [#2823](https://github.com/amruthpillai/reactive-resume/pull/2823)
|
||||
- Multiple translation and language updates across all locales.
|
||||
|
||||
## New Contributors
|
||||
- @lukadfagundes
|
||||
- @Hephaisto-dev
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.12" description="17th March 2026">
|
||||
## Features & Improvements
|
||||
- Refactored the component system to `@base-ui/react` across the app for standardized UI behavior (dialogs, menus, accordions, comboboxes, switches, etc.).
|
||||
- Improved auth infrastructure with Better Auth dashboard/sentinel integration and expanded IP/header handling for proxied deployments.
|
||||
- Added a dedicated `src/server.ts` server entry using `FastResponse` for better runtime compatibility.
|
||||
- Sidebar button list now scrolls internally on overflow.
|
||||
|
||||
## Fixes
|
||||
- Printer endpoint now resolves to IP when using chromedp, improving compatibility with proxied deployments.
|
||||
- Switched OpenAI integration to `chat/completions` for resume import and AI features.
|
||||
- AI PDF/DOCX resume parsing is now more robust via direct file send (multimodal input).
|
||||
- Resume import normalization hardened: section defaults merged, required fields enforced, missing IDs/flags autogenerated.
|
||||
- Fixed UI migration regressions (accordion behavior, button interaction, right sidebar scrollbar).
|
||||
- Fixed multi-page PDF crashes and Gemini API ingestion errors.
|
||||
- Fixed role title styling, role spacing, and issues with skills item rendering and template headings.
|
||||
|
||||
## Maintenance & Dependencies
|
||||
- Updated dependencies and lockfile.
|
||||
- Synced translation strings across locales, including multiple contributions from Crowdin.
|
||||
- General dependency and code style improvements, plus removal of redundant indexes and plugins.
|
||||
|
||||
## New Contributors
|
||||
- @yang-summer
|
||||
- @FrancocDev
|
||||
- @obitton
|
||||
- @jondycz
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.10" description="24th February 2026">
|
||||
## Fixes
|
||||
- Show section titles for summary-type custom sections in the resume builder. [#2744](https://github.com/amruthpillai/reactive-resume/pull/2744)
|
||||
- Prevent browser password managers and Edge autofill/save prompts from appearing on AI settings API key fields. [#2719](https://github.com/amruthpillai/reactive-resume/pull/2719)
|
||||
- Replace deprecated Tailwind CSS classes: use `inset-s-*`/`inset-e-*` instead of `start-*`/`end-*`.
|
||||
- Fix PDF downloader to work correctly in offline mode. [#2743](https://github.com/amruthpillai/reactive-resume/pull/2743)
|
||||
- Make bold formatting visible for `<strong>` in the resume rich text editor (uses plain `font-weight: bold` fallback if the CSS variable is unset). Fixes [#2730](https://github.com/amruthpillai/reactive-resume/issues/2730)
|
||||
- Prevent credentials sign-in from dropping `session_token` Set-Cookie, improves login reliability. [#2718](https://github.com/amruthpillai/reactive-resume/pull/2718)
|
||||
- Remove redundant `resume-` prefix from download filename; add spacing between pages in shared view. [#2709](https://github.com/amruthpillai/reactive-resume/pull/2709)
|
||||
- Normalize autocomplete tokens for login and register forms. [#2714](https://github.com/amruthpillai/reactive-resume/pull/2714)
|
||||
- Fix improper chips reordering and update dependency/translations. [#2711](https://github.com/amruthpillai/reactive-resume/issues/2711)
|
||||
- Fix clipping of heading in Lapras resume template.
|
||||
- Remove error-causing plugins in oRPC integration.
|
||||
- Remove duplicate database indexes; add index for `created_at` on user and resume tables.
|
||||
|
||||
## Features & Improvements
|
||||
- Added feature flag: `FLAG_DISABLE_IMAGE_PROCESSING` (allows disabling image processing site-wide).
|
||||
- Updated Discord invite link in the app and documentation.
|
||||
- Added comprehensive codebase and architecture documentation in `CLAUDE.md`.
|
||||
- Synced latest translations from Crowdin (multiple language updates and additions).
|
||||
|
||||
## Maintenance & Other
|
||||
- Refactored auth utility from `originWith` to `withHostname` to preserve localhost/127.0.0.1 sibling trust.
|
||||
- Updated dependencies and improved code style.
|
||||
- Updated documentation and README files.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.9" description="9th February 2026">
|
||||
## Features & Improvements
|
||||
- Added Computer Modern web fonts to the font selector, allowing users to choose from LaTeX-style "Computer Modern" fonts for a classic scientific look.
|
||||
- Updated dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.8" description="9th February 2026">
|
||||
## Fixes
|
||||
- Removed Passkey support from the authentication system due to provider compatibility issues.
|
||||
- Updated dependencies to the latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.7" description="9th February 2026">
|
||||
## Features & Improvements
|
||||
- Introduced a new **MCP (Model Context Protocol) server** for managing and editing resumes from MCP-compatible AI tools (Claude Desktop, Cursor, Codex, and others). Supports listing, reading, creating, deleting, locking/unlocking, and patching resumes via natural language. [(guide)](/guides/using-the-mcp-server)
|
||||
- Added an **AI Chat** panel to the resume builder, allowing conversational resume modifications through tool-calling (JSON Patch operations applied to the resume in real time with visual feedback).
|
||||
- Added a system prompt and `patch_resume` tool for structured, minimal-diff resume edits via RFC 6902 JSON Patch.
|
||||
- Chat history is now persisted per resume in localStorage.
|
||||
|
||||
## Fixes
|
||||
- Fixed rendering issues in the Lapras and Onyx resume templates.
|
||||
- Improved Combobox and ScrollArea UI components.
|
||||
- Fixed skills item rendering in shared resume components.
|
||||
- Updated authentication configuration and route handling.
|
||||
- Updated JSON Schema to match the latest resume data model.
|
||||
|
||||
## Maintenance
|
||||
- Updated dependencies to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.6" description="8th February 2026">
|
||||
## Features & Improvements
|
||||
- Implemented Atomic Resume Patching API for fine-grained, partial updates to resumes via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692)
|
||||
- API endpoint `PUT /resume/{id}` now returns the updated resume object rather than void, with explicit DTOs for input/output. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688)
|
||||
- Added error logging for API server errors (server-side only) to improve debugging and reliability.
|
||||
- Refactored and cleaned up imports/exports for clarity and maintainability.
|
||||
- Added `.devcontainer` configuration for improved contributor development environment.
|
||||
- Added build status badge and documentation link to README.
|
||||
|
||||
## Fixes
|
||||
- General bug fixes and minor improvements.
|
||||
- Synced latest translations from Crowdin (notably: French, other languages).
|
||||
- Updated dependencies to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.5" description="31st January 2026">
|
||||
## Features & Improvements
|
||||
- Implemented Cover Letter functionality as a custom section in the resume builder. [(link)](/guides/adding-a-cover-letter)
|
||||
- Added full-screen mode to the rich text editor for distraction-free writing.
|
||||
- Introduced a new custom section type: `summary`, enabling users to add extra summary sections.
|
||||
- Added `useFormBlocker` hook to prevent dialogs from being closed with unsaved form changes.
|
||||
|
||||
## Fixes
|
||||
- Fixed keyword spacing issue in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631)
|
||||
- Fixed AI connection test and improved error reporting for AI provider issues.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.4" description="28th January 2026">
|
||||
## Features & Improvements
|
||||
- Brought back Undo/Redo functionality in the resume builder for an improved editing experience.
|
||||
- Sidebar builder now arranges dynamically based on the section type in each template. [#2564](https://github.com/amruthpillai/reactive-resume/pull/2603)
|
||||
- Added PWA (Progressive Web App) support and updated PDF example file links in documentation.
|
||||
|
||||
## Fixes
|
||||
- Removed extra spacing when proficiency is empty. [#2607](https://github.com/amruthpillai/reactive-resume/pull/2626)
|
||||
- Fixed Pikachu template: header/page picture rendering, and respect for `fullWidth` property.
|
||||
- General template improvements for layout and rendering consistency.
|
||||
- Fixed GitHub OAuth login for users migrated from previous versions.
|
||||
- Improved printer service communication and reduced resource usage for more reliable PDF generation.
|
||||
- Fixed "empty" Git merge remnants in codebase.
|
||||
|
||||
## Maintenance
|
||||
- Synced translations from Crowdin (Afrikaans, Persian, Portuguese/Brazilian, others).
|
||||
- Updated translation sources and configuration.
|
||||
- Updated package dependencies and fixed self-hosting guide links in README.
|
||||
- Removed dead code and updated screenshots.
|
||||
- Other bug fixes and minor improvements (#2542, #2573, #2598).
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.3" description="25th January 2026">
|
||||
- Implement the ability to print Free-Form PDFs which do not have a fixed page height, allowing the user to fit the content on a page as they see fit. [(link)](/guides/selecting-page-format)
|
||||
- Allow the user to override the default endpoint for all AI providers, not just Ollama. Also display the default endpoint for each provider in the AI settings page.
|
||||
- Updated the chip input component, to allow the user to add, edit, remove or reorder keywords for skills and other sections.
|
||||
- Improved RTL support across the app, thanks to @obreo for the contribution. [(link)](https://github.com/amruthpillai/reactive-resume/pull/2583)
|
||||
- Updates to the translation configuration to remove line numbers from the translation files, as this was causing unnecessary diffs.
|
||||
- Updated the video on the homepage to be lighter and faster to load, while still maintaining the same quality.
|
||||
- Increased the screenshot TTL for resumes, to avoid regenerating screenshots unnecessarily unless the resume has been updated recently (in the last hour).
|
||||
- Update dependencies and translations to the latest versions.
|
||||
## Features & Improvements
|
||||
- Added support for printing Free-Form PDFs (no fixed page height), allowing for custom-fit page content. [(link)](/guides/selecting-page-format)
|
||||
- Allow overriding the default endpoint for all AI providers, not just Ollama; default endpoint now visible in AI settings.
|
||||
- Updated the chip input component: supports adding, editing, removing, and reordering keywords for skills and other sections.
|
||||
- Improved RTL support across the app, thanks to @obreo. [(link)](https://github.com/amruthpillai/reactive-resume/pull/2583)
|
||||
|
||||
## Fixes
|
||||
- Updated translation configuration to remove line numbers, reducing unnecessary diffs.
|
||||
- Homepage video optimized for faster loading without quality loss.
|
||||
- Increased screenshot TTL for resumes to reduce unnecessary regenerations.
|
||||
- Updated dependencies and translations to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.2" description="24th January 2026">
|
||||
- Added an agent skill `skills/resume-builder` for agentic AI assistants to build resumes for Reactive Resume through conversational AI.
|
||||
- Added a new guide on how to fit content on a page, to avoid issues when exporting to PDF. [(link)](/guides/fitting-content-on-a-page)
|
||||
- Display an alert when the content is too tall for a page, to help the user fit the content on a page.
|
||||
- Fix an issue with the Ditgar template, where the page was not respecting the `fullWidth` setting.
|
||||
- Updated the JSON Schema to conform to a proper format.
|
||||
- Updated the Discord Server invite link to a new one.
|
||||
- Updated dependencies to the latest versions.
|
||||
## Features & Improvements
|
||||
- Added an agent skill `skills/resume-builder` for agentic AI assistants, enabling resume construction through conversational AI.
|
||||
- Added a guide for how to fit content on a page when exporting to PDF. [(link)](/guides/fitting-content-on-a-page)
|
||||
- Alert user when content is too tall for a page for easier export troubleshooting.
|
||||
|
||||
## Fixes
|
||||
- Ditgar template now respects `fullWidth` setting.
|
||||
- Updated JSON Schema for proper format.
|
||||
- Updated Discord server invite link.
|
||||
- Updated dependencies to latest versions.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.1" description="23th January 2026">
|
||||
- Updated translations from Crowdin.
|
||||
- Added a Community Spotlight section to the documentation.
|
||||
- Remove `-r require-metadata` from the Dockerfile as it was not needed.
|
||||
- Fixed inconsistencies in the docker compose examples in the documentation.
|
||||
- Fixed an issue with usernames not allowing hyphens in them.
|
||||
- Fixed issues with the printer service, when using the `getResumeScreenshot` or `printResumeAsPDF` endpoints.
|
||||
## Maintenance & Fixes
|
||||
- Updated translations from Crowdin.
|
||||
- Added Community Spotlight section to the documentation.
|
||||
- Removed `-r require-metadata` from the Dockerfile (was not needed).
|
||||
- Fixed inconsistencies in Docker compose documentation examples.
|
||||
- Fixed username validation to allow hyphens.
|
||||
- Fixed issues with the printer service when using `getResumeScreenshot` or `printResumeAsPDF` endpoints.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.0.0" description="22th January 2026">
|
||||
This has been a major overhaul from the previous version of Reactive Resume. The app has been completely redesigned and rebuilt from scratch, to be more intuitive and user-friendly.
|
||||
This release is a major overhaul of Reactive Resume. The app has been completely redesigned and rebuilt from scratch for a more intuitive, user-friendly experience.
|
||||
|
||||
**Here are some of the key changes from the previous version:**
|
||||
- 2 new templates: _Ditgar_ and _Lapras_
|
||||
- Authentication via Passkeys
|
||||
- New user interface and refreshed design
|
||||
- API Access and Reference Documentation
|
||||
- AI Integration with OpenAI, Google, Anthropic, and Ollama
|
||||
- A better font selector, with real-time preview of the font
|
||||
- More comprehensive documentation, with guides for specific features
|
||||
- An improved templates gallery, with clear overview of each template
|
||||
- Ability to import resumes from PDF or DOCX (requires AI Integration)
|
||||
- Reliable server infrastructure, with improved performance and scalability
|
||||
- Ability to move items between sections and pages, for better organization
|
||||
- A more powerful CSS Editor, with better autocomplete and syntax highlighting
|
||||
- Extended Custom Sections to include a type, for better customization and organization
|
||||
- Ability to choose the kind of icons to display for level indicators (stars, circles, custom icons, etc.)
|
||||
- Ability to resize how wide/narrow the sidebar should be on the resume, or convert a page to be full width
|
||||
**Highlights:**
|
||||
- 2 new resume templates: _Ditgar_ and _Lapras_
|
||||
- Passkeys for authentication
|
||||
- Entirely new user interface and refreshed design
|
||||
- API access and reference docs
|
||||
- AI Integration with OpenAI, Google, Anthropic, and Ollama
|
||||
- Improved font selector with real-time preview
|
||||
- Expanded and improved documentation, with more how-to guides
|
||||
- Redesigned template gallery with detailed views
|
||||
- Resume import from PDF/DOCX (requires AI Integration)
|
||||
- Stronger infrastructure for better reliability and scalability
|
||||
- Drag-and-drop for moving items between sections and pages
|
||||
- Powerful new CSS Editor with autocompletion and highlighting
|
||||
- Custom sections now support typed extension for finer control
|
||||
- Icon style selector for indicators (stars, circles, custom, etc.)
|
||||
- Sidebar width adjustment and full-width page support
|
||||
|
||||
There's still a lot more that I'm forgetting, but I'm sure you'll find out soon enough as you explore the new version. I hope you enjoy building your resume now, better than ever. If you have any feedback, please feel free to [contact me](https://amruthpillai.com/#contact) or [open an issue](https://github.com/amruthpillai/reactive-resume/issues) on GitHub.
|
||||
</Update>
|
||||
And much more! Explore the new version and share your feedback via [contact](https://amruthpillai.com/#contact) or [GitHub issues](https://github.com/amruthpillai/reactive-resume/issues).
|
||||
</Update>
|
||||
|
||||
@@ -4,7 +4,9 @@ description: "A showcase of articles, videos, and social media posts from the Re
|
||||
---
|
||||
|
||||
<Note>
|
||||
Have you created something that makes use of Reactive Resume, or spread the word about it in your own way? I'd love to feature it here! Send me an email at [hello@amruthpillai.com](mailto:hello@amruthpillai.com) and I'll make sure to add it to this page.
|
||||
Have you created something that makes use of Reactive Resume, or spread the word about it in your own way? I'd love to
|
||||
feature it here! Send me an email at [hello@amruthpillai.com](mailto:hello@amruthpillai.com) and I'll make sure to add
|
||||
it to this page.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -16,16 +18,15 @@ A collection of blog posts and articles written by the community about Reactive
|
||||
<CardGroup cols={2}>
|
||||
{/* Add article cards here */}
|
||||
|
||||
{/* Example:
|
||||
{/\* Example:
|
||||
|
||||
<Card title="Article Title" icon="newspaper" href="https://example.com/article">
|
||||
A brief description of the article.
|
||||
</Card>
|
||||
*/}
|
||||
</CardGroup>
|
||||
|
||||
<Info>
|
||||
No articles have been featured yet. Be the first to contribute!
|
||||
</Info>
|
||||
<Info>No articles have been featured yet. Be the first to contribute!</Info>
|
||||
|
||||
---
|
||||
|
||||
@@ -36,16 +37,15 @@ Video tutorials, reviews, and walkthroughs created by the community.
|
||||
<CardGroup cols={2}>
|
||||
{/* Add video cards here */}
|
||||
|
||||
{/* Example:
|
||||
{/\* Example:
|
||||
|
||||
<Card title="Video Title" icon="youtube" href="https://youtube.com/watch?v=...">
|
||||
A brief description of the video.
|
||||
</Card>
|
||||
*/}
|
||||
</CardGroup>
|
||||
|
||||
<Info>
|
||||
No videos have been featured yet. Be the first to contribute!
|
||||
</Info>
|
||||
<Info>No videos have been featured yet. Be the first to contribute!</Info>
|
||||
|
||||
---
|
||||
|
||||
@@ -54,15 +54,14 @@ No videos have been featured yet. Be the first to contribute!
|
||||
Posts and threads from social media platforms sharing experiences with Reactive Resume.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
{/* Add social media cards here */}
|
||||
|
||||
{/* Example:
|
||||
{/* Add social media cards here */}
|
||||
|
||||
{/* Example:
|
||||
|
||||
<Card title="Post Title" icon="twitter" href="https://twitter.com/...">
|
||||
A brief description of the post.
|
||||
</Card>
|
||||
*/}
|
||||
</CardGroup>
|
||||
|
||||
<Info>
|
||||
No social media posts have been featured yet. Be the first to contribute!
|
||||
</Info>
|
||||
<Info>No social media posts have been featured yet. Be the first to contribute!</Info>
|
||||
|
||||
+101
-159
@@ -13,16 +13,12 @@ Reactive Resume is built with a modern, type-safe stack:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend" icon="browser">
|
||||
- **React 19** with TanStack Start
|
||||
- **TypeScript** for type safety
|
||||
- **Tailwind CSS** for styling
|
||||
- **Radix UI** for accessible components
|
||||
- **React 19** with TanStack Start - **TypeScript** for type safety - **Tailwind CSS** for styling - **Radix UI**
|
||||
for accessible components
|
||||
</Card>
|
||||
<Card title="Backend" icon="server">
|
||||
- **ORPC** for type-safe RPC
|
||||
- **Drizzle ORM** with PostgreSQL
|
||||
- **Better Auth** for authentication
|
||||
- **Sharp** for image processing
|
||||
- **ORPC** for type-safe RPC - **Drizzle ORM** with PostgreSQL - **Better Auth** for authentication - **Sharp** for
|
||||
image processing
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -70,89 +66,61 @@ flowchart TD
|
||||
|
||||
### Root Level
|
||||
|
||||
| Directory | Purpose |
|
||||
|-----------|---------|
|
||||
| `src/` | Main application source code |
|
||||
| `public/` | Static assets served directly |
|
||||
| `locales/` | Translation files (.po format) |
|
||||
| `migrations/` | Database migration files |
|
||||
| `docs/` | Mintlify documentation |
|
||||
| `data/` | Local data storage (fonts, uploads) |
|
||||
| `scripts/` | Utility scripts |
|
||||
| Directory | Purpose |
|
||||
| ------------- | ----------------------------------- |
|
||||
| `src/` | Main application source code |
|
||||
| `public/` | Static assets served directly |
|
||||
| `locales/` | Translation files (.po format) |
|
||||
| `migrations/` | Database migration files |
|
||||
| `docs/` | Mintlify documentation |
|
||||
| `data/` | Local data storage (fonts, uploads) |
|
||||
| `scripts/` | Utility scripts |
|
||||
|
||||
### Source Code (`src/`)
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="components/" icon="cube">
|
||||
Reusable React components organized by category:
|
||||
|
||||
- **`ui/`** — Base UI components (Button, Card, Dialog, etc.)
|
||||
- **`resume/`** — Resume-specific components (sections, templates)
|
||||
- **`input/`** — Form input components (ColorPicker, RichInput)
|
||||
- **`layout/`** — Layout components (Sidebar, LoadingScreen)
|
||||
- **`animation/`** — Animation components (Spotlight, TextMask)
|
||||
- **`theme/`** — Theme management components
|
||||
- **`typography/`** — Font management components
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="routes/" icon="route">
|
||||
File-based routing using TanStack Router:
|
||||
|
||||
- **`__root.tsx`** — Root layout with providers
|
||||
- **`_home/`** — Public home page routes
|
||||
- **`auth/`** — Authentication routes (login, register, etc.)
|
||||
- **`dashboard/`** — User dashboard routes
|
||||
- **`builder/`** — Resume builder routes (the main editor)
|
||||
- **`printer/`** — PDF printing route
|
||||
- **`api/`** — API routes
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="integrations/" icon="plug">
|
||||
Third-party service integrations:
|
||||
|
||||
- **`auth/`** — Better Auth client configuration
|
||||
- **`drizzle/`** — Database schema and utilities
|
||||
- **`orpc/`** — API router, client, and services
|
||||
- **`ai/`** — AI service integrations
|
||||
- **`import/`** — Resume import utilities
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="dialogs/" icon="window-maximize">
|
||||
Modal dialog components:
|
||||
|
||||
- **`auth/`** — Authentication dialogs
|
||||
- **`resume/`** — Resume management dialogs
|
||||
- **`api-key/`** — API key management dialogs
|
||||
- **`manager.tsx`** — Dialog manager component
|
||||
- **`store.ts`** — Dialog state management (Zustand)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="schema/" icon="file-code">
|
||||
Zod schemas for validation:
|
||||
|
||||
- **`resume/`** — Resume data schemas
|
||||
- **`icons.ts`** — Icon definitions
|
||||
- **`templates.ts`** — Template definitions
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="hooks/" icon="hook">
|
||||
Custom React hooks:
|
||||
|
||||
- `use-confirm.tsx` — Confirmation dialog hook
|
||||
- `use-prompt.tsx` — Prompt dialog hook
|
||||
- `use-mobile.tsx` — Mobile detection hook
|
||||
- `use-safe-context.tsx` — Safe context consumption
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="utils/" icon="wrench">
|
||||
Utility functions:
|
||||
|
||||
- `env.ts` — Environment variable validation
|
||||
- `locale.ts` — Locale utilities
|
||||
- `theme.ts` — Theme utilities
|
||||
- `string.ts` — String manipulation
|
||||
- `file.ts` — File handling utilities
|
||||
</Accordion>
|
||||
<Accordion title="components/" icon="cube">
|
||||
Reusable React components organized by category: - **`ui/`** — Base UI components (Button, Card, Dialog, etc.) -
|
||||
**`resume/`** — Resume-specific components (sections, templates) - **`input/`** — Form input components
|
||||
(ColorPicker, RichInput) - **`layout/`** — Layout components (Sidebar, LoadingScreen) - **`animation/`** — Animation
|
||||
components (Spotlight, TextMask) - **`theme/`** — Theme management components - **`typography/`** — Font management
|
||||
components
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="routes/" icon="route">
|
||||
File-based routing using TanStack Router: - **`__root.tsx`** — Root layout with providers - **`_home/`** — Public
|
||||
home page routes - **`auth/`** — Authentication routes (login, register, etc.) - **`dashboard/`** — User dashboard
|
||||
routes - **`builder/`** — Resume builder routes (the main editor) - **`printer/`** — PDF printing route - **`api/`**
|
||||
— API routes
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="integrations/" icon="plug">
|
||||
Third-party service integrations: - **`auth/`** — Better Auth client configuration - **`drizzle/`** — Database
|
||||
schema and utilities - **`orpc/`** — API router, client, and services - **`ai/`** — AI service integrations -
|
||||
**`import/`** — Resume import utilities
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="dialogs/" icon="window-maximize">
|
||||
Modal dialog components: - **`auth/`** — Authentication dialogs - **`resume/`** — Resume management dialogs -
|
||||
**`api-key/`** — API key management dialogs - **`manager.tsx`** — Dialog manager component - **`store.ts`** — Dialog
|
||||
state management (Zustand)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="schema/" icon="file-code">
|
||||
Zod schemas for validation: - **`resume/`** — Resume data schemas - **`icons.ts`** — Icon definitions -
|
||||
**`templates.ts`** — Template definitions
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="hooks/" icon="hook">
|
||||
Custom React hooks: - `use-confirm.tsx` — Confirmation dialog hook - `use-prompt.tsx` — Prompt dialog hook -
|
||||
`use-mobile.tsx` — Mobile detection hook - `use-safe-context.tsx` — Safe context consumption
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="utils/" icon="wrench">
|
||||
Utility functions: - `env.ts` — Environment variable validation - `locale.ts` — Locale utilities - `theme.ts` —
|
||||
Theme utilities - `string.ts` — String manipulation - `file.ts` — File handling utilities
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
@@ -163,16 +131,14 @@ flowchart TD
|
||||
|
||||
Routes are automatically generated from the file structure in `src/routes/`. TanStack Router conventions:
|
||||
|
||||
| Pattern | Description | Example |
|
||||
|---------|-------------|---------|
|
||||
| `index.tsx` | Index route | `/dashboard` |
|
||||
| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
|
||||
| `_layout/` | Layout group (prefix) | `_home/` |
|
||||
| `__root.tsx` | Root layout | Wraps all routes |
|
||||
| Pattern | Description | Example |
|
||||
| ------------ | --------------------- | -------------------- |
|
||||
| `index.tsx` | Index route | `/dashboard` |
|
||||
| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
|
||||
| `_layout/` | Layout group (prefix) | `_home/` |
|
||||
| `__root.tsx` | Root layout | Wraps all routes |
|
||||
|
||||
<Warning>
|
||||
Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.
|
||||
</Warning>
|
||||
<Warning>Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.</Warning>
|
||||
|
||||
### API Layer (ORPC)
|
||||
|
||||
@@ -203,11 +169,11 @@ const { data } = useQuery(orpc.resume.findMany.queryOptions());
|
||||
|
||||
Reactive Resume uses a hybrid approach:
|
||||
|
||||
| Type | Tool | Use Case |
|
||||
|------|------|----------|
|
||||
| Server State | TanStack Query | API data, caching, sync |
|
||||
| Client State | Zustand | UI state, dialogs, preferences |
|
||||
| Form State | React Hook Form | Form inputs and validation |
|
||||
| Type | Tool | Use Case |
|
||||
| ------------ | --------------- | ------------------------------ |
|
||||
| Server State | TanStack Query | API data, caching, sync |
|
||||
| Client State | Zustand | UI state, dialogs, preferences |
|
||||
| Form State | React Hook Form | Form inputs and validation |
|
||||
|
||||
### Database Schema
|
||||
|
||||
@@ -280,11 +246,9 @@ import { z } from "zod";
|
||||
import { publicProcedure, router } from "../server";
|
||||
|
||||
export const myRouter = router({
|
||||
hello: publicProcedure
|
||||
.input(z.object({ name: z.string() }))
|
||||
.handler(async ({ input }) => {
|
||||
return { message: `Hello, ${input.name}!` };
|
||||
}),
|
||||
hello: publicProcedure.input(z.object({ name: z.string() })).handler(async ({ input }) => {
|
||||
return { message: `Hello, ${input.name}!` };
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
@@ -300,81 +264,59 @@ import { Trans } from "@lingui/react/macro";
|
||||
|
||||
// In component
|
||||
const title = t`Welcome`;
|
||||
<Trans>Click here to continue</Trans>
|
||||
<Trans>Click here to continue</Trans>;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `vite.config.ts` | Vite bundler configuration |
|
||||
| `tsconfig.json` | TypeScript configuration |
|
||||
| `biome.json` | Linter and formatter settings |
|
||||
| `drizzle.config.ts` | Drizzle ORM configuration |
|
||||
| `lingui.config.ts` | Lingui i18n configuration |
|
||||
| `components.json` | shadcn/ui component configuration |
|
||||
| File | Purpose |
|
||||
| ------------------- | --------------------------------- |
|
||||
| `vite.config.ts` | Vite bundler configuration |
|
||||
| `tsconfig.json` | TypeScript configuration |
|
||||
| `.oxfmtrc.json` | Formatter settings |
|
||||
| `.oxlintrc.json` | Linter settings |
|
||||
| `drizzle.config.ts` | Drizzle ORM configuration |
|
||||
| `lingui.config.ts` | Lingui i18n configuration |
|
||||
| `components.json` | shadcn/ui component configuration |
|
||||
|
||||
---
|
||||
|
||||
## Contributing Guidelines
|
||||
|
||||
<Steps>
|
||||
<Step title="Fork & Clone">
|
||||
Fork the repository on GitHub and clone your fork locally.
|
||||
</Step>
|
||||
|
||||
<Step title="Create a Branch">
|
||||
Create a feature branch from `main`:
|
||||
```bash
|
||||
git checkout -b feature/my-feature
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Make Changes">
|
||||
Implement your changes following the patterns described above.
|
||||
</Step>
|
||||
|
||||
<Step title="Test Locally">
|
||||
Ensure the app works correctly with your changes:
|
||||
```bash
|
||||
pnpm run dev
|
||||
pnpm run lint
|
||||
pnpm run typecheck
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Commit & Push">
|
||||
Write clear commit messages and push to your fork.
|
||||
</Step>
|
||||
|
||||
<Step title="Open a Pull Request">
|
||||
Open a PR against the main repository with a clear description of your changes.
|
||||
</Step>
|
||||
<Step title="Fork & Clone">Fork the repository on GitHub and clone your fork locally.</Step>
|
||||
|
||||
<Step title="Create a Branch">
|
||||
Create a feature branch from `main`: ```bash git checkout -b feature/my-feature ```
|
||||
</Step>
|
||||
|
||||
<Step title="Make Changes">Implement your changes following the patterns described above.</Step>
|
||||
|
||||
<Step title="Test Locally">
|
||||
Ensure the app works correctly with your changes: ```bash pnpm run dev pnpm run lint pnpm run typecheck ```
|
||||
</Step>
|
||||
|
||||
<Step title="Commit & Push">Write clear commit messages and push to your fork.</Step>
|
||||
|
||||
<Step title="Open a Pull Request">
|
||||
Open a PR against the main repository with a clear description of your changes.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.
|
||||
</Note>
|
||||
<Note>Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.</Note>
|
||||
|
||||
---
|
||||
|
||||
## Need Help?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="GitHub Discussions"
|
||||
icon="comments"
|
||||
href="https://github.com/amruthpillai/reactive-resume/discussions"
|
||||
>
|
||||
<Card title="GitHub Discussions" icon="comments" href="https://github.com/amruthpillai/reactive-resume/discussions">
|
||||
Ask questions and discuss ideas with the community.
|
||||
</Card>
|
||||
<Card
|
||||
title="GitHub Issues"
|
||||
icon="bug"
|
||||
href="https://github.com/amruthpillai/reactive-resume/issues"
|
||||
>
|
||||
<Card title="GitHub Issues" icon="bug" href="https://github.com/amruthpillai/reactive-resume/issues">
|
||||
Report bugs or request features.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -4,11 +4,8 @@ description: "Set up a local development environment for Reactive Resume"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Prerequisites**:
|
||||
- [Node.js](https://nodejs.org/) v20 or higher
|
||||
- [pnpm](https://pnpm.io/) v10.28.0 or higher (package manager)
|
||||
- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
|
||||
- [Git](https://git-scm.com/)
|
||||
**Prerequisites**: - [Node.js](https://nodejs.org/) v20 or higher - [pnpm](https://pnpm.io/) v10.28.0 or higher
|
||||
(package manager) - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/)
|
||||
</Info>
|
||||
|
||||
This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running.
|
||||
@@ -87,14 +84,15 @@ This guide walks you through setting up Reactive Resume for local development. W
|
||||
SMTP_HOST=localhost
|
||||
SMTP_PORT=1025
|
||||
```
|
||||
|
||||
|
||||
<Note>
|
||||
**PDF Generation Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the printer service is running inside Docker (which is the case when using `compose.dev.yml`). The printer needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
|
||||
</Note>
|
||||
|
||||
|
||||
<Tip>
|
||||
**Email Testing**: The development stack includes [Mailpit](https://mailpit.axllent.org/), an email testing tool. All emails sent by the application will be captured and viewable at [http://localhost:8025](http://localhost:8025). No emails will actually be sent to real addresses during development.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Run Database Migrations">
|
||||
@@ -122,32 +120,33 @@ Here are the most commonly used scripts during development:
|
||||
|
||||
### Development
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pnpm run dev` | Start the development server with hot reload |
|
||||
| `pnpm run build` | Build the application for production |
|
||||
| `pnpm run start` | Start the production server |
|
||||
| `pnpm run lint` | Run Biome linter and formatter |
|
||||
| `pnpm run typecheck` | Run TypeScript type checking |
|
||||
| Command | Description |
|
||||
| -------------------- | -------------------------------------------- |
|
||||
| `pnpm run dev` | Start the development server with hot reload |
|
||||
| `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 typecheck` | Run TypeScript type checking |
|
||||
|
||||
### Database
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| Command | Description |
|
||||
| ---------------------- | -------------------------------------------- |
|
||||
| `pnpm run db:generate` | Generate migration files from schema changes |
|
||||
| `pnpm run db:migrate` | Apply pending migrations |
|
||||
| `pnpm run db:studio` | Open Drizzle Studio (database GUI) |
|
||||
| `pnpm run db:migrate` | Apply pending migrations |
|
||||
| `pnpm run db:studio` | Open Drizzle Studio (database GUI) |
|
||||
|
||||
### Internationalization
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| Command | Description |
|
||||
| ------------------------- | -------------------------------------- |
|
||||
| `pnpm run lingui:extract` | Extract translatable strings from code |
|
||||
|
||||
### Documentation
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| Command | Description |
|
||||
| ------------------- | ------------------------------------------ |
|
||||
| `pnpm run docs:dev` | Start the Mintlify docs development server |
|
||||
|
||||
---
|
||||
@@ -210,9 +209,7 @@ This opens a web-based GUI at [https://local.drizzle.studio](https://local.drizz
|
||||
pnpm run db:migrate
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Always review generated migrations before applying them, especially when working with existing data.
|
||||
</Warning>
|
||||
<Warning>Always review generated migrations before applying them, especially when working with existing data.</Warning>
|
||||
|
||||
---
|
||||
|
||||
@@ -232,7 +229,7 @@ import { Trans } from "@lingui/react/macro";
|
||||
const message = t`Hello, World!`;
|
||||
|
||||
// For JSX content
|
||||
<Trans>Welcome to Reactive Resume</Trans>
|
||||
<Trans>Welcome to Reactive Resume</Trans>;
|
||||
```
|
||||
|
||||
### Extracting Translations
|
||||
@@ -251,11 +248,12 @@ Translation files are located in the `locales/` directory in `.po` format.
|
||||
|
||||
### Linting & Formatting
|
||||
|
||||
Uses [Biome](https://biomejs.dev/) for linting and formatting:
|
||||
Uses [Oxlint](https://oxlint.dev/) for linting and [Oxfmt](https://oxfmt.dev/) for formatting:
|
||||
|
||||
```bash
|
||||
# Check and auto-fix issues
|
||||
pnpm run lint
|
||||
pnpm run fmt
|
||||
```
|
||||
|
||||
### Type Checking
|
||||
@@ -267,7 +265,9 @@ pnpm run typecheck
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Configure your IDE to use Biome for automatic formatting on save. For VS Code, install the [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome).
|
||||
Configure your IDE to use Oxlint for automatic linting and Oxfmt for automatic formatting on save. For VS Code,
|
||||
install the [Oxlint extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and configure the
|
||||
settings in `.vscode/settings.json`.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
@@ -275,53 +275,27 @@ pnpm run typecheck
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Port 3000 is already in use">
|
||||
Another process is using port 3000. Either stop that process or start the dev server on a different port:
|
||||
|
||||
```bash
|
||||
PORT=3001 pnpm run dev
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database connection refused">
|
||||
Ensure Docker containers are running:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml ps
|
||||
docker compose -f compose.dev.yml up -d
|
||||
```
|
||||
|
||||
Check that PostgreSQL is healthy and accessible on port 5432.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3/Storage errors">
|
||||
Verify SeaweedFS is running and the bucket exists:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml logs seaweedfs
|
||||
docker compose -f compose.dev.yml logs seaweedfs-create-bucket
|
||||
```
|
||||
|
||||
If the bucket wasn't created, restart the bucket creation service:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml restart seaweedfs-create-bucket
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Type errors after pulling changes">
|
||||
The route tree may need regeneration. Run the dev server which auto-generates routes:
|
||||
|
||||
```bash
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
Or run type checking to see specific errors:
|
||||
|
||||
```bash
|
||||
pnpm run typecheck
|
||||
```
|
||||
</Accordion>
|
||||
<Accordion title="Port 3000 is already in use">
|
||||
Another process is using port 3000. Either stop that process or start the dev server on a different port: ```bash
|
||||
PORT=3001 pnpm run dev ```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database connection refused">
|
||||
Ensure Docker containers are running: ```bash docker compose -f compose.dev.yml ps docker compose -f compose.dev.yml
|
||||
up -d ``` Check that PostgreSQL is healthy and accessible on port 5432.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3/Storage errors">
|
||||
Verify SeaweedFS is running and the bucket exists: ```bash docker compose -f compose.dev.yml logs seaweedfs docker
|
||||
compose -f compose.dev.yml logs seaweedfs_create_bucket ``` If the bucket wasn't created, restart the bucket
|
||||
creation service: ```bash docker compose -f compose.dev.yml restart seaweedfs_create_bucket ```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Type errors after pulling changes">
|
||||
The route tree may need regeneration. Run the dev server which auto-generates routes: ```bash pnpm run dev ``` Or
|
||||
run type checking to see specific errors: ```bash pnpm run typecheck ```
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
@@ -329,18 +303,10 @@ pnpm run typecheck
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Project Architecture"
|
||||
icon="folder-open"
|
||||
href="/contributing/architecture"
|
||||
>
|
||||
<Card title="Project Architecture" icon="folder-open" href="/contributing/architecture">
|
||||
Deep dive into the project architecture and codebase structure.
|
||||
</Card>
|
||||
<Card
|
||||
title="GitHub Repository"
|
||||
icon="github"
|
||||
href="https://github.com/amruthpillai/reactive-resume"
|
||||
>
|
||||
<Card title="GitHub Repository" icon="github" href="https://github.com/amruthpillai/reactive-resume">
|
||||
View the source code and contribute to the project.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -12,7 +12,8 @@ Reactive Resume is used by people all around the world, and translations help ma
|
||||
Reactive Resume uses [Crowdin](https://crowdin.com/) as its localization management platform. Crowdin provides a user-friendly interface where translators can contribute translations without needing to write code or work with files directly.
|
||||
|
||||
<Info>
|
||||
The Reactive Resume Crowdin project is available at [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume).
|
||||
The Reactive Resume Crowdin project is available at
|
||||
[https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume).
|
||||
</Info>
|
||||
|
||||
Once translations are submitted and approved on Crowdin, they are automatically synced to the codebase and will be available in the next release of the app.
|
||||
@@ -22,33 +23,36 @@ Once translations are submitted and approved on Crowdin, they are automatically
|
||||
## Getting Started
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Crowdin Account">
|
||||
If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your email or sign up with Google, Facebook, Twitter, GitHub, or GitLab.
|
||||
|
||||
<Tip>
|
||||
For detailed instructions on creating an account and getting started, see Crowdin's official [For Translators](https://support.crowdin.com/for-translators/) documentation.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Join the Reactive Resume Project">
|
||||
Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join** to become a contributor.
|
||||
</Step>
|
||||
|
||||
<Step title="Select Your Language">
|
||||
From the project dashboard, click on the language you want to translate. You'll see a list of files that need translation along with the progress for each.
|
||||
</Step>
|
||||
|
||||
<Step title="Start Translating">
|
||||
Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for your translation on the right.
|
||||
|
||||
- Translate the text accurately while preserving any placeholders or formatting
|
||||
- Use the suggestions from Translation Memory and Machine Translation as a starting point
|
||||
- Vote on existing translations if you agree with them
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Translations">
|
||||
Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release.
|
||||
</Step>
|
||||
<Step title="Create a Crowdin Account">
|
||||
If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your
|
||||
email or sign up with Google, Facebook, Twitter, GitHub, or GitLab.
|
||||
<Tip>
|
||||
For detailed instructions on creating an account and getting started, see Crowdin's official [For
|
||||
Translators](https://support.crowdin.com/for-translators/) documentation.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Join the Reactive Resume Project">
|
||||
Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join**
|
||||
to become a contributor.
|
||||
</Step>
|
||||
|
||||
<Step title="Select Your Language">
|
||||
From the project dashboard, click on the language you want to translate. You'll see a list of files that need
|
||||
translation along with the progress for each.
|
||||
</Step>
|
||||
|
||||
<Step title="Start Translating">
|
||||
Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for
|
||||
your translation on the right. - Translate the text accurately while preserving any placeholders or formatting - Use
|
||||
the suggestions from Translation Memory and Machine Translation as a starting point - Vote on existing translations
|
||||
if you agree with them
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Translations">
|
||||
Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
---
|
||||
@@ -90,7 +94,8 @@ Some technical terms (like "PDF", "URL", "JSON") are often kept in English acros
|
||||
If your language is not listed in the Crowdin project, you can request it to be added.
|
||||
|
||||
<Warning>
|
||||
Before requesting a new language, please check if it's already available in the [Crowdin project](https://crowdin.com/project/reactive-resume).
|
||||
Before requesting a new language, please check if it's already available in the [Crowdin
|
||||
project](https://crowdin.com/project/reactive-resume).
|
||||
</Warning>
|
||||
|
||||
To request a new language:
|
||||
@@ -110,7 +115,8 @@ Once approved, the language will be added to Crowdin and you can begin translati
|
||||
Translations submitted on Crowdin are synced to the codebase periodically. Once merged, they will be included in the next release of Reactive Resume.
|
||||
|
||||
<Info>
|
||||
There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on the release cycle.
|
||||
There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on
|
||||
the release cycle.
|
||||
</Info>
|
||||
|
||||
---
|
||||
@@ -137,18 +143,10 @@ Translations submitted on Crowdin are synced to the codebase periodically. Once
|
||||
## Need Help?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
icon="book-open"
|
||||
title="Crowdin Translator Docs"
|
||||
href="https://support.crowdin.com/for-translators/"
|
||||
>
|
||||
<Card icon="book-open" title="Crowdin Translator Docs" href="https://support.crowdin.com/for-translators/">
|
||||
Official Crowdin documentation for translators.
|
||||
</Card>
|
||||
<Card
|
||||
icon="github"
|
||||
title="GitHub Issues"
|
||||
href="https://github.com/amruthpillai/reactive-resume/issues"
|
||||
>
|
||||
<Card icon="github" title="GitHub Issues" href="https://github.com/amruthpillai/reactive-resume/issues">
|
||||
Report issues or request new languages.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
+148
-141
@@ -1,143 +1,150 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Reactive Resume",
|
||||
"favicon": "/favicon.svg",
|
||||
"description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!",
|
||||
"seo": {
|
||||
"indexing": "all",
|
||||
"metatags": {
|
||||
"canonical": "https://docs.rxresu.me",
|
||||
"og:image": "https://rxresu.me/opengraph/banner.jpg"
|
||||
}
|
||||
},
|
||||
"fonts": {
|
||||
"heading": {
|
||||
"family": "IBM Plex Sans"
|
||||
},
|
||||
"body": {
|
||||
"family": "IBM Plex Sans"
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#166534",
|
||||
"light": "#07C983",
|
||||
"dark": "#15803D"
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"icon": "book",
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": [
|
||||
"getting-started/index",
|
||||
"getting-started/quickstart",
|
||||
"guides/checking-service-status",
|
||||
"guides/accessing-the-previous-version"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Account",
|
||||
"pages": [
|
||||
"guides/creating-an-account",
|
||||
"guides/updating-your-profile",
|
||||
"guides/linking-social-accounts",
|
||||
"guides/deleting-your-account"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Resume Builder",
|
||||
"pages": [
|
||||
"guides/creating-your-first-resume",
|
||||
"guides/choosing-a-template",
|
||||
"guides/selecting-page-format",
|
||||
"guides/moving-items-between-sections",
|
||||
"guides/fitting-content-on-a-page",
|
||||
"guides/exporting-your-resume",
|
||||
"guides/sharing-your-resume-publicly",
|
||||
"guides/using-private-notes",
|
||||
"guides/using-custom-css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Security",
|
||||
"pages": ["guides/setting-up-two-factor-authentication", "guides/setting-up-passkeys"]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": ["guides/using-the-api", "guides/using-ai", "guides/json-resume-schema"]
|
||||
},
|
||||
{
|
||||
"group": "Self-Hosting",
|
||||
"pages": ["self-hosting/docker", "self-hosting/examples", "self-hosting/sso", "self-hosting/migration"]
|
||||
},
|
||||
{
|
||||
"group": "Contributing",
|
||||
"pages": ["contributing/architecture", "contributing/development", "contributing/translations"]
|
||||
},
|
||||
{
|
||||
"group": "Community",
|
||||
"pages": ["community/spotlight"]
|
||||
},
|
||||
{
|
||||
"group": "Legal",
|
||||
"pages": ["legal/license", "legal/privacy-policy", "legal/terms-of-service"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"icon": "clock",
|
||||
"tab": "Changelog",
|
||||
"pages": ["changelog/index"]
|
||||
},
|
||||
{
|
||||
"icon": "code",
|
||||
"tab": "API Reference",
|
||||
"openapi": "/spec.json"
|
||||
}
|
||||
],
|
||||
"global": {
|
||||
"anchors": [
|
||||
{
|
||||
"icon": "link",
|
||||
"anchor": "Reactive Resume",
|
||||
"href": "https://rxresu.me"
|
||||
},
|
||||
{
|
||||
"icon": "github",
|
||||
"anchor": "Source Code",
|
||||
"href": "https://github.com/amruthpillai/reactive-resume"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"logo": {
|
||||
"light": "/logo/light.svg",
|
||||
"dark": "/logo/dark.svg"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "Support",
|
||||
"href": "https://github.com/amruthpillai/reactive-resume/issues"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Get Started",
|
||||
"href": "https://rxresu.me"
|
||||
}
|
||||
},
|
||||
"contextual": {
|
||||
"options": ["copy", "view"]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"website": "https://rxresu.me",
|
||||
"github": "https://github.com/amruthpillai/reactive-resume"
|
||||
}
|
||||
}
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Reactive Resume",
|
||||
"favicon": "/favicon.svg",
|
||||
"description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!",
|
||||
"seo": {
|
||||
"indexing": "all",
|
||||
"metatags": {
|
||||
"canonical": "https://docs.rxresu.me",
|
||||
"og:image": "https://rxresu.me/opengraph/banner.jpg"
|
||||
}
|
||||
},
|
||||
"fonts": {
|
||||
"heading": {
|
||||
"family": "IBM Plex Sans"
|
||||
},
|
||||
"body": {
|
||||
"family": "IBM Plex Sans"
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#166534",
|
||||
"light": "#07C983",
|
||||
"dark": "#15803D"
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"icon": "book",
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": [
|
||||
"getting-started/index",
|
||||
"getting-started/quickstart",
|
||||
"guides/checking-service-status",
|
||||
"guides/accessing-the-previous-version"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Account",
|
||||
"pages": [
|
||||
"guides/creating-an-account",
|
||||
"guides/updating-your-profile",
|
||||
"guides/linking-social-accounts",
|
||||
"guides/deleting-your-account"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Resume Builder",
|
||||
"pages": [
|
||||
"guides/creating-your-first-resume",
|
||||
"guides/choosing-a-template",
|
||||
"guides/selecting-page-format",
|
||||
"guides/moving-items-between-sections",
|
||||
"guides/fitting-content-on-a-page",
|
||||
"guides/adding-a-cover-letter",
|
||||
"guides/exporting-your-resume",
|
||||
"guides/sharing-your-resume-publicly",
|
||||
"guides/using-private-notes",
|
||||
"guides/using-custom-css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Security",
|
||||
"pages": ["guides/setting-up-two-factor-authentication", "guides/setting-up-passkeys"]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"guides/using-the-api",
|
||||
"guides/using-the-patch-api",
|
||||
"guides/using-the-mcp-server",
|
||||
"guides/using-ai",
|
||||
"guides/json-resume-schema"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Self-Hosting",
|
||||
"pages": ["self-hosting/docker", "self-hosting/examples", "self-hosting/sso", "self-hosting/migration"]
|
||||
},
|
||||
{
|
||||
"group": "Contributing",
|
||||
"pages": ["contributing/architecture", "contributing/development", "contributing/translations"]
|
||||
},
|
||||
{
|
||||
"group": "Community",
|
||||
"pages": ["community/spotlight"]
|
||||
},
|
||||
{
|
||||
"group": "Legal",
|
||||
"pages": ["legal/license", "legal/privacy-policy", "legal/terms-of-service"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"icon": "clock",
|
||||
"tab": "Changelog",
|
||||
"pages": ["changelog/index"]
|
||||
},
|
||||
{
|
||||
"icon": "code",
|
||||
"tab": "API Reference",
|
||||
"openapi": "/spec.json"
|
||||
}
|
||||
],
|
||||
"global": {
|
||||
"anchors": [
|
||||
{
|
||||
"icon": "link",
|
||||
"anchor": "Reactive Resume",
|
||||
"href": "https://rxresu.me"
|
||||
},
|
||||
{
|
||||
"icon": "github",
|
||||
"anchor": "Source Code",
|
||||
"href": "https://github.com/amruthpillai/reactive-resume"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"logo": {
|
||||
"light": "/logo/light.svg",
|
||||
"dark": "/logo/dark.svg"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "Support",
|
||||
"href": "https://github.com/amruthpillai/reactive-resume/issues"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Get Started",
|
||||
"href": "https://rxresu.me"
|
||||
}
|
||||
},
|
||||
"contextual": {
|
||||
"options": ["copy", "view"]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"website": "https://rxresu.me",
|
||||
"github": "https://github.com/amruthpillai/reactive-resume"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/getting-started/banner.webp" alt="Reactive Resume Banner" />
|
||||
<img src="/images/getting-started/banner.webp" alt="Reactive Resume Banner" />
|
||||
</Frame>
|
||||
|
||||
## What is Reactive Resume?
|
||||
@@ -33,29 +33,34 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
|
||||
</Frame>
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Completely Free & Open Source" icon="code-branch">
|
||||
Reactive Resume is licensed under MIT. You can use it for free, modify it, and even host your own instance. The entire codebase is available on [GitHub](https://github.com/amruthpillai/reactive-resume).
|
||||
</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.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rich Text Editor" icon="text">
|
||||
Format your content with bold, italic, links, lists, and more using our intuitive rich text editor powered by Tiptap.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Multi-language Support" icon="globe">
|
||||
Reactive Resume supports multiple languages, making it accessible to users worldwide. Contribute translations to help us reach more people.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dark Mode" icon="moon">
|
||||
Work comfortably in any lighting condition with built-in dark mode support that's easy on your eyes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Self-hosting Ready" icon="server">
|
||||
Deploy your own instance of Reactive Resume using Docker. Keep complete control over your data and infrastructure.
|
||||
</Accordion>
|
||||
<Accordion title="Completely Free & Open Source" icon="code-branch">
|
||||
Reactive Resume is licensed under MIT. You can use it for free, modify it, and even host your own instance. The
|
||||
entire codebase is available on [GitHub](https://github.com/amruthpillai/reactive-resume).
|
||||
</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.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rich Text Editor" icon="text">
|
||||
Format your content with bold, italic, links, lists, and more using our intuitive rich text editor powered by
|
||||
Tiptap.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Multi-language Support" icon="globe">
|
||||
Reactive Resume supports multiple languages, making it accessible to users worldwide. Contribute translations to
|
||||
help us reach more people.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dark Mode" icon="moon">
|
||||
Work comfortably in any lighting condition with built-in dark mode support that's easy on your eyes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Self-hosting Ready" icon="server">
|
||||
Deploy your own instance of Reactive Resume using Docker. Keep complete control over your data and infrastructure.
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
## Getting Started
|
||||
@@ -63,18 +68,10 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
|
||||
Whether you want to use our hosted version or run your own instance, Reactive Resume has got you covered.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Quickstart"
|
||||
icon="rocket"
|
||||
href="/getting-started/quickstart"
|
||||
>
|
||||
<Card title="Quickstart" icon="rocket" href="/getting-started/quickstart">
|
||||
Get up and running in minutes with our hosted version or deploy your own instance.
|
||||
</Card>
|
||||
<Card
|
||||
title="Development Setup"
|
||||
icon="code"
|
||||
href="/contributing/development"
|
||||
>
|
||||
<Card title="Development Setup" icon="code" href="/contributing/development">
|
||||
Set up a local development environment to contribute or customize Reactive Resume.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -83,17 +80,17 @@ Whether you want to use our hosted version or run your own instance, Reactive Re
|
||||
|
||||
Reactive Resume is built with modern web technologies:
|
||||
|
||||
| Category | Technology |
|
||||
|-------------------|------------|
|
||||
| Framework | TanStack Start (React 19, Vite) |
|
||||
| Runtime | Node.js |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL with Drizzle ORM |
|
||||
| API | ORPC (Type-safe RPC) |
|
||||
| Auth | Better Auth |
|
||||
| Styling | Tailwind CSS |
|
||||
| UI Components | Radix UI |
|
||||
| State Management | Zustand + TanStack Query |
|
||||
| Category | Technology |
|
||||
| ---------------- | ------------------------------- |
|
||||
| Framework | TanStack Start (React 19, Vite) |
|
||||
| Runtime | Node.js |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL with Drizzle ORM |
|
||||
| API | ORPC (Type-safe RPC) |
|
||||
| Auth | Better Auth |
|
||||
| Styling | Tailwind CSS |
|
||||
| UI Components | Radix UI |
|
||||
| State Management | Zustand + TanStack Query |
|
||||
|
||||
## Community & Support
|
||||
|
||||
@@ -104,7 +101,7 @@ Reactive Resume is built with modern web technologies:
|
||||
<Card title="Reddit" icon="reddit" href="https://reddit.com/r/reactiveresume">
|
||||
Join our Reddit community to get help and connect with other users.
|
||||
</Card>
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/EE8yFqW4">
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/aSyA5ZSxpb">
|
||||
Join our Discord server to get help and connect with other users.
|
||||
</Card>
|
||||
<Card title="Sponsor" icon="heart" href="https://opencollective.com/reactive-resume">
|
||||
|
||||
+103
-133
@@ -23,32 +23,27 @@ Reactive Resume offers flexibility in how you want to use it. Choose the option
|
||||
The easiest way to use Reactive Resume is through our cloud version at [rxresu.me](https://rxresu.me). **This service is completely free and will always remain free.**
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an Account">
|
||||
Visit [rxresu.me](https://rxresu.me) and sign up for free using your email, or sign in with your GitHub or Google account.
|
||||
</Step>
|
||||
|
||||
<Step title="Create Your First Resume">
|
||||
Click the **Create Resume** button on your dashboard. Give your resume a name and select a template to get started.
|
||||
</Step>
|
||||
|
||||
<Step title="Fill in Your Details">
|
||||
Use our intuitive builder to add your:
|
||||
- Personal information
|
||||
- Work experience
|
||||
- Education
|
||||
- Skills
|
||||
- Projects
|
||||
- And more...
|
||||
</Step>
|
||||
|
||||
<Step title="Export & Share">
|
||||
When you're ready, export your resume as a PDF or share it via a unique public link.
|
||||
</Step>
|
||||
<Step title="Create an Account">
|
||||
Visit [rxresu.me](https://rxresu.me) and sign up for free using your email, or sign in with your GitHub or Google
|
||||
account.
|
||||
</Step>
|
||||
|
||||
<Step title="Create Your First Resume">
|
||||
Click the **Create Resume** button on your dashboard. Give your resume a name and select a template to get started.
|
||||
</Step>
|
||||
|
||||
<Step title="Fill in Your Details">
|
||||
Use our intuitive builder to add your: - Personal information - Work experience - Education - Skills - Projects -
|
||||
And more...
|
||||
</Step>
|
||||
|
||||
<Step title="Export & Share">
|
||||
When you're ready, export your resume as a PDF or share it via a unique public link.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
Your resume updates in real-time as you type. The preview panel shows exactly how your final PDF will look.
|
||||
</Tip>
|
||||
<Tip>Your resume updates in real-time as you type. The preview panel shows exactly how your final PDF will look.</Tip>
|
||||
|
||||
---
|
||||
|
||||
@@ -64,77 +59,50 @@ Before you begin, ensure you have the following installed:
|
||||
- [Docker Compose](https://docs.docker.com/compose/install/) (v2.0 or higher)
|
||||
|
||||
<Info>
|
||||
There is <strong>no difference in features</strong> between the cloud-hosted version and the self-hosted option. Both provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs!
|
||||
There is <strong>no difference in features</strong> between the cloud-hosted version and the self-hosted option. Both
|
||||
provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs!
|
||||
</Info>
|
||||
|
||||
|
||||
### Quick Deployment
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone the Repository">
|
||||
```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
|
||||
```
|
||||
|
||||
<Warning>
|
||||
For production deployments, always use strong, unique values for `AUTH_SECRET` 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
|
||||
</Step>
|
||||
|
||||
<Step title="Access Your Instance">
|
||||
Once all services are running, access your Reactive Resume instance at:
|
||||
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
</Step>
|
||||
<Step title="Clone the Repository">
|
||||
```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 ```
|
||||
<Warning>
|
||||
For production deployments, always use strong, unique values for `AUTH_SECRET` 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
|
||||
</Step>
|
||||
|
||||
<Step title="Access Your Instance">
|
||||
Once all services are running, access your Reactive Resume instance at: ``` http://localhost:3000 ```
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
### Docker Compose Services
|
||||
|
||||
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 |
|
||||
| `printer` | 4000 | Headless Chromium service for PDF and screenshot generation |
|
||||
| `app` | 3000 | The main Reactive Resume application |
|
||||
|
||||
### Health Checks
|
||||
|
||||
@@ -154,54 +122,63 @@ 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 -base64 32` |
|
||||
| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
|
||||
| `PRINTER_ENDPOINT` | URL of the printer service | `http://printer:3000` |
|
||||
|
||||
### 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` |
|
||||
| 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_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` |
|
||||
|
||||
> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused.
|
||||
|
||||
<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 resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
|
||||
**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
|
||||
resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must
|
||||
set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to
|
||||
communicate with services running on your host machine.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**Alternative Printer Options**: If you don't want to use browserless, you can use any headless Chrome/Chromium instance with its remote debugging port open. For example, run `chromium --remote-debugging-port=9222` and point `PRINTER_ENDPOINT` to that instance.
|
||||
**Alternative Printer Options**: If you don't want to use browserless, you can use any headless Chrome/Chromium
|
||||
instance with its remote debugging port open. For example, run `chromium --remote-debugging-port=9222` and point
|
||||
`PRINTER_ENDPOINT` to that instance.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -209,22 +186,15 @@ Here's a complete list of environment variables you can configure:
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Development Setup"
|
||||
icon="code"
|
||||
href="/contributing/development"
|
||||
>
|
||||
<Card title="Development Setup" icon="code" href="/contributing/development">
|
||||
Set up a development environment to contribute or customize Reactive Resume.
|
||||
</Card>
|
||||
<Card
|
||||
title="Project Architecture"
|
||||
icon="folder-open"
|
||||
href="/contributing/architecture"
|
||||
>
|
||||
<Card title="Project Architecture" icon="folder-open" href="/contributing/architecture">
|
||||
Learn about the project structure and architecture.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
**Having trouble?** Check our [GitHub Issues](https://github.com/amruthpillai/reactive-resume/issues) or reach out via [email](mailto:hello@amruthpillai.com).
|
||||
**Having trouble?** Check our [GitHub Issues](https://github.com/amruthpillai/reactive-resume/issues) or reach out via
|
||||
[email](mailto:hello@amruthpillai.com).
|
||||
</Note>
|
||||
|
||||
@@ -7,9 +7,7 @@ description: "Learn how to access the previous version (v4) of Reactive Resume i
|
||||
|
||||
If you've been using Reactive Resume for a while, you may have resumes saved in the previous version of the application. The good news is that **version 4 (v4) is still fully accessible** and will remain online for the foreseeable future.
|
||||
|
||||
<Info>
|
||||
The previous version of Reactive Resume is available at [https://v4.rxresu.me](https://v4.rxresu.me).
|
||||
</Info>
|
||||
<Info>The previous version of Reactive Resume is available at [https://v4.rxresu.me](https://v4.rxresu.me).</Info>
|
||||
|
||||
## Why keep the old version running?
|
||||
|
||||
@@ -24,7 +22,8 @@ There are several reasons why v4 remains available:
|
||||
The previous version will continue to run for as long as possible—until the maintainer runs out of breath or funds to keep the server active. There are no immediate plans to shut it down.
|
||||
|
||||
<Warning>
|
||||
While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version to benefit from new features, improvements, and ongoing support.
|
||||
While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version
|
||||
to benefit from new features, improvements, and ongoing support.
|
||||
</Warning>
|
||||
|
||||
## Accessing your v4 resumes
|
||||
@@ -40,6 +39,7 @@ The previous version will continue to run for as long as possible—until the ma
|
||||
<Tip>
|
||||
If you used social login (Google, GitHub, etc.) in v4, use the same method to sign in.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Access your resumes">
|
||||
@@ -57,7 +57,8 @@ If you'd like to move your resumes to the latest version of Reactive Resume, you
|
||||
4. Use the import feature to upload your JSON file (select the "Reactive Resume v4 (JSON)" option)
|
||||
|
||||
<Info>
|
||||
Your existing resumes should already be available in the new version. If you don't see them, you can manually import them using the steps above.
|
||||
Your existing resumes should already be available in the new version. If you don't see them, you can manually import
|
||||
them using the steps above.
|
||||
</Info>
|
||||
|
||||
## Questions or issues?
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: "Adding a cover letter"
|
||||
description: "Learn how to create a cover letter as a custom section in Reactive Resume, format it professionally, and place it on a separate page."
|
||||
---
|
||||
|
||||
A cover letter is a personalized document that accompanies your resume when applying for a job. It introduces you to the employer, highlights your relevant qualifications, and explains why you're a good fit for the position. Reactive Resume lets you create cover letters as custom sections, giving you full control over formatting and placement.
|
||||
|
||||
## Why include a cover letter?
|
||||
|
||||
- **Stand out**: A well-written cover letter demonstrates genuine interest in the position
|
||||
- **Personalize your application**: Tailor your message to each employer and role
|
||||
- **Explain gaps or transitions**: Address career changes or gaps in your resume
|
||||
- **Show communication skills**: Demonstrate your ability to write clearly and professionally
|
||||
|
||||
## Creating a cover letter section
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the resume builder">
|
||||
Navigate to your resume and open it in the builder.
|
||||
</Step>
|
||||
|
||||
<Step title="Scroll to Custom Sections">
|
||||
In the left sidebar, scroll down to find the **Custom Sections** area at the bottom.
|
||||
</Step>
|
||||
|
||||
<Step title="Add a new custom section">
|
||||
Click <Badge>Add a new custom section</Badge> to open the section creation dialog.
|
||||
</Step>
|
||||
|
||||
<Step title="Select 'Cover Letter' as the type">
|
||||
In the dialog: - Enter a **Title** for your section (e.g., "Cover Letter" or "Cover Letter - [Company Name]") - Select
|
||||
**Cover Letter** from the **Type** dropdown - Click **Create** to add the section
|
||||
</Step>
|
||||
|
||||
<Step title="Add a cover letter item">
|
||||
In your new cover letter section, click <Badge>Add a new item</Badge> to create your cover letter.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Formatting your cover letter
|
||||
|
||||
A cover letter has two main fields:
|
||||
|
||||
### Recipient field
|
||||
|
||||
The **Recipient** field is where you enter the recipient's information. This typically includes:
|
||||
|
||||
- Date (optional)
|
||||
- Hiring manager's name
|
||||
- Their job title
|
||||
- Company name
|
||||
- Company address
|
||||
|
||||
<Tip>
|
||||
Use line breaks to format the address block. For example:
|
||||
|
||||
```
|
||||
January 31, 2026
|
||||
|
||||
Jane Smith
|
||||
Senior Hiring Manager
|
||||
Acme Corporation
|
||||
123 Main Street, Suite 400
|
||||
New York, NY 10001
|
||||
```
|
||||
|
||||
</Tip>
|
||||
|
||||
### Content field
|
||||
|
||||
The **Content** field is where you write the body of your cover letter. Include:
|
||||
|
||||
- **Salutation**: "Dear Ms. Smith," or "Dear Hiring Team,"
|
||||
- **Opening paragraph**: State the position you're applying for and how you learned about it
|
||||
- **Body paragraphs**: Highlight relevant experience, skills, and achievements
|
||||
- **Closing paragraph**: Express enthusiasm and include a call to action
|
||||
- **Sign-off**: "Sincerely," followed by your name
|
||||
|
||||
<Info>
|
||||
The recipient and content are both rendered directly on your resume without a section header, giving your cover letter
|
||||
a clean, professional appearance.
|
||||
</Info>
|
||||
|
||||
## Using fullscreen mode for writing
|
||||
|
||||
For a distraction-free writing experience, use the fullscreen mode in the rich text editor:
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the cover letter item">
|
||||
Click on your cover letter item in the sidebar to open the edit dialog.
|
||||
</Step>
|
||||
|
||||
<Step title="Expand the editor">
|
||||
In either the Recipient or Content field, click the **expand icon** (arrows pointing outward) in the bottom-right
|
||||
corner of the editor.
|
||||
</Step>
|
||||
|
||||
<Step title="Write in fullscreen">
|
||||
The editor will expand to nearly fill your screen, giving you a focused writing environment with all formatting tools
|
||||
available.
|
||||
</Step>
|
||||
|
||||
<Step title="Exit fullscreen">
|
||||
Click the **collapse icon** or press **Escape** to return to the normal view.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Formatting options
|
||||
|
||||
The rich text editor supports various formatting options for your cover letter:
|
||||
|
||||
- **Text styling**: Bold, italic, underline, strikethrough, highlight
|
||||
- **Headings**: H1 through H6 (though typically not needed in a cover letter)
|
||||
- **Alignment**: Left, center, right, or justified text
|
||||
- **Lists**: Bullet points and numbered lists
|
||||
- **Links**: Add hyperlinks to your portfolio or LinkedIn
|
||||
|
||||
## Moving your cover letter to a separate page
|
||||
|
||||
Cover letters are typically on their own page, separate from the resume content. To achieve this:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a second page">
|
||||
In the right sidebar, go to the **Layout** section and add a new page to your resume.
|
||||
</Step>
|
||||
|
||||
<Step title="Move the cover letter section">
|
||||
Use the **Move to** feature to relocate your cover letter section to the second page.
|
||||
|
||||
<Tip>
|
||||
See [Moving items between sections](/guides/moving-items-between-sections) for detailed instructions on how to move sections between pages.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the layout">
|
||||
Check the preview to ensure your cover letter appears on its own page with proper formatting.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
When exporting as PDF, pages are rendered in order. If you want the cover letter first, place it on page 1 and move
|
||||
your resume content to subsequent pages.
|
||||
</Warning>
|
||||
|
||||
## Tips for effective cover letters
|
||||
|
||||
<Tip>**Keep it concise**: Aim for 250-400 words. Recruiters spend about one minute reading cover letters.</Tip>
|
||||
|
||||
<Tip>
|
||||
**Tailor each letter**: Customize your cover letter for each application. Reference specific job requirements and
|
||||
company values.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Use the same styling**: Your cover letter will inherit the fonts and colors from your resume template, ensuring a
|
||||
cohesive look.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before
|
||||
exporting.
|
||||
</Tip>
|
||||
|
||||
## Managing multiple cover letters
|
||||
|
||||
Since cover letters are custom sections, you can:
|
||||
|
||||
- Create multiple cover letter sections for different job applications
|
||||
- Name each section descriptively (e.g., "Cover Letter - Google", "Cover Letter - Meta")
|
||||
- Show or hide sections as needed when exporting
|
||||
- Duplicate sections to use as templates for new applications
|
||||
@@ -35,6 +35,7 @@ If you notice the servers are experiencing high load or downtime, here are some
|
||||
<Info>
|
||||
Reactive Resume is a **free, open-source service** used by thousands of people worldwide. During peak times, the servers may experience higher than usual load.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Check for announcements">
|
||||
@@ -47,7 +48,8 @@ If you notice the servers are experiencing high load or downtime, here are some
|
||||
## A Note on Server Capacity
|
||||
|
||||
<Warning>
|
||||
Reactive Resume is a **free service** that runs on limited server resources. As an open-source project maintained by a single developer, it's not feasible to invest in powerful dedicated servers without community support.
|
||||
Reactive Resume is a **free service** that runs on limited server resources. As an open-source project maintained by a
|
||||
single developer, it's not feasible to invest in powerful dedicated servers without community support.
|
||||
</Warning>
|
||||
|
||||
The reality is:
|
||||
@@ -65,7 +67,8 @@ If you find Reactive Resume valuable and want to help keep the servers running s
|
||||
Your donations directly help cover server costs, improve infrastructure, and keep Reactive Resume free for everyone.
|
||||
|
||||
<Card title="Donate on Open Collective" icon="heart" href="https://opencollective.com/reactive-resume">
|
||||
Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the service running.
|
||||
Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the
|
||||
service running.
|
||||
</Card>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
|
||||
@@ -16,11 +16,12 @@ Changing your resume template is simple and can be done at any time without losi
|
||||
<Frame caption="Screenshot of your resumes dashboard showing all your resume">
|
||||
<img src="/images/guides/choosing-a-template/screenshot-1.webp" alt="Screenshot of your resumes dashboard showing all your resume" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Open the right sidebar">
|
||||
In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options.
|
||||
</Step>
|
||||
<Step title="Open the right sidebar">
|
||||
In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to the Template section">
|
||||
In the right sidebar, find and click on the **Template** section to expand it.
|
||||
@@ -28,6 +29,7 @@ Changing your resume template is simple and can be done at any time without losi
|
||||
<Frame caption="Screenshot of the template section in the right sidebar">
|
||||
<img src="/images/guides/choosing-a-template/screenshot-2.webp" alt="Screenshot of the template section in the right sidebar" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Select your new template">
|
||||
@@ -36,6 +38,7 @@ Changing your resume template is simple and can be done at any time without losi
|
||||
<Frame caption="Screenshot of selecting a new template from the gallery">
|
||||
<img src="/images/guides/choosing-a-template/screenshot-3.webp" alt="Screenshot of selecting a new template from the gallery" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Review and adjust">
|
||||
@@ -48,6 +51,7 @@ Changing your resume template is simple and can be done at any time without losi
|
||||
<Tip>
|
||||
Different templates may display your content differently. Some templates work better with shorter content, while others are designed to handle more detailed information.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -61,13 +65,15 @@ Changing your resume template is simple and can be done at any time without losi
|
||||
- **Tech/Startups**: Modern, balanced templates like Chikorita or Leafish
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Think about content length" icon="ruler">
|
||||
If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes, templates with more white space can make your content feel more substantial.
|
||||
</Accordion>
|
||||
<Accordion title="Think about content length" icon="ruler">
|
||||
If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes,
|
||||
templates with more white space can make your content feel more substantial.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Match your personal brand" icon="palette">
|
||||
Your resume is part of your personal brand. Choose a template that reflects your personality while remaining professional and appropriate for your target roles.
|
||||
</Accordion>
|
||||
<Accordion title="Match your personal brand" icon="palette">
|
||||
Your resume is part of your personal brand. Choose a template that reflects your personality while remaining
|
||||
professional and appropriate for your target roles.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Test with real content" icon="file-lines">
|
||||
Don't choose a template based on how it looks empty. Fill in your actual content and see how it flows across pages. What looks great with sample data might not work as well with your specific information.
|
||||
@@ -81,7 +87,8 @@ Changing your resume template is simple and can be done at any time without losi
|
||||
Reactive Resume includes a variety of professionally designed templates. Each template can be further customized with your choice of colors, fonts, and layout options.
|
||||
|
||||
<Info>
|
||||
All templates support the same features and sections. The difference is purely in how they present your information visually.
|
||||
All templates support the same features and sections. The difference is purely in how they present your information
|
||||
visually.
|
||||
</Info>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
@@ -89,49 +96,49 @@ Reactive Resume includes a variety of professionally designed templates. Each te
|
||||
<img src="/images/templates/azurill.webp" alt="Azurill template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Bronzor">
|
||||
<img src="/images/templates/bronzor.webp" alt="Bronzor template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Bronzor">
|
||||
<img src="/images/templates/bronzor.webp" alt="Bronzor template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Chikorita">
|
||||
<img src="/images/templates/chikorita.webp" alt="Chikorita template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Chikorita">
|
||||
<img src="/images/templates/chikorita.webp" alt="Chikorita template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Ditto">
|
||||
<img src="/images/templates/ditto.webp" alt="Ditto template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Ditto">
|
||||
<img src="/images/templates/ditto.webp" alt="Ditto template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Ditgar">
|
||||
<img src="/images/templates/ditgar.webp" alt="Ditgar template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Ditgar">
|
||||
<img src="/images/templates/ditgar.webp" alt="Ditgar template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Gengar">
|
||||
<img src="/images/templates/gengar.webp" alt="Gengar template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Gengar">
|
||||
<img src="/images/templates/gengar.webp" alt="Gengar template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Glalie">
|
||||
<img src="/images/templates/glalie.webp" alt="Glalie template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Glalie">
|
||||
<img src="/images/templates/glalie.webp" alt="Glalie template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Kakuna">
|
||||
<img src="/images/templates/kakuna.webp" alt="Kakuna template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Kakuna">
|
||||
<img src="/images/templates/kakuna.webp" alt="Kakuna template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Lapras">
|
||||
<img src="/images/templates/lapras.webp" alt="Lapras template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Lapras">
|
||||
<img src="/images/templates/lapras.webp" alt="Lapras template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Leafish">
|
||||
<img src="/images/templates/leafish.webp" alt="Leafish template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Leafish">
|
||||
<img src="/images/templates/leafish.webp" alt="Leafish 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>
|
||||
<Frame caption="Onyx">
|
||||
<img src="/images/templates/onyx.webp" alt="Onyx template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Pikachu">
|
||||
<img src="/images/templates/pikachu.webp" alt="Pikachu template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
<Frame caption="Pikachu">
|
||||
<img src="/images/templates/pikachu.webp" alt="Pikachu template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Rhyhorn">
|
||||
<img src="/images/templates/rhyhorn.webp" alt="Rhyhorn template preview" style={{ aspectRatio: "210/297" }} />
|
||||
@@ -144,13 +151,11 @@ Reactive Resume includes a variety of professionally designed templates. Each te
|
||||
|
||||
After selecting a template, you can customize it further:
|
||||
|
||||
| Setting | Description |
|
||||
|---------|-------------|
|
||||
| **Colors** | Change the primary color scheme to match your personal brand |
|
||||
| **Typography** | Choose from various Google Fonts for headings and body text |
|
||||
| **Layout** | Adjust sidebar width, section order, and page margins |
|
||||
| **Spacing** | Fine-tune gaps between sections and elements |
|
||||
| Setting | Description |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| **Colors** | Change the primary color scheme to match your personal brand |
|
||||
| **Typography** | Choose from various Google Fonts for headings and body text |
|
||||
| **Layout** | Adjust sidebar width, section order, and page margins |
|
||||
| **Spacing** | Fine-tune gaps between sections and elements |
|
||||
|
||||
<Tip>
|
||||
For more advanced customization options, check out [Using Custom CSS](/guides/using-custom-css).
|
||||
</Tip>
|
||||
<Tip>For more advanced customization options, check out [Using Custom CSS](/guides/using-custom-css).</Tip>
|
||||
|
||||
@@ -23,6 +23,7 @@ description: "Learn how to create an account on Reactive Resume and get started
|
||||
<Warning>
|
||||
Make sure to choose a username you're happy with, as it will be part of your public resume URL (e.g., `rxresu.me/your-username/resume-slug`).
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Sign up and log in">
|
||||
@@ -71,15 +72,19 @@ description: "Learn how to create an account on Reactive Resume and get started
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Username Already Taken">
|
||||
If you see an error that your username is already taken, try a different variation. Usernames must be unique across all users.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email Already Registered">
|
||||
If your email is already registered, you can use the **Forgot Password** link on the login page to reset your password and regain access to your account.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Didn't Receive Verification Email">
|
||||
Check your spam folder first. If you still don't see it, you can request a new verification email from your account settings.
|
||||
</Accordion>
|
||||
<Accordion title="Username Already Taken">
|
||||
If you see an error that your username is already taken, try a different variation. Usernames must be unique across
|
||||
all users.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email Already Registered">
|
||||
If your email is already registered, you can use the **Forgot Password** link on the login page to reset your
|
||||
password and regain access to your account.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Didn't Receive Verification Email">
|
||||
Check your spam folder first. If you still don't see it, you can request a new verification email from your account
|
||||
settings.
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -10,6 +10,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
|
||||
<Tip>
|
||||
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Open the Resumes Dashboard">
|
||||
@@ -18,11 +19,12 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
|
||||
<Info>
|
||||
This is where you can create, organize, and manage multiple resumes for different roles or versions.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Click “Create a new resume”">
|
||||
In the Resumes Dashboard, click on the <Badge>Create a new resume</Badge> card to open the creation form.
|
||||
</Step>
|
||||
<Step title="Click “Create a new resume”">
|
||||
In the Resumes Dashboard, click on the <Badge>Create a new resume</Badge> card to open the creation form.
|
||||
</Step>
|
||||
|
||||
<Step title="Name your resume">
|
||||
Fill in the resume name. This can be a generic name (e.g., “General Resume”) or something tied to the position you’re applying for.
|
||||
@@ -30,6 +32,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
|
||||
<Tip>
|
||||
If you can’t think of a name yet, click the <Badge>Magic Wand</Badge> button to generate a random name for you.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Review (or edit) the slug">
|
||||
@@ -38,6 +41,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
|
||||
<Warning>
|
||||
If you choose to publicly share your resume, it will be accessible at `https://rxresu.me/{username}/{slug}`.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Add tags (optional)">
|
||||
@@ -46,6 +50,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na
|
||||
<Info>
|
||||
You can filter resumes by tags later from the Dashboard.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Open the resume in the builder">
|
||||
|
||||
@@ -8,9 +8,10 @@ description: "Learn how to permanently delete your Reactive Resume account and a
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Danger Zone">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on <Badge>Danger Zone</Badge>.
|
||||
</Step>
|
||||
<Step title="Navigate to Danger Zone">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on <Badge>Danger Zone</Badge>.
|
||||
</Step>
|
||||
|
||||
<Step title="Type the confirmation text">
|
||||
On the Danger Zone page, type <Badge>delete</Badge> into the confirmation input.
|
||||
@@ -18,6 +19,7 @@ description: "Learn how to permanently delete your Reactive Resume account and a
|
||||
<Info>
|
||||
The <Badge>Delete Account</Badge> button will stay disabled until the confirmation text matches exactly.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Delete your account">
|
||||
@@ -26,10 +28,10 @@ description: "Learn how to permanently delete your Reactive Resume account and a
|
||||
<Warning>
|
||||
This action cannot be undone. All your data will be permanently deleted.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="You will be signed out">
|
||||
After deletion completes, you will be signed out and redirected to the homepage.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -6,11 +6,16 @@ description: "Learn how to reorganize and format your resume content to fit with
|
||||
When your resume content overflows the page, Reactive Resume displays a warning just below the page. This guide explains how to reorganize and adjust your content so everything fits within your chosen page format.
|
||||
|
||||
<Frame caption="Screenshot of the overflow warning message in the resume builder">
|
||||
<img src="/images/guides/fitting-content-on-a-page/screenshot-1.webp" alt="Screenshot of the overflow warning message in the resume builder" />
|
||||
<img
|
||||
src="/images/guides/fitting-content-on-a-page/screenshot-1.webp"
|
||||
alt="Screenshot of the overflow warning message in the resume builder"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
While Reactive Resume supports multi-page resumes, each page has a fixed height based on your chosen format (A4 or Letter). If a single page's content exceeds this height, parts of your resume may be rendered improperly when printed or exported.
|
||||
While Reactive Resume supports multi-page resumes, each page has a fixed height based on your chosen format (A4 or
|
||||
Letter). If a single page's content exceeds this height, parts of your resume may be rendered improperly when printed
|
||||
or exported.
|
||||
</Warning>
|
||||
|
||||
## Quick fixes
|
||||
@@ -31,7 +36,8 @@ With Free-Form:
|
||||
To switch formats, go to the **Page** section in the right sidebar and change the **Format** to Free-Form. For more details, see [Selecting the right page format](/guides/selecting-page-format).
|
||||
|
||||
<Tip>
|
||||
Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to print physical copies.
|
||||
Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to
|
||||
print physical copies.
|
||||
</Tip>
|
||||
|
||||
### 2. Shorten text blocks
|
||||
@@ -43,9 +49,7 @@ Long paragraphs take up space without adding proportional value. Review each sec
|
||||
- **Focus on impact.** Keep measurable achievements; cut generic descriptions.
|
||||
- **Limit bullets per entry.** Three to five bullets per job is usually enough.
|
||||
|
||||
<Tip>
|
||||
Read each bullet point and ask: "Does this help me get an interview?" If not, cut it.
|
||||
</Tip>
|
||||
<Tip>Read each bullet point and ask: "Does this help me get an interview?" If not, cut it.</Tip>
|
||||
|
||||
### 3. Use multi-column layouts
|
||||
|
||||
@@ -54,21 +58,25 @@ Some sections work better in multiple columns, especially lists of short items.
|
||||
In the left sidebar, find the section you want to adjust, click on the section heading (not an item), and change the **Columns** setting.
|
||||
|
||||
<Frame caption="Screenshot of the columns setting for a section">
|
||||
<img src="/images/guides/fitting-content-on-a-page/screenshot-2.webp" alt="Screenshot of the columns setting for a section" />
|
||||
<img
|
||||
src="/images/guides/fitting-content-on-a-page/screenshot-2.webp"
|
||||
alt="Screenshot of the columns setting for a section"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
Good candidates for multi-column layouts:
|
||||
|
||||
| Section | Recommended Columns |
|
||||
|---------|---------------------|
|
||||
| Skills | > 2 columns |
|
||||
| Languages | > 3 columns |
|
||||
| Interests | > 2 columns |
|
||||
| Profiles | > 3 columns |
|
||||
| Certifications (if brief) | > 2 columns |
|
||||
| Section | Recommended Columns |
|
||||
| ------------------------- | ------------------- |
|
||||
| Skills | > 2 columns |
|
||||
| Languages | > 3 columns |
|
||||
| Interests | > 2 columns |
|
||||
| Profiles | > 3 columns |
|
||||
| Certifications (if brief) | > 2 columns |
|
||||
|
||||
<Info>
|
||||
Multi-column layouts work best for sections with short, uniform items. Sections with long descriptions (like Experience or Projects) usually work better in a single column.
|
||||
Multi-column layouts work best for sections with short, uniform items. Sections with long descriptions (like
|
||||
Experience or Projects) usually work better in a single column.
|
||||
</Info>
|
||||
|
||||
### 4. Move items to another page
|
||||
@@ -84,7 +92,8 @@ Use the **Move to** feature to relocate items:
|
||||
For detailed instructions, see [Moving items between sections](/guides/moving-items-between-sections).
|
||||
|
||||
<Tip>
|
||||
Keep your most recent and relevant experience on page one. Move older positions or less critical sections (like older projects or volunteer work) to subsequent pages.
|
||||
Keep your most recent and relevant experience on page one. Move older positions or less critical sections (like older
|
||||
projects or volunteer work) to subsequent pages.
|
||||
</Tip>
|
||||
|
||||
### 5. Adjust layout and design settings
|
||||
@@ -93,17 +102,19 @@ The right sidebar contains settings that control how much space your content use
|
||||
|
||||
Open the right sidebar and explore these options:
|
||||
|
||||
| Setting | Where to find it | Effect |
|
||||
|---------|------------------|--------|
|
||||
| **Font size** | Typography | Smaller fonts fit more text per line and per page |
|
||||
| **Line height** | Typography | Tighter line spacing reduces vertical space |
|
||||
| **Margins** | Page | Smaller margins give you more usable area |
|
||||
| **Section gaps** | Page | Reducing gaps between sections saves space |
|
||||
| **Sidebar width** | Layout | Adjusting the sidebar ratio can balance content better |
|
||||
| **Picture size** | Picture (left sidebar) | A smaller photo leaves more room for text |
|
||||
| Setting | Where to find it | Effect |
|
||||
| ----------------- | ---------------------- | ------------------------------------------------------ |
|
||||
| **Font size** | Typography | Smaller fonts fit more text per line and per page |
|
||||
| **Line height** | Typography | Tighter line spacing reduces vertical space |
|
||||
| **Margins** | Page | Smaller margins give you more usable area |
|
||||
| **Section gaps** | Page | Reducing gaps between sections saves space |
|
||||
| **Sidebar width** | Layout | Adjusting the sidebar ratio can balance content better |
|
||||
| **Picture size** | Picture (left sidebar) | A smaller photo leaves more room for text |
|
||||
|
||||
<Info>
|
||||
**Reducing font size is the best option** when you need to fit more content while keeping A4 or Letter format. Reducing body font from 11pt to 10.5pt (or even 10pt) can free up significant space while remaining readable. The editor supports 0.1pt increments, so you can fine-tune precisely.
|
||||
**Reducing font size is the best option** when you need to fit more content while keeping A4 or Letter format.
|
||||
Reducing body font from 11pt to 10.5pt (or even 10pt) can free up significant space while remaining readable. The
|
||||
editor supports 0.1pt increments, so you can fine-tune precisely.
|
||||
</Info>
|
||||
|
||||
### 6. Hide less important sections
|
||||
@@ -130,7 +141,8 @@ Try this order:
|
||||
5. Fine-tune font size and spacing last
|
||||
|
||||
<Tip>
|
||||
Use the live preview to see changes as you make them. Small adjustments add up—reducing font size by 0.5pt combined with slightly smaller margins can recover enough space for several lines of content.
|
||||
Use the live preview to see changes as you make them. Small adjustments add up—reducing font size by 0.5pt combined
|
||||
with slightly smaller margins can recover enough space for several lines of content.
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
+646
-983
File diff suppressed because it is too large
Load Diff
@@ -8,13 +8,15 @@ description: "Learn how to connect or disconnect social sign-in providers like G
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Find the social provider you want to link">
|
||||
On the Authentication page, you may see sections for one or more providers (for example, <Badge>Google</Badge> or <Badge>GitHub</Badge>), depending on what is enabled on your instance.
|
||||
</Step>
|
||||
<Step title="Find the social provider you want to link">
|
||||
On the Authentication page, you may see sections for one or more providers (for example, <Badge>Google</Badge> or{" "}
|
||||
<Badge>GitHub</Badge>), depending on what is enabled on your instance.
|
||||
</Step>
|
||||
|
||||
<Step title="Connect your account">
|
||||
Click the <Badge>Connect</Badge> button for the provider you want to link. You'll be redirected to the provider to authorize access, then returned to the Authentication settings page.
|
||||
@@ -22,6 +24,7 @@ description: "Learn how to connect or disconnect social sign-in providers like G
|
||||
<Info>
|
||||
After a successful link, the button will change to <Badge>Disconnect</Badge>.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Disconnect a provider (optional)">
|
||||
@@ -30,6 +33,6 @@ description: "Learn how to connect or disconnect social sign-in providers like G
|
||||
<Warning>
|
||||
Before disconnecting, make sure you still have another way to sign in (for example, a password or another linked provider) so you don't get locked out.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ If you have a long work history or extensive project list, you may want to split
|
||||
<Info>
|
||||
Make sure you have at least one item in a section (e.g., an experience entry, project, or skill) before proceeding.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Locate the item you want to move">
|
||||
@@ -28,6 +29,7 @@ If you have a long work history or extensive project list, you may want to split
|
||||
<Frame caption="Screenshot of the left sidebar with an expanded section containing multiple items">
|
||||
<img src="/images/guides/moving-items-between-sections/screenshot-1.webp" alt="Screenshot of the left sidebar with an expanded section containing multiple items" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Open the item dropdown menu">
|
||||
@@ -36,11 +38,13 @@ If you have a long work history or extensive project list, you may want to split
|
||||
<Frame caption="Screenshot of the dropdown menu icon on a section item">
|
||||
<img src="/images/guides/moving-items-between-sections/screenshot-2.webp" alt="Screenshot of the dropdown menu icon on a section item" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Hover over 'Move to'">
|
||||
In the dropdown menu, hover over or click the <Badge>Move to</Badge> option. This will open a submenu showing all available destinations.
|
||||
</Step>
|
||||
<Step title="Hover over 'Move to'">
|
||||
In the dropdown menu, hover over or click the <Badge>Move to</Badge> option. This will open a submenu showing all
|
||||
available destinations.
|
||||
</Step>
|
||||
|
||||
<Step title="Select a destination">
|
||||
The submenu displays available destinations organized by:
|
||||
@@ -54,10 +58,11 @@ If you have a long work history or extensive project list, you may want to split
|
||||
<Frame caption="Screenshot of the 'Move to' submenu with destination options">
|
||||
<img src="/images/guides/moving-items-between-sections/screenshot-3.webp" alt="Screenshot of the 'Move to' submenu with destination options" />
|
||||
</Frame>
|
||||
|
||||
|
||||
<Tip>
|
||||
If a custom section of the same type doesn't exist on your target page, Reactive Resume will **automatically create one** for you. This makes it easy to split sections across pages without manual setup.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the move">
|
||||
@@ -69,6 +74,7 @@ If you have a long work history or extensive project list, you may want to split
|
||||
<Frame caption="Screenshot of the item in its new location">
|
||||
<img src="/images/guides/moving-items-between-sections/screenshot-4.webp" alt="Screenshot of the item in its new location" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -91,6 +97,7 @@ A common use case is when your work history is too long to fit on a single page.
|
||||
<Info>
|
||||
If no Experience section exists on page 2, a new custom section will be created automatically with the same type, so your formatting stays consistent.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Review the result">
|
||||
@@ -101,21 +108,25 @@ A common use case is when your work history is too long to fit on a single page.
|
||||
- The section headings and styling remain consistent
|
||||
|
||||
{/* TODO: Add screenshot showing a resume with Experience split across page 1 and page 2 */}
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Tips for organizing multi-page resumes
|
||||
|
||||
<Tip>
|
||||
**Keep it logical**: Maintain chronological order within each page. Move complete job entries rather than splitting a single role across pages.
|
||||
**Keep it logical**: Maintain chronological order within each page. Move complete job entries rather than splitting a
|
||||
single role across pages.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Use custom section names**: After moving items to a new custom section, you can rename it (e.g., "Earlier Experience" or "Additional Projects") to provide context for recruiters.
|
||||
**Use custom section names**: After moving items to a new custom section, you can rename it (e.g., "Earlier
|
||||
Experience" or "Additional Projects") to provide context for recruiters.
|
||||
</Tip>
|
||||
|
||||
<Warning>
|
||||
Moving items reorganizes your content but doesn't affect the data itself. You can always move items back or to different sections as needed.
|
||||
Moving items reorganizes your content but doesn't affect the data itself. You can always move items back or to
|
||||
different sections as needed.
|
||||
</Warning>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -11,10 +11,10 @@ Reactive Resume offers three page format options: **A4**, **Letter**, and **Free
|
||||
|
||||
A4 is the international standard paper size used in most countries outside North America. When you select A4, your resume pages conform to these dimensions:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Width | 210mm (794px) |
|
||||
| Height | 297mm (1123px) |
|
||||
| Property | Value |
|
||||
| -------- | -------------- |
|
||||
| Width | 210mm (794px) |
|
||||
| Height | 297mm (1123px) |
|
||||
|
||||
Choose A4 if you're applying to jobs internationally or in regions that use the metric system.
|
||||
|
||||
@@ -22,10 +22,10 @@ Choose A4 if you're applying to jobs internationally or in regions that use the
|
||||
|
||||
Letter is the standard paper size in the United States and Canada. When you select Letter, your resume pages conform to these dimensions:
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Width | 216mm (816px) |
|
||||
| Height | 279mm (1056px) |
|
||||
| Property | Value |
|
||||
| -------- | -------------- |
|
||||
| Width | 216mm (816px) |
|
||||
| Height | 279mm (1056px) |
|
||||
|
||||
Choose Letter if you're applying to jobs in North America.
|
||||
|
||||
@@ -33,13 +33,14 @@ Choose Letter if you're applying to jobs in North America.
|
||||
|
||||
Free-Form is a modern format designed for digital-first resumes. Instead of conforming to a physical page size, Free-Form produces a **single continuous page** with no height limit. The width matches A4 (210mm), but the height extends as needed to fit all your content.
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Width | 210mm (794px) |
|
||||
| Height | Unlimited |
|
||||
| Property | Value |
|
||||
| -------- | ------------- |
|
||||
| Width | 210mm (794px) |
|
||||
| Height | Unlimited |
|
||||
|
||||
<Info>
|
||||
With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much content you have.
|
||||
With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much
|
||||
content you have.
|
||||
</Info>
|
||||
|
||||
## Why Free-Form exists
|
||||
@@ -56,7 +57,8 @@ When your resume is processed digitally:
|
||||
Since physical page constraints no longer matter for most use cases, Free-Form lets you focus on content rather than worrying about fitting everything into arbitrary page limits.
|
||||
|
||||
<Tip>
|
||||
If you don't plan on printing your resume, Free-Form is often the simplest choice. You never have to worry about content overflowing or awkward page breaks.
|
||||
If you don't plan on printing your resume, Free-Form is often the simplest choice. You never have to worry about
|
||||
content overflowing or awkward page breaks.
|
||||
</Tip>
|
||||
|
||||
## How to change your page format
|
||||
@@ -66,13 +68,13 @@ Since physical page constraints no longer matter for most use cases, Free-Form l
|
||||
Navigate to your Dashboard and click on the resume you want to edit.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the right sidebar">
|
||||
In the resume builder, look for the right sidebar on the right side of the screen.
|
||||
</Step>
|
||||
<Step title="Open the right sidebar">
|
||||
In the resume builder, look for the right sidebar on the right side of the screen.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to the Page section">
|
||||
In the right sidebar, find and click on the **Page** section to expand it.
|
||||
</Step>
|
||||
<Step title="Navigate to the Page section">
|
||||
In the right sidebar, find and click on the **Page** section to expand it.
|
||||
</Step>
|
||||
|
||||
<Step title="Select your format">
|
||||
Find the **Format** dropdown and select your preferred option: A4, Letter, or Free-Form.
|
||||
@@ -80,6 +82,7 @@ Since physical page constraints no longer matter for most use cases, Free-Form l
|
||||
<Frame caption="Screenshot of the Format dropdown in the Page section">
|
||||
<img src="/images/guides/selecting-page-format/screenshot-1.webp" alt="Screenshot of the Format dropdown in the Page section" />
|
||||
</Frame>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Review the preview">
|
||||
@@ -91,18 +94,19 @@ Since physical page constraints no longer matter for most use cases, Free-Form l
|
||||
|
||||
Use this quick reference to decide which format fits your needs:
|
||||
|
||||
| Situation | Recommended Format |
|
||||
|-----------|-------------------|
|
||||
| Applying to jobs in North America | Letter |
|
||||
| Applying to jobs internationally | A4 |
|
||||
| Digital-only applications (no printing) | Free-Form |
|
||||
| Uploading to ATS or job portals | Free-Form |
|
||||
| Need to print physical copies | A4 or Letter |
|
||||
| Long resume with lots of content | Free-Form |
|
||||
| Traditional industries (law, finance) | A4 or Letter |
|
||||
| Situation | Recommended Format |
|
||||
| --------------------------------------- | ------------------ |
|
||||
| Applying to jobs in North America | Letter |
|
||||
| Applying to jobs internationally | A4 |
|
||||
| Digital-only applications (no printing) | Free-Form |
|
||||
| Uploading to ATS or job portals | Free-Form |
|
||||
| Need to print physical copies | A4 or Letter |
|
||||
| Long resume with lots of content | Free-Form |
|
||||
| Traditional industries (law, finance) | A4 or Letter |
|
||||
|
||||
<Warning>
|
||||
If you switch from Free-Form to A4 or Letter, your content will be split across multiple pages. Review the result carefully to ensure page breaks don't occur in awkward places.
|
||||
If you switch from Free-Form to A4 or Letter, your content will be split across multiple pages. Review the result
|
||||
carefully to ensure page breaks don't occur in awkward places.
|
||||
</Warning>
|
||||
|
||||
## Example PDFs
|
||||
@@ -110,10 +114,22 @@ Use this quick reference to decide which format fits your needs:
|
||||
Download these sample resumes to see how different formats affect the final PDF output:
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card arrow horizontal title="A4 Format Example" icon="file-pdf" href="https://github.com/user-attachments/files/24849548/a4.pdf">
|
||||
<Card
|
||||
arrow
|
||||
horizontal
|
||||
title="A4 Format Example"
|
||||
icon="file-pdf"
|
||||
href="https://github.com/user-attachments/files/24849548/a4.pdf"
|
||||
>
|
||||
A sample resume in A4 format showing traditional page breaks and constraints.
|
||||
</Card>
|
||||
<Card arrow horizontal title="Free-Form Example" icon="file-lines" href="https://github.com/user-attachments/files/24849552/free-form.pdf">
|
||||
<Card
|
||||
arrow
|
||||
horizontal
|
||||
title="Free-Form Example"
|
||||
icon="file-lines"
|
||||
href="https://github.com/user-attachments/files/24849552/free-form.pdf"
|
||||
>
|
||||
A sample resume in Free-Form format showing a continuous single-page layout.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -125,17 +141,20 @@ Download these sample resumes to see how different formats affect the final PDF
|
||||
Yes. ATS systems parse the text content of your PDF, not the page dimensions. Free-Form resumes are fully compatible with applicant tracking systems.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I switch formats after creating my resume?">
|
||||
Absolutely. You can change the format at any time from the Page section in the right sidebar. Your content is preserved—only the layout changes.
|
||||
</Accordion>
|
||||
<Accordion title="Can I switch formats after creating my resume?">
|
||||
Absolutely. You can change the format at any time from the Page section in the right sidebar. Your content is
|
||||
preserved—only the layout changes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if a job posting asks for a 'one-page resume'?">
|
||||
If the employer specifically requests a one-page resume and expects a traditional format, use A4 or Letter and ensure your content fits within a single page. See [Fitting content on a page](/guides/fitting-content-on-a-page) for tips.
|
||||
</Accordion>
|
||||
<Accordion title="What if a job posting asks for a 'one-page resume'?">
|
||||
If the employer specifically requests a one-page resume and expects a traditional format, use A4 or Letter and ensure
|
||||
your content fits within a single page. See [Fitting content on a page](/guides/fitting-content-on-a-page) for tips.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Does Free-Form affect the file size?">
|
||||
Slightly. A longer single page may produce a marginally larger PDF than a paginated version of the same content, but the difference is negligible for typical resume lengths.
|
||||
</Accordion>
|
||||
<Accordion title="Does Free-Form affect the file size?">
|
||||
Slightly. A longer single page may produce a marginally larger PDF than a paginated version of the same content, but
|
||||
the difference is negligible for typical resume lengths.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Which format should I use for LinkedIn?">
|
||||
LinkedIn doesn't display uploaded resumes in their original format—it extracts the content. Free-Form works perfectly for LinkedIn uploads.
|
||||
|
||||
@@ -3,22 +3,33 @@ 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.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Register a new device">
|
||||
In the <Badge>Passkeys</Badge> section, click on the <Badge>Register New Device</Badge> button.
|
||||
</Step>
|
||||
<Step title="Register a new device">
|
||||
In the <Badge>Passkeys</Badge> section, click on the <Badge>Register New Device</Badge> button.
|
||||
</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”).
|
||||
</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”).
|
||||
</Step>
|
||||
|
||||
<Step title="Complete the passkey prompt">
|
||||
Your browser or device will show a passkey prompt (WebAuthn). Follow the on-screen instructions to register your passkey using your biometric (Face ID / Touch ID / fingerprint) or device PIN.
|
||||
@@ -26,6 +37,7 @@ description: "Learn how to register passkeys (WebAuthn) to sign in securely usin
|
||||
<Info>
|
||||
Passkeys are tied to your device (or password manager) and are a more secure alternative to passwords.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Manage passkeys (optional)">
|
||||
@@ -34,6 +46,6 @@ description: "Learn how to register passkeys (WebAuthn) to sign in securely usin
|
||||
<Warning>
|
||||
Deleting a passkey cannot be undone. After deletion, you won't be able to sign in using that passkey anymore.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
|
||||
<Warning>
|
||||
Two-factor authentication requires a password to be set on your account. If you signed up using a social provider (Google or GitHub), you'll need to set a password first from the Authentication settings page.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Sign in to the dashboard">
|
||||
@@ -18,15 +19,18 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
|
||||
<Tip>
|
||||
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Click on Enable 2FA">
|
||||
On the Authentication page, find the <Badge>Two-Factor Authentication</Badge> section and click on the <Badge>Enable 2FA</Badge> button.
|
||||
</Step>
|
||||
<Step title="Click on Enable 2FA">
|
||||
On the Authentication page, find the <Badge>Two-Factor Authentication</Badge> section and click on the{" "}
|
||||
<Badge>Enable 2FA</Badge> button.
|
||||
</Step>
|
||||
|
||||
<Step title="Enter your password">
|
||||
You will be asked to re-enter your password to authenticate that it's really you. Enter your password and click continue.
|
||||
@@ -34,6 +38,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
|
||||
<Info>
|
||||
This password verification step ensures that only authorized users can enable two-factor authentication on your account.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Scan the QR code">
|
||||
@@ -51,6 +56,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
|
||||
<Info>
|
||||
You can also copy the secret key above the QR code and paste it into your authenticator app if you prefer manual entry.
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Save your backup codes">
|
||||
@@ -59,6 +65,7 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
|
||||
<Warning>
|
||||
Make sure to copy and store these backup codes in a safe place. If you lose your authenticator device and don't have backup codes, you may lose access to your account.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Sign in with 2FA">
|
||||
@@ -67,5 +74,6 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr
|
||||
<Tip>
|
||||
If you've lost access to your authenticator device, you can use one of your backup codes to sign in. Remember that each backup code can only be used once.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -29,13 +29,11 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can
|
||||
Navigate to your resume in the resume builder.
|
||||
</Step>
|
||||
|
||||
<Step title="Go to the Sharing section">
|
||||
In the **right sidebar**, select **Sharing**.
|
||||
</Step>
|
||||
<Step title="Go to the Sharing section">In the **right sidebar**, select **Sharing**.</Step>
|
||||
|
||||
<Step title="Toggle 'Allow Public Access'">
|
||||
Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL.
|
||||
</Step>
|
||||
<Step title="Toggle 'Allow Public Access'">
|
||||
Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL.
|
||||
</Step>
|
||||
|
||||
<Step title="Copy your public URL">
|
||||
Your public URL is displayed below the toggle. It follows this format:
|
||||
@@ -45,11 +43,13 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can
|
||||
```
|
||||
|
||||
Click the **copy** button to copy the URL to your clipboard.
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details.
|
||||
The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your
|
||||
slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details.
|
||||
</Tip>
|
||||
|
||||
## How the public URL works
|
||||
@@ -62,7 +62,8 @@ When someone visits your public resume URL:
|
||||
4. **Views are tracked** - Each visit is counted in your resume statistics (see below)
|
||||
|
||||
<Info>
|
||||
Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your public resume is always in sync.
|
||||
Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your
|
||||
public resume is always in sync.
|
||||
</Info>
|
||||
|
||||
## Tracking views and downloads
|
||||
@@ -75,15 +76,16 @@ In the resume builder, open the **right sidebar** and select **Statistics**.
|
||||
|
||||
You'll see:
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| **Views** | Number of times your public resume page was visited |
|
||||
| **Last viewed** | The date when your resume was last viewed |
|
||||
| **Downloads** | Number of times your resume was downloaded (via the public page) |
|
||||
| **Last downloaded** | The date when your resume was last downloaded |
|
||||
| Metric | Description |
|
||||
| ------------------- | ---------------------------------------------------------------- |
|
||||
| **Views** | Number of times your public resume page was visited |
|
||||
| **Last viewed** | The date when your resume was last viewed |
|
||||
| **Downloads** | Number of times your resume was downloaded (via the public page) |
|
||||
| **Last downloaded** | The date when your resume was last downloaded |
|
||||
|
||||
<Info>
|
||||
Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your existing stats are preserved.
|
||||
Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your
|
||||
existing stats are preserved.
|
||||
</Info>
|
||||
|
||||
### What counts as a view?
|
||||
@@ -95,7 +97,8 @@ A view is counted each time someone loads your public resume page. This includes
|
||||
- Search engine visits (if your resume is indexed)
|
||||
|
||||
<Note>
|
||||
Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your resume has.
|
||||
Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your
|
||||
resume has.
|
||||
</Note>
|
||||
|
||||
## Password protecting your resume
|
||||
@@ -115,13 +118,11 @@ When password protection is enabled:
|
||||
First, make sure **Allow Public Access** is turned on in the **Sharing** section.
|
||||
</Step>
|
||||
|
||||
<Step title="Click 'Set Password'">
|
||||
Below the public URL, click the **Set Password** button.
|
||||
</Step>
|
||||
<Step title="Click 'Set Password'">Below the public URL, click the **Set Password** button.</Step>
|
||||
|
||||
<Step title="Enter your password">
|
||||
Type a password (6-64 characters) and confirm. This password will be required to view your resume.
|
||||
</Step>
|
||||
<Step title="Enter your password">
|
||||
Type a password (6-64 characters) and confirm. This password will be required to view your resume.
|
||||
</Step>
|
||||
|
||||
<Step title="Share the password separately">
|
||||
Share the password with your intended audience through a secure channel (e.g., direct message, email).
|
||||
@@ -149,21 +150,21 @@ Your resume will become accessible to anyone with the public URL.
|
||||
Add your public resume URL to your LinkedIn profile's **Featured** section or **Contact Info**. Recruiters can view your detailed resume directly.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email signature" icon="envelope">
|
||||
Include your resume link in your email signature. Anyone you correspond with can quickly access your resume.
|
||||
</Accordion>
|
||||
<Accordion title="Email signature" icon="envelope">
|
||||
Include your resume link in your email signature. Anyone you correspond with can quickly access your resume.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Portfolio website" icon="globe">
|
||||
Embed or link to your resume from your personal website. The link always shows your latest resume.
|
||||
</Accordion>
|
||||
<Accordion title="Portfolio website" icon="globe">
|
||||
Embed or link to your resume from your personal website. The link always shows your latest resume.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Job applications" icon="briefcase">
|
||||
Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads.
|
||||
</Accordion>
|
||||
<Accordion title="Job applications" icon="briefcase">
|
||||
Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Networking events" icon="users">
|
||||
Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version.
|
||||
</Accordion>
|
||||
<Accordion title="Networking events" icon="users">
|
||||
Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Confidential job search" icon="lock">
|
||||
Use password protection to share your resume only with specific recruiters while keeping it hidden from your current employer.
|
||||
@@ -185,7 +186,8 @@ When public access is disabled:
|
||||
- Password protection settings are preserved
|
||||
|
||||
<Tip>
|
||||
If you only want to temporarily hide your resume, consider using password protection instead of disabling public access entirely. This way, your URL remains active for people who have the password.
|
||||
If you only want to temporarily hide your resume, consider using password protection instead of disabling public
|
||||
access entirely. This way, your URL remains active for people who have the password.
|
||||
</Tip>
|
||||
|
||||
## Frequently asked questions
|
||||
@@ -195,17 +197,19 @@ When public access is disabled:
|
||||
Yes! The URL is based on your **username** and the resume's **slug**. You can change the slug in the **Update Resume** dialog. To open it, right-click on your resume card in the dashboard and select "Update". The username is set in your account settings.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Will search engines index my public resume?">
|
||||
Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your resume private.
|
||||
</Accordion>
|
||||
<Accordion title="Will search engines index my public resume?">
|
||||
Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your
|
||||
resume private.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do views from my own visits count?">
|
||||
Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged in to your account are not included in the view statistics.
|
||||
</Accordion>
|
||||
<Accordion title="Do views from my own visits count?">
|
||||
Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged
|
||||
in to your account are not included in the view statistics.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I see who viewed my resume?">
|
||||
No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy.
|
||||
</Accordion>
|
||||
<Accordion title="Can I see who viewed my resume?">
|
||||
No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if I change my username?">
|
||||
Your public URL will change to reflect the new username. Old URLs will stop working immediately. You might need to update any links you've shared.
|
||||
|
||||
@@ -10,11 +10,13 @@ description: "Learn how to update your profile information including your name,
|
||||
<Tip>
|
||||
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Profile settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Profile</Badge> link.
|
||||
</Step>
|
||||
<Step title="Navigate to Profile settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on the <Badge>Profile</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Update your information">
|
||||
On the Profile page, you can update the following information:
|
||||
@@ -26,6 +28,7 @@ description: "Learn how to update your profile information including your name,
|
||||
<Warning>
|
||||
If you update your email address, you will receive a verification link on your current email address. The change will only be accepted after you click on the verification link.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Verify email changes (if applicable)">
|
||||
@@ -34,5 +37,6 @@ description: "Learn how to update your profile information including your name,
|
||||
<Tip>
|
||||
Make sure to check your spam folder if you don't see the verification email in your inbox. The verification link is required to complete the email address change.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
+15
-10
@@ -8,9 +8,10 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Artificial Intelligence settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on <Badge>Artificial Intelligence</Badge>.
|
||||
</Step>
|
||||
<Step title="Navigate to Artificial Intelligence settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on <Badge>Artificial Intelligence</Badge>.
|
||||
</Step>
|
||||
|
||||
<Step title="How your data is stored and sent">
|
||||
When you configure your AI provider settings on this page, all information (such as the API key and model) is stored locally in your browser and **not** in our database.
|
||||
@@ -20,6 +21,7 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
|
||||
</Info>
|
||||
|
||||
However, whenever you use an AI-assisted feature (such as text generation), these credentials need to be sent to the server **temporarily and only for the duration of your request**. This is required to connect with the AI provider on your behalf. See below for a deeper explanation.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Choose a provider">
|
||||
@@ -29,11 +31,13 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
|
||||
- **Google Gemini**
|
||||
- **Anthropic Claude**
|
||||
- **Ollama** (coming soon)
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Enter a model name">
|
||||
In the <Badge>Model</Badge> field, enter the model you want to use (for example, <code>gpt-5.2</code>, <code>claude-4.5-opus</code>, or <code>gemini-3-flash</code>).
|
||||
</Step>
|
||||
<Step title="Enter a model name">
|
||||
In the <Badge>Model</Badge> field, enter the model you want to use (for example, <code>gpt-5.2</code>,{" "}
|
||||
<code>claude-4.5-opus</code>, or <code>gemini-3-flash</code>).
|
||||
</Step>
|
||||
|
||||
<Step title="Paste your API key">
|
||||
In the <Badge>API Key</Badge> field, paste your provider API key.
|
||||
@@ -41,6 +45,7 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
|
||||
<Warning>
|
||||
Treat this key like a password. Anyone with it can use your provider account and may incur costs.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Verify it's enabled">
|
||||
@@ -49,12 +54,13 @@ description: "Learn how to configure an AI provider and API key for AI-assisted
|
||||
<Tip>
|
||||
If the status shows <Badge>Disabled</Badge>, double-check that all three fields are filled in.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Why AI requests are sent through our server
|
||||
|
||||
Most AI providers (like OpenAI, Gemini, or Anthropic) block requests that come directly from the browser due to something called **CORS** ("Cross-Origin Resource Sharing"), a security mechanism built into web browsers.
|
||||
Most AI providers (like OpenAI, Gemini, or Anthropic) block requests that come directly from the browser due to something called **CORS** ("Cross-Origin Resource Sharing"), a security mechanism built into web browsers.
|
||||
|
||||
**What is CORS?**
|
||||
CORS is a browser feature that prevents web pages from making requests to a different domain than the one that served the web page, unless that domain explicitly allows it. Most AI providers do not allow browser-based requests from random websites, primarily for security and abuse prevention.
|
||||
@@ -62,11 +68,10 @@ CORS is a browser feature that prevents web pages from making requests to a diff
|
||||
Because of this restriction, the app has to **forward your AI requests (along with your credentials) through our server**, which then contacts the AI provider and returns the response to your browser. **Your credentials are not stored on our server; they are only used to make the request you initiated, and are not logged or retained.**
|
||||
|
||||
<Info>
|
||||
If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself.
|
||||
See the relevant implementation in <code>src/integrations/orpc/router/ai.ts</code> within the codebase.
|
||||
If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself. See the
|
||||
relevant implementation in <code>src/integrations/orpc/router/ai.ts</code> within the codebase.
|
||||
</Info>
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Do you know a workaround?
|
||||
|
||||
@@ -9,10 +9,13 @@ The **Custom CSS** panel lets you write your own CSS rules to change how your re
|
||||
|
||||
- **Live updates**: your changes are applied immediately as you type.
|
||||
- **Auto-save**: there is no “Save” button—your CSS is saved automatically when it changes.
|
||||
- **Scoped styling (mostly)**: to avoid affecting the rest of the app, your CSS is *usually* scoped to the resume preview.
|
||||
- **Scoped styling (mostly)**: to avoid affecting the rest of the app, your CSS is _usually_ scoped to the resume preview.
|
||||
|
||||
<Frame caption="Screenshot of the Custom CSS section in the right sidebar">
|
||||
<img src="/images/guides/using-custom-css/screenshot-1.webp" alt="Screenshot of the Custom CSS section in the right sidebar" />
|
||||
<img
|
||||
src="/images/guides/using-custom-css/screenshot-1.webp"
|
||||
alt="Screenshot of the Custom CSS section in the right sidebar"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Where to find it
|
||||
@@ -61,7 +64,9 @@ Example:
|
||||
Keep each rule's selector on **one line**, for example:
|
||||
|
||||
```css
|
||||
.page-section-experience .section-item-title { font-weight: 700; }
|
||||
.page-section-experience .section-item-title {
|
||||
font-weight: 700;
|
||||
}
|
||||
```
|
||||
|
||||
Avoid splitting a selector across multiple lines unless you've confirmed it behaves as expected.
|
||||
@@ -266,4 +271,3 @@ Example (tighten spacing without changing your layout settings):
|
||||
### My `@media` rules affect the whole app / don't apply
|
||||
|
||||
At-rules aren't auto-scoped. Prefix selectors inside them with `.resume-preview-container` as shown above.
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ Think of it as a built-in notebook attached to each resume where you can jot dow
|
||||
In the resume builder, open the **right sidebar** and select **Notes** from the available sections.
|
||||
|
||||
<Frame caption="Screenshot of the Notes section in the right sidebar">
|
||||
<img src="/images/guides/using-private-notes/screenshot-1.webp" alt="Screenshot of the Notes section in the right sidebar" />
|
||||
<img
|
||||
src="/images/guides/using-private-notes/screenshot-1.webp"
|
||||
alt="Screenshot of the Notes section in the right sidebar"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
## Use cases
|
||||
@@ -39,7 +42,8 @@ Keep a record of where you've sent this particular resume:
|
||||
Paste links to the original job postings so you can quickly reference them when preparing for interviews or following up.
|
||||
|
||||
<Tip>
|
||||
Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities into your notes as a backup.
|
||||
Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities
|
||||
into your notes as a backup.
|
||||
</Tip>
|
||||
|
||||
### Interview preparation
|
||||
@@ -74,9 +78,9 @@ Track your follow-up schedule:
|
||||
In the resume builder, click on **Notes** in the right sidebar to expand the section.
|
||||
</Step>
|
||||
|
||||
<Step title="Write your notes">
|
||||
Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more.
|
||||
</Step>
|
||||
<Step title="Write your notes">
|
||||
Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more.
|
||||
</Step>
|
||||
|
||||
<Step title="Notes save automatically">
|
||||
Your notes are saved automatically as you type—there's no need to click a save button.
|
||||
@@ -86,10 +90,8 @@ Track your follow-up schedule:
|
||||
## Privacy guarantee
|
||||
|
||||
<Warning>
|
||||
Private Notes are for your eyes only. They will **never** appear on:
|
||||
- Your exported PDF or other file formats
|
||||
- Your publicly shared resume link
|
||||
- Any printed version of your resume
|
||||
Private Notes are for your eyes only. They will **never** appear on: - Your exported PDF or other file formats - Your
|
||||
publicly shared resume link - Any printed version of your resume
|
||||
</Warning>
|
||||
|
||||
This makes it safe to store sensitive information like salary expectations, candid thoughts about opportunities, or personal reminders that you wouldn't want an employer to see.
|
||||
|
||||
@@ -8,9 +8,10 @@ description: "Learn how to create API keys and authenticate requests to the Reac
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to API Keys settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>API Keys</Badge> link.
|
||||
</Step>
|
||||
<Step title="Navigate to API Keys settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section,
|
||||
click on the <Badge>API Keys</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the API reference">
|
||||
On the API Keys page, click <Badge>API Documentation</Badge> to view the API reference.
|
||||
@@ -18,6 +19,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
|
||||
<Tip>
|
||||
You can also open it directly here: [API Reference](https://docs.rxresu.me/api-reference).
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new API key">
|
||||
@@ -25,6 +27,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
|
||||
|
||||
- **Name**: A label to help you identify what you use this key for
|
||||
- **Expires in**: How long the key should remain valid
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Copy the API key (important)">
|
||||
@@ -33,6 +36,7 @@ description: "Learn how to create API keys and authenticate requests to the Reac
|
||||
<Warning>
|
||||
For security reasons, your API key is only displayed once. If you lose it, you must create a new one.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Authenticate requests with x-api-key">
|
||||
@@ -43,9 +47,10 @@ description: "Learn how to create API keys and authenticate requests to the Reac
|
||||
</Info>
|
||||
|
||||
```bash
|
||||
curl "https://rxresu.me/api/openapi/resume/list" \
|
||||
curl "https://rxresu.me/api/openapi/resumes" \
|
||||
-H "x-api-key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Delete an API key (optional)">
|
||||
@@ -54,6 +59,6 @@ description: "Learn how to create API keys and authenticate requests to the Reac
|
||||
<Warning>
|
||||
Deleted API keys stop working immediately, and the action cannot be undone.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
||||
@@ -0,0 +1,303 @@
|
||||
---
|
||||
title: "Using the MCP Server"
|
||||
description: "Connect Reactive Resume to AI tools like Claude Desktop, Cursor, and Codex using the Model Context Protocol"
|
||||
---
|
||||
|
||||
The Reactive Resume MCP server lets you manage and modify your resumes through any MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. It connects to the Reactive Resume API and exposes tools for listing, reading, and patching resumes using natural language.
|
||||
|
||||
## What is MCP?
|
||||
|
||||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standard that lets LLM-powered tools connect to external services. Instead of being limited to the built-in chat UI, you can use any MCP client to interact with your resumes.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Steps>
|
||||
<Step title="Choose your authentication method">
|
||||
Reactive Resume MCP supports two authentication methods:
|
||||
|
||||
- **OAuth2 (recommended):** best user experience for clients that support MCP OAuth.
|
||||
- **API key (fallback):** works in all clients that can send custom headers.
|
||||
|
||||
Use OAuth2 whenever your MCP client supports it. Use API key only when OAuth is unavailable in that client.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="If using API key, create one">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) (or your self-hosted instance), sign in, and navigate to **Settings → API Keys**. Click **Create a new API key**, give it a name, and copy the secret — it's only shown once.
|
||||
|
||||
For the full walkthrough, see [Using the API](/guides/using-the-api).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Configuration
|
||||
|
||||
There are two transport options, and each can use either OAuth2 or API key depending on your client capabilities.
|
||||
|
||||
### Method 1: Streamable HTTP (recommended)
|
||||
|
||||
If your client supports the `url` field (e.g. **Cursor**, **Codex**, Claude custom connectors), use this.
|
||||
|
||||
#### Option A: OAuth2 (recommended)
|
||||
|
||||
Most OAuth-capable clients only need the MCP URL:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then connect/sign in from the client UI (or with the client's OAuth login command).
|
||||
|
||||
#### Option B: API key (fallback)
|
||||
|
||||
If OAuth is not supported in your client, send `x-api-key`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp",
|
||||
"headers": {
|
||||
"x-api-key": "your-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Method 2: mcp-remote
|
||||
|
||||
If your client only supports `command` / `args` (for example, local-only Claude Desktop config), use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a bridge. This requires [Node.js](https://nodejs.org) **20 or later**.
|
||||
|
||||
`mcp-remote` is most commonly used with API keys:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"command": "npx",
|
||||
"args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:your-api-key"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Info>Replace `your-api-key` with the API key you created in the prerequisites step.</Info>
|
||||
|
||||
### Where to put the config
|
||||
|
||||
| Client | Config file |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| Cursor | `.cursor/mcp.json` in your project or home directory |
|
||||
| Claude Desktop | `claude_desktop_config.json` ([docs](https://modelcontextprotocol.io/quickstart/user)) |
|
||||
| Codex | `~/.codex/config.toml` or `.codex/config.toml` ([docs](https://developers.openai.com/codex/mcp)) |
|
||||
| Other MCP clients | Refer to the client's documentation |
|
||||
|
||||
## Authentication Details (How Reactive Resume MCP Works)
|
||||
|
||||
Reactive Resume MCP accepts authentication in this order:
|
||||
|
||||
1. **Bearer token (OAuth2 access token)** via `Authorization: Bearer <token>`
|
||||
2. **API key fallback** via `x-api-key: <key>`
|
||||
|
||||
If neither is valid, the MCP endpoint responds with `401` and advertises OAuth metadata using:
|
||||
|
||||
- `WWW-Authenticate: Bearer resource_metadata="<instance>/.well-known/oauth-protected-resource"`
|
||||
|
||||
This lets OAuth-capable MCP clients discover and complete the OAuth flow automatically.
|
||||
|
||||
### OAuth2 flow used by this server
|
||||
|
||||
Reactive Resume is configured as an OAuth authorization server for MCP clients:
|
||||
|
||||
- The MCP endpoint is `https://rxresu.me/mcp`.
|
||||
- OAuth discovery metadata is exposed under `/.well-known/*` endpoints.
|
||||
- The login/authorization route is `/auth/oauth`.
|
||||
- If the user is not signed in, `/auth/oauth` redirects to `/auth/login`, then resumes OAuth.
|
||||
- If the user is signed in, `/auth/oauth` validates `client_id` and `redirect_uri`, issues an authorization code, and redirects back to the client.
|
||||
- PKCE parameters (`code_challenge`, `code_challenge_method`) are preserved in the authorization flow.
|
||||
|
||||
## Popular Client Setup
|
||||
|
||||
### Cursor
|
||||
|
||||
**OAuth2 (recommended):**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**API key fallback:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"reactive-resume": {
|
||||
"url": "https://rxresu.me/mcp",
|
||||
"headers": {
|
||||
"x-api-key": "your-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Codex (CLI / IDE extension)
|
||||
|
||||
Add server:
|
||||
|
||||
```bash
|
||||
codex mcp add reactive-resume --url https://rxresu.me/mcp
|
||||
```
|
||||
|
||||
Then log in with OAuth:
|
||||
|
||||
```bash
|
||||
codex mcp login reactive-resume
|
||||
```
|
||||
|
||||
API key fallback (`config.toml`):
|
||||
|
||||
```toml
|
||||
[mcp_servers."reactive-resume"]
|
||||
url = "https://rxresu.me/mcp"
|
||||
http_headers = { "x-api-key" = "your-api-key" }
|
||||
```
|
||||
|
||||
### Claude (web app custom connector)
|
||||
|
||||
Add `https://rxresu.me/mcp` as a custom remote MCP connector, then connect with OAuth in Claude's connector UI.
|
||||
|
||||
### Claude Desktop (local config file)
|
||||
|
||||
Use `mcp-remote` bridge with API key (example shown above in **Method 2**).
|
||||
|
||||
## External References
|
||||
|
||||
- [Cursor MCP docs](https://cursor.sh/docs/mcp)
|
||||
- [MCP quickstart for users (Claude Desktop example)](https://modelcontextprotocol.io/quickstart/user)
|
||||
- [OpenAI Codex MCP docs](https://developers.openai.com/codex/mcp)
|
||||
- [Claude custom connectors (remote MCP)](https://claude.com/docs/connectors/custom/remote-mcp)
|
||||
- [MCP Authorization spec](https://modelcontextprotocol.io/specification/latest/basic/authorization)
|
||||
|
||||
## Self-Hosting
|
||||
|
||||
If you're running a self-hosted Reactive Resume instance, replace `https://rxresu.me/mcp` with your instance URL:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://resume.example.com/mcp",
|
||||
"headers": {
|
||||
"x-api-key": "your-api-key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Tools
|
||||
|
||||
The MCP server exposes the following tools:
|
||||
|
||||
| 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 |
|
||||
|
||||
## 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 |
|
||||
|
||||
## 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.
|
||||
|
||||
| Prompt | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `build_resume` | Guide you step-by-step through building a resume from scratch — basics, summary, experience, education, skills, and design |
|
||||
| `improve_resume` | Review your resume and suggest concrete improvements to wording, impact, metrics, and structure |
|
||||
| `tailor_resume` | Adapt your resume to match a specific job description with keyword optimization and ATS targeting. Requires the job description as input |
|
||||
| `review_resume` | Get a structured, professional critique with a scorecard (1–10 across seven dimensions) and prioritized recommendations. **Read-only** — no changes are made |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
Once your MCP client is connected, you can use natural language to interact with your resumes:
|
||||
|
||||
### Browsing
|
||||
|
||||
- "List my resumes"
|
||||
- "Show me my resume named 'Software Engineer'"
|
||||
- "What skills are listed on my resume?"
|
||||
- "Show me the stats for my resume"
|
||||
|
||||
### Creating & Managing
|
||||
|
||||
- "Create a new resume called 'Frontend Engineer 2026'"
|
||||
- "Duplicate my 'Software Engineer' resume for a product manager role"
|
||||
- "Lock my finalized resume so it can't be accidentally edited"
|
||||
- "Delete my old draft resume"
|
||||
|
||||
### Editing
|
||||
|
||||
- "Update my name to Jane Doe"
|
||||
- "Change my headline to Senior Software Engineer"
|
||||
- "Add TypeScript to my skills with an Advanced proficiency level"
|
||||
- "Add a new experience entry for my role as Staff Engineer at Acme Corp from Jan 2024 to Present"
|
||||
- "Remove the third item from my skills section"
|
||||
|
||||
### Styling
|
||||
|
||||
- "Change the template to bronzor"
|
||||
- "Set the primary color to blue"
|
||||
- "Hide the interests section"
|
||||
|
||||
### Exporting
|
||||
|
||||
- "Export my resume as a PDF"
|
||||
- "Show me a screenshot of my resume"
|
||||
|
||||
### Using Prompts
|
||||
|
||||
- "Help me build my resume from scratch" (uses `build_resume`)
|
||||
- "Review my resume and give me a score" (uses `review_resume`)
|
||||
- "Improve the wording on my resume" (uses `improve_resume`)
|
||||
- "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.
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| "Unauthorized" with no login prompt | Your client may not support MCP OAuth discovery. Use API key mode (`x-api-key`) |
|
||||
| OAuth login opens but fails redirect/callback | Confirm your client's MCP OAuth callback settings and retry the connection |
|
||||
| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → API Keys** |
|
||||
| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs |
|
||||
| "API error (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` |
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: "Using the Patch API"
|
||||
description: "Learn how to partially update your resume using JSON Patch (RFC 6902) operations"
|
||||
---
|
||||
|
||||
The Patch API lets you make small, targeted changes to your resume without sending the entire data object. Instead of replacing the whole resume with a `PUT`, you send a list of **JSON Patch** operations that describe exactly what to change.
|
||||
|
||||
This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/html/rfc6902) standard.
|
||||
|
||||
## When to Use PATCH vs PUT
|
||||
|
||||
| Use case | Method |
|
||||
| -------------------------------------------- | --------- |
|
||||
| Update a single field (e.g., name, headline) | **PATCH** |
|
||||
| Add or remove an item in a section | **PATCH** |
|
||||
| Change template, colors, or fonts | **PATCH** |
|
||||
| Replace the entire resume data at once | **PUT** |
|
||||
|
||||
<Info>
|
||||
The PATCH endpoint only modifies the resume `data` (the JSONB column). To update top-level resume properties like
|
||||
`name`, `slug`, `tags`, or `isPublic`, use the existing `PUT /resume/{id}` endpoint.
|
||||
</Info>
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require your API key in the `x-api-key` header. See [Using the API](/guides/using-the-api) for how to create one.
|
||||
|
||||
<Info>
|
||||
If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`.
|
||||
</Info>
|
||||
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
PATCH /api/openapi/resume/{id}
|
||||
```
|
||||
|
||||
### Request Body
|
||||
|
||||
The resume ID is taken from the URL path, so the request body only requires the `operations` array:
|
||||
|
||||
```json
|
||||
{
|
||||
"operations": [{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }]
|
||||
}
|
||||
```
|
||||
|
||||
Each operation is an object with the following properties:
|
||||
|
||||
| Property | Required | Description |
|
||||
| -------- | ---------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `op` | Yes | The operation to perform: `add`, `remove`, `replace`, `move`, `copy`, or `test` |
|
||||
| `path` | Yes | A JSON Pointer (RFC 6901) to the target location in the resume data |
|
||||
| `value` | For `add`, `replace`, `test` | The value to use for the operation |
|
||||
| `from` | For `move`, `copy` | A JSON Pointer to the source location |
|
||||
|
||||
## Examples
|
||||
|
||||
### Replace a Basic Field
|
||||
|
||||
Update the resume holder's name and headline:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" },
|
||||
{ "op": "replace", "path": "/basics/headline", "value": "Senior Software Engineer" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Add an Experience Entry
|
||||
|
||||
Append a new item to the experience section:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/sections/experience/items/-",
|
||||
"value": {
|
||||
"id": "a1b2c3d4-0000-0000-0000-000000000000",
|
||||
"hidden": false,
|
||||
"company": "Acme Corp",
|
||||
"position": "Staff Engineer",
|
||||
"location": "San Francisco, CA",
|
||||
"period": "Jan 2024 - Present",
|
||||
"website": { "url": "https://acme.example.com", "label": "Acme Corp" },
|
||||
"description": "<p>Leading the platform team.</p>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
<Tip>
|
||||
The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array". To
|
||||
insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning.
|
||||
</Tip>
|
||||
|
||||
### Remove an Item from a Section
|
||||
|
||||
Remove the second skill (index `1`) from the skills section:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "remove", "path": "/sections/skills/items/1" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Update Metadata (Template, Colors, Fonts)
|
||||
|
||||
Switch the template and update the primary color:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "replace", "path": "/metadata/template", "value": "bronzor" },
|
||||
{ "op": "replace", "path": "/metadata/design/colors/primary", "value": "rgba(37, 99, 235, 1)" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Test-Then-Replace (Optimistic Concurrency)
|
||||
|
||||
The `test` operation checks that a value matches before proceeding. If the test fails, the entire patch is rejected. This is useful to avoid overwriting changes made by another client:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "test", "path": "/basics/name", "value": "Albert Einstein" },
|
||||
{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
If `/basics/name` is not `"Albert Einstein"` at the time of the request, the entire patch will fail with a `400` error and no changes will be applied.
|
||||
|
||||
### Move an Item Within a Section
|
||||
|
||||
Move the first experience item to the third position:
|
||||
|
||||
```bash
|
||||
curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \
|
||||
-H "x-api-key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"operations": [
|
||||
{ "op": "move", "from": "/sections/experience/items/0", "path": "/sections/experience/items/2" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Status | Error Code | Description |
|
||||
| ------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `400` | `INVALID_PATCH_OPERATIONS` | The operations are structurally invalid, target a non-existent path, or produce resume data that fails schema validation. |
|
||||
| `401` | `UNAUTHORIZED` | Missing or invalid API key. |
|
||||
| `404` | `NOT_FOUND` | The resume does not exist or does not belong to the authenticated user. |
|
||||
| `403` | `RESUME_LOCKED` | The resume is locked and cannot be modified. Unlock it first. |
|
||||
|
||||
<Warning>
|
||||
All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the
|
||||
operations are applied.
|
||||
</Warning>
|
||||
|
||||
## Tips
|
||||
|
||||
- **Fetch first, then patch.** Use `GET /resume/{id}` to inspect the current structure before crafting your operations. This helps you target the correct paths and array indices.
|
||||
- **Use `test` for safety.** When updating a value you expect to be a specific value, combine `test` + `replace` to avoid accidentally overwriting concurrent changes.
|
||||
- **Batch related changes.** You can send multiple operations in a single request. They are applied in order, so later operations can depend on earlier ones.
|
||||
- **The `-` index appends.** When adding items to arrays, use `-` as the index (e.g., `/sections/skills/items/-`) to append to the end.
|
||||
@@ -36,4 +36,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ Reactive Resume is open-source and can be operated in different ways (for exampl
|
||||
If you are self-hosting, **you** are the Service Operator and responsible for compliance (including configuring email delivery, storage, and logging appropriately).
|
||||
|
||||
<Note>
|
||||
**Note for self-hosted deployments:** If you are using the official hosted service at <code>rxresu.me</code>, the project's published support contact is <code>hello@amruthpillai.com</code>. Replace the placeholders above with the correct operator details for your deployment if you are self-hosting.
|
||||
**Note for self-hosted deployments:** If you are using the official hosted service at <code>rxresu.me</code>, the
|
||||
project's published support contact is <code>hello@amruthpillai.com</code>. Replace the placeholders above with the
|
||||
correct operator details for your deployment if you are self-hosting.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -37,6 +39,7 @@ Reactive Resume is a resume builder that lets you:
|
||||
## Information We Collect
|
||||
|
||||
### Account information
|
||||
|
||||
When you create an account or sign in, the Service stores:
|
||||
|
||||
- **Identity and profile**: name, email address, username/display username, optional profile image
|
||||
@@ -45,6 +48,7 @@ When you create an account or sign in, the Service stores:
|
||||
If you use social sign-in (e.g., Google, GitHub, or a custom OAuth provider), the Service stores identifiers and tokens needed to link and maintain that login.
|
||||
|
||||
### Authentication and security data
|
||||
|
||||
To keep your account secure and keep you signed in, the Service stores:
|
||||
|
||||
- **Session data**: session token, session expiry, and (if provided) IP address and user agent
|
||||
@@ -55,6 +59,7 @@ To keep your account secure and keep you signed in, the Service stores:
|
||||
The Service Operator may also send **transactional emails** (for example, password reset or email verification). Depending on deployment, these emails may be delivered via an email provider or (in development/testing) the links may be logged to server output.
|
||||
|
||||
### Resume content
|
||||
|
||||
When you create or import a resume, the Service stores the resume data you provide, which may include personal data such as:
|
||||
|
||||
- Contact details, location, summary
|
||||
@@ -64,12 +69,14 @@ When you create or import a resume, the Service stores the resume data you provi
|
||||
Resumes may also have metadata such as tags, a slug, visibility (public/private), and an optional resume password (if you lock a resume).
|
||||
|
||||
### Public resume access and statistics
|
||||
|
||||
If you publish a resume, other users may access it via its public link. The Service may also maintain simple statistics such as:
|
||||
|
||||
- View count and download count
|
||||
- Last viewed/downloaded timestamps
|
||||
|
||||
### Uploaded files (e.g., profile pictures)
|
||||
|
||||
If you upload files, the Service stores them either:
|
||||
|
||||
- On the **local filesystem** of the server (default: under a `data/` directory), or
|
||||
@@ -78,12 +85,14 @@ If you upload files, the Service stores them either:
|
||||
Depending on configuration, uploaded files may be publicly accessible (for example, some S3 configurations may default to public read access for uploaded objects). The Service Operator is responsible for selecting appropriate access controls for uploads.
|
||||
|
||||
### API keys created in the Service
|
||||
|
||||
If the Service Operator enables API key functionality, the Service can store:
|
||||
|
||||
- API key metadata and rate limit counters
|
||||
- The API key value itself (as stored by the Service)
|
||||
|
||||
### Local-only preferences and settings
|
||||
|
||||
Some settings are stored on your device:
|
||||
|
||||
- **Cookies**: UI preferences such as `theme` and `locale`
|
||||
@@ -121,6 +130,7 @@ The Service does not include built-in behavioral advertising or third-party anal
|
||||
We share information only as needed to provide the Service:
|
||||
|
||||
### PDF generation and screenshots (Printer)
|
||||
|
||||
When you export to PDF or request a screenshot, the Service sends a request to a configured printer endpoint (a headless Chromium browser) to render a resume URL.
|
||||
|
||||
Depending on your deployment, the printer may be:
|
||||
@@ -129,12 +139,15 @@ Depending on your deployment, the printer may be:
|
||||
- Operated by a third party (in which case the third party will process the resume content for rendering)
|
||||
|
||||
### Storage providers (optional)
|
||||
|
||||
If configured, uploaded files may be stored in an S3-compatible provider. In that case, the storage provider processes and stores file data on behalf of the Service Operator.
|
||||
|
||||
### OAuth providers (optional)
|
||||
|
||||
If you sign in via OAuth (Google/GitHub/custom), those providers receive authentication requests and return profile information (such as email/name) to the Service, as permitted by your provider settings.
|
||||
|
||||
### AI providers (optional, user-supplied)
|
||||
|
||||
If you enable AI features and provide your own API key, prompts and generated content may be sent to your selected AI provider (OpenAI, Google, Anthropic), according to your use of those features and the provider's policies.
|
||||
|
||||
---
|
||||
@@ -196,4 +209,3 @@ For privacy requests or questions, contact:
|
||||
|
||||
- **Service Operator**: Amruth Pillai
|
||||
- **Email**: hello@amruthpillai.com
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ These Terms of Service (the “Terms”) govern your access to and use of **Reac
|
||||
If you do not agree, do not use the Service.
|
||||
|
||||
<Note>
|
||||
Replace the placeholders above with the correct operator details for your deployment. If you are using the official hosted service at <code>rxresu.me</code>, the project's published support contact is <code>hello@amruthpillai.com</code>.
|
||||
Replace the placeholders above with the correct operator details for your deployment. If you are using the official
|
||||
hosted service at <code>rxresu.me</code>, the project's published support contact is{" "}
|
||||
<code>hello@amruthpillai.com</code>.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -45,15 +47,19 @@ The Service Operator may suspend or terminate accounts to protect the Service, o
|
||||
## Your Content
|
||||
|
||||
### What you provide
|
||||
|
||||
“Content” includes the resume data you enter or import (text, links, personal details, etc.) and any files you upload (e.g., profile pictures).
|
||||
|
||||
### Ownership
|
||||
|
||||
You retain ownership of your Content.
|
||||
|
||||
### License to operate the Service
|
||||
|
||||
You grant the Service Operator a limited license to host, store, process, and display your Content **only as necessary** to provide the Service (for example, to render your resume in the builder, generate PDFs, serve uploaded images, or show a public resume page you publish).
|
||||
|
||||
### Public resumes and sharing
|
||||
|
||||
If you set a resume as **public** or share a link, you understand that anyone with access to that link may view (and potentially copy) the content you make available. You are responsible for what you choose to publish.
|
||||
|
||||
If you password-protect a resume, you are responsible for controlling distribution of that password.
|
||||
@@ -183,4 +189,3 @@ Questions about these Terms can be sent to:
|
||||
|
||||
- **Service Operator**: Amruth Pillai
|
||||
- **Email**: hello@amruthpillai.com
|
||||
|
||||
|
||||
@@ -8,12 +8,8 @@ description: "A comprehensive guide to self-host Reactive Resume with Docker (Po
|
||||
Reactive Resume can be self-hosted using Docker in a matter of minutes, and this guide will walk you through the process. Here are some of the services you'll need to get started:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="PostgreSQL">
|
||||
Stores accounts, resumes, and application data.
|
||||
</Card>
|
||||
<Card title="Printer">
|
||||
Generates PDFs and screenshots using a headless Chromium browser.
|
||||
</Card>
|
||||
<Card title="PostgreSQL">Stores accounts, resumes, and application data.</Card>
|
||||
<Card title="Printer">Generates PDFs and screenshots using a headless Chromium browser.</Card>
|
||||
<Card title="Email (optional)">
|
||||
SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console.
|
||||
</Card>
|
||||
@@ -30,15 +26,9 @@ You can pull the latest app image from:
|
||||
## Minimum requirements
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Docker + Docker Compose">
|
||||
Docker Engine + Docker Compose plugin (or Docker Desktop).
|
||||
</Card>
|
||||
<Card title="Compute">
|
||||
2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Printer run on the same host).
|
||||
</Card>
|
||||
<Card title="Storage">
|
||||
Enough for Postgres + uploads (start with 10-20 GB and scale as needed).
|
||||
</Card>
|
||||
<Card title="Docker + Docker Compose">Docker Engine + Docker Compose plugin (or Docker Desktop).</Card>
|
||||
<Card title="Compute">2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Printer run on the same host).</Card>
|
||||
<Card title="Storage">Enough for Postgres + uploads (start with 10-20 GB and scale as needed).</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quickstart using Docker Compose
|
||||
@@ -83,6 +73,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=""
|
||||
@@ -122,6 +116,7 @@ FLAG_DEBUG_PRINTER="false"
|
||||
FLAG_DISABLE_SIGNUPS="false"
|
||||
FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Generate AUTH_SECRET">
|
||||
@@ -143,6 +138,7 @@ FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
certutil -generateSRS 32 | findstr /r /v "^$" | findstr /v ":" | findstr /v " " | findstr /v "-" | findstr /v "certutil"
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Create compose.yml">
|
||||
@@ -153,14 +149,14 @@ FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
```yaml compose.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
||||
interval: 10s
|
||||
@@ -184,7 +180,7 @@ services:
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
app:
|
||||
reactive-resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
# image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
@@ -229,6 +225,7 @@ volumes:
|
||||
<Tip>
|
||||
Prefer pulling from Docker Hub? Keep <code>amruthpillai/reactive-resume:latest</code>. Prefer GHCR? Swap it to <code>ghcr.io/amruthpillai/reactive-resume:latest</code>.
|
||||
</Tip>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Start the stack">
|
||||
@@ -249,13 +246,15 @@ docker compose logs -f reactive-resume
|
||||
</CodeGroup>
|
||||
|
||||
Reactive Resume should now be available at your `APP_URL` (for the example above: `http://localhost:3000`).
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## How startup works (database migrations)
|
||||
|
||||
<Info>
|
||||
On every start, the server <b>automatically runs database migrations</b> before serving traffic. If migrations fail (usually due to a DB connection issue), the container will exit with an error.
|
||||
On every start, the server <b>automatically runs database migrations</b> before serving traffic. If migrations fail
|
||||
(usually due to a DB connection issue), the container will exit with an error.
|
||||
</Info>
|
||||
|
||||
## Environment variables
|
||||
@@ -263,18 +262,34 @@ docker compose logs -f reactive-resume
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Required">
|
||||
<ul>
|
||||
<li><code>APP_URL</code></li>
|
||||
<li><code>DATABASE_URL</code></li>
|
||||
<li><code>PRINTER_ENDPOINT</code></li>
|
||||
<li><code>AUTH_SECRET</code></li>
|
||||
<li>
|
||||
<code>APP_URL</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>DATABASE_URL</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>PRINTER_ENDPOINT</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>AUTH_SECRET</code>
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card title="Optional">
|
||||
<ul>
|
||||
<li>SMTP (<code>SMTP_*</code>)</li>
|
||||
<li>Social auth (<code>GOOGLE_*</code>, <code>GITHUB_*</code>)</li>
|
||||
<li>S3 storage (<code>S3_*</code>)</li>
|
||||
<li>Feature flags (<code>FLAG_*</code>)</li>
|
||||
<li>
|
||||
SMTP (<code>SMTP_*</code>)
|
||||
</li>
|
||||
<li>
|
||||
Social auth (<code>GOOGLE_*</code>, <code>GITHUB_*</code>)
|
||||
</li>
|
||||
<li>
|
||||
S3 storage (<code>S3_*</code>)
|
||||
</li>
|
||||
<li>
|
||||
Feature flags (<code>FLAG_*</code>)
|
||||
</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -302,12 +317,13 @@ docker compose logs -f reactive-resume
|
||||
|
||||
Set `PRINTER_ENDPOINT` to `http://chrome:9222` (in Docker Compose) or `http://localhost:9222` (if running externally). This provides the same PDF/screenshot generation with a smaller image footprint.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<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`.
|
||||
</Accordion>
|
||||
<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`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Authentication">
|
||||
**`AUTH_SECRET`**: Secret used to secure authentication. Changing it invalidates existing sessions.
|
||||
@@ -326,14 +342,17 @@ 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.
|
||||
|
||||
**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_SCOPES`**: Space-separated scopes (defaults to `openid profile email`)
|
||||
|
||||
|
||||
Configure endpoints using **one** of these methods:
|
||||
- **Option A — OIDC Discovery (preferred)**: Set `OAUTH_DISCOVERY_URL` to your provider's `.well-known/openid-configuration` URL
|
||||
- **Option B — Manual URLs**: Set all three: `OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, and `OAUTH_USER_INFO_URL`
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email (SMTP, optional)">
|
||||
@@ -344,25 +363,26 @@ openssl rand -hex 32
|
||||
- **`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.
|
||||
|
||||
</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.
|
||||
</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.
|
||||
</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>
|
||||
|
||||
@@ -370,17 +390,19 @@ 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. **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.
|
||||
2. **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.
|
||||
3. **(Optional) Remove old, unused Docker images** to free up disk space.
|
||||
```bash
|
||||
docker image prune -f
|
||||
```
|
||||
@@ -429,7 +451,8 @@ Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream
|
||||
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.
|
||||
|
||||
<Tip>
|
||||
If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy containers are excluded from routing without any additional configuration.
|
||||
If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy
|
||||
containers are excluded from routing without any additional configuration.
|
||||
</Tip>
|
||||
|
||||
### Manually checking health
|
||||
@@ -458,27 +481,27 @@ A healthy response returns HTTP 200. Any other response (or a connection failure
|
||||
```
|
||||
</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.
|
||||
</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.
|
||||
</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.
|
||||
</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.
|
||||
</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.
|
||||
</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.
|
||||
</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.
|
||||
</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.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3 storage error: ENOTFOUND bucket.endpoint">
|
||||
- **Common cause**: The S3 client is using virtual-hosted-style addressing (prepending the bucket name to the endpoint), but your S3-compatible storage expects path-style addressing.
|
||||
|
||||
+47
-101
@@ -10,7 +10,9 @@ Every self-hosted setup is unique. You might be running on a single VPS, a Kuber
|
||||
These examples go beyond the basic setup in the [Self-Hosting with Docker](/self-hosting/docker) guide, showing production-ready configurations with reverse proxies, SSL termination, and other common patterns.
|
||||
|
||||
<Info>
|
||||
**Help others by sharing your setup!** If you have a working configuration that isn't covered here, I'd love to include it. Simply [open a pull request](https://github.com/amruthpillai/reactive-resume) with your example added to this page. Your contribution helps the community and makes self-hosting easier for everyone.
|
||||
**Help others by sharing your setup!** If you have a working configuration that isn't covered here, I'd love to
|
||||
include it. Simply [open a pull request](https://github.com/amruthpillai/reactive-resume) with your example added to
|
||||
this page. Your contribution helps the community and makes self-hosting easier for everyone.
|
||||
</Info>
|
||||
|
||||
---
|
||||
@@ -20,7 +22,8 @@ These examples go beyond the basic setup in the [Self-Hosting with Docker](/self
|
||||
This example uses [Traefik](https://traefik.io/) as a reverse proxy with automatic SSL certificate management via Let's Encrypt. Only the Reactive Resume app is exposed through Traefik—Postgres and the printer remain on an internal network.
|
||||
|
||||
<Tip>
|
||||
Traefik automatically discovers services via Docker labels and handles SSL certificates, making it ideal for setups where you want minimal configuration.
|
||||
Traefik automatically discovers services via Docker labels and handles SSL certificates, making it ideal for setups
|
||||
where you want minimal configuration.
|
||||
</Tip>
|
||||
|
||||
```yaml compose-traefik.yml lines expandable
|
||||
@@ -59,14 +62,14 @@ services:
|
||||
- "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_DASHBOARD_AUTH}"
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
@@ -79,10 +82,11 @@ services:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TIMEOUT=120000
|
||||
- CONCURRENT=10
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- TOKEN=your-secret-token
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
@@ -91,18 +95,18 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_URL=https://resume.${DOMAIN}
|
||||
- PRINTER_APP_URL=http://app:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
|
||||
- PRINTER_ENDPOINT=http://printer:3000
|
||||
- AUTH_SECRET=${AUTH_SECRET}
|
||||
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
|
||||
volumes:
|
||||
- app_data:/app/data
|
||||
- reactive_resume_data:/app/data
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
depends_on:
|
||||
@@ -129,7 +133,7 @@ networks:
|
||||
volumes:
|
||||
traefik_letsencrypt:
|
||||
postgres_data:
|
||||
app_data:
|
||||
reactive_resume_data:
|
||||
```
|
||||
|
||||
**Environment variables (`.env`):**
|
||||
@@ -162,18 +166,16 @@ services:
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
depends_on:
|
||||
- app
|
||||
|
||||
postgres:
|
||||
image: postgres:16
|
||||
image: postgres:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
@@ -186,30 +188,26 @@ services:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TIMEOUT=120000
|
||||
- CONCURRENT=10
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- TOKEN=your-secret-token
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APP_URL=https://resume.${DOMAIN}
|
||||
- PRINTER_APP_URL=http://app:3000
|
||||
- PRINTER_APP_URL=http://reactive_resume:3000
|
||||
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
|
||||
- PRINTER_ENDPOINT=http://printer:3000
|
||||
- AUTH_SECRET=${AUTH_SECRET}
|
||||
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
|
||||
volumes:
|
||||
- app_data:/app/data
|
||||
- reactive_resume_data:/app/data
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
depends_on:
|
||||
@@ -229,7 +227,7 @@ networks:
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
app_data:
|
||||
reactive_resume_data:
|
||||
```
|
||||
|
||||
**nginx configuration (`nginx.conf`):**
|
||||
@@ -241,7 +239,7 @@ events {
|
||||
|
||||
http {
|
||||
upstream reactive_resume {
|
||||
server app:3000;
|
||||
server reactive_resume:3000;
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
@@ -296,7 +294,8 @@ http {
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx` plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion).
|
||||
For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx`
|
||||
plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion).
|
||||
</Tip>
|
||||
|
||||
---
|
||||
@@ -306,14 +305,14 @@ http {
|
||||
This example demonstrates a production-grade Docker Swarm deployment with multiple replicas, health checks, rolling updates, and Traefik integration. It includes SeaweedFS for S3-compatible storage and a PostgreSQL database with custom configuration.
|
||||
|
||||
<Tip>
|
||||
Docker Swarm is great for multi-node deployments where you need high availability and easy scaling. The app service is configured with 2 replicas and rolling update strategy.
|
||||
Docker Swarm is great for multi-node deployments where you need high availability and easy scaling. The app service is
|
||||
configured with 2 replicas and rolling update strategy.
|
||||
</Tip>
|
||||
|
||||
```yaml compose-swarm.yml lines expandable
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18
|
||||
command: postgres -c config_file=/etc/postgresql.conf
|
||||
image: postgres:latest
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
volumes:
|
||||
@@ -322,37 +321,24 @@ services:
|
||||
- POSTGRES_DB=$POSTGRES_DB
|
||||
- POSTGRES_USER=$POSTGRES_USER
|
||||
- POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
configs:
|
||||
- source: reactive_resume_postgres_config
|
||||
target: /etc/postgresql.conf
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
|
||||
printer:
|
||||
image: ghcr.io/browserless/chromium:latest
|
||||
networks:
|
||||
- reactive_resume_network
|
||||
environment:
|
||||
- TIMEOUT=120000
|
||||
- CONCURRENT=10
|
||||
- QUEUED=10
|
||||
- HEALTH=true
|
||||
- CONCURRENT=5
|
||||
# Optional: Set a token for authentication
|
||||
# - TOKEN=your-secret-token
|
||||
healthcheck:
|
||||
@@ -361,19 +347,9 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
replicas: 1
|
||||
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
@@ -391,19 +367,9 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
|
||||
seaweedfs_create_bucket:
|
||||
image: quay.io/minio/mc:latest
|
||||
@@ -420,19 +386,14 @@ services:
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 10s
|
||||
max_attempts: 5
|
||||
window: 120s
|
||||
|
||||
app:
|
||||
reactive_resume:
|
||||
image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
networks:
|
||||
- traefik_network
|
||||
- reactive_resume_network
|
||||
volumes:
|
||||
- reactive_resume_app_data:/app/data
|
||||
- reactive_resume_data:/app/data
|
||||
environment:
|
||||
- APP_URL=$APP_URL
|
||||
# If using browserless with token auth, include the token in the URL:
|
||||
@@ -444,6 +405,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
|
||||
@@ -461,27 +424,9 @@ services:
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
window: 120s
|
||||
update_config:
|
||||
parallelism: 1
|
||||
delay: 10s
|
||||
failure_action: rollback
|
||||
order: start-first
|
||||
rollback_config:
|
||||
parallelism: 1
|
||||
delay: 10s
|
||||
replicas: 1
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.app.rule=Host(`rxresu.me`)"
|
||||
@@ -507,8 +452,8 @@ volumes:
|
||||
name: reactive_resume_postgres_data
|
||||
reactive_resume_seaweedfs_data:
|
||||
name: reactive_resume_seaweedfs_data
|
||||
reactive_resume_app_data:
|
||||
name: reactive_resume_app_data
|
||||
reactive_resume_data:
|
||||
name: reactive_resume_data
|
||||
```
|
||||
|
||||
**Deploy the stack:**
|
||||
@@ -534,7 +479,8 @@ docker stack rm reactive_resume
|
||||
```
|
||||
|
||||
<Note>
|
||||
This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and labels based on your Traefik configuration.
|
||||
This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and
|
||||
labels based on your Traefik configuration.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -555,4 +501,4 @@ To contribute, [open a pull request](https://github.com/amruthpillai/reactive-re
|
||||
1. A brief description of when/why someone would use this setup
|
||||
2. The complete Docker Compose (or equivalent) configuration
|
||||
3. Any additional configuration files (nginx.conf, etc.)
|
||||
4. Required environment variables
|
||||
4. Required environment variables
|
||||
|
||||
@@ -8,7 +8,8 @@ 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.
|
||||
|
||||
<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.
|
||||
**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.
|
||||
</Warning>
|
||||
|
||||
## Prerequisites
|
||||
@@ -16,18 +17,15 @@ This guide walks you through migrating your Reactive Resume installation from **
|
||||
Before starting the migration, ensure you have:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Running v4 Instance">
|
||||
Your existing Reactive Resume v4 instance should be running and accessible.
|
||||
</Card>
|
||||
<Card title="Running v4 Instance">Your existing Reactive Resume v4 instance should be running and accessible.</Card>
|
||||
<Card title="New v5 Instance">
|
||||
A fresh Reactive Resume v5 instance set up and running. Follow the [Self-Hosting with Docker](/self-hosting/docker) guide if you haven't done this yet.
|
||||
A fresh Reactive Resume v5 instance set up and running. Follow the [Self-Hosting with Docker](/self-hosting/docker)
|
||||
guide if you haven't done this yet.
|
||||
</Card>
|
||||
<Card title="Database Access">
|
||||
Access to both your v4 PostgreSQL database (source) and v5 PostgreSQL database (target).
|
||||
</Card>
|
||||
<Card title="Backup">
|
||||
A recent backup of your v4 database. Always backup before any migration.
|
||||
</Card>
|
||||
<Card title="Backup">A recent backup of your v4 database. Always backup before any migration.</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Choosing a Migration Method
|
||||
@@ -36,14 +34,12 @@ The best migration approach depends on the size of your instance:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Manual Migration" icon="hand">
|
||||
**Best for**: Small instances with a handful of resumes.
|
||||
|
||||
Uses the built-in Import Dialog to manually convert resumes one at a time.
|
||||
**Best for**: Small instances with a handful of resumes. Uses the built-in Import Dialog to manually convert resumes
|
||||
one at a time.
|
||||
</Card>
|
||||
<Card title="Automated Migration" icon="robot">
|
||||
**Best for**: Large instances with many users and resumes.
|
||||
|
||||
Uses migration scripts to batch-process all users and resumes automatically.
|
||||
**Best for**: Large instances with many users and resumes. Uses migration scripts to batch-process all users and
|
||||
resumes automatically.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -56,15 +52,11 @@ If you have only a few resumes to migrate, the simplest approach is to use the *
|
||||
In your v4 instance, go to each resume and export it as JSON. This creates a portable file containing all your resume data.
|
||||
</Step>
|
||||
|
||||
<Step title="Import into v5">
|
||||
In your new v5 instance:
|
||||
1. Log in or create a new account
|
||||
2. Click **Create Resume** or use the **Import** option
|
||||
3. Select the **Reactive Resume v4** format
|
||||
4. Upload your exported JSON file
|
||||
|
||||
The import process automatically converts the v4 format to v5.
|
||||
</Step>
|
||||
<Step title="Import into v5">
|
||||
In your new v5 instance: 1. Log in or create a new account 2. Click **Create Resume** or use the **Import** option 3.
|
||||
Select the **Reactive Resume v4** format 4. Upload your exported JSON file The import process automatically converts
|
||||
the v4 format to v5.
|
||||
</Step>
|
||||
|
||||
<Step title="Verify and repeat">
|
||||
Review the imported resume to ensure all data transferred correctly. Repeat for each resume you need to migrate.
|
||||
@@ -72,7 +64,8 @@ If you have only a few resumes to migrate, the simplest approach is to use the *
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences between v4 and v5.
|
||||
The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences
|
||||
between v4 and v5.
|
||||
</Tip>
|
||||
|
||||
## Automated Migration (Large Instances)
|
||||
@@ -85,25 +78,15 @@ To run the migration scripts, you need the following installed on your host mach
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Node.js Runtime">
|
||||
**tsx** - TypeScript execution environment. Install globally with:
|
||||
```bash
|
||||
npm install -g tsx
|
||||
```
|
||||
**tsx** - TypeScript execution environment. Install globally with: ```bash npm install -g tsx ```
|
||||
</Card>
|
||||
<Card title="Environment Loader">
|
||||
**dotenvx** (or any tool to load `.env` files). Install globally with:
|
||||
```bash
|
||||
npm install -g @dotenvx/dotenvx
|
||||
```
|
||||
**dotenvx** (or any tool to load `.env` files). Install globally with: ```bash npm install -g @dotenvx/dotenvx ```
|
||||
Alternatively, you can use `dotenv`, `direnv`, or export the variables manually.
|
||||
</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
|
||||
```
|
||||
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 ```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
@@ -120,7 +103,8 @@ PRODUCTION_DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resu
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and `PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss.
|
||||
Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and
|
||||
`PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss.
|
||||
</Warning>
|
||||
|
||||
### Step 1: Migrate Users
|
||||
@@ -140,7 +124,8 @@ dotenvx run -- tsx scripts/migration/user.ts
|
||||
- Creates a mapping file (`scripts/migration/user-id-map.json`) that links old user IDs to new ones
|
||||
|
||||
<Info>
|
||||
The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where it left off.
|
||||
The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where
|
||||
it left off.
|
||||
</Info>
|
||||
|
||||
**Expected output:**
|
||||
@@ -179,9 +164,7 @@ dotenvx run -- tsx scripts/migration/resume.ts
|
||||
- Migrates resume statistics (views, downloads)
|
||||
- Preserves visibility settings (public/private) and lock status
|
||||
|
||||
<Info>
|
||||
Like the user script, the resume migration also saves progress and can be resumed if interrupted.
|
||||
</Info>
|
||||
<Info>Like the user script, the resume migration also saves progress and can be resumed if interrupted.</Info>
|
||||
|
||||
**Expected output:**
|
||||
|
||||
@@ -213,7 +196,8 @@ Both migration scripts support graceful shutdown and resume:
|
||||
- **Resume migration**: Run the script again to continue from where you left off
|
||||
|
||||
<Tip>
|
||||
If you need to restart the migration from scratch, delete the progress files and the user ID mapping file before running the scripts again.
|
||||
If you need to restart the migration from scratch, delete the progress files and the user ID mapping file before
|
||||
running the scripts again.
|
||||
</Tip>
|
||||
|
||||
## Post-Migration Steps
|
||||
@@ -225,15 +209,14 @@ After completing the migration:
|
||||
Log into your v5 instance and spot-check several user accounts and resumes to ensure data transferred correctly.
|
||||
</Step>
|
||||
|
||||
<Step title="Test functionality">
|
||||
- Create a test resume and export it as PDF
|
||||
- Verify social logins work (if configured)
|
||||
- Check that two-factor authentication works for migrated users
|
||||
</Step>
|
||||
<Step title="Test functionality">
|
||||
- Create a test resume and export it as PDF - Verify social logins work (if configured) - Check that two-factor
|
||||
authentication works for migrated users
|
||||
</Step>
|
||||
|
||||
<Step title="Update DNS/Proxy">
|
||||
Once verified, update your DNS records or reverse proxy to point to the new v5 instance.
|
||||
</Step>
|
||||
<Step title="Update DNS/Proxy">
|
||||
Once verified, update your DNS records or reverse proxy to point to the new v5 instance.
|
||||
</Step>
|
||||
|
||||
<Step title="Decommission v4">
|
||||
After confirming everything works and allowing a grace period, you can safely shut down your v4 instance.
|
||||
@@ -247,17 +230,20 @@ After completing the migration:
|
||||
Users who signed up with email/password can continue using their existing passwords. No password reset is required after migration.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Profile pictures are not migrated">
|
||||
User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5 instance has access to the same bucket, or users may need to re-upload their avatars.
|
||||
</Accordion>
|
||||
<Accordion title="Profile pictures are not migrated">
|
||||
User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5
|
||||
instance has access to the same bucket, or users may need to re-upload their avatars.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Resume images and uploads">
|
||||
Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider migrating your storage bucket or updating references as needed.
|
||||
</Accordion>
|
||||
<Accordion title="Resume images and uploads">
|
||||
Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider
|
||||
migrating your storage bucket or updating references as needed.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="OAuth provider changes">
|
||||
If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users authenticate with the same provider ID, so mismatched configurations will cause login failures.
|
||||
</Accordion>
|
||||
<Accordion title="OAuth provider changes">
|
||||
If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users
|
||||
authenticate with the same provider ID, so mismatched configurations will cause login failures.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Schema differences">
|
||||
The v5 schema has some changes from v4:
|
||||
@@ -276,25 +262,20 @@ After completing the migration:
|
||||
Ensure your `.env` file contains both `DATABASE_URL` and `PRODUCTION_DATABASE_URL`, and that you're using a tool like `dotenvx` to load them before running the script.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Users are skipped during migration">
|
||||
Users are skipped if:
|
||||
- Their email already exists in the v5 database
|
||||
- Their username already exists in the v5 database
|
||||
- They were already migrated in a previous run
|
||||
|
||||
Check the console output for skip reasons.
|
||||
</Accordion>
|
||||
<Accordion title="Users are skipped during migration">
|
||||
Users are skipped if: - Their email already exists in the v5 database - Their username already exists in the v5
|
||||
database - They were already migrated in a previous run Check the console output for skip reasons.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Resumes are skipped during migration">
|
||||
Resumes are skipped if:
|
||||
- The associated user wasn't migrated (user ID not in mapping file)
|
||||
- A resume with the same slug already exists for that user
|
||||
- They were already migrated in a previous run
|
||||
</Accordion>
|
||||
<Accordion title="Resumes are skipped during migration">
|
||||
Resumes are skipped if: - The associated user wasn't migrated (user ID not in mapping file) - A resume with the same
|
||||
slug already exists for that user - They were already migrated in a previous run
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Resume data parsing fails">
|
||||
If a resume can't be parsed from v4 format, it will be created with default empty data. Check the console output for warnings about specific resumes, and consider manually importing those using the Import Dialog.
|
||||
</Accordion>
|
||||
<Accordion title="Resume data parsing fails">
|
||||
If a resume can't be parsed from v4 format, it will be created with default empty data. Check the console output for
|
||||
warnings about specific resumes, and consider manually importing those using the Import Dialog.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Migration is slow">
|
||||
The scripts process data in batches to avoid overwhelming the database. For very large instances:
|
||||
|
||||
+44
-40
@@ -12,7 +12,8 @@ Reactive Resume supports custom OAuth providers, allowing you to integrate with
|
||||
- Integrate with existing LDAP/Active Directory infrastructure
|
||||
|
||||
<Info>
|
||||
Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options.
|
||||
Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at
|
||||
[rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options.
|
||||
</Info>
|
||||
|
||||
## Environment Variables
|
||||
@@ -21,9 +22,9 @@ To enable a custom OAuth provider, you need to configure the following environme
|
||||
|
||||
### Required Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OAUTH_CLIENT_ID` | The client ID provided by your OAuth provider |
|
||||
| Variable | Description |
|
||||
| --------------------- | ------------------------------------------------- |
|
||||
| `OAUTH_CLIENT_ID` | The client ID provided by your OAuth provider |
|
||||
| `OAUTH_CLIENT_SECRET` | The client secret provided by your OAuth provider |
|
||||
|
||||
### Endpoint Configuration
|
||||
@@ -44,6 +45,7 @@ You must configure endpoints using **one** of these two methods:
|
||||
- Authentik: `https://auth.example.com/application/o/reactive-resume/.well-known/openid-configuration`
|
||||
- Keycloak: `https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration`
|
||||
- Authelia: `https://auth.example.com/.well-known/openid-configuration`
|
||||
|
||||
</Tab>
|
||||
|
||||
<Tab title="Option B: Manual URLs">
|
||||
@@ -54,15 +56,16 @@ You must configure endpoints using **one** of these two methods:
|
||||
| `OAUTH_AUTHORIZATION_URL` | The URL where users are redirected to authorize |
|
||||
| `OAUTH_TOKEN_URL` | The URL to exchange authorization codes for tokens |
|
||||
| `OAUTH_USER_INFO_URL` | The URL to fetch user profile information |
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### 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` |
|
||||
|
||||
## Callback URL
|
||||
|
||||
@@ -79,7 +82,8 @@ https://resume.example.com/api/auth/oauth2/callback/custom
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause authentication to fail.
|
||||
Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause
|
||||
authentication to fail.
|
||||
</Warning>
|
||||
|
||||
## Profile Mapping
|
||||
@@ -87,11 +91,11 @@ https://resume.example.com/api/auth/oauth2/callback/custom
|
||||
Reactive Resume automatically maps user profile data from the OAuth provider. The following fields are used:
|
||||
|
||||
| Reactive Resume Field | OAuth Profile Fields (in order of preference) |
|
||||
|-----------------------|-----------------------------------------------|
|
||||
| **Email** (required) | `email` |
|
||||
| **Name** | `name` → `preferred_username` → email prefix |
|
||||
| **Username** | `preferred_username` → email prefix |
|
||||
| **Avatar** | `image` → `picture` → `avatar_url` |
|
||||
| --------------------- | --------------------------------------------- |
|
||||
| **Email** (required) | `email` |
|
||||
| **Name** | `name` → `preferred_username` → email prefix |
|
||||
| **Username** | `preferred_username` → email prefix |
|
||||
| **Avatar** | `image` → `picture` → `avatar_url` |
|
||||
|
||||
<Info>
|
||||
The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error.
|
||||
@@ -109,6 +113,7 @@ Reactive Resume automatically maps user profile data from the OAuth provider. Th
|
||||
- **Authorization flow**: Use your preferred authorization flow
|
||||
- **Client type**: Confidential
|
||||
- **Redirect URIs**: `https://resume.example.com/api/auth/oauth2/callback/custom`
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Create an Application">
|
||||
@@ -117,11 +122,10 @@ Reactive Resume automatically maps user profile data from the OAuth provider. Th
|
||||
- **Name**: Reactive Resume
|
||||
- **Slug**: `reactive-resume`
|
||||
- **Provider**: Select the provider you just created
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Copy credentials">
|
||||
From the provider settings, copy the **Client ID** and **Client Secret**.
|
||||
</Step>
|
||||
<Step title="Copy credentials">From the provider settings, copy the **Client ID** and **Client Secret**.</Step>
|
||||
|
||||
<Step title="Configure environment variables">
|
||||
```bash .env
|
||||
@@ -145,9 +149,9 @@ identity_providers:
|
||||
clients:
|
||||
- client_id: reactive-resume
|
||||
client_name: Reactive Resume
|
||||
client_secret: 'your-hashed-secret' # Use authelia hash-password to generate
|
||||
client_secret: "your-hashed-secret" # Use authelia hash-password to generate
|
||||
public: false
|
||||
authorization_policy: two_factor # or one_factor
|
||||
authorization_policy: two_factor # or one_factor
|
||||
redirect_uris:
|
||||
- https://resume.example.com/api/auth/oauth2/callback/custom
|
||||
scopes:
|
||||
@@ -160,6 +164,7 @@ identity_providers:
|
||||
<Info>
|
||||
Generate the hashed secret using: `authelia crypto hash generate pbkdf2 --variant sha512`
|
||||
</Info>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Configure environment variables">
|
||||
@@ -173,6 +178,7 @@ OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration"
|
||||
<Warning>
|
||||
Use the **plain text** secret in Reactive Resume's environment, not the hashed version used in Authelia's configuration.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -187,17 +193,17 @@ OAUTH_DISCOVERY_URL="https://auth.example.com/.well-known/openid-configuration"
|
||||
3. Set **Client ID** (e.g., `reactive-resume`)
|
||||
4. Set **Client authentication** to **On**
|
||||
5. Enable **Standard flow**
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Configure redirect URI">
|
||||
In the client settings, add the redirect URI:
|
||||
|
||||
- **Valid redirect URIs**: `https://resume.example.com/api/auth/oauth2/callback/custom`
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Copy credentials">
|
||||
Go to the **Credentials** tab and copy the **Client secret**.
|
||||
</Step>
|
||||
<Step title="Copy credentials">Go to the **Credentials** tab and copy the **Client secret**.</Step>
|
||||
|
||||
<Step title="Configure environment variables">
|
||||
```bash .env
|
||||
@@ -247,6 +253,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"
|
||||
@@ -266,17 +274,11 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
- The user has an email address set in the identity provider
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Redirect URI mismatch error">
|
||||
The callback URL configured in your OAuth provider must exactly match:
|
||||
```
|
||||
{APP_URL}/api/auth/oauth2/callback/custom
|
||||
```
|
||||
Common issues:
|
||||
- Trailing slash mismatch
|
||||
- HTTP vs HTTPS mismatch
|
||||
- Port number differences
|
||||
- Path case sensitivity
|
||||
</Accordion>
|
||||
<Accordion title="Redirect URI mismatch error">
|
||||
The callback URL configured in your OAuth provider must exactly match: ```
|
||||
{APP_URL}/api/auth/oauth2/callback/custom ``` Common issues: - Trailing slash mismatch - HTTP vs HTTPS mismatch - Port
|
||||
number differences - Path case sensitivity
|
||||
</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:
|
||||
@@ -284,14 +286,14 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
- All three manual URLs are set (`OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, `OAUTH_USER_INFO_URL`)
|
||||
|
||||
Double-check your environment variables and restart the container.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="CORS or network errors during authentication">
|
||||
If running behind a reverse proxy:
|
||||
- Ensure `APP_URL` matches your public URL
|
||||
- Verify the proxy passes the correct headers (`X-Forwarded-Proto`, `X-Forwarded-Host`)
|
||||
- Check that your OAuth provider allows the redirect URI from your domain
|
||||
</Accordion>
|
||||
<Accordion title="CORS or network errors during authentication">
|
||||
If running behind a reverse proxy: - Ensure `APP_URL` matches your public URL - Verify the proxy passes the correct
|
||||
headers (`X-Forwarded-Proto`, `X-Forwarded-Host`) - Check that your OAuth provider allows the redirect URI from your
|
||||
domain
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="User profile data is missing or incorrect">
|
||||
The profile mapping depends on your provider returning standard claims:
|
||||
@@ -300,6 +302,7 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
- `picture`, `image`, or `avatar_url` for avatar
|
||||
|
||||
Check your provider's documentation to ensure these claims are included in the ID token or userinfo response.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -307,7 +310,8 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Use HTTPS" icon="lock">
|
||||
Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never be transmitted over unencrypted connections.
|
||||
Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never
|
||||
be transmitted over unencrypted connections.
|
||||
</Card>
|
||||
<Card title="Protect secrets" icon="key">
|
||||
Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager.
|
||||
|
||||
+16696
-1
File diff suppressed because one or more lines are too long
+6
-9
@@ -1,13 +1,10 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
invariant(process.env.DATABASE_URL, "DATABASE_URL is not set");
|
||||
|
||||
export default defineConfig({
|
||||
out: "./migrations",
|
||||
dialect: "postgresql",
|
||||
schema: "./src/integrations/drizzle/schema.ts",
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL,
|
||||
},
|
||||
out: "./migrations",
|
||||
dialect: "postgresql",
|
||||
schema: "./src/integrations/drizzle/schema.ts",
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@5/schema.json",
|
||||
"entry": ["src/router.tsx"],
|
||||
"tailwind": { "entry": ["src/styles.css"] },
|
||||
"project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"],
|
||||
"ignoreBinaries": ["mint"],
|
||||
"ignoreDependencies": ["npm-check-updates", "reflect-metadata", "node-addon-api", "node-gyp"],
|
||||
"ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}", "src/routeTree.gen.ts"]
|
||||
"$schema": "https://unpkg.com/knip@5/schema.json",
|
||||
"entry": ["src/server.ts", "src/router.tsx"],
|
||||
"tailwind": { "entry": ["src/styles.css"] },
|
||||
"project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"],
|
||||
"ignoreBinaries": ["mint"],
|
||||
"ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"],
|
||||
"ignore": ["src/schema/**/*.ts", "src/components/ui/**/*.{ts,tsx}"]
|
||||
}
|
||||
|
||||
+72
-70
@@ -1,74 +1,76 @@
|
||||
import { defineConfig } from "@lingui/cli";
|
||||
|
||||
export default defineConfig({
|
||||
sourceLocale: "en-US",
|
||||
pseudoLocale: "zu-ZA",
|
||||
locales: [
|
||||
"af-ZA",
|
||||
"am-ET",
|
||||
"ar-SA",
|
||||
"az-AZ",
|
||||
"bg-BG",
|
||||
"bn-BD",
|
||||
"ca-ES",
|
||||
"cs-CZ",
|
||||
"da-DK",
|
||||
"de-DE",
|
||||
"el-GR",
|
||||
"en-US",
|
||||
"es-ES",
|
||||
"fa-IR",
|
||||
"fi-FI",
|
||||
"fr-FR",
|
||||
"he-IL",
|
||||
"hi-IN",
|
||||
"hu-HU",
|
||||
"id-ID",
|
||||
"it-IT",
|
||||
"ja-JP",
|
||||
"km-KH",
|
||||
"kn-IN",
|
||||
"ko-KR",
|
||||
"lt-LT",
|
||||
"lv-LV",
|
||||
"ml-IN",
|
||||
"mr-IN",
|
||||
"ms-MY",
|
||||
"ne-NP",
|
||||
"nl-NL",
|
||||
"no-NO",
|
||||
"or-IN",
|
||||
"pl-PL",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"ro-RO",
|
||||
"ru-RU",
|
||||
"sk-SK",
|
||||
"sq-AL",
|
||||
"sr-SP",
|
||||
"sv-SE",
|
||||
"ta-IN",
|
||||
"te-IN",
|
||||
"th-TH",
|
||||
"tr-TR",
|
||||
"uk-UA",
|
||||
"uz-UZ",
|
||||
"vi-VN",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
"zu-ZA",
|
||||
],
|
||||
fallbackLocales: {
|
||||
"zu-ZA": "en-US",
|
||||
default: "en-US",
|
||||
},
|
||||
formatOptions: {
|
||||
lineNumbers: false,
|
||||
},
|
||||
catalogs: [
|
||||
{
|
||||
path: "<rootDir>/locales/{locale}",
|
||||
include: ["src"],
|
||||
},
|
||||
],
|
||||
sourceLocale: "en-US",
|
||||
pseudoLocale: "zu-ZA",
|
||||
locales: [
|
||||
"af-ZA",
|
||||
"am-ET",
|
||||
"ar-SA",
|
||||
"az-AZ",
|
||||
"bg-BG",
|
||||
"bn-BD",
|
||||
"ca-ES",
|
||||
"cs-CZ",
|
||||
"da-DK",
|
||||
"de-DE",
|
||||
"el-GR",
|
||||
"en-US",
|
||||
"en-GB",
|
||||
"es-ES",
|
||||
"fa-IR",
|
||||
"fi-FI",
|
||||
"fr-FR",
|
||||
"he-IL",
|
||||
"hi-IN",
|
||||
"hu-HU",
|
||||
"id-ID",
|
||||
"it-IT",
|
||||
"ja-JP",
|
||||
"km-KH",
|
||||
"kn-IN",
|
||||
"ko-KR",
|
||||
"lt-LT",
|
||||
"lv-LV",
|
||||
"ml-IN",
|
||||
"mr-IN",
|
||||
"ms-MY",
|
||||
"ne-NP",
|
||||
"nl-NL",
|
||||
"no-NO",
|
||||
"or-IN",
|
||||
"pl-PL",
|
||||
"pt-BR",
|
||||
"pt-PT",
|
||||
"ro-RO",
|
||||
"ru-RU",
|
||||
"sk-SK",
|
||||
"sl-SI",
|
||||
"sq-AL",
|
||||
"sr-SP",
|
||||
"sv-SE",
|
||||
"ta-IN",
|
||||
"te-IN",
|
||||
"th-TH",
|
||||
"tr-TR",
|
||||
"uk-UA",
|
||||
"uz-UZ",
|
||||
"vi-VN",
|
||||
"zh-CN",
|
||||
"zh-TW",
|
||||
"zu-ZA",
|
||||
],
|
||||
fallbackLocales: {
|
||||
"zu-ZA": "en-US",
|
||||
default: "en-US",
|
||||
},
|
||||
formatOptions: {
|
||||
lineNumbers: false,
|
||||
},
|
||||
catalogs: [
|
||||
{
|
||||
path: "<rootDir>/locales/{locale}",
|
||||
include: ["src"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Generated
BIN
Binary file not shown.
Generated
+529
-67
File diff suppressed because it is too large
Load Diff
Generated
+532
-70
File diff suppressed because it is too large
Load Diff
Generated
+531
-69
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+533
-71
File diff suppressed because it is too large
Load Diff
Generated
+532
-70
File diff suppressed because it is too large
Load Diff
Generated
+531
-69
File diff suppressed because it is too large
Load Diff
Generated
+531
-69
File diff suppressed because it is too large
Load Diff
Generated
+532
-70
File diff suppressed because it is too large
Load Diff
Generated
+3250
File diff suppressed because it is too large
Load Diff
Generated
+512
-50
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+519
-57
File diff suppressed because it is too large
Load Diff
Generated
+532
-70
File diff suppressed because it is too large
Load Diff
Generated
+521
-59
File diff suppressed because it is too large
Load Diff
Generated
+531
-69
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+535
-73
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+529
-67
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+529
-67
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+533
-71
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+531
-69
File diff suppressed because it is too large
Load Diff
Generated
+530
-68
File diff suppressed because it is too large
Load Diff
Generated
+529
-67
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user