@nocobase/plugin-file-manager 0.5.0-alpha.38 → 0.7.0-alpha.10

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 (85) hide show
  1. package/LICENSE +201 -21
  2. package/esm/actions/upload.d.ts +3 -0
  3. package/esm/actions/upload.js +143 -0
  4. package/esm/actions/upload.js.map +1 -0
  5. package/esm/collections/attachments.d.ts +3 -0
  6. package/esm/collections/attachments.js +59 -0
  7. package/esm/collections/attachments.js.map +1 -0
  8. package/esm/collections/storages.d.ts +3 -0
  9. package/esm/collections/storages.js +60 -0
  10. package/esm/collections/storages.js.map +1 -0
  11. package/esm/constants.d.ts +6 -0
  12. package/esm/constants.js +7 -0
  13. package/esm/constants.js.map +1 -0
  14. package/esm/index.d.ts +2 -0
  15. package/esm/index.js +3 -0
  16. package/esm/index.js.map +1 -0
  17. package/esm/rules/index.d.ts +1 -0
  18. package/esm/rules/index.js +3 -0
  19. package/esm/rules/index.js.map +1 -0
  20. package/esm/rules/mimetype.d.ts +1 -0
  21. package/esm/rules/mimetype.js +5 -0
  22. package/esm/rules/mimetype.js.map +1 -0
  23. package/esm/server.d.ts +7 -0
  24. package/esm/server.js +48 -0
  25. package/esm/server.js.map +1 -0
  26. package/esm/storages/ali-oss.d.ts +16 -0
  27. package/esm/storages/ali-oss.js +26 -0
  28. package/esm/storages/ali-oss.js.map +1 -0
  29. package/esm/storages/index.d.ts +8 -0
  30. package/esm/storages/index.js +12 -0
  31. package/esm/storages/index.js.map +1 -0
  32. package/esm/storages/local.d.ts +14 -0
  33. package/esm/storages/local.js +127 -0
  34. package/esm/storages/local.js.map +1 -0
  35. package/esm/storages/s3.d.ts +17 -0
  36. package/esm/storages/s3.js +53 -0
  37. package/esm/storages/s3.js.map +1 -0
  38. package/esm/utils.d.ts +2 -0
  39. package/esm/utils.js +16 -0
  40. package/esm/utils.js.map +1 -0
  41. package/lib/actions/upload.js +145 -228
  42. package/lib/actions/upload.js.map +1 -1
  43. package/lib/collections/attachments.d.ts +2 -2
  44. package/lib/collections/attachments.js +58 -56
  45. package/lib/collections/attachments.js.map +1 -1
  46. package/lib/collections/storages.d.ts +2 -2
  47. package/lib/collections/storages.js +59 -62
  48. package/lib/collections/storages.js.map +1 -1
  49. package/lib/constants.js +2 -17
  50. package/lib/constants.js.map +1 -1
  51. package/lib/index.js +14 -47
  52. package/lib/index.js.map +1 -1
  53. package/lib/rules/index.js +6 -30
  54. package/lib/rules/index.js.map +1 -1
  55. package/lib/rules/mimetype.js +5 -26
  56. package/lib/rules/mimetype.js.map +1 -1
  57. package/lib/server.d.ts +7 -3
  58. package/lib/server.js +46 -95
  59. package/lib/server.js.map +1 -1
  60. package/lib/storages/ali-oss.js +23 -44
  61. package/lib/storages/ali-oss.js.map +1 -1
  62. package/lib/storages/index.js +5 -31
  63. package/lib/storages/index.js.map +1 -1
  64. package/lib/storages/local.d.ts +2 -1
  65. package/lib/storages/local.js +110 -198
  66. package/lib/storages/local.js.map +1 -1
  67. package/lib/storages/s3.js +49 -84
  68. package/lib/storages/s3.js.map +1 -1
  69. package/lib/utils.js +14 -39
  70. package/lib/utils.js.map +1 -1
  71. package/package.json +17 -3
  72. package/tsconfig.build.json +9 -0
  73. package/lib/actions/upload.d.ts.map +0 -1
  74. package/lib/collections/attachments.d.ts.map +0 -1
  75. package/lib/collections/storages.d.ts.map +0 -1
  76. package/lib/constants.d.ts.map +0 -1
  77. package/lib/index.d.ts.map +0 -1
  78. package/lib/rules/index.d.ts.map +0 -1
  79. package/lib/rules/mimetype.d.ts.map +0 -1
  80. package/lib/server.d.ts.map +0 -1
  81. package/lib/storages/ali-oss.d.ts.map +0 -1
  82. package/lib/storages/index.d.ts.map +0 -1
  83. package/lib/storages/local.d.ts.map +0 -1
  84. package/lib/storages/s3.d.ts.map +0 -1
  85. package/lib/utils.d.ts.map +0 -1
