feat: Paperless-Webhook ersetzt Tag-16-Cron-Job durch Echtzeit-Verarbeitung
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s
Build and Push Multi-Platform Images / build-and-push (push) Successful in 42s
Statt stündlichem Cron ruft Paperless-NGX nun per Webhook das Backend auf,
sobald ein Dokument bearbeitet wurde. Der Endpunkt verarbeitet ein einzelnes
Dokument (per doc_url/document_id), prüft weiterhin den Tag "paperlessmanager"
(ID 16) und ist per API-Key (X-API-Key) abgesichert, sodass nur Paperless ihn
aufrufen kann.
- paperless-processor.service.ts: @Cron entfernt; neue Methode
processDocumentById; Tag-16 als Konstante; gemeinsamer Helper
processAndEvaluate (Batch + Webhook)
- paperless.module.ts: PaperlessProcessorService exportiert
- webhook.controller.ts: Route auf api/webhook (erreichbar via /api-Proxy);
@Public() -> @UseGuards(ApiKeyGuard); ID-Extraktion aus {{doc_url}}
- webhook.module.ts: PaperlessModule + AuthModule importiert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Cron } from '@nestjs/schedule';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
@@ -8,6 +7,8 @@ import { DocumentType } from '../database/entities/document-type.entity';
|
||||
import { PaperlessService } from './paperless.service';
|
||||
import { PostprocessingService } from '../postprocessing/postprocessing.service';
|
||||
|
||||
const PAPERLESSMANAGER_TAG_ID = 16; // Tag "paperlessmanager"
|
||||
|
||||
@Injectable()
|
||||
export class PaperlessProcessorService {
|
||||
private readonly logger = new Logger(PaperlessProcessorService.name);
|
||||
@@ -22,11 +23,13 @@ export class PaperlessProcessorService {
|
||||
private readonly docFieldRepo: Repository<DocumentField>,
|
||||
) {}
|
||||
|
||||
@Cron(process.env.PAPERLESS_PROCESSOR_CRON || '0 * * * * *')
|
||||
// Manueller Batch-Lauf ("alle paperlessmanager-Dokumente neu durchlaufen").
|
||||
// Die laufende Verarbeitung erfolgt ereignisgesteuert über den Webhook
|
||||
// (processDocumentById), daher ist hier kein @Cron-Trigger mehr gesetzt.
|
||||
async processDocuments() {
|
||||
try {
|
||||
const response = await this.paperlessService.getDocuments({
|
||||
tags__id__all: 16,
|
||||
tags__id__all: PAPERLESSMANAGER_TAG_ID,
|
||||
page_size: 9999,
|
||||
});
|
||||
const documents: any[] = Array.isArray(response)
|
||||
@@ -38,17 +41,12 @@ export class PaperlessProcessorService {
|
||||
const validFieldIds = new Set(customFields.map((f: any) => f.id));
|
||||
|
||||
this.logger.log(
|
||||
`Verarbeite ${documents.length} Dokument(e) mit Tag "paperlessmanager" (ID: 16).`,
|
||||
`Verarbeite ${documents.length} Dokument(e) mit Tag "paperlessmanager" (ID: ${PAPERLESSMANAGER_TAG_ID}).`,
|
||||
);
|
||||
|
||||
for (const doc of documents) {
|
||||
try {
|
||||
const updatedDoc = await this.processSingleDocument(
|
||||
doc,
|
||||
validFieldIds,
|
||||
);
|
||||
// Postprocessing nach dem Speichern evaluieren
|
||||
await this.postprocessingService.evaluate(updatedDoc || doc);
|
||||
await this.processAndEvaluate(doc, validFieldIds);
|
||||
} catch (innerErr: any) {
|
||||
this.logger.error(
|
||||
`Fehler bei Dokument ID ${doc.id}: ${innerErr.message}`,
|
||||
@@ -65,6 +63,45 @@ export class PaperlessProcessorService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verarbeitet ein einzelnes Dokument anhand seiner ID – die ereignisgesteuerte
|
||||
* Variante des früheren Cron-Jobs (vom Paperless-Webhook aufgerufen). Es wird
|
||||
* nur verarbeitet, wenn das Dokument den Tag "paperlessmanager" trägt.
|
||||
*/
|
||||
async processDocumentById(
|
||||
documentId: number,
|
||||
): Promise<{ processed: boolean; reason?: string }> {
|
||||
const doc = await this.paperlessService.getDocument(documentId);
|
||||
const tags: number[] = doc.tags || [];
|
||||
if (!tags.includes(PAPERLESSMANAGER_TAG_ID)) {
|
||||
this.logger.log(
|
||||
`Dokument ${documentId} ohne Tag "paperlessmanager" (ID ${PAPERLESSMANAGER_TAG_ID}) – übersprungen.`,
|
||||
);
|
||||
return { processed: false, reason: 'tag-missing' };
|
||||
}
|
||||
|
||||
const customFields = await this.paperlessService.getCustomFields();
|
||||
const validFieldIds = new Set<number>(customFields.map((f: any) => f.id));
|
||||
|
||||
await this.processAndEvaluate(doc, validFieldIds);
|
||||
this.logger.log(`Dokument ${documentId} per Webhook verarbeitet.`);
|
||||
return { processed: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reichert ein Dokument an (Tags, Pflichtfelder, Titel) und evaluiert
|
||||
* anschließend das Postprocessing. Gemeinsame Logik von Batch-Lauf und Webhook.
|
||||
*/
|
||||
private async processAndEvaluate(
|
||||
doc: any,
|
||||
validFieldIds: Set<number>,
|
||||
): Promise<any> {
|
||||
const updatedDoc = await this.processSingleDocument(doc, validFieldIds);
|
||||
// Postprocessing nach dem Speichern evaluieren
|
||||
await this.postprocessingService.evaluate(updatedDoc || doc);
|
||||
return updatedDoc;
|
||||
}
|
||||
|
||||
private async processSingleDocument(
|
||||
doc: any,
|
||||
validFieldIds: Set<number>,
|
||||
|
||||
@@ -32,6 +32,6 @@ import { AuthModule } from '../auth/auth.module';
|
||||
PaperlessProcessorService,
|
||||
PaperlessTaskProcessorService,
|
||||
],
|
||||
exports: [PaperlessService],
|
||||
exports: [PaperlessService, PaperlessProcessorService],
|
||||
})
|
||||
export class PaperlessModule {}
|
||||
|
||||
Reference in New Issue
Block a user