Compare commits

..
83 Commits
Author SHA1 Message Date
Amruth Pillai 47abce351b sync translations with crowdin 2026-04-27 15:22:13 +02:00
Amruth Pillai 8f6c65b7fd chore(release): 🚀 v5.0.20 2026-04-27 15:21:07 +02:00
github-actions[bot]andCrowdin Bot 5a88ac12bf Sync Translations from Crowdin (#2935)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-27 15:13:05 +02:00
RuzenieAmruth Pillaiautofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
118004b3d3 feat:icon colors (#2928)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-27 15:12:55 +02:00
github-actions[bot]andCrowdin Bot 6a8dd480cb Sync Translations from Crowdin (#2934)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-27 13:39:40 +02:00
Amruth Pillai 5d8126d4b0 feat: combine settings pages into a single integrations page 2026-04-27 12:56:15 +02:00
JamesGoslings 8566a9fd0f fix(i18n): translate Skills section title to Chinese (zh-CN) (#2931) 2026-04-27 10:47:46 +02:00
github-actions[bot]andCrowdin Bot 0d0bc61582 Sync Translations from Crowdin (#2932)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-27 10:47:22 +02:00
JamesGoslings e3af637100 docs: list Meowth alongside existing templates (#2929) 2026-04-27 10:47:14 +02:00
github-actions[bot]andCrowdin Bot f752145c57 Sync Translations from Crowdin (#2926)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-27 10:45:59 +02:00
Amruth Pillai b87f200767 feat: Add better email templates for password reset and email verification. 2026-04-27 10:45:44 +02:00
Amruth Pillai 73ec8b2ffb chore: release version 5.0.20 with new Meowth resume template and updated translations 2026-04-27 08:52:18 +02:00
github-actions[bot]andCrowdin Bot 107537999f Sync Translations from Crowdin (#2925)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-27 08:50:43 +02:00
Amruth Pillai e1bccbcc93 chore: update dependencies, cleanup exports (knip) 2026-04-27 08:48:35 +02:00
JamesGoslingsAmruth Pillaiautofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
54e256be09 feat: add Meowth template with inline three-column entry header (Asian-style compact ATS) (#2923)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-27 08:45:08 +02:00
github-actions[bot]andCrowdin Bot b26d510294 Sync Translations from Crowdin (#2922)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-26 11:46:56 +02:00
Amruth Pillai 623ca5c675 allow loopback to localhost on mcp, only for local MCP clients 2026-04-26 10:59:10 +02:00
Amruth PillaiandClaude 76e00ae019 Revert "fix(resume): prevent orphaned section headings at page break (#2851)" (#2920)
This reverts commit 775f625f6e.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-26 01:23:22 +02:00
Amruth Pillai ff0ef12ecc Merge branch 'main' of github.com:amruthpillai/reactive-resume 2026-04-26 01:08:00 +02:00
Amruth Pillai 1bcb2e2af6 update changelog 2026-04-26 01:07:58 +02:00
github-actions[bot]andCrowdin Bot 796dc2ca41 Sync Translations from Crowdin (#2919)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-26 01:01:34 +02:00
github-actions[bot]andCrowdin Bot 80bf41254b Sync Translations from Crowdin (#2918)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-26 00:58:11 +02:00
Amruth Pillai bbc38d2f09 implement logic from #2853, thanks to @trigger-xyz 2026-04-26 00:57:50 +02:00
Christian PojoniClaude Opus 4.6autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
775f625f6e fix(resume): prevent orphaned section headings at page break (#2851)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-26 00:49:03 +02:00
2203933fac Sync Translations from Crowdin (#2917)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-04-26 00:48:24 +02:00
Platinum1154andAmruth Pillai 907e32a731 feat: add text color support to the rich text editor (#2903)
* feat: add text color support to the rich text editor

* improve design of text color picker

* Update translations for color picker features in multiple languages

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-04-26 00:48:03 +02:00
autofix-ci[bot] d6919e340b [autofix.ci] apply automated fixes 2026-04-25 22:34:53 +00:00
Amruth Pillai e05ae42fbb Remove "Reactive Resume" translations from multiple language files to streamline localization efforts. 2026-04-26 00:33:45 +02:00
github-actions[bot]andCrowdin Bot e99f39d139 Sync Translations from Crowdin (#2916)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-26 00:33:12 +02:00
Platinum1154andAmruth Pillai a4e7d6680d feat: add Chinese font options (#2905)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-04-26 00:32:42 +02:00
iago macedoandAmruth Pillai 77ad14b359 feat: add OpenRouter as AI provider (#2906)
Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-04-26 00:00:14 +02:00
Amruth Pillai 9b3916d43e chore: ⬆️ update dependencies 2026-04-25 23:34:19 +02:00
github-actions[bot]andCrowdin Bot 29f1aaf0da [skip ci] chore(i18n): sync translations from crowdin (#2914)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-25 17:32:04 +02:00
github-actions[bot]andCrowdin Bot 73cef02a4f Sync Translations from Crowdin (#2913)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-25 15:47:33 +02:00
github-actions[bot]andCrowdin Bot 2cb1897922 Sync Translations from Crowdin (#2912)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-25 15:32:52 +02:00
Amruth Pillai d0af9f4b4f test(security): cover url validation and form edge cases
Add and update tests for new security utilities and tightened UI behavior to prevent regressions in validation and error handling paths.

Made-with: Cursor
2026-04-25 15:31:19 +02:00
Amruth Pillai 08e9c80037 refactor(ui): improve error handling and input safety in app flows
Normalize frontend error rendering and tighten input/path handling across auth, builder, dashboard, and shared components for more resilient UX behavior.

Made-with: Cursor
2026-04-25 15:31:13 +02:00
Amruth Pillai a42dbcd452 feat(security): harden auth, oauth, and printer endpoints
Add stricter URL and redirect validation, endpoint rate limiting, safer defaults for printer and compose config, and CSP protections across server and API surfaces.

Made-with: Cursor
2026-04-25 15:31:06 +02:00
Amruth Pillai d3102565e4 chore(i18n): sync translation catalogs
Update locale catalogs to include the latest extracted strings used by the recent auth and security UX updates.

Made-with: Cursor
2026-04-25 15:30:51 +02:00
Amruth Pillai c0387298c5 docs(self-hosting): refresh docker and setup guidance
Clarify self-hosting, migration, and quickstart instructions to reflect the new secure defaults and deployment flow.

Made-with: Cursor
2026-04-25 15:30:47 +02:00
Amruth Pillai 9e1e593dc4 add SECURITY.md 2026-04-25 11:29:27 +02:00
Amruth Pillai 849aad6497 allow for running dev environment inside docker 2026-04-25 11:26:02 +02:00
Amruth Pillai 847d69b621 pin vite-plus versions to 0.1.19 2026-04-25 10:53:19 +02:00
Yeung Lihan 6ff754d125 fix: reduce preview wheel zoom sensitivity (#2911) 2026-04-25 09:43:58 +02:00
JamesGoslings ff3d4b1337 fix: align role period text to end in experience item (#2876) (#2908)
When an experience entry has multiple roles (Role Progression),
the period/date for each role was left-aligned in the grid layout,
causing it to appear off-center instead of right-aligned under the
location field.

Added `text-end` class to the role period element to match the
alignment behavior of the single-role header period. Uses `text-end`
instead of `text-right` for proper RTL language support.
2026-04-25 09:43:31 +02:00
github-actions[bot]andCrowdin Bot 2d2d5cd505 Sync Translations from Crowdin (#2902)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-23 14:17:30 +02:00
Amruth Pillai c6e8df0a00 - Pinned some packages to specific versions to avoid potential security vulnerabilities through transitive dependencies. (through running pnpm audit)
- Updated dependencies and lockfile.
- Synced translation catalogs from Crowdin.
2026-04-22 15:15:10 +02:00
Amruth Pillai 7df9b1e4b5 autocomplete passkey, if exists 2026-04-14 15:04:19 +02:00
github-actions[bot]andCrowdin Bot 0b4bac62a7 [skip ci] chore(i18n): sync translations from crowdin (#2892)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-14 14:42:06 +02:00
Amruth Pillai 82cb6e7590 update translations for passkeys 2026-04-14 14:40:07 +02:00
github-actions[bot]andCrowdin Bot 46dac6d3b1 [skip ci] chore(i18n): sync translations from crowdin (#2891)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-14 12:16:24 +02:00
Amruth Pillai b0b0d27c1f try again 2026-04-14 12:15:01 +02:00
Amruth Pillai b12d1184bc switch to crowdin github action 2026-04-14 11:53:20 +02:00
Amruth Pillai d6e0d9ac61 switch to crowdin github action 2026-04-14 11:50:07 +02:00
Amruth Pillai c19b9746c8 📦 v5.0.18 - https://docs.rxresu.me/changelog (Passkeys Support) 2026-04-14 10:51:04 +02:00
Amruth Pillai 4ed6177aee New translations en-us.po (Dutch) (#2886)
[ci skip]
2026-04-09 20:46:17 +02:00
Amruth Pillai 5900de101c update peer dependencies 2026-04-09 16:05:14 +02:00
Vedant Shankar BhavsarandAmruth Pillai 61b3324941 fix: improve email handling and user lookup in OAuth configuration (#2874)
* refactor: improve email handling and user lookup in OAuth configuration

* refactor: enhance OAuth user mapping and improve email handling

---------

Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
2026-04-09 16:02:30 +02:00
Amruth Pillai 0e858c5967 update webfontlist.json 2026-04-09 15:15:08 +02:00
russandClaude 8c968e92f4 fix: correct Computer Modern Sans italic font file mappings (#2881)
The 400italic and 700italic variants for Computer Modern Sans pointed
to incorrect filenames. `cmunsl.woff` does not exist in the upstream
bitmaks/cm-web-fonts repository (returns 404 from jsDelivr CDN).

This causes PDF export to fail with "Waiting failed: 5000ms exceeded"
because Puppeteer's waitForFonts stalls on the 404, preventing the
page from signalling data-wf-loaded="true" within the timeout.

Correct mapping verified against the upstream @font-face declarations
in bitmaks/cm-web-fonts font/Sans/cmun-sans.css:

  - 400 normal:  cmunss.woff (unchanged)
  - 400 italic:  cmunsi.woff (was cmunsl.woff, which does not exist)
  - 700 normal:  cmunsx.woff (unchanged)
  - 700 italic:  cmunso.woff (was cmunsi.woff, which is the 400 italic)

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-09 15:12:30 +02:00
Amruth Pillai bea8ff1beb Fix MCP tool names for Claude Desktop incompatibility (#2885)
* fixes #2884, rename tool names for claude to work

* update dependencies
2026-04-09 15:03:18 +02:00
Amruth Pillai 85e0b0a96d further improvements to the mcp server 2026-04-09 10:06:50 +02:00
Amruth Pillai 1b266ba7ac Implement Resume Analysis (#2882)
* Implement Resume Analysis

* 📦 v5.0.17 - https://docs.rxresu.me/changelog
2026-04-09 09:04:27 +02:00
Amruth Pillai 1810dc8b07 better mcp server 2026-04-09 00:28:31 +02:00
Amruth Pillai 06b9da39ed Sync Translations from Crowdin (#2864)
* New translations en-us.po (Romanian)
[ci skip]

* New translations en-us.po (French)
[ci skip]

* New translations en-us.po (Spanish)
[ci skip]

* New translations en-us.po (Afrikaans)
[ci skip]

* New translations en-us.po (Arabic)
[ci skip]

* New translations en-us.po (Bulgarian)
[ci skip]

* New translations en-us.po (Catalan)
[ci skip]

* New translations en-us.po (Czech)
[ci skip]

* New translations en-us.po (Danish)
[ci skip]

* New translations en-us.po (German)
[ci skip]

* New translations en-us.po (Greek)
[ci skip]

* New translations en-us.po (Finnish)
[ci skip]

* New translations en-us.po (Hebrew)
[ci skip]

* New translations en-us.po (Hungarian)
[ci skip]

* New translations en-us.po (Italian)
[ci skip]

* New translations en-us.po (Japanese)
[ci skip]

* New translations en-us.po (Korean)
[ci skip]

* New translations en-us.po (Lithuanian)
[ci skip]

* New translations en-us.po (Dutch)
[ci skip]

* New translations en-us.po (Norwegian)
[ci skip]

* New translations en-us.po (Polish)
[ci skip]

* New translations en-us.po (Portuguese)
[ci skip]

* New translations en-us.po (Russian)
[ci skip]

* New translations en-us.po (Slovak)
[ci skip]

* New translations en-us.po (Slovenian)
[ci skip]

* New translations en-us.po (Albanian)
[ci skip]

* New translations en-us.po (Serbian (Cyrillic))
[ci skip]

* New translations en-us.po (Swedish)
[ci skip]

* New translations en-us.po (Turkish)
[ci skip]

* New translations en-us.po (Ukrainian)
[ci skip]

* New translations en-us.po (Chinese Simplified)
[ci skip]

* New translations en-us.po (Chinese Traditional)
[ci skip]

* New translations en-us.po (Vietnamese)
[ci skip]

* New translations en-us.po (Portuguese, Brazilian)
[ci skip]

* New translations en-us.po (Indonesian)
[ci skip]

* New translations en-us.po (Persian)
[ci skip]

* New translations en-us.po (Khmer)
[ci skip]

* New translations en-us.po (Tamil)
[ci skip]

* New translations en-us.po (Bengali)
[ci skip]

* New translations en-us.po (Marathi)
[ci skip]

* New translations en-us.po (Thai)
[ci skip]

* New translations en-us.po (Latvian)
[ci skip]

* New translations en-us.po (Azerbaijani)
[ci skip]

* New translations en-us.po (Hindi)
[ci skip]

* New translations en-us.po (Malay)
[ci skip]

* New translations en-us.po (Telugu)
[ci skip]

* New translations en-us.po (English, United Kingdom)
[ci skip]

* New translations en-us.po (Malayalam)
[ci skip]

* New translations en-us.po (Uzbek)
[ci skip]

* New translations en-us.po (Kannada)
[ci skip]

* New translations en-us.po (Odia)
[ci skip]

* New translations en-us.po (Amharic)
[ci skip]

* New translations en-us.po (Nepali)
[ci skip]

* New translations en-us.po (Swedish)
[ci skip]
2026-04-05 09:50:40 +02:00
Amruth Pillai 11373763fd fixes #2868 2026-04-05 09:03:49 +02:00
Amruth Pillai b3224ce59b Merge branch 'main' of github.com:amruthpillai/reactive-resume 2026-04-04 20:59:38 +02:00
Amruth Pillai 145b17de0f fix #2865, spacing between section items messed up 2026-04-04 20:59:30 +02:00
Amruth Pillai 623c5f6f81 Downgrade Cosign installer version to v3 2026-04-04 12:53:26 +02:00
Amruth Pillai bcbe70d231 📦 v5.0.16 - https://docs.rxresu.me/changelog 2026-04-04 12:31:24 +02:00
Amruth Pillai 8b52b9d8fc update translations 2026-04-04 12:29:10 +02:00
Amruth Pillai 4ecc66d081 Update title/metadata of generated PDF (#2863) 2026-04-04 12:28:32 +02:00
Amruth Pillai 10d58175f1 refactor: update layout of resume items for better alignment and spacing 2026-04-04 12:21:05 +02:00
Amruth Pillai 923f5f6173 fixes #2733: Bug where date range is displayed on separate line (#2862) 2026-04-04 12:03:09 +02:00
Amruth Pillai 9332e1e3ff chore: update GitHub Actions workflows to use Node 24 and upgrade action versions 2026-04-02 00:28:36 +02:00
AaronJoel Dev 77f31fa127 fix: prevent desktop horizontal overflow in home hero (#2854) 2026-04-02 00:16:16 +02:00
Amruth Pillai 4fd43657dc 📦 v5.0.15 - https://docs.rxresu.me/changelog 2026-04-02 00:14:54 +02:00
Aman Gupta d9a24448e8 fix: refactor useCSSVariables to ensure valid highest font weights (#2852) 2026-03-31 22:41:24 +02:00
Amruth Pillai 69ff78b656 Sync Translations from Crowdin (#2848)
* New translations en-us.po (Romanian)
[ci skip]

* New translations en-us.po (French)
[ci skip]

* New translations en-us.po (Spanish)
[ci skip]

* New translations en-us.po (Afrikaans)
[ci skip]

* New translations en-us.po (Arabic)
[ci skip]

* New translations en-us.po (Bulgarian)
[ci skip]

* New translations en-us.po (Catalan)
[ci skip]

* New translations en-us.po (Czech)
[ci skip]

* New translations en-us.po (Danish)
[ci skip]

* New translations en-us.po (German)
[ci skip]

* New translations en-us.po (Greek)
[ci skip]

* New translations en-us.po (Finnish)
[ci skip]

* New translations en-us.po (Hebrew)
[ci skip]

* New translations en-us.po (Hungarian)
[ci skip]

* New translations en-us.po (Italian)
[ci skip]

* New translations en-us.po (Japanese)
[ci skip]

* New translations en-us.po (Korean)
[ci skip]

* New translations en-us.po (Lithuanian)
[ci skip]

* New translations en-us.po (Dutch)
[ci skip]

* New translations en-us.po (Norwegian)
[ci skip]

* New translations en-us.po (Polish)
[ci skip]

* New translations en-us.po (Portuguese)
[ci skip]

* New translations en-us.po (Russian)
[ci skip]

* New translations en-us.po (Slovak)
[ci skip]

* New translations en-us.po (Slovenian)
[ci skip]

* New translations en-us.po (Albanian)
[ci skip]

* New translations en-us.po (Serbian (Cyrillic))
[ci skip]

* New translations en-us.po (Swedish)
[ci skip]

* New translations en-us.po (Turkish)
[ci skip]

* New translations en-us.po (Ukrainian)
[ci skip]

* New translations en-us.po (Chinese Simplified)
[ci skip]

* New translations en-us.po (Chinese Traditional)
[ci skip]

* New translations en-us.po (Vietnamese)
[ci skip]

* New translations en-us.po (Portuguese, Brazilian)
[ci skip]

* New translations en-us.po (Indonesian)
[ci skip]

* New translations en-us.po (Persian)
[ci skip]

* New translations en-us.po (Khmer)
[ci skip]

* New translations en-us.po (Tamil)
[ci skip]

* New translations en-us.po (Bengali)
[ci skip]

* New translations en-us.po (Marathi)
[ci skip]

* New translations en-us.po (Thai)
[ci skip]

* New translations en-us.po (Latvian)
[ci skip]

* New translations en-us.po (Azerbaijani)
[ci skip]

* New translations en-us.po (Hindi)
[ci skip]

* New translations en-us.po (Malay)
[ci skip]

* New translations en-us.po (Telugu)
[ci skip]

* New translations en-us.po (English, United Kingdom)
[ci skip]

* New translations en-us.po (Malayalam)
[ci skip]

* New translations en-us.po (Uzbek)
[ci skip]

* New translations en-us.po (Kannada)
[ci skip]

* New translations en-us.po (Odia)
[ci skip]

* New translations en-us.po (Amharic)
[ci skip]

* New translations en-us.po (Nepali)
[ci skip]
2026-03-29 23:34:21 +02:00
Amruth Pillai 0794b5c955 update dependencies 2026-03-29 23:09:25 +02:00
Amruth Pillai 5c986cc68d Sync Translations from Crowdin (#2838)
* New translations en-us.po (Romanian)
[ci skip]

* New translations en-us.po (French)
[ci skip]

* New translations en-us.po (Spanish)
[ci skip]

* New translations en-us.po (Afrikaans)
[ci skip]

* New translations en-us.po (Arabic)
[ci skip]

* New translations en-us.po (Bulgarian)
[ci skip]

* New translations en-us.po (Catalan)
[ci skip]

* New translations en-us.po (Czech)
[ci skip]

* New translations en-us.po (Danish)
[ci skip]

* New translations en-us.po (German)
[ci skip]

* New translations en-us.po (Greek)
[ci skip]

* New translations en-us.po (Finnish)
[ci skip]

* New translations en-us.po (Hebrew)
[ci skip]

* New translations en-us.po (Hungarian)
[ci skip]

* New translations en-us.po (Italian)
[ci skip]

* New translations en-us.po (Japanese)
[ci skip]

* New translations en-us.po (Korean)
[ci skip]

* New translations en-us.po (Lithuanian)
[ci skip]

* New translations en-us.po (Dutch)
[ci skip]

* New translations en-us.po (Norwegian)
[ci skip]

* New translations en-us.po (Polish)
[ci skip]

* New translations en-us.po (Portuguese)
[ci skip]

* New translations en-us.po (Russian)
[ci skip]

* New translations en-us.po (Slovak)
[ci skip]

* New translations en-us.po (Slovenian)
[ci skip]

* New translations en-us.po (Albanian)
[ci skip]

* New translations en-us.po (Serbian (Cyrillic))
[ci skip]

* New translations en-us.po (Swedish)
[ci skip]

* New translations en-us.po (Turkish)
[ci skip]

* New translations en-us.po (Ukrainian)
[ci skip]

* New translations en-us.po (Chinese Simplified)
[ci skip]

* New translations en-us.po (Chinese Traditional)
[ci skip]

* New translations en-us.po (Vietnamese)
[ci skip]

* New translations en-us.po (Portuguese, Brazilian)
[ci skip]

* New translations en-us.po (Indonesian)
[ci skip]

* New translations en-us.po (Persian)
[ci skip]

* New translations en-us.po (Khmer)
[ci skip]

* New translations en-us.po (Tamil)
[ci skip]

* New translations en-us.po (Bengali)
[ci skip]

* New translations en-us.po (Marathi)
[ci skip]

* New translations en-us.po (Thai)
[ci skip]

* New translations en-us.po (Latvian)
[ci skip]

* New translations en-us.po (Azerbaijani)
[ci skip]

* New translations en-us.po (Hindi)
[ci skip]

* New translations en-us.po (Malay)
[ci skip]

* New translations en-us.po (Telugu)
[ci skip]

* New translations en-us.po (English, United Kingdom)
[ci skip]

* New translations en-us.po (Malayalam)
[ci skip]

* New translations en-us.po (Uzbek)
[ci skip]

* New translations en-us.po (Kannada)
[ci skip]

* New translations en-us.po (Odia)
[ci skip]

* New translations en-us.po (Amharic)
[ci skip]

* New translations en-us.po (Nepali)
[ci skip]

* New translations en-us.po (Russian)
[ci skip]
2026-03-29 23:07:48 +02:00
autofix-ci[bot] 688c002822 [autofix.ci] apply automated fixes 2026-03-24 10:58:09 +00:00
Amruth Pillai ced49d5314 Update Crowdin configuration file 2026-03-24 11:57:03 +01:00
345 changed files with 76902 additions and 22351 deletions
+14 -1
View File
@@ -10,7 +10,7 @@ APP_URL="http://localhost:3000"
PRINTER_APP_URL="http://host.docker.internal:3000"
# --- Printer ---
PRINTER_ENDPOINT="ws://localhost:4000?token=1234567890"
PRINTER_ENDPOINT="ws://localhost:4000?token=change-me"
# --- Database (PostgreSQL) ---
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
@@ -30,12 +30,21 @@ GOOGLE_CLIENT_SECRET=""
GITHUB_CLIENT_ID=""
GITHUB_CLIENT_SECRET=""
# Social Auth (LinkedIn, optional)
LINKEDIN_CLIENT_ID=""
LINKEDIN_CLIENT_SECRET=""
# Custom OAuth Provider (optional)
OAUTH_PROVIDER_NAME=""
OAUTH_CLIENT_ID=""
OAUTH_CLIENT_SECRET=""
OAUTH_DISCOVERY_URL=""
OAUTH_AUTHORIZATION_URL=""
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=""
# AI provider base URL allowlist (optional, comma-separated hosts/origins)
# Example: api.openai.com,https://gateway.ai.vercel.com
AI_ALLOWED_BASE_URLS=""
# --- Email (optional) ---
# If all keys are disabled, the app logs the email to be sent to the console instead.
@@ -78,3 +87,7 @@ FLAG_DISABLE_IMAGE_PROCESSING="false"
# This is not used within Reactive Resume, but in src/scripts/fonts/generate.ts to generate a list of fonts served by Google Fonts.
# Note: Make sure "Google Fonts Developer API" is unrestricted.
GOOGLE_CLOUD_API_KEY=""
# --- Crowdin ---
CROWDIN_PROJECT_ID=""
CROWDIN_API_TOKEN=""
+16 -6
View File
@@ -8,21 +8,31 @@ on:
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
autofix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Checkout Repository
uses: actions/checkout@v6
- uses: voidzero-dev/setup-vp@v1
- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version: "24"
cache: true
- run: vp install
- name: Install Dependencies
run: vp install --frozen-lockfile
- run: vp test
- run: vp check --fix
- name: Run Tests
run: vp test
- uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8
- name: Run Lint and Format
run: vp check --fix
- name: Autofix
uses: autofix-ci/action@7a166d7532b277f34e16238930461bf77f9d7ed8
+36
View File
@@ -0,0 +1,36 @@
name: Sync Translations from Crowdin
on:
push:
branches: ["main"]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
crowdin-sync:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v6
- name: Sync Translations from Crowdin
uses: crowdin/github-action@v2
with:
download_translations: true
localization_branch_name: "l10n"
commit_message: "[skip ci] chore(i18n): sync translations from crowdin"
pull_request_title: "Sync Translations from Crowdin"
pull_request_body: "This is an automated pull request to sync translations from [Crowdin](https://crowdin.com/project/reactive-resume)."
pull_request_labels: "l10n"
pull_request_reviewers: "amruthpillai"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
+12 -11
View File
@@ -9,6 +9,7 @@ concurrency:
env:
IMAGE: ${{ github.repository }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build:
@@ -41,16 +42,16 @@ jobs:
run: echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -58,7 +59,7 @@ jobs:
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: |
ghcr.io/${{ env.IMAGE }}
@@ -68,7 +69,7 @@ jobs:
- name: Build and Push by Digest
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: .
sbom: true
@@ -88,7 +89,7 @@ jobs:
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: digests-${{ matrix.arch }}
path: /tmp/digests/*
@@ -118,23 +119,23 @@ jobs:
run: echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
- name: Download digests
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -152,7 +153,7 @@ jobs:
- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
uses: docker/metadata-action@v6
with:
images: |
ghcr.io/${{ env.IMAGE }}
+1
View File
@@ -2,6 +2,7 @@ dist
.env*
/data
.nitro
.agents
.output
.vercel
.cursor
+3 -6
View File
@@ -1,12 +1,8 @@
{
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll.oxc": "explicit"
},
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"files.associations": {
@@ -40,5 +36,6 @@
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
],
"tailwindCSS.experimental.configFile": "src/styles/globals.css",
"typescript.experimental.useTsgo": true
"typescript.experimental.useTsgo": true,
"i18n-ally.keystyle": "nested"
}
+19 -21
View File
@@ -1,5 +1,3 @@
# AGENTS.md
## Overview
Reactive Resume is a single-package full-stack TypeScript app (not a monorepo) built with [TanStack Start](https://tanstack.com/start/latest/docs/framework/react/overview) (React, Vite, Nitro). It serves both frontend and API on port 3000.
@@ -91,24 +89,24 @@ Copy `.env.example` to `.env` if not present. Key notes for local dev:
`vp` is the global CLI for Vite+. Do not use pnpm/npm/yarn directly — Vite+ wraps the underlying package manager.
| Task | Command |
| -------------------------- | --------------------------------------------------------------- |
| Install dependencies | `vp install` |
| Dev server (port 3000) | `vp dev` |
| Lint (Oxlint, type-aware) | `vp lint --type-aware` |
| Format (Oxfmt) | `vp fmt` |
| Check (lint + fmt + types) | `vp check` |
| Typecheck | `pnpm typecheck` (uses tsgo) |
| Run tests | `vp test` |
| DB migrations | `pnpm db:generate` / `pnpm db:migrate` (auto-runs on dev start) |
| DB studio | `pnpm db:studio` |
| i18n extraction | `pnpm lingui:extract` |
| Add a dependency | `vp add <package>` |
| Remove a dependency | `vp remove <package>` |
| One-off binary | `vp dlx <package>` |
| Build for production | `vp build` |
| Preview production build | `vp preview` |
| Start production server | `pnpm start` |
| Task | Command |
| ----------------------------- | --------------------------------------------------------------- |
| Install dependencies | `vp install` |
| Dev server (port 3000) | `vp dev` |
| Lint (Oxlint, type-aware) | `vp lint --type-aware` |
| Format (Oxfmt) | `vp fmt` |
| Check (lint + format + types) | `vp check` |
| Typecheck | `pnpm typecheck` (uses tsgo) |
| Run tests | `vp test` |
| DB migrations | `pnpm db:generate` / `pnpm db:migrate` (auto-runs on dev start) |
| DB studio | `pnpm db:studio` |
| i18n extraction | `pnpm lingui:extract` |
| Add a dependency | `vp add <package>` |
| Remove a dependency | `vp remove <package>` |
| One-off binary | `vp dlx <package>` |
| Build for production | `vp build` |
| Preview production build | `vp preview` |
| Start production server | `pnpm start` |
## Vite+ Pitfalls
@@ -130,4 +128,4 @@ Copy `.env.example` to `.env` if not present. Key notes for local dev:
## Review Checklist for Agents
- [ ] Run `vp install` after pulling remote changes and before getting started.
- [ ] Run `vp check` and `vp test` to validate changes.
- [ ] Run `pnpm lint:fix`, `pnpm format:fix`, `pnpm typecheck` and `vp test` to validate changes.
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+21
View File
@@ -0,0 +1,21 @@
# syntax=docker/dockerfile:1
# ---------- Development Layer ----------
FROM node:24-slim AS development
ENV NODE_ENV="development"
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
EXPOSE 3000
CMD ["pnpm", "run", "dev"]
+4
View File
@@ -126,6 +126,10 @@ Built with privacy as a core principle, Reactive Resume gives you complete owner
<img src="public/templates/jpg/ditgar.jpg" alt="Ditgar" width="150" />
<br /><sub><b>Ditgar</b></sub>
</td>
<td align="center">
<img src="public/templates/jpg/meowth.jpg" alt="Meowth" width="150" />
<br /><sub><b>Meowth</b></sub>
</td>
</tr>
</table>
+34
View File
@@ -0,0 +1,34 @@
# Security Policy
## Supported Versions
Reactive Resume provides security updates for version `5.0.x` and newer.
Earlier major versions are no longer supported.
| Version | Supported |
| ------- | ------------------ |
| >= 5.0 | :white_check_mark: |
| 4.x | :x: |
| < 4.0 | :x: |
## Reporting a Vulnerability
If you believe you have found a security vulnerability, please report it
privately instead of opening a public issue.
Preferred reporting channels:
- Use GitHub's private vulnerability reporting for this repository.
- If that is unavailable, email `hello@amruthpillai.com` with the subject
`Security Vulnerability: Reactive Resume`.
Please include as much detail as possible, including:
- A description of the vulnerability and its impact.
- Steps to reproduce or a proof of concept.
- Affected versions, configuration, or deployment details.
- Any relevant logs, screenshots, or links.
You can expect an initial response within 7 days. If the report is accepted,
we will coordinate remediation and disclosure timing with you before publishing
details publicly.
-3
View File
@@ -19,8 +19,5 @@
"ui": "@/components/ui",
"lib": "@/utils",
"hooks": "@/hooks"
},
"registries": {
"@animate-ui": "https://animate-ui.com/r/{name}.json"
}
}
+65 -12
View File
@@ -20,32 +20,34 @@ services:
image: postgres:latest
restart: unless-stopped
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- postgres_data:/var/lib/postgresql
ports:
- "5432:5432"
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "postgres"]
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
start_period: 10s
interval: 30s
timeout: 10s
retries: 3
browserless:
image: ghcr.io/browserless/chromium:latest
image: ${BROWSERLESS_IMAGE:-ghcr.io/browserless/chromium:latest}
restart: unless-stopped
ports:
- "4000:3000"
environment:
- QUEUED=10
- HEALTH=true
- CONCURRENT=5
- TOKEN=1234567890
QUEUED: 10
HEALTH: "true"
CONCURRENT: 5
TOKEN: ${BROWSERLESS_TOKEN:-change-me}
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
test: ["CMD-SHELL", 'curl -f "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN:-change-me}"']
interval: 10s
timeout: 5s
retries: 10
@@ -57,14 +59,16 @@ services:
restart: unless-stopped
ports:
- "9222:9222"
extra_hosts:
- "host.docker.internal:host-gateway"
seaweedfs:
image: chrislusf/seaweedfs:latest
restart: unless-stopped
command: server -s3 -filer -dir=/data -ip=0.0.0.0
environment:
- AWS_ACCESS_KEY_ID=seaweedfs
- AWS_SECRET_ACCESS_KEY=seaweedfs
AWS_ACCESS_KEY_ID: seaweedfs
AWS_SECRET_ACCESS_KEY: seaweedfs
volumes:
- seaweedfs_data:/data
ports:
@@ -106,7 +110,56 @@ services:
timeout: 10s
retries: 3
web_app:
profiles: ["app"]
build:
context: .
dockerfile: Dockerfile.dev
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
environment:
PRINTER_APP_URL: http://web_app:3000
PRINTER_ENDPOINT: ws://browserless:3000?token=${BROWSERLESS_TOKEN:-change-me}
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
SMTP_HOST: mailpit
S3_ENDPOINT: http://seaweedfs:8333
CHOKIDAR_INTERVAL: 100
WATCHPACK_POLLING: "true"
CHOKIDAR_USEPOLLING: "true"
volumes:
- ./:/app
- web_app_data:/app/data
- web_app_pnpm_store:/pnpm/store
- web_app_nitro_cache:/app/.nitro
- web_app_tanstack_cache:/app/.tanstack
- web_app_node_modules:/app/node_modules
depends_on:
postgres:
condition: service_healthy
browserless:
condition: service_healthy
seaweedfs:
condition: service_healthy
seaweedfs_create_bucket:
condition: service_completed_successfully
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
start_period: 10s
interval: 30s
timeout: 10s
retries: 3
volumes:
mailpit_data:
postgres_data:
seaweedfs_data:
web_app_data:
web_app_pnpm_store:
web_app_nitro_cache:
web_app_node_modules:
web_app_tanstack_cache:
+32 -8
View File
@@ -4,6 +4,8 @@ services:
postgres:
image: postgres:latest
restart: unless-stopped
networks:
- data_network
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
@@ -18,15 +20,17 @@ services:
retries: 3
browserless:
image: ghcr.io/browserless/chromium:latest
image: ${BROWSERLESS_IMAGE:-ghcr.io/browserless/chromium:latest}
restart: unless-stopped
networks:
- printer_network
environment:
- QUEUED=10
- HEALTH=true
- CONCURRENT=5
- TOKEN=1234567890
- TOKEN=${BROWSERLESS_TOKEN:-change-me}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=1234567890"]
test: ["CMD-SHELL", 'curl -f "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN:-change-me}"']
interval: 10s
timeout: 5s
retries: 10
@@ -36,6 +40,8 @@ services:
# chrome:
# image: chromedp/headless-shell:latest
# restart: unless-stopped
# networks:
# - printer_network
# ports:
# - "9222:9222"
@@ -43,6 +49,8 @@ services:
image: chrislusf/seaweedfs:latest
restart: unless-stopped
command: server -s3 -filer -dir=/data -ip=0.0.0.0
networks:
- storage_network
environment:
- AWS_ACCESS_KEY_ID=seaweedfs
- AWS_SECRET_ACCESS_KEY=seaweedfs
@@ -65,21 +73,32 @@ services:
mc mb seaweedfs/reactive-resume;
exit 0;
"
networks:
- storage_network
depends_on:
seaweedfs:
condition: service_healthy
reactive_resume:
image: amruthpillai/reactive-resume:latest
# image: amruthpillai/reactive-resume:latest
# image: ghcr.io/amruthpillai/reactive-resume:latest
build:
context: .
dockerfile: Dockerfile
networks:
- data_network
- printer_network
- storage_network
ports:
- "3000:3000"
environment:
# Server
- TZ=Etc/UTC
- NODE_ENV=production
- APP_URL=http://localhost:3000
- PRINTER_APP_URL=http://reactive_resume:3000
- PRINTER_APP_URL=http://host.docker.internal:3000
# Printer
- PRINTER_ENDPOINT=ws://browserless:3000?token=1234567890
- PRINTER_ENDPOINT=ws://browserless:3000?token=${BROWSERLESS_TOKEN:-change-me}
# - PRINTER_ENDPOINT=http://chrome:9222 # Or, if you're using chromedp/headless-shell
# Database
- DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres
@@ -93,8 +112,6 @@ services:
- S3_FORCE_PATH_STYLE=true
volumes:
- reactive_resume_data:/app/data
ports:
- "3000:3000"
depends_on:
postgres:
condition: service_healthy
@@ -102,6 +119,8 @@ services:
condition: service_healthy
seaweedfs_create_bucket:
condition: service_completed_successfully
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
start_period: 10s
@@ -113,3 +132,8 @@ volumes:
postgres_data:
seaweedfs_data:
reactive_resume_data:
networks:
data_network:
printer_network:
storage_network:
+8 -5
View File
@@ -1,10 +1,13 @@
preserve_hierarchy: true
commit_message: "[ci skip]"
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
commit_message: "[skip ci] chore(i18n): sync translations from crowdin"
pull_request_title: "Sync Translations from Crowdin"
pull_request_labels: ["l10n"]
pull_request_reviewers: ["amruthpillai"]
pull_request_body: "This is an automated pull request to sync translations from [Crowdin](https://crowdin.com/project/reactive-resume)."
pull_request_labels:
- l10n
pull_request_reviewers:
- amruthpillai
files:
- source: /locales/en-US.po
translation: /locales/%locale%.%file_extension%
+120
View File
@@ -4,6 +4,126 @@ description: "List of all notable changes and updates to Reactive Resume"
rss: true
---
<Update label="v5.0.20" description="27th April 2026">
## Features & Improvements
- Added the new **Meowth** resume template (compact ATS-friendly layout with an inline three-column entry header), thanks to [@JamesGoslings](https://github.com/JamesGoslings). [#2923](https://github.com/amruthpillai/reactive-resume/pull/2923)
- Added support for custom icon colors on section items, thanks to [@Ruzenie](https://github.com/Ruzenie). [#2928](https://github.com/amruthpillai/reactive-resume/pull/2928)
- Improved account-related email UX with better templates for password reset and email verification. [b87f2007](https://github.com/amruthpillai/reactive-resume/commit/b87f2007)
- Consolidated separate settings pages into a single **Integrations** page for a more streamlined configuration flow. [5d8126d4](https://github.com/amruthpillai/reactive-resume/commit/5d8126d4)
## Fixes
- Added a targeted Chinese localization fix for the `Skills` section title (`zh-CN`). [#2931](https://github.com/amruthpillai/reactive-resume/pull/2931)
- Allowed MCP localhost loopback only for local MCP clients, improving local integration behavior while keeping external access restrictions intact. [623ca5c6](https://github.com/amruthpillai/reactive-resume/commit/623ca5c6)
## Maintenance & Localization
- Updated dependencies and lockfile, including cleanup of unused exports (Knip). [e1bccbcc](https://github.com/amruthpillai/reactive-resume/commit/e1bccbcc)
- Synced translation catalogs from Crowdin. [#2922](https://github.com/amruthpillai/reactive-resume/pull/2922), [#2925](https://github.com/amruthpillai/reactive-resume/pull/2925), [#2926](https://github.com/amruthpillai/reactive-resume/pull/2926), [#2932](https://github.com/amruthpillai/reactive-resume/pull/2932), [#2934](https://github.com/amruthpillai/reactive-resume/pull/2934), [#2935](https://github.com/amruthpillai/reactive-resume/pull/2935)
- Updated docs to list **Meowth** alongside existing templates. [#2929](https://github.com/amruthpillai/reactive-resume/pull/2929)
**Full Changelog**: [v5.0.19...v5.0.20](https://github.com/amruthpillai/reactive-resume/compare/v5.0.19...v5.0.20)
</Update>
<Update label="v5.0.19" description="25th April 2026">
## Highlights
- Added **OpenRouter** as an AI provider, thanks to [@iagodemacedo](https://github.com/iagodemacedo). [#2906](https://github.com/amruthpillai/reactive-resume/pull/2906)
- Added Chinese font options, thanks to [@Platinum1154](https://github.com/Platinum1154). [#2905](https://github.com/amruthpillai/reactive-resume/pull/2905)
- Added text color support to the rich text editor, including DOCX export handling for colored text, thanks to [@Platinum1154](https://github.com/Platinum1154). [#2903](https://github.com/amruthpillai/reactive-resume/pull/2903)
- Fixed MCP tool names for Claude Desktop compatibility. [#2885](https://github.com/amruthpillai/reactive-resume/pull/2885)
- Improved Docker-based local development support and refreshed self-hosting setup guidance. [849aad64](https://github.com/amruthpillai/reactive-resume/commit/849aad64), [c0387298](https://github.com/amruthpillai/reactive-resume/commit/c0387298)
## Fixes & Security
- Thank you to [@shaxbozaka](https://github.com/shaxbozaka) for running a security audit on Reactive Resume and helping make the app more secure.
- Hardened auth, OAuth, AI URL validation, printer/export endpoints, resume access controls, Browserless configuration, and public route exposure. [a42dbcd4](https://github.com/amruthpillai/reactive-resume/commit/a42dbcd4)
- Removed the public RPC surface for the printer-only resume fetch endpoint by moving it behind a server function, further shrinking the attack surface for `getByIdForPrinter`, thanks to [@shaxbozaka](https://github.com/shaxbozaka).
- Corrected Computer Modern Sans italic font file mappings, thanks to [@russellbrenner](https://github.com/russellbrenner). [#2881](https://github.com/amruthpillai/reactive-resume/pull/2881)
- Improved email handling and user lookup in OAuth configuration, thanks to [@VedantBhawsar](https://github.com/VedantBhawsar). [#2874](https://github.com/amruthpillai/reactive-resume/pull/2874)
- Aligned role period text in experience items, thanks to [@JamesGoslings](https://github.com/JamesGoslings). [#2908](https://github.com/amruthpillai/reactive-resume/pull/2908)
- Reduced resume preview wheel zoom sensitivity, thanks to [@GoldenSection0618](https://github.com/GoldenSection0618). [#2911](https://github.com/amruthpillai/reactive-resume/pull/2911)
- Prevented orphaned section headings at page breaks, thanks to [@5queezer](https://github.com/5queezer). [#2851](https://github.com/amruthpillai/reactive-resume/pull/2851)
- Added top margin handling for subsequent PDF pages, based on feedback from [@trigger-xyz](https://github.com/trigger-xyz). [bbc38d2f](https://github.com/amruthpillai/reactive-resume/commit/bbc38d2f)
## Maintenance & Localization
- Updated dependencies and lockfile, including security-focused package pins and Vite+ version pins. [c6e8df0a](https://github.com/amruthpillai/reactive-resume/commit/c6e8df0a), [847d69b6](https://github.com/amruthpillai/reactive-resume/commit/847d69b6), [9b3916d4](https://github.com/amruthpillai/reactive-resume/commit/9b3916d4)
- Added `SECURITY.md`. [9e1e593d](https://github.com/amruthpillai/reactive-resume/commit/9e1e593d)
- Added focused tests for auth, printer/export, URL validation, form edge cases, rich text DOCX output, font handling, and CSS sanitization hardening. [d0af9f4b](https://github.com/amruthpillai/reactive-resume/commit/d0af9f4b)
- Synced translation catalogs from Crowdin and moved syncing to GitHub Actions. [#2886](https://github.com/amruthpillai/reactive-resume/pull/2886), [#2918](https://github.com/amruthpillai/reactive-resume/pull/2918)
## New Contributors
- [@russellbrenner](https://github.com/russellbrenner), [@VedantBhawsar](https://github.com/VedantBhawsar), [@JamesGoslings](https://github.com/JamesGoslings), [@GoldenSection0618](https://github.com/GoldenSection0618), [@iagodemacedo](https://github.com/iagodemacedo), and [@Platinum1154](https://github.com/Platinum1154)
**Full Changelog**: [v5.0.17...v5.0.19](https://github.com/amruthpillai/reactive-resume/compare/v5.0.17...v5.0.19)
</Update>
<Update label="v5.0.18" description="14th April 2026">
## Features & Improvements
- Re-enabled **Passkeys** across authentication flows, including passkey sign-in on the login page and passkey management in account settings.
## Maintenance
- Updated dependencies and lockfile.
- Synced translation catalogs from Crowdin.
</Update>
<Update label="v5.0.17" description="9th April 2026">
## Features & Improvements
- Added **Resume Analysis** in the resume builder right sidebar: run an on-demand AI review for an overall score (0 to 100), a dimension scorecard, strengths, and prioritized suggestions. Results are saved per resume and reload after refresh.
- When AI features are disabled, the analysis section shows a short notice with a link to **AI Settings** so you can enable your provider and model first.
- Improved the **MCP server** experience: updated tools, resources, and prompts; added an MCP server card at `/.well-known/mcp/server-card.json` for compatible clients; refreshed the **Using the MCP server** guide.
## Fixes
- Fixed [#2865](https://github.com/amruthpillai/reactive-resume/issues/2865), incorrect spacing between resume section items in shared layouts.
- Fixed [#2868](https://github.com/amruthpillai/reactive-resume/issues/2868) affecting resume item presentation and linked titles in shared item components.
## Maintenance & CI
- Downgraded the Cosign installer action to v3 in the Docker build workflow for more reliable image signing.
- Synced translation catalogs from Crowdin. [#2864](https://github.com/amruthpillai/reactive-resume/pull/2864)
- Dependency and lockfile updates (including changes bundled with MCP and analysis work).
</Update>
<Update label="v5.0.16" description="4th April 2026">
## Features & Improvements
- Updated shared resume item layouts (Experience, Education, Awards, Certifications, Publications, References, Volunteer) for better alignment and spacing consistency.
- Generated printer pages now set a meaningful document title from the resume owner's name (`<Name> - Resume`) for cleaner PDF metadata and tab titles. [#2863](https://github.com/amruthpillai/reactive-resume/pull/2863)
## Fixes
- Fixed [#2733](https://github.com/amruthpillai/reactive-resume/issues/2733), where date ranges could appear on a separate line unexpectedly in resume items. [#2862](https://github.com/amruthpillai/reactive-resume/pull/2862)
- Adjusted Experience section form field ordering/labels so `Location`, `Position`, and `Period` are mapped and displayed correctly.
## Maintenance & Localization
- Synced translation catalogs across all supported locales.
- Updated dependencies and lockfile, including AI SDK, TipTap, TanStack Query, and Vite+ package aliases.
</Update>
<Update label="v5.0.15" description="1st April 2026">
## Features & Improvements
- Added **LinkedIn sign-in** support for self-hosted instances, including sign-in on the auth page and account linking in settings.
- Improved the sign-in experience by showing loading placeholders while social login providers are being fetched.
- Resume builder panel sizes are now persisted more reliably, making the layout feel more consistent between sessions.
- Added clearer labels for resume sorting and filtering controls on the dashboard.
## Fixes
- Improved autosave reliability in the resume builder:
- Unsaved edits are flushed before page unload.
- You now get a clear persistent warning if changes fail to save (for example, due to network issues).
- Strengthened authentication defaults by increasing the minimum password length requirement to 8 characters.
- Improved reliability around resume deletion and server-side error handling for create/update operations.
- Fixed a rare language mix-up during printing/export when multiple print jobs run at the same time, so each PDF/screenshot now reliably uses the correct locale.
- Improved print stability under heavy usage by reusing in-progress PDF and screenshot generation requests for the same resume instead of starting duplicate jobs.
- Hardened printer token signing and token/session/API-key verification paths for better security.
- Improved resilience in AI tailoring output parsing and JSON Resume import normalization.
- Printer service now provides descriptive error messages ("Failed to generate PDF" / "Failed to capture screenshot") instead of generic internal server errors.
- Job descriptions in the job detail sheet now render HTML formatting (headings, lists, bold text) instead of showing raw tags.
- Fixed the "Tailor Resume" button being incorrectly disabled when AI is not configured — the dialog already supports plain resume duplication as a fallback.
- Fixed the tailor resume flow to open the resume builder in a new tab while keeping the job listing open for reference, instead of navigating away and auto-opening the application page.
- Apply option links in the job detail sheet are now validated before rendering, preventing malformed or potentially unsafe URLs.
- Improved job card loading skeletons to match the actual card layout (logo, title, badges) for a smoother loading experience.
- Centered the "Configure Job Search" empty state on the page.
- Extracted the `JobCard` component into its own file for better code organization.
## Docs & Localization
- Updated self-hosting and environment variable documentation to include LinkedIn OAuth configuration.
- Synced translations across locales for the latest authentication and save-status messaging.
</Update>
<Update label="v5.0.14" description="24th March 2026">
## Features
- Implemented OAuth 2.1 support for MCP authentication, thanks to @5queezer. [#2829](https://github.com/amruthpillai/reactive-resume/pull/2829)
+3 -3
View File
@@ -126,7 +126,7 @@ Here are the most commonly used scripts during development:
| `pnpm run build` | Build the application for production |
| `pnpm run start` | Start the production server |
| `pnpm run lint` | Run Oxlint linter and formatter |
| `pnpm run fmt` | Run Oxfmt formatter |
| `pnpm run format` | Run Oxfmt formatter |
| `pnpm run typecheck` | Run TypeScript type checking |
### Database
@@ -252,8 +252,8 @@ Uses [Oxlint](https://oxlint.dev/) for linting and [Oxfmt](https://oxfmt.dev/) f
```bash
# Check and auto-fix issues
pnpm run lint
pnpm run fmt
pnpm run lint:fix
pnpm run format:fix
```
### Type Checking
+2 -2
View File
@@ -39,8 +39,8 @@ description: "Welcome to the documentation for Reactive Resume, a free and open-
</Accordion>
<Accordion title="Multiple Templates" icon="grid-2">
Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditto, Gengar,
Glalie, Kakuna, Lapras, Leafish, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditgar, Ditto,
Gengar, Glalie, Kakuna, Lapras, Leafish, Meowth, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
</Accordion>
<Accordion title="Rich Text Editor" icon="text">
+95 -54
View File
@@ -67,28 +67,60 @@ Before you begin, ensure you have the following installed:
<Steps>
<Step title="Clone the Repository">
```bash git clone https://github.com/amruthpillai/reactive-resume.git cd Reactive-Resume ```
```bash
git clone https://github.com/amruthpillai/reactive-resume.git
cd Reactive-Resume
```
</Step>
<Step title="Configure Environment Variables">
Create a `.env` file in the root directory with the following variables: ```bash # Application
APP_URL=http://localhost:3000 # Database DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres #
Authentication (generate a secure secret) AUTH_SECRET=your-secure-secret-key-here # Storage (S3-compatible)
S3_ACCESS_KEY_ID=seaweedfs S3_SECRET_ACCESS_KEY=seaweedfs S3_ENDPOINT=http://seaweedfs:8333
S3_BUCKET=reactive-resume S3_FORCE_PATH_STYLE=true ```
Create a `.env` file in the root directory with the following variables:
```bash .env
# Application
APP_URL=http://localhost:3000
# Printer (Browserless)
BROWSERLESS_TOKEN=change-me
PRINTER_ENDPOINT=ws://browserless:3000?token=change-me
# Database
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres
# Authentication (generate a secure secret)
AUTH_SECRET=your-secure-secret-key-here
# Storage (S3-compatible via SeaweedFS)
S3_ACCESS_KEY_ID=seaweedfs
S3_SECRET_ACCESS_KEY=seaweedfs
S3_ENDPOINT=http://seaweedfs:8333
S3_BUCKET=reactive-resume
S3_FORCE_PATH_STYLE=true
```
<Warning>
For production deployments, always use strong, unique values for `AUTH_SECRET` and database credentials.
For production deployments, always use strong, unique values for `AUTH_SECRET`, `BROWSERLESS_TOKEN`, and database credentials.
</Warning>
</Step>
<Step title="Start the Services">
```bash docker compose up -d ``` This will start: - **PostgreSQL** — Database for storing user data and resumes -
**SeaweedFS** — S3-compatible storage for file uploads - **Printer** — PDF and screenshot generation service
(browserless/chromium) - **Reactive Resume** — The main application
```bash
docker compose up -d
```
This starts:
- **PostgreSQL** — Database for storing user data and resumes
- **SeaweedFS** — S3-compatible storage for file uploads
- **Browserless** — Headless Chromium service for PDF/screenshot generation
- **Reactive Resume** — The main application
</Step>
<Step title="Access Your Instance">
Once all services are running, access your Reactive Resume instance at: ``` http://localhost:3000 ```
Once all services are running, access your Reactive Resume instance at:
```text
http://localhost:3000
```
</Step>
</Steps>
@@ -97,12 +129,12 @@ Before you begin, ensure you have the following installed:
Here's what each service in the stack does:
| Service | Port | Description |
| ----------- | ---- | ----------------------------------------------------------- |
| `postgres` | 5432 | PostgreSQL database for storing all application data |
| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
| `printer` | 4000 | Headless Chromium service for PDF and screenshot generation |
| `app` | 3000 | The main Reactive Resume application |
| Service | Port | Description |
| ----------------- | ---------------------------------- | ----------------------------------------------------------- |
| `postgres` | 5432 | PostgreSQL database for storing all application data |
| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
| `browserless` | 3000 (internal) / 4000 (published) | Headless Chromium service for PDF and screenshot generation |
| `reactive_resume` | 3000 | The main Reactive Resume application |
### Health Checks
@@ -122,49 +154,58 @@ Here's a complete list of environment variables you can configure:
### Required Variables
| Variable | Description | Example |
| ------------------ | ------------------------------ | --------------------------------------- |
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -base64 32` |
| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
| `PRINTER_ENDPOINT` | URL of the printer service | `http://printer:3000` |
| Variable | Description | Example |
| ------------------ | ------------------------------ | ------------------------------------- |
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` |
| `AUTH_SECRET` | Secret key for authentication | Generate with `openssl rand -hex 32` |
| `APP_URL` | Public URL of your Application | `https://rxresu.me` |
| `PRINTER_ENDPOINT` | URL of the printer service | `ws://browserless:3000?token=...` |
### Optional Variables
| Variable | Description | Default |
| ------------------------------- | ----------------------------------------------------------- | ---------------------- |
| `PRINTER_APP_URL` | Public URL for the printer to access the Application | — |
| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — |
| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — |
| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — |
| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — |
| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — |
| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — |
| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — |
| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
| `SMTP_HOST` | SMTP Server Host (for email features) | — |
| `SMTP_PORT` | SMTP Server Port | `587` |
| `SMTP_USER` | SMTP Username | — |
| `SMTP_PASS` | SMTP Password | — |
| `SMTP_FROM` | Default FROM address for emails | — |
| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` |
| `S3_ACCESS_KEY_ID` | S3 Access Key | — |
| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — |
| `S3_REGION` | S3 Region | `us-east-1` |
| `S3_ENDPOINT` | S3-compatible Endpoint URL | — |
| `S3_BUCKET` | S3 Bucket Name | — |
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
| `FLAG_DEBUG_PRINTER` | Used for debugging the printer route | `false` |
| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` |
| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` |
| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` |
| Variable | Description | Default |
| ------------------------------------- | ----------------------------------------------------------- | ---------------------- |
| `PRINTER_APP_URL` | Public URL for the printer to access the Application | — |
| `GOOGLE_CLIENT_ID` | Google OAuth Client ID | — |
| `GOOGLE_CLIENT_SECRET` | Google OAuth Client Secret | — |
| `GITHUB_CLIENT_ID` | GitHub OAuth Client ID | — |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth Client Secret | — |
| `LINKEDIN_CLIENT_ID` | LinkedIn OAuth Client ID | — |
| `LINKEDIN_CLIENT_SECRET` | LinkedIn OAuth Client Secret | — |
| `OAUTH_PROVIDER_NAME` | Custom OAuth Provider Name | — |
| `OAUTH_CLIENT_ID` | Custom OAuth Client ID | — |
| `OAUTH_CLIENT_SECRET` | Custom OAuth Client Secret | — |
| `OAUTH_DISCOVERY_URL` | OIDC Discovery URL (use this OR manual URLs below) | — |
| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
| `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` | Trusted HTTPS hosts/origins for dynamic OAuth redirects | — |
| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
| `BETTER_AUTH_API_KEY` | Better Auth dashboard API key | — |
| `BETTER_AUTH_URL` | Better Auth base URL override (advanced) | `APP_URL` |
| `BETTER_AUTH_SECRET` | Better Auth secret override (advanced) | `AUTH_SECRET` |
| `AI_ALLOWED_BASE_URLS` | Allowlist for custom AI provider base URLs | — |
| `SMTP_HOST` | SMTP Server Host (for email features) | — |
| `SMTP_PORT` | SMTP Server Port | `587` |
| `SMTP_USER` | SMTP Username | — |
| `SMTP_PASS` | SMTP Password | — |
| `SMTP_FROM` | Default FROM address for emails | — |
| `SMTP_SECURE` | Use secure SMTP connection (`true` or `false`) | `false` |
| `S3_ACCESS_KEY_ID` | S3 Access Key | — |
| `S3_SECRET_ACCESS_KEY` | S3 Secret Key | — |
| `S3_REGION` | S3 Region | `us-east-1` |
| `S3_ENDPOINT` | S3-compatible Endpoint URL | — |
| `S3_BUCKET` | S3 Bucket Name | — |
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
| `FLAG_DEBUG_PRINTER` | Used for debugging the printer route | `false` |
| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` |
| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` |
| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` |
> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused.
> **Health check behavior:** `/api/health` reports status for database, printer, and storage. A failure in any dependency returns HTTP `503`.
<Note>
**Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while
the printer service is running inside Docker. In this scenario, the printer needs to reach your local app to render
+4
View File
@@ -132,6 +132,10 @@ Reactive Resume includes a variety of professionally designed templates. Each te
<img src="/images/templates/leafish.webp" alt="Leafish template preview" style={{ aspectRatio: "210/297" }} />
</Frame>
<Frame caption="Meowth">
<img src="/images/templates/meowth.webp" alt="Meowth template preview" style={{ aspectRatio: "210/297" }} />
</Frame>
<Frame caption="Onyx">
<img src="/images/templates/onyx.webp" alt="Onyx template preview" style={{ aspectRatio: "210/297" }} />
</Frame>
+1
View File
@@ -937,6 +937,7 @@ The exported JSON follows the Reactive Resume schema. Here's a high-level overvi
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
File diff suppressed because it is too large Load Diff
+6 -12
View File
@@ -3,15 +3,6 @@ title: "Setting up passkeys"
description: "Learn how to register passkeys (WebAuthn) to sign in securely using biometrics or your device PIN"
---
<Warning>
<strong>Passkeys Temporarily Disabled:</strong> Passkey registration and sign-in are currently unavailable in Reactive Resume due to an upstream issue with our authentication provider. We are closely monitoring the situation and will re-enable passkeys once it is resolved.
For more details and technical updates, see the <a href="https://github.com/better-auth/better-auth/issues/7463" target="_blank" rel="noopener">GitHub issue #7463</a>.
</Warning>
---
<Steps>
<Step title="Sign in to the dashboard">
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
@@ -27,8 +18,7 @@ For more details and technical updates, see the <a href="https://github.com/bett
</Step>
<Step title="Name your passkey">
You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for
example, “MacBook Touch ID” or “iPhone Face ID”).
Enter a descriptive name so you can recognize it later (for example, "MacBook Touch ID" or "iPhone Face ID").
</Step>
<Step title="Complete the passkey prompt">
@@ -41,11 +31,15 @@ For more details and technical updates, see the <a href="https://github.com/bett
</Step>
<Step title="Manage passkeys (optional)">
After registering, your passkey will show up in the list. You can delete a passkey using the trash icon next to it.
After registering, your passkey will show up in the list. You can rename it or delete it from the same section.
<Warning>
Deleting a passkey cannot be undone. After deletion, you won't be able to sign in using that passkey anymore.
</Warning>
</Step>
<Step title="Sign in with a passkey">
On the login page, click <Badge>Sign in with Passkey</Badge> to authenticate without entering your password.
</Step>
</Steps>
+50 -22
View File
@@ -207,32 +207,56 @@ If you're running a self-hosted Reactive Resume instance, replace `https://rxres
## Available Tools
The MCP server exposes the following tools:
Tool names use a `reactive_resume_` prefix so they stay distinct when multiple MCP servers are enabled in the same client.
| Tool | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
| `get_resume` | Get the full data of a specific resume by ID |
| `create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
| `duplicate_resume` | Create a copy of an existing resume with a new name and slug |
| `patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
| `delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
| `lock_resume` | Lock a resume to prevent edits, patches, and deletion |
| `unlock_resume` | Unlock a previously locked resume to re-enable editing |
| `export_resume_pdf` | Generate a PDF from the resume and return a download URL |
| `get_resume_screenshot` | Get a visual preview of the resume's first page as a WebP image URL |
| `get_resume_statistics` | Get view and download statistics for a resume |
| Tool | Description |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `reactive_resume_list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
| `reactive_resume_list_resume_tags` | List every distinct tag in use across your resumes (sorted) |
| `reactive_resume_get_resume` | Get the full data of a specific resume by ID |
| `reactive_resume_get_resume_analysis` | Get the latest saved AI analysis for a resume (from the web app), if any |
| `reactive_resume_create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
| `reactive_resume_import_resume` | Create a resume from a full ResumeData JSON export (random name/slug). Large files may exceed client limits |
| `reactive_resume_duplicate_resume` | Create a copy of an existing resume with a new name and slug |
| `reactive_resume_patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
| `reactive_resume_update_resume` | Update metadata only: name, slug, tags, `isPublic`. Returns canonical share URL; passwords are not managed via MCP |
| `reactive_resume_delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
| `reactive_resume_lock_resume` | Lock a resume to prevent edits, patches, and deletion |
| `reactive_resume_unlock_resume` | Unlock a previously locked resume to re-enable editing |
| `reactive_resume_export_resume_pdf` | Generate a PDF from the resume and return a download URL |
| `reactive_resume_get_resume_screenshot` | Get a visual preview of the resume's first page as a WebP image URL |
| `reactive_resume_get_resume_statistics` | Get view and download statistics for a resume |
### Breaking change (tool names)
Older clients may refer to unprefixed names (`list_resumes`, `get_resume`, …) or dot-separated names (`reactive_resume.list_resumes`, …). Those names are no longer used; update automations and saved prompts to the `reactive_resume_*` names above.
## Available Resources
| Resource | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
| `resume://{id}` | The full resume data as a readable JSON resource. Lists all resumes and supports reading individual ones by ID |
| `resume://schema` | The ResumeData JSON Schema — reference this to understand valid paths and value types for JSON Patch operations |
Resources follow MCP conventions: **static** items appear in `resources/list`; **parameterized** access is declared in `resources/templates/list` and read via `resources/read` once you know the ID.
| Discovery | What you get |
| ------------------------------------- | --------------------------------------------------------------------------------------------- |
| `resources/list` | Static resources only — currently **`resume://_meta/schema`** (ResumeData JSON Schema) |
| `resources/templates/list` | **`resume://{id}`** — template for reading full resume JSON by ID (not enumerated per resume) |
| `reactive_resume_list_resumes` (tool) | **Primary way to discover resume IDs** — resumes are not listed as separate MCP resources |
| URI | Description |
| ----------------------- | ------------------------------------------------------------------------ |
| `resume://_meta/schema` | ResumeData JSON Schema — use for valid JSON Patch paths and value types |
| `resume://{id}` | Full resume data as JSON — use an ID from `reactive_resume_list_resumes` |
### Breaking change (schema URI)
The schema resource was previously `resume://schema`. It is now **`resume://_meta/schema`**. Update any saved prompts, automations, or client configs that referenced the old URI.
### Static server card (`/.well-known/mcp/server-card.json`)
`GET /.well-known/mcp/server-card.json` returns a JSON document ([SEP-1649](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1649)) with `serverInfo`, optional authentication metadata, and summaries of tools, resources, resource templates, and prompts. It is generated to match the live MCP server and can be used for discovery when a client cannot run a full capability scan against `/mcp/`.
## Available Prompts
Prompts are pre-built workflows that provide the AI with structured instructions and context. Each prompt embeds the resume data and schema automatically.
Prompts are pre-built workflows that provide the AI with structured instructions and context. Each prompt embeds the resume data and the schema resource (`resume://_meta/schema`) automatically.
| Prompt | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -255,7 +279,10 @@ Once your MCP client is connected, you can use natural language to interact with
### Creating & Managing
- "Create a new resume called 'Frontend Engineer 2026'"
- "Import this exported ResumeData JSON as a new resume"
- "What tags do I use across my resumes?"
- "Duplicate my 'Software Engineer' resume for a product manager role"
- "Make my resume public and give me the share link"
- "Lock my finalized resume so it can't be accidentally edited"
- "Delete my old draft resume"
@@ -286,8 +313,9 @@ Once your MCP client is connected, you can use natural language to interact with
- "Tailor my resume for this job description: ..." (uses `tailor_resume`)
<Tip>
The AI will use `get_resume` to inspect your current resume before making changes with `patch_resume`. This ensures
the correct JSON paths are used.
The AI will use `reactive_resume_get_resume` to inspect your current resume before making changes with
`reactive_resume_patch_resume`. This ensures the correct JSON paths are used. Use `reactive_resume_update_resume` for
name, slug, tags, and public visibility (not for section content).
</Tip>
## Troubleshooting
@@ -297,7 +325,7 @@ Once your MCP client is connected, you can use natural language to interact with
| "Unauthorized" with no login prompt | Your client may not support MCP OAuth discovery. Use API key mode (`x-api-key`) |
| OAuth login opens but fails redirect/callback | Confirm your client's MCP OAuth callback settings and retry the connection |
| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → API Keys** |
| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs |
| "API error (404)" | The resume ID doesn't exist. Use `reactive_resume_list_resumes` to find valid IDs |
| "API error (403)" | The resume is locked. Unlock it in the Reactive Resume dashboard |
| Connection refused | Check that the URL is correct and the instance is running |
| "ReferenceError: File is not defined" when using `mcp-remote` | You're running Node.js 18. `mcp-remote` requires **Node.js 20 or later** — upgrade with `nvm use 20` or `nvm alias default 20` |
Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

+96 -44
View File
@@ -54,9 +54,9 @@ APP_URL="http://localhost:3000"
PRINTER_APP_URL="http://host.docker.internal:3000"
# --- Printer ---
# If using browserless with token authentication, include the token as a query parameter:
# PRINTER_ENDPOINT="ws://printer:3000?token=your-secret-token"
PRINTER_ENDPOINT="ws://printer:3000"
# Keep this token in sync with the Browserless TOKEN value.
BROWSERLESS_TOKEN="change-me"
PRINTER_ENDPOINT="ws://printer:3000?token=change-me"
# --- Database (PostgreSQL) ---
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
@@ -64,6 +64,8 @@ DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
# --- Authentication ---
# Generated using `openssl rand -hex 32`
AUTH_SECRET=""
# Better Auth dashboard API key (optional)
BETTER_AUTH_API_KEY=""
# Social Auth (Google, optional)
GOOGLE_CLIENT_ID=""
@@ -73,6 +75,10 @@ GOOGLE_CLIENT_SECRET=""
GITHUB_CLIENT_ID=""
GITHUB_CLIENT_SECRET=""
# Social Auth (LinkedIn, optional)
LINKEDIN_CLIENT_ID=""
LINKEDIN_CLIENT_SECRET=""
# Custom OAuth Provider
OAUTH_PROVIDER_NAME=""
OAUTH_CLIENT_ID=""
@@ -83,20 +89,30 @@ OAUTH_DISCOVERY_URL=""
OAUTH_AUTHORIZATION_URL=""
OAUTH_TOKEN_URL=""
OAUTH_USER_INFO_URL=""
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=""
# Custom scopes (space-separated, defaults to "openid profile email")
OAUTH_SCOPES=""
# Optional Better Auth runtime overrides for advanced deployments:
# BETTER_AUTH_URL="https://auth.example.com"
# BETTER_AUTH_SECRET=""
# --- AI (optional) ---
# Comma-separated hostnames/origins for custom AI base URLs
# Example: api.openai.com,https://gateway.ai.vercel.com
AI_ALLOWED_BASE_URLS=""
# --- Email (optional) ---
# If all keys are disabled, the app logs the email to be sent to the console instead.
SMTP_HOST=""
SMTP_PORT="465"
SMTP_PORT="587"
SMTP_USER=""
SMTP_PASS=""
SMTP_FROM="Reactive Resume <noreply@rxresu.me>"
SMTP_SECURE="false"
# --- Storage (optional) ---
# If all keys are disabled, the app uses local filesystem (/data) to store uploads instead.
# If all keys are disabled, the app uses local filesystem (usually /app/data) to store uploads instead.
# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity.
S3_ACCESS_KEY_ID=""
S3_SECRET_ACCESS_KEY=""
@@ -111,6 +127,7 @@ S3_FORCE_PATH_STYLE="false"
FLAG_DEBUG_PRINTER="false"
FLAG_DISABLE_SIGNUPS="false"
FLAG_DISABLE_EMAIL_AUTH="false"
FLAG_DISABLE_IMAGE_PROCESSING="false"
```
</Step>
@@ -130,9 +147,6 @@ FLAG_DISABLE_EMAIL_AUTH="false"
[byte[]]$bytes = New-Object byte[] 32; (New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($bytes); $bytes | ForEach-Object { "{0:x2}" -f $_ } | Out-String -Stream | ForEach-Object { $_.Trim() } | Write-Host -NoNewline
```
```cmd Windows (alternative)
certutil -generateSRS 32 | findstr /r /v "^$" | findstr /v ":" | findstr /v " " | findstr /v "-" | findstr /v "certutil"
```
</CodeGroup>
</Step>
@@ -168,10 +182,9 @@ services:
- HEALTH=true
- CONCURRENT=20
- QUEUED=10
# Optional: Set a token for authentication
# - TOKEN=your-secret-token
- TOKEN=${BROWSERLESS_TOKEN}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN}" > /dev/null']
interval: 10s
timeout: 5s
retries: 10
@@ -278,11 +291,15 @@ docker compose logs -f reactive-resume
SMTP (<code>SMTP_&#42;</code>)
</li>
<li>
Social auth (<code>GOOGLE_&#42;</code>, <code>GITHUB_&#42;</code>)
Social auth (<code>GOOGLE_&#42;</code>, <code>GITHUB_&#42;</code>, <code>LINKEDIN_&#42;</code>,{" "}
<code>OAUTH_&#42;</code>)
</li>
<li>
S3 storage (<code>S3_&#42;</code>)
</li>
<li>
AI URL allowlist (<code>AI_ALLOWED_BASE_URLS</code>)
</li>
<li>
Feature flags (<code>FLAG_&#42;</code>)
</li>
@@ -298,7 +315,9 @@ docker compose logs -f reactive-resume
</Accordion>
<Accordion title="Printer">
- **`PRINTER_ENDPOINT`**: Base URL where Reactive Resume reaches the printer service. In Compose: `http://printer:3000`. If using browserless with token authentication, include the token as a query parameter: `ws://printer:3000?token=your-secret-token`.
- **`PRINTER_ENDPOINT`**: Endpoint where Reactive Resume connects to the printer browser.
- **Recommended (Browserless)**: `ws://printer:3000?token=...` and keep the token value in sync with Browserless `TOKEN`.
- **Also supported**: `http://chrome:9222` for Chrome DevTools Protocol endpoints.
<Note>
**Alternative to browserless**: You can use a lightweight headless Chrome Docker image like `chromedp/headless-shell`:
@@ -318,7 +337,9 @@ docker compose logs -f reactive-resume
<Accordion title="Database (PostgreSQL)">
- **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`. - In
Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`.
Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`. - If your password
contains special characters (`@`, `#`, `:`), URL-encode it. - For managed Postgres, add provider-specific params (for
example `?sslmode=require`) when needed.
</Accordion>
<Accordion title="Authentication">
@@ -338,9 +359,18 @@ openssl rand -hex 32
**`GITHUB_CLIENT_ID`** / **`GITHUB_CLIENT_SECRET`** (optional): Enables GitHub sign-in.
**`LINKEDIN_CLIENT_ID`** / **`LINKEDIN_CLIENT_SECRET`** (optional): Enables LinkedIn sign-in.
**`BETTER_AUTH_API_KEY`** (optional): Enables Better Auth dashboard integrations.
**`BETTER_AUTH_URL`** (optional, advanced): Overrides auth base URL if it must differ from `APP_URL` (for split-host deployments).
**`BETTER_AUTH_SECRET`** (optional, advanced): Overrides `AUTH_SECRET` for Better Auth internals.
**Custom OAuth provider** (optional):
- **`OAUTH_PROVIDER_NAME`**: Display name in the UI
- **`OAUTH_CLIENT_ID`** / **`OAUTH_CLIENT_SECRET`**: Required for any custom OAuth provider
- **`OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`**: Comma-separated allowlist for extra dynamic OAuth redirect hosts/origins (HTTPS only, non-private hosts).
- **`OAUTH_SCOPES`**: Space-separated scopes (defaults to `openid profile email`)
Configure endpoints using **one** of these methods:
@@ -352,8 +382,9 @@ openssl rand -hex 32
<Accordion title="Email (SMTP, optional)">
If SMTP is not configured, the app logs emails to the server console instead of sending them.
- Email delivery is enabled only when **all** of `SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM` are set.
- **`SMTP_HOST`**: SMTP host (if empty, email sending is disabled).
- **`SMTP_PORT`**: Usually `465` (implicit TLS) or `587` (STARTTLS).
- **`SMTP_PORT`**: Defaults to `587` in the app.
- **`SMTP_USER`** / **`SMTP_PASS`**: SMTP credentials.
- **`SMTP_FROM`**: Default from address (for example, `Reactive Resume <noreply@rxresu.me>`).
- **`SMTP_SECURE`**: `"true"` or `"false"` (string). Match your provider settings.
@@ -361,21 +392,23 @@ openssl rand -hex 32
</Accordion>
<Accordion title="Storage (S3 or local)">
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent
storage (for example `./data:/app/data`) or uploads can be lost on container recreation. - **S3/S3-compatible**:
Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.): -
**`S3_ACCESS_KEY_ID`** - **`S3_SECRET_ACCESS_KEY`** - **`S3_REGION`** - **`S3_ENDPOINT`** (for S3-compatible
providers; may be blank for AWS depending on your setup) - **`S3_BUCKET`** - **`S3_FORCE_PATH_STYLE`**: Controls how
the bucket is addressed in URLs. Defaults to `"false"`. - Set to `"true"` for **path-style** URLs
(`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services.
- Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**,
**Cloudflare R2**, and most cloud providers.
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `<cwd>/data` (usually `/app/data` in the official image).
- Mount local uploads to persistent storage (for example `./data:/app/data`) or uploads can be lost on container recreation.
- **S3/S3-compatible**: Configure `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_REGION`, `S3_ENDPOINT`, and `S3_BUCKET`.
- **`S3_FORCE_PATH_STYLE`** controls bucket addressing (defaults to `"false"`):
- `"true"` for path-style URLs (`https://endpoint/bucket`) common with MinIO/SeaweedFS.
- `"false"` for virtual-hosted-style URLs (`https://bucket.endpoint`) common with AWS S3 / Cloudflare R2.
</Accordion>
<Accordion title="AI (optional)">
- **`AI_ALLOWED_BASE_URLS`**: Comma-separated hosts or origins allowed as custom AI API base URLs. - Use this when
routing AI requests through your own gateway/proxy. - Example: `api.openai.com,https://gateway.ai.vercel.com`
</Accordion>
<Accordion title="Feature Flags">
- **`FLAG_DEBUG_PRINTER`**: Bypasses the printer-only access restriction (useful when debugging `/printer/{resumeId}`). Recommended: keep `"false"` in production.
- **`FLAG_DISABLE_SIGNUPS`**: Disables new signups (web app and server). Useful for private instances.
- **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required.
- **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/LinkedIn/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required.
- **`FLAG_DISABLE_IMAGE_PROCESSING`**: Disables image processing. This is useful if you are using a machine with limited resources, like a Raspberry Pi.
</Accordion>
</AccordionGroup>
@@ -384,24 +417,32 @@ openssl rand -hex 32
To update your Reactive Resume installation to the latest available version, follow these steps:
1. **Pull the latest images** for all services defined in your Docker Compose file.
1. **Back up your database and uploads first** (highly recommended before every update).
2. **Pull the latest images** for all services defined in your Docker Compose file.
```bash
docker compose pull
```
2. **Restart the containers** to run the new images.
3. **Restart the containers** to run the new images.
```bash
docker compose up -d
```
3. **(Optional) Remove old, unused Docker images** to free up disk space.
4. **Check migration/startup logs** after deploy.
```bash
docker compose logs -f reactive-resume
```
5. **(Optional) Remove old, unused Docker images** to free up disk space.
```bash
docker image prune -f
```
This process ensures your app, database, and printer are all up-to-date while keeping your data and configuration intact.
This process updates app services and automatically runs DB migrations on startup. If migration fails, restore from backup and fix configuration before retrying.
## Backups (recommended)
@@ -419,7 +460,7 @@ If you're using S3-compatible storage, consider enabling versioning on your buck
## Health Checks
Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies are functioning correctly. If any critical service (such as the database connection) fails, the health check will return an unhealthy status.
Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies. It checks **database**, **printer**, and **storage**; if any one is unhealthy, the endpoint returns HTTP `503`.
### How it works
@@ -442,7 +483,7 @@ Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream
- **Healthy containers** receive traffic as normal
- **Unhealthy containers** are automatically removed from the load balancer pool
This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses its database connection), the reverse proxy will stop routing traffic to it until it recovers.
This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses database, printer, or storage connectivity), the reverse proxy will stop routing traffic to it until it recovers.
<Tip>
If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy
@@ -461,40 +502,51 @@ curl -f http://localhost:3000/api/health
docker compose ps
```
A healthy response returns HTTP 200. Any other response (or a connection failure) indicates a problem that should be investigated in the container logs.
A healthy response returns HTTP 200. Any other response (or a connection failure) indicates a problem that should be investigated in the JSON response body and container logs.
## Troubleshooting
<AccordionGroup>
<Accordion title="The app container exits immediately">
- **Common cause**: database migrations failed (often a bad `DATABASE_URL`).
- **What to do**:
When the app container exits right away, you'll want to check the logs for more information about the error. Run the following command to view real-time logs from the Reactive Resume container:
- **What to do**:
Check logs for migration errors and database connectivity details:
```bash
docker compose logs -f reactive-resume
```
</Accordion>
<Accordion title="Can't sign in / redirects loop / cookies don't stick">
- **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy). -
**Fix**: set `APP_URL` to the public URL (preferably HTTPS) and restart the container.
- **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy), or
you're serving HTTPS while `APP_URL` is `http://...`. - **Fix**: set `APP_URL` to your canonical public HTTPS URL and
restart the container.
</Accordion>
<Accordion title="PDF export fails / printing is stuck">
- **Common cause**: Reactive Resume can't reach the printer or the printer can't reach your app. - **Checks**: -
`PRINTER_ENDPOINT` should usually be `http://printer:3000` in Compose. - If you use
`PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for the printer
service.
- **Common cause**: Reactive Resume can't reach the printer, token mismatch, or the printer can't reach your app. -
**Checks**: - `PRINTER_ENDPOINT` should usually be `ws://printer:3000?token=...` in Compose. - Browserless `TOKEN` and
the token in `PRINTER_ENDPOINT` must match. - If you use `PRINTER_APP_URL="http://host.docker.internal:3000"` on
Linux, set `extra_hosts: ["host.docker.internal:host-gateway"]` for the printer service.
</Accordion>
<Accordion title="/api/health returns 503 even though Postgres is up">
- **Common cause**: printer or storage health failed (not only database). - **Fix**: inspect the endpoint response
payload and check `printer` / `storage` fields: ```bash curl -s http://localhost:3000/api/health ```
</Accordion>
<Accordion title="Uploads disappear after restart">
- **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3). - **Fix**: add a volume mount
like `./data:/app/data` and redeploy.
- **Cause**: local upload storage wasn't mounted to a persistent volume. - **Fix**: add a volume mount like
`./data:/app/data` and redeploy.
</Accordion>
<Accordion title="Emails aren't being delivered">
- **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead. - **Fix**: configure SMTP
and verify your provider's TLS/port settings.
- **Expected behavior**: if SMTP isn't fully configured, the app logs emails to the console. - **Fix**: set
`SMTP_HOST`, `SMTP_USER`, `SMTP_PASS`, and `SMTP_FROM`, then verify `SMTP_PORT` and `SMTP_SECURE`.
</Accordion>
<Accordion title="Dynamic OAuth redirect URI is rejected">
- **Common cause**: redirect host is not trusted for dynamic client registration. - **Fix**: add trusted HTTPS
hosts/origins to `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`.
</Accordion>
<Accordion title="S3 storage error: ENOTFOUND bucket.endpoint">
+17 -12
View File
@@ -85,12 +85,11 @@ services:
- QUEUED=10
- HEALTH=true
- CONCURRENT=5
# Optional: Set a token for authentication
# - TOKEN=your-secret-token
- TOKEN=${BROWSERLESS_TOKEN}
networks:
- reactive_resume_network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN}" > /dev/null']
interval: 30s
timeout: 10s
retries: 3
@@ -102,7 +101,7 @@ services:
- APP_URL=https://resume.${DOMAIN}
- PRINTER_APP_URL=http://reactive_resume:3000
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
- PRINTER_ENDPOINT=http://printer:3000
- PRINTER_ENDPOINT=ws://printer:3000?token=${BROWSERLESS_TOKEN}
- AUTH_SECRET=${AUTH_SECRET}
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
volumes:
@@ -143,6 +142,7 @@ DOMAIN="example.com"
ACME_EMAIL="admin@example.com"
POSTGRES_PASSWORD="your-secure-postgres-password"
AUTH_SECRET="your-auth-secret-from-openssl-rand-hex-32"
BROWSERLESS_TOKEN="your-browserless-token"
# Optional: Traefik dashboard auth (generate with: htpasswd -nb admin password)
TRAEFIK_DASHBOARD_AUTH="admin:$$apr1$$..."
```
@@ -191,10 +191,14 @@ services:
- QUEUED=10
- HEALTH=true
- CONCURRENT=5
# Optional: Set a token for authentication
# - TOKEN=your-secret-token
- TOKEN=${BROWSERLESS_TOKEN}
networks:
- reactive_resume_network
healthcheck:
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=${BROWSERLESS_TOKEN}" > /dev/null']
interval: 30s
timeout: 10s
retries: 3
reactive_resume:
image: amruthpillai/reactive-resume:latest
@@ -203,7 +207,7 @@ services:
- APP_URL=https://resume.${DOMAIN}
- PRINTER_APP_URL=http://reactive_resume:3000
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/postgres
- PRINTER_ENDPOINT=http://printer:3000
- PRINTER_ENDPOINT=ws://printer:3000?token=${BROWSERLESS_TOKEN}
- AUTH_SECRET=${AUTH_SECRET}
# Add other optional env vars as needed (SMTP, S3, OAuth, etc.)
volumes:
@@ -339,10 +343,9 @@ services:
- QUEUED=10
- HEALTH=true
- CONCURRENT=5
# Optional: Set a token for authentication
# - TOKEN=your-secret-token
- TOKEN=$BROWSERLESS_TOKEN
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/pressure?token=your-secret-token"]
test: ["CMD-SHELL", 'curl -fsS "http://localhost:3000/pressure?token=$BROWSERLESS_TOKEN" > /dev/null']
interval: 30s
timeout: 10s
retries: 3
@@ -405,6 +408,8 @@ services:
- GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET
- GITHUB_CLIENT_ID=$GITHUB_CLIENT_ID
- GITHUB_CLIENT_SECRET=$GITHUB_CLIENT_SECRET
- LINKEDIN_CLIENT_ID=$LINKEDIN_CLIENT_ID
- LINKEDIN_CLIENT_SECRET=$LINKEDIN_CLIENT_SECRET
- SMTP_HOST=$SMTP_HOST
- SMTP_PORT=$SMTP_PORT
- SMTP_USER=$SMTP_USER
@@ -467,10 +472,10 @@ docker stack deploy -c compose-swarm.yml reactive_resume
docker stack services reactive_resume
# View logs for the app
docker service logs -f reactive_resume_app
docker service logs -f reactive_resume_reactive_resume
# Scale the app
docker service scale reactive_resume_app=3
docker service scale reactive_resume_reactive_resume=3
# Remove the stack
docker stack rm reactive_resume
+8 -1
View File
@@ -7,6 +7,11 @@ description: "A step-by-step guide to migrate your Reactive Resume instance from
This guide walks you through migrating your Reactive Resume installation from **v4 to v5**. The migration process involves setting up a new v5 instance alongside your existing v4 instance, then transferring your users and resumes to the new system.
<Info>
This page is for **v4 → v5 data migration** only. For normal v5 upgrades, use the [Self-Hosting with
Docker](/self-hosting/docker) guide. v5 schema migrations run automatically on app startup.
</Info>
<Warning>
**Keep your v4 instance running** until you have successfully migrated all data to v5 and verified everything works
correctly. This ensures you have a fallback in case anything goes wrong during the migration.
@@ -86,7 +91,7 @@ To run the migration scripts, you need the following installed on your host mach
</Card>
<Card title="Reactive Resume Source Code">
Clone the Reactive Resume repository to access the migration scripts: ```bash git clone
https://github.com/amruthpillai/reactive-resume.git cd reactive-resume pnpm install ```
https://github.com/amruthpillai/reactive-resume.git cd reactive-resume vp install ```
</Card>
</CardGroup>
@@ -107,6 +112,8 @@ PRODUCTION_DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resu
`PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss.
</Warning>
<Warning>`PRODUCTION_DATABASE_URL` is used only by these migration scripts. It is not a runtime app variable.</Warning>
### Step 1: Migrate Users
The user migration script transfers all user accounts, authentication data, and two-factor settings from v4 to v5.
+34 -4
View File
@@ -62,10 +62,13 @@ You must configure endpoints using **one** of these two methods:
### Optional Variables
| Variable | Description | Default |
| --------------------- | ---------------------------------------- | ---------------------- |
| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
| Variable | Description | Default |
| ------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- |
| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
| `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` | Comma-separated allowlist for dynamic OAuth client redirect hosts/origins (HTTPS only) | _empty_ |
| `BETTER_AUTH_URL` | Optional auth base URL override for split-host setups | `APP_URL` |
| `BETTER_AUTH_SECRET` | Optional Better Auth secret override | `AUTH_SECRET` |
## Callback URL
@@ -86,6 +89,18 @@ https://resume.example.com/api/auth/oauth2/callback/custom
authentication to fail.
</Warning>
<Info>
Built-in providers (Google, GitHub, LinkedIn) use callback URLs in this format: `{APP_URL}/api/auth/callback/
{provider}` (for example `.../google`, `.../github`, `.../linkedin`).
</Info>
## URL and Proxy Requirements
- Set `APP_URL` to the exact public URL users access (prefer HTTPS in production).
- If auth metadata/JWKS must be served from a different public host, set `BETTER_AUTH_URL`.
- Behind a reverse proxy, forward `Host` and `X-Forwarded-Proto` correctly, or cookie/session behavior may break.
- `trustedOrigins` are derived from `APP_URL`, so alternate domains are not automatically trusted.
## Profile Mapping
Reactive Resume automatically maps user profile data from the OAuth provider. The following fields are used:
@@ -253,6 +268,8 @@ AUTH_SECRET="your-32-byte-hex-secret"
# GOOGLE_CLIENT_SECRET=""
# GITHUB_CLIENT_ID=""
# GITHUB_CLIENT_SECRET=""
# LINKEDIN_CLIENT_ID=""
# LINKEDIN_CLIENT_SECRET=""
# Custom OAuth Provider (e.g., Authentik)
OAUTH_PROVIDER_NAME="Company SSO"
@@ -278,6 +295,11 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
number differences - Path case sensitivity
</Accordion>
<Accordion title="Session/cookie issues after successful OAuth login">
Common cause: `APP_URL` does not match the real HTTPS public origin (for example, app is behind TLS but `APP_URL` is
`http://...`). Fix: set `APP_URL` to the canonical HTTPS URL and restart the app.
</Accordion>
<Accordion title="Custom OAuth button not appearing">
The custom OAuth option only appears if both `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are set, **and** either:
- `OAUTH_DISCOVERY_URL` is set, **or**
@@ -293,6 +315,11 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
domain
</Accordion>
<Accordion title="Dynamic client redirect URI is rejected">
Dynamic OAuth client registration only allows HTTPS redirect URIs on trusted hosts. Add allowed hosts/origins to
`OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` (comma-separated).
</Accordion>
<Accordion title="User profile data is missing or incorrect">
The profile mapping depends on your provider returning standard claims:
- `email` (required)
@@ -317,6 +344,9 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
<Card title="Verify redirect URIs" icon="shield-check">
Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
</Card>
<Card title="Protect auth internals" icon="shield">
Keep `BETTER_AUTH_SECRET` and `BETTER_AUTH_API_KEY` private. Rotating these values may invalidate active sessions.
</Card>
<Card title="Review scopes" icon="list-check">
Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
</Card>
+7
View File
@@ -1686,6 +1686,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
@@ -5050,6 +5051,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
@@ -7222,6 +7224,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
@@ -9428,6 +9431,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
@@ -11741,6 +11745,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
@@ -14020,6 +14025,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
@@ -16189,6 +16195,7 @@
"kakuna",
"lapras",
"leafish",
"meowth",
"onyx",
"pikachu",
"rhyhorn"
+2 -2
View File
@@ -1,8 +1,8 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/server.ts", "src/router.tsx"],
"entry": ["src/server.ts", "src/router.tsx", "lingui.config.ts"],
"tailwind": { "entry": ["src/styles.css"] },
"project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"],
"project": ["src/**/*.{js,jsx,ts,tsx,css}"],
"ignoreBinaries": ["mint"],
"ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"],
"ignore": ["src/schema/**/*.ts", "src/components/ui/**/*.{ts,tsx}"]
+3 -2
View File
@@ -1,4 +1,5 @@
import { defineConfig } from "@lingui/cli";
import { formatter } from "@lingui/format-po";
export default defineConfig({
sourceLocale: "en-US",
@@ -64,9 +65,9 @@ export default defineConfig({
"zu-ZA": "en-US",
default: "en-US",
},
formatOptions: {
format: formatter({
lineNumbers: false,
},
}),
catalogs: [
{
path: "<rootDir>/locales/{locale}",
+825 -117
View File
File diff suppressed because it is too large Load Diff
+905 -197
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+825 -117
View File
File diff suppressed because it is too large Load Diff
+820 -113
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+824 -116
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+828 -120
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+905 -197
View File
File diff suppressed because it is too large Load Diff
+905 -197
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+828 -120
View File
File diff suppressed because it is too large Load Diff
+905 -197
View File
File diff suppressed because it is too large Load Diff
+905 -197
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+830 -122
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+830 -122
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+825 -117
View File
File diff suppressed because it is too large Load Diff
+825 -117
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+825 -117
View File
File diff suppressed because it is too large Load Diff
+825 -117
View File
File diff suppressed because it is too large Load Diff
+825 -117
View File
File diff suppressed because it is too large Load Diff
+827 -119
View File
File diff suppressed because it is too large Load Diff
+826 -118
View File
File diff suppressed because it is too large Load Diff
+814 -107
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
ALTER TABLE "session" ADD COLUMN "impersonated_by" text;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "role" text DEFAULT 'user';--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "banned" boolean DEFAULT false;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "ban_reason" text;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp(6) with time zone;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
CREATE TABLE "resume_analysis" (
"id" uuid PRIMARY KEY,
"analysis" jsonb NOT NULL,
"resume_id" uuid NOT NULL UNIQUE,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE INDEX "resume_analysis_resume_id_index" ON "resume_analysis" ("resume_id");--> statement-breakpoint
ALTER TABLE "resume_analysis" ADD CONSTRAINT "resume_analysis_resume_id_resume_id_fkey" FOREIGN KEY ("resume_id") REFERENCES "resume"("id") ON DELETE CASCADE;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
CREATE UNIQUE INDEX "user_email_lower_unique_idx" ON "user" (lower("email"));
File diff suppressed because it is too large Load Diff
+98 -88
View File
@@ -1,6 +1,6 @@
{
"name": "reactive-resume",
"version": "5.0.14",
"version": "5.0.20",
"description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
"license": "MIT",
"author": {
@@ -22,8 +22,9 @@
"db:studio": "drizzle-kit studio",
"dev": "vp dev",
"docs:dev": "cd docs && npx mint dev",
"fmt": "vp fmt --check",
"fmt:fix": "vp fmt",
"email:dev": "email dev --dir src/integrations/email/templates --port 3001",
"format": "vp fmt --check",
"format:fix": "vp fmt",
"knip": "knip",
"lingui:extract": "lingui extract --clean --overwrite",
"lint": "vp lint --type-aware",
@@ -31,92 +32,99 @@
"preview": "vp preview",
"start": "node .output/server/index.mjs",
"test": "vp test",
"test:coverage": "vp test --coverage --run",
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@ai-sdk/anthropic": "^3.0.64",
"@ai-sdk/google": "^3.0.53",
"@ai-sdk/openai": "^3.0.48",
"@aws-sdk/client-s3": "^3.1015.0",
"@base-ui/react": "^1.3.0",
"@better-auth/api-key": "^1.5.6",
"@better-auth/drizzle-adapter": "^1.5.6",
"@better-auth/infra": "^0.1.13",
"@better-auth/oauth-provider": "^1.5.6",
"@ai-sdk/anthropic": "^3.0.71",
"@ai-sdk/google": "^3.0.64",
"@ai-sdk/openai": "^3.0.53",
"@ai-sdk/openai-compatible": "^2.0.41",
"@aws-sdk/client-s3": "^3.1037.0",
"@base-ui/react": "^1.4.1",
"@better-auth/api-key": "^1.6.9",
"@better-auth/drizzle-adapter": "^1.6.9",
"@better-auth/infra": "^0.2.5",
"@better-auth/oauth-provider": "^1.6.9",
"@better-auth/passkey": "^1.6.9",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
"@hookform/resolvers": "^5.2.2",
"@lingui/core": "^5.9.3",
"@lingui/react": "^5.9.3",
"@modelcontextprotocol/sdk": "^1.27.1",
"@lingui/core": "^6.0.0",
"@lingui/react": "^6.0.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"@monaco-editor/react": "4.8.0-rc.3",
"@orpc/client": "^1.13.9",
"@orpc/json-schema": "^1.13.9",
"@orpc/openapi": "^1.13.9",
"@orpc/server": "^1.13.9",
"@orpc/tanstack-query": "^1.13.9",
"@orpc/zod": "^1.13.9",
"@orpc/client": "^1.14.0",
"@orpc/experimental-ratelimit": "^1.14.0",
"@orpc/json-schema": "^1.14.0",
"@orpc/openapi": "^1.14.0",
"@orpc/server": "^1.14.0",
"@orpc/tanstack-query": "^1.14.0",
"@orpc/zod": "^1.14.0",
"@phosphor-icons/react": "^2.1.10",
"@phosphor-icons/web": "^2.1.2",
"@sindresorhus/slugify": "^3.0.0",
"@t3-oss/env-core": "^0.13.11",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-router": "^1.168.3",
"@tanstack/react-router-ssr-query": "^1.166.10",
"@tanstack/react-start": "^1.167.5",
"@tanstack/react-query": "^5.100.5",
"@tanstack/react-router": "^1.168.25",
"@tanstack/react-router-ssr-query": "^1.166.12",
"@tanstack/react-start": "^1.167.50",
"@tanstack/zod-adapter": "^1.166.9",
"@tiptap/extension-highlight": "^3.20.4",
"@tiptap/extension-table": "^3.20.4",
"@tiptap/extension-text-align": "^3.20.4",
"@tiptap/pm": "^3.20.4",
"@tiptap/react": "^3.20.4",
"@tiptap/starter-kit": "^3.20.4",
"@uiw/color-convert": "^2.9.6",
"@uiw/react-color-colorful": "^2.9.6",
"ai": "^6.0.137",
"ai-sdk-ollama": "^3.8.2",
"@tiptap/extension-color": "^3.22.4",
"@tiptap/extension-highlight": "^3.22.4",
"@tiptap/extension-table": "^3.22.4",
"@tiptap/extension-text-align": "^3.22.4",
"@tiptap/extension-text-style": "^3.22.4",
"@tiptap/pm": "^3.22.4",
"@tiptap/react": "^3.22.4",
"@tiptap/starter-kit": "^3.22.4",
"@uiw/color-convert": "^2.10.1",
"@uiw/react-color-colorful": "^2.10.1",
"ai": "^6.0.168",
"bcrypt": "^6.0.0",
"better-auth": "^1.5.6",
"better-auth": "^1.6.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"docx": "^9.6.1",
"dompurify": "^3.3.3",
"drizzle-orm": "1.0.0-beta.19",
"drizzle-zod": "^1.0.0-beta.14-a36c63d",
"es-toolkit": "^1.45.1",
"dompurify": "^3.4.1",
"drizzle-orm": "1.0.0-beta.22",
"drizzle-zod": "1.0.0-beta.14-a36c63d",
"es-toolkit": "^1.46.0",
"fast-deep-equal": "^3.1.3",
"fast-json-patch": "^3.1.1",
"fuse.js": "^7.1.0",
"fuse.js": "^7.3.0",
"immer": "^11.1.4",
"js-cookie": "^3.0.5",
"jsonrepair": "^3.13.3",
"jsonrepair": "^3.14.0",
"monaco-editor": "^0.55.1",
"motion": "^12.38.0",
"nodemailer": "^8.0.3",
"nodemailer": "^8.0.7",
"ollama-ai-provider-v2": "^3.5.0",
"pg": "^8.20.0",
"puppeteer-core": "^24.40.0",
"puppeteer-core": "^24.42.0",
"qrcode.react": "^4.2.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.72.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-email": "^6.0.0",
"react-hook-form": "^7.74.0",
"react-hotkeys-hook": "^5.2.4",
"react-resizable-panels": "^4.7.5",
"react-resizable-panels": "^4.10.0",
"react-window": "^2.2.7",
"react-zoom-pan-pinch": "^3.7.0",
"shadcn": "^4.1.0",
"react-zoom-pan-pinch": "^4.0.3",
"shadcn": "^4.5.0",
"sharp": "^0.34.5",
"sonner": "^2.0.7",
"srvx": "^0.11.13",
"srvx": "^0.11.15",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.2",
"tailwindcss": "^4.2.4",
"ts-pattern": "^5.9.0",
"tw-animate-css": "^1.4.0",
"unique-names-generator": "^4.7.1",
"usehooks-ts": "^3.1.1",
"uuid": "^13.0.0",
"uuid": "^14.0.0",
"workbox-window": "^7.4.0",
"zod": "^4.3.6",
"zundo": "^2.3.0",
@@ -124,56 +132,58 @@
},
"devDependencies": {
"@babel/core": "^7.29.0",
"@lingui/babel-plugin-lingui-macro": "^5.9.3",
"@lingui/cli": "^5.9.3",
"@lingui/vite-plugin": "^5.9.3",
"@rolldown/plugin-babel": "^0.2.2",
"@tailwindcss/vite": "^4.2.2",
"@lingui/babel-plugin-lingui-macro": "^6.0.0",
"@lingui/cli": "^6.0.0",
"@lingui/format-po": "^6.0.0",
"@lingui/vite-plugin": "^6.0.0",
"@react-email/ui": "^6.0.0",
"@rolldown/plugin-babel": "^0.2.3",
"@tailwindcss/vite": "^4.2.4",
"@testing-library/react": "^16.3.2",
"@types/bcrypt": "^6.0.0",
"@types/js-cookie": "^3.0.6",
"@types/node": "^25.5.0",
"@types/nodemailer": "^7.0.11",
"@types/node": "^25.6.0",
"@types/nodemailer": "^8.0.0",
"@types/pg": "^8.20.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript/native-preview": "7.0.0-dev.20260324.1",
"@typescript/native-preview": "7.0.0-dev.20260427.1",
"@vitejs/plugin-react": "^6.0.1",
"@vitest/coverage-v8": "^4.1.5",
"babel-plugin-macros": "^3.1.0",
"drizzle-kit": "1.0.0-beta.19",
"drizzle-kit": "1.0.0-beta.22",
"happy-dom": "^20.9.0",
"jose": "^6.2.2",
"jsdom": "^29.0.1",
"knip": "^6.0.4",
"nitro": "3.0.260311-beta",
"node-addon-api": "^8.6.0",
"node-gyp": "^12.2.0",
"npm-check-updates": "^19.6.5",
"tsx": "^4.21.0",
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"knip": "^6.7.0",
"nitro": "3.0.260415-beta",
"node-addon-api": "^8.7.0",
"node-gyp": "^12.3.0",
"npm-check-updates": "^22.0.1",
"vite": "npm:@voidzero-dev/vite-plus-core@^0.1.19",
"vite-plugin-pwa": "^1.2.0",
"vite-plus": "latest",
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
"vite-plus": "^0.1.19",
"vitest": "npm:@voidzero-dev/vite-plus-test@^0.1.19"
},
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
"packageManager": "pnpm@10.33.2",
"pnpm": {
"overrides": {
"@isaacs/brace-expansion": "^5.0.1",
"ajv": "^8.18.0",
"basic-ftp": "^5.2.0",
"fast-xml-parser": "^5.3.8",
"hono": "^4.11.10",
"lodash": "^4.17.23",
"markdown-it": "^14.1.1",
"minimatch": "^10.2.3",
"qs": "^6.14.2",
"serialize-javascript": "^7.0.4",
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
},
"onlyBuiltDependencies": [
"bcrypt",
"esbuild",
"msw",
"sharp"
]
],
"overrides": {
"dompurify@<=3.3.1": ">=3.3.2",
"dompurify@<=3.3.3": ">=3.4.0",
"dompurify@<3.3.2": ">=3.3.2",
"dompurify@<3.4.0": ">=3.4.0",
"dompurify@>=1.0.10 <3.4.0": ">=3.4.0",
"dompurify@>=3.0.1 <3.4.0": ">=3.4.0",
"dompurify@>=3.1.3 <=3.3.1": ">=3.3.2",
"postcss@<8.5.10": ">=8.5.10",
"serialize-javascript@<=7.0.2": ">=7.0.3",
"serialize-javascript@<7.0.5": ">=7.0.5",
"uuid@<14.0.0": ">=14.0.0"
}
}
}
+4808 -4723
View File
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
{
"name": "Reactive Resume",
"short_name": "Reactive Resume",
"description": "A free and open-source resume builder.",
"start_url": "/?source=pwa",
"display": "standalone",
"background_color": "#09090B",
"theme_color": "#09090B",
"lang": "en",
"scope": "/",
"id": "/?source=pwa",
"orientation": "portrait",
"icons": [
{ "src": "favicon.ico", "sizes": "128x128", "type": "image/x-icon" },
{ "src": "pwa-64x64.png", "sizes": "64x64", "type": "image/png" },
{ "src": "pwa-192x192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "pwa-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
{ "src": "maskable-icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
],
"screenshots": [
{
"src": "screenshots/web/1-landing-page.webp",
"sizes": "1920x1080 any",
"type": "image/webp",
"form_factor": "wide",
"label": "Landing Page"
},
{
"src": "screenshots/web/2-resume-dashboard.webp",
"sizes": "1920x1080 any",
"type": "image/webp",
"form_factor": "wide",
"label": "Resume Dashboard"
},
{
"src": "screenshots/web/3-builder-screen.webp",
"sizes": "1920x1080 any",
"type": "image/webp",
"form_factor": "wide",
"label": "Builder Screen"
},
{
"src": "screenshots/web/4-template-gallery.webp",
"sizes": "1920x1080 any",
"type": "image/webp",
"form_factor": "wide",
"label": "Template Gallery"
},
{
"src": "screenshots/mobile/1-landing-page.webp",
"sizes": "1284x2778 any",
"type": "image/webp",
"form_factor": "narrow",
"label": "Landing Page"
},
{
"src": "screenshots/mobile/2-resume-dashboard.webp",
"sizes": "1284x2778 any",
"type": "image/webp",
"form_factor": "narrow",
"label": "Resume Dashboard"
},
{
"src": "screenshots/mobile/3-builder-screen.webp",
"sizes": "1284x2778 any",
"type": "image/webp",
"form_factor": "narrow",
"label": "Builder Screen"
},
{
"src": "screenshots/mobile/4-template-gallery.webp",
"sizes": "1284x2778 any",
"type": "image/webp",
"form_factor": "narrow",
"label": "Template Gallery"
}
],
"categories": [
"ai",
"builder",
"business",
"career",
"cv",
"editor",
"free",
"generator",
"job-search",
"multilingual",
"open-source",
"privacy",
"productivity",
"resume",
"self-hosted",
"templates",
"utilities",
"writing"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.
+2 -2
View File
@@ -99,9 +99,9 @@ function getComputerModernWebFonts(): WebFont[] {
preview: `${CDN}/Sans/cmunss.woff`,
files: {
"400": `${CDN}/Sans/cmunss.woff`,
"400italic": `${CDN}/Sans/cmunsl.woff`,
"400italic": `${CDN}/Sans/cmunsi.woff`,
"700": `${CDN}/Sans/cmunsx.woff`,
"700italic": `${CDN}/Sans/cmunsi.woff`,
"700italic": `${CDN}/Sans/cmunso.woff`,
},
},
{
+2 -4
View File
@@ -192,8 +192,7 @@ export async function migrateResumes() {
process.exit(0);
};
process.on("SIGINT", handleShutdown);
process.on("SIGTERM", handleShutdown);
process.on("exit", handleShutdown);
// Initialize the importer
const importer = new ReactiveResumeV4JSONImporter();
@@ -465,8 +464,7 @@ export async function migrateResumes() {
}
// Remove signal handlers
process.off("SIGINT", handleShutdown);
process.off("SIGTERM", handleShutdown);
process.off("exit", handleShutdown);
const migrationEnd = performance.now();
const migrationDurationMs = migrationEnd - migrationStart;

Some files were not shown because too many files have changed in this diff Show More