Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ ORIGIN=$APP_URL
SYNC_FREQUENCY='* * * * *'
# Set to 'true' to include Junk and Trash folders in the email archive. Defaults to false.
ALL_INCLUSIVE_ARCHIVE=false
# Number of mailbox jobs that run concurrently in the ingestion worker. Increase on servers with more RAM.
INGESTION_WORKER_CONCURRENCY=5

# --- Docker Compose Service Configuration ---
# These variables are used by docker-compose.yml to configure the services. Leave them unchanged if you use Docker services for Postgresql, Valkey (Redis) and Meilisearch. If you decide to use your own instances of these services, you can substitute them with your own connection credentials.
Expand Down Expand Up @@ -43,7 +45,11 @@ REDIS_USER=notdefaultuser
# --- Storage Settings ---
# Choose your storage backend. Valid options are 'local' or 's3'.
STORAGE_TYPE=local
# The maximum request body size to accept in bytes including while streaming. The body size can also be specified with a unit suffix for kilobytes (K), megabytes (M), or gigabytes (G). For example, 512K or 1M. Defaults to 512kb. Or the value of Infinity if you don't want any upload limit.
# The maximum request body size the SvelteKit frontend server will accept (including file uploads via streaming).
# Accepts a numeric value in bytes, or a unit suffix: K (kilobytes), M (megabytes), G (gigabytes).
# Set to 'Infinity' to remove the limit entirely (recommended for archiving large PST/Mbox files).
# Examples: 512K, 100M, 5G, Infinity. Defaults to 512K if not set.
# For very large files (multi-GB), consider using the "Local Path" ingestion option which bypasses this limit entirely.
BODY_SIZE_LIMIT=100M

# --- Local Storage Settings ---
Expand Down
7 changes: 7 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { defineConfig } from 'vitepress';
import { useSidebar } from 'vitepress-openapi';
import spec from '../api/openapi.json';

