lupine.api 1.1.45 → 1.1.46
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/admin/admin-frame-helper.tsx +356 -0
- package/admin/admin-frame.tsx +24 -327
- package/admin/admin-index.tsx +6 -7
- package/admin/admin-login.tsx +39 -27
- package/admin/admin-menu-list.tsx +4 -4
- package/admin/admin-page-list.tsx +4 -4
- package/admin/admin-release.tsx +0 -7
- package/admin/admin-table-list.tsx +3 -4
- package/admin/index.ts +6 -3
- package/package.json +1 -1
- package/src/admin-api/admin-api-helper.ts +205 -0
- package/src/admin-api/admin-api.ts +5 -2
- package/src/admin-api/admin-auth.ts +61 -8
- package/src/admin-api/admin-config.ts +1 -12
- package/src/admin-api/admin-performance.ts +2 -2
- package/src/admin-api/admin-release.ts +2 -2
- package/src/admin-api/admin-resources.ts +3 -3
- package/src/admin-api/admin-token-helper.ts +2 -2
- package/src/admin-api/index.ts +1 -1
- package/src/lang/api-lang-en.ts +0 -1
- package/src/lang/api-lang-zh-cn.ts +0 -1
- package/admin/admin-frame-props.tsx +0 -9
- package/src/admin-api/admin-helper.ts +0 -111
package/admin/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from './admin-css';
|
|
2
|
+
export * from './admin-db';
|
|
3
|
+
export * from './admin-frame';
|
|
4
|
+
export * from './admin-frame-helper';
|
|
2
5
|
export * from './admin-index';
|
|
3
6
|
export * from './admin-login';
|
|
4
|
-
|
|
5
|
-
export * from './admin-
|
|
6
|
-
export * from './admin-
|
|
7
|
+
export * from './admin-release';
|
|
8
|
+
export * from './admin-tokens';
|
|
9
|
+
export * from './admin-performance';
|
package/package.json
CHANGED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import { ApiHelper } from '../api';
|
|
3
|
+
import { CryptoUtils, Logger } from '../lib';
|
|
4
|
+
import { ServerRequest } from '../models';
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
dev-admin uses different authentication method from frontend.
|
|
8
|
+
dev-admin only provides fixed username and password authentication, no user maintenance.
|
|
9
|
+
saved cookie name: _token_dev
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// DEFAULT_ADMIN_PASS is DEFAULT_ADMIN_NAME + ':' + login password hash.
|
|
13
|
+
// Use below command to generate hash:
|
|
14
|
+
// node -e "console.log(require('crypto').createHash('md5').update('admin:F4AZ5O@2fPUjw%f$LmhZpJTQ^DoXnWPkH#hqE', 'utf8').digest('hex'))"
|
|
15
|
+
export type DevAdminSessionProps = {
|
|
16
|
+
u: string; // username
|
|
17
|
+
t: string; // type: admin, user
|
|
18
|
+
ip: string;
|
|
19
|
+
h: string; // md5 of name+pass
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
dev admin has more permissions than app admin, and the dashboard also supports for app admin only users.
|
|
24
|
+
app admin is supposed to manage the application, not the site, and may have different fields in cookie.
|
|
25
|
+
This is a sample how to set login process for app admin.
|
|
26
|
+
|
|
27
|
+
export const appAdminHookSetCookie: AppAdminHookSetCookieProps = async (
|
|
28
|
+
req: ServerRequest,
|
|
29
|
+
res: ServerResponse,
|
|
30
|
+
username: string
|
|
31
|
+
) => {
|
|
32
|
+
const cryptoKey = process.env['CRYPTO_KEY'];
|
|
33
|
+
const u = process.env['ADMIN_USER'];
|
|
34
|
+
const p = process.env['ADMIN_PASS'];
|
|
35
|
+
if (!cryptoKey || !u || !p) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const specialToken = CryptoUtils.hash((u + ':' + p) as string);
|
|
40
|
+
const loginJson: LoginJsonProps = {
|
|
41
|
+
ip: '',
|
|
42
|
+
id: 0,
|
|
43
|
+
u: u,
|
|
44
|
+
t: 'admin',
|
|
45
|
+
h: specialToken,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const token = JSON.stringify(loginJson);
|
|
49
|
+
const tokenCookie = CryptoUtils.encrypt(token, cryptoKey);
|
|
50
|
+
const response = {
|
|
51
|
+
status: 'ok',
|
|
52
|
+
message: langHelper.getLang('shared:login_success'),
|
|
53
|
+
result: tokenCookie,
|
|
54
|
+
user: {
|
|
55
|
+
u: loginJson.u,
|
|
56
|
+
t: loginJson.t,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// sameSite: 'none' needs secure=true
|
|
61
|
+
req.locals.setCookie('_token', tokenCookie, {
|
|
62
|
+
expireDays: 360,
|
|
63
|
+
path: '/',
|
|
64
|
+
httpOnly: false,
|
|
65
|
+
secure: true,
|
|
66
|
+
sameSite: 'none',
|
|
67
|
+
});
|
|
68
|
+
return response;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const appAdminHookCheckLogin: AppAdminHookCheckLoginProps = async (
|
|
72
|
+
req: ServerRequest,
|
|
73
|
+
res: ServerResponse,
|
|
74
|
+
username: string,
|
|
75
|
+
password: string
|
|
76
|
+
) => {
|
|
77
|
+
if (process.env['ADMIN_PASS'] && username === process.env['ADMIN_USER'] && password === process.env['ADMIN_PASS']) {
|
|
78
|
+
const appAdminResponse = await appAdminHookSetCookie(req, res, username);
|
|
79
|
+
ApiHelper.sendJson(req, res, appAdminResponse);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
adminHelper.setAppAdminHookSetCookie(appAdminHookSetCookie);
|
|
86
|
+
adminHelper.setAppAdminHookCheckLogin(appAdminHookCheckLogin);
|
|
87
|
+
*/
|
|
88
|
+
export type AppAdminHookSetCookieProps = (req: ServerRequest, res: ServerResponse, username: string) => Promise<any>;
|
|
89
|
+
export type AppAdminHookCheckLoginProps = (req: ServerRequest, res: ServerResponse, username: string, password: string) => Promise<boolean>;
|
|
90
|
+
export type AppAdminHookLogoutProps = (req: ServerRequest, res: ServerResponse) => Promise<void>;
|
|
91
|
+
|
|
92
|
+
export const DEV_ADMIN_TYPE = 'dev-admin';
|
|
93
|
+
export const DEV_ADMIN_CRYPTO_KEY_NAME = 'DEV_CRYPTO_KEY';
|
|
94
|
+
export const DEV_ADMIN_SESSION_NAME = '_token_dev';
|
|
95
|
+
export class AdminApiHelper {
|
|
96
|
+
private static instance: AdminApiHelper;
|
|
97
|
+
private logger = new Logger('admin-api');
|
|
98
|
+
|
|
99
|
+
private constructor() {}
|
|
100
|
+
|
|
101
|
+
public static getInstance(): AdminApiHelper {
|
|
102
|
+
if (!AdminApiHelper.instance) {
|
|
103
|
+
AdminApiHelper.instance = new AdminApiHelper();
|
|
104
|
+
}
|
|
105
|
+
return AdminApiHelper.instance;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private AppAdminHookSetCookie?: AppAdminHookSetCookieProps;
|
|
109
|
+
setAppAdminHookSetCookie(hook: AppAdminHookSetCookieProps) {
|
|
110
|
+
this.AppAdminHookSetCookie = hook;
|
|
111
|
+
}
|
|
112
|
+
getAppAdminHookSetCookie() {
|
|
113
|
+
return this.AppAdminHookSetCookie;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private AppAdminHookCheckLogin?: AppAdminHookCheckLoginProps;
|
|
117
|
+
setAppAdminHookCheckLogin(hook: AppAdminHookCheckLoginProps) {
|
|
118
|
+
this.AppAdminHookCheckLogin = hook;
|
|
119
|
+
}
|
|
120
|
+
getAppAdminHookCheckLogin() {
|
|
121
|
+
return this.AppAdminHookCheckLogin;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private AppAdminHookLogout?: AppAdminHookLogoutProps;
|
|
125
|
+
setAppAdminHookLogout(hook: AppAdminHookLogoutProps) {
|
|
126
|
+
this.AppAdminHookLogout = hook;
|
|
127
|
+
}
|
|
128
|
+
getAppAdminHookLogout() {
|
|
129
|
+
return this.AppAdminHookLogout;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
decryptJson(text: string) {
|
|
133
|
+
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
134
|
+
if (cryptoKey && text) {
|
|
135
|
+
try {
|
|
136
|
+
const deCrypto = CryptoUtils.decrypt(text, cryptoKey);
|
|
137
|
+
const json = JSON.parse(deCrypto);
|
|
138
|
+
return json;
|
|
139
|
+
} catch (error: any) {
|
|
140
|
+
this.logger.error(error.message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
encryptJson(jsonOrText: string | object) {
|
|
147
|
+
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
148
|
+
if (cryptoKey && jsonOrText) {
|
|
149
|
+
try {
|
|
150
|
+
const text = typeof jsonOrText === 'string' ? jsonOrText : JSON.stringify(jsonOrText);
|
|
151
|
+
const encryptText = CryptoUtils.encrypt(text, cryptoKey);
|
|
152
|
+
return encryptText;
|
|
153
|
+
} catch (error: any) {
|
|
154
|
+
this.logger.error(error.message);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async getDevAdminFromCookie(
|
|
161
|
+
req: ServerRequest,
|
|
162
|
+
res: ServerResponse,
|
|
163
|
+
sendResponseWhenError = true
|
|
164
|
+
): Promise<DevAdminSessionProps | false> {
|
|
165
|
+
try {
|
|
166
|
+
const cookies = req.locals.cookies();
|
|
167
|
+
const token = cookies.get(DEV_ADMIN_SESSION_NAME, '');
|
|
168
|
+
if (token) {
|
|
169
|
+
const json = this.decryptJson(token) as DevAdminSessionProps;
|
|
170
|
+
if (!json || json.t !== DEV_ADMIN_TYPE) {
|
|
171
|
+
if (sendResponseWhenError) {
|
|
172
|
+
const response = {
|
|
173
|
+
status: 'error',
|
|
174
|
+
message: 'Wrong session data, contact site admin please.',
|
|
175
|
+
};
|
|
176
|
+
ApiHelper.sendJson(req, res, response);
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// if it's special admin
|
|
182
|
+
if (json.h && json.u === process.env['DEV_ADMIN_USER']) {
|
|
183
|
+
const hash = CryptoUtils.hash(process.env['DEV_ADMIN_USER'] + ':' + process.env['DEV_ADMIN_PASS']);
|
|
184
|
+
if (json.h === hash) {
|
|
185
|
+
return json;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
} catch (error: any) {
|
|
191
|
+
this.logger.error(error.message);
|
|
192
|
+
}
|
|
193
|
+
if (sendResponseWhenError) {
|
|
194
|
+
const response = {
|
|
195
|
+
status: 'error',
|
|
196
|
+
message: 'Please login to use this system.',
|
|
197
|
+
};
|
|
198
|
+
ApiHelper.sendJson(req, res, response);
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// add comment for tree shaking
|
|
205
|
+
export const adminApiHelper = /* @__PURE__ */ AdminApiHelper.getInstance();
|
|
@@ -2,7 +2,7 @@ import { ServerResponse } from 'http';
|
|
|
2
2
|
// import { AdminUser } from './admin-user';
|
|
3
3
|
import { AdminDb } from './admin-db';
|
|
4
4
|
import { AdminMenu } from './admin-menu';
|
|
5
|
-
import { devAdminAuth, needDevAdminSession } from './admin-auth';
|
|
5
|
+
import { devAdminAuth, devAdminLogout, needDevAdminSession } from './admin-auth';
|
|
6
6
|
import { AdminPerformance } from './admin-performance';
|
|
7
7
|
import { AdminRelease } from './admin-release';
|
|
8
8
|
import { AdminResources } from './admin-resources';
|
|
@@ -10,7 +10,7 @@ import { AdminTokens } from './admin-tokens';
|
|
|
10
10
|
import { AdminConfig } from './admin-config';
|
|
11
11
|
import { Logger } from '../lib';
|
|
12
12
|
import { IApiBase, ServerRequest } from '../models';
|
|
13
|
-
import {ApiRouter } from '../api';
|
|
13
|
+
import { ApiRouter } from '../api';
|
|
14
14
|
|
|
15
15
|
const logger = new Logger('admin-api');
|
|
16
16
|
|
|
@@ -55,5 +55,8 @@ export class AdminApi implements IApiBase {
|
|
|
55
55
|
this.router.use('/auth', async (req: ServerRequest, res: ServerResponse) => {
|
|
56
56
|
return devAdminAuth(req, res);
|
|
57
57
|
});
|
|
58
|
+
this.router.use('/logout', async (req: ServerRequest, res: ServerResponse) => {
|
|
59
|
+
return devAdminLogout(req, res);
|
|
60
|
+
});
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ServerResponse } from 'http';
|
|
2
2
|
import { Logger, ServerRequest, ApiHelper } from 'lupine.api';
|
|
3
3
|
import { CryptoUtils } from '../lib/utils/crypto';
|
|
4
|
-
import {
|
|
4
|
+
import { adminApiHelper, DEV_ADMIN_CRYPTO_KEY_NAME, DEV_ADMIN_TYPE, DevAdminSessionProps } from './admin-api-helper';
|
|
5
5
|
import { langHelper } from '../lang';
|
|
6
6
|
|
|
7
7
|
const logger = new Logger('admin-auth');
|
|
8
|
+
|
|
8
9
|
export const needDevAdminSession = async (req: ServerRequest, res: ServerResponse) => {
|
|
9
|
-
const devAdminSession = await
|
|
10
|
+
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, true);
|
|
10
11
|
if (!devAdminSession) {
|
|
11
12
|
// return true to skip the rest of the middleware
|
|
12
13
|
return true;
|
|
@@ -18,8 +19,8 @@ export const needDevAdminSession = async (req: ServerRequest, res: ServerRespons
|
|
|
18
19
|
export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
19
20
|
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
20
21
|
if (!cryptoKey) {
|
|
21
|
-
const msg = langHelper.getLang('shared:
|
|
22
|
-
|
|
22
|
+
const msg = langHelper.getLang('shared:name_not_set', {
|
|
23
|
+
name: 'ENV ' + DEV_ADMIN_CRYPTO_KEY_NAME,
|
|
23
24
|
});
|
|
24
25
|
logger.error(msg);
|
|
25
26
|
const response = {
|
|
@@ -31,7 +32,7 @@ export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
|
31
32
|
}
|
|
32
33
|
if (!process.env['DEV_ADMIN_PASS']) {
|
|
33
34
|
const msg = langHelper.getLang('shared:name_not_set', {
|
|
34
|
-
name: 'DEV_ADMIN_PASS',
|
|
35
|
+
name: 'ENV DEV_ADMIN_PASS',
|
|
35
36
|
});
|
|
36
37
|
logger.error(msg);
|
|
37
38
|
const response = {
|
|
@@ -46,14 +47,34 @@ export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
|
46
47
|
const data = req.locals.json();
|
|
47
48
|
if (!data || Array.isArray(data) || !data.u || !data.p) {
|
|
48
49
|
// if session already exists, use session data login
|
|
49
|
-
const devAdminSession = await
|
|
50
|
+
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, false);
|
|
50
51
|
if (!devAdminSession) {
|
|
52
|
+
// check is app admin
|
|
53
|
+
const appAdminHookCheckLogin = adminApiHelper.getAppAdminHookCheckLogin();
|
|
54
|
+
if (appAdminHookCheckLogin) {
|
|
55
|
+
if (await appAdminHookCheckLogin(req, res, '', '')) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const response = {
|
|
61
|
+
status: 'error',
|
|
62
|
+
message: 'Please login to use this system.',
|
|
63
|
+
};
|
|
64
|
+
ApiHelper.sendJson(req, res, response);
|
|
51
65
|
return true;
|
|
52
66
|
}
|
|
67
|
+
// if it's dev admin, then set app admin cookie as well
|
|
68
|
+
let addLoginResponse = {};
|
|
69
|
+
const appAdminHookSetCookie = adminApiHelper.getAppAdminHookSetCookie();
|
|
70
|
+
if (appAdminHookSetCookie) {
|
|
71
|
+
addLoginResponse = await appAdminHookSetCookie(req, res, devAdminSession.u);
|
|
72
|
+
}
|
|
53
73
|
const response = {
|
|
74
|
+
...addLoginResponse,
|
|
54
75
|
status: 'ok',
|
|
55
76
|
message: langHelper.getLang('shared:login_success'),
|
|
56
|
-
|
|
77
|
+
devLogin: CryptoUtils.encrypt(JSON.stringify(devAdminSession), cryptoKey),
|
|
57
78
|
};
|
|
58
79
|
ApiHelper.sendJson(req, res, response);
|
|
59
80
|
return true;
|
|
@@ -68,15 +89,40 @@ export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
|
68
89
|
h: CryptoUtils.hash(data.u + ':' + data.p),
|
|
69
90
|
};
|
|
70
91
|
const token = JSON.stringify(devSession);
|
|
92
|
+
const tokenCookie = CryptoUtils.encrypt(token, cryptoKey);
|
|
93
|
+
|
|
94
|
+
// if it's dev admin, then set app admin cookie as well
|
|
95
|
+
let addLoginResponse = {};
|
|
96
|
+
const appAdminHookSetCookie = adminApiHelper.getAppAdminHookSetCookie();
|
|
97
|
+
if (appAdminHookSetCookie) {
|
|
98
|
+
addLoginResponse = await appAdminHookSetCookie(req, res, data.u);
|
|
99
|
+
}
|
|
100
|
+
|
|
71
101
|
const response = {
|
|
102
|
+
...addLoginResponse,
|
|
72
103
|
status: 'ok',
|
|
73
104
|
message: langHelper.getLang('shared:login_success'),
|
|
74
|
-
|
|
105
|
+
devLogin: tokenCookie,
|
|
75
106
|
};
|
|
107
|
+
req.locals.setCookie('_token', tokenCookie, {
|
|
108
|
+
expireDays: 360,
|
|
109
|
+
path: '/',
|
|
110
|
+
httpOnly: false,
|
|
111
|
+
secure: true,
|
|
112
|
+
sameSite: 'none',
|
|
113
|
+
});
|
|
76
114
|
ApiHelper.sendJson(req, res, response);
|
|
77
115
|
return true;
|
|
78
116
|
}
|
|
79
117
|
|
|
118
|
+
// check is app admin
|
|
119
|
+
const appAdminHookCheckLogin = adminApiHelper.getAppAdminHookCheckLogin();
|
|
120
|
+
if (appAdminHookCheckLogin) {
|
|
121
|
+
if (await appAdminHookCheckLogin(req, res, data.u as string, data.p as string)) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
80
126
|
logger.info(`dev admin login failed: ${((data.u as string) || '').substring(0, 30)}`);
|
|
81
127
|
const response = {
|
|
82
128
|
status: 'error',
|
|
@@ -85,3 +131,10 @@ export const devAdminAuth = async (req: ServerRequest, res: ServerResponse) => {
|
|
|
85
131
|
ApiHelper.sendJson(req, res, response);
|
|
86
132
|
return true;
|
|
87
133
|
};
|
|
134
|
+
|
|
135
|
+
export const devAdminLogout = async (req: ServerRequest, res: ServerResponse) => {
|
|
136
|
+
req.locals.setCookie('_token_dev', '', { expireDays: 360, path: '/', httpOnly: false, secure: true, sameSite: 'none' });
|
|
137
|
+
await adminApiHelper.getAppAdminHookLogout()?.(req, res);
|
|
138
|
+
ApiHelper.sendJson(req, res, { status: 'ok', message: langHelper.getLang('shared:process_completed') });
|
|
139
|
+
return true;
|
|
140
|
+
};
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
import { ServerResponse } from 'http';
|
|
2
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
|
-
adminHelper,
|
|
13
|
-
} from 'lupine.api';
|
|
3
|
+
import { IApiBase, Logger, apiCache, ServerRequest, ApiRouter, ApiHelper, langHelper, FsUtils } from 'lupine.api';
|
|
14
4
|
import path from 'path';
|
|
15
|
-
import { needDevAdminSession } from './admin-auth';
|
|
16
5
|
|
|
17
6
|
export class AdminConfig implements IApiBase {
|
|
18
7
|
private logger = new Logger('config-api');
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
AppCacheGlobal,
|
|
17
17
|
getAppCache,
|
|
18
18
|
} from 'lupine.api';
|
|
19
|
-
import {
|
|
19
|
+
import { adminApiHelper } from './admin-api-helper';
|
|
20
20
|
import { needDevAdminSession } from './admin-auth';
|
|
21
21
|
|
|
22
22
|
// #https://github.com/sebhildebrandt/systeminformation
|
|
@@ -38,7 +38,7 @@ export class AdminPerformance implements IApiBase {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
async performanceData(req: ServerRequest, res: ServerResponse) {
|
|
41
|
-
const json =
|
|
41
|
+
const json = adminApiHelper.getDevAdminFromCookie(req, res, true);
|
|
42
42
|
if (!json) {
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
ApiHelper,
|
|
9
9
|
langHelper,
|
|
10
10
|
FsUtils,
|
|
11
|
-
|
|
11
|
+
adminApiHelper,
|
|
12
12
|
processRefreshCache,
|
|
13
13
|
} from 'lupine.api';
|
|
14
14
|
import path from 'path';
|
|
@@ -86,7 +86,7 @@ export class AdminRelease implements IApiBase {
|
|
|
86
86
|
|
|
87
87
|
async refreshCache(req: ServerRequest, res: ServerResponse) {
|
|
88
88
|
// check whether it's from online admin
|
|
89
|
-
const json = await
|
|
89
|
+
const json = await adminApiHelper.getDevAdminFromCookie(req, res, false);
|
|
90
90
|
const jsonData = req.locals.json();
|
|
91
91
|
if (json && jsonData && !Array.isArray(jsonData) && jsonData.isLocal) {
|
|
92
92
|
await processRefreshCache(req);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
ApiHelper,
|
|
10
10
|
langHelper,
|
|
11
11
|
FsUtils,
|
|
12
|
-
|
|
12
|
+
adminApiHelper,
|
|
13
13
|
} from 'lupine.api';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
|
|
@@ -64,7 +64,7 @@ export class AdminResources implements IApiBase {
|
|
|
64
64
|
const chunkNumberStr = req.locals.query.get('chunkNumber') as string;
|
|
65
65
|
const chunkNumber = parseInt(chunkNumberStr);
|
|
66
66
|
const totalChunks = parseInt(req.locals.query.get('totalChunks') as string);
|
|
67
|
-
const decryptedKey = key &&
|
|
67
|
+
const decryptedKey = key && adminApiHelper.decryptJson(key.replace(/ /g, '+'));
|
|
68
68
|
const keyNG =
|
|
69
69
|
!chunkNumberStr ||
|
|
70
70
|
!totalChunks ||
|
|
@@ -100,7 +100,7 @@ export class AdminResources implements IApiBase {
|
|
|
100
100
|
chunkNumber,
|
|
101
101
|
totalChunks,
|
|
102
102
|
message: langHelper.getLang('shared:file_part_updated'),
|
|
103
|
-
key:
|
|
103
|
+
key: adminApiHelper.encryptJson({ ind: chunkNumber + 1, cnt: totalChunks, t: new Date().getTime() }),
|
|
104
104
|
};
|
|
105
105
|
ApiHelper.sendJson(req, res, response);
|
|
106
106
|
return true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { apiStorage } from '../api';
|
|
2
2
|
import { CryptoUtils, Logger } from '../lib';
|
|
3
|
-
import {
|
|
3
|
+
import { adminApiHelper } from './admin-api-helper';
|
|
4
4
|
|
|
5
5
|
export type TokenProps = {
|
|
6
6
|
token: string;
|
|
@@ -64,7 +64,7 @@ export class AdminTokenHelper {
|
|
|
64
64
|
|
|
65
65
|
generate() {
|
|
66
66
|
const salt = 'Lupine:' + CryptoUtils.uuid() + ':' + new Date().getTime().toString();
|
|
67
|
-
return
|
|
67
|
+
return adminApiHelper.encryptJson(salt) as string;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
async validateToken(token: string) {
|
package/src/admin-api/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './admin-api';
|
|
2
|
-
export * from './admin-helper';
|
|
2
|
+
export * from './admin-api-helper';
|
package/src/lang/api-lang-en.ts
CHANGED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { ServerResponse } from 'http';
|
|
2
|
-
import { ApiHelper } from '../api';
|
|
3
|
-
import { CryptoUtils, Logger } from '../lib';
|
|
4
|
-
import { ServerRequest } from '../models';
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
dev-admin uses different authentication method from frontend.
|
|
8
|
-
dev-admin only provides fixed username and password authentication, no user maintenance.
|
|
9
|
-
saved cookie name: _token_dev
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// DEFAULT_ADMIN_PASS is DEFAULT_ADMIN_NAME + ':' + login password hash.
|
|
13
|
-
// Use below command to generate hash:
|
|
14
|
-
// node -e "console.log(require('crypto').createHash('md5').update('admin:F4AZ5O@2fPUjw%f$LmhZpJTQ^DoXnWPkH#hqE', 'utf8').digest('hex'))"
|
|
15
|
-
export type DevAdminSessionProps = {
|
|
16
|
-
u: string; // username
|
|
17
|
-
t: string; // type: admin, user
|
|
18
|
-
ip: string;
|
|
19
|
-
h: string; // md5 of name+pass
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const DEV_ADMIN_TYPE = 'dev-admin';
|
|
23
|
-
export const DEV_ADMIN_CRYPTO_KEY_NAME = 'DEV_CRYPTO_KEY';
|
|
24
|
-
export const DEV_ADMIN_SESSION_NAME = '_token_dev';
|
|
25
|
-
export class AdminHelper {
|
|
26
|
-
private static instance: AdminHelper;
|
|
27
|
-
private logger = new Logger('admin-api');
|
|
28
|
-
|
|
29
|
-
private constructor() {}
|
|
30
|
-
|
|
31
|
-
public static getInstance(): AdminHelper {
|
|
32
|
-
if (!AdminHelper.instance) {
|
|
33
|
-
AdminHelper.instance = new AdminHelper();
|
|
34
|
-
}
|
|
35
|
-
return AdminHelper.instance;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
decryptJson(text: string) {
|
|
39
|
-
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
40
|
-
if (cryptoKey && text) {
|
|
41
|
-
try {
|
|
42
|
-
const deCrypto = CryptoUtils.decrypt(text, cryptoKey);
|
|
43
|
-
const json = JSON.parse(deCrypto);
|
|
44
|
-
return json;
|
|
45
|
-
} catch (error: any) {
|
|
46
|
-
this.logger.error(error.message);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
encryptJson(jsonOrText: string | object) {
|
|
53
|
-
const cryptoKey = process.env[DEV_ADMIN_CRYPTO_KEY_NAME];
|
|
54
|
-
if (cryptoKey && jsonOrText) {
|
|
55
|
-
try {
|
|
56
|
-
const text = typeof jsonOrText === 'string' ? jsonOrText : JSON.stringify(jsonOrText);
|
|
57
|
-
const encryptText = CryptoUtils.encrypt(text, cryptoKey);
|
|
58
|
-
return encryptText;
|
|
59
|
-
} catch (error: any) {
|
|
60
|
-
this.logger.error(error.message);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async getDevAdminFromCookie(
|
|
67
|
-
req: ServerRequest,
|
|
68
|
-
res: ServerResponse,
|
|
69
|
-
sendResponseWhenError = true
|
|
70
|
-
): Promise<DevAdminSessionProps | false> {
|
|
71
|
-
try {
|
|
72
|
-
const cookies = req.locals.cookies();
|
|
73
|
-
const token = cookies.get(DEV_ADMIN_SESSION_NAME, '');
|
|
74
|
-
if (token) {
|
|
75
|
-
const json = this.decryptJson(token) as DevAdminSessionProps;
|
|
76
|
-
if (!json || json.t !== DEV_ADMIN_TYPE) {
|
|
77
|
-
if (sendResponseWhenError) {
|
|
78
|
-
const response = {
|
|
79
|
-
status: 'error',
|
|
80
|
-
message: 'Wrong session data, contact site admin please.',
|
|
81
|
-
};
|
|
82
|
-
ApiHelper.sendJson(req, res, response);
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// if it's special admin
|
|
88
|
-
if (json.h && json.u === process.env['DEV_ADMIN_USER']) {
|
|
89
|
-
const hash = CryptoUtils.hash(process.env['DEV_ADMIN_USER'] + ':' + process.env['DEV_ADMIN_PASS']);
|
|
90
|
-
if (json.h === hash) {
|
|
91
|
-
return json;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
} catch (error: any) {
|
|
97
|
-
this.logger.error(error.message);
|
|
98
|
-
}
|
|
99
|
-
if (sendResponseWhenError) {
|
|
100
|
-
const response = {
|
|
101
|
-
status: 'error',
|
|
102
|
-
message: 'Please login to use this system.',
|
|
103
|
-
};
|
|
104
|
-
ApiHelper.sendJson(req, res, response);
|
|
105
|
-
}
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// add comment for tree shaking
|
|
111
|
-
export const adminHelper = /* @__PURE__ */ AdminHelper.getInstance();
|