@kne/fastify-file-manager 3.0.1 → 3.0.4
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/index.js +8 -0
- package/libs/controllers/index.js +526 -106
- package/libs/services/file-record.js +21 -3
- package/libs/services/folder.js +654 -0
- package/package.json +4 -2
package/index.js
CHANGED
|
@@ -22,6 +22,14 @@ module.exports = fp(
|
|
|
22
22
|
},
|
|
23
23
|
createAuthenticate: () => {
|
|
24
24
|
return [];
|
|
25
|
+
},
|
|
26
|
+
// 文件夹能力依赖 @kne/fastify-group,需先注册;设为 false 可关闭
|
|
27
|
+
enableFolder: true,
|
|
28
|
+
groupName: 'group',
|
|
29
|
+
// 上传自动挂载文件系统时的默认业务域
|
|
30
|
+
defaultFolderType: 'admin-file-system',
|
|
31
|
+
getAuthenticate: () => {
|
|
32
|
+
return [];
|
|
25
33
|
}
|
|
26
34
|
},
|
|
27
35
|
options
|
|
@@ -1,148 +1,568 @@
|
|
|
1
1
|
const fp = require('fastify-plugin');
|
|
2
2
|
|
|
3
|
+
const DEFAULT_FOLDER_TYPE = 'admin-file-system';
|
|
4
|
+
|
|
5
|
+
const readMultipartField = (file, name) => {
|
|
6
|
+
const field = file?.fields?.[name];
|
|
7
|
+
if (!field) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const target = Array.isArray(field) ? field[0] : field;
|
|
11
|
+
return target?.value;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const pickUploadFolderParams = (request, file, defaultType) => {
|
|
15
|
+
const path =
|
|
16
|
+
request.query?.path !== undefined
|
|
17
|
+
? request.query.path
|
|
18
|
+
: request.body?.path !== undefined
|
|
19
|
+
? request.body.path
|
|
20
|
+
: readMultipartField(file, 'path');
|
|
21
|
+
const type =
|
|
22
|
+
request.query?.type || request.body?.type || readMultipartField(file, 'type') || defaultType || DEFAULT_FOLDER_TYPE;
|
|
23
|
+
const language = request.query?.language || request.body?.language || readMultipartField(file, 'language');
|
|
24
|
+
// 未传 path 时挂到根目录
|
|
25
|
+
return { path: path === undefined ? '' : path, type, language };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const attachUploadedRecord = async ({ services, record, path, type, language }) => {
|
|
29
|
+
if (!services.folder) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
if (!type) {
|
|
33
|
+
throw new Error('挂载文件系统时必须传入 type');
|
|
34
|
+
}
|
|
35
|
+
const parentId = await services.folder.ensurePath({ type, path, language });
|
|
36
|
+
return await services.folder.attachRecord({
|
|
37
|
+
type,
|
|
38
|
+
parentId,
|
|
39
|
+
record,
|
|
40
|
+
language,
|
|
41
|
+
linked: true
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
3
45
|
module.exports = fp(async (fastify, options) => {
|
|
4
46
|
const { services } = fastify.fileManager;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
47
|
+
const defaultFolderType = options.defaultFolderType || DEFAULT_FOLDER_TYPE;
|
|
48
|
+
|
|
49
|
+
fastify.post(
|
|
50
|
+
`${options.prefix}/upload`,
|
|
51
|
+
{
|
|
52
|
+
onRequest: options.createAuthenticate('file:write'),
|
|
53
|
+
schema: {
|
|
54
|
+
summary: '上传文件',
|
|
55
|
+
description:
|
|
56
|
+
'上传单个文件到服务器或配置的存储服务。保存成功后自动挂到文件系统(默认 type=admin-file-system,未传 path 时挂到根目录;传 path 则按路径创建并挂载)',
|
|
57
|
+
query: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
namespace: { type: 'string', description: '文件分类命名空间' },
|
|
61
|
+
path: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
description: '文件系统文件夹路径,如 a/b/c;未传则挂到根目录;路径不存在则自动创建'
|
|
64
|
+
},
|
|
65
|
+
type: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: '文件系统业务域 type,默认 admin-file-system'
|
|
68
|
+
},
|
|
69
|
+
language: { type: 'string', description: '语言' }
|
|
70
|
+
}
|
|
10
71
|
}
|
|
11
72
|
}
|
|
73
|
+
},
|
|
74
|
+
async request => {
|
|
75
|
+
const file = await request.file();
|
|
76
|
+
if (!file) {
|
|
77
|
+
throw new Error('不能获取到上传文件');
|
|
78
|
+
}
|
|
79
|
+
const record = await services.fileRecord.uploadToFileSystem({
|
|
80
|
+
file,
|
|
81
|
+
namespace: request.query.namespace || options.namespace
|
|
82
|
+
});
|
|
83
|
+
const { path, type, language } = pickUploadFolderParams(request, file, defaultFolderType);
|
|
84
|
+
const folder = await attachUploadedRecord({ services, record, path, type, language });
|
|
85
|
+
return folder ? Object.assign({}, record, { folder }) : record;
|
|
12
86
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
fastify.post(
|
|
90
|
+
`${options.prefix}/uploadUrl`,
|
|
91
|
+
{
|
|
92
|
+
onRequest: options.createAuthenticate('file:write'),
|
|
93
|
+
schema: {
|
|
94
|
+
summary: '上传URL文件',
|
|
95
|
+
description:
|
|
96
|
+
'上传单个文件到服务器或配置的存储服务。保存成功后自动挂到文件系统(默认 type=admin-file-system,未传 path 时挂到根目录;传 path 则按路径创建并挂载)',
|
|
97
|
+
body: {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: {
|
|
100
|
+
url: { type: 'string', description: '文件url' },
|
|
101
|
+
namespace: { type: 'string', description: '文件分类命名空间' },
|
|
102
|
+
path: {
|
|
103
|
+
type: 'string',
|
|
104
|
+
description: '文件系统文件夹路径,如 a/b/c;未传则挂到根目录;路径不存在则自动创建'
|
|
105
|
+
},
|
|
106
|
+
type: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: '文件系统业务域 type,默认 admin-file-system'
|
|
109
|
+
},
|
|
110
|
+
language: { type: 'string', description: '语言' }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
async request => {
|
|
116
|
+
const record = await services.fileRecord.uploadFromUrl({
|
|
117
|
+
url: request.body.url,
|
|
118
|
+
namespace: request.body.namespace || options.namespace
|
|
119
|
+
});
|
|
120
|
+
const { path, type, language } = pickUploadFolderParams(request, null, defaultFolderType);
|
|
121
|
+
const folder = await attachUploadedRecord({ services, record, path, type, language });
|
|
122
|
+
return folder ? Object.assign({}, record, { folder }) : record;
|
|
17
123
|
}
|
|
18
|
-
|
|
19
|
-
file, namespace: request.query.namespace || options.namespace
|
|
20
|
-
});
|
|
21
|
-
});
|
|
124
|
+
);
|
|
22
125
|
|
|
23
|
-
fastify.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
126
|
+
fastify.get(
|
|
127
|
+
`${options.prefix}/file-url/:id`,
|
|
128
|
+
{
|
|
129
|
+
onRequest: options.createAuthenticate('file:read'),
|
|
130
|
+
schema: {
|
|
131
|
+
summary: '获取文件url',
|
|
132
|
+
description: '获取文件url',
|
|
133
|
+
params: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
required: ['id'],
|
|
136
|
+
properties: {
|
|
137
|
+
id: { type: 'string', description: '文件id' }
|
|
138
|
+
}
|
|
29
139
|
}
|
|
30
140
|
}
|
|
141
|
+
},
|
|
142
|
+
async request => {
|
|
143
|
+
const { id } = request.params;
|
|
144
|
+
return await services.fileRecord.getFileUrl({ id });
|
|
31
145
|
}
|
|
32
|
-
|
|
33
|
-
return await services.fileRecord.uploadFromUrl({
|
|
34
|
-
url: request.body.url, namespace: request.body.namespace || options.namespace
|
|
35
|
-
});
|
|
36
|
-
});
|
|
146
|
+
);
|
|
37
147
|
|
|
38
|
-
fastify.get(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
148
|
+
fastify.get(
|
|
149
|
+
`${options.prefix}/file-id/:id`,
|
|
150
|
+
{
|
|
151
|
+
onRequest: options.createAuthenticate('file:read'),
|
|
152
|
+
schema: {
|
|
153
|
+
summary: '获取文件信息',
|
|
154
|
+
description: '获取文件信息',
|
|
155
|
+
query: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {
|
|
158
|
+
attachment: { type: 'boolean', description: '是否下载' },
|
|
159
|
+
filename: { type: 'string', description: '下载文件名' }
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
params: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
required: ['id'],
|
|
165
|
+
properties: {
|
|
166
|
+
id: { type: 'string', description: '文件id' }
|
|
167
|
+
}
|
|
43
168
|
}
|
|
44
169
|
}
|
|
170
|
+
},
|
|
171
|
+
async (request, reply) => {
|
|
172
|
+
const { id } = request.params;
|
|
173
|
+
const { attachment, filename: targetFilename } = request.query;
|
|
174
|
+
const { filePath, targetFile, filename, mimetype, ...props } = await services.fileRecord.getFileInfo({
|
|
175
|
+
id
|
|
176
|
+
});
|
|
177
|
+
if (targetFile) {
|
|
178
|
+
const outputFilename = encodeURIComponent(targetFilename || filename);
|
|
179
|
+
reply.header('Content-Type', mimetype);
|
|
180
|
+
reply.header('Content-Disposition', attachment ? `attachment; filename="${outputFilename}"` : `filename="${outputFilename}"`);
|
|
181
|
+
return reply.send(targetFile);
|
|
182
|
+
}
|
|
183
|
+
return attachment ? reply.download(filePath, targetFilename || filename) : reply.sendFile(filePath);
|
|
45
184
|
}
|
|
46
|
-
|
|
47
|
-
const { id } = request.params;
|
|
48
|
-
return await services.fileRecord.getFileUrl({ id });
|
|
49
|
-
});
|
|
185
|
+
);
|
|
50
186
|
|
|
51
|
-
fastify.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
187
|
+
fastify.post(
|
|
188
|
+
`${options.prefix}/file-list`,
|
|
189
|
+
{
|
|
190
|
+
onRequest: options.createAuthenticate('file:mange'),
|
|
191
|
+
schema: {
|
|
192
|
+
summary: '获取文件列表',
|
|
193
|
+
description: '查询指定命名空间下的文件列表',
|
|
194
|
+
body: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
perPage: { type: 'number', description: '每页数量' },
|
|
198
|
+
currentPage: { type: 'number', description: '当前页数' },
|
|
199
|
+
filter: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: {
|
|
202
|
+
namespace: { type: 'string', description: '文件分类命名空间' },
|
|
203
|
+
size: { type: 'array', items: { type: 'number' }, description: '文件大小' },
|
|
204
|
+
filename: { type: 'string', description: '文件名' }
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
57
208
|
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
async request => {
|
|
212
|
+
const { filter, perPage, currentPage } = Object.assign(
|
|
213
|
+
{},
|
|
214
|
+
{
|
|
215
|
+
perPage: 20,
|
|
216
|
+
currentPage: 1
|
|
217
|
+
},
|
|
218
|
+
request.body
|
|
219
|
+
);
|
|
220
|
+
return await services.fileRecord.getFileList({
|
|
221
|
+
filter,
|
|
222
|
+
perPage,
|
|
223
|
+
currentPage
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Replace file
|
|
229
|
+
|
|
230
|
+
fastify.post(
|
|
231
|
+
`${options.prefix}/replace-file`,
|
|
232
|
+
{
|
|
233
|
+
onRequest: options.createAuthenticate('file:mange'),
|
|
234
|
+
schema: {
|
|
235
|
+
summary: '替换文件',
|
|
236
|
+
description: '替换文件',
|
|
237
|
+
query: {
|
|
238
|
+
type: 'object',
|
|
239
|
+
properties: {
|
|
240
|
+
id: { type: 'string', description: '文件id' }
|
|
241
|
+
}
|
|
61
242
|
}
|
|
62
243
|
}
|
|
244
|
+
},
|
|
245
|
+
async request => {
|
|
246
|
+
const file = await request.file();
|
|
247
|
+
if (!file) {
|
|
248
|
+
throw new Error('不能获取到上传文件');
|
|
249
|
+
}
|
|
250
|
+
return await services.fileRecord.uploadToFileSystem({ id: request.query.id, file });
|
|
63
251
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
fastify.post(
|
|
255
|
+
`${options.prefix}/rename-file`,
|
|
256
|
+
{
|
|
257
|
+
onRequest: options.createAuthenticate('file:mange'),
|
|
258
|
+
schema: {
|
|
259
|
+
summary: '重命名文件',
|
|
260
|
+
description: '重命名文件',
|
|
261
|
+
body: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
properties: {
|
|
264
|
+
id: { type: 'string', description: '文件id' },
|
|
265
|
+
filename: { type: 'string', description: '新文件名' }
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
async request => {
|
|
271
|
+
await services.fileRecord.renameFile(request.body);
|
|
272
|
+
return {};
|
|
75
273
|
}
|
|
76
|
-
|
|
77
|
-
});
|
|
274
|
+
);
|
|
78
275
|
|
|
79
|
-
fastify.post(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
276
|
+
fastify.post(
|
|
277
|
+
`${options.prefix}/delete-files`,
|
|
278
|
+
{
|
|
279
|
+
onRequest: options.createAuthenticate('file:mange'),
|
|
280
|
+
schema: {
|
|
281
|
+
summary: '删除文件',
|
|
282
|
+
description: '删除文件',
|
|
283
|
+
body: {
|
|
284
|
+
type: 'object',
|
|
285
|
+
required: ['ids'],
|
|
286
|
+
properties: {
|
|
287
|
+
ids: { type: 'array', items: { type: 'string' }, description: '文件id列表' }
|
|
91
288
|
}
|
|
92
289
|
}
|
|
93
290
|
}
|
|
291
|
+
},
|
|
292
|
+
async request => {
|
|
293
|
+
const { ids } = request.body;
|
|
294
|
+
await services.fileRecord.deleteFiles({ ids });
|
|
295
|
+
return {};
|
|
94
296
|
}
|
|
95
|
-
|
|
96
|
-
const { filter, perPage, currentPage } = Object.assign({}, {
|
|
97
|
-
perPage: 20, currentPage: 1
|
|
98
|
-
}, request.body);
|
|
99
|
-
return await services.fileRecord.getFileList({
|
|
100
|
-
filter, perPage, currentPage
|
|
101
|
-
});
|
|
102
|
-
});
|
|
297
|
+
);
|
|
103
298
|
|
|
104
|
-
|
|
299
|
+
fastify.post(
|
|
300
|
+
`${options.prefix}/download-files`,
|
|
301
|
+
{
|
|
302
|
+
onRequest: options.createAuthenticate('file:mange'),
|
|
303
|
+
schema: {
|
|
304
|
+
summary: '批量下载压缩包',
|
|
305
|
+
description: '将选中文件打包为 zip 并下载',
|
|
306
|
+
body: {
|
|
307
|
+
type: 'object',
|
|
308
|
+
required: ['ids'],
|
|
309
|
+
properties: {
|
|
310
|
+
ids: { type: 'array', items: { type: 'string' }, description: '文件id列表' },
|
|
311
|
+
filename: { type: 'string', description: '下载压缩包文件名,默认 files.zip' }
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
async (request, reply) => {
|
|
317
|
+
const { ids, filename } = request.body;
|
|
318
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
319
|
+
throw new Error('请选择要下载的文件');
|
|
320
|
+
}
|
|
321
|
+
const compressStream = await services.fileRecord.getCompressFileStream({ ids, type: 'zip' });
|
|
322
|
+
const outputFilename = encodeURIComponent(filename || 'files.zip');
|
|
323
|
+
reply.header('Content-Type', 'application/zip');
|
|
324
|
+
reply.header('Content-Disposition', `attachment; filename="${outputFilename}"`);
|
|
325
|
+
return reply.send(compressStream);
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
if (options.enableFolder === false || !services.folder) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const getFolderAuthenticate = action => {
|
|
334
|
+
if (typeof options.getAuthenticate === 'function') {
|
|
335
|
+
return options.getAuthenticate(action);
|
|
336
|
+
}
|
|
337
|
+
return [];
|
|
338
|
+
};
|
|
105
339
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
340
|
+
const withDefaultFolderType = source => async request => {
|
|
341
|
+
const current = request[source] || {};
|
|
342
|
+
if (!current.type) {
|
|
343
|
+
request[source] = Object.assign({}, current, { type: defaultFolderType });
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const folderOnRequest = (action, source) => {
|
|
348
|
+
const auth = getFolderAuthenticate(action);
|
|
349
|
+
const hooks = [withDefaultFolderType(source)].concat(Array.isArray(auth) ? auth : [auth].filter(Boolean));
|
|
350
|
+
return hooks;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
fastify.get(
|
|
354
|
+
`${options.prefix}/folder/tree`,
|
|
355
|
+
{
|
|
356
|
+
onRequest: folderOnRequest('read', 'query'),
|
|
357
|
+
schema: {
|
|
358
|
+
summary: '获取文件夹树',
|
|
359
|
+
description: '基于 fastify-group 获取指定 type 的文件树;未传 type 时使用 defaultFolderType',
|
|
360
|
+
query: {
|
|
361
|
+
type: 'object',
|
|
362
|
+
properties: {
|
|
363
|
+
type: { type: 'string', description: '业务域类型,默认 admin-file-system' },
|
|
364
|
+
language: { type: 'string', description: '语言' }
|
|
365
|
+
}
|
|
111
366
|
}
|
|
112
367
|
}
|
|
368
|
+
},
|
|
369
|
+
async request => {
|
|
370
|
+
return await services.folder.getTree(request.query);
|
|
113
371
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
fastify.post(
|
|
375
|
+
`${options.prefix}/folder/mkdir`,
|
|
376
|
+
{
|
|
377
|
+
onRequest: getFolderAuthenticate('write'),
|
|
378
|
+
schema: {
|
|
379
|
+
summary: '新建文件夹',
|
|
380
|
+
description: '在指定 type 下创建文件夹节点',
|
|
381
|
+
body: {
|
|
382
|
+
type: 'object',
|
|
383
|
+
required: ['type', 'name'],
|
|
384
|
+
properties: {
|
|
385
|
+
type: { type: 'string', description: '业务域类型' },
|
|
386
|
+
name: { type: 'string', description: '文件夹名称' },
|
|
387
|
+
parentId: { type: 'string', description: '父文件夹 id' },
|
|
388
|
+
language: { type: 'string', description: '语言' }
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
async request => {
|
|
394
|
+
return await services.folder.mkdir(request.body);
|
|
118
395
|
}
|
|
119
|
-
|
|
120
|
-
});
|
|
396
|
+
);
|
|
121
397
|
|
|
122
|
-
fastify.post(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
398
|
+
fastify.post(
|
|
399
|
+
`${options.prefix}/folder/upload`,
|
|
400
|
+
{
|
|
401
|
+
onRequest: getFolderAuthenticate('write'),
|
|
402
|
+
schema: {
|
|
403
|
+
summary: '上传文件到文件夹',
|
|
404
|
+
description: '上传文件实体并在 group 中创建文件节点',
|
|
405
|
+
query: {
|
|
406
|
+
type: 'object',
|
|
407
|
+
required: ['type'],
|
|
408
|
+
properties: {
|
|
409
|
+
type: { type: 'string', description: '业务域类型' },
|
|
410
|
+
parentId: { type: 'string', description: '父文件夹 id' },
|
|
411
|
+
namespace: { type: 'string', description: '文件命名空间' },
|
|
412
|
+
language: { type: 'string', description: '语言' }
|
|
413
|
+
}
|
|
127
414
|
}
|
|
128
415
|
}
|
|
416
|
+
},
|
|
417
|
+
async request => {
|
|
418
|
+
const file = await request.file();
|
|
419
|
+
if (!file) {
|
|
420
|
+
throw new Error('不能获取到上传文件');
|
|
421
|
+
}
|
|
422
|
+
return await services.folder.upload({
|
|
423
|
+
type: request.query.type,
|
|
424
|
+
parentId: request.query.parentId,
|
|
425
|
+
namespace: request.query.namespace,
|
|
426
|
+
language: request.query.language,
|
|
427
|
+
file
|
|
428
|
+
});
|
|
129
429
|
}
|
|
130
|
-
|
|
131
|
-
await services.fileRecord.renameFile(request.body);
|
|
132
|
-
return {};
|
|
133
|
-
});
|
|
430
|
+
);
|
|
134
431
|
|
|
135
|
-
fastify.post(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
432
|
+
fastify.post(
|
|
433
|
+
`${options.prefix}/folder/remove`,
|
|
434
|
+
{
|
|
435
|
+
onRequest: getFolderAuthenticate('delete'),
|
|
436
|
+
schema: {
|
|
437
|
+
summary: '删除文件夹节点',
|
|
438
|
+
description: '递归删除节点及其子孙;文件节点同步删除 file-record',
|
|
439
|
+
body: {
|
|
440
|
+
type: 'object',
|
|
441
|
+
required: ['type', 'id'],
|
|
442
|
+
properties: {
|
|
443
|
+
type: { type: 'string', description: '业务域类型' },
|
|
444
|
+
id: { type: 'string', description: '节点 id' },
|
|
445
|
+
language: { type: 'string', description: '语言' }
|
|
446
|
+
}
|
|
140
447
|
}
|
|
141
448
|
}
|
|
449
|
+
},
|
|
450
|
+
async request => {
|
|
451
|
+
return await services.folder.remove(request.body);
|
|
142
452
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
fastify.post(
|
|
456
|
+
`${options.prefix}/folder/move`,
|
|
457
|
+
{
|
|
458
|
+
onRequest: getFolderAuthenticate('write'),
|
|
459
|
+
schema: {
|
|
460
|
+
summary: '移动文件夹节点',
|
|
461
|
+
description: '将一个或多个文件/文件夹节点移动到目标文件夹(parentId 为空表示根目录)',
|
|
462
|
+
body: {
|
|
463
|
+
type: 'object',
|
|
464
|
+
required: ['type', 'ids'],
|
|
465
|
+
properties: {
|
|
466
|
+
type: { type: 'string', description: '业务域类型' },
|
|
467
|
+
ids: {
|
|
468
|
+
type: 'array',
|
|
469
|
+
items: { type: 'string' },
|
|
470
|
+
minItems: 1,
|
|
471
|
+
description: '要移动的节点 id 列表'
|
|
472
|
+
},
|
|
473
|
+
parentId: { type: ['string', 'null'], description: '目标父文件夹 id,空为根目录' },
|
|
474
|
+
language: { type: 'string', description: '语言' }
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
async request => {
|
|
480
|
+
return await services.folder.move(request.body);
|
|
481
|
+
}
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
fastify.post(
|
|
485
|
+
`${options.prefix}/folder/copy`,
|
|
486
|
+
{
|
|
487
|
+
onRequest: getFolderAuthenticate('write'),
|
|
488
|
+
schema: {
|
|
489
|
+
summary: '复制文件夹节点',
|
|
490
|
+
description:
|
|
491
|
+
'将一个或多个文件/文件夹节点复制到目标文件夹(parentId 为空表示根目录);文件以 linked 节点挂载,文件夹递归复制',
|
|
492
|
+
body: {
|
|
493
|
+
type: 'object',
|
|
494
|
+
required: ['type', 'ids'],
|
|
495
|
+
properties: {
|
|
496
|
+
type: { type: 'string', description: '业务域类型' },
|
|
497
|
+
ids: {
|
|
498
|
+
type: 'array',
|
|
499
|
+
items: { type: 'string' },
|
|
500
|
+
minItems: 1,
|
|
501
|
+
description: '要复制的节点 id 列表'
|
|
502
|
+
},
|
|
503
|
+
parentId: { type: ['string', 'null'], description: '目标父文件夹 id,空为根目录' },
|
|
504
|
+
language: { type: 'string', description: '语言' }
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
async request => {
|
|
510
|
+
return await services.folder.copy(request.body);
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
fastify.post(
|
|
515
|
+
`${options.prefix}/folder/rename`,
|
|
516
|
+
{
|
|
517
|
+
onRequest: getFolderAuthenticate('write'),
|
|
518
|
+
schema: {
|
|
519
|
+
summary: '重命名文件夹节点',
|
|
520
|
+
description: '重命名文件或文件夹节点;文件节点同步更新 file-record 文件名',
|
|
521
|
+
body: {
|
|
522
|
+
type: 'object',
|
|
523
|
+
required: ['type', 'id', 'name'],
|
|
524
|
+
properties: {
|
|
525
|
+
type: { type: 'string', description: '业务域类型' },
|
|
526
|
+
id: { type: 'string', description: '节点 id' },
|
|
527
|
+
name: { type: 'string', description: '新名称' },
|
|
528
|
+
language: { type: 'string', description: '语言' }
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
async request => {
|
|
534
|
+
return await services.folder.rename(request.body);
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
fastify.post(
|
|
539
|
+
`${options.prefix}/folder/add-files`,
|
|
540
|
+
{
|
|
541
|
+
onRequest: getFolderAuthenticate('write'),
|
|
542
|
+
preHandler: withDefaultFolderType('body'),
|
|
543
|
+
schema: {
|
|
544
|
+
summary: '将已有文件挂到文件夹树',
|
|
545
|
+
description:
|
|
546
|
+
'把文件库中已有文件以 linked 节点形式加入指定文件夹(parentId 为空表示根目录);删除该类节点不会删除文件库实体;未传 type 时使用 defaultFolderType',
|
|
547
|
+
body: {
|
|
548
|
+
type: 'object',
|
|
549
|
+
required: ['ids'],
|
|
550
|
+
properties: {
|
|
551
|
+
type: { type: 'string', description: '业务域类型,默认 admin-file-system' },
|
|
552
|
+
ids: {
|
|
553
|
+
type: 'array',
|
|
554
|
+
items: { type: 'string' },
|
|
555
|
+
minItems: 1,
|
|
556
|
+
description: '文件库 file-record id 列表'
|
|
557
|
+
},
|
|
558
|
+
parentId: { type: ['string', 'null'], description: '目标父文件夹 id,空为根目录' },
|
|
559
|
+
language: { type: 'string', description: '语言' }
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
async request => {
|
|
565
|
+
return await services.folder.addFiles(request.body);
|
|
566
|
+
}
|
|
567
|
+
);
|
|
148
568
|
});
|