plugin-file-preview-auth 1.3.5 → 1.3.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.
Files changed (105) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/dist/client/713.79a55458f5b67f39.js +30 -0
  4. package/dist/client/823.8b0ab22c181d4523.js +10 -0
  5. package/dist/client/828.ae8e47a2e7a3bc9e.js +49 -0
  6. package/dist/client/892.a568eb42fd6f0047.js +10 -0
  7. package/dist/client/index.js +1 -1
  8. package/dist/client-v2/index.js +10 -0
  9. package/dist/externalVersion.js +8 -7
  10. package/dist/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +3086 -3725
  11. package/dist/node_modules/@aws-sdk/client-s3/node_modules/.bin/fxparser +16 -0
  12. package/dist/node_modules/@aws-sdk/client-s3/node_modules/.bin/fxparser.cmd +17 -0
  13. package/dist/node_modules/@aws-sdk/client-s3/node_modules/.bin/fxparser.ps1 +28 -0
  14. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-cjs/index.js +110 -0
  15. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-es/SignatureV4MultiRegion.js +66 -0
  16. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-es/index.js +2 -0
  17. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-es/signature-v4-crt-container.js +3 -0
  18. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-types/SignatureV4MultiRegion.d.ts +30 -0
  19. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-types/index.d.ts +5 -0
  20. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-types/signature-v4-crt-container.d.ts +28 -0
  21. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-types/ts3.4/SignatureV4MultiRegion.d.ts +40 -0
  22. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-types/ts3.4/index.d.ts +2 -0
  23. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/dist-types/ts3.4/signature-v4-crt-container.d.ts +20 -0
  24. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/signature-v4-multi-region/package.json +57 -0
  25. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/AdaptiveRetryStrategy.js +1 -0
  26. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/ConfiguredRetryStrategy.js +1 -0
  27. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/DefaultRateLimiter.js +1 -0
  28. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/StandardRetryStrategy.js +1 -0
  29. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/config.js +1 -0
  30. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/constants.js +1 -0
  31. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/defaultRetryBackoffStrategy.js +1 -0
  32. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/defaultRetryToken.js +1 -0
  33. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/index.js +358 -0
  34. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-cjs/types.js +1 -0
  35. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/AdaptiveRetryStrategy.js +24 -0
  36. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/ConfiguredRetryStrategy.js +18 -0
  37. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/DefaultRateLimiter.js +100 -0
  38. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/StandardRetryStrategy.js +65 -0
  39. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/config.js +7 -0
  40. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/constants.js +9 -0
  41. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/defaultRetryBackoffStrategy.js +14 -0
  42. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/defaultRetryToken.js +11 -0
  43. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/index.js +7 -0
  44. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-es/types.js +1 -0
  45. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/AdaptiveRetryStrategy.d.ts +33 -0
  46. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ConfiguredRetryStrategy.d.ts +32 -0
  47. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/DefaultRateLimiter.d.ts +49 -0
  48. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/StandardRetryStrategy.d.ts +26 -0
  49. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/config.d.ts +20 -0
  50. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/constants.d.ts +59 -0
  51. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/defaultRetryBackoffStrategy.d.ts +5 -0
  52. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/defaultRetryToken.d.ts +9 -0
  53. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/index.d.ts +7 -0
  54. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/AdaptiveRetryStrategy.d.ts +33 -0
  55. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/ConfiguredRetryStrategy.d.ts +32 -0
  56. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/DefaultRateLimiter.d.ts +49 -0
  57. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/StandardRetryStrategy.d.ts +26 -0
  58. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/config.d.ts +20 -0
  59. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/constants.d.ts +59 -0
  60. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/defaultRetryBackoffStrategy.d.ts +5 -0
  61. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/defaultRetryToken.d.ts +9 -0
  62. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/index.d.ts +7 -0
  63. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/ts3.4/types.d.ts +19 -0
  64. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/dist-types/types.d.ts +19 -0
  65. package/dist/node_modules/@aws-sdk/client-s3/node_modules/@smithy/util-retry/package.json +68 -0
  66. package/dist/node_modules/@aws-sdk/client-s3/package.json +1 -1
  67. package/dist/node_modules/xlsx/package.json +1 -1
  68. package/dist/server/ocr/tesseract-runner.js +3 -1
  69. package/dist/server/plugin.js +22 -4
  70. package/package.json +57 -45
  71. package/src/client/AIFilePreviewAction.tsx +282 -0
  72. package/src/client/__tests__/ocr-utils.test.ts +85 -0
  73. package/src/client/client.d.ts +258 -0
  74. package/src/client/index.tsx +1807 -0
  75. package/src/client/locale.ts +21 -0
  76. package/src/client-v2/index.tsx +1 -0
  77. package/src/client-v2/plugin.tsx +7 -0
  78. package/{dist/index.d.ts → src/index.ts} +11 -10
  79. package/src/locale/en-US.json +14 -0
  80. package/src/locale/vi-VN.json +14 -0
  81. package/src/locale/zh-CN.json +14 -0
  82. package/src/server/__tests__/smoke.test.ts +17 -0
  83. package/src/server/collections/attachment-ocr-results.ts +40 -0
  84. package/{dist/server/collections/file-preview-auth.d.ts → src/server/collections/file-preview-auth.ts} +15 -14
  85. package/src/server/excel-parser-handler.ts +128 -0
  86. package/{dist/server/index.d.ts → src/server/index.ts} +10 -9
  87. package/src/server/migrations/20260528000000-move-ocr-fields-out-of-attachments.ts +39 -0
  88. package/src/server/ocr/tesseract-runner.ts +389 -0
  89. package/src/server/ocr/tesseract-worker.ts +235 -0
  90. package/src/server/plugin.ts +1470 -0
  91. package/dist/client/166.17caa11c2ba40313.js +0 -10
  92. package/dist/client/351.0f0ce45c92425c8f.js +0 -10
  93. package/dist/client/374.96762d13b15e7467.js +0 -30
  94. package/dist/client/514.2a8b6aa0d2fcd4b2.js +0 -49
  95. package/dist/client/AIFilePreviewAction.d.ts +0 -42
  96. package/dist/client/index.d.ts +0 -14
  97. package/dist/client/locale.d.ts +0 -10
  98. package/dist/node_modules/xlsx/node_modules/.bin/crc32 +0 -15
  99. package/dist/node_modules/xlsx/node_modules/.bin/crc32.cmd +0 -7
  100. package/dist/server/collections/attachment-ocr-results.d.ts +0 -2
  101. package/dist/server/excel-parser-handler.d.ts +0 -60
  102. package/dist/server/migrations/20260528000000-move-ocr-fields-out-of-attachments.d.ts +0 -5
  103. package/dist/server/ocr/tesseract-runner.d.ts +0 -34
  104. package/dist/server/ocr/tesseract-worker.d.ts +0 -27
  105. package/dist/server/plugin.d.ts +0 -54
