lupine.api 1.1.58 → 1.1.59
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/README.md +3 -3
- package/admin/admin-about.tsx +12 -16
- package/admin/admin-config.tsx +47 -44
- package/admin/admin-css.tsx +3 -3
- package/admin/admin-db.tsx +75 -75
- package/admin/admin-frame-helper.tsx +364 -364
- package/admin/admin-frame.tsx +164 -164
- package/admin/admin-index.tsx +65 -65
- package/admin/admin-login.tsx +111 -111
- package/admin/admin-menu-edit.tsx +637 -637
- package/admin/admin-menu-list.tsx +87 -87
- package/admin/admin-page-edit.tsx +564 -564
- package/admin/admin-page-list.tsx +83 -83
- package/admin/admin-performance.tsx +28 -28
- package/admin/admin-release.tsx +427 -426
- package/admin/admin-resources.tsx +382 -382
- package/admin/admin-shell.tsx +89 -89
- package/admin/admin-table-data.tsx +146 -146
- package/admin/admin-table-list.tsx +230 -230
- package/admin/admin-test-animations.tsx +395 -395
- package/admin/admin-test-component.tsx +823 -808
- package/admin/admin-test-edit.tsx +319 -319
- package/admin/admin-test-themes.tsx +56 -56
- package/admin/admin-tokens.tsx +338 -338
- package/admin/design/admin-design.tsx +174 -174
- package/admin/design/block-grid.tsx +36 -36
- package/admin/design/block-grid1.tsx +21 -21
- package/admin/design/block-paragraph.tsx +19 -19
- package/admin/design/block-title.tsx +19 -19
- package/admin/design/design-block-box.tsx +140 -140
- package/admin/design/drag-data.tsx +24 -24
- package/admin/index.ts +9 -9
- package/admin/package.json +15 -15
- package/admin/tsconfig.json +127 -127
- package/dev/copy-folder.js +32 -32
- package/dev/cp-index-html.js +69 -69
- package/dev/file-utils.js +12 -12
- package/dev/index.js +18 -19
- package/dev/package.json +12 -12
- package/dev/plugin-ifelse.js +168 -168
- package/dev/plugin-ifelse.test.js +37 -37
- package/dev/run-cmd.js +14 -14
- package/dev/send-request.js +12 -12
- package/package.json +55 -55
- package/src/admin-api/admin-api-helper.ts +210 -205
- package/src/admin-api/admin-api.ts +65 -65
- package/src/admin-api/admin-auth.ts +152 -146
- package/src/admin-api/admin-config.ts +94 -84
- package/src/admin-api/admin-csv.ts +94 -94
- package/src/admin-api/admin-db.ts +269 -269
- package/src/admin-api/admin-menu.ts +135 -135
- package/src/admin-api/admin-page.ts +135 -135
- package/src/admin-api/admin-performance.ts +128 -128
- package/src/admin-api/admin-release.ts +703 -700
- package/src/admin-api/admin-resources.ts +318 -318
- package/src/admin-api/admin-token-helper.ts +82 -79
- package/src/admin-api/admin-tokens.ts +90 -90
- package/src/admin-api/index.ts +2 -2
- package/src/admin-api/web-config-api.ts +19 -19
- package/src/api/api-cache.ts +103 -103
- package/src/api/api-helper.ts +44 -44
- package/src/api/api-module.ts +67 -60
- package/src/api/api-router.ts +177 -177
- package/src/api/api-shared-storage.ts +64 -64
- package/src/api/async-storage.ts +5 -5
- package/src/api/debug-service.ts +56 -56
- package/src/api/encode-html.ts +27 -27
- package/src/api/handle-status.ts +75 -75
- package/src/api/index.ts +15 -16
- package/src/api/mini-web-socket.ts +270 -270
- package/src/api/server-content-type.ts +82 -82
- package/src/api/server-render.ts +235 -215
- package/src/api/shell-service.ts +74 -74
- package/src/api/simple-storage.ts +80 -80
- package/src/api/static-server.ts +128 -125
- package/src/api/to-client-delivery.ts +26 -26
- package/src/app/app-cache.ts +55 -55
- package/src/app/app-helper.ts +62 -62
- package/src/app/app-message.ts +109 -109
- package/src/app/app-shared-storage.ts +363 -363
- package/src/app/app-start.ts +136 -136
- package/src/app/cleanup-exit.ts +16 -16
- package/src/app/host-to-path.ts +38 -38
- package/src/app/index.ts +11 -11
- package/src/app/process-dev-requests.ts +130 -130
- package/src/app/web-listener.ts +294 -294
- package/src/app/web-processor.ts +47 -42
- package/src/app/web-server.ts +100 -100
- package/src/common-js/web-env.js +104 -104
- package/src/index.ts +7 -7
- package/src/lang/api-lang-en.ts +26 -26
- package/src/lang/api-lang-zh-cn.ts +27 -27
- package/src/lang/index.ts +2 -2
- package/src/lang/lang-helper.ts +76 -76
- package/src/lang/lang-props.ts +6 -6
- package/src/lib/db/db-helper.ts +23 -23
- package/src/lib/db/db-mysql.ts +249 -250
- package/src/lib/db/db-sqlite.ts +101 -101
- package/src/lib/db/db.spec.ts +28 -28
- package/src/lib/db/db.ts +325 -325
- package/src/lib/db/index.ts +5 -5
- package/src/lib/index.ts +3 -3
- package/src/lib/logger.spec.ts +214 -214
- package/src/lib/logger.ts +281 -281
- package/src/lib/runtime-require.ts +37 -37
- package/src/lib/utils/cookie-util.ts +34 -34
- package/src/lib/utils/crypto.ts +58 -58
- package/src/lib/utils/date-utils.ts +317 -317
- package/src/lib/utils/deep-merge.ts +37 -37
- package/src/lib/utils/delay.ts +12 -12
- package/src/lib/utils/file-setting.ts +55 -55
- package/src/lib/utils/format-bytes.ts +11 -11
- package/src/lib/utils/fs-utils.ts +158 -158
- package/src/lib/utils/get-env.ts +27 -27
- package/src/lib/utils/index.ts +12 -12
- package/src/lib/utils/is-type.ts +48 -48
- package/src/lib/utils/load-env.ts +14 -14
- package/src/lib/utils/pad.ts +6 -6
- package/src/models/api-base.ts +5 -5
- package/src/models/api-module-props.ts +10 -11
- package/src/models/api-router-props.ts +26 -26
- package/src/models/app-cache-props.ts +33 -33
- package/src/models/app-data-props.ts +10 -10
- package/src/models/app-helper-props.ts +6 -6
- package/src/models/app-shared-storage-props.ts +38 -38
- package/src/models/app-start-props.ts +18 -18
- package/src/models/async-storage-props.ts +13 -13
- package/src/models/db-config.ts +30 -30
- package/src/models/host-to-path-props.ts +12 -12
- package/src/models/index.ts +16 -16
- package/src/models/json-object.ts +8 -8
- package/src/models/locals-props.ts +36 -36
- package/src/models/logger-props.ts +84 -84
- package/src/models/simple-storage-props.ts +13 -14
- package/src/models/to-client-delivery-props.ts +6 -6
- package/tsconfig.json +115 -115
- package/dev/plugin-gen-versions.js +0 -20
|
@@ -1,146 +1,152 @@
|
|
|
1
|
-
import { ServerResponse } from 'http';
|
|
2
|
-
import { Logger, ServerRequest, ApiHelper } from 'lupine.api';
|
|
3
|
-
import { CryptoUtils } from '../lib/utils/crypto';
|
|
4
|
-
import { adminApiHelper, DEV_ADMIN_CRYPTO_KEY_NAME, DEV_ADMIN_TYPE, DevAdminSessionProps } from './admin-api-helper';
|
|
5
|
-
import { langHelper } from '../lang';
|
|
6
|
-
|
|
7
|
-
const logger = new Logger('admin-auth');
|
|
8
|
-
|
|
9
|
-
export const needDevAdminSession = async (req: ServerRequest, res: ServerResponse) => {
|
|
10
|
-
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, true);
|
|
11
|
-
if (!devAdminSession) {
|
|
12
|
-
// return true to skip the rest of the middleware
|
|
13
|
-
const response = {
|
|
14
|
-
status: 'error',
|
|
15
|
-
message: langHelper.getLang('shared:permission_denied'),
|
|
16
|
-
};
|
|
17
|
-
ApiHelper.sendJson(req, res, response);
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
return false;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// dev admin, for development only
|
|
24
|
-
export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
25
|
-
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
26
|
-
if (!cryptoKey) {
|
|
27
|
-
const msg = langHelper.getLang('shared:name_not_set', {
|
|
28
|
-
name: 'ENV ' + DEV_ADMIN_CRYPTO_KEY_NAME,
|
|
29
|
-
});
|
|
30
|
-
logger.error(msg);
|
|
31
|
-
const response = {
|
|
32
|
-
status: 'error',
|
|
33
|
-
message: msg,
|
|
34
|
-
};
|
|
35
|
-
ApiHelper.sendJson(req, res, response);
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
if (!process.env['DEV_ADMIN_PASS']) {
|
|
39
|
-
const msg = langHelper.getLang('shared:name_not_set', {
|
|
40
|
-
name: 'ENV DEV_ADMIN_PASS',
|
|
41
|
-
});
|
|
42
|
-
logger.error(msg);
|
|
43
|
-
const response = {
|
|
44
|
-
status: 'error',
|
|
45
|
-
message: msg,
|
|
46
|
-
};
|
|
47
|
-
ApiHelper.sendJson(req, res, response);
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// TODO: secure and httpOnly cookies
|
|
52
|
-
const data = req.locals.json();
|
|
53
|
-
if (!data || Array.isArray(data) || !data.u || !data.p) {
|
|
54
|
-
// if session already exists, use session data login
|
|
55
|
-
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, false);
|
|
56
|
-
if (!devAdminSession) {
|
|
57
|
-
// check is app admin
|
|
58
|
-
const appAdminHookCheckLogin = adminApiHelper.getAppAdminHookCheckLogin();
|
|
59
|
-
if (appAdminHookCheckLogin) {
|
|
60
|
-
if (await appAdminHookCheckLogin(req, res, '', '')) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const response = {
|
|
66
|
-
status: 'error',
|
|
67
|
-
message: 'Please login to use this system.',
|
|
68
|
-
};
|
|
69
|
-
ApiHelper.sendJson(req, res, response);
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
// on set app cookie when login, not every time
|
|
73
|
-
// // if it's dev admin, then set app admin cookie as well
|
|
74
|
-
// let addLoginResponse = {};
|
|
75
|
-
// const appAdminHookSetCookie = adminApiHelper.getAppAdminHookSetCookie();
|
|
76
|
-
// if (appAdminHookSetCookie) {
|
|
77
|
-
// addLoginResponse = await appAdminHookSetCookie(req, res, devAdminSession.u);
|
|
78
|
-
// }
|
|
79
|
-
const response = {
|
|
80
|
-
// ...addLoginResponse,
|
|
81
|
-
status: 'ok',
|
|
82
|
-
message: langHelper.getLang('shared:login_success'),
|
|
83
|
-
devLogin: CryptoUtils.encrypt(JSON.stringify(devAdminSession), cryptoKey),
|
|
84
|
-
};
|
|
85
|
-
ApiHelper.sendJson(req, res, response);
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (data.u === process.env['DEV_ADMIN_USER'] && data.p === process.env['DEV_ADMIN_PASS']) {
|
|
90
|
-
logger.info('dev admin logged in');
|
|
91
|
-
const devSession: DevAdminSessionProps = {
|
|
92
|
-
u: data.u,
|
|
93
|
-
t: DEV_ADMIN_TYPE,
|
|
94
|
-
ip: req.socket.remoteAddress as string,
|
|
95
|
-
h: CryptoUtils.hash(data.u + ':' + data.p),
|
|
96
|
-
};
|
|
97
|
-
const token = JSON.stringify(devSession);
|
|
98
|
-
const tokenCookie = CryptoUtils.encrypt(token, cryptoKey);
|
|
99
|
-
|
|
100
|
-
// if it's dev admin, then set app admin cookie as well
|
|
101
|
-
let addLoginResponse = {};
|
|
102
|
-
const appAdminHookSetCookie = adminApiHelper.getAppAdminHookSetCookie();
|
|
103
|
-
if (appAdminHookSetCookie) {
|
|
104
|
-
addLoginResponse = await appAdminHookSetCookie(req, res, data.u);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const response = {
|
|
108
|
-
...addLoginResponse,
|
|
109
|
-
status: 'ok',
|
|
110
|
-
message: langHelper.getLang('shared:login_success'),
|
|
111
|
-
devLogin: tokenCookie,
|
|
112
|
-
};
|
|
113
|
-
// req.locals.setCookie('_token', tokenCookie, {
|
|
114
|
-
// expireDays: 360,
|
|
115
|
-
// path: '/',
|
|
116
|
-
// httpOnly: false,
|
|
117
|
-
// secure: true,
|
|
118
|
-
// sameSite: 'none',
|
|
119
|
-
// });
|
|
120
|
-
ApiHelper.sendJson(req, res, response);
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// check is app admin
|
|
125
|
-
const appAdminHookCheckLogin = adminApiHelper.getAppAdminHookCheckLogin();
|
|
126
|
-
if (appAdminHookCheckLogin) {
|
|
127
|
-
if (await appAdminHookCheckLogin(req, res, data.u as string, data.p as string)) {
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
logger.info(`dev admin login failed: ${((data.u as string) || '').substring(0, 30)}`);
|
|
133
|
-
const response = {
|
|
134
|
-
status: 'error',
|
|
135
|
-
message: langHelper.getLang('shared:login_failed'),
|
|
136
|
-
};
|
|
137
|
-
ApiHelper.sendJson(req, res, response);
|
|
138
|
-
return true;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
export const devAdminLogout = async (req: ServerRequest, res: ServerResponse) => {
|
|
142
|
-
req.locals.setCookie('_token_dev', '', {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import { Logger, ServerRequest, ApiHelper } from 'lupine.api';
|
|
3
|
+
import { CryptoUtils } from '../lib/utils/crypto';
|
|
4
|
+
import { adminApiHelper, DEV_ADMIN_CRYPTO_KEY_NAME, DEV_ADMIN_TYPE, DevAdminSessionProps } from './admin-api-helper';
|
|
5
|
+
import { langHelper } from '../lang';
|
|
6
|
+
|
|
7
|
+
const logger = new Logger('admin-auth');
|
|
8
|
+
|
|
9
|
+
export const needDevAdminSession = async (req: ServerRequest, res: ServerResponse) => {
|
|
10
|
+
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, true);
|
|
11
|
+
if (!devAdminSession) {
|
|
12
|
+
// return true to skip the rest of the middleware
|
|
13
|
+
const response = {
|
|
14
|
+
status: 'error',
|
|
15
|
+
message: langHelper.getLang('shared:permission_denied'),
|
|
16
|
+
};
|
|
17
|
+
ApiHelper.sendJson(req, res, response);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// dev admin, for development only
|
|
24
|
+
export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
25
|
+
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
26
|
+
if (!cryptoKey) {
|
|
27
|
+
const msg = langHelper.getLang('shared:name_not_set', {
|
|
28
|
+
name: 'ENV ' + DEV_ADMIN_CRYPTO_KEY_NAME,
|
|
29
|
+
});
|
|
30
|
+
logger.error(msg);
|
|
31
|
+
const response = {
|
|
32
|
+
status: 'error',
|
|
33
|
+
message: msg,
|
|
34
|
+
};
|
|
35
|
+
ApiHelper.sendJson(req, res, response);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (!process.env['DEV_ADMIN_PASS']) {
|
|
39
|
+
const msg = langHelper.getLang('shared:name_not_set', {
|
|
40
|
+
name: 'ENV DEV_ADMIN_PASS',
|
|
41
|
+
});
|
|
42
|
+
logger.error(msg);
|
|
43
|
+
const response = {
|
|
44
|
+
status: 'error',
|
|
45
|
+
message: msg,
|
|
46
|
+
};
|
|
47
|
+
ApiHelper.sendJson(req, res, response);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// TODO: secure and httpOnly cookies
|
|
52
|
+
const data = req.locals.json();
|
|
53
|
+
if (!data || Array.isArray(data) || !data.u || !data.p) {
|
|
54
|
+
// if session already exists, use session data login
|
|
55
|
+
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, false);
|
|
56
|
+
if (!devAdminSession) {
|
|
57
|
+
// check is app admin
|
|
58
|
+
const appAdminHookCheckLogin = adminApiHelper.getAppAdminHookCheckLogin();
|
|
59
|
+
if (appAdminHookCheckLogin) {
|
|
60
|
+
if (await appAdminHookCheckLogin(req, res, '', '')) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const response = {
|
|
66
|
+
status: 'error',
|
|
67
|
+
message: 'Please login to use this system.',
|
|
68
|
+
};
|
|
69
|
+
ApiHelper.sendJson(req, res, response);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
// on set app cookie when login, not every time
|
|
73
|
+
// // if it's dev admin, then set app admin cookie as well
|
|
74
|
+
// let addLoginResponse = {};
|
|
75
|
+
// const appAdminHookSetCookie = adminApiHelper.getAppAdminHookSetCookie();
|
|
76
|
+
// if (appAdminHookSetCookie) {
|
|
77
|
+
// addLoginResponse = await appAdminHookSetCookie(req, res, devAdminSession.u);
|
|
78
|
+
// }
|
|
79
|
+
const response = {
|
|
80
|
+
// ...addLoginResponse,
|
|
81
|
+
status: 'ok',
|
|
82
|
+
message: langHelper.getLang('shared:login_success'),
|
|
83
|
+
devLogin: CryptoUtils.encrypt(JSON.stringify(devAdminSession), cryptoKey),
|
|
84
|
+
};
|
|
85
|
+
ApiHelper.sendJson(req, res, response);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (data.u === process.env['DEV_ADMIN_USER'] && data.p === process.env['DEV_ADMIN_PASS']) {
|
|
90
|
+
logger.info('dev admin logged in');
|
|
91
|
+
const devSession: DevAdminSessionProps = {
|
|
92
|
+
u: data.u,
|
|
93
|
+
t: DEV_ADMIN_TYPE,
|
|
94
|
+
ip: req.socket.remoteAddress as string,
|
|
95
|
+
h: CryptoUtils.hash(data.u + ':' + data.p),
|
|
96
|
+
};
|
|
97
|
+
const token = JSON.stringify(devSession);
|
|
98
|
+
const tokenCookie = CryptoUtils.encrypt(token, cryptoKey);
|
|
99
|
+
|
|
100
|
+
// if it's dev admin, then set app admin cookie as well
|
|
101
|
+
let addLoginResponse = {};
|
|
102
|
+
const appAdminHookSetCookie = adminApiHelper.getAppAdminHookSetCookie();
|
|
103
|
+
if (appAdminHookSetCookie) {
|
|
104
|
+
addLoginResponse = await appAdminHookSetCookie(req, res, data.u);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const response = {
|
|
108
|
+
...addLoginResponse,
|
|
109
|
+
status: 'ok',
|
|
110
|
+
message: langHelper.getLang('shared:login_success'),
|
|
111
|
+
devLogin: tokenCookie,
|
|
112
|
+
};
|
|
113
|
+
// req.locals.setCookie('_token', tokenCookie, {
|
|
114
|
+
// expireDays: 360,
|
|
115
|
+
// path: '/',
|
|
116
|
+
// httpOnly: false,
|
|
117
|
+
// secure: true,
|
|
118
|
+
// sameSite: 'none',
|
|
119
|
+
// });
|
|
120
|
+
ApiHelper.sendJson(req, res, response);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// check is app admin
|
|
125
|
+
const appAdminHookCheckLogin = adminApiHelper.getAppAdminHookCheckLogin();
|
|
126
|
+
if (appAdminHookCheckLogin) {
|
|
127
|
+
if (await appAdminHookCheckLogin(req, res, data.u as string, data.p as string)) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
logger.info(`dev admin login failed: ${((data.u as string) || '').substring(0, 30)}`);
|
|
133
|
+
const response = {
|
|
134
|
+
status: 'error',
|
|
135
|
+
message: langHelper.getLang('shared:login_failed'),
|
|
136
|
+
};
|
|
137
|
+
ApiHelper.sendJson(req, res, response);
|
|
138
|
+
return true;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const devAdminLogout = async (req: ServerRequest, res: ServerResponse) => {
|
|
142
|
+
req.locals.setCookie('_token_dev', '', {
|
|
143
|
+
expireDays: 360,
|
|
144
|
+
path: '/',
|
|
145
|
+
httpOnly: false,
|
|
146
|
+
secure: true,
|
|
147
|
+
sameSite: 'none',
|
|
148
|
+
});
|
|
149
|
+
await adminApiHelper.getAppAdminHookLogout()?.(req, res);
|
|
150
|
+
ApiHelper.sendJson(req, res, { status: 'ok', message: langHelper.getLang('shared:process_completed') });
|
|
151
|
+
return true;
|
|
152
|
+
};
|
|
@@ -1,84 +1,94 @@
|
|
|
1
|
-
import { ServerResponse } from 'http';
|
|
2
|
-
import * as fs from 'fs/promises';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
protected
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import {
|
|
4
|
+
IApiBase,
|
|
5
|
+
Logger,
|
|
6
|
+
apiCache,
|
|
7
|
+
ServerRequest,
|
|
8
|
+
ApiRouter,
|
|
9
|
+
ApiHelper,
|
|
10
|
+
langHelper,
|
|
11
|
+
FsUtils,
|
|
12
|
+
appStorage,
|
|
13
|
+
} from 'lupine.api';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
|
|
16
|
+
export class AdminConfig implements IApiBase {
|
|
17
|
+
private logger = new Logger('config-api');
|
|
18
|
+
protected router = new ApiRouter();
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
this.mountDashboard();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public getRouter(): ApiRouter {
|
|
25
|
+
return this.router;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected mountDashboard() {
|
|
29
|
+
// called by FE
|
|
30
|
+
this.router.use('/load', this.load.bind(this));
|
|
31
|
+
this.router.use('/save', this.save.bind(this));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async load(req: ServerRequest, res: ServerResponse) {
|
|
35
|
+
const appData = apiCache.getAppData();
|
|
36
|
+
let cfgPath = path.join(appData.dataPath, 'config.json');
|
|
37
|
+
if (!(await FsUtils.pathExist(cfgPath))) {
|
|
38
|
+
cfgPath = path.join(appData.dataPath, 'resources', 'config_default.json');
|
|
39
|
+
}
|
|
40
|
+
if (!(await FsUtils.pathExist(cfgPath))) {
|
|
41
|
+
const response = {
|
|
42
|
+
status: 'error',
|
|
43
|
+
message: langHelper.getLang('shared:not_found_file', { fileName: 'config.json' }),
|
|
44
|
+
};
|
|
45
|
+
ApiHelper.sendJson(req, res, response);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let jsonCfg: any;
|
|
50
|
+
try {
|
|
51
|
+
jsonCfg = JSON.parse(await fs.readFile(cfgPath, 'utf8'));
|
|
52
|
+
} catch (e) {
|
|
53
|
+
const response = {
|
|
54
|
+
status: 'error',
|
|
55
|
+
message: langHelper.getLang('shared:not_found_file', { fileName: 'config.json' }),
|
|
56
|
+
};
|
|
57
|
+
ApiHelper.sendJson(req, res, response);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
const response = {
|
|
61
|
+
status: 'ok',
|
|
62
|
+
message: langHelper.getLang('shared:operation_success'),
|
|
63
|
+
result: jsonCfg,
|
|
64
|
+
};
|
|
65
|
+
ApiHelper.sendJson(req, res, response);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// called by clients
|
|
70
|
+
async save(req: ServerRequest, res: ServerResponse) {
|
|
71
|
+
const data = req.locals.json();
|
|
72
|
+
if (!data || Array.isArray(data) || !data.json) {
|
|
73
|
+
const response = {
|
|
74
|
+
status: 'error',
|
|
75
|
+
message: langHelper.getLang('shared:wrong_data'),
|
|
76
|
+
};
|
|
77
|
+
ApiHelper.sendJson(req, res, response);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const appData = apiCache.getAppData();
|
|
82
|
+
const cfgPath = path.join(appData.dataPath, 'config.json');
|
|
83
|
+
await fs.writeFile(cfgPath, JSON.stringify(data.json));
|
|
84
|
+
|
|
85
|
+
await appStorage.load(appData.appName, appData.dataPath);
|
|
86
|
+
|
|
87
|
+
const response = {
|
|
88
|
+
status: 'ok',
|
|
89
|
+
message: langHelper.getLang('shared:operation_success'),
|
|
90
|
+
};
|
|
91
|
+
ApiHelper.sendJson(req, res, response);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
import { ServerResponse } from 'http';
|
|
2
|
-
import { Logger } from 'lupine.api';
|
|
3
|
-
import { Db } from '../lib/db';
|
|
4
|
-
|
|
5
|
-
const logger = new Logger('admin-csv');
|
|
6
|
-
export const exportCSVData = async (db: Db, tableName: string, res: ServerResponse) => {
|
|
7
|
-
res.write(`@TABLE,${tableName}\r\n`);
|
|
8
|
-
|
|
9
|
-
const result = await db.selectObject(tableName);
|
|
10
|
-
if (result && result.length > 0) {
|
|
11
|
-
const fields = Object.keys(result[0]).join(',').toLowerCase();
|
|
12
|
-
res.write(`@FIELD,${fields}\r\n`);
|
|
13
|
-
result.forEach((row: any) => {
|
|
14
|
-
res.write(`${JSON.stringify(Object.values(row))}\r\n`);
|
|
15
|
-
});
|
|
16
|
-
} else {
|
|
17
|
-
res.write('#no data\r\n');
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const exportCSVHead = (fileName: string, res: ServerResponse) => {
|
|
22
|
-
res.writeHead(200, {
|
|
23
|
-
'Content-Type': 'text/csv',
|
|
24
|
-
// 'Expires': '0',
|
|
25
|
-
// 'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
26
|
-
'Content-Disposition': 'attachment; filename=' + fileName + '.ljcsv',
|
|
27
|
-
// 'Content-Description': 'File Transfer',
|
|
28
|
-
});
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const exportCSV = async (db: Db, tableName: string, res: ServerResponse) => {
|
|
32
|
-
if (tableName) {
|
|
33
|
-
exportCSVHead(tableName + '-' + new Date().toJSON().replace(/:/g, '-'), res);
|
|
34
|
-
await exportCSVData(db, tableName, res);
|
|
35
|
-
} else {
|
|
36
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
37
|
-
res.write('Need a table name.');
|
|
38
|
-
}
|
|
39
|
-
res.end();
|
|
40
|
-
return true;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const exportCSVTables = async (db: Db, tables: string[], res: ServerResponse) => {
|
|
44
|
-
exportCSVHead('all-tables-' + new Date().toJSON().replace(/:/g, '-'), res);
|
|
45
|
-
for (const i in tables) {
|
|
46
|
-
await exportCSVData(db, tables[i], res);
|
|
47
|
-
res.write('\r\n');
|
|
48
|
-
}
|
|
49
|
-
res.end();
|
|
50
|
-
return true;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const loadCSV = async (db: Db, lines: string[]) => {
|
|
54
|
-
let table = '';
|
|
55
|
-
let insSql = '';
|
|
56
|
-
const result: any = {};
|
|
57
|
-
let toFields: string[] = [];
|
|
58
|
-
let toFieldsIndex: number[] = [];
|
|
59
|
-
for (const i in lines) {
|
|
60
|
-
if (!lines[i] || lines[i].startsWith('#')) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (lines[i].startsWith('@TABLE,')) {
|
|
64
|
-
table = lines[i].substring(7);
|
|
65
|
-
result[table] = { succeeded: 0, failed: 0, errorMessage: [] };
|
|
66
|
-
|
|
67
|
-
const toTableInfo = await db.getTableInfo(table);
|
|
68
|
-
toFields = toTableInfo.map((item: any) => item.name);
|
|
69
|
-
} else if (lines[i].startsWith('@FIELD,')) {
|
|
70
|
-
const fromFields = lines[i]
|
|
71
|
-
.substring(7)
|
|
72
|
-
.split(',')
|
|
73
|
-
.filter((item: string) => toFields.includes(item));
|
|
74
|
-
toFieldsIndex = toFields.map((item: string) => fromFields.indexOf(item));
|
|
75
|
-
const values = Array(fromFields.length).fill('?').join(',');
|
|
76
|
-
insSql = `INSERT INTO ${table} (${fromFields.join(',')} ) VALUES (${values})`;
|
|
77
|
-
} else {
|
|
78
|
-
if (toFields.length === 0 || !insSql) {
|
|
79
|
-
throw new Error('Invalid CSV format (no @TABLE or @FIELD)');
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const row = JSON.parse(lines[i]);
|
|
83
|
-
const values = toFieldsIndex.map((index: number) => row[index]);
|
|
84
|
-
await db.execute(insSql, values);
|
|
85
|
-
result[table].succeeded++;
|
|
86
|
-
} catch (error: any) {
|
|
87
|
-
result[table].failed++;
|
|
88
|
-
result[table].errorMessage.push(error.message);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return result;
|
|
94
|
-
};
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import { Logger } from 'lupine.api';
|
|
3
|
+
import { Db } from '../lib/db';
|
|
4
|
+
|
|
5
|
+
const logger = new Logger('admin-csv');
|
|
6
|
+
export const exportCSVData = async (db: Db, tableName: string, res: ServerResponse) => {
|
|
7
|
+
res.write(`@TABLE,${tableName}\r\n`);
|
|
8
|
+
|
|
9
|
+
const result = await db.selectObject(tableName);
|
|
10
|
+
if (result && result.length > 0) {
|
|
11
|
+
const fields = Object.keys(result[0]).join(',').toLowerCase();
|
|
12
|
+
res.write(`@FIELD,${fields}\r\n`);
|
|
13
|
+
result.forEach((row: any) => {
|
|
14
|
+
res.write(`${JSON.stringify(Object.values(row))}\r\n`);
|
|
15
|
+
});
|
|
16
|
+
} else {
|
|
17
|
+
res.write('#no data\r\n');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const exportCSVHead = (fileName: string, res: ServerResponse) => {
|
|
22
|
+
res.writeHead(200, {
|
|
23
|
+
'Content-Type': 'text/csv',
|
|
24
|
+
// 'Expires': '0',
|
|
25
|
+
// 'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
26
|
+
'Content-Disposition': 'attachment; filename=' + fileName + '.ljcsv',
|
|
27
|
+
// 'Content-Description': 'File Transfer',
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const exportCSV = async (db: Db, tableName: string, res: ServerResponse) => {
|
|
32
|
+
if (tableName) {
|
|
33
|
+
exportCSVHead(tableName + '-' + new Date().toJSON().replace(/:/g, '-'), res);
|
|
34
|
+
await exportCSVData(db, tableName, res);
|
|
35
|
+
} else {
|
|
36
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
37
|
+
res.write('Need a table name.');
|
|
38
|
+
}
|
|
39
|
+
res.end();
|
|
40
|
+
return true;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const exportCSVTables = async (db: Db, tables: string[], res: ServerResponse) => {
|
|
44
|
+
exportCSVHead('all-tables-' + new Date().toJSON().replace(/:/g, '-'), res);
|
|
45
|
+
for (const i in tables) {
|
|
46
|
+
await exportCSVData(db, tables[i], res);
|
|
47
|
+
res.write('\r\n');
|
|
48
|
+
}
|
|
49
|
+
res.end();
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const loadCSV = async (db: Db, lines: string[]) => {
|
|
54
|
+
let table = '';
|
|
55
|
+
let insSql = '';
|
|
56
|
+
const result: any = {};
|
|
57
|
+
let toFields: string[] = [];
|
|
58
|
+
let toFieldsIndex: number[] = [];
|
|
59
|
+
for (const i in lines) {
|
|
60
|
+
if (!lines[i] || lines[i].startsWith('#')) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (lines[i].startsWith('@TABLE,')) {
|
|
64
|
+
table = lines[i].substring(7);
|
|
65
|
+
result[table] = { succeeded: 0, failed: 0, errorMessage: [] };
|
|
66
|
+
|
|
67
|
+
const toTableInfo = await db.getTableInfo(table);
|
|
68
|
+
toFields = toTableInfo.map((item: any) => item.name);
|
|
69
|
+
} else if (lines[i].startsWith('@FIELD,')) {
|
|
70
|
+
const fromFields = lines[i]
|
|
71
|
+
.substring(7)
|
|
72
|
+
.split(',')
|
|
73
|
+
.filter((item: string) => toFields.includes(item));
|
|
74
|
+
toFieldsIndex = toFields.map((item: string) => fromFields.indexOf(item));
|
|
75
|
+
const values = Array(fromFields.length).fill('?').join(',');
|
|
76
|
+
insSql = `INSERT INTO ${table} (${fromFields.join(',')} ) VALUES (${values})`;
|
|
77
|
+
} else {
|
|
78
|
+
if (toFields.length === 0 || !insSql) {
|
|
79
|
+
throw new Error('Invalid CSV format (no @TABLE or @FIELD)');
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const row = JSON.parse(lines[i]);
|
|
83
|
+
const values = toFieldsIndex.map((index: number) => row[index]);
|
|
84
|
+
await db.execute(insSql, values);
|
|
85
|
+
result[table].succeeded++;
|
|
86
|
+
} catch (error: any) {
|
|
87
|
+
result[table].failed++;
|
|
88
|
+
result[table].errorMessage.push(error.message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return result;
|
|
94
|
+
};
|