@nocobase/plugin-file-manager 1.8.0-beta.9 → 1.8.1

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.
@@ -0,0 +1,52 @@
1
+ // 'mime-score' back-ported to CommonJS
2
+
3
+ // Score RFC facets (see https://tools.ietf.org/html/rfc6838#section-3)
4
+ var FACET_SCORES = {
5
+ 'prs.': 100,
6
+ 'x-': 200,
7
+ 'x.': 300,
8
+ 'vnd.': 400,
9
+ default: 900
10
+ }
11
+
12
+ // Score mime source (Logic originally from `jshttp/mime-types` module)
13
+ var SOURCE_SCORES = {
14
+ nginx: 10,
15
+ apache: 20,
16
+ iana: 40,
17
+ default: 30 // definitions added by `jshttp/mime-db` project?
18
+ }
19
+
20
+ var TYPE_SCORES = {
21
+ // prefer application/xml over text/xml
22
+ // prefer application/rtf over text/rtf
23
+ application: 1,
24
+
25
+ // prefer font/woff over application/font-woff
26
+ font: 2,
27
+
28
+ default: 0
29
+ }
30
+
31
+ /**
32
+ * Get each component of the score for a mime type. The sum of these is the
33
+ * total score. The higher the score, the more "official" the type.
34
+ */
35
+ module.exports = function mimeScore (mimeType, source = 'default') {
36
+ if (mimeType === 'application/octet-stream') {
37
+ return 0
38
+ }
39
+
40
+ const [type, subtype] = mimeType.split('/')
41
+
42
+ const facet = subtype.replace(/(\.|x-).*/, '$1')
43
+
44
+ const facetScore = FACET_SCORES[facet] || FACET_SCORES.default
45
+ const sourceScore = SOURCE_SCORES[source] || SOURCE_SCORES.default
46
+ const typeScore = TYPE_SCORES[type] || TYPE_SCORES.default
47
+
48
+ // All else being equal prefer shorter types
49
+ const lengthScore = 1 - mimeType.length / 100
50
+
51
+ return facetScore + sourceScore + typeScore + lengthScore
52
+ }
@@ -0,0 +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":"2025-07-09T08:38:42.224Z"}
@@ -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":"2025-06-17T23:39:56.309Z"}
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":"2025-07-09T08:38:46.490Z"}
@@ -1 +1 @@
1
- {"name":"multer-aliyun-oss","version":"2.1.3","description":"Multer Storage for AliYun OSS","main":"index.js","scripts":{},"author":"AngusYoung","license":"ISC","repository":{"type":"git","url":"git+https://github.com/ay86/multer-aliyun-oss.git"},"keywords":["multer","storage","aliyun","oss"],"dependencies":{"ali-oss":"^6.1.0"},"_lastModified":"2025-06-17T23:39:56.229Z"}
1
+ {"name":"multer-aliyun-oss","version":"2.1.3","description":"Multer Storage for AliYun OSS","main":"index.js","scripts":{},"author":"AngusYoung","license":"ISC","repository":{"type":"git","url":"git+https://github.com/ay86/multer-aliyun-oss.git"},"keywords":["multer","storage","aliyun","oss"],"dependencies":{"ali-oss":"^6.1.0"},"_lastModified":"2025-07-09T08:38:46.415Z"}
@@ -1 +1 @@
1
- {"_from":"multer-cos","_id":"multer-cos@1.0.2","_inBundle":false,"_integrity":"sha512-4F8P1VTCSNhiem+BFJFLe3Ixco6cIuAQ6j7U+PBRvdbBJRZgq5Q+vaDMMBWJ1HmPGOOP3AyKS5yk2f0nbFoqqA==","_location":"/multer-cos","_phantomChildren":{},"_requested":{"type":"tag","registry":true,"raw":"multer-cos","name":"multer-cos","escapedName":"multer-cos","rawSpec":"","saveSpec":null,"fetchSpec":"latest"},"_requiredBy":["#USER","/"],"_resolved":"https://registry.npmjs.org/multer-cos/-/multer-cos-1.0.2.tgz","_shasum":"95c7c06cdee1b9311675a895481f9d946de6dcf3","_spec":"multer-cos","_where":"/Users/lanbo/workplace/websocket","author":{"name":"lanbosm"},"bugs":{"url":"https://github.com/lanbosm/multer-COS/issues"},"bundleDependencies":false,"deprecated":false,"description":"Streaming multer storage engine for QCloud COS","devDependencies":{"cos-nodejs-sdk-v5":"^2.2.6","dotenv":"^5.0.1","multer":"^1.3.0"},"engines":{"node":">= 6.10.0"},"homepage":"https://github.com/lanbosm/multer-COS#readme","keywords":["express","multer","COS"],"license":"MIT","main":"index.js","name":"multer-cos","repository":{"type":"git","url":"git+ssh://git@github.com/lanbosm/multer-COS.git"},"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"version":"1.0.3","_lastModified":"2025-06-17T23:40:02.901Z"}
1
+ {"_from":"multer-cos","_id":"multer-cos@1.0.2","_inBundle":false,"_integrity":"sha512-4F8P1VTCSNhiem+BFJFLe3Ixco6cIuAQ6j7U+PBRvdbBJRZgq5Q+vaDMMBWJ1HmPGOOP3AyKS5yk2f0nbFoqqA==","_location":"/multer-cos","_phantomChildren":{},"_requested":{"type":"tag","registry":true,"raw":"multer-cos","name":"multer-cos","escapedName":"multer-cos","rawSpec":"","saveSpec":null,"fetchSpec":"latest"},"_requiredBy":["#USER","/"],"_resolved":"https://registry.npmjs.org/multer-cos/-/multer-cos-1.0.2.tgz","_shasum":"95c7c06cdee1b9311675a895481f9d946de6dcf3","_spec":"multer-cos","_where":"/Users/lanbo/workplace/websocket","author":{"name":"lanbosm"},"bugs":{"url":"https://github.com/lanbosm/multer-COS/issues"},"bundleDependencies":false,"deprecated":false,"description":"Streaming multer storage engine for QCloud COS","devDependencies":{"cos-nodejs-sdk-v5":"^2.2.6","dotenv":"^5.0.1","multer":"^1.3.0"},"engines":{"node":">= 6.10.0"},"homepage":"https://github.com/lanbosm/multer-COS#readme","keywords":["express","multer","COS"],"license":"MIT","main":"index.js","name":"multer-cos","repository":{"type":"git","url":"git+ssh://git@github.com/lanbosm/multer-COS.git"},"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"version":"1.0.3","_lastModified":"2025-07-09T08:38:53.031Z"}
@@ -1 +1 @@
1
- {"name":"multer-s3","version":"3.0.1","description":"Streaming multer storage engine for AWS S3","main":"index.js","scripts":{"test":"standard && mocha test/basic.js"},"engines":{"node":">= 12.0.0"},"repository":{"type":"git","url":"git+https://github.com/badunk/multer-s3.git"},"keywords":["multer","s3","amazon","aws"],"author":"badunk","license":"MIT","bugs":{"url":"https://github.com/badunk/multer-s3/issues"},"homepage":"https://github.com/badunk/multer-s3#readme","dependencies":{"@aws-sdk/lib-storage":"^3.46.0","file-type":"^3.3.0","html-comment-regex":"^1.1.2","run-parallel":"^1.1.6"},"peerDependencies":{"@aws-sdk/client-s3":"^3.0.0"},"devDependencies":{"express":"^4.13.1","form-data":"^1.0.0-rc3","mocha":"^2.2.5","multer":"^1.1.0","on-finished":"^2.3.0","standard":"^5.4.1","xtend":"^4.0.1"},"_lastModified":"2025-06-17T23:39:59.381Z"}
1
+ {"name":"multer-s3","version":"3.0.1","description":"Streaming multer storage engine for AWS S3","main":"index.js","scripts":{"test":"standard && mocha test/basic.js"},"engines":{"node":">= 12.0.0"},"repository":{"type":"git","url":"git+https://github.com/badunk/multer-s3.git"},"keywords":["multer","s3","amazon","aws"],"author":"badunk","license":"MIT","bugs":{"url":"https://github.com/badunk/multer-s3/issues"},"homepage":"https://github.com/badunk/multer-s3#readme","dependencies":{"@aws-sdk/lib-storage":"^3.46.0","file-type":"^3.3.0","html-comment-regex":"^1.1.2","run-parallel":"^1.1.6"},"peerDependencies":{"@aws-sdk/client-s3":"^3.0.0"},"devDependencies":{"express":"^4.13.1","form-data":"^1.0.0-rc3","mocha":"^2.2.5","multer":"^1.1.0","on-finished":"^2.3.0","standard":"^5.4.1","xtend":"^4.0.1"},"_lastModified":"2025-07-09T08:38:49.290Z"}
@@ -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":"2025-06-17T23:39:51.723Z"}
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":"2025-07-09T08:38:42.035Z"}
@@ -39,37 +39,129 @@ __export(attachments_exports, {
39
39
  createMiddleware: () => createMiddleware
40
40
  });
41
41
  module.exports = __toCommonJS(attachments_exports);
42
+ var import_stream = require("stream");
43
+ var import_mime_match = __toESM(require("mime-match"));
44
+ var import_mime_types = __toESM(require("mime-types"));
42
45
  var import_utils = require("@nocobase/utils");
43
46
  var import__ = __toESM(require(".."));
44
47
  var import_constants = require("../../constants");
45
- var Rules = __toESM(require("../rules"));
46
- function getFileFilter(storage) {
47
- return (req, file, cb) => {
48
- const { size, ...rules } = storage.rules;
49
- const ruleKeys = Object.keys(rules);
50
- const result = !ruleKeys.length || !ruleKeys.some((key) => typeof Rules[key] !== "function" || !Rules[key](file, rules[key]));
51
- cb(null, result);
48
+ function makeMulterStorage(storage) {
49
+ const innerStorage = storage.make();
50
+ return {
51
+ _handleFile(req, file, cb) {
52
+ const pattern = storage.storage.rules.mimetype;
53
+ const peekSize = 4100;
54
+ const originalStream = file.stream;
55
+ const passThrough = new import_stream.PassThrough();
56
+ const proxyFile = { ...file, stream: passThrough };
57
+ let detectedMime = null;
58
+ const finalCallback = (err, result) => {
59
+ if (err) {
60
+ return cb(err);
61
+ }
62
+ if (detectedMime && result) {
63
+ result.mimetype = detectedMime;
64
+ }
65
+ cb(null, result);
66
+ };
67
+ innerStorage._handleFile(req, proxyFile, finalCallback);
68
+ const chunks = [];
69
+ let bytesRead = 0;
70
+ let validationTriggered = false;
71
+ const cleanup = () => {
72
+ originalStream.removeListener("data", onData);
73
+ originalStream.removeListener("end", onEnd);
74
+ originalStream.removeListener("error", onError);
75
+ };
76
+ const onData = (chunk) => {
77
+ if (validationTriggered) return;
78
+ chunks.push(chunk);
79
+ bytesRead += chunk.length;
80
+ if (bytesRead >= peekSize) {
81
+ validationTriggered = true;
82
+ cleanup();
83
+ originalStream.pause();
84
+ validate(Buffer.concat(chunks));
85
+ }
86
+ };
87
+ const onEnd = () => {
88
+ if (!validationTriggered) {
89
+ validationTriggered = true;
90
+ cleanup();
91
+ validate(Buffer.concat(chunks));
92
+ }
93
+ };
94
+ const onError = (err) => {
95
+ cleanup();
96
+ passThrough.destroy(err);
97
+ };
98
+ const validate = async (header) => {
99
+ try {
100
+ const { fileTypeFromBuffer } = await import("file-type");
101
+ const type = await fileTypeFromBuffer(new Uint8Array(header));
102
+ if (type) {
103
+ detectedMime = type.mime;
104
+ } else {
105
+ const fromFilename = import_mime_types.default.lookup(file.originalname);
106
+ if (fromFilename) {
107
+ detectedMime = fromFilename;
108
+ }
109
+ }
110
+ if (!detectedMime || pattern !== "*" && !pattern.toString().split(",").some((0, import_mime_match.default)(detectedMime))) {
111
+ const err = new Error("Mime type not allowed by storage rule");
112
+ err.name = "MulterError";
113
+ originalStream.destroy();
114
+ passThrough.destroy();
115
+ return cb(err);
116
+ }
117
+ passThrough.write(header, (writeErr) => {
118
+ if (writeErr) {
119
+ originalStream.destroy();
120
+ passThrough.destroy();
121
+ return cb(writeErr);
122
+ }
123
+ if (originalStream.readableEnded) {
124
+ passThrough.end();
125
+ } else {
126
+ originalStream.pipe(passThrough);
127
+ originalStream.resume();
128
+ }
129
+ });
130
+ } catch (err) {
131
+ originalStream.destroy();
132
+ passThrough.destroy();
133
+ return cb(err);
134
+ }
135
+ };
136
+ originalStream.on("data", onData);
137
+ originalStream.on("end", onEnd);
138
+ originalStream.on("error", onError);
139
+ },
140
+ _removeFile(req, file, cb) {
141
+ innerStorage._removeFile(req, file, cb);
142
+ }
52
143
  };
53
144
  }
54
145
  async function multipart(ctx, next) {
146
+ var _a;
55
147
  const { storage } = ctx;
56
148
  if (!storage) {
57
149
  ctx.logger.error("[file-manager] no linked or default storage provided");
58
150
  return ctx.throw(500);
59
151
  }
60
- const StorageType = ctx.app.pm.get(import__.default).storageTypes.get(storage.type);
61
- if (!StorageType) {
152
+ const StorageClass = ctx.app.pm.get(import__.default).storageTypes.get(storage.type);
153
+ if (!StorageClass) {
62
154
  ctx.logger.error(`[file-manager] storage type "${storage.type}" is not defined`);
63
155
  return ctx.throw(500);
64
156
  }
65
- const storageInstance = new StorageType(storage);
157
+ const storageInstance = new StorageClass(storage);
66
158
  const multerOptions = {
67
- fileFilter: getFileFilter(storage),
159
+ // fileFilter: getFileFilter(storageInstance),
68
160
  limits: {
69
161
  // 每次只允许提交一个文件
70
162
  files: import_constants.LIMIT_FILES
71
163
  },
72
- storage: storageInstance.make()
164
+ storage: ((_a = storage.rules) == null ? void 0 : _a.mimetype) ? makeMulterStorage(storageInstance) : storageInstance.make()
73
165
  };
74
166
  multerOptions.limits["fileSize"] = Math.max(import_constants.FILE_SIZE_LIMIT_MIN, storage.rules.size ?? import_constants.FILE_SIZE_LIMIT_DEFAULT);
75
167
  const upload = (0, import_utils.koaMulter)(multerOptions).single(import_constants.FILE_FIELD_NAME);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/plugin-file-manager",
3
- "version": "1.8.0-beta.9",
3
+ "version": "1.8.1",
4
4
  "displayName": "File manager",
5
5
  "displayName.zh-CN": "文件管理器",
6
6
  "description": "Provides files storage services with files collection template and attachment field.",
@@ -17,22 +17,28 @@
17
17
  "@formily/shared": "2.x",
18
18
  "@koa/multer": "^3.1.0",
19
19
  "@types/koa-multer": "^1.0.4",
20
+ "@types/mime-types": "^3.0.1",
20
21
  "@types/multer": "^1.4.5",
21
22
  "antd": "5.x",
22
23
  "axios": "^1.7.0",
23
24
  "cos-nodejs-sdk-v5": "^2.11.14",
24
25
  "koa-static": "^5.0.0",
25
26
  "mime-match": "^1.0.2",
27
+ "mime-types": "^3.0.1",
26
28
  "mkdirp": "~0.5.4",
27
29
  "multer": "^1.4.5-lts.2",
28
30
  "multer-aliyun-oss": "2.1.3",
29
31
  "multer-cos": "^1.0.3",
30
32
  "multer-s3": "^3.0.1",
33
+ "multistream": "^4.1.0",
31
34
  "react": "^18.2.0",
32
35
  "react-i18next": "^11.15.1",
33
36
  "supertest": "^6.1.6",
34
37
  "url-join": "4.0.1"
35
38
  },
39
+ "dependencies": {
40
+ "file-type": "^21.0.0"
41
+ },
36
42
  "peerDependencies": {
37
43
  "@nocobase/actions": "1.x",
38
44
  "@nocobase/client": "1.x",
@@ -45,5 +51,5 @@
45
51
  "Collections",
46
52
  "Collection fields"
47
53
  ],
48
- "gitHead": "a3449d646c72965845f8c52e52fff9dba759c564"
54
+ "gitHead": "85bbe64895160a66603856c6ff0c05e4ebb5fbc2"
49
55
  }