package/lib/utils.js CHANGED
@@ -1,49 +1,24 @@
1
1
  "use strict";
2
-
3
- function _react() {
4
- const data = _interopRequireDefault(require("react"));
5
-
6
- _react = function _react() {
7
- return data;
8
- };
9
-
10
- return data;
11
- }
12
-
13
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
-
15
- var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
16
- return mod && mod.__esModule ? mod : {
17
- "default": mod
18
- };
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
4
  };
20
-
21
- Object.defineProperty(exports, "__esModule", {
22
- value: true
23
- });
5
+ Object.defineProperty(exports, "__esModule", { value: true });
24
6
  exports.cloudFilenameGetter = exports.getFilename = void 0;
25
-
26
7
  const crypto_1 = __importDefault(require("crypto"));
27
-
28
8
  const path_1 = __importDefault(require("path"));
29
-
30
9
  function getFilename(req, file, cb) {
31
- crypto_1.default.pseudoRandomBytes(16, function (err, raw) {
32
- cb(err, err ? undefined : `${raw.toString('hex')}${path_1.default.extname(file.originalname)}`);
33
- });
10
+ crypto_1.default.pseudoRandomBytes(16, function (err, raw) {
11
+ cb(err, err ? undefined : `${raw.toString('hex')}${path_1.default.extname(file.originalname)}`);
12
+ });
34
13
  }
35
-
36
14
  exports.getFilename = getFilename;
