plugin-file-preview-auth 1.2.3 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/AIFilePreviewAction.d.ts +37 -0
- package/dist/client/index.d.ts +13 -0
- package/dist/client/locale.d.ts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/server/collections/attachments.d.ts +13 -0
- package/dist/server/collections/file-preview-auth.d.ts +22 -0
- package/dist/server/excel-parser-handler.d.ts +60 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/ocr/tesseract-runner.d.ts +34 -0
- package/dist/server/ocr/tesseract-worker.d.ts +26 -0
- package/dist/server/ocr/tesseract-worker.js +20 -1
- package/dist/server/plugin.d.ts +41 -0
- package/dist/server/plugin.js +61 -11
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import type { Application } from '@nocobase/client';
|
|
11
|
+
export declare const FILE_PREVIEW_WORK_CONTEXT_TYPE = "file-preview";
|
|
12
|
+
export declare function createFilePreviewWorkContext(file: any): {
|
|
13
|
+
type: string;
|
|
14
|
+
uid: string;
|
|
15
|
+
title: string;
|
|
16
|
+
content: {
|
|
17
|
+
source: string;
|
|
18
|
+
file: {
|
|
19
|
+
id: any;
|
|
20
|
+
uid: any;
|
|
21
|
+
url: any;
|
|
22
|
+
preview: any;
|
|
23
|
+
filename: any;
|
|
24
|
+
name: any;
|
|
25
|
+
title: any;
|
|
26
|
+
extname: any;
|
|
27
|
+
mimetype: any;
|
|
28
|
+
size: any;
|
|
29
|
+
path: any;
|
|
30
|
+
storageId: any;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export declare const AIFilePreviewAction: React.FC<{
|
|
35
|
+
file: any;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function registerFilePreviewAIWorkContext(app: Application): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Plugin } from '@nocobase/client';
|
|
10
|
+
export declare class PluginFilePreviewAuthClient extends Plugin {
|
|
11
|
+
load(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export default PluginFilePreviewAuthClient;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useT(): (str: string) => string;
|
|
10
|
+
export declare function tStr(key: string): string;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export * from './server';
|
|
10
|
+
export { default } from './server';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DUMMY COLLECTION
|
|
3
|
+
* This collection is created to prevent NocoBase workflow/ACL from throwing the error:
|
|
4
|
+
* '[Workflow pre-action]: collection "filePreviewAuth" not found'
|
|
5
|
+
*
|
|
6
|
+
* Since 'filePreviewAuth' is registered as a resourcer in plugin.ts, NocoBase
|
|
7
|
+
* implicitly looks for a collection with the same name.
|
|
8
|
+
* We set dumpRules: 'skip' and avoid timestamps to ensure this collection
|
|
9
|
+
* is completely ignored during backups/migrations and doesn't pollute the actual DB.
|
|
10
|
+
*/
|
|
11
|
+
declare const _default: {
|
|
12
|
+
name: string;
|
|
13
|
+
dumpRules: string;
|
|
14
|
+
autoGenId: boolean;
|
|
15
|
+
createdAt: boolean;
|
|
16
|
+
updatedAt: boolean;
|
|
17
|
+
fields: {
|
|
18
|
+
name: string;
|
|
19
|
+
type: string;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
export default _default;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from '@nocobase/actions';
|
|
10
|
+
interface AttachmentLike {
|
|
11
|
+
id?: string | number;
|
|
12
|
+
filename?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
mimetype?: string;
|
|
15
|
+
extname?: string;
|
|
16
|
+
url?: string;
|
|
17
|
+
storageId?: number;
|
|
18
|
+
size?: number;
|
|
19
|
+
meta?: Record<string, any>;
|
|
20
|
+
[key: string]: any;
|
|
21
|
+
}
|
|
22
|
+
interface InternalParseResult {
|
|
23
|
+
text: string;
|
|
24
|
+
handled: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface InternalParserHandler {
|
|
27
|
+
name: string;
|
|
28
|
+
supports(attachment: AttachmentLike): boolean;
|
|
29
|
+
parse(attachment: AttachmentLike, ctx: Context): Promise<InternalParseResult>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Server-side Excel parser handler.
|
|
33
|
+
*
|
|
34
|
+
* Uses SheetJS (xlsx) — the same library already bundled by this plugin for
|
|
35
|
+
* client-side XLSX preview — to extract plain text from .xlsx/.xls files on
|
|
36
|
+
* the server.
|
|
37
|
+
*
|
|
38
|
+
* Each worksheet is converted to CSV and separated by a section header so LLMs
|
|
39
|
+
* can distinguish sheet boundaries:
|
|
40
|
+
*
|
|
41
|
+
* === Sheet: Sheet1 ===
|
|
42
|
+
* col1,col2,col3
|
|
43
|
+
* val1,val2,val3
|
|
44
|
+
*
|
|
45
|
+
* === Sheet: Sheet2 ===
|
|
46
|
+
* ...
|
|
47
|
+
*
|
|
48
|
+
* This handler is registered into plugin-document-parser's InternalParserRegistry
|
|
49
|
+
* by PluginFilePreviewAuthServer with `prepend: true` so it takes priority over
|
|
50
|
+
* any other handlers that might be registered later.
|
|
51
|
+
*
|
|
52
|
+
* File bytes are fetched via plugin-document-parser's public `fetchFileBuffer`
|
|
53
|
+
* helper, which transparently handles both S3 URLs and local file paths.
|
|
54
|
+
*/
|
|
55
|
+
export declare class ExcelParserHandler implements InternalParserHandler {
|
|
56
|
+
readonly name = "file-preview-auth-excel-parser";
|
|
57
|
+
supports(attachment: AttachmentLike): boolean;
|
|
58
|
+
parse(attachment: AttachmentLike, ctx: Context): Promise<InternalParseResult>;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export { default } from './plugin';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface OcrRect {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
unit: string;
|
|
7
|
+
}
|
|
8
|
+
export interface OcrWordItem {
|
|
9
|
+
id: string;
|
|
10
|
+
key: string;
|
|
11
|
+
value: string;
|
|
12
|
+
page: number;
|
|
13
|
+
confidence: number;
|
|
14
|
+
rect: OcrRect;
|
|
15
|
+
status: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class TesseractRunner {
|
|
18
|
+
private log;
|
|
19
|
+
constructor(app: any);
|
|
20
|
+
/**
|
|
21
|
+
* Run Tesseract OCR on a file (PDF or Image).
|
|
22
|
+
* Generates word-level coordinates and text.
|
|
23
|
+
*/
|
|
24
|
+
runOcr(filePath: string, attachmentId: number): Promise<{
|
|
25
|
+
pages: Array<{
|
|
26
|
+
page_number: number;
|
|
27
|
+
items: OcrWordItem[];
|
|
28
|
+
}>;
|
|
29
|
+
}>;
|
|
30
|
+
private executeTesseract;
|
|
31
|
+
private parseTsv;
|
|
32
|
+
private convertPdfToImages;
|
|
33
|
+
private generateMockOcrData;
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class TesseractWorker {
|
|
2
|
+
private app;
|
|
3
|
+
private db;
|
|
4
|
+
private log;
|
|
5
|
+
private runner;
|
|
6
|
+
private isRunning;
|
|
7
|
+
private pollTimer;
|
|
8
|
+
private redisKey;
|
|
9
|
+
constructor(app: any);
|
|
10
|
+
/**
|
|
11
|
+
* Start the background worker.
|
|
12
|
+
*/
|
|
13
|
+
start(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Stop the background worker.
|
|
16
|
+
*/
|
|
17
|
+
stop(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Enqueue a new OCR job.
|
|
20
|
+
*/
|
|
21
|
+
enqueue(attachmentId: number): Promise<boolean>;
|
|
22
|
+
private getRedisClient;
|
|
23
|
+
private listenRedisQueue;
|
|
24
|
+
private startDbPolling;
|
|
25
|
+
private processJob;
|
|
26
|
+
}
|
|
@@ -158,7 +158,7 @@ class TesseractWorker {
|
|
|
158
158
|
if (!fileManager) {
|
|
159
159
|
throw new Error("File manager plugin is not active.");
|
|
160
160
|
}
|
|
161
|
-
const storageModel = fileManager.storagesCache
|
|
161
|
+
const storageModel = getStorageFromCache(fileManager.storagesCache, attachment.storageId);
|
|
162
162
|
if (!storageModel || storageModel.type !== "local") {
|
|
163
163
|
this.log.info(`[TesseractWorker] Non-local storage detected or virtual file. Using fallback.`);
|
|
164
164
|
}
|
|
@@ -185,6 +185,25 @@ class TesseractWorker {
|
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
+
function getStorageFromCache(cache, storageId) {
|
|
189
|
+
if (storageId === void 0 || storageId === null) return void 0;
|
|
190
|
+
let res = cache.get(storageId);
|
|
191
|
+
if (res) return res;
|
|
192
|
+
const strId = String(storageId);
|
|
193
|
+
res = cache.get(strId);
|
|
194
|
+
if (res) return res;
|
|
195
|
+
const numId = Number(storageId);
|
|
196
|
+
if (!isNaN(numId)) {
|
|
197
|
+
res = cache.get(numId);
|
|
198
|
+
if (res) return res;
|
|
199
|
+
}
|
|
200
|
+
for (const [k, v] of cache.entries()) {
|
|
201
|
+
if (String(k) === strId) {
|
|
202
|
+
return v;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
188
207
|
// Annotate the CommonJS export names for ESM import in node:
|
|
189
208
|
0 && (module.exports = {
|
|
190
209
|
TesseractWorker
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Plugin } from '@nocobase/server';
|
|
10
|
+
export declare class PluginFilePreviewAuthServer extends Plugin {
|
|
11
|
+
private cache;
|
|
12
|
+
private ocrWorker;
|
|
13
|
+
beforeLoad(): Promise<void>;
|
|
14
|
+
load(): Promise<void>;
|
|
15
|
+
afterEnable(): Promise<void>;
|
|
16
|
+
beforeDisable(): Promise<void>;
|
|
17
|
+
beforeDestroy(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Disable NocoBase's built-in Office previewer after plugins have loaded.
|
|
20
|
+
* This keeps this authenticated previewer as the active Office handler without causing a restart loop.
|
|
21
|
+
*/
|
|
22
|
+
private disableBuiltinOfficePreviewer;
|
|
23
|
+
private registerDownloadApi;
|
|
24
|
+
private registerAIWorkContext;
|
|
25
|
+
private resolveAttachment;
|
|
26
|
+
private assertCanAccessAttachment;
|
|
27
|
+
private assertAuthenticated;
|
|
28
|
+
private consumeUploadedParseFile;
|
|
29
|
+
private extractUploadedFileText;
|
|
30
|
+
private getMarkItDownParserPlugin;
|
|
31
|
+
private extractAttachmentText;
|
|
32
|
+
private readAttachmentAsText;
|
|
33
|
+
private formatAttachmentWorkContext;
|
|
34
|
+
/**
|
|
35
|
+
* Register Excel handler into plugin-document-parser's InternalParserRegistry.
|
|
36
|
+
* Uses prepend:true so SheetJS takes priority over the AI-loader fallback.
|
|
37
|
+
* Silent no-op when plugin-document-parser is not loaded.
|
|
38
|
+
*/
|
|
39
|
+
private registerExcelParser;
|
|
40
|
+
}
|
|
41
|
+
export default PluginFilePreviewAuthServer;
|
package/dist/server/plugin.js
CHANGED
|
@@ -188,28 +188,44 @@ class PluginFilePreviewAuthServer extends import_server.Plugin {
|
|
|
188
188
|
await this.assertCanAccessAttachment(ctx, attachment);
|
|
189
189
|
}
|
|
190
190
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
let matchedKey = null;
|
|
192
|
+
const rawStorageId = attachment.storageId || getAttachmentValue(attachment, "storageId");
|
|
193
|
+
if (rawStorageId) {
|
|
194
|
+
const strId = String(rawStorageId);
|
|
195
|
+
for (const key of fileManager.storagesCache.keys()) {
|
|
196
|
+
if (String(key) === strId) {
|
|
197
|
+
matchedKey = key;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const attachmentObj = typeof attachment.toJSON === "function" ? attachment.toJSON() : { ...attachment };
|
|
203
|
+
if (matchedKey !== null) {
|
|
204
|
+
attachmentObj.storageId = matchedKey;
|
|
205
|
+
}
|
|
206
|
+
const storageModel = getStorageFromCache(fileManager.storagesCache, attachmentObj.storageId);
|
|
207
|
+
if (storageModel && (storageModel.type === "s3" || storageModel.type === "aws-s3" || storageModel.type === "s3-private")) {
|
|
193
208
|
const StorageTypeClass = fileManager.storageTypes.get(storageModel.type);
|
|
194
209
|
const storageInstance = new StorageTypeClass(storageModel);
|
|
195
|
-
|
|
210
|
+
const s3Client = storageInstance.client || (typeof storageInstance.getS3Client === "function" ? storageInstance.getS3Client() : null);
|
|
211
|
+
if (s3Client) {
|
|
196
212
|
const { GetObjectCommand } = require("@aws-sdk/client-s3");
|
|
197
|
-
const key = storageInstance.getFileKey(
|
|
213
|
+
const key = storageInstance.getFileKey(attachmentObj);
|
|
198
214
|
const getCommand = new GetObjectCommand({
|
|
199
215
|
Bucket: storageModel.options.bucket,
|
|
200
216
|
Key: key
|
|
201
217
|
});
|
|
202
|
-
const response = await
|
|
203
|
-
ctx.type = response.ContentType ||
|
|
204
|
-
ctx.attachment(
|
|
218
|
+
const response = await s3Client.send(getCommand);
|
|
219
|
+
ctx.type = response.ContentType || attachmentObj.mimetype || "application/octet-stream";
|
|
220
|
+
ctx.attachment(attachmentObj.filename);
|
|
205
221
|
ctx.body = response.Body;
|
|
206
222
|
await next();
|
|
207
223
|
return;
|
|
208
224
|
}
|
|
209
225
|
}
|
|
210
|
-
const { stream, contentType } = await fileManager.getFileStream(
|
|
211
|
-
ctx.type = contentType ||
|
|
212
|
-
ctx.attachment(
|
|
226
|
+
const { stream, contentType } = await fileManager.getFileStream(attachmentObj);
|
|
227
|
+
ctx.type = contentType || attachmentObj.mimetype || "application/octet-stream";
|
|
228
|
+
ctx.attachment(attachmentObj.filename);
|
|
213
229
|
ctx.body = stream;
|
|
214
230
|
} catch (err) {
|
|
215
231
|
this.log.error(`[FilePreviewAuth] Error fetching stream for URL ${url}: ${err.message}`);
|
|
@@ -433,7 +449,22 @@ class PluginFilePreviewAuthServer extends import_server.Plugin {
|
|
|
433
449
|
if (!(fileManager == null ? void 0 : fileManager.getFileStream)) {
|
|
434
450
|
return "";
|
|
435
451
|
}
|
|
436
|
-
|
|
452
|
+
let matchedKey = null;
|
|
453
|
+
const rawStorageId = attachment.storageId || getAttachmentValue(attachment, "storageId");
|
|
454
|
+
if (rawStorageId) {
|
|
455
|
+
const strId = String(rawStorageId);
|
|
456
|
+
for (const key of fileManager.storagesCache.keys()) {
|
|
457
|
+
if (String(key) === strId) {
|
|
458
|
+
matchedKey = key;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const attachmentObj = typeof attachment.toJSON === "function" ? attachment.toJSON() : { ...attachment };
|
|
464
|
+
if (matchedKey !== null) {
|
|
465
|
+
attachmentObj.storageId = matchedKey;
|
|
466
|
+
}
|
|
467
|
+
const { stream } = await fileManager.getFileStream(attachmentObj);
|
|
437
468
|
const chunks = [];
|
|
438
469
|
for await (const chunk of stream) {
|
|
439
470
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
@@ -479,6 +510,25 @@ class PluginFilePreviewAuthServer extends import_server.Plugin {
|
|
|
479
510
|
}
|
|
480
511
|
}
|
|
481
512
|
var plugin_default = PluginFilePreviewAuthServer;
|
|
513
|
+
function getStorageFromCache(cache, storageId) {
|
|
514
|
+
if (storageId === void 0 || storageId === null) return void 0;
|
|
515
|
+
let res = cache.get(storageId);
|
|
516
|
+
if (res) return res;
|
|
517
|
+
const strId = String(storageId);
|
|
518
|
+
res = cache.get(strId);
|
|
519
|
+
if (res) return res;
|
|
520
|
+
const numId = Number(storageId);
|
|
521
|
+
if (!isNaN(numId)) {
|
|
522
|
+
res = cache.get(numId);
|
|
523
|
+
if (res) return res;
|
|
524
|
+
}
|
|
525
|
+
for (const [k, v] of cache.entries()) {
|
|
526
|
+
if (String(k) === strId) {
|
|
527
|
+
return v;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return void 0;
|
|
531
|
+
}
|
|
482
532
|
function getAttachmentValue(attachment, key) {
|
|
483
533
|
if (!attachment) return void 0;
|
|
484
534
|
if (typeof attachment.get === "function") return attachment.get(key);
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Preview PDF, image, and text files with Bearer token authentication via blob URLs.",
|
|
7
7
|
"description.vi-VN": "Xem trước file PDF, hình ảnh và văn bản với xác thực Bearer token qua blob URL.",
|
|
8
8
|
"description.zh-CN": "通过 Bearer 令牌认证和 Blob URL 预览 PDF、图片和文本文件。",
|
|
9
|
-
"version": "1.2.
|
|
9
|
+
"version": "1.2.6",
|
|
10
10
|
"main": "dist/server/index.js",
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|