midway-fatcms 0.0.11 → 0.0.13
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/configuration.d.ts +0 -2
- package/dist/configuration.js +14 -12
- package/dist/controller/gateway/CrudMtdGatewayController.js +2 -1
- package/dist/controller/gateway/DocGatewayController.js +7 -5
- package/dist/controller/gateway/PublicApiController.js +32 -3
- package/dist/controller/gateway/StaticController.d.ts +0 -2
- package/dist/controller/gateway/StaticController.js +4 -39
- package/dist/controller/home.controller.js +2 -1
- package/dist/controller/manage/AccountRoleManageApi.d.ts +2 -1
- package/dist/controller/manage/AccountRoleManageApi.js +34 -0
- package/dist/controller/manage/DataDictManageApi.d.ts +2 -0
- package/dist/controller/manage/DataDictManageApi.js +41 -9
- package/dist/controller/manage/DocManageApi.js +2 -2
- package/dist/controller/manage/UserAccountManageApi.js +4 -3
- package/dist/controller/render/AppRenderController.js +7 -6
- package/dist/libs/crud-pro/CrudPro.d.ts +0 -1
- package/dist/libs/crud-pro/CrudPro.js +2 -11
- package/dist/libs/crud-pro/exceptions.d.ts +6 -0
- package/dist/libs/crud-pro/exceptions.js +6 -0
- package/dist/libs/crud-pro/models/keys.d.ts +1 -0
- package/dist/libs/crud-pro/models/keys.js +1 -0
- package/dist/libs/crud-pro/services/CrudProExecuteFuncService.js +2 -2
- package/dist/libs/crud-pro/services/CrudProFieldUpdateService.js +3 -0
- package/dist/libs/crud-pro/services/CrudProTableMetaService.js +2 -2
- package/dist/libs/crud-pro/utils/DatabaseName.js +11 -0
- package/dist/libs/crud-pro/utils/DateTimeUtils.js +1 -1
- package/dist/libs/crud-pro/utils/SqlErrorParseUtils.d.ts +22 -0
- package/dist/libs/crud-pro/utils/SqlErrorParseUtils.js +219 -0
- package/dist/libs/utils/functions.d.ts +8 -1
- package/dist/libs/utils/functions.js +29 -1
- package/dist/service/AuthService.js +1 -1
- package/dist/service/anyapi/AnyApiService.js +1 -1
- package/dist/service/crudstd/CrudStdService.js +8 -3
- package/dist/service/curd/CurdMixByDictService.js +1 -1
- package/dist/service/curd/CurdMixBySysConfigService.js +1 -1
- package/dist/service/curd/CurdMixByWorkbenchService.js +1 -1
- package/dist/service/curd/fixCfgModel.js +3 -1
- package/dist/service/proxyapi/ProxyApiLoadService.js +3 -0
- package/package.json +1 -1
- package/src/configuration.ts +18 -11
- package/src/controller/gateway/CrudMtdGatewayController.ts +2 -1
- package/src/controller/gateway/DocGatewayController.ts +9 -5
- package/src/controller/gateway/PublicApiController.ts +39 -4
- package/src/controller/gateway/StaticController.ts +11 -40
- package/src/controller/home.controller.ts +3 -1
- package/src/controller/manage/AccountRoleManageApi.ts +38 -0
- package/src/controller/manage/DataDictManageApi.ts +48 -11
- package/src/controller/manage/DocManageApi.ts +2 -2
- package/src/controller/manage/UserAccountManageApi.ts +4 -3
- package/src/controller/render/AppRenderController.ts +9 -7
- package/src/libs/crud-pro/CrudPro.ts +2 -12
- package/src/libs/crud-pro/exceptions.ts +6 -0
- package/src/libs/crud-pro/models/keys.ts +1 -0
- package/src/libs/crud-pro/services/CrudProExecuteFuncService.ts +3 -2
- package/src/libs/crud-pro/services/CrudProFieldUpdateService.ts +5 -0
- package/src/libs/crud-pro/services/CrudProTableMetaService.ts +2 -2
- package/src/libs/crud-pro/utils/DatabaseName.ts +12 -0
- package/src/libs/crud-pro/utils/DateTimeUtils.ts +1 -1
- package/src/libs/crud-pro/utils/SqlErrorParseUtils.ts +236 -0
- package/src/libs/utils/functions.ts +37 -2
- package/src/service/AuthService.ts +2 -2
- package/src/service/anyapi/AnyApiService.ts +2 -2
- package/src/service/crudstd/CrudStdService.ts +9 -4
- package/src/service/curd/CurdMixByDictService.ts +1 -1
- package/src/service/curd/CurdMixBySysConfigService.ts +1 -1
- package/src/service/curd/CurdMixByWorkbenchService.ts +1 -1
- package/src/service/curd/fixCfgModel.ts +3 -1
- package/src/service/proxyapi/ProxyApiLoadService.ts +4 -1
|
@@ -4,7 +4,7 @@ import * as _ from 'lodash';
|
|
|
4
4
|
import { BaseApiController } from '../base/BaseApiController';
|
|
5
5
|
import { EnumInfoService } from '@/service/EnumInfoService';
|
|
6
6
|
import { CommonResult } from '@/libs/utils/common-dto';
|
|
7
|
-
import { parseJsonObject } from '@/libs/utils/functions';
|
|
7
|
+
import { isEntityOK, parseJsonObject } from '@/libs/utils/functions';
|
|
8
8
|
import { WorkbenchService } from '@/service/WorkbenchService';
|
|
9
9
|
import { SystemRoleCode } from '@/models/SystemPerm';
|
|
10
10
|
import { KeysOfAuthType } from '@/libs/crud-pro/models/keys';
|
|
@@ -71,10 +71,33 @@ export class PublicApiController extends BaseApiController {
|
|
|
71
71
|
const allMenuList = await this.sysMenuService.getCachedMenusList();
|
|
72
72
|
|
|
73
73
|
const menuList = allMenuList.filter(e => {
|
|
74
|
-
return workbenchMenuCodeSet.has(e.menu_code) && e
|
|
74
|
+
return workbenchMenuCodeSet.has(e.menu_code) && isEntityOK(e);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
const addPermissionInfo = (menu_list: any[], level: number) => {
|
|
79
|
+
if (!Array.isArray(menu_list)) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
if (level > 10) {
|
|
83
|
+
return menu_list;
|
|
84
|
+
}
|
|
85
|
+
return menu_list.map(e => {
|
|
86
|
+
if (Array.isArray(e.children)) {
|
|
87
|
+
e.children = addPermissionInfo(e.children, level + 1);
|
|
88
|
+
}
|
|
89
|
+
const view_auth_config = e.view_auth_config;
|
|
90
|
+
const view_auth_type: KeysOfAuthType = e.view_auth_type || KeysOfAuthType.byFuncCode;
|
|
91
|
+
if (!view_auth_config) {
|
|
92
|
+
return { ...e, hasPermission: true };
|
|
93
|
+
}
|
|
94
|
+
const hasPermission = this.ctx.userSession.isAuthPass(view_auth_type, view_auth_config);
|
|
95
|
+
return { ...e, hasPermission };
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
78
101
|
const mapResult: any = {};
|
|
79
102
|
for (let i = 0; i < menuList.length; i++) {
|
|
80
103
|
const rowElement = cloneObject(menuList[i]);
|
|
@@ -86,7 +109,8 @@ export class PublicApiController extends BaseApiController {
|
|
|
86
109
|
|
|
87
110
|
const menu_config_content = rowElement.menu_config_content;
|
|
88
111
|
delete rowElement.menu_config_content;
|
|
89
|
-
|
|
112
|
+
let menu_list = parseJsonObject(menu_config_content) || [];
|
|
113
|
+
menu_list = addPermissionInfo(menu_list, 0);
|
|
90
114
|
mapResult[menu_code] = { ...rowElement, menu_list, hasPermission };
|
|
91
115
|
}
|
|
92
116
|
|
|
@@ -121,10 +145,21 @@ export class PublicApiController extends BaseApiController {
|
|
|
121
145
|
}
|
|
122
146
|
|
|
123
147
|
if (pathname && workbenchCode) {
|
|
124
|
-
|
|
148
|
+
|
|
149
|
+
const pageData = await this.sysAppService.getSysAppPageOne({
|
|
125
150
|
page_path: pathname,
|
|
126
151
|
workbench_code: workbenchCode
|
|
127
152
|
});
|
|
153
|
+
|
|
154
|
+
if (pageData) {
|
|
155
|
+
const clonePageInfo = { ...pageData };
|
|
156
|
+
const view_auth_config = clonePageInfo.view_auth_config;
|
|
157
|
+
const view_auth_type: KeysOfAuthType = clonePageInfo.view_auth_type;
|
|
158
|
+
const hasPermission = this.ctx.userSession.isAuthPass(view_auth_type, view_auth_config);
|
|
159
|
+
clonePageInfo.hasPermission = hasPermission;
|
|
160
|
+
resultData.pageData = clonePageInfo;
|
|
161
|
+
}
|
|
162
|
+
|
|
128
163
|
}
|
|
129
164
|
|
|
130
165
|
return CommonResult.successRes(resultData);
|
|
@@ -31,9 +31,8 @@ function getPathConfig(ossName: string) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// 404
|
|
34
|
+
// 404列表缓存: 只再内存中记录。重启后继续
|
|
35
35
|
let notFoundList = new Set();
|
|
36
|
-
let notFoundListIsInit = false;
|
|
37
36
|
|
|
38
37
|
/**
|
|
39
38
|
* 静态文件代理功能
|
|
@@ -47,33 +46,11 @@ export class StaticController extends BaseApiController {
|
|
|
47
46
|
const app = ANONYMOUS_CONTEXT.getApp();
|
|
48
47
|
const appDir = app.getAppDir();
|
|
49
48
|
const localDir = path.join(appDir, './public/static'); // 本地文件存储目录
|
|
50
|
-
const notFoundListFile = path.join(localDir, '__404__list.json'); // 404列表文件
|
|
51
49
|
return {
|
|
52
50
|
localDir,
|
|
53
|
-
notFoundListFile,
|
|
54
51
|
};
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
private async initNotFoundList() {
|
|
58
|
-
if (notFoundListIsInit) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
notFoundListIsInit = true;
|
|
62
|
-
|
|
63
|
-
const { localDir, notFoundListFile } = this.getLocalPaths();
|
|
64
|
-
try {
|
|
65
|
-
// 确保本地存储目录存在
|
|
66
|
-
await fs.mkdir(localDir, { recursive: true });
|
|
67
|
-
const data = await fs.readFile(notFoundListFile, 'utf8');
|
|
68
|
-
|
|
69
|
-
notFoundList = new Set(JSON.parse(data));
|
|
70
|
-
console.log(`已加载${notFoundList.size}个404记录`);
|
|
71
|
-
} catch (error) {
|
|
72
|
-
if (error.code !== 'ENOENT') {
|
|
73
|
-
console.error('读取404列表失败:', error);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
54
|
|
|
78
55
|
private async responseLocalFile(localFilePath: string, localHeaderPath: string, relativePath: string): Promise<boolean> {
|
|
79
56
|
const headers = {
|
|
@@ -87,13 +64,18 @@ export class StaticController extends BaseApiController {
|
|
|
87
64
|
if (stats.isFile()) {
|
|
88
65
|
const headerDataStr = await fs.readFile(localHeaderPath, 'utf8');
|
|
89
66
|
const headerData = parseJsonObject(headerDataStr);
|
|
90
|
-
|
|
67
|
+
|
|
68
|
+
if (headerData && headerData['last-modified']) {
|
|
91
69
|
headers['last-modified'] = headerData['last-modified'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (headerData && headerData['etag']) {
|
|
92
73
|
headers['etag'] = headerData['etag'];
|
|
93
74
|
}
|
|
75
|
+
|
|
94
76
|
}
|
|
95
77
|
} catch (error) {
|
|
96
|
-
this.logDebug('[StaticController]responseLocalFile not found header file => ' + localHeaderPath
|
|
78
|
+
this.logDebug('[StaticController]responseLocalFile not found header file => ' + localHeaderPath);
|
|
97
79
|
}
|
|
98
80
|
|
|
99
81
|
try {
|
|
@@ -125,7 +107,7 @@ export class StaticController extends BaseApiController {
|
|
|
125
107
|
|
|
126
108
|
@All('/:ossName/:relativePath+')
|
|
127
109
|
async proxyStaticFile(@Param('ossName') ossName: string, @Param('relativePath') relativePath: string) {
|
|
128
|
-
|
|
110
|
+
|
|
129
111
|
const { localDir } = this.getLocalPaths();
|
|
130
112
|
const PATH_CONFIG = getPathConfig(ossName);
|
|
131
113
|
|
|
@@ -161,9 +143,10 @@ export class StaticController extends BaseApiController {
|
|
|
161
143
|
return;
|
|
162
144
|
}
|
|
163
145
|
} else if (remoteRes.statusCode === 404) {
|
|
146
|
+
|
|
164
147
|
// 记录404并返回
|
|
165
148
|
notFoundList.add(relativePath);
|
|
166
|
-
|
|
149
|
+
|
|
167
150
|
this.logInfo(`[StaticController]远程文件不存在,已记录404: ${relativePath}`);
|
|
168
151
|
this.ctx.status = 404;
|
|
169
152
|
return this.ctx.render('500', { errorMsg: ' 远程文件不存在,已记录404' });
|
|
@@ -185,19 +168,7 @@ export class StaticController extends BaseApiController {
|
|
|
185
168
|
return this.ctx.render('500', { errorMsg: '未知异常' });
|
|
186
169
|
}
|
|
187
170
|
|
|
188
|
-
// 保存404列表
|
|
189
|
-
async saveNotFoundList() {
|
|
190
|
-
const { notFoundListFile } = this.getLocalPaths();
|
|
191
|
-
try {
|
|
192
|
-
// 确保存储404列表的目录存在
|
|
193
|
-
const dir = path.dirname(notFoundListFile);
|
|
194
|
-
await fs.mkdir(dir, { recursive: true });
|
|
195
171
|
|
|
196
|
-
await fs.writeFile(notFoundListFile, JSON.stringify(Array.from(notFoundList), null), 'utf8');
|
|
197
|
-
} catch (error) {
|
|
198
|
-
this.logError('[StaticController]保存404列表失败:', error);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
172
|
|
|
202
173
|
// 封装https请求为异步函数
|
|
203
174
|
async httpsGet(url: string): Promise<HttpGetRes> {
|
|
@@ -6,6 +6,7 @@ import { createRenderUtils } from '@/libs/utils/render-utils';
|
|
|
6
6
|
import { privateAES } from '@/libs/utils/crypto-utils';
|
|
7
7
|
import { AuthService } from '@/service/AuthService';
|
|
8
8
|
import { ISessionInfo } from '@/models/userSession';
|
|
9
|
+
import { isEntityOK } from '@/libs/utils/functions';
|
|
9
10
|
|
|
10
11
|
@Controller('/')
|
|
11
12
|
export class HomeController extends BaseApiController {
|
|
@@ -25,7 +26,7 @@ export class HomeController extends BaseApiController {
|
|
|
25
26
|
|
|
26
27
|
const workbenchInfo = await this.workbenchService.getCurrentHostWorkbenchInfo();
|
|
27
28
|
|
|
28
|
-
if (!workbenchInfo
|
|
29
|
+
if (!isEntityOK(workbenchInfo)) {
|
|
29
30
|
this.logInfo(`域名不存在:hostname = ${hostname}, host=${host}`);
|
|
30
31
|
const infos = {
|
|
31
32
|
host,
|
|
@@ -35,6 +36,7 @@ export class HomeController extends BaseApiController {
|
|
|
35
36
|
return this.ctx.render('404_workbench', infos);
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
|
|
38
40
|
const html_content = workbenchInfo.html_content || '未配置HTML模版';
|
|
39
41
|
|
|
40
42
|
const userInfo = await this.getUserAndRefreshSessionInfo();
|
|
@@ -7,6 +7,9 @@ import { refreshCache } from '@/middleware/cacherefresh.middleware';
|
|
|
7
7
|
import { CacheNameEnum } from '@/models/bizmodels';
|
|
8
8
|
import { SystemFuncCode } from '@/models/SystemPerm';
|
|
9
9
|
import { RelatedType } from '@/service/curd/CurdMixUtils';
|
|
10
|
+
import { SystemTables } from '@/models/SystemTables';
|
|
11
|
+
import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
|
|
12
|
+
import { CommonResult } from '@/libs/utils/common-dto';
|
|
10
13
|
|
|
11
14
|
@Controller('/ns/api/manage/accountRole', { middleware: [checkPermission(SystemFuncCode.AccountRoleManageRead)] })
|
|
12
15
|
export class AccountRoleManageApi extends BaseApiController {
|
|
@@ -39,6 +42,41 @@ export class AccountRoleManageApi extends BaseApiController {
|
|
|
39
42
|
|
|
40
43
|
@Post('/createAccountRole', { middleware: [checkPermission(SystemFuncCode.AccountRoleManageWrite), refreshCache(CacheNameEnum.CurdMixByAccount)] })
|
|
41
44
|
async createAccountRole() {
|
|
45
|
+
const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
|
|
46
|
+
const body = this.ctx.request.body as any;
|
|
47
|
+
const accountId = body.data?.account_id;
|
|
48
|
+
const roleCode = body.data?.role_code;
|
|
49
|
+
|
|
50
|
+
// 校验 account_id 是否存在
|
|
51
|
+
if (accountId) {
|
|
52
|
+
const accountInfoRes = await this.curdMixService.getQuickCrud({
|
|
53
|
+
sqlDatabase: SystemDbName,
|
|
54
|
+
sqlDbType: SystemDbType,
|
|
55
|
+
sqlTable: SystemTables.sys_user_account,
|
|
56
|
+
}).isExist({
|
|
57
|
+
condition: { account_id: accountId }
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (!accountInfoRes.exists) {
|
|
61
|
+
return CommonResult.errorRes(`账户ID '${accountId}' 不存在`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 校验 role_code 是否存在
|
|
66
|
+
if (roleCode) {
|
|
67
|
+
const roleInfoRes = await this.curdMixService.getQuickCrud({
|
|
68
|
+
sqlDatabase: SystemDbName,
|
|
69
|
+
sqlDbType: SystemDbType,
|
|
70
|
+
sqlTable: SystemTables.sys_perm_role,
|
|
71
|
+
}).isExist({
|
|
72
|
+
condition: { role_code: roleCode }
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!roleInfoRes.exists) {
|
|
76
|
+
return CommonResult.errorRes(`角色编码 '${roleCode}' 不存在`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
42
80
|
return this.executeSysSimpleSQL('sys_perm_user_role', KeysOfSimpleSQL.SIMPLE_INSERT, {
|
|
43
81
|
validateCfg: {
|
|
44
82
|
'data.account_id': [KeysOfValidators.REQUIRED, KeysOfValidators.STRING],
|
|
@@ -6,7 +6,9 @@ import { checkPermission } from '@/middleware/permission.middleware';
|
|
|
6
6
|
import { refreshCache } from '@/middleware/cacherefresh.middleware';
|
|
7
7
|
import { CacheNameEnum } from '@/models/bizmodels';
|
|
8
8
|
import { SystemFuncCode } from '@/models/SystemPerm';
|
|
9
|
-
import {CommonResult} from "@/libs/utils/common-dto";
|
|
9
|
+
import { CommonResult } from "@/libs/utils/common-dto";
|
|
10
|
+
import { CommonException, Exceptions } from '@/libs/crud-pro/exceptions';
|
|
11
|
+
import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
|
|
10
12
|
|
|
11
13
|
@Controller('/ns/api/manage/dataDict', { middleware: [checkPermission(SystemFuncCode.DataDictMangeRead)] })
|
|
12
14
|
export class DataDictManageApi extends BaseApiController {
|
|
@@ -29,12 +31,47 @@ export class DataDictManageApi extends BaseApiController {
|
|
|
29
31
|
|
|
30
32
|
@Post('/createDataDict', { middleware: [checkPermission(SystemFuncCode.DataDictMangeWrite), refreshCache(CacheNameEnum.GetDataDictItemsByDictCode)] })
|
|
31
33
|
async createDataDict() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const body = this.ctx.request.body as any;
|
|
35
|
+
const dict_code = body?.data?.dict_code;
|
|
36
|
+
try {
|
|
37
|
+
return await this.executeSysSimpleSQL('sys_data_dict', KeysOfSimpleSQL.SIMPLE_INSERT, {
|
|
38
|
+
enableSoftDelete: true,
|
|
39
|
+
validateCfg: {
|
|
40
|
+
'data.dict_code': [KeysOfValidators.REQUIRED],
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
} catch (e) {
|
|
44
|
+
if (e?.code === Exceptions.RUN_SQL_EXCEPTION_ER_DUP_ENTRY && dict_code) {
|
|
45
|
+
const isSoftDeleted = await this.isSoftDeletedDictCode(dict_code);
|
|
46
|
+
if (isSoftDeleted) {
|
|
47
|
+
throw new CommonException(
|
|
48
|
+
Exceptions.RUN_SQL_EXCEPTION_ER_DUP_ENTRY,
|
|
49
|
+
'该 dict_code 对应的数据曾被软删除,无法直接创建。请联系 DBA 处理,或更换 dict_code 后重试。'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw e;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 查询 dict_code 是否仅被软删除记录占用(不含 deleted_at=0 的活跃记录) */
|
|
58
|
+
private async isSoftDeletedDictCode(dict_code: string): Promise<boolean> {
|
|
59
|
+
const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
|
|
60
|
+
const result = await this.curdMixService.executeCrudByCfg(
|
|
61
|
+
{ condition: { dict_code } },
|
|
62
|
+
{
|
|
63
|
+
sqlTable: 'sys_data_dict',
|
|
64
|
+
sqlSimpleName: KeysOfSimpleSQL.SIMPLE_QUERY_ONE,
|
|
65
|
+
sqlDatabase: SystemDbName,
|
|
66
|
+
sqlDbType: SystemDbType,
|
|
67
|
+
enableSoftDelete: false,
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
const row = result.getOneObj();
|
|
71
|
+
if (!row) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return Number(row.deleted_at) !== 0;
|
|
38
75
|
}
|
|
39
76
|
|
|
40
77
|
@Post('/updateDataDict', { middleware: [checkPermission(SystemFuncCode.DataDictMangeWrite), refreshCache(CacheNameEnum.GetDataDictItemsByDictCode)] })
|
|
@@ -42,7 +79,7 @@ export class DataDictManageApi extends BaseApiController {
|
|
|
42
79
|
return this.executeSysSimpleSQL('sys_data_dict', KeysOfSimpleSQL.SIMPLE_UPDATE, {
|
|
43
80
|
enableSoftDelete: true,
|
|
44
81
|
validateCfg: {
|
|
45
|
-
'condition.id': [KeysOfValidators.REQUIRED
|
|
82
|
+
'condition.id': [KeysOfValidators.REQUIRED],
|
|
46
83
|
},
|
|
47
84
|
});
|
|
48
85
|
}
|
|
@@ -57,7 +94,7 @@ export class DataDictManageApi extends BaseApiController {
|
|
|
57
94
|
return this.executeSysSimpleSQL('sys_data_dict', KeysOfSimpleSQL.SIMPLE_DELETE, {
|
|
58
95
|
enableSoftDelete: true,
|
|
59
96
|
validateCfg: {
|
|
60
|
-
'condition.id': [KeysOfValidators.REQUIRED
|
|
97
|
+
'condition.id': [KeysOfValidators.REQUIRED],
|
|
61
98
|
'condition.dict_code': [KeysOfValidators.REQUIRED],
|
|
62
99
|
},
|
|
63
100
|
});
|
|
@@ -94,7 +131,7 @@ export class DataDictManageApi extends BaseApiController {
|
|
|
94
131
|
return this.executeSysSimpleSQL('sys_data_dict_item', KeysOfSimpleSQL.SIMPLE_UPDATE, {
|
|
95
132
|
enableSoftDelete: true,
|
|
96
133
|
validateCfg: {
|
|
97
|
-
'condition.id': [KeysOfValidators.REQUIRED
|
|
134
|
+
'condition.id': [KeysOfValidators.REQUIRED],
|
|
98
135
|
},
|
|
99
136
|
});
|
|
100
137
|
}
|
|
@@ -104,7 +141,7 @@ export class DataDictManageApi extends BaseApiController {
|
|
|
104
141
|
return this.executeSysSimpleSQL('sys_data_dict_item', KeysOfSimpleSQL.SIMPLE_DELETE, {
|
|
105
142
|
enableSoftDelete: true,
|
|
106
143
|
validateCfg: {
|
|
107
|
-
'condition.id': [KeysOfValidators.REQUIRED
|
|
144
|
+
'condition.id': [KeysOfValidators.REQUIRED],
|
|
108
145
|
'condition.dict_code': [KeysOfValidators.REQUIRED],
|
|
109
146
|
},
|
|
110
147
|
});
|
|
@@ -80,7 +80,7 @@ export class DocManageApi extends BaseApiController {
|
|
|
80
80
|
return this.executeSysSimpleSQL('sys_doc', KeysOfSimpleSQL.SIMPLE_UPDATE, {
|
|
81
81
|
enableSoftDelete: true,
|
|
82
82
|
validateCfg: {
|
|
83
|
-
'condition.id': [KeysOfValidators.REQUIRED
|
|
83
|
+
'condition.id': [KeysOfValidators.REQUIRED],
|
|
84
84
|
},
|
|
85
85
|
updateCfg: {
|
|
86
86
|
...updateCfg,
|
|
@@ -94,7 +94,7 @@ export class DocManageApi extends BaseApiController {
|
|
|
94
94
|
return this.executeSysSimpleSQL('sys_doc', KeysOfSimpleSQL.SIMPLE_DELETE, {
|
|
95
95
|
enableSoftDelete: true,
|
|
96
96
|
validateCfg: {
|
|
97
|
-
'condition.id': [KeysOfValidators.REQUIRED
|
|
97
|
+
'condition.id': [KeysOfValidators.REQUIRED],
|
|
98
98
|
},
|
|
99
99
|
updateCfg: {
|
|
100
100
|
'condition.workbench_code': { contextAsString: CTX_WORKBENCH_CODE },
|
|
@@ -2,13 +2,13 @@ import { Controller, Inject, Post } from '@midwayjs/core';
|
|
|
2
2
|
import { Context } from '@midwayjs/koa';
|
|
3
3
|
import { KeysOfSimpleSQL, KeysOfValidators } from '../../libs/crud-pro/models/keys';
|
|
4
4
|
import { BaseApiController } from '../base/BaseApiController';
|
|
5
|
-
import { createUniqueId } from '../../libs/utils/functions';
|
|
6
5
|
import { checkPermission } from '../../middleware/permission.middleware';
|
|
7
6
|
import { SystemFuncCode } from '../../models/SystemPerm';
|
|
8
7
|
import { CommonException, Exceptions } from '../../libs/crud-pro/exceptions';
|
|
9
8
|
import { SystemTables } from '../../models/SystemTables';
|
|
10
9
|
import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
|
|
11
10
|
import { CommonResult } from "@/libs/utils/common-dto";
|
|
11
|
+
import { MixinUtils } from '@/libs/crud-pro/utils/MixinUtils';
|
|
12
12
|
|
|
13
13
|
const accountNameBlacklist = new Set(['sa', 'root', 'admin', 'superadmin', 'administrator', 'sys', 'sysop', 'schedule_user']);
|
|
14
14
|
function checkAccountCreateBlacklist(value: string) {
|
|
@@ -80,23 +80,24 @@ export class UserAccountManageApi extends BaseApiController {
|
|
|
80
80
|
@Post('/createUserAccount', { middleware: [checkPermission(SystemFuncCode.UserAccountMangeWrite)] })
|
|
81
81
|
async createUserAccount() {
|
|
82
82
|
const body = this.ctx.request.body as any;
|
|
83
|
-
body.data.account_id = createUniqueId();
|
|
84
83
|
|
|
85
84
|
const { generateUserAccountId } = GLOBAL_STATIC_CONFIG.getConfig();
|
|
86
85
|
if (typeof generateUserAccountId === 'function') {
|
|
87
86
|
body.data.account_id = await generateUserAccountId(body.data);
|
|
87
|
+
} else {
|
|
88
|
+
body.data.account_id = MixinUtils.generateSnowflakeId();
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
const result = await this.executeSysSimpleSQL(SystemTables.sys_user_account, KeysOfSimpleSQL.SIMPLE_INSERT, {
|
|
91
92
|
validateCfg: {
|
|
92
93
|
'data.login_name': LOGIN_NAME_VALIDATE,
|
|
93
94
|
'data.nick_name': [KeysOfValidators.REQUIRED, KeysOfValidators.STRING, 'length:2,20', checkAccountCreateBlacklist],
|
|
94
|
-
'data.avatar': [KeysOfValidators.REQUIRED, KeysOfValidators.STRING],
|
|
95
95
|
},
|
|
96
96
|
});
|
|
97
97
|
return result;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
|
|
100
101
|
@Post('/deleteUserAccount', { middleware: [checkPermission(SystemFuncCode.UserAccountMangeWrite)] })
|
|
101
102
|
async deleteUserAccount() {
|
|
102
103
|
const result = await this.executeSysSimpleSQL(SystemTables.sys_user_account, KeysOfSimpleSQL.SIMPLE_DELETE, {
|
|
@@ -7,6 +7,7 @@ import { privateAES } from '@/libs/utils/crypto-utils';
|
|
|
7
7
|
import { SysAppService } from "@/service/SysAppService";
|
|
8
8
|
import { ISessionInfo } from '@/models/userSession';
|
|
9
9
|
import { AuthService } from '@/service/AuthService';
|
|
10
|
+
import { isEntityOK } from '@/libs/utils/functions';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 渲染独立外部应用
|
|
@@ -33,14 +34,9 @@ export class AppRenderController extends BaseApiController {
|
|
|
33
34
|
|
|
34
35
|
const appInfo = await this.sysAppService.getSysAppOne(appCode);
|
|
35
36
|
|
|
36
|
-
if (!appInfo) {
|
|
37
|
+
if (!isEntityOK(appInfo)) {
|
|
37
38
|
this.ctx.status = 404;
|
|
38
|
-
return this.ctx.render('404_app', { appCode, errorMsg: '
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (appInfo.status !== 1) {
|
|
42
|
-
this.ctx.status = 404;
|
|
43
|
-
return this.ctx.render('404_app', { appCode, errorMsg: ' 应用已下线' });
|
|
39
|
+
return this.ctx.render('404_app', { appCode, errorMsg: '应用不存在或已下线' });
|
|
44
40
|
}
|
|
45
41
|
|
|
46
42
|
const isSupportWorkbench = await this.isSupportCurrentWorkbench(appInfo);
|
|
@@ -51,6 +47,12 @@ export class AppRenderController extends BaseApiController {
|
|
|
51
47
|
|
|
52
48
|
const workbenchInfo = await this.workbenchService.getCurrentHostWorkbenchInfo();
|
|
53
49
|
|
|
50
|
+
if (!isEntityOK(workbenchInfo)) {
|
|
51
|
+
this.ctx.status = 404;
|
|
52
|
+
return this.ctx.render('404_app', { appCode, errorMsg: '站点不存在或已下线' });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
54
56
|
const html_content = appInfo.html_content || '<b style="color: red">错误:未配置HTML模版,请检查!!</b>';
|
|
55
57
|
const userInfo = await this.getUserAndRefreshSessionInfo();
|
|
56
58
|
|
|
@@ -5,6 +5,7 @@ import { CurdProServiceHub } from './services/CurdProServiceHub';
|
|
|
5
5
|
import { CommonException, Exceptions } from './exceptions';
|
|
6
6
|
import { RequestCfgModel } from './models/RequestCfgModel';
|
|
7
7
|
import { MixinUtils } from './utils/MixinUtils';
|
|
8
|
+
import { SqlErrorParseUtils } from './utils/SqlErrorParseUtils';
|
|
8
9
|
import { Transaction } from './models/Transaction';
|
|
9
10
|
import { IExecuteContextFunc } from './models/ExecuteContextFunc';
|
|
10
11
|
import { KeysOfSimpleSQL } from './models/keys';
|
|
@@ -417,21 +418,10 @@ class CrudPro {
|
|
|
417
418
|
const message = MixinUtils.getErrorMessage(e);
|
|
418
419
|
const logger = this.executeContext.getLogger();
|
|
419
420
|
logger.error('RUN_SQL_EXCEPTION', message);
|
|
420
|
-
throw
|
|
421
|
+
throw SqlErrorParseUtils.parseRunSqlException(e || {});
|
|
421
422
|
}
|
|
422
423
|
}
|
|
423
424
|
|
|
424
|
-
private parseRunSqlException(e: any): CommonException {
|
|
425
|
-
const sqlMessage = '' + e.sqlMessage;
|
|
426
|
-
if (e.code === 'ER_DUP_ENTRY' && sqlMessage.startsWith('Duplicate entry')) {
|
|
427
|
-
return new CommonException(Exceptions.RUN_SQL_EXCEPTION_ER_DUP_ENTRY, '此对象实体已存在,不能创建重复对象');
|
|
428
|
-
}
|
|
429
|
-
if (e.code === 'ER_NO_SUCH_TABLE') {
|
|
430
|
-
return new CommonException(Exceptions.RUN_SQL_EXCEPTION_ER_NO_SUCH_TABLE, '查询的表不存在:' + sqlMessage);
|
|
431
|
-
}
|
|
432
|
-
return e;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
425
|
private async afterExecuteSQLList() {
|
|
436
426
|
return this.executeContext.contextFunc.afterExecuteSQLList();
|
|
437
427
|
}
|
|
@@ -86,6 +86,12 @@ export enum Exceptions {
|
|
|
86
86
|
RUN_SQL_EXCEPTION = 'RUN_SQL_EXCEPTION',
|
|
87
87
|
RUN_SQL_EXCEPTION_ER_DUP_ENTRY = 'RUN_SQL_EXCEPTION_ER_DUP_ENTRY',
|
|
88
88
|
RUN_SQL_EXCEPTION_ER_NO_SUCH_TABLE = 'RUN_SQL_EXCEPTION_ER_NO_SUCH_TABLE',
|
|
89
|
+
RUN_SQL_EXCEPTION_ER_FK_PARENT_REFERENCED = 'RUN_SQL_EXCEPTION_ER_FK_PARENT_REFERENCED',
|
|
90
|
+
RUN_SQL_EXCEPTION_ER_FK_PARENT_NOT_FOUND = 'RUN_SQL_EXCEPTION_ER_FK_PARENT_NOT_FOUND',
|
|
91
|
+
RUN_SQL_EXCEPTION_ER_NOT_NULL = 'RUN_SQL_EXCEPTION_ER_NOT_NULL',
|
|
92
|
+
RUN_SQL_EXCEPTION_ER_DATA_TOO_LONG = 'RUN_SQL_EXCEPTION_ER_DATA_TOO_LONG',
|
|
93
|
+
RUN_SQL_EXCEPTION_ER_BAD_FIELD = 'RUN_SQL_EXCEPTION_ER_BAD_FIELD',
|
|
94
|
+
RUN_SQL_EXCEPTION_ER_DEADLOCK = 'RUN_SQL_EXCEPTION_ER_DEADLOCK',
|
|
89
95
|
RUN_IS_NEED_EXECUTE_ERR = 'RUN_IS_NEED_EXECUTE_ERR',
|
|
90
96
|
RUN_PICK_ERR_VISITOR_NULL = 'RUN_PICK_ERR_VISITOR_NULL',
|
|
91
97
|
RUN_PICK_ERR_VISITOR_FIELD = 'RUN_PICK_ERR_VISITOR_FIELD',
|
|
@@ -95,8 +95,9 @@ class CrudProExecuteFuncService extends CrudProServiceBase {
|
|
|
95
95
|
if (pickString.startsWith(KeysOfFunCtx.VISITOR_ATTR)) {
|
|
96
96
|
const reqModel = funcCtx?.reqModel;
|
|
97
97
|
const attrValue = _.get(reqModel, pickString); // visitor.nickName
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
|
|
99
|
+
if (isNil(attrValue) && ![`${KeysOfFunCtx.VISITOR_AVATAR}`].includes(pickString)) {
|
|
100
|
+
throw new CommonException(Exceptions.RUN_PICK_ERR_VISITOR_NULL, '获取用户字段${pickString}失败, 可能用户没有登陆');
|
|
100
101
|
}
|
|
101
102
|
return attrValue;
|
|
102
103
|
}
|
|
@@ -49,7 +49,12 @@ class CrudProFieldUpdateService extends CrudProServiceBase {
|
|
|
49
49
|
|
|
50
50
|
result = this.serviceHub.executeFuncCfg(updateCfg, exeFunCtx);
|
|
51
51
|
} catch (e) {
|
|
52
|
+
|
|
52
53
|
this.logger.error('getUpdateNewValueByCfg->executeFuncCfg', e);
|
|
54
|
+
if (e instanceof CommonException) {
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
throw new CommonException(Exceptions.UPDATE_EXCEPTION, key + '.' + e);
|
|
54
59
|
}
|
|
55
60
|
|
|
@@ -166,12 +166,12 @@ class CrudProTableMetaService extends CrudProServiceBase {
|
|
|
166
166
|
const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType } as IExecuteUnsafeQueryCtx;
|
|
167
167
|
|
|
168
168
|
// 获取物理表
|
|
169
|
-
const tableSql = `SHOW TABLES
|
|
169
|
+
const tableSql = `SHOW TABLES`;
|
|
170
170
|
const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
|
|
171
171
|
const tableNames: string[] = (tableRes.rows || []).map((row: any) => Object.values(row)[0] as string);
|
|
172
172
|
|
|
173
173
|
// 获取视图
|
|
174
|
-
const viewSql = `SHOW FULL TABLES
|
|
174
|
+
const viewSql = `SHOW FULL TABLES WHERE Table_type = 'VIEW'`;
|
|
175
175
|
const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
|
|
176
176
|
const viewNames: string[] = (viewRes.rows || []).map((row: any) => Object.values(row)[0] as string);
|
|
177
177
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SqlDbType } from '../models/keys';
|
|
2
|
+
import { GLOBAL_STATIC_CONFIG } from '../../global-config/global-config'
|
|
2
3
|
|
|
3
4
|
interface IDbTypeAndName {
|
|
4
5
|
dbType: SqlDbType;
|
|
@@ -35,6 +36,17 @@ function parseDatabaseName(databaseName: string): IDbTypeAndName {
|
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
const gConfig = GLOBAL_STATIC_CONFIG.getConfig();
|
|
40
|
+
// 访问系统库
|
|
41
|
+
if (databaseName === 'fatcms' || databaseName === gConfig.SystemDbName) {
|
|
42
|
+
if (dbNameArr.length === 1) {
|
|
43
|
+
return {
|
|
44
|
+
dbType: gConfig.SystemDbType,
|
|
45
|
+
dbName: dbNameArr[0],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
38
50
|
if (dbNameArr.length === 1) {
|
|
39
51
|
return {
|
|
40
52
|
dbType: SqlDbType.mysql, // 默认
|