Compare commits

...

5 Commits

Author SHA1 Message Date
Corentin Thomasset
38dcc765f9 chore(release): update versions (#462)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-09 11:39:03 +02:00
Corentin Thomasset
c085b9d676 fix(documents): apply tagging rules after the content is extracted (#461) 2025-08-09 11:36:33 +02:00
Corentin Thomasset
a64a93544d chore(release): update versions (#460)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-09 02:11:20 +02:00
Corentin Thomasset
f20559e95d chore(migrations): update migration command for production environment (#459) 2025-08-09 00:07:40 +00:00
Corentin Thomasset
7d755d0981 chore(docker): add build dependencies for sharp/node-gyp in Dockerfile (#457) 2025-08-09 02:01:44 +02:00
10 changed files with 73 additions and 13 deletions

View File

@@ -1,5 +1,9 @@
# @papra/app-client
## 0.8.2
## 0.8.1
## 0.8.0
### Minor Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@papra/app-client",
"type": "module",
"version": "0.8.0",
"version": "0.8.2",
"private": true,
"packageManager": "pnpm@10.12.3",
"description": "Papra frontend client",

View File

@@ -1,5 +1,17 @@
# @papra/app-server
## 0.8.2
### Patch Changes
- [#461](https://github.com/papra-hq/papra/pull/461) [`c085b9d`](https://github.com/papra-hq/papra/commit/c085b9d6766297943112601d3c634c716c4be440) Thanks [@CorentinTh](https://github.com/CorentinTh)! - Fix a regression bug that executed tagging rules before the file content was extracted
## 0.8.1
### Patch Changes
- [#459](https://github.com/papra-hq/papra/pull/459) [`f20559e`](https://github.com/papra-hq/papra/commit/f20559e95d1dc7d7a099dfd9a9df42bf5ce1b0b2) Thanks [@CorentinTh](https://github.com/CorentinTh)! - Removed dev-dependency needed in production build
## 0.8.0
### Minor Changes

View File

@@ -1,7 +1,7 @@
{
"name": "@papra/app-server",
"type": "module",
"version": "0.8.0",
"version": "0.8.2",
"private": true,
"packageManager": "pnpm@10.12.3",
"description": "Papra app server",
@@ -12,13 +12,14 @@
"dev": "tsx watch --env-file-if-exists=.env src/index.ts | crowlog-pretty",
"build": "pnpm esbuild --bundle src/index.ts --platform=node --packages=external --format=esm --outfile=dist/index.js --minify",
"start": "node dist/index.js",
"start:with-migrations": "pnpm migrate:up && pnpm start",
"start:with-migrations": "pnpm migrate:up:prod && pnpm start",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"test": "vitest run",
"test:watch": "vitest watch",
"typecheck": "tsc --noEmit",
"migrate:up": "tsx --env-file-if-exists=.env src/scripts/migrate-up.script.ts | crowlog-pretty",
"migrate:up:prod": "tsx src/scripts/migrate-up.script.ts",
"migrate:push": "drizzle-kit push",
"migrate:create": "sh -c 'drizzle-kit generate --name \"$1\" && tsx --env-file-if-exists=.env src/scripts/create-migration.ts \"$1\" | crowlog-pretty' --",
"db:studio": "drizzle-kit studio",

View File

@@ -4,6 +4,8 @@ import { overrideConfig } from '../config/config.test-utils';
import { ORGANIZATION_ROLES } from '../organizations/organizations.constants';
import { nextTick } from '../shared/async/defer.test-utils';
import { collectReadableStreamToString } from '../shared/streams/readable-stream';
import { createTaggingRulesRepository } from '../tagging-rules/tagging-rules.repository';
import { createTagsRepository } from '../tags/tags.repository';
import { documentsTagsTable } from '../tags/tags.table';
import { createInMemoryTaskServices } from '../tasks/tasks.test-utils';
import { documentActivityLogTable } from './document-activity/document-activity.table';
@@ -345,6 +347,8 @@ describe('documents usecases', () => {
const documentsRepository = createDocumentsRepository({ db });
const documentsStorageService = await createDocumentStorageService({ config });
const taggingRulesRepository = createTaggingRulesRepository({ db });
const tagsRepository = createTagsRepository({ db });
await db.insert(documentsTable).values({
id: 'document-1',
@@ -366,6 +370,8 @@ describe('documents usecases', () => {
organizationId: 'organization-1',
documentsRepository,
documentsStorageService,
taggingRulesRepository,
tagsRepository,
});
const documentRecords = await db.select().from(documentsTable);

View File

@@ -95,6 +95,7 @@ export async function createDocument({
organizationId,
documentsRepository,
tagsRepository,
taggingRulesRepository,
logger,
})
: await createNewDocument({
@@ -109,6 +110,8 @@ export async function createDocument({
documentsStorageService,
generateDocumentId,
trackingServices,
taskServices,
ocrLanguages,
logger,
});
@@ -119,8 +122,6 @@ export async function createDocument({
documentActivityRepository,
});
await applyTaggingRules({ document, taggingRulesRepository, tagsRepository });
deferTriggerWebhooks({
webhookRepository,
organizationId,
@@ -134,11 +135,6 @@ export async function createDocument({
},
});
await taskServices.scheduleJob({
taskName: 'extract-document-file-content',
data: { documentId: document.id, organizationId, ocrLanguages },
});
return { document };
}
@@ -181,6 +177,7 @@ async function handleExistingDocument({
organizationId,
documentsRepository,
tagsRepository,
taggingRulesRepository,
logger,
}: {
existingDocument: Document;
@@ -189,6 +186,7 @@ async function handleExistingDocument({
organizationId: string;
documentsRepository: DocumentsRepository;
tagsRepository: TagsRepository;
taggingRulesRepository: TaggingRulesRepository;
logger: Logger;
}) {
if (!existingDocument.isDeleted) {
@@ -202,6 +200,8 @@ async function handleExistingDocument({
documentsRepository.restoreDocument({ documentId: existingDocument.id, organizationId, name: fileName, userId }),
]);
await applyTaggingRules({ document: restoredDocument, taggingRulesRepository, tagsRepository });
return { document: restoredDocument };
}
@@ -217,6 +217,8 @@ async function createNewDocument({
documentsStorageService,
generateDocumentId,
trackingServices,
taskServices,
ocrLanguages = [],
logger,
}: {
file: File;
@@ -230,6 +232,8 @@ async function createNewDocument({
documentsStorageService: DocumentStorageService;
generateDocumentId: () => string;
trackingServices: TrackingServices;
taskServices: TaskServices;
ocrLanguages?: string[];
logger: Logger;
}) {
const documentId = generateDocumentId();
@@ -268,6 +272,11 @@ async function createNewDocument({
throw error;
}
await taskServices.scheduleJob({
taskName: 'extract-document-file-content',
data: { documentId, organizationId, ocrLanguages },
});
if (isDefined(userId)) {
trackingServices.captureUserEvent({ userId, event: 'Document created' });
}
@@ -316,8 +325,6 @@ export async function hardDeleteDocument({
documentsRepository: DocumentsRepository;
documentsStorageService: DocumentStorageService;
}) {
// TODO: use transaction
await Promise.all([
documentsRepository.hardDeleteDocument({ documentId: document.id }),
documentsStorageService.deleteFile({ storageKey: document.originalStorageKey }),
@@ -409,12 +416,16 @@ export async function extractAndSaveDocumentFileContent({
documentsRepository,
documentsStorageService,
ocrLanguages,
taggingRulesRepository,
tagsRepository,
}: {
documentId: string;
ocrLanguages?: string[];
organizationId: string;
documentsRepository: DocumentsRepository;
documentsStorageService: DocumentStorageService;
taggingRulesRepository: TaggingRulesRepository;
tagsRepository: TagsRepository;
}) {
const { document } = await documentsRepository.getDocumentById({ documentId, organizationId });
@@ -428,5 +439,14 @@ export async function extractAndSaveDocumentFileContent({
const { text } = await extractDocumentText({ file, ocrLanguages });
await documentsRepository.updateDocument({ documentId, organizationId, content: text });
const { document: updatedDocument } = await documentsRepository.updateDocument({ documentId, organizationId, content: text });
if (!updatedDocument) {
// This should never happen, but for type safety
throw createDocumentNotFoundError();
}
await applyTaggingRules({ document: updatedDocument, taggingRulesRepository, tagsRepository });
return { document: updatedDocument };
}

View File

@@ -1,6 +1,8 @@
import type { Database } from '../../app/database/database.types';
import type { Config } from '../../config/config.types';
import type { TaskServices } from '../../tasks/tasks.services';
import { createTaggingRulesRepository } from '../../tagging-rules/tagging-rules.repository';
import { createTagsRepository } from '../../tags/tags.repository';
import { createDocumentsRepository } from '../documents.repository';
import { extractAndSaveDocumentFileContent } from '../documents.usecases';
import { createDocumentStorageService } from '../storage/documents.storage.services';
@@ -13,6 +15,8 @@ export async function registerExtractDocumentFileContentTask({ taskServices, db,
handler: async ({ data }) => {
const documentsRepository = createDocumentsRepository({ db });
const documentsStorageService = await createDocumentStorageService({ config });
const taggingRulesRepository = createTaggingRulesRepository({ db });
const tagsRepository = createTagsRepository({ db });
// TODO: remove type cast
const { documentId, organizationId, ocrLanguages } = data as { documentId: string; organizationId: string; ocrLanguages: string[] };
@@ -23,6 +27,8 @@ export async function registerExtractDocumentFileContentTask({ taskServices, db,
ocrLanguages,
documentsRepository,
documentsStorageService,
taggingRulesRepository,
tagsRepository,
});
},
});

View File

@@ -11,6 +11,10 @@ FROM base AS build
WORKDIR /app
# --- add build deps for sharp/node-gyp, needed to be explicitly installed for armv7 ---
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ && rm -rf /var/lib/apt/lists/*
ENV npm_config_python=/usr/bin/python3
COPY pnpm-lock.yaml ./
COPY pnpm-workspace.yaml ./
COPY apps/papra-client/package.json apps/papra-client/package.json

View File

@@ -13,6 +13,10 @@ FROM base AS build
WORKDIR /app
# --- add build deps for sharp/node-gyp, needed to be explicitly installed for armv7 ---
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ && rm -rf /var/lib/apt/lists/*
ENV npm_config_python=/usr/bin/python3
COPY pnpm-lock.yaml ./
COPY pnpm-workspace.yaml ./
COPY apps/papra-client/package.json apps/papra-client/package.json

View File

@@ -8,6 +8,9 @@
"keywords": [],
"scripts": {
"docker:build:root": "docker build -t papra -f docker/Dockerfile .",
"docker:build:root:armv7": "docker buildx build --platform linux/arm/v7 -t papra -f docker/Dockerfile --load .",
"docker:build:root:amd64": "docker buildx build --platform linux/amd64 -t papra -f docker/Dockerfile --load .",
"docker:build:root:arm64": "docker buildx build --platform linux/arm64 -t papra -f docker/Dockerfile --load .",
"docker:build:rootless": "docker build -t papra-rootless -f docker/Dockerfile.rootless .",
"version": "changeset version && pnpm install --no-frozen-lockfile",
"changeset": "changeset",