mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-26 01:44:53 +10:00
Migrate from Biome to Oxlint/Oxfmt (#2822)
* Migrate from Biome to Oxlint/Oxfmt * pin version of autofix * set version of autofix * pin version of autofix * [autofix.ci] apply automated fixes * better comments, test formatter * [autofix.ci] apply automated fixes * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -8,18 +8,14 @@ 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="Email (optional)">
|
||||
SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console.
|
||||
</Card>
|
||||
<Card title="Storage (optional)">
|
||||
Use S3-compatible storage, or local persistent storage via <code>/app/data</code>.
|
||||
</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>
|
||||
<Card title="Storage (optional)">
|
||||
Use S3-compatible storage, or local persistent storage via <code>/app/data</code>.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
You can pull the latest app image from:
|
||||
@@ -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
|
||||
@@ -122,6 +112,7 @@ FLAG_DEBUG_PRINTER="false"
|
||||
FLAG_DISABLE_SIGNUPS="false"
|
||||
FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Generate AUTH_SECRET">
|
||||
@@ -143,6 +134,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">
|
||||
@@ -229,6 +221,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,34 +242,52 @@ 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
|
||||
|
||||
<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>
|
||||
</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>
|
||||
</ul>
|
||||
</Card>
|
||||
<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>
|
||||
</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>
|
||||
</ul>
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<AccordionGroup>
|
||||
@@ -302,12 +313,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.
|
||||
@@ -330,10 +342,11 @@ openssl rand -hex 32
|
||||
- **`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,20 +357,20 @@ 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.
|
||||
@@ -371,17 +384,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
|
||||
```
|
||||
@@ -430,7 +445,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
|
||||
@@ -459,27 +475,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.
|
||||
|
||||
@@ -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
|
||||
@@ -291,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>
|
||||
|
||||
---
|
||||
@@ -301,7 +305,8 @@ 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
|
||||
@@ -472,7 +477,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>
|
||||
|
||||
---
|
||||
@@ -493,4 +499,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="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.
|
||||
</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="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.
|
||||
</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>
|
||||
</CardGroup>
|
||||
|
||||
## Choosing a Migration Method
|
||||
@@ -35,16 +33,14 @@ Before starting the migration, ensure you have:
|
||||
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.
|
||||
</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.
|
||||
</Card>
|
||||
<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.
|
||||
</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.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Manual Migration (Small Instances)
|
||||
@@ -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)
|
||||
@@ -84,27 +77,17 @@ For instances with many users and resumes, use the migration scripts to automate
|
||||
To run the migration scripts, you need the following installed on your host machine:
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Node.js Runtime">
|
||||
**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
|
||||
```
|
||||
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
|
||||
```
|
||||
</Card>
|
||||
<Card title="Node.js Runtime">
|
||||
**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 ```
|
||||
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 ```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
### Environment Setup
|
||||
@@ -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:
|
||||
|
||||
+54
-52
@@ -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,14 +91,14 @@ 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.
|
||||
The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error.
|
||||
</Info>
|
||||
|
||||
## Provider-Specific Setup
|
||||
@@ -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
|
||||
@@ -266,17 +272,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 +284,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,22 +300,24 @@ 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>
|
||||
|
||||
## Security Considerations
|
||||
|
||||
<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.
|
||||
</Card>
|
||||
<Card title="Protect secrets" icon="key">
|
||||
Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager.
|
||||
</Card>
|
||||
<Card title="Verify redirect URIs" icon="shield-check">
|
||||
Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
|
||||
</Card>
|
||||
<Card title="Review scopes" icon="list-check">
|
||||
Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
|
||||
</Card>
|
||||
<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.
|
||||
</Card>
|
||||
<Card title="Protect secrets" icon="key">
|
||||
Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager.
|
||||
</Card>
|
||||
<Card title="Verify redirect URIs" icon="shield-check">
|
||||
Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
|
||||
</Card>
|
||||
<Card title="Review scopes" icon="list-check">
|
||||
Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Reference in New Issue
Block a user