lupine.api 1.1.44 → 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.
@@ -1,5 +1,4 @@
1
1
  import {
2
- DomUtils,
3
2
  NotificationColor,
4
3
  NotificationMessage,
5
4
  CssProps,
@@ -8,8 +7,8 @@ import {
8
7
  mountInnerComponent,
9
8
  downloadLink,
10
9
  } from 'lupine.components';
11
- import { adminFrameProps } from './admin-frame-props';
12
10
  import { TableDataPage } from './admin-table-data';
11
+ import { adminFrameHelper } from './admin-frame-helper';
13
12
 
14
13
  const fetchTableList = async () => {
15
14
  const data = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/db/tables/list');
@@ -29,10 +28,10 @@ const fetchTableTruncateAll = async () => {
29
28
  };
30
29
 
31
30
  export const TableListPage = () => {
32
- const refUpdate = adminFrameProps.tabsHook;
31
+ const refUpdate = adminFrameHelper.getTabsHook();
33
32
 
34
33
  const openTablePanel = async (tableName: string) => {
35
- if (refUpdate?.getCount && refUpdate.getCount() > adminFrameProps.maxTabsCount) {
34
+ if (refUpdate?.getCount && refUpdate.getCount() > adminFrameHelper.getMaxTabsCount()) {
36
35
  alert('You are opening too many pages');
37
36
  return;
38
37
  }
@@ -1,4 +1,4 @@
1
- import { bindGlobalStyles, ComponentChildren, CssProps } from 'lupine.web';
1
+ import { bindGlobalStyle, ComponentChildren, CssProps } from 'lupine.web';
2
2
  import { dragData, findTopBlock } from './drag-data';
3
3
 
4
4
  export type BlockBoxProps = {
@@ -51,7 +51,7 @@ export const DesignBlockBox = (props: BlockBoxProps) => {
51
51
  height: '100%',
52
52
  },
53
53
  };
54
- bindGlobalStyles('design-block-box', css);
54
+ bindGlobalStyle('design-block-box', css);
55
55
 
56
56
  const findMsgSender = (ev: any) => {
57
57
  const msgSender = (window as any)._lj_design;
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
- // export * from './admin-users';
5
- export * from './admin-frame';
6
- export * from './admin-db';
7
+ export * from './admin-release';
8
+ export * from './admin-tokens';
9
+ export * from './admin-performance';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lupine.api",
3
- "version": "1.1.44",
3
+ "version": "1.1.46",
4
4
  "license": "MIT",
5
5
  "author": "uuware.com",
6
6
  "homepage": "https://github.com/uuware/lupine.js",
@@ -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 { adminHelper, DEV_ADMIN_CRYPTO_KEY_NAME, DEV_ADMIN_TYPE, DevAdminSessionProps } from './admin-helper';
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 adminHelper.getDevAdminFromCookie(req, res, true);
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:crypto_key_not_set', {
22
- cryptoKey: DEV_ADMIN_CRYPTO_KEY_NAME,
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 adminHelper.getDevAdminFromCookie(req, res, true);
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
- result: CryptoUtils.encrypt(JSON.stringify(devAdminSession), cryptoKey),
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
- result: CryptoUtils.encrypt(token, cryptoKey),
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 { adminHelper } from './admin-helper';
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 = adminHelper.getDevAdminFromCookie(req, res, true);
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
- adminHelper,
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 adminHelper.getDevAdminFromCookie(req, res, false);
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
- adminHelper,
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 && adminHelper.decryptJson(key.replace(/ /g, '+'));
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: adminHelper.encryptJson({ ind: chunkNumber + 1, cnt: totalChunks, t: new Date().getTime() }),
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 { adminHelper } from './admin-helper';
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 adminHelper.encryptJson(salt) as string;
67
+ return adminApiHelper.encryptJson(salt) as string;
68
68
  }
69
69
 
70
70
  async validateToken(token: string) {
@@ -1,2 +1,2 @@
1
1
  export * from './admin-api';
2
- export * from './admin-helper';
2
+ export * from './admin-api-helper';
@@ -22,6 +22,5 @@ export const apiLangEn: OneLangProps = {
22
22
  'shared:not_found_file': 'File {fileName} is not found.',
23
23
 
24
24
  'shared:wrong_hash': 'Wrong hash.',
25
- 'shared:crypto_key_not_set': 'Crypto key [{cryptoKey}] not set',
26
25
  },
27
26
  };
@@ -23,6 +23,5 @@ export const apiLangZhCn: OneLangProps = {
23
23
  'shared:not_found_file': '文件 {fileName} 未找到',
24
24
 
25
25
  'shared:wrong_hash': '错误的hash',
26
- 'shared:crypto_key_not_set': 'Crypto key [{cryptoKey}] 未设置',
27
26
  },
28
27
  };
@@ -1,9 +0,0 @@
1
- import { TabsHookProps } from 'lupine.components';
2
-
3
- const adminFrameProps = {
4
- maxWidthMobileMenu: '800px',
5
- maxTabsCount: 20,
6
- tabsHook: {} as TabsHookProps,
7
- };
8
-
9
- export { adminFrameProps };
@@ -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();