mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-06-22 04:11:55 +10:00
allow loopback to localhost on mcp, only for local MCP clients
This commit is contained in:
@@ -15,6 +15,7 @@ rss: true
|
||||
## 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)
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Besig om jou wagwoord te herstel..."
|
||||
msgid "Resources"
|
||||
msgstr "Hulpbronne"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "CV"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "CV-ontleding"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zoem uit"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zoeloe"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "የይለፍ ቃልዎን በመዳግም ላይ…"
|
||||
msgid "Resources"
|
||||
msgstr "ሀብቶች"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr ""
|
||||
@@ -3951,4 +3945,3 @@ msgstr "አጉር"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ዙሉ"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "جاري إعادة تعيين كلمة المرور..."
|
||||
msgid "Resources"
|
||||
msgstr "الموارد"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "السيرة الذاتية"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "تحليل السيرة الذاتية"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "تصغير"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "الزولو"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Parolunuz sıfırlanır..."
|
||||
msgid "Resources"
|
||||
msgstr "Resurslar"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Təcrübə"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Təcrübə Təhlili"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Uzaqlaşdır"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Нулиране на паролата ви..."
|
||||
msgid "Resources"
|
||||
msgstr "Ресурси"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Резюме"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Анализ на автобиографията"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Намаляване"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Зулуски"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "আপনার পাসওয়ার্ড রিসেট করা
|
||||
msgid "Resources"
|
||||
msgstr "রিসোর্সসমূহ"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "জীবনবৃত্তান্ত"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "রিজিউমে বিশ্লেষণ"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "জুম আউট"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "জুলু"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "S’està restablint la contrasenya..."
|
||||
msgid "Resources"
|
||||
msgstr "Recursos"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Resum"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Anàlisi de currículum"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Allunya"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulú"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Obnovování hesla…"
|
||||
msgid "Resources"
|
||||
msgstr "Zdroje"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Životopis"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analýza životopisu"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Oddálit"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Nulstiller din adgangskode..."
|
||||
msgid "Resources"
|
||||
msgstr "Ressourcer"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "CV"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "CV-analyse"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zoom ud"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Ihr Passwort wird zurückgesetzt..."
|
||||
msgid "Resources"
|
||||
msgstr "Ressourcen"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Lebenslauf"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Lebenslauf Analyse"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Herauszoomen"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Επαναφορά κωδικού πρόσβασης..."
|
||||
msgid "Resources"
|
||||
msgstr "Πόροι"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Βιογραφικό σημείωμα"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Ανάλυση βιογραφικού σημειώματος"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Σμίκρυνση"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Ζουλού"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Resetting your password..."
|
||||
msgid "Resources"
|
||||
msgstr "Resources"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Resume"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Resume Analysis"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zoom out"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-6
@@ -2767,12 +2767,6 @@ msgstr "Resetting your password..."
|
||||
msgid "Resources"
|
||||
msgstr "Resources"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Resume"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Resume Analysis"
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Restableciendo tu contraseña..."
|
||||
msgid "Resources"
|
||||
msgstr "Recursos"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Currículum"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Análisis del currículum"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Alejar"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulú"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "در حال تنظیم مجدد گذرواژه شما..."
|
||||
msgid "Resources"
|
||||
msgstr "منابع"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "رزومه"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "تحلیل رزومه"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "کوچکنمایی"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "زولو"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Palautetaan salasanaasi..."
|
||||
msgid "Resources"
|
||||
msgstr "Resurssit"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Yhteenveto"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Yhteenveto Analyysi"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Loitonna"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Réinitialisation de votre mot de passe..."
|
||||
msgid "Resources"
|
||||
msgstr "Ressources"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Curriculum vitae"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analyse de CV"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zoom arrière"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zoulou"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "איפוס הסיסמה שלך..."
|
||||
msgid "Resources"
|
||||
msgstr "משאבים"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "קורות חיים"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "ניתוח קורות חיים"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "התרחקות"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "זולו"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "आपका पासवर्ड रीसेट किया जा
|
||||
msgid "Resources"
|
||||
msgstr "संसाधन"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "जीवन-परिचय"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "रिज़्यूमे विश्लेषण"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "ज़ूम आउट"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ज़ुलु"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Jelszó visszaállítása..."
|
||||
msgid "Resources"
|
||||
msgstr "Erőforrások"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Önéletrajz"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Összefoglaló elemzés"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Kicsinyítés"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Mengatur ulang kata sandi Anda..."
|
||||
msgid "Resources"
|
||||
msgstr "Sumber Daya"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Melanjutkan"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Melanjutkan Analisis"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Perkecil"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Reimpostazione della tua password in corso..."
|
||||
msgid "Resources"
|
||||
msgstr "Risorse"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Il curriculum"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analisi del curriculum"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Rimpicciolisci"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "パスワードをリセットしています..."
|
||||
msgid "Resources"
|
||||
msgstr "リソース"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "履歴書"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "履歴書分析"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "ズームアウト"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ズールー語"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "កំពុងកំណត់ពាក្យសម្ងាត់
|
||||
msgid "Resources"
|
||||
msgstr "ធនធាន"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr ""
|
||||
@@ -3951,4 +3945,3 @@ msgstr "បង្រួម"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಮರು
|
||||
msgid "Resources"
|
||||
msgstr "ಸಂಪನ್ಮೂಲಗಳು"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr ""
|
||||
@@ -3951,4 +3945,3 @@ msgstr "ಗಾತ್ರ ಕುಗ್ಗಿಸಿ"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ಜೂಲೂ"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "비밀번호를 재설정하는 중입니다..."
|
||||
msgid "Resources"
|
||||
msgstr "리소스"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "이력서"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "이력서 분석"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "축소"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "줄루어"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Atstatomas slaptažodis..."
|
||||
msgid "Resources"
|
||||
msgstr "Ištekliai"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "CV"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Gyvenimo aprašymo analizė"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Tolinti"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulų"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Tiek atiestatīta jūsu parole..."
|
||||
msgid "Resources"
|
||||
msgstr "Resursi"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "CV"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "CV analīze"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Tālināt"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "നിങ്ങളുടെ പാസ്വേഡ് റീസെറ
|
||||
msgid "Resources"
|
||||
msgstr "റിസോഴ്സുകൾ"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "പുനരാരംഭിക്കുക"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "റെസ്യൂമെ വിശകലനം"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "സൂം ഔട്ട് ചെയ്യുക"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "സൂളു"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "तुमचा पासवर्ड रीसेट केला ज
|
||||
msgid "Resources"
|
||||
msgstr "साधने"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "पुनरावलोकन"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "रेझ्युमे विश्लेषण"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "बाहेर झूम करा"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "झुलू"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Menetapkan semula kata laluan anda..."
|
||||
msgid "Resources"
|
||||
msgstr "Sumber"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Ringkasan"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analisis Resume"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zum keluar"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "तपाईंको पासवर्ड रिसेट हुँद
|
||||
msgid "Resources"
|
||||
msgstr "स्रोतहरू"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "जीवन बृत्तान्त"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "बायोडाटा विश्लेषण"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "जूम आउट"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "जुलु"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Uw wachtwoord wordt gereset..."
|
||||
msgid "Resources"
|
||||
msgstr "Bronnen"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "CV"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "C.V. Analyse"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Uitzoomen"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Tilbakestiller passordet ditt..."
|
||||
msgid "Resources"
|
||||
msgstr "Ressurser"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr ""
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zoom ut"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "ଆପଣଙ୍କ ପାସୱାର୍ଡ ରିସେଟ୍ ହେଉ
|
||||
msgid "Resources"
|
||||
msgstr "ସମ୍ପଦ"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr ""
|
||||
@@ -3951,4 +3945,3 @@ msgstr "ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ଜୁଲୁ"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Resetowanie hasła..."
|
||||
msgid "Resources"
|
||||
msgstr "Zasoby"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Proszę wznowić"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analiza CV"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Pomniejsz"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Redefinindo a sua senha..."
|
||||
msgid "Resources"
|
||||
msgstr "Recursos"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Currículo"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Análise de currículo"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Diminuir zoom"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "A repor a sua senha..."
|
||||
msgid "Resources"
|
||||
msgstr "Recursos"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Currículo"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Análise de currículos"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Afastar"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Se resetează parola..."
|
||||
msgid "Resources"
|
||||
msgstr "Resurse"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "CV"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analiza CV-ului"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Micșorează"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Сброс пароля..."
|
||||
msgid "Resources"
|
||||
msgstr "Ресурсы"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Резюме"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Анализ резюме"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Уменьшить"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Зулу"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Obnovujem tvoje heslo..."
|
||||
msgid "Resources"
|
||||
msgstr "Zdroje"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Životopis"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analýza životopisu"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Oddialiť"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Ponastavljanje gesla..."
|
||||
msgid "Resources"
|
||||
msgstr "Viri"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Življenjepis"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analiza življenjepisa"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Oddalji"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zuluščina"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Po rivendoset fjalëkalimi juaj..."
|
||||
msgid "Resources"
|
||||
msgstr "Burime"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Përmbledhje"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analiza e CV-së"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zvogëlo"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Ресетовање ваше лозинке..."
|
||||
msgid "Resources"
|
||||
msgstr "Ресурси"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Резиме"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Анализа резимеа"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Умањи"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Зулу"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Återställer ditt lösenord..."
|
||||
msgid "Resources"
|
||||
msgstr "Resurser"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Meritförteckning"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Analys av CV"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Zooma ut"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "உங்கள் கடவுச்சொல்லை மீட்ட
|
||||
msgid "Resources"
|
||||
msgstr "வளங்கள்"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "தொடர்"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "விண்ணப்பப் பகுப்பாய்வு"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "சிறிதாக்கு"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ஜூலு"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "మీ పాస్వర్డ్ను రీసెట్ చే
|
||||
msgid "Resources"
|
||||
msgstr "వనరులు"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "రిజ్యూమ్"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "రిజ్యూమ్ విశ్లేషణ"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "జూమ్ అవుట్ చేయండి"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "జులు"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "กำลังรีเซ็ตรหัสผ่านของคุ
|
||||
msgid "Resources"
|
||||
msgstr "ทรัพยากร"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "ประวัติย่อ"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "การวิเคราะห์ประวัติย่อ"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "ซูมออก"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "ซูลู"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Şifreniz sıfırlanıyor..."
|
||||
msgid "Resources"
|
||||
msgstr "Kaynaklar"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Özgeçmiş"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Özgeçmiş Analizi"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Uzaklaştır"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Скидання пароля..."
|
||||
msgid "Resources"
|
||||
msgstr "Ресурси"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Резюме"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Аналіз резюме"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Зменшити"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Зулу"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Parolingiz tiklanmoqda..."
|
||||
msgid "Resources"
|
||||
msgstr "Resurslar"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Rezyume"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Rezyume tahlili"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Kichraytirish"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "Đang đặt lại mật khẩu..."
|
||||
msgid "Resources"
|
||||
msgstr "Tài nguyên"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "Sơ yếu lý lịch"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "Phân tích sơ yếu lý lịch"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "Thu nhỏ"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "Zulu"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "正在重置你的密码…"
|
||||
msgid "Resources"
|
||||
msgstr "资源"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "简历"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "简历分析"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "缩小"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "祖鲁语"
|
||||
|
||||
|
||||
Generated
-7
@@ -2772,12 +2772,6 @@ msgstr "正在重設您的密碼…"
|
||||
msgid "Resources"
|
||||
msgstr "資源"
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr "簡歷"
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr "履歷分析"
|
||||
@@ -3951,4 +3945,3 @@ msgstr "縮小"
|
||||
#: src/utils/locale.ts
|
||||
msgid "Zulu"
|
||||
msgstr "祖魯語"
|
||||
|
||||
|
||||
Generated
-6
@@ -2767,12 +2767,6 @@ msgstr ""
|
||||
msgid "Resources"
|
||||
msgstr ""
|
||||
|
||||
#. Browser tab suffix for printable resume pages
|
||||
#. Browser tab title before printable resume data finishes loading
|
||||
#: src/routes/printer/$resumeId.tsx
|
||||
msgid "Resume"
|
||||
msgstr ""
|
||||
|
||||
#: src/utils/resume/section.tsx
|
||||
msgid "Resume Analysis"
|
||||
msgstr ""
|
||||
|
||||
@@ -13,9 +13,16 @@ import { useStoreWithEqualityFn } from "zustand/traditional";
|
||||
|
||||
import type { ResumeData } from "@/schema/resume/data";
|
||||
|
||||
import { orpc, type RouterOutput } from "@/integrations/orpc/client";
|
||||
import { orpc } from "@/integrations/orpc/client";
|
||||
|
||||
type Resume = Pick<RouterOutput["resume"]["getByIdForPrinter"], "id" | "name" | "slug" | "tags" | "data" | "isLocked">;
|
||||
type Resume = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
tags: string[];
|
||||
data: ResumeData;
|
||||
isLocked: boolean;
|
||||
};
|
||||
|
||||
type ResumeStoreState = {
|
||||
resume: Resume;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { eq, or } from "drizzle-orm";
|
||||
import { env } from "@/utils/env";
|
||||
import { hashPassword, verifyPassword } from "@/utils/password";
|
||||
import { generateId, toUsername } from "@/utils/string";
|
||||
import { isPrivateOrLoopbackHost, parseAllowedHostList, parseUrl } from "@/utils/url-security";
|
||||
import { isAllowedOAuthRedirectUri, parseAllowedHostList } from "@/utils/url-security";
|
||||
|
||||
import { schema } from "../drizzle";
|
||||
import { db } from "../drizzle/client";
|
||||
@@ -74,25 +74,6 @@ function getTrustedOrigins(): string[] {
|
||||
const TRUSTED_ORIGINS = getTrustedOrigins();
|
||||
const OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS = parseAllowedHostList(env.OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS);
|
||||
|
||||
function isAllowedDynamicClientRedirectHost(origin: string, hostname: string): boolean {
|
||||
if (TRUSTED_ORIGINS.includes(origin)) return true;
|
||||
if (OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS.has(origin)) return true;
|
||||
return OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS.has(hostname);
|
||||
}
|
||||
|
||||
function isAllowedDynamicClientRedirectUri(value: string) {
|
||||
const parsed = parseUrl(value);
|
||||
if (!parsed) return false;
|
||||
if (parsed.username || parsed.password) return false;
|
||||
if (parsed.hash) return false;
|
||||
if (parsed.protocol !== "https:") return false;
|
||||
if (isPrivateOrLoopbackHost(parsed.hostname)) return false;
|
||||
|
||||
const origin = parsed.origin.toLowerCase();
|
||||
const hostname = parsed.hostname.toLowerCase();
|
||||
return isAllowedDynamicClientRedirectHost(origin, hostname);
|
||||
}
|
||||
|
||||
async function findExistingUserByEmail(email: string) {
|
||||
const normalizedEmail = email.trim().toLowerCase();
|
||||
|
||||
@@ -284,7 +265,7 @@ const getAuthConfig = () => {
|
||||
if (typeof uri !== "string") {
|
||||
throw new APIError("BAD_REQUEST", { message: "redirect_uris entries must be strings" });
|
||||
}
|
||||
if (!isAllowedDynamicClientRedirectUri(uri)) {
|
||||
if (!isAllowedOAuthRedirectUri(uri, TRUSTED_ORIGINS, OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS)) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "redirect_uri is not allowed for dynamic client registration",
|
||||
});
|
||||
@@ -409,6 +390,8 @@ const getAuthConfig = () => {
|
||||
consentPage: "/auth/oauth",
|
||||
validAudiences: OAUTH_AUDIENCES,
|
||||
allowDynamicClientRegistration: true,
|
||||
// Required for MCP client onboarding (RFC 7591). Phishing vector is closed by the
|
||||
// redirect_uri allowlist in the hooks.before middleware above and in src/routes/api/auth.$.ts.
|
||||
allowUnauthenticatedClientRegistration: true,
|
||||
rateLimit: {
|
||||
register: { window: 60, max: 5 },
|
||||
|
||||
@@ -116,21 +116,6 @@ export const resumeRouter = {
|
||||
return resumeService.getById({ id: input.id, userId: context.user.id });
|
||||
}),
|
||||
|
||||
getByIdForPrinter: publicProcedure
|
||||
.route({ tags: ["Internal"], operationId: "getResumeForPrinter", summary: "Get resume by ID for printer" })
|
||||
.input(
|
||||
resumeDto.getById.input.extend({
|
||||
token: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.handler(async ({ input, context }) => {
|
||||
return resumeService.getByIdForPrinter({
|
||||
id: input.id,
|
||||
currentUserId: context.user?.id,
|
||||
printerToken: input.token,
|
||||
});
|
||||
}),
|
||||
|
||||
getBySlug: publicProcedure
|
||||
.route({
|
||||
method: "GET",
|
||||
|
||||
+10
-22
@@ -2,9 +2,10 @@ import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { auth } from "@/integrations/auth/config";
|
||||
import { env } from "@/utils/env";
|
||||
import { isPrivateOrLoopbackHost, parseAllowedHostList, parseUrl } from "@/utils/url-security";
|
||||
import { isAllowedOAuthRedirectUri, parseAllowedHostList } from "@/utils/url-security";
|
||||
|
||||
const oauthDynamicClientRedirectHosts = parseAllowedHostList(env.OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS);
|
||||
const oauthTrustedOrigins = [new URL(env.APP_URL).origin.toLowerCase()];
|
||||
const oauthAuthorizeSanitizedParams = [
|
||||
"prompt",
|
||||
"redirect_uri",
|
||||
@@ -17,25 +18,6 @@ const oauthAuthorizeSanitizedParams = [
|
||||
"resource",
|
||||
] as const;
|
||||
|
||||
function isAllowedDynamicClientRedirectUri(value: string) {
|
||||
const parsed = parseUrl(value);
|
||||
if (!parsed) return false;
|
||||
if (parsed.protocol !== "https:") return false;
|
||||
if (parsed.username || parsed.password) return false;
|
||||
if (parsed.hash) return false;
|
||||
if (isPrivateOrLoopbackHost(parsed.hostname)) return false;
|
||||
|
||||
const appOrigin = new URL(env.APP_URL).origin.toLowerCase();
|
||||
const origin = parsed.origin.toLowerCase();
|
||||
const hostname = parsed.hostname.toLowerCase();
|
||||
|
||||
if (origin === appOrigin) return true;
|
||||
if (oauthDynamicClientRedirectHosts.has(origin)) return true;
|
||||
if (oauthDynamicClientRedirectHosts.has(hostname)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function sanitizeOAuthAuthorizeRequest(request: Request): Request {
|
||||
if (request.method !== "GET") return request;
|
||||
|
||||
@@ -117,8 +99,14 @@ async function validateDynamicClientRegistrationRequest(request: Request): Promi
|
||||
|
||||
const redirectUris = Array.isArray(body.redirect_uris) ? body.redirect_uris : [];
|
||||
for (const redirectUri of redirectUris) {
|
||||
if (typeof redirectUri !== "string" || !isAllowedDynamicClientRedirectUri(redirectUri)) {
|
||||
return Response.json({ message: "redirect_uri is not allowed" }, { status: 400 });
|
||||
if (
|
||||
typeof redirectUri !== "string" ||
|
||||
!isAllowedOAuthRedirectUri(redirectUri, oauthTrustedOrigins, oauthDynamicClientRedirectHosts)
|
||||
) {
|
||||
return Response.json(
|
||||
{ error: "invalid_redirect_uri", error_description: "redirect_uri is not allowed" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { useEffect } from "react";
|
||||
import { z } from "zod";
|
||||
@@ -7,7 +7,7 @@ import { z } from "zod";
|
||||
import { LoadingScreen } from "@/components/layout/loading-screen";
|
||||
import { ResumePreview } from "@/components/resume/preview";
|
||||
import { useResumeStore } from "@/components/resume/store/resume";
|
||||
import { getORPCClient } from "@/integrations/orpc/client";
|
||||
import { resumeService } from "@/integrations/orpc/services/resume";
|
||||
import { env } from "@/utils/env";
|
||||
import { verifyPrinterToken } from "@/utils/printer-token";
|
||||
|
||||
@@ -15,6 +15,12 @@ const searchSchema = z.object({
|
||||
token: z.string().catch(""),
|
||||
});
|
||||
|
||||
const getResumeForPrinterFn = createServerFn({ method: "GET" })
|
||||
.inputValidator(z.object({ id: z.string(), token: z.string() }))
|
||||
.handler(({ data }) => {
|
||||
return resumeService.getByIdForPrinter({ id: data.id, printerToken: data.token });
|
||||
});
|
||||
|
||||
function assertValidPrinterToken(token: string, resumeId: string): void {
|
||||
const tokenResumeId = verifyPrinterToken(token);
|
||||
if (tokenResumeId === resumeId) return;
|
||||
@@ -35,25 +41,12 @@ export const Route = createFileRoute("/printer/$resumeId")({
|
||||
},
|
||||
loaderDeps: ({ search }) => ({ token: search.token }),
|
||||
loader: async ({ params, deps }) => {
|
||||
const client = getORPCClient();
|
||||
const resume = await client.resume.getByIdForPrinter({ id: params.resumeId, token: deps.token });
|
||||
const resume = await getResumeForPrinterFn({ data: { id: params.resumeId, token: deps.token } });
|
||||
|
||||
return { resume };
|
||||
},
|
||||
head: ({ loaderData }) => ({
|
||||
meta: [
|
||||
{
|
||||
title: loaderData
|
||||
? `${loaderData.resume.data.basics.name} - ${t({
|
||||
comment: "Browser tab suffix for printable resume pages",
|
||||
message: "Resume",
|
||||
})}`
|
||||
: t({
|
||||
comment: "Browser tab title before printable resume data finishes loading",
|
||||
message: "Resume",
|
||||
}),
|
||||
},
|
||||
],
|
||||
meta: [{ title: loaderData ? `${loaderData.resume.data.basics.name} - Resume` : "Resume" }],
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vite-plus/test";
|
||||
|
||||
import {
|
||||
isAllowedOAuthRedirectUri,
|
||||
isAllowedExternalUrl,
|
||||
isPrivateOrLoopbackHost,
|
||||
parseAllowedHostList,
|
||||
@@ -55,6 +56,32 @@ describe("isAllowedExternalUrl", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isAllowedOAuthRedirectUri", () => {
|
||||
const trustedOrigins = ["https://rxresu.me"];
|
||||
const allowedHosts = new Set(["https://client.example.com", "trusted.example.com"]);
|
||||
|
||||
it("allows local loopback HTTP callbacks for native OAuth clients", () => {
|
||||
expect(isAllowedOAuthRedirectUri("http://localhost:6188/callback", trustedOrigins, allowedHosts)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("http://127.0.0.1:6188/callback", trustedOrigins, allowedHosts)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("http://[::1]:6188/callback", trustedOrigins, allowedHosts)).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps rejecting unsafe HTTP and malformed redirects", () => {
|
||||
expect(isAllowedOAuthRedirectUri("http://192.168.1.20:6188/callback", trustedOrigins, allowedHosts)).toBe(false);
|
||||
expect(isAllowedOAuthRedirectUri("http://example.com/callback", trustedOrigins, allowedHosts)).toBe(false);
|
||||
expect(isAllowedOAuthRedirectUri("not-a-url", trustedOrigins, allowedHosts)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows only trusted HTTPS redirects without credentials or fragments", () => {
|
||||
expect(isAllowedOAuthRedirectUri("https://rxresu.me/callback", trustedOrigins, allowedHosts)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("https://client.example.com/callback", trustedOrigins, allowedHosts)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("https://trusted.example.com/callback", trustedOrigins, allowedHosts)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("https://evil.example.com/callback", trustedOrigins, allowedHosts)).toBe(false);
|
||||
expect(isAllowedOAuthRedirectUri("https://user:pass@rxresu.me/callback", trustedOrigins, allowedHosts)).toBe(false);
|
||||
expect(isAllowedOAuthRedirectUri("https://rxresu.me/callback#token", trustedOrigins, allowedHosts)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("sanitizeResumePictureUrl", () => {
|
||||
const appUrl = "https://rxresu.me";
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@ export function isPrivateOrLoopbackHost(hostname: string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isOAuthLoopbackRedirectHost(hostname: string) {
|
||||
const normalized = stripIpv6Brackets(normalizeHostname(hostname));
|
||||
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
|
||||
}
|
||||
|
||||
export function parseUrl(input: string) {
|
||||
try {
|
||||
return new URL(input);
|
||||
@@ -80,6 +85,24 @@ export function isAllowedExternalUrl(input: string, allowedHosts: Set<string>) {
|
||||
return allowedHosts.has(origin);
|
||||
}
|
||||
|
||||
export function isAllowedOAuthRedirectUri(input: string, trustedOrigins: string[], allowedHosts: Set<string>) {
|
||||
const parsed = parseUrl(input);
|
||||
if (!parsed) return false;
|
||||
if (parsed.username || parsed.password) return false;
|
||||
if (parsed.hash) return false;
|
||||
|
||||
const origin = parsed.origin.toLowerCase();
|
||||
const hostname = normalizeHostname(parsed.hostname);
|
||||
|
||||
if (parsed.protocol === "http:") return isOAuthLoopbackRedirectHost(hostname);
|
||||
if (parsed.protocol !== "https:") return false;
|
||||
if (isPrivateOrLoopbackHost(hostname)) return false;
|
||||
|
||||
if (trustedOrigins.includes(origin)) return true;
|
||||
if (allowedHosts.has(origin)) return true;
|
||||
return allowedHosts.has(hostname);
|
||||
}
|
||||
|
||||
export function sanitizeResumePictureUrl(url: string, appUrl: string) {
|
||||
if (!url) return "";
|
||||
if (url.startsWith("/uploads/")) return url;
|
||||
|
||||
Reference in New Issue
Block a user