export default defineConfig({
head: [
Expand Down Expand Up @@ -95,7 +97,12 @@ export default defineConfig({
{ text: 'Integrity Check', link: '/api/integrity' },
{ text: 'Search', link: '/api/search' },
{ text: 'Storage', link: '/api/storage' },
{ text: 'Upload', link: '/api/upload' },
{ text: 'Jobs', link: '/api/jobs' },
{ text: 'Users', link: '/api/users' },
{ text: 'IAM', link: '/api/iam' },
{ text: 'API Keys', link: '/api/api-keys' },
{ text: 'Settings', link: '/api/settings' },
],
},
{
Expand Down
19 changes: 19 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import DefaultTheme from 'vitepress/theme';
import type { EnhanceAppContext } from 'vitepress';
import { theme, useOpenapi } from 'vitepress-openapi/client';
import 'vitepress-openapi/dist/style.css';
import spec from '../../api/openapi.json';

export default {
...DefaultTheme,
enhanceApp({ app, router, siteData }: EnhanceAppContext) {
// Delegate to DefaultTheme first
DefaultTheme.enhanceApp?.({ app, router, siteData });

// Install vitepress-openapi theme: registers i18n plugin + all OA components
theme.enhanceApp({ app, router, siteData });

// Initialize the global OpenAPI spec
useOpenapi({ spec });
},
};
19 changes: 19 additions & 0 deletions docs/api/api-keys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
aside: false
---

# API Keys

Generate and manage API keys for programmatic access to the Open Archiver API. API keys are scoped to the user that created them and carry the same permissions as that user. The raw key value is only shown once at creation time.

## Generate an API Key

<OAOperation operationId="generateApiKey" />

## List API Keys

<OAOperation operationId="getApiKeys" />

## Delete an API Key

<OAOperation operationId="deleteApiKey" />
110 changes: 11 additions & 99 deletions docs/api/archived-email.md
Original file line number Diff line number Diff line change
@@ -1,107 +1,19 @@
# Archived Email Service API
---
aside: false
---

The Archived Email Service is responsible for retrieving archived emails and their details from the database and storage.
# Archived Email API

## Endpoints
Endpoints for retrieving and deleting archived emails. All endpoints require authentication and the appropriate `archive` permission.

All endpoints in this service require authentication.
## List Emails for an Ingestion Source

### GET /api/v1/archived-emails/ingestion-source/:ingestionSourceId
<OAOperation operationId="getArchivedEmails" />

Retrieves a paginated list of archived emails for a specific ingestion source.
## Get a Single Email

**Access:** Authenticated
<OAOperation operationId="getArchivedEmailById" />

#### URL Parameters
## Delete an Email

| Parameter | Type | Description |
| :------------------ | :----- | :------------------------------------------------ |
| `ingestionSourceId` | string | The ID of the ingestion source to get emails for. |

#### Query Parameters

| Parameter | Type | Description | Default |
| :-------- | :----- | :------------------------------ | :------ |
| `page` | number | The page number for pagination. | 1 |
| `limit` | number | The number of items per page. | 10 |

#### Responses

- **200 OK:** A paginated list of archived emails.

```json
{
"items": [
{
"id": "email-id",
"subject": "Test Email",
"from": "[email protected]",
"sentAt": "2023-10-27T10:00:00.000Z",
"hasAttachments": true,
"recipients": [{ "name": "Recipient 1", "email": "[email protected]" }]
}
],
"total": 100,
"page": 1,
"limit": 10
}
```

- **500 Internal Server Error:** An unexpected error occurred.

### GET /api/v1/archived-emails/:id

Retrieves a single archived email by its ID, including its raw content and attachments.

**Access:** Authenticated

#### URL Parameters

| Parameter | Type | Description |
| :-------- | :----- | :---------------------------- |
| `id` | string | The ID of the archived email. |

#### Responses

- **200 OK:** The archived email details.

```json
{
"id": "email-id",
"subject": "Test Email",
"from": "[email protected]",
"sentAt": "2023-10-27T10:00:00.000Z",
"hasAttachments": true,
"recipients": [{ "name": "Recipient 1", "email": "[email protected]" }],
"raw": "...",
"attachments": [
{
"id": "attachment-id",
"filename": "document.pdf",
"mimeType": "application/pdf",
"sizeBytes": 12345
}
]
}
```

- **404 Not Found:** The archived email with the specified ID was not found.
- **500 Internal Server Error:** An unexpected error occurred.

## Service Methods

### `getArchivedEmails(ingestionSourceId: string, page: number, limit: number): Promise<PaginatedArchivedEmails>`

Retrieves a paginated list of archived emails from the database for a given ingestion source.

- **ingestionSourceId:** The ID of the ingestion source.
- **page:** The page number for pagination.
- **limit:** The number of items per page.
- **Returns:** A promise that resolves to a `PaginatedArchivedEmails` object.

### `getArchivedEmailById(emailId: string): Promise<ArchivedEmail | null>`

Retrieves a single archived email by its ID, including its raw content and attachments.

- **emailId:** The ID of the archived email.
- **Returns:** A promise that resolves to an `ArchivedEmail` object or `null` if not found.
<OAOperation operationId="deleteArchivedEmail" />
87 changes: 11 additions & 76 deletions docs/api/auth.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,19 @@
# Auth Service API
---
aside: false
---

The Auth Service is responsible for handling user authentication, including login and token verification.
# Auth API

## Endpoints
Handles user authentication including initial setup, login, and application setup status.

### POST /api/v1/auth/login
## Setup

Authenticates a user and returns a JWT if the credentials are valid.
<OAOperation operationId="authSetup" />

**Access:** Public
## Login

**Rate Limiting:** This endpoint is rate-limited to prevent brute-force attacks.
<OAOperation operationId="authLogin" />

#### Request Body
## Check Setup Status

| Field | Type | Description |
| :--------- | :----- | :------------------------ |
| `email` | string | The user's email address. |
| `password` | string | The user's password. |

#### Responses

- **200 OK:** Authentication successful.

```json
{
"accessToken": "your.jwt.token",
"user": {
"id": "user-id",
"email": "[email protected]",
"role": "user"
}
}
```

- **400 Bad Request:** Email or password not provided.

```json
{
"message": "Email and password are required"
}
```

- **401 Unauthorized:** Invalid credentials.

```json
{
"message": "Invalid credentials"
}
```

- **500 Internal Server Error:** An unexpected error occurred.

```json
{
"message": "An internal server error occurred"
}
```

## Service Methods

### `verifyPassword(password: string, hash: string): Promise<boolean>`

Compares a plain-text password with a hashed password to verify its correctness.

- **password:** The plain-text password.
- **hash:** The hashed password to compare against.
- **Returns:** A promise that resolves to `true` if the password is valid, otherwise `false`.

### `login(email: string, password: string): Promise<LoginResponse | null>`

Handles the user login process. It finds the user by email, verifies the password, and generates a JWT upon successful authentication.

- **email:** The user's email.
- **password:** The user's password.
- **Returns:** A promise that resolves to a `LoginResponse` object containing the `accessToken` and `user` details, or `null` if authentication fails.

### `verifyToken(token: string): Promise<AuthTokenPayload | null>`

Verifies the authenticity and expiration of a JWT.

- **token:** The JWT string to verify.
- **Returns:** A promise that resolves to the token's `AuthTokenPayload` if valid, otherwise `null`.
<OAOperation operationId="authStatus" />
35 changes: 25 additions & 10 deletions docs/api/authentication.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
---
aside: false
---

# API Authentication

To access protected API endpoints, you need to include a user-generated API key in the `X-API-KEY` header of your requests.
The API supports two authentication methods. Use whichever fits your use case.

## 1. Creating an API Key
## Method 1: JWT (User Login)

You can create, manage, and view your API keys through the application's user interface.
Obtain a short-lived JWT by calling `POST /v1/auth/login` with your email and password, then pass it as a Bearer token in the `Authorization` header.

1. Navigate to **Settings > API Keys** in the dashboard.
2. Click the **"Generate API Key"** button.
3. Provide a descriptive name for your key and select an expiration period.
4. The new API key will be displayed. **Copy this key immediately and store it in a secure location. You will not be able to see it again.**
**Example:**

## 2. Making Authenticated Requests
```http
GET /api/v1/dashboard/stats
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

Once you have your API key, you must include it in the `X-API-KEY` header of all subsequent requests to protected API endpoints.
## Method 2: API Key

Long-lived API keys are suited for automated scripts and integrations. Create one in **Settings > API Keys**, then pass it in the `X-API-KEY` header.

**Example:**

Expand All @@ -22,4 +28,13 @@ GET /api/v1/dashboard/stats
X-API-KEY: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```

If the API key is missing, expired, or invalid, the API will respond with a `401 Unauthorized` status code.
### Creating an API Key

1. Navigate to **Settings > API Keys** in the dashboard.
2. Click **"Generate API Key"**.
3. Provide a descriptive name and select an expiration period (max 2 years).
4. Copy the key immediately — it will not be shown again.

---

If the token or API key is missing, expired, or invalid, the API responds with `401 Unauthorized`.
Loading
Loading