@@ -0,0 +1,21 @@
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
+
10
+ // @ts-ignore
11
+ import pkg from './../../package.json';
12
+ import { useApp } from '@nocobase/client';
13
+
14
+ export function useT() {
15
+ const app = useApp();
16
+ return (str: string) => app.i18n.t(str, { ns: [pkg.name, 'client'] });
17
+ }
18
+
19
+ export function tStr(key: string) {
20
+ return `{{t(${JSON.stringify(key)}, { ns: ['${pkg.name}', 'client'], nsMode: 'fallback' })}}`;
21
+ }
@@ -0,0 +1 @@
1
+ export { default } from './plugin';
@@ -0,0 +1,7 @@
1
+ import { Plugin, Application } from '@nocobase/client-v2';
2
+
3
+ export class PluginFilePreviewAuthClient extends Plugin<Record<string, never>, Application> {
4
+ async load() {}
5
+ }
6
+
7
+ export default PluginFilePreviewAuthClient;
@@ -1,10 +1,11 @@
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';
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
+
10
+ export * from './server';
11
+ export { default } from './server';
@@ -0,0 +1,14 @@
1
+ {
2
+ "Preview": "Preview",
3
+ "Open in new window": "Open in new window",
4
+ "Download": "Download",
5
+ "Close": "Close",
6
+ "Loading preview...": "Loading preview...",
7
+ "Failed to load file preview": "Failed to load file preview",
8
+ "Failed to download file": "Failed to download file",
9
+ "Ask AI": "Ask AI",
10
+ "Please help me analyze the file currently open in preview.": "Please help me analyze the file currently open in preview.",
11
+ "Failed to open AI chat": "Failed to open AI chat",
12
+ "This file type cannot be previewed. Click Download to save the file.": "This file type cannot be previewed. Click Download to save the file.",
13
+ "This file type cannot be previewed. ": "This file type cannot be previewed. "
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "Preview": "Xem trước",
3
+ "Open in new window": "Mở trong cửa sổ mới",
4
+ "Download": "Tải xuống",
5
+ "Close": "Đóng",
6
+ "Loading preview...": "Đang tải xem trước...",
7
+ "Failed to load file preview": "Không thể tải xem trước file",
8
+ "Failed to download file": "Không thể tải file",
9
+ "Ask AI": "Hỏi AI",
10
+ "Please help me analyze the file currently open in preview.": "Hãy giúp tôi phân tích file đang được mở xem trước.",
11
+ "Failed to open AI chat": "Không thể mở chat AI",
12
+ "This file type cannot be previewed. Click Download to save the file.": "Loại file này không thể xem trước. Nhấn Tải xuống để lưu file.",
13
+ "This file type cannot be previewed. ": "Loại file này không thể xem trước. "
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "Preview": "预览",
3
+ "Open in new window": "在新窗口中打开",
4
+ "Download": "下载",
5
+ "Close": "关闭",
6
+ "Loading preview...": "正在加载预览...",
7
+ "Failed to load file preview": "无法加载文件预览",
8
+ "Failed to download file": "无法下载文件",
9
+ "Ask AI": "询问 AI",
10
+ "Please help me analyze the file currently open in preview.": "请帮我分析当前正在预览的文件。",
11
+ "Failed to open AI chat": "无法打开 AI 聊天",
12
+ "This file type cannot be previewed. Click Download to save the file.": "此文件类型不支持预览。请点击下载以保存文件。",
13
+ "This file type cannot be previewed. ": "此文件类型不支持预览。"
14
+ }
@@ -0,0 +1,17 @@
1
+ import { createMockServer } from '@nocobase/test';
2
+
3
+ describe('Authenticated File Previewer plugin smoke', () => {
4
+ let app;
5
+
6
+ afterEach(async () => {
7
+ await app?.destroy();
8
+ });
9
+
10
+ it('loads without starting the full app', async () => {
11
+ app = await createMockServer({
12
+ plugins: ['nocobase', 'file-preview-auth'],
13
+ });
14
+
15
+ expect(app).toBeTruthy();
16
+ });
17
+ });
@@ -0,0 +1,40 @@
1
+ import { defineCollection } from '@nocobase/database';
2
+
3
+ export default defineCollection({
4
+ name: 'attachmentOcrResults',
5
+ title: 'Attachment OCR results',
6
+ dumpRules: 'required',
7
+ migrationRules: ['schema-only', 'overwrite'],
8
+ indexes: [
9
+ {
10
+ fields: ['attachmentId'],
11
+ unique: true,
12
+ },
13
+ {
14
+ fields: ['status'],
15
+ },
16
+ ],
17
+ fields: [
18
+ {
19
+ type: 'belongsTo',
20
+ name: 'attachment',
21
+ target: 'attachments',
22
+ foreignKey: 'attachmentId',
23
+ onDelete: 'CASCADE',
24
+ interface: 'm2o',
25
+ },
26
+ {
27
+ type: 'string',
28
+ name: 'status',
29
+ defaultValue: 'no-ocr',
30
+ },
31
+ {
32
+ type: 'json',
33
+ name: 'data',
34
+ },
35
+ {
36
+ type: 'text',
37
+ name: 'error',
38
+ },
39
+ ],
40
+ });
@@ -2,21 +2,22 @@
2
2
  * DUMMY COLLECTION
