initial commit of v5
@@ -0,0 +1,380 @@
|
||||
---
|
||||
title: "Project Architecture"
|
||||
description: "Understand the architecture and codebase structure of Reactive Resume"
|
||||
---
|
||||
|
||||
This guide provides a comprehensive overview of Reactive Resume's architecture and codebase structure, helping you understand how different parts of the application work together.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack Overview
|
||||
|
||||
Reactive Resume is built with a modern, type-safe stack:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend" icon="browser">
|
||||
- **React 19** with TanStack Start
|
||||
- **TypeScript** for type safety
|
||||
- **Tailwind CSS** for styling
|
||||
- **Radix UI** for accessible components
|
||||
</Card>
|
||||
<Card title="Backend" icon="server">
|
||||
- **ORPC** for type-safe RPC
|
||||
- **Drizzle ORM** with PostgreSQL
|
||||
- **Better Auth** for authentication
|
||||
- **Sharp** for image processing
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Application Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
%% Client Side
|
||||
subgraph Client ["Client"]
|
||||
Router["TanStack Router"]
|
||||
Query["TanStack Query"]
|
||||
ORPCClient["oRPC Client"]
|
||||
end
|
||||
|
||||
%% Server Side
|
||||
subgraph Server ["Server"]
|
||||
ORPCRouter["oRPC Router"]
|
||||
Auth["Better Auth"]
|
||||
Services["Services Layer"]
|
||||
Drizzle["Drizzle ORM"]
|
||||
end
|
||||
|
||||
Database["PostgreSQL"]
|
||||
Storage["File System OR S3-Compatible Storage"]
|
||||
Printer["Gotenberg"]
|
||||
|
||||
Router --> ORPCClient
|
||||
Query --> ORPCClient
|
||||
ORPCClient -- calls --> ORPCRouter
|
||||
ORPCRouter --> Services
|
||||
Auth --> Services
|
||||
Services --> Drizzle
|
||||
Drizzle --> Database
|
||||
Services --> Storage
|
||||
Services --> Printer
|
||||
```
|
||||
|
||||
**Diagram:** This flow shows data and control flow between the main architectural layers of Reactive Resume.
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
### Root Level
|
||||
|
||||
| Directory | Purpose |
|
||||
|-----------|---------|
|
||||
| `src/` | Main application source code |
|
||||
| `public/` | Static assets served directly |
|
||||
| `locales/` | Translation files (.po format) |
|
||||
| `migrations/` | Database migration files |
|
||||
| `docs/` | Mintlify documentation |
|
||||
| `data/` | Local data storage (fonts, uploads) |
|
||||
| `scripts/` | Utility scripts |
|
||||
|
||||
### Source Code (`src/`)
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="components/" icon="cube">
|
||||
Reusable React components organized by category:
|
||||
|
||||
- **`ui/`** — Base UI components (Button, Card, Dialog, etc.)
|
||||
- **`resume/`** — Resume-specific components (sections, templates)
|
||||
- **`input/`** — Form input components (ColorPicker, RichInput)
|
||||
- **`layout/`** — Layout components (Sidebar, LoadingScreen)
|
||||
- **`animation/`** — Animation components (Spotlight, TextMask)
|
||||
- **`theme/`** — Theme management components
|
||||
- **`typography/`** — Font management components
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="routes/" icon="route">
|
||||
File-based routing using TanStack Router:
|
||||
|
||||
- **`__root.tsx`** — Root layout with providers
|
||||
- **`_home/`** — Public home page routes
|
||||
- **`auth/`** — Authentication routes (login, register, etc.)
|
||||
- **`dashboard/`** — User dashboard routes
|
||||
- **`builder/`** — Resume builder routes (the main editor)
|
||||
- **`printer/`** — PDF printing route
|
||||
- **`api/`** — API routes
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="integrations/" icon="plug">
|
||||
Third-party service integrations:
|
||||
|
||||
- **`auth/`** — Better Auth client configuration
|
||||
- **`drizzle/`** — Database schema and utilities
|
||||
- **`orpc/`** — API router, client, and services
|
||||
- **`ai/`** — AI service integrations
|
||||
- **`import/`** — Resume import utilities
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="dialogs/" icon="window-maximize">
|
||||
Modal dialog components:
|
||||
|
||||
- **`auth/`** — Authentication dialogs
|
||||
- **`resume/`** — Resume management dialogs
|
||||
- **`api-key/`** — API key management dialogs
|
||||
- **`manager.tsx`** — Dialog manager component
|
||||
- **`store.ts`** — Dialog state management (Zustand)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="schema/" icon="file-code">
|
||||
Zod schemas for validation:
|
||||
|
||||
- **`resume/`** — Resume data schemas
|
||||
- **`icons.ts`** — Icon definitions
|
||||
- **`templates.ts`** — Template definitions
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="hooks/" icon="hook">
|
||||
Custom React hooks:
|
||||
|
||||
- `use-confirm.tsx` — Confirmation dialog hook
|
||||
- `use-prompt.tsx` — Prompt dialog hook
|
||||
- `use-mobile.tsx` — Mobile detection hook
|
||||
- `use-safe-context.tsx` — Safe context consumption
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="utils/" icon="wrench">
|
||||
Utility functions:
|
||||
|
||||
- `env.ts` — Environment variable validation
|
||||
- `locale.ts` — Locale utilities
|
||||
- `theme.ts` — Theme utilities
|
||||
- `string.ts` — String manipulation
|
||||
- `file.ts` — File handling utilities
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### File-Based Routing
|
||||
|
||||
Routes are automatically generated from the file structure in `src/routes/`. TanStack Router conventions:
|
||||
|
||||
| Pattern | Description | Example |
|
||||
|---------|-------------|---------|
|
||||
| `index.tsx` | Index route | `/dashboard` |
|
||||
| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
|
||||
| `_layout/` | Layout group (prefix) | `_home/` |
|
||||
| `__root.tsx` | Root layout | Wraps all routes |
|
||||
|
||||
<Warning>
|
||||
Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.
|
||||
</Warning>
|
||||
|
||||
### API Layer (ORPC)
|
||||
|
||||
ORPC provides end-to-end type safety for API calls:
|
||||
|
||||
```
|
||||
src/integrations/orpc/
|
||||
├── client.ts # Client-side ORPC setup
|
||||
├── router/ # API route definitions
|
||||
│ ├── auth.ts # Authentication endpoints
|
||||
│ ├── resume.ts # Resume CRUD operations
|
||||
│ └── storage.ts # File storage operations
|
||||
├── services/ # Business logic layer
|
||||
└── helpers/ # Utility functions
|
||||
```
|
||||
|
||||
**Using the API client:**
|
||||
|
||||
```tsx
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { orpc } from "@/integrations/orpc/client";
|
||||
|
||||
// Type-safe API calls with TanStack Query
|
||||
const { data } = useQuery(orpc.resume.findMany.queryOptions());
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
Reactive Resume uses a hybrid approach:
|
||||
|
||||
| Type | Tool | Use Case |
|
||||
|------|------|----------|
|
||||
| Server State | TanStack Query | API data, caching, sync |
|
||||
| Client State | Zustand | UI state, dialogs, preferences |
|
||||
| Form State | React Hook Form | Form inputs and validation |
|
||||
|
||||
### Database Schema
|
||||
|
||||
The database schema is defined using Drizzle ORM in `src/integrations/drizzle/schema.ts`:
|
||||
|
||||
```tsx
|
||||
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
|
||||
export const resume = pgTable("resume", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
title: text("title").notNull(),
|
||||
slug: text("slug").notNull(),
|
||||
// ... more fields
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Development Patterns
|
||||
|
||||
### Adding a New Component
|
||||
|
||||
1. Create the component in the appropriate `src/components/` subdirectory
|
||||
2. Export it from the directory's index file (if applicable)
|
||||
3. Use TypeScript props interfaces for type safety
|
||||
4. Follow existing patterns for consistency
|
||||
|
||||
```tsx
|
||||
// src/components/ui/my-component.tsx
|
||||
import { cn } from "@/utils/style";
|
||||
|
||||
interface MyComponentProps {
|
||||
title: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const MyComponent = ({ title, className }: MyComponentProps) => {
|
||||
return <div className={cn("p-4", className)}>{title}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### Adding a New Route
|
||||
|
||||
1. Create a new file in `src/routes/` following TanStack Router conventions
|
||||
2. The route tree auto-generates when you save
|
||||
3. Use `createFileRoute` for type-safe routes
|
||||
|
||||
```tsx
|
||||
// src/routes/my-page.tsx
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/my-page")({
|
||||
component: MyPage,
|
||||
});
|
||||
|
||||
function MyPage() {
|
||||
return <div>My Page Content</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Adding an API Endpoint
|
||||
|
||||
1. Add the route handler in `src/integrations/orpc/router/`
|
||||
2. Create service functions in `src/integrations/orpc/services/` if needed
|
||||
3. The endpoint is automatically typed on the client
|
||||
|
||||
```tsx
|
||||
// In router file
|
||||
import { z } from "zod";
|
||||
import { publicProcedure, router } from "../server";
|
||||
|
||||
export const myRouter = router({
|
||||
hello: publicProcedure
|
||||
.input(z.object({ name: z.string() }))
|
||||
.handler(async ({ input }) => {
|
||||
return { message: `Hello, ${input.name}!` };
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### Adding Translations
|
||||
|
||||
1. Wrap text with `t` macro or `<Trans>` component
|
||||
2. Run `pnpm run lingui:extract` to update locale files
|
||||
3. Edit the `.po` files in `locales/` to add translations
|
||||
|
||||
```tsx
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
|
||||
// In component
|
||||
const title = t`Welcome`;
|
||||
<Trans>Click here to continue</Trans>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `vite.config.ts` | Vite bundler configuration |
|
||||
| `tsconfig.json` | TypeScript configuration |
|
||||
| `biome.json` | Linter and formatter settings |
|
||||
| `drizzle.config.ts` | Drizzle ORM configuration |
|
||||
| `lingui.config.ts` | Lingui i18n configuration |
|
||||
| `components.json` | shadcn/ui component configuration |
|
||||
|
||||
---
|
||||
|
||||
## Contributing Guidelines
|
||||
|
||||
<Steps>
|
||||
<Step title="Fork & Clone">
|
||||
Fork the repository on GitHub and clone your fork locally.
|
||||
</Step>
|
||||
|
||||
<Step title="Create a Branch">
|
||||
Create a feature branch from `main`:
|
||||
```bash
|
||||
git checkout -b feature/my-feature
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Make Changes">
|
||||
Implement your changes following the patterns described above.
|
||||
</Step>
|
||||
|
||||
<Step title="Test Locally">
|
||||
Ensure the app works correctly with your changes:
|
||||
```bash
|
||||
pnpm run dev
|
||||
pnpm run lint
|
||||
pnpm run typecheck
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Commit & Push">
|
||||
Write clear commit messages and push to your fork.
|
||||
</Step>
|
||||
|
||||
<Step title="Open a Pull Request">
|
||||
Open a PR against the main repository with a clear description of your changes.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Need Help?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="GitHub Discussions"
|
||||
icon="comments"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/discussions"
|
||||
>
|
||||
Ask questions and discuss ideas with the community.
|
||||
</Card>
|
||||
<Card
|
||||
title="GitHub Issues"
|
||||
icon="bug"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/issues"
|
||||
>
|
||||
Report bugs or request features.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,371 @@
|
||||
---
|
||||
title: "Development Setup"
|
||||
description: "Set up a local development environment for Reactive Resume"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Prerequisites**:
|
||||
- [Node.js](https://nodejs.org/) v20 or higher
|
||||
- [pnpm](https://pnpm.io/) v10.28.0 or higher (package manager)
|
||||
- [Docker](https://docs.docker.com/get-docker/) and Docker Compose
|
||||
- [Git](https://git-scm.com/)
|
||||
</Info>
|
||||
|
||||
This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running.
|
||||
|
||||
---
|
||||
|
||||
## Setting Up Your Development Environment
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone the Repository">
|
||||
```bash
|
||||
git clone https://github.com/AmruthPillai/Reactive-Resume.git
|
||||
cd Reactive-Resume
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install Dependencies">
|
||||
This project uses [pnpm](https://pnpm.io/) as its package manager for its speed and efficiency.
|
||||
|
||||
```bash
|
||||
# Install pnpm if you haven't already
|
||||
npm install -g pnpm
|
||||
|
||||
# Install project dependencies
|
||||
pnpm install
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start Infrastructure Services">
|
||||
Start the required services using the development-specific Docker Compose file:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml up -d
|
||||
```
|
||||
|
||||
This starts the following infrastructure services:
|
||||
- **PostgreSQL** — Database (port 5432)
|
||||
- **SeaweedFS** — S3-compatible storage (port 8333)
|
||||
- **Gotenberg** — PDF generation service (port 4000)
|
||||
- **Mailpit** — Email testing server (SMTP on port 1025, UI on port 8025)
|
||||
|
||||
<Tip>
|
||||
Use `compose.dev.yml` instead of `compose.yml` for local development. The development file only includes infrastructure services with ports exposed to your host machine, while the main `compose.yml` includes the full application stack intended for production deployments.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
Wait for all services to be healthy before proceeding. Check with `docker compose -f compose.dev.yml ps`.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Environment Variables">
|
||||
Create a `.env` file in the project root:
|
||||
|
||||
```bash
|
||||
# Server
|
||||
APP_URL=http://localhost:3000
|
||||
|
||||
# Database
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
||||
|
||||
# Authentication
|
||||
AUTH_SECRET=development-secret-change-in-production
|
||||
|
||||
# Storage (SeaweedFS)
|
||||
S3_ACCESS_KEY_ID=seaweedfs
|
||||
S3_SECRET_ACCESS_KEY=seaweedfs
|
||||
S3_ENDPOINT=http://localhost:8333
|
||||
S3_BUCKET=reactive-resume
|
||||
S3_FORCE_PATH_STYLE=true
|
||||
|
||||
# PDF Printer (required for local development)
|
||||
PRINTER_APP_URL=http://host.docker.internal:3000
|
||||
|
||||
# Email (Mailpit for local development)
|
||||
SMTP_HOST=localhost
|
||||
SMTP_PORT=1025
|
||||
```
|
||||
|
||||
<Note>
|
||||
**PDF Generation Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the Gotenberg PDF service is running inside Docker (which is the case when using `compose.dev.yml`). Gotenberg needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
**Email Testing**: The development stack includes [Mailpit](https://mailpit.axllent.org/), an email testing tool. All emails sent by the application will be captured and viewable at [http://localhost:8025](http://localhost:8025). No emails will actually be sent to real addresses during development.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Run Database Migrations">
|
||||
Apply the database schema:
|
||||
|
||||
```bash
|
||||
pnpm run db:migrate
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start the Development Server">
|
||||
```bash
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
Your local Reactive Resume instance will be available at [http://localhost:3000](http://localhost:3000).
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Available Scripts
|
||||
|
||||
Here are the most commonly used scripts during development:
|
||||
|
||||
### Development
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pnpm run dev` | Start the development server with hot reload |
|
||||
| `pnpm run build` | Build the application for production |
|
||||
| `pnpm run start` | Start the production server |
|
||||
| `pnpm run lint` | Run Biome linter and formatter |
|
||||
| `pnpm run typecheck` | Run TypeScript type checking |
|
||||
|
||||
### Database
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pnpm run db:generate` | Generate migration files from schema changes |
|
||||
| `pnpm run db:migrate` | Apply pending migrations |
|
||||
| `pnpm run db:push` | Push schema changes directly (dev only) |
|
||||
| `pnpm run db:pull` | Pull schema from existing database |
|
||||
| `pnpm run db:studio` | Open Drizzle Studio (database GUI) |
|
||||
|
||||
### Internationalization
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pnpm run lingui:extract` | Extract translatable strings from code |
|
||||
|
||||
### Documentation
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pnpm run docs` | Start the Mintlify docs development server |
|
||||
|
||||
---
|
||||
|
||||
## Understanding the Project Structure
|
||||
|
||||
Understanding the project structure will help you navigate the codebase:
|
||||
|
||||
```
|
||||
reactive-resume/
|
||||
├── src/
|
||||
│ ├── components/ # Reusable React components
|
||||
│ │ ├── ui/ # Base UI components (Button, Card, etc.)
|
||||
│ │ ├── resume/ # Resume-specific components
|
||||
│ │ └── ...
|
||||
│ ├── dialogs/ # Modal dialogs
|
||||
│ ├── hooks/ # Custom React hooks
|
||||
│ ├── integrations/ # Third-party integrations
|
||||
│ │ ├── auth/ # Better Auth integration
|
||||
│ │ ├── drizzle/ # Database schema & utilities
|
||||
│ │ └── orpc/ # API routes & services
|
||||
│ ├── routes/ # File-based routing (TanStack Router)
|
||||
│ │ ├── builder/ # Resume builder pages
|
||||
│ │ ├── dashboard/ # User dashboard
|
||||
│ │ ├── auth/ # Authentication pages
|
||||
│ │ └── ...
|
||||
│ ├── schema/ # Zod schemas for validation
|
||||
│ ├── utils/ # Utility functions
|
||||
│ └── styles/ # Global CSS styles
|
||||
├── public/ # Static assets
|
||||
├── locales/ # Translation files (.po format)
|
||||
├── migrations/ # Database migrations
|
||||
├── docs/ # Mintlify documentation
|
||||
└── data/ # Local data (fonts, uploads)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Working with the Database
|
||||
|
||||
### Viewing the Database
|
||||
|
||||
Use Drizzle Studio to explore and manage your database:
|
||||
|
||||
```bash
|
||||
pnpm run db:studio
|
||||
```
|
||||
|
||||
This opens a web-based GUI at [https://local.drizzle.studio](https://local.drizzle.studio).
|
||||
|
||||
### Making Schema Changes
|
||||
|
||||
1. Edit the schema in `src/integrations/drizzle/schema.ts`
|
||||
2. Generate a migration:
|
||||
```bash
|
||||
pnpm run db:generate
|
||||
```
|
||||
3. Apply the migration:
|
||||
```bash
|
||||
pnpm run db:migrate
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Always review generated migrations before applying them, especially when working with existing data.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Working with Translations
|
||||
|
||||
Reactive Resume uses [Lingui](https://lingui.dev/) for internationalization.
|
||||
|
||||
### Adding Translatable Text
|
||||
|
||||
Use the `t` macro for strings or `<Trans>` component for JSX:
|
||||
|
||||
```tsx
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
|
||||
// For plain strings
|
||||
const message = t`Hello, World!`;
|
||||
|
||||
// For JSX content
|
||||
<Trans>Welcome to Reactive Resume</Trans>
|
||||
```
|
||||
|
||||
### Extracting Translations
|
||||
|
||||
After adding new translatable text, extract them to the locale files:
|
||||
|
||||
```bash
|
||||
pnpm run lingui:extract
|
||||
```
|
||||
|
||||
Translation files are located in the `locales/` directory in `.po` format.
|
||||
|
||||
---
|
||||
|
||||
## Code Quality
|
||||
|
||||
### Linting & Formatting
|
||||
|
||||
Uses [Biome](https://biomejs.dev/) for linting and formatting:
|
||||
|
||||
```bash
|
||||
# Check and auto-fix issues
|
||||
pnpm run lint
|
||||
```
|
||||
|
||||
### Type Checking
|
||||
|
||||
Run TypeScript type checking:
|
||||
|
||||
```bash
|
||||
pnpm run typecheck
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Configure your IDE to use Biome for automatic formatting on save. For VS Code, install the [Biome extension](https://marketplace.visualstudio.com/items?itemName=biomejs.biome).
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Port 3000 is already in use">
|
||||
Another process is using port 3000. Either stop that process or start the dev server on a different port:
|
||||
|
||||
```bash
|
||||
PORT=3001 pnpm run dev
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database connection refused">
|
||||
Ensure Docker containers are running:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml ps
|
||||
docker compose -f compose.dev.yml up -d
|
||||
```
|
||||
|
||||
Check that PostgreSQL is healthy and accessible on port 5432.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3/Storage errors">
|
||||
Verify SeaweedFS is running and the bucket exists:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml logs seaweedfs
|
||||
docker compose -f compose.dev.yml logs seaweedfs-create-bucket
|
||||
```
|
||||
|
||||
If the bucket wasn't created, restart the bucket creation service:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml restart seaweedfs-create-bucket
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Type errors after pulling changes">
|
||||
The route tree may need regeneration. Run the dev server which auto-generates routes:
|
||||
|
||||
```bash
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
Or run type checking to see specific errors:
|
||||
|
||||
```bash
|
||||
pnpm run typecheck
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Uploaded images not appearing in PDF exports">
|
||||
When running in development mode (Docker services + app on host), images you upload to your resume may not appear in PDF exports. This is because the printer service runs inside Docker and cannot access URLs that resolve to `localhost` on your host machine.
|
||||
|
||||
**Why this happens:**
|
||||
|
||||
The app running on your host uploads images to SeaweedFS (S3 storage) using `localhost:8333`. When Gotenberg tries to fetch these images to render the PDF, it cannot resolve `localhost` the same way your host machine does — they're on different networks.
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Run the entire stack in Docker** (recommended for testing PDF exports):
|
||||
|
||||
Use the production `compose.yml` instead of `compose.dev.yml` to run all services, including the app, on the same Docker network:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This ensures all services can communicate with each other seamlessly.
|
||||
|
||||
2. **Use publicly accessible images**:
|
||||
|
||||
If you need to test PDF exports while developing on the host, use images hosted on publicly accessible URLs (e.g., images already hosted online) instead of uploading local files. The printer service can fetch these without network issues.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Project Architecture"
|
||||
icon="folder-open"
|
||||
href="/contributing/architecture"
|
||||
>
|
||||
Deep dive into the project architecture and codebase structure.
|
||||
</Card>
|
||||
<Card
|
||||
title="GitHub Repository"
|
||||
icon="github"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume"
|
||||
>
|
||||
View the source code and contribute to the project.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,158 @@
|
||||
---
|
||||
title: "Contributing Translations"
|
||||
description: "Help translate Reactive Resume into your language using Crowdin"
|
||||
---
|
||||
|
||||
Reactive Resume is used by people all around the world, and translations help make the app accessible to everyone. If you speak a language other than English, you can help by contributing translations.
|
||||
|
||||
---
|
||||
|
||||
## How Translations Work
|
||||
|
||||
Reactive Resume uses [Crowdin](https://crowdin.com/) as its localization management platform. Crowdin provides a user-friendly interface where translators can contribute translations without needing to write code or work with files directly.
|
||||
|
||||
<Info>
|
||||
The Reactive Resume Crowdin project is available at [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume).
|
||||
</Info>
|
||||
|
||||
Once translations are submitted and approved on Crowdin, they are automatically synced to the codebase and will be available in the next release of the app.
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
<Steps>
|
||||
<Step title="Create a Crowdin Account">
|
||||
If you don't already have an account, sign up at [crowdin.com](https://crowdin.com/). You can register using your email or sign up with Google, Facebook, Twitter, GitHub, or GitLab.
|
||||
|
||||
<Tip>
|
||||
For detailed instructions on creating an account and getting started, see Crowdin's official [For Translators](https://support.crowdin.com/for-translators/) documentation.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Join the Reactive Resume Project">
|
||||
Navigate to the [Reactive Resume project on Crowdin](https://crowdin.com/project/reactive-resume) and click **Join** to become a contributor.
|
||||
</Step>
|
||||
|
||||
<Step title="Select Your Language">
|
||||
From the project dashboard, click on the language you want to translate. You'll see a list of files that need translation along with the progress for each.
|
||||
</Step>
|
||||
|
||||
<Step title="Start Translating">
|
||||
Click on a file to open the Crowdin Editor. You'll see the source text (English) on the left and a text field for your translation on the right.
|
||||
|
||||
- Translate the text accurately while preserving any placeholders or formatting
|
||||
- Use the suggestions from Translation Memory and Machine Translation as a starting point
|
||||
- Vote on existing translations if you agree with them
|
||||
</Step>
|
||||
|
||||
<Step title="Save Your Translations">
|
||||
Your translations are saved automatically as you work. Once reviewed, they'll be included in the next app release.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Translation Guidelines
|
||||
|
||||
To maintain consistency across all translations, please follow these guidelines:
|
||||
|
||||
### Preserve Placeholders
|
||||
|
||||
Some strings contain placeholders like `{name}` or `{count}`. These must remain unchanged in your translation:
|
||||
|
||||
```
|
||||
English: "Hello, {name}!"
|
||||
Spanish: "¡Hola, {name}!"
|
||||
```
|
||||
|
||||
### Keep Formatting
|
||||
|
||||
Preserve any HTML tags or markdown formatting in the source text:
|
||||
|
||||
```
|
||||
English: "Click <1>here</1> to continue"
|
||||
German: "Klicken Sie <1>hier</1>, um fortzufahren"
|
||||
```
|
||||
|
||||
### Use Formal or Informal Tone Consistently
|
||||
|
||||
Choose either formal or informal language based on what's standard for software in your language, and stick with it throughout.
|
||||
|
||||
### Technical Terms
|
||||
|
||||
Some technical terms (like "PDF", "URL", "JSON") are often kept in English across languages. Use your judgment based on what's common in your language's software community.
|
||||
|
||||
---
|
||||
|
||||
## Requesting a New Language
|
||||
|
||||
If your language is not listed in the Crowdin project, you can request it to be added.
|
||||
|
||||
<Warning>
|
||||
Before requesting a new language, please check if it's already available in the [Crowdin project](https://crowdin.com/project/reactive-resume).
|
||||
</Warning>
|
||||
|
||||
To request a new language:
|
||||
|
||||
1. Go to the [GitHub Issues](https://github.com/AmruthPillai/Reactive-Resume/issues) page
|
||||
2. Click **New Issue**
|
||||
3. Select the appropriate template or create a blank issue
|
||||
4. Title it something like: "Add [Language Name] to Reactive Resume"
|
||||
5. Include the language name and locale code (e.g., "Japanese - ja-JP") in the issue description.
|
||||
|
||||
Once approved, the language will be added to Crowdin and you can begin translating.
|
||||
|
||||
---
|
||||
|
||||
## When Will My Translations Appear?
|
||||
|
||||
Translations submitted on Crowdin are synced to the codebase periodically. Once merged, they will be included in the next release of Reactive Resume.
|
||||
|
||||
<Info>
|
||||
There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on the release cycle.
|
||||
</Info>
|
||||
|
||||
---
|
||||
|
||||
## Tips for Effective Translation
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Use Context" icon="eye">
|
||||
Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app.
|
||||
</Card>
|
||||
<Card title="Check Existing Translations" icon="check">
|
||||
Review translations by other contributors and vote for accurate ones to help maintain quality.
|
||||
</Card>
|
||||
<Card title="Ask Questions" icon="comment">
|
||||
Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors.
|
||||
</Card>
|
||||
<Card title="Stay Consistent" icon="book">
|
||||
Check the project glossary (if available) to ensure terminology is used consistently across the app.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Need Help?
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
icon="book-open"
|
||||
title="Crowdin Translator Docs"
|
||||
href="https://support.crowdin.com/for-translators/"
|
||||
>
|
||||
Official Crowdin documentation for translators.
|
||||
</Card>
|
||||
<Card
|
||||
icon="github"
|
||||
title="GitHub Issues"
|
||||
href="https://github.com/AmruthPillai/Reactive-Resume/issues"
|
||||
>
|
||||
Report issues or request new languages.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
Thank you for helping make Reactive Resume accessible to users worldwide!
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Reactive Resume",
|
||||
"favicon": "/favicon.svg",
|
||||
"description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!",
|
||||
"seo": {
|
||||
"indexing": "all",
|
||||
"metatags": {
|
||||
"canonical": "https://docs.rxresu.me"
|
||||
}
|
||||
},
|
||||
"fonts": {
|
||||
"heading": {
|
||||
"family": "IBM Plex Sans"
|
||||
},
|
||||
"body": {
|
||||
"family": "IBM Plex Sans"
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#166534",
|
||||
"light": "#07C983",
|
||||
"dark": "#15803D"
|
||||
},
|
||||
"navigation": {
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": [
|
||||
"getting-started/index",
|
||||
"getting-started/quickstart",
|
||||
"guides/accessing-the-previous-version",
|
||||
"getting-started/changelog"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Account",
|
||||
"pages": [
|
||||
"guides/creating-an-account",
|
||||
"guides/updating-your-profile",
|
||||
"guides/linking-social-accounts",
|
||||
"guides/deleting-your-account"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Resume Builder",
|
||||
"pages": [
|
||||
"guides/creating-your-first-resume",
|
||||
"guides/choosing-a-template",
|
||||
"guides/exporting-your-resume",
|
||||
"guides/sharing-your-resume-publicly",
|
||||
"guides/using-private-notes",
|
||||
"guides/using-custom-css"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Security",
|
||||
"pages": ["guides/setting-up-two-factor-authentication", "guides/setting-up-passkeys"]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": ["guides/using-the-api", "guides/using-ai", "guides/json-resume-schema"]
|
||||
},
|
||||
{
|
||||
"group": "Self-hosting",
|
||||
"pages": ["guides/self-hosting-with-docker"]
|
||||
},
|
||||
{
|
||||
"group": "Contributing",
|
||||
"pages": ["contributing/architecture", "contributing/development", "contributing/translations"]
|
||||
},
|
||||
{
|
||||
"group": "Legal",
|
||||
"pages": ["legal/license", "legal/privacy-policy", "legal/terms-of-service"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"openapi": "/spec.json"
|
||||
}
|
||||
],
|
||||
"global": {
|
||||
"anchors": [
|
||||
{
|
||||
"icon": "link",
|
||||
"anchor": "Reactive Resume",
|
||||
"href": "https://rxresu.me"
|
||||
},
|
||||
{
|
||||
"icon": "github",
|
||||
"anchor": "Source Code",
|
||||
"href": "https://github.com/AmruthPillai/Reactive-Resume"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"logo": {
|
||||
"light": "/logo/light.svg",
|
||||
"dark": "/logo/dark.svg"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "Support",
|
||||
"href": "https://github.com/AmruthPillai/Reactive-Resume/issues"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Get Started",
|
||||
"href": "https://rxresu.me"
|
||||
}
|
||||
},
|
||||
"contextual": {
|
||||
"options": ["copy", "view"]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"website": "https://rxresu.me",
|
||||
"github": "https://github.com/AmruthPillai/Reactive-Resume"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M173.611 166.311L132.877 219.804H173.524L193.973 191.813L213.183 219.804H256L215.673 165.707L215.15 165.046L207.461 155.332L195.329 140.004L195.258 139.915L193.813 138.089L193.923 138.001L176.286 112.861H134.061L173.611 166.311ZM199.89 133.554L214.959 112.861H254.619L219.874 158.8L199.89 133.554Z" fill="#FAFAFA"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 36.1959V174.314H39.0678V137.614H60.3938L60.4323 137.671C60.8436 137.653 61.2517 137.634 61.6567 137.614C75.0665 136.968 85.1471 135.549 96.385 131.385C96.7596 131.246 97.1355 131.104 97.5128 130.959L97.4591 130.881C105.816 126.86 112.331 121.344 117.006 114.331C122.005 106.702 124.504 97.6915 124.504 87.2997C124.504 76.7764 122.005 67.7 117.006 60.0706C112.007 52.3097 104.904 46.3903 95.6964 42.3125C86.62 38.2347 75.7679 36.1959 63.1399 36.1959H0ZM102.156 137.725L64.8705 144.175L85.4361 174.314H127.266L102.156 137.725ZM39.0678 107.426H60.7721C68.9277 107.426 74.9786 105.65 78.9248 102.098C83.0026 98.5465 85.0415 93.6137 85.0415 87.2997C85.0415 80.8542 83.0026 75.8556 78.9248 72.304C74.9786 68.7523 68.9277 66.9765 60.7721 66.9765H39.0678V107.426Z" fill="#FAFAFA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: "Changelog"
|
||||
description: "List of all notable changes and updates to Reactive Resume"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.0.0" description="23rd January 2026">
|
||||
## New Features
|
||||
- Brand new UI/UX and refreshed design.
|
||||
</Update>
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "Welcome to the documentation for Reactive Resume, a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume."
|
||||
---
|
||||
|
||||
<Frame>
|
||||
<img src="/images/banner.jpg" alt="Reactive Resume Banner" style={{ borderRadius: '0.75rem' }} />
|
||||
</Frame>
|
||||
|
||||
## What is Reactive Resume?
|
||||
|
||||
**Reactive Resume** is a free and open-source resume builder designed to simplify the process of creating, updating, and sharing your professional resume. Built with privacy as a core principle, it gives you complete control over your data while providing a powerful, modern experience.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Privacy First" icon="shield-check">
|
||||
Your data stays yours. No tracking, no ads, and complete transparency with our open-source codebase.
|
||||
</Card>
|
||||
<Card title="Beautiful Templates" icon="palette">
|
||||
Choose from professionally designed templates that make your resume stand out from the crowd.
|
||||
</Card>
|
||||
<Card title="Real-time Preview" icon="eye">
|
||||
See changes instantly as you type. What you see is exactly what you'll get when you export.
|
||||
</Card>
|
||||
<Card title="Export Anywhere" icon="file-export">
|
||||
Download your resume as PDF, share it via a unique link, or print it directly from your browser.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Key Features
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Completely Free & Open Source" icon="code-branch">
|
||||
Reactive Resume is licensed under MIT. You can use it for free, modify it, and even host your own instance. The entire codebase is available on [GitHub](https://github.com/AmruthPillai/Reactive-Resume).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Multiple Templates" icon="grid-2">
|
||||
Choose from a variety of professionally designed templates including Azurill, Bronzor, Chikorita, Ditto, Gengar, Glalie, Kakuna, Lapras, Leafish, Onyx, Pikachu, and Rhyhorn - each with unique layouts and styles.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Rich Text Editor" icon="text">
|
||||
Format your content with bold, italic, links, lists, and more using our intuitive rich text editor powered by Tiptap.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Multi-language Support" icon="globe">
|
||||
Reactive Resume supports multiple languages, making it accessible to users worldwide. Contribute translations to help us reach more people.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dark Mode" icon="moon">
|
||||
Work comfortably in any lighting condition with built-in dark mode support that's easy on your eyes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Self-hosting Ready" icon="server">
|
||||
Deploy your own instance of Reactive Resume using Docker. Keep complete control over your data and infrastructure.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Getting Started
|
||||
|
||||
Whether you want to use our hosted version or run your own instance, Reactive Resume has got you covered.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Quickstart"
|
||||
icon="rocket"
|
||||
href="/getting-started/quickstart"
|
||||
>
|
||||
Get up and running in minutes with our hosted version or deploy your own instance.
|
||||
</Card>
|
||||
<Card
|
||||
title="Development Setup"
|
||||
icon="code"
|
||||
href="/contributing/development"
|
||||
>
|
||||
Set up a local development environment to contribute or customize Reactive Resume.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Tech Stack
|
||||
|
||||
Reactive Resume is built with modern web technologies:
|
||||
|
||||
| Category | Technology |
|
||||
|-------------------|------------|
|
||||
| Framework | TanStack Start (React 19, Vite) |
|
||||
| Runtime | Node.js |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL with Drizzle ORM |
|
||||
| API | ORPC (Type-safe RPC) |
|
||||
| Auth | Better Auth |
|
||||
| Styling | Tailwind CSS |
|
||||
| UI Components | Radix UI |
|
||||
| State Management | Zustand + TanStack Query |
|
||||
|
||||
## Community & Support
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="GitHub" icon="github" href="https://github.com/AmruthPillai/Reactive-Resume">
|
||||
Star the repo, report issues, and contribute to the project.
|
||||
</Card>
|
||||
<Card title="Reddit" icon="reddit" href="https://reddit.com/r/reactiveresume">
|
||||
Join our Reddit community to get help and connect with other users.
|
||||
</Card>
|
||||
<Card title="Discord" icon="discord" href="https://discord.gg/hzwkZbyvUW">
|
||||
Join our Discord server to get help and connect with other users.
|
||||
</Card>
|
||||
<Card title="Sponsor" icon="heart" href="https://opencollective.com/reactive-resume">
|
||||
Support the development and long-term sustainability of Reactive Resume.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
**Need help?** Feel free to reach out via [email](mailto:hello@amruthpillai.com) or open an issue on GitHub.
|
||||
</Note>
|
||||
@@ -0,0 +1,227 @@
|
||||
---
|
||||
title: "Quickstart"
|
||||
description: "Get started with Reactive Resume in minutes — use our hosted version or deploy your own instance"
|
||||
---
|
||||
|
||||
## Options
|
||||
|
||||
Reactive Resume offers flexibility in how you want to use it. Choose the option that best fits your needs:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Use the Cloud Version" icon="cloud" href="#use-the-cloud-version">
|
||||
The fastest way to get started. **Recommended for most users.**
|
||||
</Card>
|
||||
<Card title="Self-Host with Docker" icon="docker" href="#self-host-with-docker">
|
||||
Deploy your own instance with complete control. **Requires some technical knowledge.**
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Using the Cloud Version
|
||||
|
||||
The easiest way to use Reactive Resume is through our cloud version at [rxresu.me](https://rxresu.me). **This service is completely free and will always remain free.**
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an Account">
|
||||
Visit [rxresu.me](https://rxresu.me) and sign up for free using your email, or sign in with your GitHub or Google account.
|
||||
</Step>
|
||||
|
||||
<Step title="Create Your First Resume">
|
||||
Click the **Create Resume** button on your dashboard. Give your resume a name and select a template to get started.
|
||||
</Step>
|
||||
|
||||
<Step title="Fill in Your Details">
|
||||
Use our intuitive builder to add your:
|
||||
- Personal information
|
||||
- Work experience
|
||||
- Education
|
||||
- Skills
|
||||
- Projects
|
||||
- And more...
|
||||
</Step>
|
||||
|
||||
<Step title="Export & Share">
|
||||
When you're ready, export your resume as a PDF or share it via a unique public link.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
Your resume updates in real-time as you type. The preview panel shows exactly how your final PDF will look.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
|
||||
## Self-Host with Docker
|
||||
|
||||
For users who prefer complete control over their data, you can deploy Reactive Resume on your own infrastructure using Docker.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following installed:
|
||||
|
||||
- [Docker](https://docs.docker.com/get-docker/) (v20.10 or higher)
|
||||
- [Docker Compose](https://docs.docker.com/compose/install/) (v2.0 or higher)
|
||||
|
||||
<Info>
|
||||
There is <strong>no difference in features</strong> between the cloud-hosted version and the self-hosted option. Both provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs!
|
||||
</Info>
|
||||
|
||||
|
||||
### Quick Deployment
|
||||
|
||||
<Steps>
|
||||
<Step title="Clone the Repository">
|
||||
```bash
|
||||
git clone https://github.com/AmruthPillai/Reactive-Resume.git
|
||||
cd Reactive-Resume
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Environment Variables">
|
||||
Create a `.env` file in the root directory with the following variables:
|
||||
|
||||
```bash
|
||||
# Application
|
||||
APP_URL=http://localhost:3000
|
||||
|
||||
# Database
|
||||
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/postgres
|
||||
|
||||
# Authentication (generate a secure secret)
|
||||
AUTH_SECRET=your-secure-secret-key-here
|
||||
|
||||
# Storage (S3-compatible)
|
||||
S3_ACCESS_KEY_ID=seaweedfs
|
||||
S3_SECRET_ACCESS_KEY=seaweedfs
|
||||
S3_ENDPOINT=http://seaweedfs:8333
|
||||
S3_BUCKET=reactive-resume
|
||||
S3_FORCE_PATH_STYLE=true
|
||||
```
|
||||
|
||||
<Warning>
|
||||
For production deployments, always use strong, unique values for `AUTH_SECRET` and database credentials.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Start the Services">
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This will start:
|
||||
- **PostgreSQL** — Database for storing user data and resumes
|
||||
- **SeaweedFS** — S3-compatible storage for file uploads
|
||||
- **Gotenberg** — PDF generation service
|
||||
- **Reactive Resume** — The main application
|
||||
</Step>
|
||||
|
||||
<Step title="Access Your Instance">
|
||||
Once all services are running, access your Reactive Resume instance at:
|
||||
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Docker Compose Services
|
||||
|
||||
Here's what each service in the stack does:
|
||||
|
||||
| Service | Port | Description |
|
||||
|---------|------|-------------|
|
||||
| `postgres` | 5432 | PostgreSQL database for storing all application data |
|
||||
| `seaweedfs` | 8333 | S3-compatible object storage for file uploads |
|
||||
| `gotenberg` | 4000 | Headless Chrome service for PDF generation |
|
||||
| `app` | 3000 | The main Reactive Resume application |
|
||||
|
||||
### Health Checks
|
||||
|
||||
All services include built-in health checks. You can verify everything is running correctly:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
You should see all services with a `healthy` status.
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
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` |
|
||||
| `GOTENBERG_ENDPOINT` | URL of the Gotenberg PDF service | `http://gotenberg:4000` |
|
||||
|
||||
### Optional Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `PRINTER_APP_URL` | Public URL for Gotenberg to access the Application | — |
|
||||
| `GOTENBERG_USERNAME` | Gotenberg Basic Auth Username (if enabled) | — |
|
||||
| `GOTENBERG_PASSWORD` | Gotenberg Basic Auth Password (if enabled) | — |
|
||||
| `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_SIGNUP` | Disables new user signups | `false` |
|
||||
|
||||
> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused.
|
||||
|
||||
<Note>
|
||||
**Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the Gotenberg PDF service is running inside Docker. In this scenario, Gotenberg needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Development Setup"
|
||||
icon="code"
|
||||
href="/contributing/development"
|
||||
>
|
||||
Set up a development environment to contribute or customize Reactive Resume.
|
||||
</Card>
|
||||
<Card
|
||||
title="Project Architecture"
|
||||
icon="folder-open"
|
||||
href="/contributing/architecture"
|
||||
>
|
||||
Learn about the project structure and architecture.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Note>
|
||||
**Having trouble?** Check our [GitHub Issues](https://github.com/AmruthPillai/Reactive-Resume/issues) or reach out via [email](mailto:hello@amruthpillai.com).
|
||||
</Note>
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: "Accessing the Previous Version"
|
||||
description: "Learn how to access the previous version (v4) of Reactive Resume if you need to retrieve old resumes or prefer the classic experience."
|
||||
---
|
||||
|
||||
## The previous version is still available
|
||||
|
||||
If you've been using Reactive Resume for a while, you may have resumes saved in the previous version of the application. The good news is that **version 4 (v4) is still fully accessible** and will remain online for the foreseeable future.
|
||||
|
||||
<Info>
|
||||
The previous version of Reactive Resume is available at [https://v4.rxresu.me](https://v4.rxresu.me).
|
||||
</Info>
|
||||
|
||||
## Why keep the old version running?
|
||||
|
||||
There are several reasons why v4 remains available:
|
||||
|
||||
- **Access to existing resumes**: Users who created resumes in v4 can still access, edit, and export them
|
||||
- **Familiarity**: Some users may prefer the interface they're already comfortable with
|
||||
- **Transition time**: Moving to a new version takes time, and there's no rush
|
||||
|
||||
## How long will v4 be available?
|
||||
|
||||
The previous version will continue to run for as long as possible—until the maintainer runs out of breath or funds to keep the server active. There are no immediate plans to shut it down.
|
||||
|
||||
<Warning>
|
||||
While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version to benefit from new features, improvements, and ongoing support.
|
||||
</Warning>
|
||||
|
||||
## Accessing your v4 resumes
|
||||
|
||||
<Steps>
|
||||
<Step title="Visit the v4 application">
|
||||
Go to [https://v4.rxresu.me](https://v4.rxresu.me) in your browser.
|
||||
</Step>
|
||||
|
||||
<Step title="Log in with your existing credentials">
|
||||
Use the same account credentials you used when you originally created your resumes in v4.
|
||||
|
||||
<Tip>
|
||||
If you used social login (Google, GitHub, etc.) in v4, use the same method to sign in.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Access your resumes">
|
||||
Once logged in, you'll find all your previously created resumes in your dashboard, exactly as you left them.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Migrating to the new version
|
||||
|
||||
If you'd like to move your resumes to the latest version of Reactive Resume, you can export them from v4 and import them into the new version:
|
||||
|
||||
1. In v4, open the resume you want to migrate
|
||||
2. Export it as a JSON file
|
||||
3. In the new version at [https://rxresu.me](https://rxresu.me), create a new account or log in
|
||||
4. Use the import feature to upload your JSON file (select the "Reactive Resume v4 (JSON)" option)
|
||||
|
||||
<Info>
|
||||
Your existing resumes should already be available in the new version. If you don't see them, you can manually import them using the steps above.
|
||||
</Info>
|
||||
|
||||
## Questions or issues?
|
||||
|
||||
If you encounter any problems accessing v4 or have questions about migrating your resumes, feel free to open an issue on [GitHub](https://github.com/AmruthPillai/Reactive-Resume/issues).
|
||||
@@ -0,0 +1,156 @@
|
||||
---
|
||||
title: "Choosing a template"
|
||||
description: "Learn how to choose the perfect template for your resume and explore all available template designs in Reactive Resume."
|
||||
---
|
||||
|
||||
Reactive Resume offers a variety of unique templates, each with its own design aesthetic. This guide will help you understand the differences between templates and how to change your resume's template at any time.
|
||||
|
||||
## How to change your template
|
||||
|
||||
Changing your resume template is simple and can be done at any time without losing any of your content.
|
||||
|
||||
<Steps>
|
||||
<Step title="Open your resume in the builder">
|
||||
Navigate to your Dashboard and click on the resume you want to edit.
|
||||
|
||||
{/* TODO: Add screenshot of dashboard with resume cards */}
|
||||
<Frame caption="Your resumes dashboard showing all your resume cards">
|
||||
<img src="/images/choosing-template/dashboard-placeholder.jpg" alt="Dashboard showing resume cards" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Open the right sidebar">
|
||||
In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options.
|
||||
|
||||
{/* TODO: Add screenshot of builder with right sidebar visible */}
|
||||
<Frame caption="The resume builder with the right sidebar open">
|
||||
<img src="/images/choosing-template/builder-sidebar-placeholder.jpg" alt="Resume builder showing the right sidebar" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to the Template section">
|
||||
In the right sidebar, find and click on the **Template** section to expand it.
|
||||
|
||||
{/* TODO: Add screenshot of template section in sidebar */}
|
||||
<Frame caption="The Template section in the right sidebar">
|
||||
<img src="/images/choosing-template/template-section-placeholder.jpg" alt="Template section expanded in the sidebar" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Select your new template">
|
||||
Browse through the available templates and click on the one you want to use. Your resume will instantly update to reflect the new design.
|
||||
|
||||
{/* TODO: Add screenshot showing template selection */}
|
||||
<Frame caption="Selecting a new template from the available options">
|
||||
<img src="/images/choosing-template/template-selection-placeholder.jpg" alt="Template selection dropdown or grid" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Review and adjust">
|
||||
After changing the template, review your resume in the live preview. You may want to adjust spacing, colors, or layout to optimize for the new design.
|
||||
|
||||
<Tip>
|
||||
Different templates may display your content differently. Some templates work better with shorter content, while others are designed to handle more detailed information.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Tips for choosing the right template
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Consider your industry" icon="briefcase">
|
||||
Different industries have different expectations:
|
||||
- **Creative fields** (design, marketing, arts): Templates with more visual flair like Gengar or Pikachu
|
||||
- **Corporate/Traditional** (finance, law, consulting): Clean, minimal templates like Onyx or Ditto
|
||||
- **Tech/Startups**: Modern, balanced templates like Chikorita or Leafish
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Think about content length" icon="ruler">
|
||||
If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes, templates with more white space can make your content feel more substantial.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Match your personal brand" icon="palette">
|
||||
Your resume is part of your personal brand. Choose a template that reflects your personality while remaining professional and appropriate for your target roles.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Test with real content" icon="file-lines">
|
||||
Don't choose a template based on how it looks empty. Fill in your actual content and see how it flows across pages. What looks great with sample data might not work as well with your specific information.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
---
|
||||
|
||||
## Available templates
|
||||
|
||||
Reactive Resume includes a variety of professionally designed templates. Each template can be further customized with your choice of colors, fonts, and layout options.
|
||||
|
||||
<Info>
|
||||
All templates support the same features and sections. The difference is purely in how they present your information visually.
|
||||
</Info>
|
||||
|
||||
<Columns cols={2}>
|
||||
<Frame caption="Azurill">
|
||||
<img src="/images/templates/azurill.jpg" alt="Azurill template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Bronzor">
|
||||
<img src="/images/templates/bronzor.jpg" alt="Bronzor template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Chikorita">
|
||||
<img src="/images/templates/chikorita.jpg" alt="Chikorita template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Ditto">
|
||||
<img src="/images/templates/ditto.jpg" alt="Ditto template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Gengar">
|
||||
<img src="/images/templates/gengar.jpg" alt="Gengar template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Glalie">
|
||||
<img src="/images/templates/glalie.jpg" alt="Glalie template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Kakuna">
|
||||
<img src="/images/templates/kakuna.jpg" alt="Kakuna template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Lapras">
|
||||
<img src="/images/templates/lapras.jpg" alt="Lapras template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Leafish">
|
||||
<img src="/images/templates/leafish.jpg" alt="Leafish template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Onyx">
|
||||
<img src="/images/templates/onyx.jpg" alt="Onyx template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Pikachu">
|
||||
<img src="/images/templates/pikachu.jpg" alt="Pikachu template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
|
||||
<Frame caption="Rhyhorn">
|
||||
<img src="/images/templates/rhyhorn.jpg" alt="Rhyhorn template preview" style={{ aspectRatio: "210/297" }} />
|
||||
</Frame>
|
||||
</Columns>
|
||||
|
||||
---
|
||||
|
||||
## Customizing your template
|
||||
|
||||
After selecting a template, you can customize it further:
|
||||
|
||||
| Setting | Description |
|
||||
|---------|-------------|
|
||||
| **Colors** | Change the primary color scheme to match your personal brand |
|
||||
| **Typography** | Choose from various Google Fonts for headings and body text |
|
||||
| **Layout** | Adjust sidebar width, section order, and page margins |
|
||||
| **Spacing** | Fine-tune gaps between sections and elements |
|
||||
|
||||
<Tip>
|
||||
For more advanced customization options, check out [Using Custom CSS](/guides/using-custom-css).
|
||||
</Tip>
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: "Creating an account"
|
||||
description: "Learn how to create an account on Reactive Resume and get started on building your resume"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Visit the homepage">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and click on the <Badge>Get Started</Badge> button.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/creating-an-account/get-started-button.png" alt="Get Started button on homepage" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to the sign up page">
|
||||
You should see a link that says <Badge>Don't have an account? Create one now →</Badge>. Click on that link to go to the sign up page and you should see a form like this:
|
||||
|
||||
<Frame>
|
||||
<img src="/images/creating-an-account/create-account-link.png" alt="Create account link on login page" />
|
||||
</Frame>
|
||||
</Step>
|
||||
|
||||
<Step title="Fill in your details">
|
||||
Complete the sign up form with the following information:
|
||||
|
||||
- **Name**: Your full name
|
||||
- **Email Address**: A valid email address you have access to.
|
||||
- **Username**: Choose a unique username (this will be used in your public resume URLs)
|
||||
- **Password**: Create a strong password
|
||||
|
||||
<Frame>
|
||||
<img src="/images/creating-an-account/sign-up-form.png" alt="Sign up form" />
|
||||
</Frame>
|
||||
|
||||
<Warning>
|
||||
Make sure to choose a username you're happy with, as it will be part of your public resume URL (e.g., `rxresu.me/your-username/resume-slug`).
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Sign up and log in">
|
||||
After filling in all the required fields, click the **Sign Up** button. You will be immediately logged in and can start using Reactive Resume right away.
|
||||
|
||||
<Tip>
|
||||
No email verification is required to get started. However, it is highly recommended to verify your email address for account security.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Verify your email (recommended)">
|
||||
While email verification is optional, it is strongly recommended to verify your email address. This helps in:
|
||||
|
||||
- **Securing your account**: Ensures you have access to the email address associated with your account
|
||||
- **Password recovery**: Allows you to reset your password, in case you forget it
|
||||
|
||||
<Info>
|
||||
You can verify your email at any time from your account settings. Look for the verification prompt in your dashboard or navigate to **Settings → Profile**.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Access your dashboard">
|
||||
Click on **Continue** and you should be taken to your Dashboard, where you can:
|
||||
|
||||
- Create your first resume
|
||||
- Import an existing resume
|
||||
- Manage your account settings
|
||||
|
||||
<Frame>
|
||||
<img src="/images/creating-an-account/dashboard.png" alt="Reactive Resume dashboard" />
|
||||
</Frame>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Account Security Tips
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Use a strong password" icon="lock">
|
||||
Create a password that's at least 8 characters long and includes a mix of letters, numbers, and special characters.
|
||||
</Card>
|
||||
<Card title="Setup 2FA/Passkeys" icon="key">
|
||||
Setup two-factor authentication or passkeys on your account to add an extra layer of security.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Username Already Taken">
|
||||
If you see an error that your username is already taken, try a different variation. Usernames must be unique across all users.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email Already Registered">
|
||||
If your email is already registered, you can use the **Forgot Password** link on the login page to reset your password and regain access to your account.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Didn't Receive Verification Email">
|
||||
Check your spam folder first. If you still don't see it, you can request a new verification email from your account settings.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "Creating your first resume"
|
||||
description: "Learn how to create your first resume in Reactive Resume, set a name and slug, and open it in the builder."
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Log in to your account">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and log in with your account credentials.
|
||||
|
||||
<Tip>
|
||||
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Open the Resumes Dashboard">
|
||||
After logging in, navigate to your Dashboard where all of your resumes live.
|
||||
|
||||
<Info>
|
||||
This is where you can create, organize, and manage multiple resumes for different roles or versions.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Click “Create a new resume”">
|
||||
In the Resumes Dashboard, click on the <Badge>Create a new resume</Badge> card to open the creation form.
|
||||
</Step>
|
||||
|
||||
<Step title="Name your resume">
|
||||
Fill in the resume name. This can be a generic name (e.g., “General Resume”) or something tied to the position you’re applying for.
|
||||
|
||||
<Tip>
|
||||
If you can’t think of a name yet, click the <Badge>Magic Wand</Badge> button to generate a random name for you.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Review (or edit) the slug">
|
||||
The <Badge>Slug</Badge> field is auto-filled based on the name, but you can change it to anything you like.
|
||||
|
||||
<Warning>
|
||||
If you choose to publicly share your resume, it will be accessible at `https://rxresu.me/{username}/{slug}`.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Add tags (optional)">
|
||||
Add any <Badge>Tags</Badge> you want. Think of tags like folders or labels to help organize many resumes.
|
||||
|
||||
<Info>
|
||||
You can filter resumes by tags later from the Dashboard.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Open the resume in the builder">
|
||||
Once created, a new card for your resume will appear on the Dashboard. Click it to open the resume builder and start editing.
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Deleting your account"
|
||||
description: "Learn how to permanently delete your Reactive Resume account and all associated data"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Danger Zone">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on <Badge>Danger Zone</Badge>.
|
||||
</Step>
|
||||
|
||||
<Step title="Type the confirmation text">
|
||||
On the Danger Zone page, type <Badge>delete</Badge> into the confirmation input.
|
||||
|
||||
<Info>
|
||||
The <Badge>Delete Account</Badge> button will stay disabled until the confirmation text matches exactly.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Delete your account">
|
||||
Click <Badge>Delete Account</Badge>, then confirm the final prompt.
|
||||
|
||||
<Warning>
|
||||
This action cannot be undone. All your data will be permanently deleted.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="You will be signed out">
|
||||
After deletion completes, you will be signed out and redirected to the homepage.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Linking social accounts"
|
||||
description: "Learn how to connect or disconnect social sign-in providers like Google and GitHub from your account"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Find the social provider you want to link">
|
||||
On the Authentication page, you may see sections for one or more providers (for example, <Badge>Google</Badge> or <Badge>GitHub</Badge>), depending on what is enabled on your instance.
|
||||
</Step>
|
||||
|
||||
<Step title="Connect your account">
|
||||
Click the <Badge>Connect</Badge> button for the provider you want to link. You'll be redirected to the provider to authorize access, then returned to the Authentication settings page.
|
||||
|
||||
<Info>
|
||||
After a successful link, the button will change to <Badge>Disconnect</Badge>.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Disconnect a provider (optional)">
|
||||
To unlink a provider, click <Badge>Disconnect</Badge> next to the connected account.
|
||||
|
||||
<Warning>
|
||||
Before disconnecting, make sure you still have another way to sign in (for example, a password or another linked provider) so you don't get locked out.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
---
|
||||
title: "Self-hosting with Docker"
|
||||
description: "A comprehensive guide to self-host Reactive Resume with Docker (Postgres + Gotenberg), including a detailed environment variable reference and troubleshooting tips."
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Reactive Resume can be self-hosted using Docker in a matter of minutes, and this guide will walk you through the process. Here are some of the services you'll need to get started:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="PostgreSQL">
|
||||
Stores accounts, resumes, and application data.
|
||||
</Card>
|
||||
<Card title="Gotenberg">
|
||||
Generates PDFs by rendering a special print route.
|
||||
</Card>
|
||||
<Card title="Email (optional)">
|
||||
SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console.
|
||||
</Card>
|
||||
<Card title="Storage (optional)">
|
||||
Use S3-compatible storage, or local persistent storage via <code>/app/data</code>.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
You can pull the latest app image from:
|
||||
|
||||
- Docker Hub: `amruthpillai/reactive-resume:latest`
|
||||
- GitHub Container Registry: `ghcr.io/amruthpillai/reactive-resume:latest`
|
||||
|
||||
## Minimum requirements
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card title="Docker + Docker Compose">
|
||||
Docker Engine + Docker Compose plugin (or Docker Desktop).
|
||||
</Card>
|
||||
<Card title="Compute">
|
||||
2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Gotenberg run on the same host).
|
||||
</Card>
|
||||
<Card title="Storage">
|
||||
Enough for Postgres + uploads (start with 10-20 GB and scale as needed).
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Quickstart using Docker Compose
|
||||
|
||||
Create a new folder (for example `reactive-resume/`) with:
|
||||
|
||||
- `compose.yml`
|
||||
- `.env`
|
||||
- a persistent data directory for uploads (for example `./data`)
|
||||
|
||||
<Steps>
|
||||
<Step title="Create your .env">
|
||||
Start by creating a `.env` file next to your `compose.yml`.
|
||||
|
||||
```bash .env
|
||||
# --- Server ---
|
||||
TZ="Etc/UTC"
|
||||
APP_URL="http://localhost:3000"
|
||||
|
||||
# Optional, uses APP_URL by default
|
||||
# This can be set to a different URL (like http://host.docker.internal:3000 or http://{docker_service}:3000)
|
||||
# to let the browser navigate to a non-public instance of Reactive Resume
|
||||
PRINTER_APP_URL="http://host.docker.internal:3000"
|
||||
|
||||
# --- Printer ---
|
||||
GOTENBERG_ENDPOINT="http://gotenberg:3000"
|
||||
|
||||
# Gotenberg Authentication (Optional)
|
||||
# GOTENBERG_USERNAME=""
|
||||
# GOTENBERG_PASSWORD=""
|
||||
|
||||
# --- Database (PostgreSQL) ---
|
||||
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
|
||||
|
||||
# --- Authentication ---
|
||||
# Generated using `openssl rand -hex 32`
|
||||
AUTH_SECRET=""
|
||||
|
||||
# Social Auth (Google, optional)
|
||||
GOOGLE_CLIENT_ID=""
|
||||
GOOGLE_CLIENT_SECRET=""
|
||||
|
||||
# Social Auth (GitHub, optional)
|
||||
GITHUB_CLIENT_ID=""
|
||||
GITHUB_CLIENT_SECRET=""
|
||||
|
||||
# Custom OAuth Provider
|
||||
OAUTH_PROVIDER_NAME=""
|
||||
OAUTH_CLIENT_ID=""
|
||||
OAUTH_CLIENT_SECRET=""
|
||||
# Use EITHER discovery URL (preferred for OIDC-compliant providers):
|
||||
OAUTH_DISCOVERY_URL=""
|
||||
# OR manual URLs (all three required if not using discovery):
|
||||
OAUTH_AUTHORIZATION_URL=""
|
||||
OAUTH_TOKEN_URL=""
|
||||
OAUTH_USER_INFO_URL=""
|
||||
# Custom scopes (space-separated, defaults to "openid profile email")
|
||||
OAUTH_SCOPES=""
|
||||
|
||||
# --- 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_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.
|
||||
# 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=""
|
||||
S3_REGION="us-east-1"
|
||||
S3_ENDPOINT=""
|
||||
S3_BUCKET=""
|
||||
# Set to "true" for path-style URLs (https://endpoint/bucket), common with MinIO, SeaweedFS, etc.
|
||||
# Set to "false" for virtual-hosted-style URLs (https://bucket.endpoint), common with AWS S3, Cloudflare R2, etc.
|
||||
S3_FORCE_PATH_STYLE="false"
|
||||
|
||||
# --- Feature Flags ---
|
||||
FLAG_DEBUG_PRINTER="false"
|
||||
FLAG_DISABLE_SIGNUP="false"
|
||||
|
||||
# --- Others ---
|
||||
# GOOGLE_CLOUD_API_KEY=""
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Generate AUTH_SECRET">
|
||||
Generate a strong secret and paste it into `AUTH_SECRET`.
|
||||
<CodeGroup>
|
||||
```bash Linux/macOS
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
```bash Linux/macOS (alternative)
|
||||
head -c 32 /dev/urandom | hexdump -v -e '/1 "%02x"'
|
||||
```
|
||||
|
||||
```powershell Windows
|
||||
[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>
|
||||
|
||||
<Step title="Create compose.yml">
|
||||
This setup runs Postgres + Gotenberg + Reactive Resume on a private Docker network.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```yaml compose.yml
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
gotenberg:
|
||||
image: gotenberg/gotenberg:edge
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4000:3000"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
environment:
|
||||
- CHROMIUM_AUTO_START=true
|
||||
- LIBREOFFICE_DISABLE_ROUTES=true
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
app:
|
||||
image: amruthpillai/reactive-resume:latest
|
||||
# image: ghcr.io/amruthpillai/reactive-resume:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
# Used when S3 is not configured; keeps uploads persistent
|
||||
- ./data:/app/data
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
gotenberg:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3000/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\""]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<Tip>
|
||||
Prefer pulling from Docker Hub? Keep <code>amruthpillai/reactive-resume:latest</code>. Prefer GHCR? Swap it to <code>ghcr.io/amruthpillai/reactive-resume:latest</code>.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Start the stack">
|
||||
<CodeGroup>
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose logs -f reactive-resume
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
Reactive Resume should now be available at your `APP_URL` (for the example above: `http://localhost:3000`).
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## How startup works (database migrations)
|
||||
|
||||
<Info>
|
||||
On every start, the server <b>automatically runs database migrations</b> before serving traffic. If migrations fail (usually due to a DB connection issue), the container will exit with an error.
|
||||
</Info>
|
||||
|
||||
## Environment variables
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Required">
|
||||
<ul>
|
||||
<li><code>APP_URL</code></li>
|
||||
<li><code>DATABASE_URL</code></li>
|
||||
<li><code>GOTENBERG_ENDPOINT</code></li>
|
||||
<li><code>AUTH_SECRET</code></li>
|
||||
</ul>
|
||||
</Card>
|
||||
<Card title="Optional">
|
||||
<ul>
|
||||
<li>SMTP (<code>SMTP_*</code>)</li>
|
||||
<li>Social auth (<code>GOOGLE_*</code>, <code>GITHUB_*</code>)</li>
|
||||
<li>S3 storage (<code>S3_*</code>)</li>
|
||||
<li>Feature flags (<code>FLAG_*</code>)</li>
|
||||
</ul>
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Server">
|
||||
- **`TZ`**: Sets the container timezone (affects logs and server-side timestamps). Recommended: `Etc/UTC`.
|
||||
- **`APP_URL`**: Canonical/public URL for your instance (used for absolute URLs, redirects, and auth flows). If behind a reverse proxy, set this to your public HTTPS URL (for example, `https://resume.example.com`).
|
||||
- **`PRINTER_APP_URL`** (optional): Overrides the base URL used when rendering the print route for Gotenberg. Defaults to `APP_URL`. Useful when Gotenberg must access the app via a different internal URL (for example, `http://host.docker.internal:3000`).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Printer (Gotenberg)">
|
||||
- **`GOTENBERG_ENDPOINT`**: Base URL where Reactive Resume reaches Gotenberg. In Compose: `http://gotenberg:3000`.
|
||||
- **`GOTENBERG_USERNAME`** / **`GOTENBERG_PASSWORD`** (optional): Use if Gotenberg is configured with Basic Auth (recommended only if Gotenberg is reachable outside your private network).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database (PostgreSQL)">
|
||||
- **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`.
|
||||
- In Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Authentication">
|
||||
**`AUTH_SECRET`**: Secret used to secure authentication. Changing it invalidates existing sessions.
|
||||
|
||||
Generate with:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
**`GOOGLE_CLIENT_ID`** / **`GOOGLE_CLIENT_SECRET`** (optional): Enables Google sign-in.
|
||||
|
||||
**`GITHUB_CLIENT_ID`** / **`GITHUB_CLIENT_SECRET`** (optional): Enables GitHub sign-in.
|
||||
|
||||
**Custom OAuth provider** (optional):
|
||||
- **`OAUTH_PROVIDER_NAME`**: Display name in the UI
|
||||
- **`OAUTH_CLIENT_ID`** / **`OAUTH_CLIENT_SECRET`**: Required for any custom OAuth provider
|
||||
- **`OAUTH_SCOPES`**: Space-separated scopes (defaults to `openid profile email`)
|
||||
|
||||
Configure endpoints using **one** of these methods:
|
||||
- **Option A — OIDC Discovery (preferred)**: Set `OAUTH_DISCOVERY_URL` to your provider's `.well-known/openid-configuration` URL
|
||||
- **Option B — Manual URLs**: Set all three: `OAUTH_AUTHORIZATION_URL`, `OAUTH_TOKEN_URL`, and `OAUTH_USER_INFO_URL`
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email (SMTP, optional)">
|
||||
If SMTP is not configured, the app logs emails to the server console instead of sending them.
|
||||
|
||||
- **`SMTP_HOST`**: SMTP host (if empty, email sending is disabled).
|
||||
- **`SMTP_PORT`**: Usually `465` (implicit TLS) or `587` (STARTTLS).
|
||||
- **`SMTP_USER`** / **`SMTP_PASS`**: SMTP credentials.
|
||||
- **`SMTP_FROM`**: Default from address (for example, `Reactive Resume <noreply@rxresu.me>`).
|
||||
- **`SMTP_SECURE`**: `"true"` or `"false"` (string). Match your provider settings.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Storage (S3 or local)">
|
||||
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent storage (for example `./data:/app/data`) or uploads can be lost on container recreation.
|
||||
- **S3/S3-compatible**: Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.):
|
||||
- **`S3_ACCESS_KEY_ID`**
|
||||
- **`S3_SECRET_ACCESS_KEY`**
|
||||
- **`S3_REGION`**
|
||||
- **`S3_ENDPOINT`** (for S3-compatible providers; may be blank for AWS depending on your setup)
|
||||
- **`S3_BUCKET`**
|
||||
- **`S3_FORCE_PATH_STYLE`**: Controls how the bucket is addressed in URLs. Defaults to `"false"`.
|
||||
- Set to `"true"` for **path-style** URLs (`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services.
|
||||
- Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**, **Cloudflare R2**, and most cloud providers.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Feature Flags">
|
||||
- **`FLAG_DEBUG_PRINTER`**: Bypasses the printer-only access restriction (useful when debugging `/printer/{resumeId}`). Recommended: keep `"false"` in production.
|
||||
- **`FLAG_DISABLE_SIGNUP`**: Disables new signups (web app and server). Useful for private instances.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Updating your installation
|
||||
|
||||
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.
|
||||
```bash
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
2. **Restart the containers** to run the new images.
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
3. **(Optional) Remove old, unused Docker images** to free up disk space.
|
||||
```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.
|
||||
|
||||
## Backups (recommended)
|
||||
|
||||
Regular backups are essential to protect your data. Reactive Resume stores data in two places: the PostgreSQL database and file uploads (either local storage or S3).
|
||||
|
||||
### Database backups
|
||||
|
||||
Your PostgreSQL database contains all user accounts, resumes, and application data. For self-hosted deployments, you can use `pg_dump` to create periodic backups of your database and store them in a secure location. Many hosting providers also offer automated backup solutions for managed PostgreSQL instances, which handle scheduling, retention, and restoration for you.
|
||||
|
||||
### Upload backups
|
||||
|
||||
If you're using local storage (the `./data` directory), include this directory in your regular backup routine. A simple approach is to use `rsync` or a similar tool to copy the directory to a remote server or cloud storage.
|
||||
|
||||
If you're using S3-compatible storage, consider enabling versioning on your bucket to protect against accidental deletions. Most S3 providers also support lifecycle rules for automatic cleanup of old versions and cross-region replication for disaster recovery.
|
||||
|
||||
## 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.
|
||||
|
||||
### How it works
|
||||
|
||||
The Docker Compose configuration includes a health check that periodically calls the `/api/health` endpoint:
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3000/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))\""]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
When the health check fails, Docker marks the container as **unhealthy**. This status is visible when running `docker compose ps` or `docker ps`.
|
||||
|
||||
### Reverse proxy integration
|
||||
|
||||
Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream health checks) can use Docker's health status to make routing decisions:
|
||||
|
||||
- **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.
|
||||
|
||||
<Tip>
|
||||
If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy containers are excluded from routing without any additional configuration.
|
||||
</Tip>
|
||||
|
||||
### Manually checking health
|
||||
|
||||
You can manually verify the health of your Reactive Resume instance:
|
||||
|
||||
```bash
|
||||
# From outside the container
|
||||
curl -f http://localhost:3000/api/health
|
||||
|
||||
# Check Docker's health status
|
||||
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.
|
||||
|
||||
## 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:
|
||||
```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.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="PDF export fails / printing is stuck">
|
||||
- **Common cause**: Reactive Resume can't reach Gotenberg or Gotenberg can't reach your app.
|
||||
- **Checks**:
|
||||
- `GOTENBERG_ENDPOINT` should usually be `http://gotenberg:3000` in Compose.
|
||||
- If you use `PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for Gotenberg.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Uploads disappear after restart">
|
||||
- **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3).
|
||||
- **Fix**: add a volume mount like `./data:/app/data` and redeploy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Emails aren't being delivered">
|
||||
- **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead.
|
||||
- **Fix**: configure SMTP and verify your provider's TLS/port settings.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3 storage error: ENOTFOUND bucket.endpoint">
|
||||
- **Common cause**: The S3 client is using virtual-hosted-style addressing (prepending the bucket name to the endpoint), but your S3-compatible storage expects path-style addressing.
|
||||
- **Symptom**: Error message like `getaddrinfo ENOTFOUND mybucket.s3-server.com` when your endpoint is `s3-server.com`.
|
||||
- **Fix**: Set `S3_FORCE_PATH_STYLE="true"` in your environment. This is required for most self-hosted S3-compatible services like MinIO, SeaweedFS, etc.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: "Setting up passkeys"
|
||||
description: "Learn how to register passkeys (WebAuthn) to sign in securely using biometrics or your device PIN"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Register a new device">
|
||||
In the <Badge>Passkeys</Badge> section, click on the <Badge>Register New Device</Badge> button.
|
||||
</Step>
|
||||
|
||||
<Step title="Name your passkey">
|
||||
You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for example, “MacBook Touch ID” or “iPhone Face ID”).
|
||||
</Step>
|
||||
|
||||
<Step title="Complete the passkey prompt">
|
||||
Your browser or device will show a passkey prompt (WebAuthn). Follow the on-screen instructions to register your passkey using your biometric (Face ID / Touch ID / fingerprint) or device PIN.
|
||||
|
||||
<Info>
|
||||
Passkeys are tied to your device (or password manager) and are a more secure alternative to passwords.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Manage passkeys (optional)">
|
||||
After registering, your passkey will show up in the list. You can delete a passkey using the trash icon next to it.
|
||||
|
||||
<Warning>
|
||||
Deleting a passkey cannot be undone. After deletion, you won't be able to sign in using that passkey anymore.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: "Setting up two-factor authentication"
|
||||
description: "Learn how to enable two-factor authentication (2FA) to add an extra layer of security to your Reactive Resume account"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Ensure you have a password set">
|
||||
In order to see the option to set up two-factor authentication, you need to set a password. If you haven't set a password yet, possibly because you created your account through signing in with Google or GitHub, you will not be able to follow along this guide.
|
||||
|
||||
<Warning>
|
||||
Two-factor authentication requires a password to be set on your account. If you signed up using a social provider (Google or GitHub), you'll need to set a password first from the Authentication settings page.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
|
||||
<Tip>
|
||||
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Authentication settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Authentication</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Click on Enable 2FA">
|
||||
On the Authentication page, find the <Badge>Two-Factor Authentication</Badge> section and click on the <Badge>Enable 2FA</Badge> button.
|
||||
</Step>
|
||||
|
||||
<Step title="Enter your password">
|
||||
You will be asked to re-enter your password to authenticate that it's really you. Enter your password and click continue.
|
||||
|
||||
<Info>
|
||||
This password verification step ensures that only authorized users can enable two-factor authentication on your account.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Scan the QR code">
|
||||
After entering your password, you should be shown a QR code. Scan it with your authenticator device and enter the 6-digit code that the app provides to continue.
|
||||
|
||||
<Tip>
|
||||
Popular authenticator apps you can use include:
|
||||
- **Google Authenticator** - Available on iOS and Android
|
||||
- **Microsoft Authenticator** - Available on iOS and Android
|
||||
- **Authy** - Available on iOS, Android, and desktop
|
||||
- **1Password** - Available on multiple platforms
|
||||
- **LastPass Authenticator** - Available on iOS and Android
|
||||
</Tip>
|
||||
|
||||
<Info>
|
||||
You can also copy the secret key above the QR code and paste it into your authenticator app if you prefer manual entry.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Save your backup codes">
|
||||
Once verified, you'll be prompted to save backup codes. You can use each code only once, and they're useful if you ever happen to lose access to your authenticator device.
|
||||
|
||||
<Warning>
|
||||
Make sure to copy and store these backup codes in a safe place. If you lose your authenticator device and don't have backup codes, you may lose access to your account.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Sign in with 2FA">
|
||||
The next time you sign in, you will be asked for a one-time code from your authenticator app. Enter the 6-digit code displayed in your authenticator app to complete the sign-in process.
|
||||
|
||||
<Tip>
|
||||
If you've lost access to your authenticator device, you can use one of your backup codes to sign in. Remember that each backup code can only be used once.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -0,0 +1,213 @@
|
||||
---
|
||||
title: "Sharing your resume publicly"
|
||||
description: "Learn how to share your resume via a public URL, track views and downloads, and optionally protect your resume with a password."
|
||||
---
|
||||
|
||||
Reactive Resume lets you share your resume via a **public URL** that anyone can access. When you make your resume public, it becomes available at a unique link that you can share with recruiters, include in your portfolio, or add to your LinkedIn profile.
|
||||
|
||||
## Key benefits of public sharing
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Always up-to-date" icon="arrows-rotate">
|
||||
Viewers always see the latest version of your resume. No need to send new files when you make updates.
|
||||
</Card>
|
||||
<Card title="Track engagement" icon="chart-line">
|
||||
See how many times your resume has been viewed and downloaded.
|
||||
</Card>
|
||||
<Card title="Password protection" icon="lock">
|
||||
Optionally require a password so only people you trust can access your resume.
|
||||
</Card>
|
||||
<Card title="Easy to share" icon="share">
|
||||
A simple URL that works anywhere—email signatures, LinkedIn, portfolios, job applications.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## How to enable public sharing
|
||||
|
||||
<Steps>
|
||||
<Step title="Open your resume in the builder">
|
||||
Navigate to your resume in the resume builder.
|
||||
</Step>
|
||||
|
||||
<Step title="Go to the Sharing section">
|
||||
In the **right sidebar**, select **Sharing**.
|
||||
</Step>
|
||||
|
||||
<Step title="Toggle 'Allow Public Access'">
|
||||
Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL.
|
||||
</Step>
|
||||
|
||||
<Step title="Copy your public URL">
|
||||
Your public URL is displayed below the toggle. It follows this format:
|
||||
|
||||
```
|
||||
https://rxresu.me/{username}/{slug}
|
||||
```
|
||||
|
||||
Click the **copy** button to copy the URL to your clipboard.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Tip>
|
||||
The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details.
|
||||
</Tip>
|
||||
|
||||
## How the public URL works
|
||||
|
||||
When someone visits your public resume URL:
|
||||
|
||||
1. **They see the live version** - The page renders your current resume data with all your latest changes
|
||||
2. **No account required** - Visitors don't need a Reactive Resume account to view or download your resume
|
||||
3. **They can print or save as PDF** - Visitors can use their browser's print function to save a PDF copy
|
||||
4. **Views are tracked** - Each visit is counted in your resume statistics (see below)
|
||||
|
||||
<Info>
|
||||
Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your public resume is always in sync.
|
||||
</Info>
|
||||
|
||||
## Tracking views and downloads
|
||||
|
||||
When your resume is public, Reactive Resume tracks how many times it has been viewed and downloaded. This helps you understand engagement and see if people are actually looking at your resume.
|
||||
|
||||
### Where to find statistics
|
||||
|
||||
In the resume builder, open the **right sidebar** and select **Statistics**.
|
||||
|
||||
You'll see:
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------|
|
||||
| **Views** | Number of times your public resume page was visited |
|
||||
| **Last viewed** | The date when your resume was last viewed |
|
||||
| **Downloads** | Number of times your resume was downloaded (via the public page) |
|
||||
| **Last downloaded** | The date when your resume was last downloaded |
|
||||
|
||||
<Info>
|
||||
Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your existing stats are preserved.
|
||||
</Info>
|
||||
|
||||
### What counts as a view?
|
||||
|
||||
A view is counted each time someone loads your public resume page. This includes:
|
||||
|
||||
- Direct visits to your public URL
|
||||
- Clicks from links you've shared
|
||||
- Search engine visits (if your resume is indexed)
|
||||
|
||||
<Note>
|
||||
Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your resume has.
|
||||
</Note>
|
||||
|
||||
## Password protecting your resume
|
||||
|
||||
If you want to share your resume with specific people but keep it private from the general public, you can add password protection.
|
||||
|
||||
When password protection is enabled:
|
||||
|
||||
- Visitors must enter the correct password to view your resume
|
||||
- The password prompt appears before any resume content is shown
|
||||
- You can share the password separately with trusted individuals
|
||||
|
||||
### How to set a password
|
||||
|
||||
<Steps>
|
||||
<Step title="Enable public access">
|
||||
First, make sure **Allow Public Access** is turned on in the **Sharing** section.
|
||||
</Step>
|
||||
|
||||
<Step title="Click 'Set Password'">
|
||||
Below the public URL, click the **Set Password** button.
|
||||
</Step>
|
||||
|
||||
<Step title="Enter your password">
|
||||
Type a password (6-64 characters) and confirm. This password will be required to view your resume.
|
||||
</Step>
|
||||
|
||||
<Step title="Share the password separately">
|
||||
Share the password with your intended audience through a secure channel (e.g., direct message, email).
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
Choose a password you're comfortable sharing. Anyone with the password can view and download your resume.
|
||||
</Warning>
|
||||
|
||||
### How to remove password protection
|
||||
|
||||
If you no longer need password protection:
|
||||
|
||||
1. Go to the **Sharing** section in the right sidebar
|
||||
2. Click **Remove Password**
|
||||
3. Confirm the action
|
||||
|
||||
Your resume will become accessible to anyone with the public URL.
|
||||
|
||||
## Use cases for public sharing
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="LinkedIn profile" icon="linkedin">
|
||||
Add your public resume URL to your LinkedIn profile's **Featured** section or **Contact Info**. Recruiters can view your detailed resume directly.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Email signature" icon="envelope">
|
||||
Include your resume link in your email signature. Anyone you correspond with can quickly access your resume.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Portfolio website" icon="globe">
|
||||
Embed or link to your resume from your personal website. The link always shows your latest resume.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Job applications" icon="briefcase">
|
||||
Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Networking events" icon="users">
|
||||
Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Confidential job search" icon="lock">
|
||||
Use password protection to share your resume only with specific recruiters while keeping it hidden from your current employer.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Turning off public access
|
||||
|
||||
To make your resume private again:
|
||||
|
||||
1. Go to the **Sharing** section in the right sidebar
|
||||
2. Turn off the **Allow Public Access** switch
|
||||
|
||||
When public access is disabled:
|
||||
|
||||
- Your public URL returns a "not found" error
|
||||
- Existing links stop working immediately
|
||||
- Your statistics are preserved (they'll resume if you re-enable public access)
|
||||
- Password protection settings are preserved
|
||||
|
||||
<Tip>
|
||||
If you only want to temporarily hide your resume, consider using password protection instead of disabling public access entirely. This way, your URL remains active for people who have the password.
|
||||
</Tip>
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Can I customize my public URL?">
|
||||
Yes! The URL is based on your **username** and the resume's **slug**. You can change the slug in the **Update Resume** dialog. To open it, right-click on your resume card in the dashboard and select "Update". The username is set in your account settings.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Will search engines index my public resume?">
|
||||
Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your resume private.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do views from my own visits count?">
|
||||
Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged in to your account are not included in the view statistics.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I see who viewed my resume?">
|
||||
No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if I change my username?">
|
||||
Your public URL will change to reflect the new username. Old URLs will stop working immediately. You might need to update any links you've shared.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "Updating your profile"
|
||||
description: "Learn how to update your profile information including your name, username, and email address in Reactive Resume"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
|
||||
<Tip>
|
||||
If you haven't created an account yet, follow the guide on [Creating an Account](/guides/creating-an-account).
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Profile settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>Profile</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Update your information">
|
||||
On the Profile page, you can update the following information:
|
||||
|
||||
- **Name**: Your full name as it appears in your account
|
||||
- **Username**: Your unique username (used in public resume URLs)
|
||||
- **Email Address**: Your account email address
|
||||
|
||||
<Warning>
|
||||
If you update your email address, you will receive a verification link on your current email address. The change will only be accepted after you click on the verification link.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Verify email changes (if applicable)">
|
||||
If you've updated your email address, check your current email inbox for a verification link. Click on the link to confirm the email change.
|
||||
|
||||
<Tip>
|
||||
Make sure to check your spam folder if you don't see the verification email in your inbox. The verification link is required to complete the email address change.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
title: "Using Artificial Intelligence"
|
||||
description: "Learn how to configure an AI provider and API key for AI-assisted features in Reactive Resume"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to Artificial Intelligence settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on <Badge>Artificial Intelligence</Badge>.
|
||||
</Step>
|
||||
|
||||
<Step title="How your data is stored and sent">
|
||||
When you configure your AI provider settings on this page, all information (such as the API key and model) is stored locally in your browser and **not** in our database.
|
||||
|
||||
<Info>
|
||||
<strong>Your credentials and AI configuration are never saved on Reactive Resume servers.</strong> They only live in your device's browser storage.
|
||||
</Info>
|
||||
|
||||
However, whenever you use an AI-assisted feature (such as text generation), these credentials need to be sent to the server **temporarily and only for the duration of your request**. This is required to connect with the AI provider on your behalf. See below for a deeper explanation.
|
||||
</Step>
|
||||
|
||||
<Step title="Choose a provider">
|
||||
In the <Badge>Provider</Badge> field, choose one of the supported providers:
|
||||
|
||||
- **OpenAI**
|
||||
- **Google Gemini**
|
||||
- **Anthropic Claude**
|
||||
- **Ollama** (coming soon)
|
||||
</Step>
|
||||
|
||||
<Step title="Enter a model name">
|
||||
In the <Badge>Model</Badge> field, enter the model you want to use (for example, <code>gpt-5.2</code>, <code>claude-4.5-opus</code>, or <code>gemini-3-flash</code>).
|
||||
</Step>
|
||||
|
||||
<Step title="Paste your API key">
|
||||
In the <Badge>API Key</Badge> field, paste your provider API key.
|
||||
|
||||
<Warning>
|
||||
Treat this key like a password. Anyone with it can use your provider account and may incur costs.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Verify it's enabled">
|
||||
After setting the provider, model, and API key, the status indicator at the bottom of the page should show <Badge>Enabled</Badge>.
|
||||
|
||||
<Tip>
|
||||
If the status shows <Badge>Disabled</Badge>, double-check that all three fields are filled in.
|
||||
</Tip>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Why AI requests are sent through our server
|
||||
|
||||
Most AI providers (like OpenAI, Gemini, or Anthropic) block requests that come directly from the browser due to something called **CORS** ("Cross-Origin Resource Sharing"), a security mechanism built into web browsers.
|
||||
|
||||
**What is CORS?**
|
||||
CORS is a browser feature that prevents web pages from making requests to a different domain than the one that served the web page, unless that domain explicitly allows it. Most AI providers do not allow browser-based requests from random websites, primarily for security and abuse prevention.
|
||||
|
||||
Because of this restriction, the app has to **forward your AI requests (along with your credentials) through our server**, which then contacts the AI provider and returns the response to your browser. **Your credentials are not stored on our server; they are only used to make the request you initiated, and are not logged or retained.**
|
||||
|
||||
<Info>
|
||||
If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself.
|
||||
See the relevant implementation in <code>src/integrations/orpc/router/ai.ts</code> within the codebase.
|
||||
</Info>
|
||||
|
||||
|
||||
---
|
||||
|
||||
### Do you know a workaround?
|
||||
|
||||
If you have a solution or workaround for this CORS-related limitation, please consider raising an [issue or pull request on GitHub](https://github.com/reactive-resume/reactive-resume).
|
||||
@@ -0,0 +1,280 @@
|
||||
---
|
||||
title: "Using Custom CSS"
|
||||
description: "Learn how to use the Custom CSS panel in the resume builder, which selectors to target, and copy‑paste examples for common tweaks."
|
||||
---
|
||||
|
||||
## What the Custom CSS section does
|
||||
|
||||
The **Custom CSS** panel lets you write your own CSS rules to change how your resume looks in the live preview (and exports).
|
||||
|
||||
- **Live updates**: your changes are applied immediately as you type.
|
||||
- **Auto-save**: there is no “Save” button—your CSS is saved automatically when it changes.
|
||||
- **Scoped styling (mostly)**: to avoid affecting the rest of the app, your CSS is *usually* scoped to the resume preview.
|
||||
|
||||
## Where to find it
|
||||
|
||||
In the resume builder, open the **right sidebar** and select **Custom CSS** (the `CSS` section).
|
||||
|
||||
## Enable / Disable
|
||||
|
||||
- Turn on the **Enable** switch to apply your CSS.
|
||||
- Turning it off keeps your CSS saved, but stops applying it.
|
||||
|
||||
## How scoping works (important)
|
||||
|
||||
Your CSS is injected into the preview and most **top-level selectors** are automatically prefixed with:
|
||||
|
||||
- `.resume-preview-container`
|
||||
|
||||
That means:
|
||||
|
||||
- Writing `.page { ... }` effectively becomes `.resume-preview-container .page { ... }`
|
||||
- Writing `h2 { ... }` becomes `.resume-preview-container h2 { ... }`
|
||||
- Writing `body { ... }` becomes `.resume-preview-container body { ... }` (which usually matches nothing, because `body` is not inside the preview container)
|
||||
|
||||
### Scoping limitation: `@media` / `@supports` blocks
|
||||
|
||||
Rules that start with **at-rules** (like `@media print { ... }`) are **not automatically scoped**. If you want to keep those rules limited to the resume preview, you should **manually** prefix selectors inside at-rules with `.resume-preview-container`.
|
||||
|
||||
Example:
|
||||
|
||||
```css
|
||||
/* Normal rules: rely on auto-scoping */
|
||||
.page a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* At-rules: manually scope selectors */
|
||||
@media print {
|
||||
.resume-preview-container .page a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Formatting tip (to avoid scoping edge-cases)
|
||||
|
||||
Keep each rule’s selector on **one line**, for example:
|
||||
|
||||
```css
|
||||
.page-section-experience .section-item-title { font-weight: 700; }
|
||||
```
|
||||
|
||||
Avoid splitting a selector across multiple lines unless you’ve confirmed it behaves as expected.
|
||||
|
||||
## Finding the right selectors
|
||||
|
||||
### Use autocomplete in the editor
|
||||
|
||||
In the Custom CSS editor, type a `.` (dot). You’ll get suggestions for commonly-used class selectors, including:
|
||||
|
||||
- **Page-level**: `.page`, `.page-content`, `.page-header`, `.page-basics`, `.page-main`, `.page-sidebar`, `.page-picture`
|
||||
- **Section-level**: `.page-section`, `.section-content`
|
||||
- **Section types**: `.page-section-experience`, `.page-section-education`, `.page-section-projects`, etc.
|
||||
- **Generic item building blocks**: `.section-item`, `.section-item-header`, `.section-item-title`, `.section-item-description`, `.section-item-metadata`, `.section-item-link`, etc.
|
||||
- **Per-section item selectors**: `.experience-item`, `.skills-item`, `.profiles-item`, etc.
|
||||
- **Template wrapper**: `.template-azurill`, `.template-onyx`, `.template-gengar`, etc.
|
||||
|
||||
### Stable “starter selectors”
|
||||
|
||||
If you’re not sure where to start, these are usually safe:
|
||||
|
||||
- `.page` (the resume page container)
|
||||
- `.page-section` (each resume section)
|
||||
- `.section-item` (each item inside a section)
|
||||
- `.page-picture` (profile picture container)
|
||||
|
||||
### Target a specific template (optional)
|
||||
|
||||
To apply styles only on one template, prefix with the template wrapper:
|
||||
|
||||
```css
|
||||
.template-azurill .page-header {
|
||||
gap: 12pt;
|
||||
}
|
||||
```
|
||||
|
||||
### Target a specific page (optional)
|
||||
|
||||
Each page has a class like `.page-0`, `.page-1`, etc.
|
||||
|
||||
```css
|
||||
.page-0 .page-header {
|
||||
margin-bottom: 6pt;
|
||||
}
|
||||
```
|
||||
|
||||
### Target a custom section (optional)
|
||||
|
||||
Custom sections include a dynamic class: `.page-section-<sectionId>`.
|
||||
|
||||
```css
|
||||
.page-section-custom {
|
||||
border-top: 1pt solid color-mix(in srgb, var(--page-text-color) 15%, transparent);
|
||||
padding-top: 6pt;
|
||||
}
|
||||
```
|
||||
|
||||
If you need the exact `<sectionId>`, use your browser devtools on the resume preview to inspect the section element.
|
||||
|
||||
## Useful built-in CSS variables (you can override them)
|
||||
|
||||
The preview sets a number of CSS variables you can reuse or override:
|
||||
|
||||
- `--page-width`, `--page-height`
|
||||
- `--page-sidebar-width`
|
||||
- `--page-text-color`, `--page-primary-color`, `--page-background-color`
|
||||
- `--page-body-font-family`, `--page-body-font-size`, `--page-body-line-height`, `--page-body-font-weight`, `--page-body-font-weight-bold`
|
||||
- `--page-heading-font-family`, `--page-heading-font-size`, `--page-heading-line-height`, `--page-heading-font-weight`, `--page-heading-font-weight-bold`
|
||||
- `--page-margin-x`, `--page-margin-y`
|
||||
- `--page-gap-x`, `--page-gap-y`
|
||||
|
||||
Example (tighten spacing without changing your layout settings):
|
||||
|
||||
```css
|
||||
.page {
|
||||
--page-margin-x: 10pt;
|
||||
--page-margin-y: 10pt;
|
||||
--page-gap-y: 6pt;
|
||||
}
|
||||
```
|
||||
|
||||
## Common examples (copy/paste)
|
||||
|
||||
### 1) Make section headings bolder and more compact
|
||||
|
||||
```css
|
||||
.page-section > h6 {
|
||||
margin-bottom: 2pt;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
```
|
||||
|
||||
### 2) Add subtle dividers between sections
|
||||
|
||||
```css
|
||||
.page-section {
|
||||
padding-bottom: 8pt;
|
||||
border-bottom: 1pt solid color-mix(in srgb, var(--page-text-color) 12%, transparent);
|
||||
}
|
||||
|
||||
.page-section:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
```
|
||||
|
||||
### 3) Reduce the size of metadata (dates/locations)
|
||||
|
||||
```css
|
||||
.section-item-metadata {
|
||||
font-size: calc(var(--page-body-font-size) * 0.9pt);
|
||||
opacity: 0.75;
|
||||
}
|
||||
```
|
||||
|
||||
### 4) Improve readability of rich-text descriptions (bullets, spacing)
|
||||
|
||||
```css
|
||||
.section-item-description {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.section-item-description ul {
|
||||
margin-left: 12pt;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.section-item-description li {
|
||||
margin: 2pt 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 5) Style links like a “chip” (useful for project links)
|
||||
|
||||
```css
|
||||
.section-item-link a {
|
||||
display: inline-block;
|
||||
padding: 2pt 6pt;
|
||||
border-radius: 999pt;
|
||||
border: 1pt solid color-mix(in srgb, var(--page-primary-color) 45%, transparent);
|
||||
text-decoration: none;
|
||||
}
|
||||
```
|
||||
|
||||
### 6) Hide the profile picture (CSS-only)
|
||||
|
||||
```css
|
||||
.page-picture {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
### 7) Make the header more compact
|
||||
|
||||
```css
|
||||
.page-header {
|
||||
gap: 6pt;
|
||||
padding-bottom: 6pt;
|
||||
}
|
||||
|
||||
.basics-headline {
|
||||
opacity: 0.8;
|
||||
}
|
||||
```
|
||||
|
||||
### 8) Make skills look denser (better use of space)
|
||||
|
||||
```css
|
||||
.skills-item-name {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.skills-item-proficiency {
|
||||
margin-top: 1pt;
|
||||
}
|
||||
|
||||
.skills-item-keywords {
|
||||
display: block;
|
||||
opacity: 0.85;
|
||||
}
|
||||
```
|
||||
|
||||
### 9) Highlight a specific section (example: Experience)
|
||||
|
||||
```css
|
||||
.page-section-experience {
|
||||
background: color-mix(in srgb, var(--page-primary-color) 6%, transparent);
|
||||
border-radius: 8pt;
|
||||
padding: 8pt;
|
||||
}
|
||||
```
|
||||
|
||||
### 10) Print-only adjustments (manually scoped)
|
||||
|
||||
```css
|
||||
@media print {
|
||||
.resume-preview-container .page {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
|
||||
.resume-preview-container a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### My CSS doesn’t do anything
|
||||
|
||||
- Make sure **Enable** is turned on.
|
||||
- Prefer targeting `.page`, `.page-section`, and `.section-item` instead of `body`.
|
||||
- Use your browser devtools to **inspect** the preview and confirm the element/class names.
|
||||
|
||||
### My `@media` rules affect the whole app / don’t apply
|
||||
|
||||
At-rules aren’t auto-scoped. Prefix selectors inside them with `.resume-preview-container` as shown above.
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: "Using Private Notes"
|
||||
description: "Learn how to use the Private Notes section in Reactive Resume to keep track of job applications, company details, and other personal reminders for each resume."
|
||||
---
|
||||
|
||||
## What are Private Notes?
|
||||
|
||||
The **Private Notes** section is a dedicated space for storing personal information about a specific resume. This content is completely private and will never appear on your resume, whether viewed publicly or exported as a PDF.
|
||||
|
||||
Think of it as a built-in notebook attached to each resume where you can jot down anything relevant to your job search.
|
||||
|
||||
<Info>
|
||||
Private Notes are stored securely with your resume data and are only visible to you when editing your resume.
|
||||
</Info>
|
||||
|
||||
## Where to find it
|
||||
|
||||
In the resume builder, open the **right sidebar** and select **Notes** from the available sections.
|
||||
|
||||
## Use cases
|
||||
|
||||
Private Notes are designed to help you stay organized during your job search. Here are some practical ways to use them:
|
||||
|
||||
### Track job applications
|
||||
|
||||
Keep a record of where you've sent this particular resume:
|
||||
|
||||
- Company names and positions applied for
|
||||
- Application dates and deadlines
|
||||
- Recruiter or hiring manager contact information
|
||||
- Application status (submitted, interviewing, offer, rejected)
|
||||
|
||||
### Save job description links
|
||||
|
||||
Paste links to the original job postings so you can quickly reference them when preparing for interviews or following up.
|
||||
|
||||
<Tip>
|
||||
Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities into your notes as a backup.
|
||||
</Tip>
|
||||
|
||||
### Interview preparation
|
||||
|
||||
Store notes to help you prepare:
|
||||
|
||||
- Questions you want to ask the interviewer
|
||||
- Key points to highlight from your experience
|
||||
- Salary expectations and negotiation notes
|
||||
- Company research and talking points
|
||||
|
||||
### Version control reminders
|
||||
|
||||
If you maintain multiple versions of your resume, use notes to remind yourself:
|
||||
|
||||
- What makes this version unique
|
||||
- Which types of roles this resume is tailored for
|
||||
- What was changed from your base resume
|
||||
|
||||
### Follow-up reminders
|
||||
|
||||
Track your follow-up schedule:
|
||||
|
||||
- When you last followed up with a company
|
||||
- Next steps and deadlines
|
||||
- Response notes from recruiters
|
||||
|
||||
## How to use it
|
||||
|
||||
<Steps>
|
||||
<Step title="Open the Notes section">
|
||||
In the resume builder, click on **Notes** in the right sidebar to expand the section.
|
||||
</Step>
|
||||
|
||||
<Step title="Write your notes">
|
||||
Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more.
|
||||
</Step>
|
||||
|
||||
<Step title="Notes save automatically">
|
||||
Your notes are saved automatically as you type—there's no need to click a save button.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Privacy guarantee
|
||||
|
||||
<Warning>
|
||||
Private Notes are for your eyes only. They will **never** appear on:
|
||||
- Your exported PDF or other file formats
|
||||
- Your publicly shared resume link
|
||||
- Any printed version of your resume
|
||||
</Warning>
|
||||
|
||||
This makes it safe to store sensitive information like salary expectations, candid thoughts about opportunities, or personal reminders that you wouldn't want an employer to see.
|
||||
|
||||
## Tips for effective note-taking
|
||||
|
||||
- **Be consistent**: Use a similar format across all your resumes to make it easy to find information
|
||||
- **Date your entries**: Add dates when noting application submissions or follow-ups
|
||||
- **Keep it relevant**: Focus on information specific to this resume version and its target roles
|
||||
- **Update regularly**: Keep your notes current as your job search progresses
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "Using the API"
|
||||
description: "Learn how to create API keys and authenticate requests to the Reactive Resume API"
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in to the dashboard">
|
||||
Head over to [https://rxresu.me](https://rxresu.me) and sign in with your account credentials.
|
||||
</Step>
|
||||
|
||||
<Step title="Navigate to API Keys settings">
|
||||
Once you're in the dashboard, look for the sidebar navigation on the left. Under the <Badge>Settings</Badge> section, click on the <Badge>API Keys</Badge> link.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the API reference">
|
||||
On the API Keys page, click <Badge>API Documentation</Badge> to view the API reference.
|
||||
|
||||
<Tip>
|
||||
You can also open it directly here: [API Reference](https://docs.rxresu.me/api-reference).
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Create a new API key">
|
||||
Click <Badge>Create a new API key</Badge>. Fill in:
|
||||
|
||||
- **Name**: A label to help you identify what you use this key for
|
||||
- **Expires in**: How long the key should remain valid
|
||||
</Step>
|
||||
|
||||
<Step title="Copy the API key (important)">
|
||||
After creating the key, you'll be shown the secret key once. Copy it and store it securely.
|
||||
|
||||
<Warning>
|
||||
For security reasons, your API key is only displayed once. If you lose it, you must create a new one.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Authenticate requests with x-api-key">
|
||||
To authenticate API requests, include your key in the <Badge>x-api-key</Badge> header.
|
||||
|
||||
<Info>
|
||||
If you're self-hosting, replace <code>https://rxresu.me</code> with your instance URL. The API is served under <code>/api/openapi</code>.
|
||||
</Info>
|
||||
|
||||
```bash
|
||||
curl "https://rxresu.me/api/openapi/resume/list" \
|
||||
-H "x-api-key: YOUR_API_KEY"
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Delete an API key (optional)">
|
||||
In the API Keys list, click the trash icon next to a key and confirm deletion.
|
||||
|
||||
<Warning>
|
||||
Deleted API keys stop working immediately, and the action cannot be undone.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 261 KiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 274 KiB |
|
After Width: | Height: | Size: 310 KiB |
|
After Width: | Height: | Size: 266 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 301 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 319 KiB |
|
After Width: | Height: | Size: 241 KiB |
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: "License"
|
||||
description: "Reactive Resume is open-source software licensed under MIT."
|
||||
---
|
||||
|
||||
## Open Source License (MIT)
|
||||
|
||||
Reactive Resume is open-source software. The project is published under the **MIT License**, which allows you to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the software.
|
||||
|
||||
If you are running a modified/self-hosted instance, you may have additional notices or third-party licenses that apply (for example, fonts, icons, or other bundled assets).
|
||||
|
||||
For the upstream repository, see: [AmruthPillai/Reactive-Resume](https://github.com/AmruthPillai/Reactive-Resume)
|
||||
|
||||
---
|
||||
|
||||
## MIT License Text
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Amruth Pillai
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
title: "Privacy Policy"
|
||||
description: "How Reactive Resume handles personal data, cookies, uploads, and printing."
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This Privacy Policy explains how **Reactive Resume** (the “Service”) collects, uses, stores, and shares information when you use it.
|
||||
|
||||
Reactive Resume is open-source and can be operated in different ways (for example, by the official hosted service, or by an organization/self-hosted deployment). The specific data controller for your use depends on who operates the instance you are using.
|
||||
|
||||
- **Service Operator**: Amruth Pillai
|
||||
- **Contact**: hello@amruthpillai.com
|
||||
- **Service URL**: https://rxresu.me
|
||||
- **Effective date**: 2023-01-01
|
||||
|
||||
If you are self-hosting, **you** are the Service Operator and responsible for compliance (including configuring email delivery, storage, and logging appropriately).
|
||||
|
||||
<Note>
|
||||
**Note for self-hosted deployments:** If you are using the official hosted service at <code>rxresu.me</code>, the project's published support contact is <code>hello@amruthpillai.com</code>. Replace the placeholders above with the correct operator details for your deployment if you are self-hosting.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## What the Service Does
|
||||
|
||||
Reactive Resume is a resume builder that lets you:
|
||||
|
||||
- Create and edit resumes in a browser-based builder
|
||||
- Store resumes in an account, optionally mark them public, and share them via a link
|
||||
- Export/print resumes to PDF and generate preview screenshots
|
||||
- Upload files such as profile pictures (and other assets used in a resume)
|
||||
- Optionally configure AI features (e.g., using OpenAI/Gemini/Anthropic) from your own device
|
||||
|
||||
---
|
||||
|
||||
## Information We Collect
|
||||
|
||||
### Account information
|
||||
When you create an account or sign in, the Service stores:
|
||||
|
||||
- **Identity and profile**: name, email address, username/display username, optional profile image
|
||||
- **Authentication state**: whether email is verified; whether two-factor authentication is enabled
|
||||
|
||||
If you use social sign-in (e.g., Google, GitHub, or a custom OAuth provider), the Service stores identifiers and tokens needed to link and maintain that login.
|
||||
|
||||
### Authentication and security data
|
||||
To keep your account secure and keep you signed in, the Service stores:
|
||||
|
||||
- **Session data**: session token, session expiry, and (if provided) IP address and user agent
|
||||
- **Verification data**: values used for email verification, password reset, or email change flows
|
||||
- **Two-factor authentication**: a 2FA secret and backup codes (if you enable 2FA)
|
||||
- **Passkeys** (if you use them): public key, credential ID, device metadata, counters, and related fields
|
||||
|
||||
The Service Operator may also send **transactional emails** (for example, password reset or email verification). Depending on deployment, these emails may be delivered via an email provider or (in development/testing) the links may be logged to server output.
|
||||
|
||||
### Resume content
|
||||
When you create or import a resume, the Service stores the resume data you provide, which may include personal data such as:
|
||||
|
||||
- Contact details, location, summary
|
||||
- Employment, education, projects, links, and other resume sections
|
||||
- Any other content you add (including rich text)
|
||||
|
||||
Resumes may also have metadata such as tags, a slug, visibility (public/private), and an optional resume password (if you lock a resume).
|
||||
|
||||
### Public resume access and statistics
|
||||
If you publish a resume, other users may access it via its public link. The Service may also maintain simple statistics such as:
|
||||
|
||||
- View count and download count
|
||||
- Last viewed/downloaded timestamps
|
||||
|
||||
### Uploaded files (e.g., profile pictures)
|
||||
If you upload files, the Service stores them either:
|
||||
|
||||
- On the **local filesystem** of the server (default: under a `data/` directory), or
|
||||
- In **S3-compatible object storage**, if configured by the Service Operator
|
||||
|
||||
Depending on configuration, uploaded files may be publicly accessible (for example, some S3 configurations may default to public read access for uploaded objects). The Service Operator is responsible for selecting appropriate access controls for uploads.
|
||||
|
||||
### API keys created in the Service
|
||||
If the Service Operator enables API key functionality, the Service can store:
|
||||
|
||||
- API key metadata and rate limit counters
|
||||
- The API key value itself (as stored by the Service)
|
||||
|
||||
### Local-only preferences and settings
|
||||
Some settings are stored on your device:
|
||||
|
||||
- **Cookies**: UI preferences such as `theme` and `locale`
|
||||
- **Local storage**: some client-side state and, if you enable AI features, your **AI provider configuration and API key** may be stored in your browser's local storage
|
||||
|
||||
These local-only values are stored in your browser and are not necessarily transmitted to the Service Operator unless you choose to use related features.
|
||||
|
||||
---
|
||||
|
||||
## How We Use Information
|
||||
|
||||
We use the information above to:
|
||||
|
||||
- Provide and operate the Service (account access, resume editing, storage, sharing)
|
||||
- Authenticate users and prevent abuse/fraud (sessions, security logs/metadata)
|
||||
- Generate PDFs and screenshots you request
|
||||
- Maintain basic functionality such as localization and theme preferences
|
||||
- Provide support and respond to user requests (if you contact the Service Operator)
|
||||
|
||||
---
|
||||
|
||||
## Cookies and Similar Technologies
|
||||
|
||||
The Service uses cookies primarily for functionality:
|
||||
|
||||
- **Authentication cookies**: to keep you signed in
|
||||
- **Preference cookies**: theme (`theme`) and language (`locale`)
|
||||
|
||||
The Service does not include built-in behavioral advertising or third-party analytics by default. (For example, the authentication layer's built-in telemetry is disabled in this codebase.)
|
||||
|
||||
---
|
||||
|
||||
## Sharing and Third Parties
|
||||
|
||||
We share information only as needed to provide the Service:
|
||||
|
||||
### PDF generation and screenshots (Gotenberg)
|
||||
When you export to PDF or request a screenshot, the Service sends a request to a configured **Gotenberg** endpoint to render a resume URL.
|
||||
|
||||
Depending on your deployment, Gotenberg may be:
|
||||
|
||||
- Self-hosted and controlled by the Service Operator, or
|
||||
- Operated by a third party (in which case the third party will process the resume content for rendering)
|
||||
|
||||
### Storage providers (optional)
|
||||
If configured, uploaded files may be stored in an S3-compatible provider. In that case, the storage provider processes and stores file data on behalf of the Service Operator.
|
||||
|
||||
### OAuth providers (optional)
|
||||
If you sign in via OAuth (Google/GitHub/custom), those providers receive authentication requests and return profile information (such as email/name) to the Service, as permitted by your provider settings.
|
||||
|
||||
### AI providers (optional, user-supplied)
|
||||
If you enable AI features and provide your own API key, prompts and generated content may be sent to your selected AI provider (OpenAI, Google, Anthropic), according to your use of those features and the provider's policies.
|
||||
|
||||
---
|
||||
|
||||
## Data Retention
|
||||
|
||||
Retention depends on the Service Operator's configuration and your actions.
|
||||
|
||||
As a baseline:
|
||||
|
||||
- Account data and resumes are retained until you delete them (or your account is deleted).
|
||||
- Session and security data may be retained as needed for authentication and security.
|
||||
- Uploaded files are retained until deleted (for example, when you remove a picture or delete a resume/account).
|
||||
- Cached screenshot artifacts may be retained briefly (for example, minutes) for performance.
|
||||
|
||||
The Service Operator may also retain backups and logs for limited periods.
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
We take reasonable measures to protect data (authentication, access controls, and storage separation). No method of transmission or storage is 100% secure; you should use strong passwords and enable 2FA/passkeys where available.
|
||||
|
||||
If you are self-hosting, you are responsible for:
|
||||
|
||||
- Securing your infrastructure, database, and storage buckets
|
||||
- Using HTTPS and secure cookie settings
|
||||
- Configuring access controls for object storage (and avoiding unintended public access)
|
||||
|
||||
---
|
||||
|
||||
## International Transfers
|
||||
|
||||
If the Service Operator (or its vendors) stores or processes data in other countries, your information may be transferred internationally. The Service Operator is responsible for providing appropriate safeguards where required by law.
|
||||
|
||||
---
|
||||
|
||||
## Your Choices and Rights
|
||||
|
||||
Depending on your location, you may have rights to access, correct, delete, export, or restrict processing of your personal data. You can often exercise these rights directly in the Service (for example, by editing or deleting resumes), or by contacting the Service Operator.
|
||||
|
||||
---
|
||||
|
||||
## Children's Privacy
|
||||
|
||||
The Service is not intended for children under the age of 13 (or the minimum age required in your jurisdiction). If you believe a child has provided personal data, contact the Service Operator.
|
||||
|
||||
---
|
||||
|
||||
## Changes to This Policy
|
||||
|
||||
We may update this Privacy Policy from time to time. Changes will be posted on this page with an updated effective date.
|
||||
|
||||
---
|
||||
|
||||
## Contact
|
||||
|
||||
For privacy requests or questions, contact:
|
||||
|
||||
- **Service Operator**: Amruth Pillai
|
||||
- **Email**: hello@amruthpillai.com
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
title: "Terms of Service"
|
||||
description: "Terms and conditions for using Reactive Resume, including accounts, public resumes, uploads, and exports."
|
||||
---
|
||||
|
||||
## Agreement
|
||||
|
||||
These Terms of Service (the “Terms”) govern your access to and use of **Reactive Resume** (the “Service”). By using the Service, you agree to these Terms.
|
||||
|
||||
- **Service Operator**: Amruth Pillai
|
||||
- **Contact**: hello@amruthpillai.com
|
||||
- **Service URL**: https://rxresu.me
|
||||
- **Effective date**: 2023-01-01
|
||||
|
||||
If you do not agree, do not use the Service.
|
||||
|
||||
<Note>
|
||||
Replace the placeholders above with the correct operator details for your deployment. If you are using the official hosted service at <code>rxresu.me</code>, the project's published support contact is <code>hello@amruthpillai.com</code>.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## The Service
|
||||
|
||||
Reactive Resume is a resume builder that allows you to create, store, and share resumes, upload related assets, and export/print resumes (including generating PDFs and screenshots).
|
||||
|
||||
---
|
||||
|
||||
## Eligibility
|
||||
|
||||
You must be legally able to form a contract in your jurisdiction to use the Service. If you are using the Service on behalf of an organization, you represent that you are authorized to bind that organization to these Terms.
|
||||
|
||||
---
|
||||
|
||||
## Accounts and Security
|
||||
|
||||
- **Account information**: You agree to provide accurate information and keep it up to date.
|
||||
- **Credentials**: You are responsible for maintaining the confidentiality of your login credentials and for activities that occur under your account.
|
||||
- **Security features**: Where available, you should enable 2FA or passkeys to improve account security.
|
||||
|
||||
The Service Operator may suspend or terminate accounts to protect the Service, other users, or to comply with legal obligations.
|
||||
|
||||
---
|
||||
|
||||
## Your Content
|
||||
|
||||
### What you provide
|
||||
“Content” includes the resume data you enter or import (text, links, personal details, etc.) and any files you upload (e.g., profile pictures).
|
||||
|
||||
### Ownership
|
||||
You retain ownership of your Content.
|
||||
|
||||
### License to operate the Service
|
||||
You grant the Service Operator a limited license to host, store, process, and display your Content **only as necessary** to provide the Service (for example, to render your resume in the builder, generate PDFs, serve uploaded images, or show a public resume page you publish).
|
||||
|
||||
### Public resumes and sharing
|
||||
If you set a resume as **public** or share a link, you understand that anyone with access to that link may view (and potentially copy) the content you make available. You are responsible for what you choose to publish.
|
||||
|
||||
If you password-protect a resume, you are responsible for controlling distribution of that password.
|
||||
|
||||
---
|
||||
|
||||
## Acceptable Use
|
||||
|
||||
You agree not to:
|
||||
|
||||
- Use the Service for unlawful, harmful, or abusive activities
|
||||
- Attempt to gain unauthorized access to accounts, systems, or data
|
||||
- Upload malware, exploit code, or content designed to disrupt the Service
|
||||
- Use automated means to scrape, overload, or interfere with the Service without permission
|
||||
- Infringe intellectual property, privacy, or other rights of others
|
||||
|
||||
The Service Operator may remove Content or restrict access if needed to enforce these Terms or comply with law.
|
||||
|
||||
---
|
||||
|
||||
## Uploads, Storage, and Delivery
|
||||
|
||||
Uploaded files may be stored on the Service Operator's infrastructure and may be served back to you (and, if your resume is public, to others). Depending on configuration, storage may be local filesystem storage or S3-compatible object storage.
|
||||
|
||||
You represent that you have the rights necessary to upload and use any files and that doing so does not violate any law or third-party rights.
|
||||
|
||||
---
|
||||
|
||||
## Exports (PDF) and Screenshots
|
||||
|
||||
When you request a PDF export or screenshot, the Service may use a rendering service (commonly **Gotenberg**) to load a resume URL and generate the output.
|
||||
|
||||
Depending on the deployment, this rendering service may be operated by the Service Operator or a third party. You understand that resume content must be processed for rendering in order to provide the export/screenshot functionality.
|
||||
|
||||
---
|
||||
|
||||
## Third-Party Services (Optional)
|
||||
|
||||
The Service may integrate with third parties depending on configuration and your choices, including:
|
||||
|
||||
- OAuth providers (e.g., Google/GitHub/custom OAuth) for sign-in
|
||||
- Storage providers (S3-compatible)
|
||||
- AI providers (OpenAI, Google, Anthropic) if you enable AI features and provide your own API key
|
||||
|
||||
Your use of third-party services may be subject to their own terms and policies. The Service Operator is not responsible for third-party services outside its control.
|
||||
|
||||
---
|
||||
|
||||
## API Keys (If Enabled)
|
||||
|
||||
If the Service allows you to create API keys:
|
||||
|
||||
- You are responsible for keeping API keys secure and rotating/revoking them if compromised.
|
||||
- The Service Operator may enforce rate limits, quotas, and permissions.
|
||||
- Misuse of API keys may result in suspension or termination.
|
||||
|
||||
---
|
||||
|
||||
## Intellectual Property and Open Source
|
||||
|
||||
Reactive Resume is open-source software licensed under the **MIT License**. These Terms govern **use of the Service**, not the open-source license for the code.
|
||||
|
||||
See the docs [License page](/legal/license) for details.
|
||||
|
||||
---
|
||||
|
||||
## Termination
|
||||
|
||||
You may stop using the Service at any time. The Service Operator may suspend or terminate your access:
|
||||
|
||||
- To enforce these Terms
|
||||
- To address security risks or abuse
|
||||
- To comply with legal requirements
|
||||
|
||||
Upon termination, your ability to access Content may be removed. Retention/deletion of data depends on the Service Operator's policies and legal obligations.
|
||||
|
||||
---
|
||||
|
||||
## Disclaimers
|
||||
|
||||
THE SERVICE IS PROVIDED **“AS IS”** AND **“AS AVAILABLE”** WITHOUT WARRANTIES OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
||||
|
||||
The Service Operator does not guarantee that the Service will be uninterrupted, error-free, or that exports will always succeed.
|
||||
|
||||
---
|
||||
|
||||
## Limitation of Liability
|
||||
|
||||
TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICE OPERATOR WILL NOT BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR ANY LOSS OF PROFITS, DATA, OR GOODWILL, ARISING OUT OF OR RELATED TO YOUR USE OF (OR INABILITY TO USE) THE SERVICE.
|
||||
|
||||
TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICE OPERATOR'S TOTAL LIABILITY FOR ANY CLAIM ARISING OUT OF OR RELATING TO THE SERVICE WILL NOT EXCEED THE AMOUNT YOU PAID (IF ANY) TO USE THE SERVICE IN THE 12 MONTHS BEFORE THE EVENT GIVING RISE TO THE CLAIM.
|
||||
|
||||
---
|
||||
|
||||
## Indemnification
|
||||
|
||||
You agree to defend, indemnify, and hold harmless the Service Operator from and against claims, liabilities, damages, losses, and expenses arising from:
|
||||
|
||||
- Your Content
|
||||
- Your misuse of the Service
|
||||
- Your violation of these Terms
|
||||
|
||||
---
|
||||
|
||||
## Changes to These Terms
|
||||
|
||||
We may update these Terms from time to time. Updated Terms will be posted on this page with a new effective date. Continued use of the Service after changes become effective constitutes acceptance.
|
||||
|
||||
---
|
||||
|
||||
## Governing Law
|
||||
|
||||
These terms are governed by the laws of the **Federal Republic of Germany**.
|
||||
|
||||
For matters specifically relating to data protection, you may contact:
|
||||
**Berlin Commissioner for Data Protection and Freedom of Information**
|
||||
Friedrichstraße 219
|
||||
D-10969 Berlin
|
||||
|
||||
Conflict of laws principles do not apply.
|
||||
|
||||
---
|
||||
|
||||
## Contact
|
||||
|
||||
Questions about these Terms can be sent to:
|
||||
|
||||
- **Service Operator**: Amruth Pillai
|
||||
- **Email**: hello@amruthpillai.com
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M173.611 166.311L132.877 219.804H173.524L193.973 191.813L213.183 219.804H256L215.673 165.707L215.15 165.046L207.461 155.332L195.329 140.004L195.258 139.915L193.813 138.089L193.923 138.001L176.286 112.861H134.061L173.611 166.311ZM199.89 133.554L214.959 112.861H254.619L219.874 158.8L199.89 133.554Z" fill="#FAFAFA"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 36.1959V174.314H39.0678V137.614H60.3938L60.4323 137.671C60.8436 137.653 61.2517 137.634 61.6567 137.614C75.0665 136.968 85.1471 135.549 96.385 131.385C96.7596 131.246 97.1355 131.104 97.5128 130.959L97.4591 130.881C105.816 126.86 112.331 121.344 117.006 114.331C122.005 106.702 124.504 97.6915 124.504 87.2997C124.504 76.7764 122.005 67.7 117.006 60.0706C112.007 52.3097 104.904 46.3903 95.6964 42.3125C86.62 38.2347 75.7679 36.1959 63.1399 36.1959H0ZM102.156 137.725L64.8705 144.175L85.4361 174.314H127.266L102.156 137.725ZM39.0678 107.426H60.7721C68.9277 107.426 74.9786 105.65 78.9248 102.098C83.0026 98.5465 85.0415 93.6137 85.0415 87.2997C85.0415 80.8542 83.0026 75.8556 78.9248 72.304C74.9786 68.7523 68.9277 66.9765 60.7721 66.9765H39.0678V107.426Z" fill="#FAFAFA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M173.611 166.311L132.877 219.804H173.524L193.973 191.813L213.183 219.804H256L215.673 165.707L215.15 165.046L207.461 155.332L195.329 140.004L195.258 139.915L193.813 138.089L193.923 138.001L176.286 112.861H134.061L173.611 166.311ZM199.89 133.554L214.959 112.861H254.619L219.874 158.8L199.89 133.554Z" fill="#09090B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 36.1959V174.314H39.0678V137.614H60.3938L60.4323 137.671C60.8436 137.653 61.2518 137.634 61.6569 137.614C75.0665 136.968 85.1471 135.549 96.3849 131.385C96.7596 131.246 97.1355 131.104 97.5128 130.959L97.4591 130.881C105.816 126.86 112.331 121.344 117.006 114.331C122.005 106.702 124.504 97.6915 124.504 87.2997C124.504 76.7764 122.005 67.7 117.006 60.0706C112.007 52.3097 104.904 46.3903 95.6964 42.3125C86.62 38.2347 75.7678 36.1959 63.1399 36.1959H0ZM102.156 137.725L64.8705 144.175L85.4361 174.314H127.266L102.156 137.725ZM39.0678 107.426H60.7721C68.9277 107.426 74.9786 105.65 78.9248 102.098C83.0026 98.5465 85.0415 93.6137 85.0415 87.2997C85.0415 80.8542 83.0026 75.8556 78.9248 72.304C74.9786 68.7523 68.9277 66.9765 60.7721 66.9765H39.0678V107.426Z" fill="#09090B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |