@nocobase/plugin-file-manager 2.1.0-alpha.4 → 2.1.0-alpha.45
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/LICENSE +201 -661
- package/README.md +79 -10
- package/client-v2.d.ts +2 -0
- package/client-v2.js +1 -0
- package/dist/client/867ada653cd02a3e.mjs +6 -0
- package/dist/client/876.ca16d7a6e6387862.js +11 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/locale/index.d.ts +2 -1
- package/dist/client/previewer/filePreviewTypes.d.ts +1 -31
- package/dist/client/templates/file.d.ts +1 -1
- package/dist/client-v2/125.0b8eef1f19b87042.js +10 -0
- package/dist/client-v2/229.bd72c2d7aa088310.js +10 -0
- package/dist/client-v2/336.1dd1b32466d0c778.js +10 -0
- package/dist/client-v2/43.eb45d53ba3e9828b.js +10 -0
- package/dist/client-v2/450.f590b4c220108742.js +10 -0
- package/dist/client-v2/867ada653cd02a3e.mjs +6 -0
- package/dist/client-v2/876.22cd8e41ac8631ed.js +11 -0
- package/dist/client-v2/929.d7e783304cc1f236.js +10 -0
- package/dist/client-v2/942.f36d807d763a1b53.js +10 -0
- package/dist/{client/StorageOptions.d.ts → client-v2/components/BaseUrlField.d.ts} +1 -1
- package/dist/client-v2/components/DefaultField.d.ts +18 -0
- package/dist/client-v2/components/FileSizeField.d.ts +10 -0
- package/dist/client-v2/components/MimetypeField.d.ts +10 -0
- package/dist/client-v2/components/NameField.d.ts +10 -0
- package/dist/client-v2/components/ParanoidField.d.ts +10 -0
- package/dist/client-v2/components/PathField.d.ts +18 -0
- package/dist/client-v2/components/RenameModeField.d.ts +10 -0
- package/dist/client-v2/components/TitleField.d.ts +10 -0
- package/dist/client-v2/components/index.d.ts +17 -0
- package/dist/client-v2/index.d.ts +17 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/client-v2/locale.d.ts +10 -0
- package/dist/{client → client-v2}/models/DisplayPreviewFieldModel.d.ts +1 -1
- package/dist/{client → client-v2}/models/UploadActionModel.d.ts +2 -2
- package/dist/{client → client-v2}/models/UploadFieldModel.d.ts +1 -1
- package/dist/client-v2/models/index.d.ts +11 -0
- package/dist/client-v2/models/uploadFieldUtils.d.ts +64 -0
- package/dist/client-v2/pages/FileStoragePage.d.ts +10 -0
- package/dist/client-v2/plugin.d.ts +87 -0
- package/dist/client-v2/previewer/filePreviewTypes.d.ts +46 -0
- package/dist/client-v2/storage-forms/AliOssStorageForm.d.ts +10 -0
- package/dist/client-v2/storage-forms/LocalStorageForm.d.ts +10 -0
- package/dist/client-v2/storage-forms/S3StorageForm.d.ts +10 -0
- package/dist/client-v2/storage-forms/TxCosStorageForm.d.ts +10 -0
- package/dist/externalVersion.js +12 -12
- package/dist/locale/de-DE.json +3 -0
- package/dist/locale/en-US.json +4 -0
- package/dist/locale/es-ES.json +3 -0
- package/dist/locale/fr-FR.json +3 -0
- package/dist/locale/hu-HU.json +4 -1
- package/dist/locale/id-ID.json +4 -1
- package/dist/locale/it-IT.json +3 -0
- package/dist/locale/ja-JP.json +3 -0
- package/dist/locale/ko-KR.json +3 -0
- package/dist/locale/nl-NL.json +3 -0
- package/dist/locale/pt-BR.json +3 -0
- package/dist/locale/ru-RU.json +3 -0
- package/dist/locale/tr-TR.json +3 -0
- package/dist/locale/uk-UA.json +3 -0
- package/dist/locale/vi-VN.json +4 -1
- package/dist/locale/zh-CN.json +4 -0
- package/dist/locale/zh-TW.json +3 -0
- package/dist/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +190 -190
- package/dist/node_modules/@aws-sdk/client-s3/package.json +1 -1
- package/dist/node_modules/@aws-sdk/lib-storage/dist-cjs/index.js +195 -195
- package/dist/node_modules/@aws-sdk/lib-storage/node_modules/buffer/index.d.ts +186 -0
- package/dist/node_modules/@aws-sdk/lib-storage/node_modules/buffer/index.js +1794 -0
- package/dist/node_modules/@aws-sdk/lib-storage/node_modules/buffer/package.json +82 -0
- package/dist/node_modules/@aws-sdk/lib-storage/package.json +1 -1
- package/dist/node_modules/ali-oss/lib/client.js +14 -14
- package/dist/node_modules/ali-oss/package.json +1 -1
- package/dist/node_modules/cos-nodejs-sdk-v5/.github/workflows/auto-changelog.yml +55 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/.prettierrc +10 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/.travis.yml +16 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/LICENSE +21 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/demo/crc64.js +9 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/demo/demo-sts-scope.js +75 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/demo/demo-sts.js +65 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/demo/demo.js +4542 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/demo/util.js +135 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/index.d.ts +2610 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/index.js +220 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/package.json +1 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/advance.js +1659 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/async.js +59 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/base.js +4404 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/cos.js +137 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/event.js +34 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/select-stream.js +181 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/session.js +126 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/task.js +255 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/sdk/util.js +776 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/test/csp.js +1302 -0
- package/dist/node_modules/cos-nodejs-sdk-v5/test/test.js +6119 -0
- package/dist/node_modules/mime-match/index.js +1 -1
- package/dist/node_modules/mime-match/package.json +1 -1
- package/dist/node_modules/mime-types/index.js +3 -3
- package/dist/node_modules/mime-types/package.json +1 -1
- package/dist/node_modules/mkdirp/index.js +1 -1
- package/dist/node_modules/mkdirp/package.json +1 -1
- package/dist/node_modules/url-join/lib/url-join.js +1 -1
- package/dist/node_modules/url-join/package.json +1 -1
- package/dist/server/commands/repair-filenames.d.ts +55 -0
- package/dist/server/commands/repair-filenames.js +283 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/server.d.ts +3 -2
- package/dist/server/server.js +6 -2
- package/dist/server/storages/ali-oss.d.ts +3 -1
- package/dist/server/storages/ali-oss.js +23 -2
- package/dist/server/storages/index.d.ts +7 -1
- package/dist/server/storages/index.js +8 -1
- package/dist/server/storages/local.d.ts +2 -0
- package/dist/server/storages/local.js +25 -3
- package/dist/server/storages/s3.d.ts +2 -0
- package/dist/server/storages/s3.js +26 -0
- package/dist/server/storages/tx-cos.d.ts +18 -1
- package/dist/server/storages/tx-cos.js +138 -10
- package/dist/server/utils.d.ts +1 -0
- package/dist/server/utils.js +33 -4
- package/package.json +9 -4
- package/dist/node_modules/multer-cos/LICENSE +0 -24
- package/dist/node_modules/multer-cos/demo/index.js +0 -39
- package/dist/node_modules/multer-cos/demo/myMulter.js +0 -88
- package/dist/node_modules/multer-cos/index.js +0 -220
- package/dist/node_modules/multer-cos/package.json +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"mime-types","description":"The ultimate javascript content-type utility.","version":"3.0.1","contributors":["Douglas Christopher Wilson <doug@somethingdoug.com>","Jeremiah Senkpiel <fishrock123@rocketmail.com> (https://searchbeam.jit.su)","Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"],"license":"MIT","keywords":["mime","types"],"repository":"jshttp/mime-types","dependencies":{"mime-db":"^1.54.0"},"devDependencies":{"eslint":"8.33.0","eslint-config-standard":"14.1.1","eslint-plugin-import":"2.27.5","eslint-plugin-markdown":"3.0.0","eslint-plugin-node":"11.1.0","eslint-plugin-promise":"6.1.1","eslint-plugin-standard":"4.1.0","mocha":"10.2.0","nyc":"15.1.0"},"files":["HISTORY.md","LICENSE","index.js","mimeScore.js"],"engines":{"node":">= 0.6"},"scripts":{"lint":"eslint .","test":"mocha --reporter spec test/test.js","test-ci":"nyc --reporter=lcov --reporter=text npm test","test-cov":"nyc --reporter=html --reporter=text npm test"},"_lastModified":"2026-
|
|
1
|
+
{"name":"mime-types","description":"The ultimate javascript content-type utility.","version":"3.0.1","contributors":["Douglas Christopher Wilson <doug@somethingdoug.com>","Jeremiah Senkpiel <fishrock123@rocketmail.com> (https://searchbeam.jit.su)","Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"],"license":"MIT","keywords":["mime","types"],"repository":"jshttp/mime-types","dependencies":{"mime-db":"^1.54.0"},"devDependencies":{"eslint":"8.33.0","eslint-config-standard":"14.1.1","eslint-plugin-import":"2.27.5","eslint-plugin-markdown":"3.0.0","eslint-plugin-node":"11.1.0","eslint-plugin-promise":"6.1.1","eslint-plugin-standard":"4.1.0","mocha":"10.2.0","nyc":"15.1.0"},"files":["HISTORY.md","LICENSE","index.js","mimeScore.js"],"engines":{"node":">= 0.6"},"scripts":{"lint":"eslint .","test":"mocha --reporter spec test/test.js","test-ci":"nyc --reporter=lcov --reporter=text npm test","test-cov":"nyc --reporter=html --reporter=text npm test"},"_lastModified":"2026-05-29T12:50:23.168Z"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(
|
|
1
|
+
(()=>{var e={186:(e,r,i)=>{var t=i(17);var n=i(147);var a=parseInt("0777",8);e.exports=mkdirP.mkdirp=mkdirP.mkdirP=mkdirP;function mkdirP(e,r,i,f){if(typeof r==="function"){i=r;r={}}else if(!r||typeof r!=="object"){r={mode:r}}var c=r.mode;var o=r.fs||n;if(c===undefined){c=a}if(!f)f=null;var s=i||function(){};e=t.resolve(e);o.mkdir(e,c,(function(i){if(!i){f=f||e;return s(null,f)}switch(i.code){case"ENOENT":if(t.dirname(e)===e)return s(i);mkdirP(t.dirname(e),r,(function(i,t){if(i)s(i,t);else mkdirP(e,r,s,t)}));break;default:o.stat(e,(function(e,r){if(e||!r.isDirectory())s(i,f);else s(null,f)}));break}}))}mkdirP.sync=function sync(e,r,i){if(!r||typeof r!=="object"){r={mode:r}}var f=r.mode;var c=r.fs||n;if(f===undefined){f=a}if(!i)i=null;e=t.resolve(e);try{c.mkdirSync(e,f);i=i||e}catch(n){switch(n.code){case"ENOENT":i=sync(t.dirname(e),r,i);sync(e,r,i);break;default:var o;try{o=c.statSync(e)}catch(e){throw n}if(!o.isDirectory())throw n;break}}return i}},147:e=>{"use strict";e.exports=require("fs")},17:e=>{"use strict";e.exports=require("path")}};var r={};function __nccwpck_require__(i){var t=r[i];if(t!==undefined){return t.exports}var n=r[i]={exports:{}};var a=true;try{e[i](n,n.exports,__nccwpck_require__);a=false}finally{if(a)delete r[i]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var i=__nccwpck_require__(186);module.exports=i})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"mkdirp","description":"Recursively mkdir, like `mkdir -p`","version":"0.5.6","publishConfig":{"tag":"legacy"},"author":"James Halliday <mail@substack.net> (http://substack.net)","main":"index.js","keywords":["mkdir","directory"],"repository":{"type":"git","url":"https://github.com/substack/node-mkdirp.git"},"scripts":{"test":"tap test/*.js"},"dependencies":{"minimist":"^1.2.6"},"devDependencies":{"tap":"^16.0.1"},"bin":"bin/cmd.js","license":"MIT","files":["bin","index.js"],"_lastModified":"2026-
|
|
1
|
+
{"name":"mkdirp","description":"Recursively mkdir, like `mkdir -p`","version":"0.5.6","publishConfig":{"tag":"legacy"},"author":"James Halliday <mail@substack.net> (http://substack.net)","main":"index.js","keywords":["mkdir","directory"],"repository":{"type":"git","url":"https://github.com/substack/node-mkdirp.git"},"scripts":{"test":"tap test/*.js"},"dependencies":{"minimist":"^1.2.6"},"devDependencies":{"tap":"^16.0.1"},"bin":"bin/cmd.js","license":"MIT","files":["bin","index.js"],"_lastModified":"2026-05-29T12:50:27.414Z"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(
|
|
1
|
+
(()=>{var e={821:function(e){(function(r,i,t){if(true&&e.exports)e.exports=t();else if(typeof define==="function"&&define.amd)define(t);else i[r]=t()})("urljoin",this,(function(){function normalize(e){var r=[];if(e.length===0){return""}if(typeof e[0]!=="string"){throw new TypeError("Url must be a string. Received "+e[0])}if(e[0].match(/^[^/:]+:\/*$/)&&e.length>1){var i=e.shift();e[0]=i+e[0]}if(e[0].match(/^file:\/\/\//)){e[0]=e[0].replace(/^([^/:]+):\/*/,"$1:///")}else{e[0]=e[0].replace(/^([^/:]+):\/*/,"$1://")}for(var t=0;t<e.length;t++){var n=e[t];if(typeof n!=="string"){throw new TypeError("Url must be a string. Received "+n)}if(n===""){continue}if(t>0){n=n.replace(/^[\/]+/,"")}if(t<e.length-1){n=n.replace(/[\/]+$/,"")}else{n=n.replace(/[\/]+$/,"/")}r.push(n)}var a=r.join("/");a=a.replace(/\/(\?|&|#[^!])/g,"$1");var f=a.split("?");a=f.shift()+(f.length>0?"?":"")+f.join("&");return a}return function(){var e;if(typeof arguments[0]==="object"){e=arguments[0]}else{e=[].slice.call(arguments)}return normalize(e)}}))}};var r={};function __nccwpck_require__(i){var t=r[i];if(t!==undefined){return t.exports}var n=r[i]={exports:{}};var a=true;try{e[i].call(n.exports,n,n.exports,__nccwpck_require__);a=false}finally{if(a)delete r[i]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var i=__nccwpck_require__(821);module.exports=i})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"url-join","version":"4.0.1","description":"Join urls and normalize as in path.join.","main":"lib/url-join.js","scripts":{"test":"mocha --require should"},"repository":{"type":"git","url":"git://github.com/jfromaniello/url-join.git"},"keywords":["url","join"],"author":"José F. Romaniello <jfromaniello@gmail.com> (http://joseoncode.com)","license":"MIT","devDependencies":{"conventional-changelog":"^1.1.10","mocha":"^3.2.0","should":"~1.2.1"},"_lastModified":"2026-
|
|
1
|
+
{"name":"url-join","version":"4.0.1","description":"Join urls and normalize as in path.join.","main":"lib/url-join.js","scripts":{"test":"mocha --require should"},"repository":{"type":"git","url":"git://github.com/jfromaniello/url-join.git"},"keywords":["url","join"],"author":"José F. Romaniello <jfromaniello@gmail.com> (http://joseoncode.com)","license":"MIT","devDependencies":{"conventional-changelog":"^1.1.10","mocha":"^3.2.0","should":"~1.2.1"},"_lastModified":"2026-05-29T12:50:22.967Z"}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 { Application } from '@nocobase/server';
|
|
10
|
+
import { AttachmentModel, StorageClassType, StorageModel } from '../storages';
|
|
11
|
+
export type RepairFilenameStatus = 'pending' | 'repaired' | 'skipped' | 'failed';
|
|
12
|
+
export interface RepairFilenameItem {
|
|
13
|
+
collectionName: string;
|
|
14
|
+
id: number | string;
|
|
15
|
+
storageId: number | string;
|
|
16
|
+
storageType: string;
|
|
17
|
+
oldPath: string;
|
|
18
|
+
oldFilename: string;
|
|
19
|
+
newPath: string;
|
|
20
|
+
newFilename: string;
|
|
21
|
+
oldKey: string;
|
|
22
|
+
newKey: string;
|
|
23
|
+
sourceExists?: boolean;
|
|
24
|
+
targetExists?: boolean;
|
|
25
|
+
status: RepairFilenameStatus;
|
|
26
|
+
reason?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RepairFilenamesOptions {
|
|
29
|
+
apply?: boolean;
|
|
30
|
+
batchSize?: number;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface RepairFilenamesResult {
|
|
34
|
+
dryRun: boolean;
|
|
35
|
+
scanned: number;
|
|
36
|
+
candidates: RepairFilenameItem[];
|
|
37
|
+
repaired: RepairFilenameItem[];
|
|
38
|
+
skipped: RepairFilenameItem[];
|
|
39
|
+
failed: RepairFilenameItem[];
|
|
40
|
+
}
|
|
41
|
+
interface FileManagerLike {
|
|
42
|
+
storageTypes: {
|
|
43
|
+
get(type: string): StorageClassType | undefined;
|
|
44
|
+
};
|
|
45
|
+
loadStorages(): Promise<void>;
|
|
46
|
+
storagesCache: Map<number | string, StorageModel>;
|
|
47
|
+
}
|
|
48
|
+
export declare function replaceInvisibleChars(value?: string | null): string;
|
|
49
|
+
export declare function getRepairedAttachmentValues(record: Pick<AttachmentModel, 'path' | 'filename'>): {
|
|
50
|
+
path: string;
|
|
51
|
+
filename: string;
|
|
52
|
+
};
|
|
53
|
+
export declare function repairAttachmentFilenames(app: Application, fileManager: FileManagerLike, options?: RepairFilenamesOptions): Promise<RepairFilenamesResult>;
|
|
54
|
+
export declare function registerRepairFilenamesCommand(app: Application): void;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,283 @@
|
|
|
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
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var repair_filenames_exports = {};
|
|
28
|
+
__export(repair_filenames_exports, {
|
|
29
|
+
getRepairedAttachmentValues: () => getRepairedAttachmentValues,
|
|
30
|
+
registerRepairFilenamesCommand: () => registerRepairFilenamesCommand,
|
|
31
|
+
repairAttachmentFilenames: () => repairAttachmentFilenames,
|
|
32
|
+
replaceInvisibleChars: () => replaceInvisibleChars
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(repair_filenames_exports);
|
|
35
|
+
var import_sequelize = require("sequelize");
|
|
36
|
+
var import_utils = require("../utils");
|
|
37
|
+
function containsInvisibleChars(value) {
|
|
38
|
+
return Array.from(value || "").some((char) => {
|
|
39
|
+
const code = char.charCodeAt(0);
|
|
40
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function replaceInvisibleChars(value) {
|
|
44
|
+
return Array.from(value || "").map((char) => {
|
|
45
|
+
const code = char.charCodeAt(0);
|
|
46
|
+
return code <= 31 || code >= 127 && code <= 159 ? "-" : char;
|
|
47
|
+
}).join("");
|
|
48
|
+
}
|
|
49
|
+
function getRepairedAttachmentValues(record) {
|
|
50
|
+
return {
|
|
51
|
+
path: replaceInvisibleChars(record.path),
|
|
52
|
+
filename: replaceInvisibleChars(record.filename)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function updateUrlValue(url, oldKey, newKey) {
|
|
56
|
+
if (!url) {
|
|
57
|
+
return url;
|
|
58
|
+
}
|
|
59
|
+
const encodedOldKey = oldKey.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
60
|
+
const encodedNewKey = newKey.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
61
|
+
return url.split(oldKey).join(newKey).split(encodedOldKey).join(encodedNewKey);
|
|
62
|
+
}
|
|
63
|
+
function formatItem(item) {
|
|
64
|
+
return {
|
|
65
|
+
collectionName: item.collectionName,
|
|
66
|
+
id: item.id,
|
|
67
|
+
storageId: item.storageId,
|
|
68
|
+
storageType: item.storageType,
|
|
69
|
+
oldKey: item.oldKey,
|
|
70
|
+
newKey: item.newKey,
|
|
71
|
+
sourceExists: item.sourceExists,
|
|
72
|
+
targetExists: item.targetExists,
|
|
73
|
+
status: item.status,
|
|
74
|
+
reason: item.reason
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function getFileCollectionNames(app) {
|
|
78
|
+
var _a;
|
|
79
|
+
const repository = app.db.getRepository("collections") || ((_a = app.db.getCollection("collections")) == null ? void 0 : _a.repository);
|
|
80
|
+
if (repository) {
|
|
81
|
+
const collections = await repository.find({
|
|
82
|
+
filter: {
|
|
83
|
+
"options.template": "file"
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return Array.from(/* @__PURE__ */ new Set(["attachments", ...collections.map((collection) => collection.get("name"))]));
|
|
87
|
+
}
|
|
88
|
+
return Array.from(
|
|
89
|
+
/* @__PURE__ */ new Set([
|
|
90
|
+
"attachments",
|
|
91
|
+
...Array.from(app.db.collections.values()).filter((collection) => collection.options.template === "file").map((collection) => collection.name)
|
|
92
|
+
])
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
async function loadFileCollections(app) {
|
|
96
|
+
var _a, _b;
|
|
97
|
+
const collection = app.db.getCollection("collections");
|
|
98
|
+
if (!collection) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const repository = collection.repository;
|
|
102
|
+
await ((_a = repository.setApp) == null ? void 0 : _a.call(repository, app));
|
|
103
|
+
await ((_b = repository.load) == null ? void 0 : _b.call(repository));
|
|
104
|
+
}
|
|
105
|
+
async function repairCollectionAttachmentFilenames({
|
|
106
|
+
app,
|
|
107
|
+
fileManager,
|
|
108
|
+
collectionName,
|
|
109
|
+
result,
|
|
110
|
+
apply,
|
|
111
|
+
batchSize,
|
|
112
|
+
limit
|
|
113
|
+
}) {
|
|
114
|
+
const collection = app.db.getCollection(collectionName);
|
|
115
|
+
if (!collection) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const Model = collection.model;
|
|
119
|
+
let lastId = null;
|
|
120
|
+
while (result.scanned < limit) {
|
|
121
|
+
const rows = await Model.findAll({
|
|
122
|
+
attributes: ["id", "path", "filename", "storageId", "url"],
|
|
123
|
+
where: lastId ? { id: { [import_sequelize.Op.gt]: lastId } } : void 0,
|
|
124
|
+
order: [["id", "ASC"]],
|
|
125
|
+
limit: Math.min(batchSize, limit - result.scanned)
|
|
126
|
+
});
|
|
127
|
+
if (!rows.length) {
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
for (const row of rows) {
|
|
131
|
+
result.scanned += 1;
|
|
132
|
+
lastId = row.get("id");
|
|
133
|
+
const record = row.toJSON();
|
|
134
|
+
if (!containsInvisibleChars(record.path) && !containsInvisibleChars(record.filename)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const storage = fileManager.storagesCache.get(record.storageId);
|
|
138
|
+
const { path: newPath, filename: newFilename } = getRepairedAttachmentValues(record);
|
|
139
|
+
const oldKey = (0, import_utils.getFileKey)(record);
|
|
140
|
+
const newRecord = { ...record, path: newPath, filename: newFilename };
|
|
141
|
+
const newKey = (0, import_utils.getFileKey)(newRecord);
|
|
142
|
+
const item = {
|
|
143
|
+
collectionName,
|
|
144
|
+
id: lastId,
|
|
145
|
+
storageId: record.storageId,
|
|
146
|
+
storageType: (storage == null ? void 0 : storage.type) || "",
|
|
147
|
+
oldPath: record.path || "",
|
|
148
|
+
oldFilename: record.filename,
|
|
149
|
+
newPath,
|
|
150
|
+
newFilename,
|
|
151
|
+
oldKey,
|
|
152
|
+
newKey,
|
|
153
|
+
status: "pending"
|
|
154
|
+
};
|
|
155
|
+
result.candidates.push(item);
|
|
156
|
+
if (!storage) {
|
|
157
|
+
item.status = "skipped";
|
|
158
|
+
item.reason = "storage_not_found";
|
|
159
|
+
result.skipped.push(item);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (oldKey === newKey) {
|
|
163
|
+
item.status = "skipped";
|
|
164
|
+
item.reason = "unchanged";
|
|
165
|
+
result.skipped.push(item);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const StorageClass = fileManager.storageTypes.get(storage.type);
|
|
170
|
+
if (!StorageClass) {
|
|
171
|
+
item.status = "skipped";
|
|
172
|
+
item.reason = "storage_type_not_found";
|
|
173
|
+
result.skipped.push(item);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const storageInstance = new StorageClass(storage);
|
|
177
|
+
item.sourceExists = await storageInstance.exists(record);
|
|
178
|
+
if (!item.sourceExists) {
|
|
179
|
+
item.status = "skipped";
|
|
180
|
+
item.reason = "source_not_found";
|
|
181
|
+
result.skipped.push(item);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
item.targetExists = await storageInstance.exists(newRecord);
|
|
185
|
+
if (item.targetExists) {
|
|
186
|
+
item.status = "skipped";
|
|
187
|
+
item.reason = "target_exists";
|
|
188
|
+
result.skipped.push(item);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (apply) {
|
|
192
|
+
await storageInstance.copy(record, newRecord);
|
|
193
|
+
await row.update({
|
|
194
|
+
path: newPath,
|
|
195
|
+
filename: newFilename,
|
|
196
|
+
url: updateUrlValue(record.url, oldKey, newKey)
|
|
197
|
+
});
|
|
198
|
+
try {
|
|
199
|
+
const [deleted] = await storageInstance.delete([record]);
|
|
200
|
+
if (!deleted) {
|
|
201
|
+
item.reason = "delete_old_failed";
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
item.reason = `delete_old_failed: ${error.message}`;
|
|
205
|
+
}
|
|
206
|
+
item.status = "repaired";
|
|
207
|
+
result.repaired.push(item);
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
item.status = "failed";
|
|
211
|
+
item.reason = error.message;
|
|
212
|
+
result.failed.push(item);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function repairAttachmentFilenames(app, fileManager, options = {}) {
|
|
218
|
+
const apply = !!options.apply;
|
|
219
|
+
const batchSize = options.batchSize || 500;
|
|
220
|
+
const limit = options.limit || Number.POSITIVE_INFINITY;
|
|
221
|
+
const result = {
|
|
222
|
+
dryRun: !apply,
|
|
223
|
+
scanned: 0,
|
|
224
|
+
candidates: [],
|
|
225
|
+
repaired: [],
|
|
226
|
+
skipped: [],
|
|
227
|
+
failed: []
|
|
228
|
+
};
|
|
229
|
+
if (!fileManager.storagesCache.size) {
|
|
230
|
+
await fileManager.loadStorages();
|
|
231
|
+
}
|
|
232
|
+
await loadFileCollections(app);
|
|
233
|
+
for (const collectionName of await getFileCollectionNames(app)) {
|
|
234
|
+
if (result.scanned >= limit) {
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
await repairCollectionAttachmentFilenames({
|
|
238
|
+
app,
|
|
239
|
+
fileManager,
|
|
240
|
+
collectionName,
|
|
241
|
+
result,
|
|
242
|
+
apply,
|
|
243
|
+
batchSize,
|
|
244
|
+
limit
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
function registerRepairFilenamesCommand(app) {
|
|
250
|
+
const command = app.findCommand("file-manager") || app.command("file-manager");
|
|
251
|
+
command.command("repair-filenames").preload().option("--apply", "rename objects and update attachment records").option("--batch-size [batchSize]", "batch size for scanning attachments").option("--limit [limit]", "maximum number of attachment records to scan").action(async (options) => {
|
|
252
|
+
const fileManager = app.pm.get("file-manager");
|
|
253
|
+
const result = await repairAttachmentFilenames(app, fileManager, {
|
|
254
|
+
apply: !!options.apply,
|
|
255
|
+
batchSize: options.batchSize ? Number(options.batchSize) : void 0,
|
|
256
|
+
limit: options.limit ? Number(options.limit) : void 0
|
|
257
|
+
});
|
|
258
|
+
console.log(
|
|
259
|
+
JSON.stringify(
|
|
260
|
+
{
|
|
261
|
+
dryRun: result.dryRun,
|
|
262
|
+
scanned: result.scanned,
|
|
263
|
+
candidates: result.candidates.length,
|
|
264
|
+
repaired: result.repaired.length,
|
|
265
|
+
skipped: result.skipped.length,
|
|
266
|
+
failed: result.failed.length
|
|
267
|
+
},
|
|
268
|
+
null,
|
|
269
|
+
2
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
if (result.candidates.length) {
|
|
273
|
+
console.table(result.candidates.map(formatItem));
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
278
|
+
0 && (module.exports = {
|
|
279
|
+
getRepairedAttachmentValues,
|
|
280
|
+
registerRepairFilenamesCommand,
|
|
281
|
+
repairAttachmentFilenames,
|
|
282
|
+
replaceInvisibleChars
|
|
283
|
+
});
|
package/dist/server/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ import { StorageEngine } from 'multer';
|
|
|
10
10
|
export * from '../constants';
|
|
11
11
|
export { AttachmentModel, default, PluginFileManagerServer, StorageModel } from './server';
|
|
12
12
|
export { cloudFilenameGetter } from './utils';
|
|
13
|
-
export { StorageType } from './storages';
|
|
13
|
+
export { StorageType, GetFileStreamOptions } from './storages';
|
|
14
14
|
export { StorageEngine };
|
package/dist/server/index.js
CHANGED
|
@@ -38,6 +38,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
38
38
|
var server_exports = {};
|
|
39
39
|
__export(server_exports, {
|
|
40
40
|
AttachmentModel: () => import_server.AttachmentModel,
|
|
41
|
+
GetFileStreamOptions: () => import_storages.GetFileStreamOptions,
|
|
41
42
|
PluginFileManagerServer: () => import_server.PluginFileManagerServer,
|
|
42
43
|
StorageEngine: () => import_multer.StorageEngine,
|
|
43
44
|
StorageModel: () => import_server.StorageModel,
|
|
@@ -54,6 +55,7 @@ var import_storages = require("./storages");
|
|
|
54
55
|
// Annotate the CommonJS export names for ESM import in node:
|
|
55
56
|
0 && (module.exports = {
|
|
56
57
|
AttachmentModel,
|
|
58
|
+
GetFileStreamOptions,
|
|
57
59
|
PluginFileManagerServer,
|
|
58
60
|
StorageEngine,
|
|
59
61
|
StorageModel,
|
package/dist/server/server.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { Model, Transactionable } from '@nocobase/database';
|
|
|
11
11
|
import { Plugin } from '@nocobase/server';
|
|
12
12
|
import { Registry } from '@nocobase/utils';
|
|
13
13
|
import { Readable } from 'stream';
|
|
14
|
-
import { AttachmentModel, StorageClassType, StorageModel } from './storages';
|
|
14
|
+
import { AttachmentModel, GetFileStreamOptions, StorageClassType, StorageModel } from './storages';
|
|
15
15
|
export type * from './storages';
|
|
16
16
|
export type FileRecordOptions = {
|
|
17
17
|
collectionName: string;
|
|
@@ -27,6 +27,7 @@ export type UploadFileOptions = {
|
|
|
27
27
|
export declare class PluginFileManagerServer extends Plugin {
|
|
28
28
|
storageTypes: Registry<StorageClassType>;
|
|
29
29
|
storagesCache: Map<string | number, StorageModel>;
|
|
30
|
+
static staticImport(): Promise<void>;
|
|
30
31
|
afterDestroy: (record: Model, options: any) => Promise<void>;
|
|
31
32
|
registerStorageType(type: string, Type: StorageClassType): void;
|
|
32
33
|
createFileRecord(options: FileRecordOptions): Promise<any>;
|
|
@@ -50,7 +51,7 @@ export declare class PluginFileManagerServer extends Plugin {
|
|
|
50
51
|
load(): Promise<void>;
|
|
51
52
|
getFileURL(file: AttachmentModel, preview?: boolean): Promise<any>;
|
|
52
53
|
isPublicAccessStorage(storageName: any): Promise<boolean>;
|
|
53
|
-
getFileStream(file: AttachmentModel): Promise<{
|
|
54
|
+
getFileStream(file: AttachmentModel, options?: GetFileStreamOptions): Promise<{
|
|
54
55
|
stream: Readable;
|
|
55
56
|
contentType?: string;
|
|
56
57
|
}>;
|
package/dist/server/server.js
CHANGED
|
@@ -54,6 +54,7 @@ var import_local = __toESM(require("./storages/local"));
|
|
|
54
54
|
var import_s3 = __toESM(require("./storages/s3"));
|
|
55
55
|
var import_tx_cos = __toESM(require("./storages/tx-cos"));
|
|
56
56
|
var import_utils2 = require("./utils");
|
|
57
|
+
var import_repair_filenames = require("./commands/repair-filenames");
|
|
57
58
|
const DEFAULT_STORAGE_TYPE = import_constants.STORAGE_TYPE_LOCAL;
|
|
58
59
|
class FileDeleteError extends Error {
|
|
59
60
|
data;
|
|
@@ -66,6 +67,9 @@ class FileDeleteError extends Error {
|
|
|
66
67
|
class PluginFileManagerServer extends import_server.Plugin {
|
|
67
68
|
storageTypes = new import_utils.Registry();
|
|
68
69
|
storagesCache = /* @__PURE__ */ new Map();
|
|
70
|
+
static async staticImport() {
|
|
71
|
+
import_server.Application.addCommand(import_repair_filenames.registerRepairFilenamesCommand);
|
|
72
|
+
}
|
|
69
73
|
afterDestroy = async (record, options) => {
|
|
70
74
|
var _a;
|
|
71
75
|
const { collection } = record.constructor;
|
|
@@ -324,7 +328,7 @@ class PluginFileManagerServer extends import_server.Plugin {
|
|
|
324
328
|
}
|
|
325
329
|
return !!((_a = storage.options) == null ? void 0 : _a.public);
|
|
326
330
|
}
|
|
327
|
-
async getFileStream(file) {
|
|
331
|
+
async getFileStream(file, options) {
|
|
328
332
|
if (!file.storageId) {
|
|
329
333
|
throw new Error("File storageId not found");
|
|
330
334
|
}
|
|
@@ -340,7 +344,7 @@ class PluginFileManagerServer extends import_server.Plugin {
|
|
|
340
344
|
if (!storageInstance) {
|
|
341
345
|
throw new Error(`[file-manager] storage type "${storage.type}" is not defined`);
|
|
342
346
|
}
|
|
343
|
-
return storageInstance.getFileStream(file);
|
|
347
|
+
return storageInstance.getFileStream(file, options);
|
|
344
348
|
}
|
|
345
349
|
}
|
|
346
350
|
var server_default = PluginFileManagerServer;
|
|
@@ -18,7 +18,7 @@ declare class AliYunOssStorage {
|
|
|
18
18
|
filename?: typeof getRandomFilename;
|
|
19
19
|
});
|
|
20
20
|
_handleFile(req: any, file: any, cb: any): any;
|
|
21
|
-
_removeFile(req: any, file: any, cb: any): any
|
|
21
|
+
_removeFile(req: any, file: any, cb: any): Promise<any>;
|
|
22
22
|
}
|
|
23
23
|
export default class extends StorageType {
|
|
24
24
|
static defaults(): {
|
|
@@ -34,6 +34,8 @@ export default class extends StorageType {
|
|
|
34
34
|
};
|
|
35
35
|
};
|
|
36
36
|
make(): AliYunOssStorage;
|
|
37
|
+
exists(record: AttachmentModel): Promise<boolean>;
|
|
38
|
+
copy(source: AttachmentModel, target: AttachmentModel): Promise<void>;
|
|
37
39
|
delete(records: AttachmentModel[]): Promise<[number, AttachmentModel[]]>;
|
|
38
40
|
}
|
|
39
41
|
export {};
|
|
@@ -90,11 +90,16 @@ class AliYunOssStorage {
|
|
|
90
90
|
});
|
|
91
91
|
}).catch(cb);
|
|
92
92
|
}
|
|
93
|
-
_removeFile(req, file, cb) {
|
|
93
|
+
async _removeFile(req, file, cb) {
|
|
94
94
|
if (!this.client) {
|
|
95
95
|
return cb(ERROR_NO_CLIENT);
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
try {
|
|
98
|
+
const result = await this.client.delete(file.filename);
|
|
99
|
+
cb(null, result);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
cb(error);
|
|
102
|
+
}
|
|
98
103
|
}
|
|
99
104
|
}
|
|
100
105
|
class ali_oss_default extends import__.StorageType {
|
|
@@ -118,6 +123,22 @@ class ali_oss_default extends import__.StorageType {
|
|
|
118
123
|
filename: (0, import_utils.cloudFilenameGetter)(this.storage)
|
|
119
124
|
});
|
|
120
125
|
}
|
|
126
|
+
async exists(record) {
|
|
127
|
+
const { client } = this.make();
|
|
128
|
+
try {
|
|
129
|
+
await client.head((0, import_utils.getFileKey)(record));
|
|
130
|
+
return true;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
if (["NoSuchKey", "NotFoundError"].includes(error.name)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async copy(source, target) {
|
|
139
|
+
const { client } = this.make();
|
|
140
|
+
await client.copy((0, import_utils.getFileKey)(target), (0, import_utils.getFileKey)(source));
|
|
141
|
+
}
|
|
121
142
|
async delete(records) {
|
|
122
143
|
const { client } = this.make();
|
|
123
144
|
const { deleted } = await client.deleteMulti(records.map(import_utils.getFileKey));
|
|
@@ -24,6 +24,7 @@ export interface StorageModel {
|
|
|
24
24
|
settings?: Record<string, any>;
|
|
25
25
|
}
|
|
26
26
|
export interface AttachmentModel {
|
|
27
|
+
id?: number;
|
|
27
28
|
title: string;
|
|
28
29
|
filename: string;
|
|
29
30
|
mimetype?: string;
|
|
@@ -38,6 +39,8 @@ export declare abstract class StorageType {
|
|
|
38
39
|
constructor(storage: StorageModel);
|
|
39
40
|
abstract make(): StorageEngine;
|
|
40
41
|
abstract delete(records: AttachmentModel[]): [number, AttachmentModel[]] | Promise<[number, AttachmentModel[]]>;
|
|
42
|
+
exists(record: AttachmentModel): Promise<boolean>;
|
|
43
|
+
copy(source: AttachmentModel, target: AttachmentModel): Promise<void>;
|
|
41
44
|
getFileKey(record: AttachmentModel): any;
|
|
42
45
|
getFileData(file: any, meta?: {}): {
|
|
43
46
|
title: string;
|
|
@@ -50,7 +53,7 @@ export declare abstract class StorageType {
|
|
|
50
53
|
storageId: number;
|
|
51
54
|
};
|
|
52
55
|
getFileURL(file: AttachmentModel, preview?: boolean): string | Promise<string>;
|
|
53
|
-
getFileStream(file: AttachmentModel): Promise<{
|
|
56
|
+
getFileStream(file: AttachmentModel, options?: GetFileStreamOptions): Promise<{
|
|
54
57
|
stream: Readable;
|
|
55
58
|
contentType?: string;
|
|
56
59
|
}>;
|
|
@@ -58,3 +61,6 @@ export declare abstract class StorageType {
|
|
|
58
61
|
export type StorageClassType = {
|
|
59
62
|
new (storage: StorageModel): StorageType;
|
|
60
63
|
} & typeof StorageType;
|
|
64
|
+
export type GetFileStreamOptions = {
|
|
65
|
+
requestOptions?: any;
|
|
66
|
+
};
|
|
@@ -52,6 +52,12 @@ class StorageType {
|
|
|
52
52
|
return {};
|
|
53
53
|
}
|
|
54
54
|
static filenameKey;
|
|
55
|
+
async exists(record) {
|
|
56
|
+
throw new Error(`Storage type "${this.storage.type}" does not support object existence checks`);
|
|
57
|
+
}
|
|
58
|
+
async copy(source, target) {
|
|
59
|
+
throw new Error(`Storage type "${this.storage.type}" does not support object copy`);
|
|
60
|
+
}
|
|
55
61
|
getFileKey(record) {
|
|
56
62
|
return (0, import_utils2.getFileKey)(record);
|
|
57
63
|
}
|
|
@@ -88,12 +94,13 @@ class StorageType {
|
|
|
88
94
|
].filter(Boolean);
|
|
89
95
|
return (0, import_url_join.default)(keys);
|
|
90
96
|
}
|
|
91
|
-
async getFileStream(file) {
|
|
97
|
+
async getFileStream(file, options) {
|
|
92
98
|
var _a;
|
|
93
99
|
try {
|
|
94
100
|
const fileURL = await this.getFileURL(file);
|
|
95
101
|
const requestOptions = {
|
|
96
102
|
...(_a = this.storage.settings) == null ? void 0 : _a.requestOptions,
|
|
103
|
+
...(options == null ? void 0 : options.requestOptions) ?? {},
|
|
97
104
|
responseType: "stream",
|
|
98
105
|
validateStatus: (status) => status === 200,
|
|
99
106
|
timeout: 3e4
|
|
@@ -27,6 +27,8 @@ export default class extends StorageType {
|
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
29
|
make(): multer.StorageEngine;
|
|
30
|
+
exists(record: AttachmentModel): Promise<boolean>;
|
|
31
|
+
copy(source: AttachmentModel, target: AttachmentModel): Promise<void>;
|
|
30
32
|
delete(records: AttachmentModel[]): Promise<[number, AttachmentModel[]]>;
|
|
31
33
|
getFileURL(file: AttachmentModel, preview?: boolean): Promise<any>;
|
|
32
34
|
getFileStream(file: AttachmentModel): Promise<{
|
|
@@ -52,11 +52,15 @@ var import_constants = require("../../constants");
|
|
|
52
52
|
var import_utils2 = require("../utils");
|
|
53
53
|
const DEFAULT_BASE_URL = "/storage/uploads";
|
|
54
54
|
function getDocumentRoot(storage) {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
var _a;
|
|
56
|
+
const raw = ((_a = storage == null ? void 0 : storage.options) == null ? void 0 : _a.documentRoot) ?? process.env.LOCAL_STORAGE_DEST ?? (0, import_utils.storagePathJoin)("uploads");
|
|
57
|
+
if (import_path.default.isAbsolute(raw)) {
|
|
58
|
+
return raw;
|
|
59
|
+
}
|
|
60
|
+
return (0, import_utils2.normalizeDocumentRoot)(raw);
|
|
57
61
|
}
|
|
58
62
|
function resolveSafePath(documentRoot, filePath, filename) {
|
|
59
|
-
const root =
|
|
63
|
+
const root = (0, import_utils2.normalizeDocumentRoot)(documentRoot);
|
|
60
64
|
const target = import_path.default.resolve(root, filePath || "", filename || "");
|
|
61
65
|
const relative = import_path.default.relative(root, target);
|
|
62
66
|
if (relative.startsWith("..") || import_path.default.isAbsolute(relative)) {
|
|
@@ -92,6 +96,24 @@ class local_default extends import__.StorageType {
|
|
|
92
96
|
filename: (0, import_utils2.diskFilenameGetter)(this.storage)
|
|
93
97
|
});
|
|
94
98
|
}
|
|
99
|
+
async exists(record) {
|
|
100
|
+
try {
|
|
101
|
+
await import_promises.default.stat(resolveSafePath(getDocumentRoot(this.storage), record.path, record.filename));
|
|
102
|
+
return true;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
if (error.code === "ENOENT") {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async copy(source, target) {
|
|
111
|
+
const documentRoot = getDocumentRoot(this.storage);
|
|
112
|
+
const sourcePath = resolveSafePath(documentRoot, source.path, source.filename);
|
|
113
|
+
const targetPath = resolveSafePath(documentRoot, target.path, target.filename);
|
|
114
|
+
await import_promises.default.mkdir(import_path.default.dirname(targetPath), { recursive: true });
|
|
115
|
+
await import_promises.default.copyFile(sourcePath, targetPath);
|
|
116
|
+
}
|
|
95
117
|
async delete(records) {
|
|
96
118
|
const documentRoot = getDocumentRoot(this.storage);
|
|
97
119
|
let count = 0;
|
|
@@ -32,5 +32,7 @@ export default class extends StorageType {
|
|
|
32
32
|
deleteS3Objects(bucketName: string, objects: string[]): Promise<{
|
|
33
33
|
Deleted: any[];
|
|
34
34
|
}>;
|
|
35
|
+
exists(record: AttachmentModel): Promise<boolean>;
|
|
36
|
+
copy(source: AttachmentModel, target: AttachmentModel): Promise<void>;
|
|
35
37
|
delete(records: AttachmentModel[]): Promise<[number, AttachmentModel[]]>;
|
|
36
38
|
}
|