midway-fatcms 0.0.1-beta.70 → 0.0.1-beta.71
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/dist/config/config.default.js +1 -1
- package/dist/controller/gateway/FileController.d.ts +4 -0
- package/dist/controller/gateway/FileController.js +17 -2
- package/dist/libs/crud-pro/models/ExecuteContext.d.ts +2 -1
- package/dist/libs/crud-pro/models/ExecuteContext.js +8 -0
- package/dist/libs/crud-pro/models/ResModel.d.ts +5 -1
- package/dist/models/bizmodels.d.ts +6 -0
- package/dist/models/bizmodels.js +8 -1
- package/dist/service/FileCenterService.d.ts +24 -3
- package/dist/service/FileCenterService.js +84 -13
- package/dist/service/curd/CrudProQuick.d.ts +2 -2
- package/dist/service/curd/CrudProQuick.js +1 -1
- package/package.json +1 -1
- package/src/config/config.default.ts +1 -1
- package/src/controller/gateway/FileController.ts +17 -2
- package/src/libs/crud-pro/models/ExecuteContext.ts +10 -1
- package/src/libs/crud-pro/models/ResModel.ts +6 -1
- package/src/models/bizmodels.ts +7 -0
- package/src/service/FileCenterService.ts +108 -20
- package/src/service/curd/CrudProQuick.ts +3 -3
|
@@ -111,7 +111,7 @@ exports.default = (appInfo) => {
|
|
|
111
111
|
fileSize: '20mb',
|
|
112
112
|
limit: '20mb',
|
|
113
113
|
// whitelist: string[],文件扩展名白名单
|
|
114
|
-
whitelist: ['.jpg', '.jpeg', '.png', '.gif', '.pdf', '.zip', '.xlsx', '.docx', '.pptx', '.txt', '.md', '.json', '.js', '.cjs', '.ejs', '.jsx', '.css', '.java', '.ts', '.tsx', '.less', '.scss', '.html', '.ttf', '.map', '.svg'],
|
|
114
|
+
whitelist: ['.jpg', '.jpeg', '.png', '.gif', '.pdf', '.zip', '.rar', '.xlsx', '.docx', '.pptx', '.txt', '.md', '.json', '.js', '.cjs', '.ejs', '.jsx', '.css', '.java', '.ts', '.tsx', '.less', '.scss', '.html', '.ttf', '.map', '.svg'],
|
|
115
115
|
// tmpdir: string,上传的文件临时存储路径
|
|
116
116
|
tmpdir: (0, path_1.join)((0, os_1.tmpdir)(), 'fatcms-upload-files'),
|
|
117
117
|
// cleanTimeout: number,上传的文件在临时目录中多久之后自动删除,默认为 5 分钟
|
|
@@ -25,4 +25,8 @@ export declare class FileController extends BaseApiController {
|
|
|
25
25
|
* @param streaming 是否直接返回流,默认返回 302 跳转
|
|
26
26
|
*/
|
|
27
27
|
downloadFile(byMethod: string, fileKey: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* 获取用户本人上传的文件列表
|
|
30
|
+
*/
|
|
31
|
+
getCurrentUserFiles(): Promise<CommonResult>;
|
|
28
32
|
}
|
|
@@ -80,12 +80,13 @@ let FileController = class FileController extends BaseApiController_1.BaseApiCon
|
|
|
80
80
|
const isThumbnail = this.ctx.path.startsWith(`${FileCenterService_1.PATH_PREFIX}/get/thumbnail/`);
|
|
81
81
|
const isPreview = this.ctx.path.startsWith(`${FileCenterService_1.PATH_PREFIX}/get/preview/`) || isTrue(preview);
|
|
82
82
|
const isStreaming = isTrue(streaming);
|
|
83
|
-
|
|
83
|
+
const fileInfo = await this.fileCenterService.getFileInfo(fileKey);
|
|
84
|
+
if (isThumbnail && (0, FileCenterService_1.isImageFile)(fileKey, fileInfo.file_type) && !process) {
|
|
84
85
|
// x-oss-process=image/resize,w_200,p_10/quality,q_60
|
|
85
86
|
process = 'image/resize,w_200'; //图片缩略图(小图片)
|
|
86
87
|
}
|
|
87
88
|
const isOutByStreaming = isStreaming || isPreview || isThumbnail;
|
|
88
|
-
const result = await this.fileCenterService.downloadFile(
|
|
89
|
+
const result = await this.fileCenterService.downloadFile(fileInfo, isOutByStreaming, process);
|
|
89
90
|
if (isOutByStreaming) {
|
|
90
91
|
// 移除这些属性
|
|
91
92
|
const headers = _.omitBy(result.res.headers, (_v, key) => {
|
|
@@ -104,6 +105,14 @@ let FileController = class FileController extends BaseApiController_1.BaseApiCon
|
|
|
104
105
|
this.ctx.redirect(result.downloadURL);
|
|
105
106
|
}
|
|
106
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* 获取用户本人上传的文件列表
|
|
110
|
+
*/
|
|
111
|
+
async getCurrentUserFiles() {
|
|
112
|
+
let { file_category } = (this.ctx.query || {});
|
|
113
|
+
const res = await this.fileCenterService.getCurrentUserFiles(file_category);
|
|
114
|
+
return common_dto_1.CommonResult.successRes(res);
|
|
115
|
+
}
|
|
107
116
|
};
|
|
108
117
|
__decorate([
|
|
109
118
|
(0, core_1.Inject)(),
|
|
@@ -139,6 +148,12 @@ __decorate([
|
|
|
139
148
|
__metadata("design:paramtypes", [String, String]),
|
|
140
149
|
__metadata("design:returntype", Promise)
|
|
141
150
|
], FileController.prototype, "downloadFile", null);
|
|
151
|
+
__decorate([
|
|
152
|
+
(0, core_1.Get)('/getCurrentUserFiles', { middleware: [(0, permission_middleware_1.checkLogin)()] }),
|
|
153
|
+
__metadata("design:type", Function),
|
|
154
|
+
__metadata("design:paramtypes", []),
|
|
155
|
+
__metadata("design:returntype", Promise)
|
|
156
|
+
], FileController.prototype, "getCurrentUserFiles", null);
|
|
142
157
|
FileController = __decorate([
|
|
143
158
|
(0, core_1.Controller)(FileCenterService_1.PATH_PREFIX)
|
|
144
159
|
], FileController);
|
|
@@ -4,7 +4,7 @@ import { SqlCfgModel } from './SqlCfgModel';
|
|
|
4
4
|
import { Transaction } from './Transaction';
|
|
5
5
|
import { ColumnRelation, ICrudProCfg, ILogger, IVisitor } from '../interfaces';
|
|
6
6
|
import { IExecuteContextFunc } from './ExecuteContextFunc';
|
|
7
|
-
import { ResModelFlexible } from './ResModel';
|
|
7
|
+
import { ResModelFlexible, ResModelPageQuery } from './ResModel';
|
|
8
8
|
declare class ExecuteContext {
|
|
9
9
|
private transaction;
|
|
10
10
|
private logger;
|
|
@@ -30,6 +30,7 @@ declare class ExecuteContext {
|
|
|
30
30
|
getResRows(): any[];
|
|
31
31
|
setResModelItem(resName: string, data: any): void;
|
|
32
32
|
getResModelItem(resName: string): any;
|
|
33
|
+
getResModelForQueryPage(): ResModelPageQuery;
|
|
33
34
|
getResModelItemLodash(resName: string): any;
|
|
34
35
|
setCfgModel(cfgModel: RequestCfgModel): void;
|
|
35
36
|
getCfgModel(): RequestCfgModel;
|
|
@@ -60,6 +60,14 @@ class ExecuteContext {
|
|
|
60
60
|
getResModelItem(resName) {
|
|
61
61
|
return this.resModel[resName];
|
|
62
62
|
}
|
|
63
|
+
getResModelForQueryPage() {
|
|
64
|
+
const rows = this.resModel['rows'];
|
|
65
|
+
const total_count = this.resModel['total_count'];
|
|
66
|
+
return {
|
|
67
|
+
rows: rows || [],
|
|
68
|
+
total_count: total_count || 0
|
|
69
|
+
};
|
|
70
|
+
}
|
|
63
71
|
getResModelItemLodash(resName) {
|
|
64
72
|
return _.get(this.resModel, resName);
|
|
65
73
|
}
|
|
@@ -10,7 +10,11 @@ interface ResModelStandard {
|
|
|
10
10
|
insert_affected?: ResModelAffected;
|
|
11
11
|
update_affected?: ResModelAffected;
|
|
12
12
|
}
|
|
13
|
+
interface ResModelPageQuery {
|
|
14
|
+
rows: any[];
|
|
15
|
+
total_count: number;
|
|
16
|
+
}
|
|
13
17
|
declare type ResModelFlexible = ResModelStandard & {
|
|
14
18
|
[key: string]: any;
|
|
15
19
|
};
|
|
16
|
-
export { ResModelAffected, ResModelStandard, ResModelFlexible };
|
|
20
|
+
export { ResModelAffected, ResModelStandard, ResModelFlexible, ResModelPageQuery };
|
|
@@ -50,6 +50,12 @@ export declare enum UploadCategoryType {
|
|
|
50
50
|
file = "file",
|
|
51
51
|
assets = "assets"
|
|
52
52
|
}
|
|
53
|
+
export declare enum FileCategoryType {
|
|
54
|
+
image = "image",
|
|
55
|
+
office = "office",
|
|
56
|
+
zip = "zip",
|
|
57
|
+
others = "others"
|
|
58
|
+
}
|
|
53
59
|
export declare class PermFunc {
|
|
54
60
|
funcCode: string;
|
|
55
61
|
funcName: string;
|
package/dist/models/bizmodels.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CacheNameEnum = exports.StdCrudExportInputParamsAppType = exports.FILE_GET_TYPES = exports.CTX_WORKBENCH_CODE = exports.CTX_VISITOR_ACCOUNT_TYPE = exports.CTX_VISITOR_NICKNAME = exports.CTX_VISITOR_AVATAR = exports.CTX_VISITOR_ID = exports.PermRole = exports.PermFunc = exports.UploadCategoryType = exports.AccessType = void 0;
|
|
3
|
+
exports.CacheNameEnum = exports.StdCrudExportInputParamsAppType = exports.FILE_GET_TYPES = exports.CTX_WORKBENCH_CODE = exports.CTX_VISITOR_ACCOUNT_TYPE = exports.CTX_VISITOR_NICKNAME = exports.CTX_VISITOR_AVATAR = exports.CTX_VISITOR_ID = exports.PermRole = exports.PermFunc = exports.FileCategoryType = exports.UploadCategoryType = exports.AccessType = void 0;
|
|
4
4
|
var AccessType;
|
|
5
5
|
(function (AccessType) {
|
|
6
6
|
AccessType["owner"] = "owner";
|
|
@@ -12,6 +12,13 @@ var UploadCategoryType;
|
|
|
12
12
|
UploadCategoryType["file"] = "file";
|
|
13
13
|
UploadCategoryType["assets"] = "assets";
|
|
14
14
|
})(UploadCategoryType = exports.UploadCategoryType || (exports.UploadCategoryType = {}));
|
|
15
|
+
var FileCategoryType;
|
|
16
|
+
(function (FileCategoryType) {
|
|
17
|
+
FileCategoryType["image"] = "image";
|
|
18
|
+
FileCategoryType["office"] = "office";
|
|
19
|
+
FileCategoryType["zip"] = "zip";
|
|
20
|
+
FileCategoryType["others"] = "others";
|
|
21
|
+
})(FileCategoryType = exports.FileCategoryType || (exports.FileCategoryType = {}));
|
|
15
22
|
class PermFunc {
|
|
16
23
|
constructor(funcCode, funcName) {
|
|
17
24
|
this.funcName = funcName;
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { Context } from '@midwayjs/koa';
|
|
2
|
-
import { AccessType, UploadCategoryType } from '../models/bizmodels';
|
|
2
|
+
import { AccessType, FileCategoryType, IEntityCommonInfo, UploadCategoryType } from '../models/bizmodels';
|
|
3
3
|
import { BaseService } from './base/BaseService';
|
|
4
|
-
|
|
4
|
+
import { ResModelPageQuery } from "../libs/crud-pro/models/ResModel";
|
|
5
|
+
export declare function isImageFile(fileKey: string, mimeType: string): boolean;
|
|
6
|
+
export declare function isOfficeFile(fileKey: string, mimeType: string): boolean;
|
|
7
|
+
export declare function isZipFile(fileKey: string, mimeType: string): boolean;
|
|
8
|
+
export declare function getFileCategory(fileKey: string, mimeType: string): FileCategoryType;
|
|
9
|
+
interface IFileInfo extends IEntityCommonInfo {
|
|
10
|
+
file_key: string;
|
|
11
|
+
file_name: string;
|
|
12
|
+
file_size: number;
|
|
13
|
+
file_type: string;
|
|
14
|
+
file_suffix: string;
|
|
15
|
+
file_desc: string;
|
|
16
|
+
storage_path: string;
|
|
17
|
+
storage_url: string;
|
|
18
|
+
storage_engine: string;
|
|
19
|
+
access_type: string;
|
|
20
|
+
referer: string;
|
|
21
|
+
file_category: string;
|
|
22
|
+
}
|
|
5
23
|
interface IUploadFileToOSSOptions {
|
|
6
24
|
uploadCategoryType: UploadCategoryType;
|
|
7
25
|
uploadPath: string;
|
|
@@ -29,15 +47,18 @@ export declare class FileCenterService extends BaseService {
|
|
|
29
47
|
mimeType: any;
|
|
30
48
|
name: any;
|
|
31
49
|
url: any;
|
|
50
|
+
file_category: FileCategoryType;
|
|
32
51
|
}>;
|
|
33
52
|
deleteFile(fileKey: string): Promise<import("../libs/crud-pro/models/ExecuteContext").ExecuteContext>;
|
|
34
53
|
private headFile;
|
|
35
|
-
|
|
54
|
+
getFileInfo(fileKey: string): Promise<IFileInfo>;
|
|
55
|
+
downloadFile(fileInfo: IFileInfo, streaming: boolean, process: string): Promise<any>;
|
|
36
56
|
/**
|
|
37
57
|
* 检查用户下载权限
|
|
38
58
|
* @param fileInfo
|
|
39
59
|
* @private
|
|
40
60
|
*/
|
|
41
61
|
private checkAuthUserDownloadFile;
|
|
62
|
+
getCurrentUserFiles(file_category: FileCategoryType): Promise<ResModelPageQuery>;
|
|
42
63
|
}
|
|
43
64
|
export {};
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.FileCenterService = exports.toDownloadPaths = exports.PATH_PREFIX = exports.isImageFile = void 0;
|
|
12
|
+
exports.FileCenterService = exports.toDownloadPaths = exports.PATH_PREFIX = exports.getFileCategory = exports.isZipFile = exports.isOfficeFile = exports.isImageFile = void 0;
|
|
13
13
|
const core_1 = require("@midwayjs/core");
|
|
14
14
|
const mime = require("mime-types");
|
|
15
15
|
const md5 = require("md5");
|
|
@@ -76,18 +76,45 @@ function getAssetsContentType(filename) {
|
|
|
76
76
|
return 'application/octet-stream';
|
|
77
77
|
}
|
|
78
78
|
// JPG、PNG、BMP、GIF、WebP、TIFF
|
|
79
|
-
function isImageFile(fileKey) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return false;
|
|
79
|
+
function isImageFile(fileKey, mimeType) {
|
|
80
|
+
if (typeof mimeType === 'string' && mimeType.startsWith('image/'))
|
|
81
|
+
return true;
|
|
82
|
+
const fileKey2 = fileKey.toLowerCase();
|
|
83
|
+
const suffixList = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp', '.tiff'];
|
|
84
|
+
return suffixList.some(suffix => fileKey2.endsWith(suffix));
|
|
89
85
|
}
|
|
90
86
|
exports.isImageFile = isImageFile;
|
|
87
|
+
function isOfficeFile(fileKey, mimeType) {
|
|
88
|
+
if (typeof mimeType === 'string' &&
|
|
89
|
+
(mimeType.startsWith('application/vnd.openxmlformats-officedocument') ||
|
|
90
|
+
mimeType.startsWith('application/msword') ||
|
|
91
|
+
mimeType.startsWith('application/vnd.ms-'))) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
const fileKey2 = fileKey.toLowerCase();
|
|
95
|
+
const suffixList = ['.docx', '.xlsx', '.pptx', '.doc', '.xls', '.ppt', '.pdf'];
|
|
96
|
+
return suffixList.some(suffix => fileKey2.endsWith(suffix));
|
|
97
|
+
}
|
|
98
|
+
exports.isOfficeFile = isOfficeFile;
|
|
99
|
+
function isZipFile(fileKey, mimeType) {
|
|
100
|
+
const suffixList = ['.zip', '.7z', '.rar'];
|
|
101
|
+
const fileKey2 = fileKey.toLowerCase();
|
|
102
|
+
return suffixList.some(suffix => fileKey2.endsWith(suffix));
|
|
103
|
+
}
|
|
104
|
+
exports.isZipFile = isZipFile;
|
|
105
|
+
function getFileCategory(fileKey, mimeType) {
|
|
106
|
+
if (isImageFile(fileKey, mimeType)) {
|
|
107
|
+
return bizmodels_1.FileCategoryType.image;
|
|
108
|
+
}
|
|
109
|
+
if (isOfficeFile(fileKey, mimeType)) {
|
|
110
|
+
return bizmodels_1.FileCategoryType.office;
|
|
111
|
+
}
|
|
112
|
+
if (isZipFile(fileKey, mimeType)) {
|
|
113
|
+
return bizmodels_1.FileCategoryType.zip;
|
|
114
|
+
}
|
|
115
|
+
return bizmodels_1.FileCategoryType.others;
|
|
116
|
+
}
|
|
117
|
+
exports.getFileCategory = getFileCategory;
|
|
91
118
|
class FileDB {
|
|
92
119
|
constructor(curdMixService) {
|
|
93
120
|
this.curdMixService = curdMixService;
|
|
@@ -145,6 +172,17 @@ class FileDB {
|
|
|
145
172
|
};
|
|
146
173
|
return await this.curdMixService.executeCrudByCfg({ condition: { id } }, cfgModel);
|
|
147
174
|
}
|
|
175
|
+
async getFilePage(reqModel) {
|
|
176
|
+
const { SystemDbName, SystemDbType } = global_config_1.GLOBAL_STATIC_CONFIG.getConfig();
|
|
177
|
+
const cfgModel = {
|
|
178
|
+
sqlTable: 'sys_file',
|
|
179
|
+
sqlSimpleName: keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE,
|
|
180
|
+
sqlDatabase: SystemDbName,
|
|
181
|
+
sqlDbType: SystemDbType,
|
|
182
|
+
};
|
|
183
|
+
const res = await this.curdMixService.executeCrudByCfg(reqModel, cfgModel);
|
|
184
|
+
return res.getResModelForQueryPage();
|
|
185
|
+
}
|
|
148
186
|
}
|
|
149
187
|
exports.PATH_PREFIX = '/ns/gw/file';
|
|
150
188
|
function toDownloadPaths(origin, fileKey) {
|
|
@@ -239,6 +277,7 @@ let FileCenterService = class FileCenterService extends BaseService_1.BaseServic
|
|
|
239
277
|
access_type: accessType,
|
|
240
278
|
referer: referer,
|
|
241
279
|
upload_origin: uploadOrigin,
|
|
280
|
+
file_category: getFileCategory(fileKey, file.mimeType)
|
|
242
281
|
};
|
|
243
282
|
if (isAssets) {
|
|
244
283
|
await this.fileBD.saveAssetsLog(saveInfo);
|
|
@@ -256,6 +295,7 @@ let FileCenterService = class FileCenterService extends BaseService_1.BaseServic
|
|
|
256
295
|
mimeType: file.mimeType,
|
|
257
296
|
name: result.name,
|
|
258
297
|
url: result.url,
|
|
298
|
+
file_category: saveInfo.file_category,
|
|
259
299
|
};
|
|
260
300
|
}
|
|
261
301
|
async deleteFile(fileKey) {
|
|
@@ -279,8 +319,10 @@ let FileCenterService = class FileCenterService extends BaseService_1.BaseServic
|
|
|
279
319
|
return false;
|
|
280
320
|
}
|
|
281
321
|
}
|
|
282
|
-
async
|
|
283
|
-
|
|
322
|
+
async getFileInfo(fileKey) {
|
|
323
|
+
return await this.fileBD.getFileInfo(fileKey);
|
|
324
|
+
}
|
|
325
|
+
async downloadFile(fileInfo, streaming, process) {
|
|
284
326
|
if (!fileInfo || fileInfo.status !== 1) {
|
|
285
327
|
throw new devops_1.BizException('文件不存在');
|
|
286
328
|
}
|
|
@@ -336,6 +378,35 @@ let FileCenterService = class FileCenterService extends BaseService_1.BaseServic
|
|
|
336
378
|
}
|
|
337
379
|
return 'OK';
|
|
338
380
|
}
|
|
381
|
+
async getCurrentUserFiles(file_category) {
|
|
382
|
+
const userSession = this.ctx.userSession;
|
|
383
|
+
if (!userSession.isLogin()) {
|
|
384
|
+
throw new devops_1.BizException('您当前未登录,您没有访问此文件的权限');
|
|
385
|
+
}
|
|
386
|
+
const sessionInfo = this.ctx.userSession.getSessionInfo();
|
|
387
|
+
const condition = {
|
|
388
|
+
created_by: sessionInfo.accountId,
|
|
389
|
+
status: 1
|
|
390
|
+
};
|
|
391
|
+
if (file_category) {
|
|
392
|
+
condition.file_category = file_category;
|
|
393
|
+
}
|
|
394
|
+
return await this.fileBD.getFilePage({
|
|
395
|
+
condition: condition,
|
|
396
|
+
columns: [
|
|
397
|
+
'id',
|
|
398
|
+
'file_key',
|
|
399
|
+
'file_name',
|
|
400
|
+
'file_size',
|
|
401
|
+
'file_type',
|
|
402
|
+
'file_suffix',
|
|
403
|
+
'storage_path',
|
|
404
|
+
'storage_url',
|
|
405
|
+
'file_category',
|
|
406
|
+
'created_at'
|
|
407
|
+
]
|
|
408
|
+
});
|
|
409
|
+
}
|
|
339
410
|
};
|
|
340
411
|
__decorate([
|
|
341
412
|
(0, core_1.Inject)(),
|
|
@@ -2,7 +2,7 @@ import { SqlDbType } from '../../libs/crud-pro/models/keys';
|
|
|
2
2
|
import { IRequestModel } from '../../libs/crud-pro/interfaces';
|
|
3
3
|
import { IRequestCfgModel2 } from '../../models/bizmodels';
|
|
4
4
|
import { CrudPro } from '../../libs/crud-pro/CrudPro';
|
|
5
|
-
import { ResModelAffected, ResModelStandard } from '../../libs/crud-pro/models/ResModel';
|
|
5
|
+
import { ResModelAffected, ResModelPageQuery, ResModelStandard } from '../../libs/crud-pro/models/ResModel';
|
|
6
6
|
export declare class CrudProQuick {
|
|
7
7
|
private readonly curdPro;
|
|
8
8
|
private readonly sqlDbType;
|
|
@@ -34,7 +34,7 @@ export declare class CrudProQuick {
|
|
|
34
34
|
* @returns 返回多条数据对象组成的数组
|
|
35
35
|
*/
|
|
36
36
|
getList(reqJson: IRequestModel, sqlTable?: string): Promise<any[]>;
|
|
37
|
-
getListPage(reqJson: IRequestModel, sqlTable?: string): Promise<
|
|
37
|
+
getListPage(reqJson: IRequestModel, sqlTable?: string): Promise<ResModelPageQuery>;
|
|
38
38
|
getTotalCount(reqJson: IRequestModel, sqlTable?: string): Promise<number>;
|
|
39
39
|
insertObject(reqJson: IRequestModel, sqlTable?: string): Promise<ResModelAffected>;
|
|
40
40
|
updateObject(reqJson: IRequestModel, sqlTable?: string): Promise<ResModelAffected>;
|
|
@@ -91,7 +91,7 @@ class CrudProQuick {
|
|
|
91
91
|
sqlSimpleName: keys_1.KeysOfSimpleSQL.SIMPLE_QUERY_PAGE,
|
|
92
92
|
};
|
|
93
93
|
const res = await this.executeCrudByCfg(reqJson, cfgModel);
|
|
94
|
-
return res.
|
|
94
|
+
return res.getResModelForQueryPage();
|
|
95
95
|
}
|
|
96
96
|
async getTotalCount(reqJson, sqlTable) {
|
|
97
97
|
const cfgModel = {
|
package/package.json
CHANGED
|
@@ -122,7 +122,7 @@ export default (appInfo: any) => {
|
|
|
122
122
|
fileSize: '20mb',
|
|
123
123
|
limit: '20mb',
|
|
124
124
|
// whitelist: string[],文件扩展名白名单
|
|
125
|
-
whitelist: ['.jpg', '.jpeg', '.png', '.gif', '.pdf', '.zip', '.xlsx', '.docx', '.pptx', '.txt', '.md', '.json', '.js', '.cjs', '.ejs', '.jsx', '.css', '.java', '.ts', '.tsx', '.less', '.scss', '.html', '.ttf', '.map', '.svg'],
|
|
125
|
+
whitelist: ['.jpg', '.jpeg', '.png', '.gif', '.pdf', '.zip', '.rar', '.xlsx', '.docx', '.pptx', '.txt', '.md', '.json', '.js', '.cjs', '.ejs', '.jsx', '.css', '.java', '.ts', '.tsx', '.less', '.scss', '.html', '.ttf', '.map', '.svg'],
|
|
126
126
|
// tmpdir: string,上传的文件临时存储路径
|
|
127
127
|
tmpdir: join(tmpdir(), 'fatcms-upload-files'),
|
|
128
128
|
// cleanTimeout: number,上传的文件在临时目录中多久之后自动删除,默认为 5 分钟
|
|
@@ -94,14 +94,16 @@ export class FileController extends BaseApiController {
|
|
|
94
94
|
const isPreview = this.ctx.path.startsWith(`${PATH_PREFIX}/get/preview/`) || isTrue(preview);
|
|
95
95
|
const isStreaming = isTrue(streaming);
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
const fileInfo = await this.fileCenterService.getFileInfo(fileKey);
|
|
98
|
+
|
|
99
|
+
if (isThumbnail && isImageFile(fileKey, fileInfo.file_type) && !process) {
|
|
98
100
|
// x-oss-process=image/resize,w_200,p_10/quality,q_60
|
|
99
101
|
process = 'image/resize,w_200'; //图片缩略图(小图片)
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
const isOutByStreaming = isStreaming || isPreview || isThumbnail;
|
|
103
105
|
|
|
104
|
-
const result = await this.fileCenterService.downloadFile(
|
|
106
|
+
const result = await this.fileCenterService.downloadFile(fileInfo, isOutByStreaming, process);
|
|
105
107
|
|
|
106
108
|
if (isOutByStreaming) {
|
|
107
109
|
// 移除这些属性
|
|
@@ -120,4 +122,17 @@ export class FileController extends BaseApiController {
|
|
|
120
122
|
this.ctx.redirect(result.downloadURL);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 获取用户本人上传的文件列表
|
|
129
|
+
*/
|
|
130
|
+
@Get('/getCurrentUserFiles', { middleware: [checkLogin()] })
|
|
131
|
+
public async getCurrentUserFiles(): Promise<CommonResult> {
|
|
132
|
+
let { file_category } = (this.ctx.query || {}) as any;
|
|
133
|
+
const res = await this.fileCenterService.getCurrentUserFiles(file_category);
|
|
134
|
+
return CommonResult.successRes(res);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
123
138
|
}
|
|
@@ -7,7 +7,7 @@ import { Transaction } from './Transaction';
|
|
|
7
7
|
import { ColumnRelation, ICrudProCfg, ILogger, IVisitor } from '../interfaces';
|
|
8
8
|
import { IExecuteContextFunc } from './ExecuteContextFunc';
|
|
9
9
|
import { defaultCrudProCfg } from '../defaultConfigs';
|
|
10
|
-
import {
|
|
10
|
+
import {ResModelFlexible, ResModelPageQuery} from './ResModel';
|
|
11
11
|
|
|
12
12
|
class ExecuteContext {
|
|
13
13
|
// 运行前设置的
|
|
@@ -82,6 +82,15 @@ class ExecuteContext {
|
|
|
82
82
|
return this.resModel[resName];
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
getResModelForQueryPage(): ResModelPageQuery {
|
|
86
|
+
const rows = this.resModel['rows'];
|
|
87
|
+
const total_count = this.resModel['total_count'];
|
|
88
|
+
return {
|
|
89
|
+
rows: rows || [],
|
|
90
|
+
total_count: total_count || 0
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
getResModelItemLodash(resName: string): any {
|
|
86
95
|
return _.get(this.resModel, resName);
|
|
87
96
|
}
|
|
@@ -12,8 +12,13 @@ interface ResModelStandard {
|
|
|
12
12
|
update_affected?: ResModelAffected;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
interface ResModelPageQuery {
|
|
16
|
+
rows: any[];
|
|
17
|
+
total_count: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
type ResModelFlexible = ResModelStandard & {
|
|
16
21
|
[key: string]: any;
|
|
17
22
|
};
|
|
18
23
|
|
|
19
|
-
export { ResModelAffected, ResModelStandard, ResModelFlexible };
|
|
24
|
+
export { ResModelAffected, ResModelStandard, ResModelFlexible, ResModelPageQuery };
|
package/src/models/bizmodels.ts
CHANGED
|
@@ -59,6 +59,13 @@ export enum UploadCategoryType {
|
|
|
59
59
|
assets = 'assets', // 前端资源文件
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export enum FileCategoryType {
|
|
63
|
+
image='image',
|
|
64
|
+
office='office',
|
|
65
|
+
zip='zip',
|
|
66
|
+
others='others',
|
|
67
|
+
}
|
|
68
|
+
|
|
62
69
|
export class PermFunc {
|
|
63
70
|
public funcCode: string;
|
|
64
71
|
public funcName: string;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { Provide
|
|
2
|
-
import {
|
|
1
|
+
import {Inject, Provide} from '@midwayjs/core';
|
|
2
|
+
import {Context} from '@midwayjs/koa';
|
|
3
3
|
import * as mime from 'mime-types';
|
|
4
4
|
import * as md5 from 'md5';
|
|
5
5
|
import * as fs from 'fs';
|
|
6
6
|
import * as util from 'util';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
7
|
+
import {BizException} from '@/models/devops';
|
|
8
|
+
import {CurdMixService} from './curd/CurdMixService';
|
|
9
|
+
import {KeysOfSimpleSQL, KeysOfValidators} from '@/libs/crud-pro/models/keys';
|
|
10
|
+
import {AccessType, FileCategoryType, IEntityCommonInfo, UploadCategoryType} from '@/models/bizmodels';
|
|
11
|
+
import {IRequestCfgModel, IRequestModel} from '@/libs/crud-pro/interfaces';
|
|
12
|
+
import {BaseService} from './base/BaseService';
|
|
13
|
+
import {GLOBAL_STATIC_CONFIG} from '@/libs/global-config/global-config';
|
|
14
|
+
import {ResModelPageQuery} from "@/libs/crud-pro/models/ResModel";
|
|
14
15
|
|
|
15
16
|
function getSuffix(s: string): string {
|
|
16
17
|
if (s) {
|
|
@@ -72,18 +73,47 @@ function getAssetsContentType(filename: string): string {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
// JPG、PNG、BMP、GIF、WebP、TIFF
|
|
75
|
-
export function isImageFile(fileKey: string) {
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
export function isImageFile(fileKey: string, mimeType: string) {
|
|
77
|
+
if (typeof mimeType === 'string' && mimeType.startsWith('image/')) return true;
|
|
78
|
+
const fileKey2= fileKey.toLowerCase();
|
|
79
|
+
const suffixList = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.webp', '.tiff'];
|
|
80
|
+
return suffixList.some(suffix => fileKey2.endsWith(suffix));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function isOfficeFile(fileKey: string, mimeType: string) {
|
|
84
|
+
if (typeof mimeType === 'string' &&
|
|
85
|
+
(mimeType.startsWith('application/vnd.openxmlformats-officedocument') ||
|
|
86
|
+
mimeType.startsWith('application/msword') ||
|
|
87
|
+
mimeType.startsWith('application/vnd.ms-'))) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
const fileKey2= fileKey.toLowerCase();
|
|
91
|
+
const suffixList = ['.docx', '.xlsx', '.pptx', '.doc', '.xls', '.ppt', '.pdf'];
|
|
92
|
+
return suffixList.some(suffix => fileKey2.endsWith(suffix));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function isZipFile(fileKey: string, mimeType: string) {
|
|
96
|
+
const suffixList = ['.zip', '.7z', '.rar'];
|
|
97
|
+
const fileKey2= fileKey.toLowerCase();
|
|
98
|
+
return suffixList.some(suffix => fileKey2.endsWith(suffix));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
export function getFileCategory(fileKey: string, mimeType: string):FileCategoryType{
|
|
104
|
+
if (isImageFile(fileKey, mimeType)){
|
|
105
|
+
return FileCategoryType.image;
|
|
83
106
|
}
|
|
84
|
-
|
|
107
|
+
if (isOfficeFile(fileKey, mimeType)){
|
|
108
|
+
return FileCategoryType.office;
|
|
109
|
+
}
|
|
110
|
+
if (isZipFile(fileKey, mimeType)){
|
|
111
|
+
return FileCategoryType.zip;
|
|
112
|
+
}
|
|
113
|
+
return FileCategoryType.others;
|
|
85
114
|
}
|
|
86
115
|
|
|
116
|
+
|
|
87
117
|
interface IFileInfo extends IEntityCommonInfo {
|
|
88
118
|
file_key: string;
|
|
89
119
|
file_name: string;
|
|
@@ -96,6 +126,7 @@ interface IFileInfo extends IEntityCommonInfo {
|
|
|
96
126
|
storage_engine: string;
|
|
97
127
|
access_type: string;
|
|
98
128
|
referer: string;
|
|
129
|
+
file_category: string;
|
|
99
130
|
}
|
|
100
131
|
|
|
101
132
|
class FileDB {
|
|
@@ -167,6 +198,21 @@ class FileDB {
|
|
|
167
198
|
};
|
|
168
199
|
return await this.curdMixService.executeCrudByCfg({ condition: { id } }, cfgModel);
|
|
169
200
|
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
public async getFilePage(reqModel: IRequestModel): Promise<ResModelPageQuery> {
|
|
204
|
+
const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
|
|
205
|
+
const cfgModel: IRequestCfgModel = {
|
|
206
|
+
sqlTable: 'sys_file',
|
|
207
|
+
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_QUERY_PAGE,
|
|
208
|
+
sqlDatabase: SystemDbName,
|
|
209
|
+
sqlDbType: SystemDbType,
|
|
210
|
+
};
|
|
211
|
+
const res = await this.curdMixService.executeCrudByCfg(reqModel, cfgModel);
|
|
212
|
+
return res.getResModelForQueryPage();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
170
216
|
}
|
|
171
217
|
|
|
172
218
|
interface IUploadFileToOSSOptions {
|
|
@@ -284,6 +330,7 @@ export class FileCenterService extends BaseService {
|
|
|
284
330
|
access_type: accessType,
|
|
285
331
|
referer: referer,
|
|
286
332
|
upload_origin: uploadOrigin,
|
|
333
|
+
file_category: getFileCategory(fileKey, file.mimeType)
|
|
287
334
|
};
|
|
288
335
|
|
|
289
336
|
if (isAssets) {
|
|
@@ -302,6 +349,7 @@ export class FileCenterService extends BaseService {
|
|
|
302
349
|
mimeType: file.mimeType,
|
|
303
350
|
name: result.name,
|
|
304
351
|
url: result.url,
|
|
352
|
+
file_category: saveInfo.file_category,
|
|
305
353
|
};
|
|
306
354
|
}
|
|
307
355
|
|
|
@@ -328,8 +376,12 @@ export class FileCenterService extends BaseService {
|
|
|
328
376
|
}
|
|
329
377
|
}
|
|
330
378
|
|
|
331
|
-
public async
|
|
332
|
-
|
|
379
|
+
public async getFileInfo(fileKey: string): Promise<IFileInfo> {
|
|
380
|
+
return await this.fileBD.getFileInfo(fileKey);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
public async downloadFile(fileInfo: IFileInfo, streaming: boolean, process: string): Promise<any> {
|
|
384
|
+
|
|
333
385
|
if (!fileInfo || fileInfo.status !== 1) {
|
|
334
386
|
throw new BizException('文件不存在');
|
|
335
387
|
}
|
|
@@ -392,4 +444,40 @@ export class FileCenterService extends BaseService {
|
|
|
392
444
|
|
|
393
445
|
return 'OK';
|
|
394
446
|
}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
public async getCurrentUserFiles(file_category: FileCategoryType): Promise<ResModelPageQuery> {
|
|
450
|
+
const userSession = this.ctx.userSession;
|
|
451
|
+
if (!userSession.isLogin()) {
|
|
452
|
+
throw new BizException('您当前未登录,您没有访问此文件的权限');
|
|
453
|
+
}
|
|
454
|
+
const sessionInfo = this.ctx.userSession.getSessionInfo();
|
|
455
|
+
|
|
456
|
+
const condition: any = {
|
|
457
|
+
created_by: sessionInfo.accountId,
|
|
458
|
+
status: 1
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
if (file_category) {
|
|
462
|
+
condition.file_category = file_category
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return await this.fileBD.getFilePage({
|
|
466
|
+
condition: condition,
|
|
467
|
+
columns: [
|
|
468
|
+
'id',
|
|
469
|
+
'file_key',
|
|
470
|
+
'file_name',
|
|
471
|
+
'file_size',
|
|
472
|
+
'file_type',
|
|
473
|
+
'file_suffix',
|
|
474
|
+
'storage_path',
|
|
475
|
+
'storage_url',
|
|
476
|
+
'file_category',
|
|
477
|
+
'created_at'
|
|
478
|
+
]
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
|
|
395
483
|
}
|
|
@@ -2,7 +2,7 @@ import { KeysOfSimpleSQL, SqlDbType } from '@/libs/crud-pro/models/keys';
|
|
|
2
2
|
import { IRequestCfgModel, IRequestModel, ISqlCfgModel } from '@/libs/crud-pro/interfaces';
|
|
3
3
|
import { IRequestCfgModel2 } from '@/models/bizmodels';
|
|
4
4
|
import { CrudPro } from '@/libs/crud-pro/CrudPro';
|
|
5
|
-
import {
|
|
5
|
+
import {ResModelAffected, ResModelPageQuery, ResModelStandard} from '@/libs/crud-pro/models/ResModel';
|
|
6
6
|
|
|
7
7
|
const DEFAULT_MAX_LIMIT = 10 * 10000;
|
|
8
8
|
|
|
@@ -103,13 +103,13 @@ export class CrudProQuick {
|
|
|
103
103
|
return res.getResRows();
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
public async getListPage(reqJson: IRequestModel, sqlTable?: string): Promise<
|
|
106
|
+
public async getListPage(reqJson: IRequestModel, sqlTable?: string): Promise<ResModelPageQuery> {
|
|
107
107
|
const cfgModel: IRequestCfgModel = {
|
|
108
108
|
sqlTable,
|
|
109
109
|
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_QUERY_PAGE,
|
|
110
110
|
};
|
|
111
111
|
const res = await this.executeCrudByCfg(reqJson, cfgModel);
|
|
112
|
-
return res.
|
|
112
|
+
return res.getResModelForQueryPage();
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
public async getTotalCount(reqJson: IRequestModel, sqlTable?: string): Promise<number> {
|