lupine.api 1.0.41
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/LICENSE +21 -0
- package/README.md +3 -0
- package/admin/admin-about.tsx +16 -0
- package/admin/admin-config.tsx +44 -0
- package/admin/admin-css.tsx +3 -0
- package/admin/admin-db.tsx +74 -0
- package/admin/admin-frame-props.tsx +9 -0
- package/admin/admin-frame.tsx +466 -0
- package/admin/admin-index.tsx +66 -0
- package/admin/admin-login.tsx +99 -0
- package/admin/admin-menu-edit.tsx +637 -0
- package/admin/admin-menu-list.tsx +87 -0
- package/admin/admin-page-edit.tsx +564 -0
- package/admin/admin-page-list.tsx +83 -0
- package/admin/admin-performance.tsx +28 -0
- package/admin/admin-release.tsx +320 -0
- package/admin/admin-resources.tsx +385 -0
- package/admin/admin-shell.tsx +89 -0
- package/admin/admin-table-data.tsx +146 -0
- package/admin/admin-table-list.tsx +231 -0
- package/admin/admin-test-animations.tsx +379 -0
- package/admin/admin-test-component.tsx +808 -0
- package/admin/admin-test-edit.tsx +319 -0
- package/admin/admin-test-themes.tsx +56 -0
- package/admin/admin-tokens.tsx +338 -0
- package/admin/design/admin-design.tsx +174 -0
- package/admin/design/block-grid.tsx +36 -0
- package/admin/design/block-grid1.tsx +21 -0
- package/admin/design/block-paragraph.tsx +19 -0
- package/admin/design/block-title.tsx +19 -0
- package/admin/design/design-block-box.tsx +140 -0
- package/admin/design/drag-data.tsx +24 -0
- package/admin/index.ts +6 -0
- package/admin/package.json +15 -0
- package/admin/tsconfig.json +127 -0
- package/dev/copy-folder.js +32 -0
- package/dev/cp-index-html.js +69 -0
- package/dev/file-utils.js +12 -0
- package/dev/index.js +19 -0
- package/dev/package.json +12 -0
- package/dev/plugin-gen-versions.js +20 -0
- package/dev/plugin-ifelse.js +155 -0
- package/dev/plugin-ifelse.test.js +37 -0
- package/dev/run-cmd.js +14 -0
- package/dev/send-request.js +12 -0
- package/package.json +55 -0
- package/src/admin-api/admin-api.ts +59 -0
- package/src/admin-api/admin-auth.ts +87 -0
- package/src/admin-api/admin-config.ts +93 -0
- package/src/admin-api/admin-csv.ts +81 -0
- package/src/admin-api/admin-db.ts +269 -0
- package/src/admin-api/admin-helper.ts +111 -0
- package/src/admin-api/admin-menu.ts +135 -0
- package/src/admin-api/admin-page.ts +135 -0
- package/src/admin-api/admin-performance.ts +128 -0
- package/src/admin-api/admin-release.ts +498 -0
- package/src/admin-api/admin-resources.ts +318 -0
- package/src/admin-api/admin-token-helper.ts +79 -0
- package/src/admin-api/admin-tokens.ts +90 -0
- package/src/admin-api/index.ts +2 -0
- package/src/api/api-cache.ts +103 -0
- package/src/api/api-helper.ts +44 -0
- package/src/api/api-module.ts +60 -0
- package/src/api/api-router.ts +177 -0
- package/src/api/api-shared-storage.ts +64 -0
- package/src/api/async-storage.ts +5 -0
- package/src/api/debug-service.ts +56 -0
- package/src/api/encode-html.ts +27 -0
- package/src/api/handle-status.ts +71 -0
- package/src/api/index.ts +16 -0
- package/src/api/mini-web-socket.ts +270 -0
- package/src/api/server-content-type.ts +82 -0
- package/src/api/server-render.ts +216 -0
- package/src/api/shell-service.ts +66 -0
- package/src/api/simple-storage.ts +80 -0
- package/src/api/static-server.ts +125 -0
- package/src/api/to-client-delivery.ts +26 -0
- package/src/app/app-cache.ts +55 -0
- package/src/app/app-loader.ts +62 -0
- package/src/app/app-message.ts +60 -0
- package/src/app/app-shared-storage.ts +317 -0
- package/src/app/app-start.ts +117 -0
- package/src/app/cleanup-exit.ts +12 -0
- package/src/app/host-to-path.ts +38 -0
- package/src/app/index.ts +11 -0
- package/src/app/process-dev-requests.ts +90 -0
- package/src/app/web-listener.ts +230 -0
- package/src/app/web-processor.ts +42 -0
- package/src/app/web-server.ts +86 -0
- package/src/common-js/web-env.js +104 -0
- package/src/index.ts +7 -0
- package/src/lang/api-lang-en.ts +27 -0
- package/src/lang/api-lang-zh-cn.ts +28 -0
- package/src/lang/index.ts +2 -0
- package/src/lang/lang-helper.ts +76 -0
- package/src/lang/lang-props.ts +6 -0
- package/src/lib/db/db-helper.ts +23 -0
- package/src/lib/db/db-mysql.ts +250 -0
- package/src/lib/db/db-sqlite.ts +101 -0
- package/src/lib/db/db.spec.ts +28 -0
- package/src/lib/db/db.ts +304 -0
- package/src/lib/db/index.ts +5 -0
- package/src/lib/index.ts +3 -0
- package/src/lib/logger.spec.ts +214 -0
- package/src/lib/logger.ts +274 -0
- package/src/lib/runtime-require.ts +37 -0
- package/src/lib/utils/cookie-util.ts +34 -0
- package/src/lib/utils/crypto.ts +58 -0
- package/src/lib/utils/date-utils.ts +317 -0
- package/src/lib/utils/deep-merge.ts +37 -0
- package/src/lib/utils/delay.ts +12 -0
- package/src/lib/utils/file-setting.ts +55 -0
- package/src/lib/utils/format-bytes.ts +11 -0
- package/src/lib/utils/fs-utils.ts +144 -0
- package/src/lib/utils/get-env.ts +27 -0
- package/src/lib/utils/index.ts +12 -0
- package/src/lib/utils/is-type.ts +48 -0
- package/src/lib/utils/load-env.ts +14 -0
- package/src/lib/utils/pad.ts +6 -0
- package/src/models/api-base.ts +5 -0
- package/src/models/api-module-props.ts +11 -0
- package/src/models/api-router-props.ts +26 -0
- package/src/models/app-cache-props.ts +33 -0
- package/src/models/app-data-props.ts +10 -0
- package/src/models/app-loader-props.ts +6 -0
- package/src/models/app-shared-storage-props.ts +37 -0
- package/src/models/app-start-props.ts +18 -0
- package/src/models/async-storage-props.ts +13 -0
- package/src/models/db-config.ts +30 -0
- package/src/models/host-to-path-props.ts +12 -0
- package/src/models/index.ts +16 -0
- package/src/models/json-object.ts +8 -0
- package/src/models/locals-props.ts +36 -0
- package/src/models/logger-props.ts +84 -0
- package/src/models/simple-storage-props.ts +14 -0
- package/src/models/to-client-delivery-props.ts +6 -0
- package/tsconfig.json +115 -0
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
for (const i in lines) {
|
|
58
|
+
if (!lines[i] || lines[i].startsWith('#')) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (lines[i].startsWith('@TABLE,')) {
|
|
62
|
+
table = lines[i].substring(7);
|
|
63
|
+
result[table] = { succeeded: 0, failed: 0, errorMessage: [] };
|
|
64
|
+
} else if (lines[i].startsWith('@FIELD,')) {
|
|
65
|
+
const fields = lines[i].substring(7); //.split(',');
|
|
66
|
+
const values = Array(fields.split(',').length).fill('?').join(',');
|
|
67
|
+
insSql = `INSERT INTO ${table} (${fields} ) VALUES (${values})`;
|
|
68
|
+
} else {
|
|
69
|
+
try {
|
|
70
|
+
const row = JSON.parse(lines[i]);
|
|
71
|
+
const r = await db.execute(insSql, row);
|
|
72
|
+
result[table].succeeded++;
|
|
73
|
+
} catch (error: any) {
|
|
74
|
+
result[table].failed++;
|
|
75
|
+
result[table].errorMessage.push(error.message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import { IApiBase, FsUtils, Logger, apiCache, ServerRequest, ApiRouter, ApiHelper } from 'lupine.api';
|
|
3
|
+
import { exportCSV, exportCSVTables, loadCSV } from './admin-csv';
|
|
4
|
+
|
|
5
|
+
const logger = new Logger('admin-db');
|
|
6
|
+
export class AdminDb implements IApiBase {
|
|
7
|
+
protected router = new ApiRouter();
|
|
8
|
+
adminUser: any;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this.mountDashboard();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public getRouter(): ApiRouter {
|
|
15
|
+
return this.router;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected mountDashboard() {
|
|
19
|
+
this.router.use('/install', this.install.bind(this));
|
|
20
|
+
this.router.use('/run-sql', this.runSql.bind(this));
|
|
21
|
+
// this.router.use('/table-info', this.tableInfo.bind(this));
|
|
22
|
+
|
|
23
|
+
this.router.use('/tables/list', this.tableList.bind(this));
|
|
24
|
+
this.router.use('/tables/download', this.tableDownloadAll.bind(this));
|
|
25
|
+
this.router.use('/tables/upload', this.tableUpload.bind(this));
|
|
26
|
+
this.router.use('/tables/truncate', this.tableTruncateAll.bind(this));
|
|
27
|
+
this.router.use('/tables/drop', this.tableDropAll.bind(this));
|
|
28
|
+
this.router.use('/table/data/:tableName/?offset/?limit', this.tableData.bind(this));
|
|
29
|
+
this.router.use('/table/delete/:tableName/:id', this.tableDelete.bind(this));
|
|
30
|
+
this.router.use('/table/drop/:tableName', this.tableDrop.bind(this));
|
|
31
|
+
this.router.use('/table/truncate/:tableName', this.tableTruncate.bind(this));
|
|
32
|
+
this.router.use('/table/download/:tableName', this.tableDownload.bind(this));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async tableList(req: ServerRequest, res: ServerResponse) {
|
|
36
|
+
const db = apiCache.getDb();
|
|
37
|
+
const result = await db.getAllTables(true);
|
|
38
|
+
const response = {
|
|
39
|
+
status: Array.isArray(result) ? 'ok' : 'error',
|
|
40
|
+
message: 'Table List.',
|
|
41
|
+
result: result,
|
|
42
|
+
};
|
|
43
|
+
ApiHelper.sendJson(req, res, response);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async tableDownloadAll(req: ServerRequest, res: ServerResponse) {
|
|
48
|
+
const db = apiCache.getDb();
|
|
49
|
+
const tables = await db.getAllTableNames();
|
|
50
|
+
exportCSVTables(db, tables, res);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async tableUpload(req: ServerRequest, res: ServerResponse) {
|
|
55
|
+
const db = apiCache.getDb();
|
|
56
|
+
const lines = Buffer.from(req.locals.body!)
|
|
57
|
+
.toString()
|
|
58
|
+
.split(/(?:\r\n|\r|\n)/g);
|
|
59
|
+
const result = await loadCSV(db, lines);
|
|
60
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
61
|
+
res.write(JSON.stringify({ status: 'ok', result }));
|
|
62
|
+
res.end();
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async tableTruncateAll(req: ServerRequest, res: ServerResponse) {
|
|
67
|
+
const db = apiCache.getDb();
|
|
68
|
+
const tables = await db.getAllTableNames();
|
|
69
|
+
if (tables && tables.length > 0) {
|
|
70
|
+
for (const i in tables) {
|
|
71
|
+
await db.truncateTable(tables[i]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const response = {
|
|
76
|
+
status: 'ok',
|
|
77
|
+
message: 'Truncate all Tables: ' + tables.length,
|
|
78
|
+
result: '',
|
|
79
|
+
};
|
|
80
|
+
ApiHelper.sendJson(req, res, response);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async tableDropAll(req: ServerRequest, res: ServerResponse) {
|
|
85
|
+
const db = apiCache.getDb();
|
|
86
|
+
const tables = await db.getAllTableNames();
|
|
87
|
+
if (tables && tables.length > 0) {
|
|
88
|
+
for (const i in tables) {
|
|
89
|
+
await db.execute(`DROP TABLE ${tables[i]}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const response = {
|
|
94
|
+
status: 'ok',
|
|
95
|
+
message: 'Truncate all Tables: ' + tables.length,
|
|
96
|
+
result: '',
|
|
97
|
+
};
|
|
98
|
+
ApiHelper.sendJson(req, res, response);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async tableData(req: ServerRequest, res: ServerResponse) {
|
|
103
|
+
const db = apiCache.getDb();
|
|
104
|
+
const tableName = req.locals.urlParameters.get('tableName', '');
|
|
105
|
+
let pageIndex = req.locals.urlParameters.getInt('offset', 0); // Number.parseInt(post.pg_i as string);
|
|
106
|
+
const pageLimit = req.locals.urlParameters.getInt('limit', 30); //Number.parseInt(post.pg_l as string) || 50;
|
|
107
|
+
|
|
108
|
+
const response = {
|
|
109
|
+
status: 'error',
|
|
110
|
+
message: tableName ? 'Table Data.' : 'Need a table name.',
|
|
111
|
+
result: null,
|
|
112
|
+
};
|
|
113
|
+
if (tableName) {
|
|
114
|
+
const itemsCount = await db.selectOneResult(tableName, 'count(*)');
|
|
115
|
+
let maxPages = Math.floor(itemsCount / pageLimit);
|
|
116
|
+
if (itemsCount % pageLimit !== 0) {
|
|
117
|
+
maxPages++;
|
|
118
|
+
}
|
|
119
|
+
if (pageIndex > maxPages) {
|
|
120
|
+
pageIndex = maxPages - 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const offset = pageIndex * pageLimit;
|
|
124
|
+
response.result = await db.selectObject(tableName, undefined, undefined, undefined, pageLimit, offset);
|
|
125
|
+
response.status = 'ok';
|
|
126
|
+
(response as any).itemsCount = itemsCount;
|
|
127
|
+
(response as any).pageIndex = pageIndex;
|
|
128
|
+
}
|
|
129
|
+
ApiHelper.sendJson(req, res, response);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async tableDelete(req: ServerRequest, res: ServerResponse) {
|
|
134
|
+
const db = apiCache.getDb();
|
|
135
|
+
const tableName = req.locals.urlParameters.get('tableName', '');
|
|
136
|
+
const id = req.locals.urlParameters.getInt('id', -1);
|
|
137
|
+
|
|
138
|
+
const response = {
|
|
139
|
+
status: 'error',
|
|
140
|
+
message: tableName ? 'Table delete.' : 'Need a table name.',
|
|
141
|
+
result: null,
|
|
142
|
+
};
|
|
143
|
+
if (tableName && id > 0) {
|
|
144
|
+
const result = await db.deleteObject(tableName, { id: id });
|
|
145
|
+
response.result = result;
|
|
146
|
+
response.status = 'ok';
|
|
147
|
+
ApiHelper.sendJson(req, res, response);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
ApiHelper.sendJson(req, res, response);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async tableDownload(req: ServerRequest, res: ServerResponse) {
|
|
155
|
+
const db = apiCache.getDb();
|
|
156
|
+
const tableName = req.locals.urlParameters.get('tableName', '');
|
|
157
|
+
if (!tableName) {
|
|
158
|
+
const response = {
|
|
159
|
+
status: 'error',
|
|
160
|
+
message: 'Need a table name.',
|
|
161
|
+
};
|
|
162
|
+
ApiHelper.sendJson(req, res, response);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
exportCSV(db, tableName, res);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async tableDrop(req: ServerRequest, res: ServerResponse) {
|
|
170
|
+
const db = apiCache.getDb();
|
|
171
|
+
const tableName = req.locals.urlParameters.get('tableName', '');
|
|
172
|
+
const response = {
|
|
173
|
+
status: 'error',
|
|
174
|
+
message: tableName ? 'Delete Table.' : 'Need a table name.',
|
|
175
|
+
result: '',
|
|
176
|
+
};
|
|
177
|
+
if (tableName) {
|
|
178
|
+
const result = await db.execute(`DROP TABLE ${tableName}`);
|
|
179
|
+
response.result = result;
|
|
180
|
+
response.status = 'ok';
|
|
181
|
+
}
|
|
182
|
+
ApiHelper.sendJson(req, res, response);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async tableTruncate(req: ServerRequest, res: ServerResponse) {
|
|
187
|
+
const db = apiCache.getDb();
|
|
188
|
+
const tableName = req.locals.urlParameters.get('tableName', '');
|
|
189
|
+
const response = {
|
|
190
|
+
status: 'error',
|
|
191
|
+
message: tableName ? 'Truncate Table.' : 'Need a table name.',
|
|
192
|
+
result: '',
|
|
193
|
+
};
|
|
194
|
+
if (tableName) {
|
|
195
|
+
const result = await db.truncateTable(tableName);
|
|
196
|
+
response.result = result;
|
|
197
|
+
response.status = 'ok';
|
|
198
|
+
}
|
|
199
|
+
ApiHelper.sendJson(req, res, response);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async runSql(req: ServerRequest, res: ServerResponse) {
|
|
204
|
+
const db = apiCache.getDb();
|
|
205
|
+
const data = req.locals.json();
|
|
206
|
+
const response = {
|
|
207
|
+
status: 'error',
|
|
208
|
+
message: '',
|
|
209
|
+
result: '',
|
|
210
|
+
};
|
|
211
|
+
if (data && !Array.isArray(data) && data.sql) {
|
|
212
|
+
const sql = ((data.sql as string) || '').trim();
|
|
213
|
+
const result = sql.toUpperCase().startsWith('SELECT') ? await db.select(sql) : await db.execute(sql);
|
|
214
|
+
response.status = 'ok';
|
|
215
|
+
response.message = 'Executed sql.';
|
|
216
|
+
response.result = result;
|
|
217
|
+
}
|
|
218
|
+
ApiHelper.sendJson(req, res, response);
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async install(req: ServerRequest, res: ServerResponse) {
|
|
223
|
+
const db = apiCache.getDb();
|
|
224
|
+
const appData = apiCache.getAppData();
|
|
225
|
+
const installDir = appData.dataPath + '/resources';
|
|
226
|
+
const dbType = appData.dbType;
|
|
227
|
+
const sqlFile = `${installDir}/install.${dbType}.sql`;
|
|
228
|
+
const sql = await FsUtils.readFile(sqlFile);
|
|
229
|
+
if (!sql) {
|
|
230
|
+
const response = { errorMessage: `Can't read file: ${sqlFile}` };
|
|
231
|
+
ApiHelper.sendJson(req, res, response);
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const sqlArr = sql.replace(/^#.*/gm, '').replace(/\r/g, ' ').replace(/\n/g, ' ').split(';');
|
|
236
|
+
const result = [];
|
|
237
|
+
for (let one in sqlArr) {
|
|
238
|
+
const oneSql = sqlArr[one].trim();
|
|
239
|
+
if (oneSql) {
|
|
240
|
+
try {
|
|
241
|
+
const one = await db.execute(oneSql);
|
|
242
|
+
result.push(one);
|
|
243
|
+
} catch (error: any) {
|
|
244
|
+
result.push({ error: error.message });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const response = { status: 'ok', message: 'Installed', result };
|
|
249
|
+
ApiHelper.sendJson(req, res, response);
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async tableInfo(req: ServerRequest, res: ServerResponse) {
|
|
254
|
+
const db = apiCache.getDb();
|
|
255
|
+
const tableName = req.locals.query.get('table');
|
|
256
|
+
const response = {
|
|
257
|
+
status: 'error',
|
|
258
|
+
message: tableName ? 'Get Table.' : 'Need a table name.',
|
|
259
|
+
result: '',
|
|
260
|
+
};
|
|
261
|
+
if (tableName) {
|
|
262
|
+
const result = await db.getTableInfo(tableName);
|
|
263
|
+
response.result = result;
|
|
264
|
+
response.status = 'ok';
|
|
265
|
+
}
|
|
266
|
+
ApiHelper.sendJson(req, res, response);
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
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();
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
import { IApiBase, Logger, apiCache, ServerRequest, ApiRouter, ApiHelper } from 'lupine.api';
|
|
3
|
+
|
|
4
|
+
const logger = new Logger('admin-menu');
|
|
5
|
+
export class AdminMenu implements IApiBase {
|
|
6
|
+
protected router = new ApiRouter();
|
|
7
|
+
adminUser: any;
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
this.mountDashboard();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public getRouter(): ApiRouter {
|
|
14
|
+
return this.router;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
protected mountDashboard() {
|
|
18
|
+
this.router.use('/list', this.list.bind(this));
|
|
19
|
+
this.router.use('/add', this.add.bind(this));
|
|
20
|
+
this.router.use('/save', this.save.bind(this));
|
|
21
|
+
this.router.use('/get/:id', this.getRecord.bind(this));
|
|
22
|
+
this.router.use('/delete/:id', this.delete.bind(this));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async list(req: ServerRequest, res: ServerResponse) {
|
|
26
|
+
const db = apiCache.getDb();
|
|
27
|
+
const result = await db.selectObject('$__s_menu');
|
|
28
|
+
const response = {
|
|
29
|
+
status: Array.isArray(result) ? 'ok' : 'error',
|
|
30
|
+
message: 'Menu List.',
|
|
31
|
+
result: result,
|
|
32
|
+
};
|
|
33
|
+
ApiHelper.sendJson(req, res, response);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async add(req: ServerRequest, res: ServerResponse) {
|
|
38
|
+
const db = apiCache.getDb();
|
|
39
|
+
const data = req.locals.json() as any;
|
|
40
|
+
delete data['id'];
|
|
41
|
+
const result = await db.insertObject('$__st_user', data);
|
|
42
|
+
const response = {
|
|
43
|
+
status: Array.isArray(result) ? 'ok' : 'error',
|
|
44
|
+
message: 'User List.',
|
|
45
|
+
result: result,
|
|
46
|
+
};
|
|
47
|
+
ApiHelper.sendJson(req, res, response);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async save(req: ServerRequest, res: ServerResponse) {
|
|
52
|
+
const db = apiCache.getDb();
|
|
53
|
+
const data = req.locals.json() as any;
|
|
54
|
+
|
|
55
|
+
const id = data['id'];
|
|
56
|
+
if (!data['idReadonly']) {
|
|
57
|
+
// save as
|
|
58
|
+
const result = await db.selectObject('$__s_menu', ['menuid'], {
|
|
59
|
+
menuid: id,
|
|
60
|
+
});
|
|
61
|
+
if (result && result.length > 0) {
|
|
62
|
+
const response = {
|
|
63
|
+
status: 'ID_EXISTS',
|
|
64
|
+
message: `Menu id: ${id} exists`,
|
|
65
|
+
};
|
|
66
|
+
ApiHelper.sendJson(req, res, response);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await db.deleteObject('$__s_menu', { menuid: id });
|
|
72
|
+
const result = await db.insertObject('$__s_menu', {
|
|
73
|
+
menuid: id,
|
|
74
|
+
title: data['title'],
|
|
75
|
+
note: data['note'],
|
|
76
|
+
package: data['package'],
|
|
77
|
+
items: JSON.stringify(data['items']),
|
|
78
|
+
updateduserid: 1,
|
|
79
|
+
updatedstamp: new Date().toISOString(),
|
|
80
|
+
});
|
|
81
|
+
const response = {
|
|
82
|
+
status: 'ok',
|
|
83
|
+
result: result,
|
|
84
|
+
message: 'Menu Saved.',
|
|
85
|
+
};
|
|
86
|
+
ApiHelper.sendJson(req, res, response);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async getRecord(req: ServerRequest, res: ServerResponse) {
|
|
91
|
+
const db = apiCache.getDb();
|
|
92
|
+
const id = req.locals.urlParameters.get('id', '');
|
|
93
|
+
|
|
94
|
+
const response = {
|
|
95
|
+
status: 'error',
|
|
96
|
+
message: 'Get a menu item.',
|
|
97
|
+
result: {},
|
|
98
|
+
};
|
|
99
|
+
if (id) {
|
|
100
|
+
const result = await db.selectObject('$__s_menu', undefined, {
|
|
101
|
+
menuid: id,
|
|
102
|
+
});
|
|
103
|
+
if (result && result.length > 0) {
|
|
104
|
+
response.result = {
|
|
105
|
+
menuid: result[0]['menuid'],
|
|
106
|
+
title: result[0]['title'],
|
|
107
|
+
note: result[0]['note'],
|
|
108
|
+
package: result[0]['package'],
|
|
109
|
+
items: JSON.parse(result[0]['items']),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
response.status = 'ok';
|
|
113
|
+
}
|
|
114
|
+
ApiHelper.sendJson(req, res, response);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async delete(req: ServerRequest, res: ServerResponse) {
|
|
119
|
+
const db = apiCache.getDb();
|
|
120
|
+
const id = req.locals.urlParameters.get('id', '');
|
|
121
|
+
|
|
122
|
+
const response = {
|
|
123
|
+
status: 'ok',
|
|
124
|
+
message: 'Delete a menu.',
|
|
125
|
+
result: null,
|
|
126
|
+
};
|
|
127
|
+
if (id) {
|
|
128
|
+
const result = await db.deleteObject('$__s_menu', { menuid: id });
|
|
129
|
+
response.result = result;
|
|
130
|
+
response.status = 'ok';
|
|
131
|
+
}
|
|
132
|
+
ApiHelper.sendJson(req, res, response);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|