3
3
  * This collection is created to prevent NocoBase workflow/ACL from throwing the error:
4
4
  * '[Workflow pre-action]: collection "filePreviewAuth" not found'
5
- *
6
- * Since 'filePreviewAuth' is registered as a resourcer in plugin.ts, NocoBase
5
+ *
6
+ * Since 'filePreviewAuth' is registered as a resourcer in plugin.ts, NocoBase
7
7
  * implicitly looks for a collection with the same name.
8
- * We set dumpRules: 'skip' and avoid timestamps to ensure this collection
8
+ * We set dumpRules: 'skip' and avoid timestamps to ensure this collection
9
9
  * is completely ignored during backups/migrations and doesn't pollute the actual DB.
10
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
- }[];
11
+ export default {
12
+ name: 'filePreviewAuth',
13
+ dumpRules: 'skip',
14
+ autoGenId: true,
15
+ createdAt: false,
16
+ updatedAt: false,
17
+ fields: [
18
+ {
19
+ name: 'name',
20
+ type: 'string',
21
+ },
22
+ ],
21
23
  };
22
- export default _default;
@@ -0,0 +1,128 @@
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
+
10
+ import type { Context } from '@nocobase/actions';
11
+ import * as XLSX from 'xlsx';
12
+
13
+ // Local copies of plugin-document-parser contracts — avoids a compile-time dependency.
14
+
15
+ interface AttachmentLike {
16
+ id?: string | number;
17
+ filename?: string;
18
+ name?: string;
19
+ mimetype?: string;
20
+ extname?: string;
21
+ url?: string;
22
+ storageId?: number;
23
+ size?: number;
24
+ meta?: Record<string, any>;
25
+ [key: string]: any;
26
+ }
27
+
28
+ interface InternalParseResult {
29
+ text: string;
30
+ handled: boolean;
31
+ }
32
+
33
+ export interface InternalParserHandler {
34
+ name: string;
35
+ supports(attachment: AttachmentLike): boolean;
36
+ parse(attachment: AttachmentLike, ctx: Context): Promise<InternalParseResult>;
37
+ }
38
+
39
+ // ─── Constants ────────────────────────────────────────────────────────────────
40
+
41
+ const XLSX_EXTNAMES = new Set(['.xlsx', '.xls']);
42
+ const XLSX_MIMETYPES = new Set([
43
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
44
+ 'application/vnd.ms-excel',
45
+ ]);
46
+
47
+ function resolveExtname(attachment: AttachmentLike): string {
48
+ if (attachment.extname) return attachment.extname.toLowerCase();
49
+ const name = attachment.filename ?? attachment.name ?? '';
50
+ const idx = name.lastIndexOf('.');
51
+ return idx >= 0 ? name.slice(idx).toLowerCase() : '';
52
+ }
53
+
54
+ // ─── Handler ──────────────────────────────────────────────────────────────────
55
+
56
+ /**
57
+ * Server-side Excel parser handler.
58
+ *
59
+ * Uses SheetJS (xlsx) — the same library already bundled by this plugin for
60
+ * client-side XLSX preview — to extract plain text from .xlsx/.xls files on
61
+ * the server.
62
+ *
63
+ * Each worksheet is converted to CSV and separated by a section header so LLMs
64
+ * can distinguish sheet boundaries:
65
+ *
66
+ * === Sheet: Sheet1 ===
67
+ * col1,col2,col3
68
+ * val1,val2,val3
69
+ *
70
+ * === Sheet: Sheet2 ===
71
+ * ...
72
+ *
73
+ * This handler is registered into plugin-document-parser's InternalParserRegistry
74
+ * by PluginFilePreviewAuthServer with `prepend: true` so it takes priority over
75
+ * any other handlers that might be registered later.
76
+ *
77
+ * File bytes are fetched via plugin-document-parser's public `fetchFileBuffer`
78
+ * helper, which transparently handles both S3 URLs and local file paths.
79
+ */
80
+ export class ExcelParserHandler implements InternalParserHandler {
81
+ readonly name = 'file-preview-auth-excel-parser';
82
+
83
+ supports(attachment: AttachmentLike): boolean {
84
+ if (attachment.mimetype && XLSX_MIMETYPES.has(attachment.mimetype)) return true;
85
+ return XLSX_EXTNAMES.has(resolveExtname(attachment));
86
+ }
87
+
88
+ async parse(attachment: AttachmentLike, ctx: Context): Promise<InternalParseResult> {
89
+ const docParserPlugin = ((ctx.app as any).pm?.get('@nocobase/plugin-document-parser') || (ctx.app as any).pm?.get('plugin-document-parser')) as any;
90
+ if (!docParserPlugin?.fetchFileBuffer) {
91
+ ctx.log?.warn?.('[ExcelParser] plugin-document-parser not available — cannot fetch file bytes');
92
+ return { text: '', handled: false };
93
+ }
94
+
95
+ const { buffer } = await docParserPlugin.fetchFileBuffer(ctx, attachment);
96
+
97
+ const workbook = XLSX.read(buffer, { type: 'buffer' });
98
+
99
+ const parts: string[] = [];
100
+ for (const sheetName of workbook.SheetNames) {
101
+ const sheet = workbook.Sheets[sheetName];
102
+ const rows = XLSX.utils.sheet_to_json<string[]>(sheet, { header: 1, defval: '' }) as string[][];
103
+ // Drop trailing fully-empty rows
104
+ while (rows.length > 0 && rows[rows.length - 1].every((c) => String(c).trim() === '')) {
105
+ rows.pop();
106
+ }
107
+ if (rows.length === 0) continue;
108
+
109
+ const escape = (v: any) =>
110
+ String(v ?? '')
111
+ .replace(/\|/g, '\\|')
112
+ .replace(/\n/g, ' ');
113
+ const toRow = (cells: string[], colCount: number) => {
114
+ const padded = Array.from({ length: colCount }, (_, i) => escape(cells[i] ?? ''));
115
+ return `| ${padded.join(' | ')} |`;
116
+ };
117
+
118
+ const colCount = Math.max(...rows.map((r) => r.length));
119
+ const header = toRow(rows[0], colCount);
120
+ const separator = `| ${Array(colCount).fill('---').join(' | ')} |`;
121
+ const body = rows.slice(1).map((r) => toRow(r, colCount));
122
+
123
+ parts.push(`### Sheet: ${sheetName}\n\n${[header, separator, ...body].join('\n')}`);
124
+ }
125
+
126
+ return { text: parts.join('\n\n'), handled: true };
127
+ }
128
+ }
@@ -1,9 +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 { default } from './plugin';
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
+
10
+ export { default } from './plugin';
@@ -0,0 +1,39 @@
1
+ import { Migration } from '@nocobase/server';
2
+
3
+ export default class extends Migration {
4
+ on = 'afterLoad';
5
+
6
+ async up() {
7
+ const attachments = this.db.getCollection('attachments');
8
+ if (!attachments) {
9
+ return;
10
+ }
11
+
12
+ const queryInterface = this.db.sequelize.getQueryInterface();
13
+ const tableName = attachments.getTableNameWithSchema();
14
+ const columns = await queryInterface.describeTable(tableName).catch(() => null);
15
+ if (!columns) {
16
+ return;
17
+ }
18
+
19
+ await this.db.sequelize.transaction(async (transaction) => {
20
+ if (columns.ocrStatus) {
21
+ await queryInterface.removeColumn(tableName, 'ocrStatus', { transaction });
22
+ }
23
+ if (columns.ocrData) {
24
+ await queryInterface.removeColumn(tableName, 'ocrData', { transaction });
25
+ }
26
+
27
+ const fieldRepo = this.db.getRepository('fields');
28
+ if (fieldRepo) {
29
+ await fieldRepo.destroy({
30
+ filter: {
31
+ collectionName: 'attachments',
32
+ name: ['ocrStatus', 'ocrData'],
33
+ },
34
+ transaction,
35
+ });
36
+ }
37
+ });
38
+ }
39
+ }