feat(script): ensure local database directory exists before running scripts (#337)

This commit is contained in:
Corentin Thomasset
2025-06-07 17:26:28 +02:00
committed by GitHub
parent ff830c234a
commit 1c574b8305
9 changed files with 91 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
"@papra/app-server": patch
---
Ensure database directory exists when running scripts (like migrations)

View File

@@ -0,0 +1,5 @@
---
"@papra/docs": minor
---
Added troubleshooting page

View File

@@ -0,0 +1,5 @@
---
"@papra/docs": patch
---
Added `docker compose up` command in dc generator

View File

@@ -0,0 +1,28 @@
---
title: Troubleshooting
description: Troubleshooting guide for Papra
slug: resources/troubleshooting
---
You can find here some common issues and how to fix them. If you encounter an issue that is not listed here, please [open an issue](https://github.com/papra-hq/papra/issues/new/choose) or [join our Discord](https://papra.app/discord).
## Failed to ensure that the database directory exists
Upon starting the server or a script, you may encounter this error
```
Failed to ensure that the database directory exists, error while creating the directory
Error: EACCES: permission denied, mkdir './app-data/db'
```
Before accessing the DB sqlite file, the server will try to ensure that the database directory exists, and if it doesn't, it try will create it. But in case of insufficient permissions, it will fail.
To fix this, you can either:
- Create the directory manually `mkdir -p <your-app-data-dir>/db`
- Ensure that the directory is owned by the user running the container
- Run the server as root (not recommended)

View File

@@ -40,6 +40,10 @@ export const sidebar: StarlightUserConfig['sidebar'] = [
{
label: 'Resources',
items: [
{
label: 'Troubleshooting',
slug: 'resources/troubleshooting',
},
{
label: 'CLI Documentation',
slug: 'resources/cli',

View File

@@ -24,6 +24,7 @@ services:
`.trim();
const dcHtml = await codeToHtml(defaultDockerCompose, { theme: 'vitesse-black', lang: 'yaml' });
const defaultCommand = `mkdir -p ./app-data/{db,documents} && docker compose up -d`;
---
@@ -145,9 +146,12 @@ const dcHtml = await codeToHtml(defaultDockerCompose, { theme: 'vitesse-black',
<div id="docker-compose-output" class="mt-12" set:html={dcHtml} />
<pre id="command-output" class="bg-card p-4 rounded-md text-muted-foreground text-sm font-mono overflow-x-auto">{defaultCommand}</pre>
<div class="flex items-center gap-2 mt-4">
<button class="btn bg-muted mt-0" id="download-button">Download docker-compose.yml</button>
<button class="btn bg-muted mt-0" id="copy-button">Copy to clipboard</button>
<button class="btn bg-muted mt-0" id="copy-button">Copy docker compose to clipboard</button>
<button class="btn bg-muted mt-0" id="copy-command-button">Copy command</button>
</div>
@@ -178,6 +182,8 @@ const owlrelayWebhookUrlInput = document.getElementById('intake-email-owlrelay-w
const cfEmailDomainInput = document.getElementById('intake-email-cf-email-domain') as HTMLInputElement;
const webhookSecretInput = document.getElementById('intake-email-webhook-secret') as HTMLInputElement;
const refreshWebhookSecretButton = document.getElementById('refresh-webhook-secret');
const commandOutput = document.getElementById('command-output');
const copyCommandButton = document.getElementById('copy-command-button');
// Track whether the app base URL has been customized by the user
let isAppBaseUrlCustomized = false;
@@ -321,14 +327,31 @@ function getDockerComposeYml() {
return stringify(dc);
}
function getStartCommand() {
const volumePath = volumePathInput.value;
const volumePathNormalized = volumePath.replace(/\/$/, '');
const volumeWithSubdirs = `${volumePathNormalized}/{db,documents}`;
const mkdirCommand = `mkdir -p ${volumeWithSubdirs}`;
const dockerCommand = 'docker compose up -d';
return `${mkdirCommand} && ${dockerCommand}`;
}
async function updateDockerCompose() {
const dockerCompose = getDockerComposeYml();
const command = getStartCommand();
const html = await codeToHtml(dockerCompose, { theme: 'vitesse-black', lang: 'yaml' });
if (dockerComposeOutput) {
dockerComposeOutput.innerHTML = html;
}
if (commandOutput) {
commandOutput.textContent = command;
}
}
function handleCopy() {
@@ -420,6 +443,22 @@ function handleRefreshWebhookSecret() {
updateDockerCompose();
}
function handleCopyCommand() {
const command = getStartCommand();
copyToClipboard(command);
if (copyCommandButton) {
copyCommandButton.textContent = 'Copied!';
}
setTimeout(() => {
if (copyCommandButton) {
copyCommandButton.textContent = 'Copy command';
}
}, 1000);
}
// Add event listeners
portInput.addEventListener('input', handlePortChange);
sourceSelect.addEventListener('change', updateDockerCompose);
@@ -440,6 +479,7 @@ owlrelayWebhookUrlInput.addEventListener('input', handleWebhookUrlChange);
cfEmailDomainInput.addEventListener('input', updateDockerCompose);
webhookSecretInput.addEventListener('input', updateDockerCompose);
refreshWebhookSecretButton?.addEventListener('click', handleRefreshWebhookSecret);
copyCommandButton?.addEventListener('click', handleCopyCommand);
authSecretInput.value = getRandomString();

View File

@@ -31,6 +31,6 @@ export async function ensureLocalDatabaseDirectoryExists({ config }: { config: C
logger.info({ dbUrl: url, dbDir, dbPath }, 'Database directory missing, created it');
}
} catch (error) {
logger.error({ error, dbDir, dbPath }, 'Failed to ensure that the database directory exists, error while creating the directory');
logger.error({ error, dbDir, dbPath }, 'Failed to ensure that the database directory exists, error while creating the directory. Please see https://docs.papra.app/resources/troubleshooting/#failed-to-ensure-that-the-database-directory-exists for more information.');
}
}

View File

@@ -3,6 +3,7 @@ import type { Config } from '../../modules/config/config.types';
import type { Logger } from '../../modules/shared/logger/logger';
import process from 'node:process';
import { setupDatabase } from '../../modules/app/database/database';
import { ensureLocalDatabaseDirectoryExists } from '../../modules/app/database/database.services';
import { parseConfig } from '../../modules/config/config';
import { createLogger, wrapWithLoggerContext } from '../../modules/shared/logger/logger';
@@ -23,6 +24,7 @@ async function runScript(
const logger = createLogger({ namespace: 'scripts' });
const { config } = await parseConfig({ env: process.env });
await ensureLocalDatabaseDirectoryExists({ config });
const { db, client } = setupDatabase({ ...config.database });
try {