lupine.api 1.1.50 → 1.1.51
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/dev/cp-index-html.js +5 -5
- package/package.json +1 -1
- package/src/admin-api/admin-api.ts +3 -0
- package/src/admin-api/admin-auth.ts +5 -0
- package/src/admin-api/admin-release.ts +1 -1
- package/src/admin-api/web-config-api.ts +19 -0
- package/src/api/server-render.ts +7 -8
- package/src/app/app-shared-storage.ts +14 -6
- package/src/app/cleanup-exit.ts +1 -1
- package/src/lib/db/db.ts +24 -3
- package/src/models/app-shared-storage-props.ts +1 -1
package/dev/cp-index-html.js
CHANGED
|
@@ -37,7 +37,7 @@ exports.cpIndexHtml = async (htmlFile, outputFile, appName, isMobile, defaultThe
|
|
|
37
37
|
// if isMobile=true, then the last number is 1, or if isMobile=false, the last is 2
|
|
38
38
|
const chgTime = Math.trunc(f1.mtime.getTime() / 10) * 10 + (isMobile ? 1 : 2);
|
|
39
39
|
|
|
40
|
-
// when it's isMobile, need to update env and configs
|
|
40
|
+
// when it's isMobile, need to update env and configs => no configs as mobile app fetches it from api
|
|
41
41
|
if (!f2 || f2.mtime.getTime() !== chgTime || isMobile) {
|
|
42
42
|
const inHtml = await fs.readFile(htmlFile, 'utf-8');
|
|
43
43
|
let outStr = inHtml.replace(/{hash}/gi, new Date().getTime().toString(36));
|
|
@@ -48,7 +48,7 @@ exports.cpIndexHtml = async (htmlFile, outputFile, appName, isMobile, defaultThe
|
|
|
48
48
|
const metaIndexStart = inHtml.indexOf(metaTextStart);
|
|
49
49
|
const metaIndexEnd = inHtml.indexOf(metaTextEnd);
|
|
50
50
|
|
|
51
|
-
const webConfig = await readWebConfig(outdirData);
|
|
51
|
+
// const webConfig = await readWebConfig(outdirData);
|
|
52
52
|
const webEnvData = webEnv.getWebEnv(appName);
|
|
53
53
|
|
|
54
54
|
outStr =
|
|
@@ -56,9 +56,9 @@ exports.cpIndexHtml = async (htmlFile, outputFile, appName, isMobile, defaultThe
|
|
|
56
56
|
'<script id="web-env" type="application/json">' +
|
|
57
57
|
JSON.stringify(webEnvData) +
|
|
58
58
|
'</script>\r\n' +
|
|
59
|
-
'<script id="web-setting" type="application/json">' +
|
|
60
|
-
JSON.stringify(webConfig) +
|
|
61
|
-
'</script>' +
|
|
59
|
+
// '<script id="web-setting" type="application/json">' +
|
|
60
|
+
// JSON.stringify(webConfig) +
|
|
61
|
+
// '</script>' +
|
|
62
62
|
outStr.substring(metaIndexEnd + metaTextEnd.length);
|
|
63
63
|
// outStr = webEnv.replaceWebEnv(inHtml, appName, true);
|
|
64
64
|
}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import { AdminConfig } from './admin-config';
|
|
|
11
11
|
import { Logger } from '../lib';
|
|
12
12
|
import { IApiBase, ServerRequest } from '../models';
|
|
13
13
|
import { ApiRouter } from '../api';
|
|
14
|
+
import { readWebConfig } from './web-config-api';
|
|
14
15
|
|
|
15
16
|
const logger = new Logger('admin-api');
|
|
16
17
|
|
|
@@ -27,6 +28,8 @@ export class AdminApi implements IApiBase {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
protected mountDashboard() {
|
|
31
|
+
this.router.use('/web-config', readWebConfig);
|
|
32
|
+
|
|
30
33
|
const adminDb = new AdminDb();
|
|
31
34
|
this.router.use('/db', needDevAdminSession, adminDb.getRouter());
|
|
32
35
|
|
|
@@ -10,6 +10,11 @@ export const needDevAdminSession = async (req: ServerRequest, res: ServerRespons
|
|
|
10
10
|
const devAdminSession = await adminApiHelper.getDevAdminFromCookie(req, res, true);
|
|
11
11
|
if (!devAdminSession) {
|
|
12
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);
|
|
13
18
|
return true;
|
|
14
19
|
}
|
|
15
20
|
return false;
|
|
@@ -32,7 +32,7 @@ export class AdminRelease implements IApiBase {
|
|
|
32
32
|
protected mountDashboard() {
|
|
33
33
|
// called by FE
|
|
34
34
|
this.router.use('/check', needDevAdminSession, this.check.bind(this));
|
|
35
|
-
this.router.use('/update', needDevAdminSession, this.
|
|
35
|
+
this.router.use('/update', needDevAdminSession, this.callUpdate.bind(this));
|
|
36
36
|
this.router.use('/view-log', needDevAdminSession, this.viewLog.bind(this));
|
|
37
37
|
// called online or by clients
|
|
38
38
|
this.router.use('/refresh-cache', needDevAdminSession, this.refreshCache.bind(this));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import { Logger } from '../lib';
|
|
3
|
+
import { ServerRequest } from '../models';
|
|
4
|
+
import { langHelper } from '../lang';
|
|
5
|
+
import { ApiHelper, apiStorage } from '../api';
|
|
6
|
+
|
|
7
|
+
// only used by mobile app. For web, it's injected in the html by server-side render
|
|
8
|
+
const logger = new Logger('web-cfg-api');
|
|
9
|
+
export const readWebConfig = async (req: ServerRequest, res: ServerResponse) => {
|
|
10
|
+
logger.info('readWebConfig');
|
|
11
|
+
|
|
12
|
+
const response = {
|
|
13
|
+
status: 'ok',
|
|
14
|
+
result: await apiStorage.getWebAll(),
|
|
15
|
+
message: langHelper.getLang('shared:operation_success'),
|
|
16
|
+
};
|
|
17
|
+
ApiHelper.sendJson(req, res, response);
|
|
18
|
+
return true;
|
|
19
|
+
};
|
package/src/api/server-render.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { IToClientDelivery } from '../models/to-client-delivery-props';
|
|
|
9
9
|
import { JsonObject } from '../models/json-object';
|
|
10
10
|
import { getTemplateCache } from './api-cache';
|
|
11
11
|
import { apiStorage } from './api-shared-storage';
|
|
12
|
-
import { SimpleStorageDataProps } from '../models';
|
|
13
12
|
import { RuntimeRequire } from '../lib/runtime-require';
|
|
14
13
|
|
|
15
14
|
const logger = new Logger('StaticServer');
|
|
@@ -159,13 +158,13 @@ export const serverSideRenderPage = async (
|
|
|
159
158
|
const _lupineJs = cachedHtml[nearRoot]._lupineJs;
|
|
160
159
|
const currentCache = cachedHtml[nearRoot] as CachedHtmlProps;
|
|
161
160
|
const webSetting = await apiStorage.getWebAll();
|
|
162
|
-
const webSettingShortKey: SimpleStorageDataProps = {};
|
|
163
|
-
for (let item of Object.keys(webSetting)) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
161
|
+
// const webSettingShortKey: SimpleStorageDataProps = {};
|
|
162
|
+
// for (let item of Object.keys(webSetting)) {
|
|
163
|
+
// const newItem = item.substring(4);
|
|
164
|
+
// webSettingShortKey[newItem] = webSetting[item];
|
|
165
|
+
// }
|
|
167
166
|
// const webSetting = AppConfig.get(AppConfig.WEB_SETTINGS_KEY) || {};
|
|
168
|
-
const clientDelivery = new ToClientDelivery(currentCache.webEnv,
|
|
167
|
+
const clientDelivery = new ToClientDelivery(currentCache.webEnv, webSetting, req.locals.cookies());
|
|
169
168
|
const page = await _lupineJs.generatePage(props, clientDelivery);
|
|
170
169
|
// console.log(`=========load lupin: `, content);
|
|
171
170
|
|
|
@@ -199,7 +198,7 @@ export const serverSideRenderPage = async (
|
|
|
199
198
|
res.write(page.metaData);
|
|
200
199
|
res.write(page.globalCss);
|
|
201
200
|
res.write('<script id="web-env" type="application/json">' + JSON.stringify(currentCache.webEnv) + '</script>');
|
|
202
|
-
res.write('<script id="web-setting" type="application/json">' + JSON.stringify(
|
|
201
|
+
res.write('<script id="web-setting" type="application/json">' + JSON.stringify(webSetting) + '</script>');
|
|
203
202
|
res.write(
|
|
204
203
|
currentCache.content.substring(
|
|
205
204
|
currentCache.metaIndexEnd + metaTextEnd.length,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A persistent storage to store data in primary process and share to all workers
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* You should use apiStorage in api module
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from 'fs/promises';
|
|
@@ -141,9 +141,9 @@ export class AppSharedStorage implements IAppSharedStorage {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// called from primary before exit, or from api to save changes
|
|
144
|
-
async save(appName?: string) {
|
|
144
|
+
async save(appName?: string, exit?: boolean) {
|
|
145
145
|
if (!cluster.isPrimary) {
|
|
146
|
-
AppSharedStorageWorker.save(appName);
|
|
146
|
+
await AppSharedStorageWorker.save(appName);
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -183,8 +183,16 @@ export class AppSharedStorage implements IAppSharedStorage {
|
|
|
183
183
|
getApi(appName: string, key: string): Promise<string> {
|
|
184
184
|
return this.get(appName, AppSharedStorageApiPrefix + key);
|
|
185
185
|
}
|
|
186
|
-
getWebAll(appName: string): Promise<SimpleStorageDataProps> {
|
|
187
|
-
|
|
186
|
+
async getWebAll(appName: string): Promise<SimpleStorageDataProps> {
|
|
187
|
+
const webAll = await this.getWithPrefix(appName, AppSharedStorageWebPrefix);
|
|
188
|
+
|
|
189
|
+
const webSettingShortKey: SimpleStorageDataProps = {};
|
|
190
|
+
for (let item of Object.keys(webAll)) {
|
|
191
|
+
const newItem = item.substring(AppSharedStorageWebPrefix.length);
|
|
192
|
+
webSettingShortKey[newItem] = webAll[item];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return webSettingShortKey;
|
|
188
196
|
}
|
|
189
197
|
getWithPrefix(appName: string, prefixKey: string): Promise<SimpleStorageDataProps> {
|
|
190
198
|
return new Promise((resolve, reject) => {
|
|
@@ -267,7 +275,7 @@ class AppSharedStorageWorker {
|
|
|
267
275
|
workerId: cluster.worker?.id || 0,
|
|
268
276
|
action: 'save',
|
|
269
277
|
appName: appName || '',
|
|
270
|
-
key: '',
|
|
278
|
+
key: 'save',
|
|
271
279
|
};
|
|
272
280
|
process.send!(obj);
|
|
273
281
|
}
|
package/src/app/cleanup-exit.ts
CHANGED
package/src/lib/db/db.ts
CHANGED
|
@@ -2,7 +2,12 @@ import { Logger } from '../logger';
|
|
|
2
2
|
import { DbConfig } from '../../models/db-config';
|
|
3
3
|
|
|
4
4
|
// Instead, Boolean values are stored as integers 0 (false) and 1 (true).
|
|
5
|
+
export type DbFieldExprssionProps = { exprssion: string; params?: (string | number)[] };
|
|
5
6
|
export type DbFieldValue = { [key: string]: string | number };
|
|
7
|
+
export type DbFieldExpression = { [key: string]: string | number | DbFieldExprssionProps };
|
|
8
|
+
const isDbFieldExprssion = (value: any): value is DbFieldExprssionProps => {
|
|
9
|
+
return value && typeof value === 'object' && 'exprssion' in value;
|
|
10
|
+
};
|
|
6
11
|
|
|
7
12
|
const logger = new Logger('db');
|
|
8
13
|
export class Db {
|
|
@@ -252,12 +257,28 @@ export class Db {
|
|
|
252
257
|
return await this.execute(sql, params);
|
|
253
258
|
}
|
|
254
259
|
|
|
255
|
-
public async updateObject(table: string, updateFieldValues:
|
|
260
|
+
public async updateObject(table: string, updateFieldValues: DbFieldExpression, whereFieldValues: DbFieldValue) {
|
|
256
261
|
table = this.replacePrefix(table);
|
|
257
262
|
const fields = Object.keys(updateFieldValues);
|
|
258
|
-
|
|
259
|
-
const params =
|
|
263
|
+
const setClauseParts: string[] = [];
|
|
264
|
+
const params: (string | number)[] = [];
|
|
265
|
+
// let sql = 'UPDATE ' + table + ' SET ' + fields.map((item) => `${item}=?`).join(',');
|
|
266
|
+
// const params = Object.values(updateFieldValues);
|
|
267
|
+
for (const field of fields) {
|
|
268
|
+
const value = updateFieldValues[field];
|
|
269
|
+
|
|
270
|
+
// expression
|
|
271
|
+
if (isDbFieldExprssion(value)) {
|
|
272
|
+
setClauseParts.push(`${field} = ${value.exprssion}`);
|
|
273
|
+
if (value.params) params.push(...value.params);
|
|
274
|
+
} else {
|
|
275
|
+
// static value
|
|
276
|
+
setClauseParts.push(`${field} = ?`);
|
|
277
|
+
params.push(value);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
260
280
|
|
|
281
|
+
let sql = `UPDATE ${table} SET ${setClauseParts.join(', ')}`;
|
|
261
282
|
if (whereFieldValues && Object.keys(whereFieldValues).length > 0) {
|
|
262
283
|
sql +=
|
|
263
284
|
' WHERE ' +
|
|
@@ -25,7 +25,7 @@ export interface IAppSharedStorage {
|
|
|
25
25
|
// this is primary, msg is from a client
|
|
26
26
|
messageFromSubProcess(msgObject: any): void;
|
|
27
27
|
load(appName: string, rootPath: string): Promise<void>;
|
|
28
|
-
save(appName?: string): Promise<void>;
|
|
28
|
+
save(appName?: string, exit?: boolean): Promise<void>;
|
|
29
29
|
get(appName: string, key: string): Promise<string>;
|
|
30
30
|
getWeb(appName: string, key: string): Promise<string>;
|
|
31
31
|
getApi(appName: string, key: string): Promise<string>;
|