lupine.api 1.1.57 → 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 -404
- 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,318 +1,318 @@
|
|
|
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
|
-
adminApiHelper,
|
|
13
|
-
} from 'lupine.api';
|
|
14
|
-
import path from 'path';
|
|
15
|
-
|
|
16
|
-
export class AdminResources implements IApiBase {
|
|
17
|
-
private logger = new Logger('resources-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('/data', this.data.bind(this));
|
|
31
|
-
this.router.use('/download', this.download.bind(this));
|
|
32
|
-
this.router.use('/upload', this.upload.bind(this));
|
|
33
|
-
this.router.use('/rename', this.rename.bind(this));
|
|
34
|
-
this.router.use('/newFolder', this.newFolder.bind(this));
|
|
35
|
-
this.router.use('/remove', this.remove.bind(this));
|
|
36
|
-
this.router.use('/removeDir', this.removeDir.bind(this));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async upload(req: ServerRequest, res: ServerResponse) {
|
|
40
|
-
const fPath = req.locals.query.get('p');
|
|
41
|
-
const fName = req.locals.query.get('n');
|
|
42
|
-
const data = req.locals.body;
|
|
43
|
-
if (!data || data.length < 1 || !fPath || !fName) {
|
|
44
|
-
const response = {
|
|
45
|
-
status: 'error',
|
|
46
|
-
message: langHelper.getLang('shared:wrong_data'),
|
|
47
|
-
};
|
|
48
|
-
ApiHelper.sendJson(req, res, response);
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const appData = apiCache.getAppData();
|
|
53
|
-
const locPath = this.pathJoin(fPath, appData.webPath);
|
|
54
|
-
if (!(await FsUtils.pathExist(locPath))) {
|
|
55
|
-
const response = {
|
|
56
|
-
status: 'error',
|
|
57
|
-
message: langHelper.getLang('shared:wrong_data'),
|
|
58
|
-
};
|
|
59
|
-
ApiHelper.sendJson(req, res, response);
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const key = req.locals.query.get('key') as string;
|
|
64
|
-
const chunkNumberStr = req.locals.query.get('chunkNumber') as string;
|
|
65
|
-
const chunkNumber = parseInt(chunkNumberStr);
|
|
66
|
-
const totalChunks = parseInt(req.locals.query.get('totalChunks') as string);
|
|
67
|
-
const decryptedKey = key && adminApiHelper.decryptJson(key.replace(/ /g, '+'));
|
|
68
|
-
const keyNG =
|
|
69
|
-
!chunkNumberStr ||
|
|
70
|
-
!totalChunks ||
|
|
71
|
-
(chunkNumber !== 0 && (!decryptedKey || decryptedKey.ind !== chunkNumber || decryptedKey.cnt !== totalChunks));
|
|
72
|
-
if (keyNG) {
|
|
73
|
-
const response = {
|
|
74
|
-
status: 'error',
|
|
75
|
-
message: langHelper.getLang('shared:permission_denied'),
|
|
76
|
-
};
|
|
77
|
-
ApiHelper.sendJson(req, res, response);
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
const MAX_FILE_SIZE = 1024 * 1024 * 800; // MB
|
|
81
|
-
if (totalChunks * data.length > MAX_FILE_SIZE) {
|
|
82
|
-
const response = {
|
|
83
|
-
status: 'error',
|
|
84
|
-
message: langHelper.getLang('shared:file_too_large'),
|
|
85
|
-
};
|
|
86
|
-
ApiHelper.sendJson(req, res, response);
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const filename = path.join(fPath, fName);
|
|
91
|
-
// write data to a file
|
|
92
|
-
if (chunkNumber === 0) {
|
|
93
|
-
await fs.writeFile(filename, data, 'binary');
|
|
94
|
-
} else {
|
|
95
|
-
await fs.appendFile(filename, data, 'binary');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const response = {
|
|
99
|
-
status: 'ok',
|
|
100
|
-
chunkNumber,
|
|
101
|
-
totalChunks,
|
|
102
|
-
message: langHelper.getLang('shared:file_part_updated'),
|
|
103
|
-
key: adminApiHelper.encryptJson({ ind: chunkNumber + 1, cnt: totalChunks, t: new Date().getTime() }),
|
|
104
|
-
};
|
|
105
|
-
ApiHelper.sendJson(req, res, response);
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private chkData(data: any, req: ServerRequest, res: ServerResponse, chkCredential: boolean) {
|
|
110
|
-
if (!data || Array.isArray(data) || typeof data !== 'object') {
|
|
111
|
-
this.logger.error(`chkData, missing parameters`, data);
|
|
112
|
-
const response = {
|
|
113
|
-
status: 'error',
|
|
114
|
-
message: 'Wrong data [missing parameters].', //langHelper.getLang('shared:wrong_data'),
|
|
115
|
-
};
|
|
116
|
-
ApiHelper.sendJson(req, res, response);
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
return data;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private async getFolders(locPath: string) {
|
|
123
|
-
const results: any[] = [];
|
|
124
|
-
const folders = await FsUtils.getDirAndFiles(locPath);
|
|
125
|
-
|
|
126
|
-
results.push({
|
|
127
|
-
fullPath: locPath,
|
|
128
|
-
});
|
|
129
|
-
for (let i = 0; i < folders.length; i++) {
|
|
130
|
-
const oneFolder = folders[i];
|
|
131
|
-
const fileInfo = await FsUtils.fileInfo(path.join(locPath, oneFolder));
|
|
132
|
-
if (fileInfo?.isFile) {
|
|
133
|
-
results.push({
|
|
134
|
-
name: oneFolder,
|
|
135
|
-
time: new Date(fileInfo!.mtime).toLocaleString(),
|
|
136
|
-
size: fileInfo?.size,
|
|
137
|
-
});
|
|
138
|
-
} else {
|
|
139
|
-
const subFolders = await FsUtils.getDirAndFiles(path.join(locPath, oneFolder));
|
|
140
|
-
const subFoldersWithTime = [];
|
|
141
|
-
for (let j = 0; j < subFolders.length; j++) {
|
|
142
|
-
const subFile = subFolders[j];
|
|
143
|
-
const fileInfo2 = await FsUtils.fileInfo(path.join(locPath, oneFolder, subFile));
|
|
144
|
-
|
|
145
|
-
if (fileInfo2?.isFile) {
|
|
146
|
-
subFoldersWithTime.push({
|
|
147
|
-
name: subFile,
|
|
148
|
-
time: new Date(fileInfo2.mtime).toLocaleString(),
|
|
149
|
-
size: fileInfo2?.size,
|
|
150
|
-
});
|
|
151
|
-
} else {
|
|
152
|
-
subFoldersWithTime.push({
|
|
153
|
-
name: subFile,
|
|
154
|
-
time: fileInfo2?.mtime && new Date(fileInfo2.mtime).toLocaleString(),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
results.push({
|
|
159
|
-
name: oneFolder,
|
|
160
|
-
time: fileInfo?.mtime && new Date(fileInfo.mtime).toLocaleString(),
|
|
161
|
-
items: subFoldersWithTime,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return results;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private pathJoin(folder: string, apiPath: string) {
|
|
169
|
-
return folder !== '/' && folder !== '' && (folder.startsWith('/') || folder.startsWith('\\') || folder[1] === ':')
|
|
170
|
-
? path.join(folder)
|
|
171
|
-
: path.join(apiPath, folder);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async data(req: ServerRequest, res: ServerResponse) {
|
|
175
|
-
const data = this.chkData(req.locals.json(), req, res, false);
|
|
176
|
-
if (!data) return true;
|
|
177
|
-
let results: any[] = [];
|
|
178
|
-
if (typeof data.folder === 'string') {
|
|
179
|
-
const appData = apiCache.getAppData();
|
|
180
|
-
const locPath = this.pathJoin(data.folder, appData.webPath);
|
|
181
|
-
results = await this.getFolders(locPath);
|
|
182
|
-
}
|
|
183
|
-
const response = {
|
|
184
|
-
status: 'ok',
|
|
185
|
-
message: 'OK',
|
|
186
|
-
results,
|
|
187
|
-
};
|
|
188
|
-
ApiHelper.sendJson(req, res, response);
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// called by clients
|
|
193
|
-
async download(req: ServerRequest, res: ServerResponse) {
|
|
194
|
-
const data = this.chkData(req.locals.json(), req, res, true);
|
|
195
|
-
if (!data) return true;
|
|
196
|
-
|
|
197
|
-
if (typeof data.resource === 'string') {
|
|
198
|
-
const resPath = path.join(data.resource);
|
|
199
|
-
if (await FsUtils.pathExist(resPath)) {
|
|
200
|
-
ApiHelper.sendFile(req, res, resPath);
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const response = {
|
|
205
|
-
status: 'error',
|
|
206
|
-
message: 'Resource not found',
|
|
207
|
-
};
|
|
208
|
-
ApiHelper.sendJson(req, res, response);
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async rename(req: ServerRequest, res: ServerResponse) {
|
|
213
|
-
const data = this.chkData(req.locals.json(), req, res, true);
|
|
214
|
-
if (!data) return true;
|
|
215
|
-
|
|
216
|
-
if (typeof data.resource === 'string' && data.oldName && data.newName) {
|
|
217
|
-
const resPath = path.join(data.resource);
|
|
218
|
-
const basePath = path.dirname(resPath);
|
|
219
|
-
const oldPath = path.join(basePath, data.oldName);
|
|
220
|
-
if (await FsUtils.pathExist(oldPath)) {
|
|
221
|
-
const newPath = path.join(basePath, data.newName);
|
|
222
|
-
await FsUtils.rename(oldPath, newPath);
|
|
223
|
-
if (await FsUtils.pathExist(newPath)) {
|
|
224
|
-
const response = {
|
|
225
|
-
status: 'ok',
|
|
226
|
-
message: 'Resource renamed to ' + newPath,
|
|
227
|
-
};
|
|
228
|
-
ApiHelper.sendJson(req, res, response);
|
|
229
|
-
} else {
|
|
230
|
-
const response = {
|
|
231
|
-
status: 'error',
|
|
232
|
-
message: 'Resource not renamed',
|
|
233
|
-
};
|
|
234
|
-
ApiHelper.sendJson(req, res, response);
|
|
235
|
-
}
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
const response = {
|
|
240
|
-
status: 'error',
|
|
241
|
-
message: 'Resource not found',
|
|
242
|
-
};
|
|
243
|
-
ApiHelper.sendJson(req, res, response);
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
async remove(req: ServerRequest, res: ServerResponse) {
|
|
248
|
-
const data = this.chkData(req.locals.json(), req, res, true);
|
|
249
|
-
if (!data) return true;
|
|
250
|
-
|
|
251
|
-
if (typeof data.resource === 'string') {
|
|
252
|
-
const resPath = path.join(data.resource);
|
|
253
|
-
if (await FsUtils.pathExist(resPath)) {
|
|
254
|
-
await FsUtils.unlinkFile(resPath);
|
|
255
|
-
const response = {
|
|
256
|
-
status: 'ok',
|
|
257
|
-
message: 'Resource removed',
|
|
258
|
-
};
|
|
259
|
-
ApiHelper.sendJson(req, res, response);
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const response = {
|
|
264
|
-
status: 'error',
|
|
265
|
-
message: 'Resource not found',
|
|
266
|
-
};
|
|
267
|
-
ApiHelper.sendJson(req, res, response);
|
|
268
|
-
return true;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async removeDir(req: ServerRequest, res: ServerResponse) {
|
|
272
|
-
const data = this.chkData(req.locals.json(), req, res, true);
|
|
273
|
-
if (!data) return true;
|
|
274
|
-
|
|
275
|
-
if (typeof data.resource === 'string') {
|
|
276
|
-
const resPath = path.join(data.resource);
|
|
277
|
-
if (await FsUtils.pathExist(resPath)) {
|
|
278
|
-
await FsUtils.unlinkFolderEmpty(resPath);
|
|
279
|
-
const response = {
|
|
280
|
-
status: 'ok',
|
|
281
|
-
message: 'Directory removed',
|
|
282
|
-
};
|
|
283
|
-
ApiHelper.sendJson(req, res, response);
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
const response = {
|
|
288
|
-
status: 'error',
|
|
289
|
-
message: 'Directory not found or has files',
|
|
290
|
-
};
|
|
291
|
-
ApiHelper.sendJson(req, res, response);
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async newFolder(req: ServerRequest, res: ServerResponse) {
|
|
296
|
-
const data = this.chkData(req.locals.json(), req, res, true);
|
|
297
|
-
if (!data) return true;
|
|
298
|
-
|
|
299
|
-
if (typeof data.resource === 'string' && data.newName) {
|
|
300
|
-
const resPath = path.join(data.resource, data.newName);
|
|
301
|
-
if (!await FsUtils.pathExist(resPath)) {
|
|
302
|
-
await FsUtils.mkdir(resPath);
|
|
303
|
-
const response = {
|
|
304
|
-
status: 'ok',
|
|
305
|
-
message: 'Folder created',
|
|
306
|
-
};
|
|
307
|
-
ApiHelper.sendJson(req, res, response);
|
|
308
|
-
return true;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
const response = {
|
|
312
|
-
status: 'error',
|
|
313
|
-
message: 'Folder not created',
|
|
314
|
-
};
|
|
315
|
-
ApiHelper.sendJson(req, res, response);
|
|
316
|
-
return true;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
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
|
+
adminApiHelper,
|
|
13
|
+
} from 'lupine.api';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
|
|
16
|
+
export class AdminResources implements IApiBase {
|
|
17
|
+
private logger = new Logger('resources-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('/data', this.data.bind(this));
|
|
31
|
+
this.router.use('/download', this.download.bind(this));
|
|
32
|
+
this.router.use('/upload', this.upload.bind(this));
|
|
33
|
+
this.router.use('/rename', this.rename.bind(this));
|
|
34
|
+
this.router.use('/newFolder', this.newFolder.bind(this));
|
|
35
|
+
this.router.use('/remove', this.remove.bind(this));
|
|
36
|
+
this.router.use('/removeDir', this.removeDir.bind(this));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async upload(req: ServerRequest, res: ServerResponse) {
|
|
40
|
+
const fPath = req.locals.query.get('p');
|
|
41
|
+
const fName = req.locals.query.get('n');
|
|
42
|
+
const data = req.locals.body;
|
|
43
|
+
if (!data || data.length < 1 || !fPath || !fName) {
|
|
44
|
+
const response = {
|
|
45
|
+
status: 'error',
|
|
46
|
+
message: langHelper.getLang('shared:wrong_data'),
|
|
47
|
+
};
|
|
48
|
+
ApiHelper.sendJson(req, res, response);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const appData = apiCache.getAppData();
|
|
53
|
+
const locPath = this.pathJoin(fPath, appData.webPath);
|
|
54
|
+
if (!(await FsUtils.pathExist(locPath))) {
|
|
55
|
+
const response = {
|
|
56
|
+
status: 'error',
|
|
57
|
+
message: langHelper.getLang('shared:wrong_data'),
|
|
58
|
+
};
|
|
59
|
+
ApiHelper.sendJson(req, res, response);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const key = req.locals.query.get('key') as string;
|
|
64
|
+
const chunkNumberStr = req.locals.query.get('chunkNumber') as string;
|
|
65
|
+
const chunkNumber = parseInt(chunkNumberStr);
|
|
66
|
+
const totalChunks = parseInt(req.locals.query.get('totalChunks') as string);
|
|
67
|
+
const decryptedKey = key && adminApiHelper.decryptJson(key.replace(/ /g, '+'));
|
|
68
|
+
const keyNG =
|
|
69
|
+
!chunkNumberStr ||
|
|
70
|
+
!totalChunks ||
|
|
71
|
+
(chunkNumber !== 0 && (!decryptedKey || decryptedKey.ind !== chunkNumber || decryptedKey.cnt !== totalChunks));
|
|
72
|
+
if (keyNG) {
|
|
73
|
+
const response = {
|
|
74
|
+
status: 'error',
|
|
75
|
+
message: langHelper.getLang('shared:permission_denied'),
|
|
76
|
+
};
|
|
77
|
+
ApiHelper.sendJson(req, res, response);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const MAX_FILE_SIZE = 1024 * 1024 * 800; // MB
|
|
81
|
+
if (totalChunks * data.length > MAX_FILE_SIZE) {
|
|
82
|
+
const response = {
|
|
83
|
+
status: 'error',
|
|
84
|
+
message: langHelper.getLang('shared:file_too_large'),
|
|
85
|
+
};
|
|
86
|
+
ApiHelper.sendJson(req, res, response);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const filename = path.join(fPath, fName);
|
|
91
|
+
// write data to a file
|
|
92
|
+
if (chunkNumber === 0) {
|
|
93
|
+
await fs.writeFile(filename, data, 'binary');
|
|
94
|
+
} else {
|
|
95
|
+
await fs.appendFile(filename, data, 'binary');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const response = {
|
|
99
|
+
status: 'ok',
|
|
100
|
+
chunkNumber,
|
|
101
|
+
totalChunks,
|
|
102
|
+
message: langHelper.getLang('shared:file_part_updated'),
|
|
103
|
+
key: adminApiHelper.encryptJson({ ind: chunkNumber + 1, cnt: totalChunks, t: new Date().getTime() }),
|
|
104
|
+
};
|
|
105
|
+
ApiHelper.sendJson(req, res, response);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private chkData(data: any, req: ServerRequest, res: ServerResponse, chkCredential: boolean) {
|
|
110
|
+
if (!data || Array.isArray(data) || typeof data !== 'object') {
|
|
111
|
+
this.logger.error(`chkData, missing parameters`, data);
|
|
112
|
+
const response = {
|
|
113
|
+
status: 'error',
|
|
114
|
+
message: 'Wrong data [missing parameters].', //langHelper.getLang('shared:wrong_data'),
|
|
115
|
+
};
|
|
116
|
+
ApiHelper.sendJson(req, res, response);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return data;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async getFolders(locPath: string) {
|
|
123
|
+
const results: any[] = [];
|
|
124
|
+
const folders = await FsUtils.getDirAndFiles(locPath);
|
|
125
|
+
|
|
126
|
+
results.push({
|
|
127
|
+
fullPath: locPath,
|
|
128
|
+
});
|
|
129
|
+
for (let i = 0; i < folders.length; i++) {
|
|
130
|
+
const oneFolder = folders[i];
|
|
131
|
+
const fileInfo = await FsUtils.fileInfo(path.join(locPath, oneFolder));
|
|
132
|
+
if (fileInfo?.isFile) {
|
|
133
|
+
results.push({
|
|
134
|
+
name: oneFolder,
|
|
135
|
+
time: new Date(fileInfo!.mtime).toLocaleString(),
|
|
136
|
+
size: fileInfo?.size,
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
const subFolders = await FsUtils.getDirAndFiles(path.join(locPath, oneFolder));
|
|
140
|
+
const subFoldersWithTime = [];
|
|
141
|
+
for (let j = 0; j < subFolders.length; j++) {
|
|
142
|
+
const subFile = subFolders[j];
|
|
143
|
+
const fileInfo2 = await FsUtils.fileInfo(path.join(locPath, oneFolder, subFile));
|
|
144
|
+
|
|
145
|
+
if (fileInfo2?.isFile) {
|
|
146
|
+
subFoldersWithTime.push({
|
|
147
|
+
name: subFile,
|
|
148
|
+
time: new Date(fileInfo2.mtime).toLocaleString(),
|
|
149
|
+
size: fileInfo2?.size,
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
subFoldersWithTime.push({
|
|
153
|
+
name: subFile,
|
|
154
|
+
time: fileInfo2?.mtime && new Date(fileInfo2.mtime).toLocaleString(),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
results.push({
|
|
159
|
+
name: oneFolder,
|
|
160
|
+
time: fileInfo?.mtime && new Date(fileInfo.mtime).toLocaleString(),
|
|
161
|
+
items: subFoldersWithTime,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return results;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private pathJoin(folder: string, apiPath: string) {
|
|
169
|
+
return folder !== '/' && folder !== '' && (folder.startsWith('/') || folder.startsWith('\\') || folder[1] === ':')
|
|
170
|
+
? path.join(folder)
|
|
171
|
+
: path.join(apiPath, folder);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async data(req: ServerRequest, res: ServerResponse) {
|
|
175
|
+
const data = this.chkData(req.locals.json(), req, res, false);
|
|
176
|
+
if (!data) return true;
|
|
177
|
+
let results: any[] = [];
|
|
178
|
+
if (typeof data.folder === 'string') {
|
|
179
|
+
const appData = apiCache.getAppData();
|
|
180
|
+
const locPath = this.pathJoin(data.folder, appData.webPath);
|
|
181
|
+
results = await this.getFolders(locPath);
|
|
182
|
+
}
|
|
183
|
+
const response = {
|
|
184
|
+
status: 'ok',
|
|
185
|
+
message: 'OK',
|
|
186
|
+
results,
|
|
187
|
+
};
|
|
188
|
+
ApiHelper.sendJson(req, res, response);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// called by clients
|
|
193
|
+
async download(req: ServerRequest, res: ServerResponse) {
|
|
194
|
+
const data = this.chkData(req.locals.json(), req, res, true);
|
|
195
|
+
if (!data) return true;
|
|
196
|
+
|
|
197
|
+
if (typeof data.resource === 'string') {
|
|
198
|
+
const resPath = path.join(data.resource);
|
|
199
|
+
if (await FsUtils.pathExist(resPath)) {
|
|
200
|
+
ApiHelper.sendFile(req, res, resPath);
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const response = {
|
|
205
|
+
status: 'error',
|
|
206
|
+
message: 'Resource not found',
|
|
207
|
+
};
|
|
208
|
+
ApiHelper.sendJson(req, res, response);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async rename(req: ServerRequest, res: ServerResponse) {
|
|
213
|
+
const data = this.chkData(req.locals.json(), req, res, true);
|
|
214
|
+
if (!data) return true;
|
|
215
|
+
|
|
216
|
+
if (typeof data.resource === 'string' && data.oldName && data.newName) {
|
|
217
|
+
const resPath = path.join(data.resource);
|
|
218
|
+
const basePath = path.dirname(resPath);
|
|
219
|
+
const oldPath = path.join(basePath, data.oldName);
|
|
220
|
+
if (await FsUtils.pathExist(oldPath)) {
|
|
221
|
+
const newPath = path.join(basePath, data.newName);
|
|
222
|
+
await FsUtils.rename(oldPath, newPath);
|
|
223
|
+
if (await FsUtils.pathExist(newPath)) {
|
|
224
|
+
const response = {
|
|
225
|
+
status: 'ok',
|
|
226
|
+
message: 'Resource renamed to ' + newPath,
|
|
227
|
+
};
|
|
228
|
+
ApiHelper.sendJson(req, res, response);
|
|
229
|
+
} else {
|
|
230
|
+
const response = {
|
|
231
|
+
status: 'error',
|
|
232
|
+
message: 'Resource not renamed',
|
|
233
|
+
};
|
|
234
|
+
ApiHelper.sendJson(req, res, response);
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const response = {
|
|
240
|
+
status: 'error',
|
|
241
|
+
message: 'Resource not found',
|
|
242
|
+
};
|
|
243
|
+
ApiHelper.sendJson(req, res, response);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async remove(req: ServerRequest, res: ServerResponse) {
|
|
248
|
+
const data = this.chkData(req.locals.json(), req, res, true);
|
|
249
|
+
if (!data) return true;
|
|
250
|
+
|
|
251
|
+
if (typeof data.resource === 'string') {
|
|
252
|
+
const resPath = path.join(data.resource);
|
|
253
|
+
if (await FsUtils.pathExist(resPath)) {
|
|
254
|
+
await FsUtils.unlinkFile(resPath);
|
|
255
|
+
const response = {
|
|
256
|
+
status: 'ok',
|
|
257
|
+
message: 'Resource removed',
|
|
258
|
+
};
|
|
259
|
+
ApiHelper.sendJson(req, res, response);
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const response = {
|
|
264
|
+
status: 'error',
|
|
265
|
+
message: 'Resource not found',
|
|
266
|
+
};
|
|
267
|
+
ApiHelper.sendJson(req, res, response);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async removeDir(req: ServerRequest, res: ServerResponse) {
|
|
272
|
+
const data = this.chkData(req.locals.json(), req, res, true);
|
|
273
|
+
if (!data) return true;
|
|
274
|
+
|
|
275
|
+
if (typeof data.resource === 'string') {
|
|
276
|
+
const resPath = path.join(data.resource);
|
|
277
|
+
if (await FsUtils.pathExist(resPath)) {
|
|
278
|
+
await FsUtils.unlinkFolderEmpty(resPath);
|
|
279
|
+
const response = {
|
|
280
|
+
status: 'ok',
|
|
281
|
+
message: 'Directory removed',
|
|
282
|
+
};
|
|
283
|
+
ApiHelper.sendJson(req, res, response);
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const response = {
|
|
288
|
+
status: 'error',
|
|
289
|
+
message: 'Directory not found or has files',
|
|
290
|
+
};
|
|
291
|
+
ApiHelper.sendJson(req, res, response);
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async newFolder(req: ServerRequest, res: ServerResponse) {
|
|
296
|
+
const data = this.chkData(req.locals.json(), req, res, true);
|
|
297
|
+
if (!data) return true;
|
|
298
|
+
|
|
299
|
+
if (typeof data.resource === 'string' && data.newName) {
|
|
300
|
+
const resPath = path.join(data.resource, data.newName);
|
|
301
|
+
if (!(await FsUtils.pathExist(resPath))) {
|
|
302
|
+
await FsUtils.mkdir(resPath);
|
|
303
|
+
const response = {
|
|
304
|
+
status: 'ok',
|
|
305
|
+
message: 'Folder created',
|
|
306
|
+
};
|
|
307
|
+
ApiHelper.sendJson(req, res, response);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const response = {
|
|
312
|
+
status: 'error',
|
|
313
|
+
message: 'Folder not created',
|
|
314
|
+
};
|
|
315
|
+
ApiHelper.sendJson(req, res, response);
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
}
|