37
-
38
- const cloudFilenameGetter = storage => (req, file, cb) => {
39
- getFilename(req, file, (err, filename) => {
40
- if (err) {
41
- return cb(err);
42
- }
43
-
44
- cb(null, `${storage.path ? `${storage.path}/` : ''}${filename}`);
45
- });
15
+ const cloudFilenameGetter = (storage) => (req, file, cb) => {
16
+ getFilename(req, file, (err, filename) => {
17
+ if (err) {
18
+ return cb(err);
19
+ }
20
+ cb(null, `${storage.path ? `${storage.path}/` : ''}${filename}`);
21
+ });
46
22
  };
47
-
48
23
  exports.cloudFilenameGetter = cloudFilenameGetter;
49
- //# sourceMappingURL=utils.js.map
24
+ //# sourceMappingURL=utils.js.map
package/lib/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["utils.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAC5B,gDAAwB;AAExB,SAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACvC,gBAAM,CAAC,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,EAAE,GAAG;QAC7C,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACvF,CAAC,CAAC,CAAC;AACL,CAAC;AAJD,kCAIC;AAEM,MAAM,mBAAmB,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE;IAC9D,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;QACvC,IAAI,GAAG,EAAE;YACP,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;SAChB;QACD,EAAE,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAA;AAPY,QAAA,mBAAmB,uBAO/B","file":"utils.js","sourcesContent":["import crypto from 'crypto';\nimport path from 'path';\n\nexport function getFilename(req, file, cb) {\n crypto.pseudoRandomBytes(16, function (err, raw) {\n cb(err, err ? undefined : `${raw.toString('hex')}${path.extname(file.originalname)}`)\n });\n}\n\nexport const cloudFilenameGetter = storage => (req, file, cb) => {\n getFilename(req, file, (err, filename) => {\n if (err) {\n return cb(err);\n }\n cb(null, `${storage.path ? `${storage.path}/` : ''}${filename}`);\n });\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAC5B,gDAAwB;AAExB,SAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;IACvC,gBAAM,CAAC,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,EAAE,GAAG;QAC7C,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;AACL,CAAC;AAJD,kCAIC;AAEM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE;IAChE,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;QACvC,IAAI,GAAG,EAAE;YACP,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;SAChB;QACD,EAAE,CAAC,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAPW,QAAA,mBAAmB,uBAO9B","sourcesContent":["import crypto from 'crypto';\nimport path from 'path';\n\nexport function getFilename(req, file, cb) {\n crypto.pseudoRandomBytes(16, function (err, raw) {\n cb(err, err ? undefined : `${raw.toString('hex')}${path.extname(file.originalname)}`);\n });\n}\n\nexport const cloudFilenameGetter = (storage) => (req, file, cb) => {\n getFilename(req, file, (err, filename) => {\n if (err) {\n return cb(err);\n }\n cb(null, `${storage.path ? `${storage.path}/` : ''}${filename}`);\n });\n};\n"]}
package/package.json CHANGED
@@ -1,10 +1,22 @@
1
1
  {
2
2
  "name": "@nocobase/plugin-file-manager",
3
- "version": "0.5.0-alpha.38",
3
+ "version": "0.7.0-alpha.10",
4
4
  "main": "lib/index.js",
5
- "license": "MIT",
5
+ "license": "Apache-2.0",
6
+ "licenses": [
7
+ {
8
+ "type": "Apache-2.0",
9
+ "url": "http://www.apache.org/licenses/LICENSE-2.0"
10
+ }
11
+ ],
12
+ "scripts": {
13
+ "build": "rimraf -rf lib esm dist && npm run build:cjs && npm run build:esm",
14
+ "build:cjs": "tsc --project tsconfig.build.json",
15
+ "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm"
16
+ },
6
17
  "dependencies": {
7
18
  "@koa/multer": "^3.0.0",
19
+ "@nocobase/server": "0.7.0-alpha.10",
8
20
  "aws-sdk": "^2.2.32",
9
21
  "koa-static": "^5.0.0",
10
22
  "mime-match": "^1.0.2",
@@ -13,7 +25,9 @@
13
25
  "multer-s3": "^2.10.0"
14
26
  },
15
27
  "devDependencies": {
28
+ "@nocobase/test": "0.7.0-alpha.10",
29
+ "@types/koa-multer": "^1.0.1",
16
30
  "@types/multer": "^1.4.5"
17
31
  },
18
- "gitHead": "ac34f5b49e78963b9d8f03159f02cf15b450c0b5"
32
+ "gitHead": "8144cfdbea320a9e6e1c228e320f8acea02d98b3"
19
33
  }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../../tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "declaration": true
6
+ },
7
+ "include": ["./src/**/*.ts", "./src/**/*.tsx"],
8
+ "exclude": ["./src/__tests__/*", "./esm/*", "./lib/*"]
9
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["actions/upload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AA2BlD,wBAAsB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,gBAkDxD;AAED,wBAAsB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,kBAkDpD","file":"upload.d.ts","sourcesContent":["import path from 'path';\nimport multer from '@koa/multer';\nimport { Context, Next } from '@nocobase/actions';\nimport { getStorageConfig } from '../storages';\nimport * as Rules from '../rules';\nimport { FILE_FIELD_NAME, LIMIT_FILES, LIMIT_MAX_FILE_SIZE } from '../constants';\n\nfunction getRules(ctx: Context) {\n const { resourceField } = ctx.action.params;\n if (!resourceField) {\n return ctx.storage.rules;\n }\n const { rules = {} } = resourceField.getOptions().attachment || {};\n return Object.assign({}, ctx.storage.rules, rules);\n}\n\n// TODO(optimize): 需要优化错误处理,计算失败后需要抛出对应错误,以便程序处理\nfunction getFileFilter(ctx: Context) {\n return (req, file, cb) => {\n // size 交给 limits 处理\n const { size, ...rules } = getRules(ctx);\n const ruleKeys = Object.keys(rules);\n const result = !ruleKeys.length || !ruleKeys\n .some(key => typeof Rules[key] !== 'function'\n || !Rules[key](file, rules[key], ctx));\n cb(null, result);\n }\n}\n\nexport async function middleware(ctx: Context, next: Next) {\n const { resourceName, actionName, resourceField } = ctx.action.params;\n if (actionName !== 'upload') {\n return next();\n }\n\n // NOTE:\n // 1. 存储引擎选择依赖于字段定义\n // 2. 字段定义中需包含引擎的外键值\n // 3. 无字段时按 storages 表的默认项\n // 4. 插件初始化后应提示用户添加至少一个存储引擎并设为默认\n\n const StorageModel = ctx.db.getModel('storages');\n let storage;\n\n if (resourceName === 'attachments') {\n // 如果没有包含关联,则直接按默认文件上传至默认存储引擎\n storage = await StorageModel.findOne({ where: { default: true } });\n } else {\n const { attachment = {} } = resourceField.getOptions();\n storage = await StorageModel.findOne({\n where: attachment.storage\n ? { name: attachment.storage }\n : { default: true }\n });\n }\n\n if (!storage) {\n console.error('[file-manager] no default or linked storage provided');\n return ctx.throw(500);\n }\n // 传递已取得的存储引擎,避免重查\n ctx.storage = storage;\n\n const storageConfig = getStorageConfig(storage.type);\n if (!storageConfig) {\n console.error(`[file-manager] storage type \"${storage.type}\" is not defined`);\n return ctx.throw(500);\n }\n const multerOptions = {\n fileFilter: getFileFilter(ctx),\n limits: {\n fileSize: Math.min(getRules(ctx).size || LIMIT_MAX_FILE_SIZE, LIMIT_MAX_FILE_SIZE),\n // 每次只允许提交一个文件\n files: LIMIT_FILES\n },\n storage: storageConfig.make(storage),\n };\n const upload = multer(multerOptions).single(FILE_FIELD_NAME);\n return upload(ctx, next);\n};\n\nexport async function action(ctx: Context, next: Next) {\n const { [FILE_FIELD_NAME]: file, storage } = ctx;\n if (!file) {\n return ctx.throw(400, 'file validation failed');\n }\n\n const storageConfig = getStorageConfig(storage.type);\n const { [storageConfig.filenameKey || 'filename']: name } = file;\n // make compatible filename across cloud service (with path)\n const filename = path.basename(name);\n const extname = path.extname(filename);\n const urlPath = storage.path\n ? storage.path.replace(/^([^\\/])/, '/$1')\n : '';\n\n const data = {\n title: file.originalname.replace(extname, ''),\n filename,\n extname,\n // TODO(feature): 暂时两者相同,后面 storage.path 模版化以后,这里只是 file 实际的 path\n path: storage.path,\n size: file.size,\n // 直接缓存起来\n url: `${storage.baseUrl}${urlPath}/${filename}`,\n mimetype: file.mimetype,\n // @ts-ignore\n meta: ctx.request.body,\n ...(storageConfig.getFileData ? storageConfig.getFileData(file) : {})\n };\n \n const attachment = await ctx.db.sequelize.transaction(async transaction => {\n // TODO(optimize): 应使用关联 accessors 获取\n const result = await storage.createAttachment(data, { transaction });\n \n const { associatedName, associatedIndex, resourceField } = ctx.action.params;\n if (associatedIndex && resourceField) {\n const Attachment = ctx.db.getModel('attachments');\n const SourceModel = ctx.db.getModel(associatedName);\n const source = await SourceModel.findByPk(associatedIndex, { transaction });\n await source[resourceField.getAccessors().set](result[Attachment.primaryKeyAttribute], { transaction });\n }\n\n return result;\n });\n\n // 将存储引擎的信息附在已创建的记录里,节省一次查询\n // attachment.setDataValue('storage', storage);\n ctx.body = attachment;\n\n await next();\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["collections/attachments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;;AAElD,wBAyDkB","file":"attachments.d.ts","sourcesContent":["import { TableOptions } from '@nocobase/database';\n\nexport default {\n name: 'attachments',\n title: '文件管理器',\n fields: [\n {\n comment: '用户文件名(不含扩展名)',\n type: 'string',\n name: 'title',\n },\n {\n comment: '系统文件名(含扩展名)',\n type: 'string',\n name: 'filename'\n },\n {\n comment: '扩展名(含“.”)',\n type: 'string',\n name: 'extname',\n },\n {\n comment: '文件体积(字节)',\n type: 'integer',\n name: 'size',\n },\n // TODO: 使用暂不明确,以后再考虑\n // {\n // comment: '文件类型(mimetype 前半段,通常用于预览)',\n // type: 'string',\n // name: 'type',\n // },\n {\n type: 'string',\n name: 'mimetype',\n },\n {\n comment: '存储引擎',\n type: 'belongsTo',\n name: 'storage',\n },\n {\n comment: '相对路径(含“/”前缀)',\n type: 'string',\n name: 'path',\n },\n {\n comment: '其他文件信息(如图片的宽高)',\n type: 'jsonb',\n name: 'meta',\n defaultValue: {}\n },\n {\n comment: '网络访问地址',\n type: 'string',\n name: 'url',\n // formula: '{{ storage.baseUrl }}{{ path }}/{{ filename }}'\n }\n ],\n} as TableOptions;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["collections/storages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;;AAElD,wBA0DkB","file":"storages.d.ts","sourcesContent":["import { TableOptions } from '@nocobase/database';\n\nexport default {\n name: 'storages',\n title: '存储引擎',\n fields: [\n {\n title: '存储引擎名称',\n comment: '存储引擎名称',\n type: 'string',\n name: 'title',\n },\n {\n title: '英文标识',\n // comment: '英文标识,用于代码层面配置',\n type: 'string',\n name: 'name',\n unique: true,\n },\n {\n comment: '类型标识,如 local/ali-oss 等',\n type: 'string',\n name: 'type',\n },\n {\n comment: '配置项',\n type: 'jsonb',\n name: 'options',\n defaultValue: {}\n },\n {\n comment: '文件规则',\n type: 'jsonb',\n name: 'rules',\n defaultValue: {}\n },\n {\n comment: '存储相对路径模板',\n type: 'string',\n name: 'path',\n defaultValue: ''\n },\n {\n comment: '访问地址前缀',\n type: 'string',\n name: 'baseUrl',\n defaultValue: ''\n },\n // TODO(feature): 需要使用一个实现了可设置默认值的字段\n {\n comment: '默认引擎',\n type: 'boolean',\n name: 'default',\n defaultValue: false\n },\n {\n type: 'hasMany',\n name: 'attachments'\n }\n ]\n} as TableOptions;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AAEtD,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAC1C,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAC9C,eAAO,MAAM,eAAe,OAAO,CAAC","file":"constants.d.ts","sourcesContent":["export const FILE_FIELD_NAME = 'file';\nexport const LIMIT_FILES = 1;\nexport const LIMIT_MAX_FILE_SIZE = 1024 * 1024 * 1024;\n\nexport const STORAGE_TYPE_LOCAL = 'local';\nexport const STORAGE_TYPE_ALI_OSS = 'ali-oss';\nexport const STORAGE_TYPE_S3 = 's3';\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC","file":"index.d.ts","sourcesContent":["export * from './constants';\nexport { default } from './server';\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC","file":"index.d.ts","sourcesContent":["export { default as mimetype } from './mimetype';\n\n// TODO(feature): 提供注册新规则的方法,规则可动态添加\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["rules/mimetype.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,WAAW,IAAI,KAAA,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAQ,EAAE,GAAG,KAAA,GAAG,OAAO,CAE7E","file":"mimetype.d.ts","sourcesContent":["import match from 'mime-match';\n\nexport default function (file, options: string | string[] = '*', ctx): boolean {\n return options.toString().split(',').some(match(file.mimetype));\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["server.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;;AASjD,wBAkCmB","file":"server.d.ts","sourcesContent":["import path from 'path';\nimport Database from '@nocobase/database';\nimport Resourcer from '@nocobase/resourcer';\nimport { PluginOptions } from '@nocobase/server';\n\nimport {\n action as uploadAction,\n middleware as uploadMiddleware,\n} from './actions/upload';\nimport { getStorageConfig } from './storages';\nimport { STORAGE_TYPE_LOCAL } from './constants';\n\nexport default {\n name: 'file-manager',\n async load() {\n const database: Database = this.app.db;\n const resourcer: Resourcer = this.app.resourcer;\n \n database.import({\n directory: path.resolve(__dirname, 'collections'),\n });\n \n // 暂时中间件只能通过 use 加进来\n resourcer.use(uploadMiddleware);\n resourcer.registerActionHandler('upload', uploadAction);\n\n const { DEFAULT_STORAGE_TYPE } = process.env;\n\n if (process.env.NOCOBASE_ENV !== 'production') {\n this.app.on('beforeStart', async () => {\n await getStorageConfig(STORAGE_TYPE_LOCAL).middleware(this.app);\n });\n }\n\n this.app.on('db.init', async () => {\n const defaultStorageConfig = getStorageConfig(DEFAULT_STORAGE_TYPE);\n if (defaultStorageConfig) {\n const StorageModel = database.getModel('storages');\n await StorageModel.create({\n ...defaultStorageConfig.defaults(),\n type: DEFAULT_STORAGE_TYPE,\n default: true\n });\n }\n });\n },\n} as PluginOptions;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["storages/ali-oss.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAGA,wBAsBC","file":"ali-oss.d.ts","sourcesContent":["import { STORAGE_TYPE_ALI_OSS } from '../constants';\nimport { cloudFilenameGetter } from '../utils';\n\nexport default {\n make(storage) {\n const createAliOssStorage = require('multer-aliyun-oss');\n return new createAliOssStorage({\n config: storage.options,\n filename: cloudFilenameGetter(storage)\n });\n },\n defaults() {\n return {\n title: '阿里云对象存储',\n type: STORAGE_TYPE_ALI_OSS,\n name: 'ali-oss-1',\n baseUrl: process.env.ALI_OSS_STORAGE_BASE_URL,\n options: {\n region: process.env.ALI_OSS_REGION,\n accessKeyId: process.env.ALI_OSS_ACCESS_KEY_ID,\n accessKeySecret: process.env.ALI_OSS_ACCESS_KEY_SECRET,\n bucket: process.env.ALI_OSS_BUCKET,\n }\n }\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["storages/index.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,QAAQ;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAQD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAEtD","file":"index.d.ts","sourcesContent":["import local from './local';\nimport oss from './ali-oss';\nimport s3 from './s3';\n\nimport {\n STORAGE_TYPE_LOCAL,\n STORAGE_TYPE_ALI_OSS,\n STORAGE_TYPE_S3\n} from '../constants';\n\nexport interface IStorage {\n filenameKey?: string;\n middleware?: Function;\n getFileData?: Function;\n make: Function;\n defaults: Function;\n}\n\nconst map = new Map<string, IStorage>();\nmap.set(STORAGE_TYPE_LOCAL, local);\nmap.set(STORAGE_TYPE_ALI_OSS, oss);\nmap.set(STORAGE_TYPE_S3, s3);\n\n\nexport function getStorageConfig(key: string): IStorage {\n return map.get(key);\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["storages/local.ts"],"names":[],"mappings":"AAGA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAoD5B,iBAAe,UAAU,CAAC,GAAG,KAAA,EAAE,OAAO,CAAC,KAAA,iBAmDtC;;;;;;;;;;;AAED,wBAmBE","file":"local.d.ts","sourcesContent":["import path from 'path';\nimport { URL } from 'url';\nimport mkdirp from 'mkdirp';\nimport multer from 'multer';\nimport serve from 'koa-static';\nimport { STORAGE_TYPE_LOCAL } from '../constants';\nimport { getFilename } from '../utils';\n\n// use koa-mount match logic\nfunction match(basePath: string, pathname: string): boolean {\n if (!pathname.startsWith(basePath)) {\n return false;\n }\n\n const newPath = pathname.replace(basePath, '') || '/';\n if (basePath.slice(-1) === '/') {\n return true;\n }\n\n return newPath[0] === '/';\n}\n\nasync function update(app, storages) {\n const StorageModel = app.db.getModel('storages');\n\n const items = await StorageModel.findAll({\n where: {\n type: STORAGE_TYPE_LOCAL,\n }\n });\n\n const primaryKey = StorageModel.primaryKeyAttribute;\n\n storages.clear();\n for (const storage of items) {\n storages.set(storage[primaryKey], storage);\n }\n}\n\nfunction createLocalServerUpdateHook(app, storages) {\n return async function (row) {\n if (row.get('type') === STORAGE_TYPE_LOCAL) {\n await update(app, storages);\n }\n }\n}\n\nfunction getDocumentRoot(storage): string {\n const { documentRoot = 'uploads' } = storage.options || {};\n // TODO(feature): 后面考虑以字符串模板的方式使用,可注入 req/action 相关变量,以便于区分文件夹\n return path.resolve(path.isAbsolute(documentRoot)\n ? documentRoot\n : path.join(process.cwd(), documentRoot));\n}\n\nasync function middleware(app, options?) {\n const LOCALHOST = `http://localhost:${process.env.API_PORT}`;\n\n const StorageModel = app.db.getModel('storages');\n const storages = new Map<string, any>();\n\n const localServerUpdateHook = createLocalServerUpdateHook(app, storages);\n StorageModel.addHook('afterCreate', localServerUpdateHook);\n StorageModel.addHook('afterUpdate', localServerUpdateHook);\n StorageModel.addHook('afterDestroy', localServerUpdateHook);\n\n await update(app, storages);\n\n app.use(async function (ctx, next) {\n for (const storage of storages.values()) {\n const baseUrl = storage.get('baseUrl');\n\n let url;\n try {\n url = new URL(baseUrl);\n } catch (e) {\n url = {\n pathname: baseUrl\n };\n }\n\n // 以下情况才认为当前进程所应该提供静态服务\n // 否则都忽略,交给其他 server 来提供(如 nginx/cdn 等)\n if (url.origin && url.origin !== LOCALHOST) {\n continue;\n }\n\n const basePath = url.pathname.startsWith('/') ? url.pathname : `/${url.pathname}`;\n if (!match(basePath, ctx.path)) {\n continue;\n }\n\n return serve(getDocumentRoot(storage), {\n // for handle files after any api handlers\n defer: true\n })(ctx, async () => {\n if (ctx.path.startsWith(basePath)) {\n ctx.path = ctx.path.replace(basePath, '');\n }\n // console.log('file request:', `${basePath}${ctx.path}`);\n await next();\n });\n }\n\n await next();\n });\n}\n\nexport default {\n middleware,\n make(storage) {\n return multer.diskStorage({\n destination: function (req, file, cb) {\n const destPath = path.join(getDocumentRoot(storage), storage.path);\n mkdirp(destPath, (err: Error | null) => cb(err, destPath));\n },\n filename: getFilename\n });\n },\n defaults() {\n return {\n title: '本地存储',\n type: STORAGE_TYPE_LOCAL,\n name: `local`,\n baseUrl: process.env.LOCAL_STORAGE_BASE_URL || `http://localhost:${process.env.API_PORT}/uploads`\n };\n }\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["storages/s3.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAGA,wBAiDC","file":"s3.d.ts","sourcesContent":["import { STORAGE_TYPE_S3 } from '../constants';\nimport { cloudFilenameGetter } from '../utils';\n\nexport default {\n filenameKey: 'key',\n make(storage) {\n const S3Client = require('aws-sdk/clients/s3');\n const multerS3 = require('multer-s3');\n const {\n accessKeyId,\n secretAccessKey,\n bucket,\n acl = 'public-read',\n ...options\n } = storage.options;\n const s3 = new S3Client({\n ...options,\n credentials: {\n accessKeyId,\n secretAccessKey\n }\n });\n\n return multerS3({\n s3,\n bucket,\n acl,\n contentType(req, file, cb) {\n if (file.mimetype) {\n cb(null, file.mimetype);\n return;\n }\n \n multerS3.AUTO_CONTENT_TYPE(req, file, cb);\n },\n key: cloudFilenameGetter(storage)\n });\n },\n defaults() {\n return {\n title: 'AWS S3',\n name: 'aws-s3',\n type: STORAGE_TYPE_S3,\n baseUrl: process.env.AWS_S3_STORAGE_BASE_URL,\n options: {\n region: process.env.AWS_S3_REGION,\n accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n bucket: process.env.AWS_S3_BUCKET,\n }\n }\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["utils.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CAAC,GAAG,KAAA,EAAE,IAAI,KAAA,EAAE,EAAE,KAAA,QAIxC;AAED,eAAO,MAAM,mBAAmB,0DAO/B,CAAA","file":"utils.d.ts","sourcesContent":["import crypto from 'crypto';\nimport path from 'path';\n\nexport function getFilename(req, file, cb) {\n crypto.pseudoRandomBytes(16, function (err, raw) {\n cb(err, err ? undefined : `${raw.toString('hex')}${path.extname(file.originalname)}`)\n });\n}\n\nexport const cloudFilenameGetter = storage => (req, file, cb) => {\n getFilename(req, file, (err, filename) => {\n if (err) {\n return cb(err);\n }\n cb(null, `${storage.path ? `${storage.path}/` : ''}${filename}`);\n });\n}\n"]}