lupine.api 1.1.58 → 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 -426
- 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
package/admin/admin-release.tsx
CHANGED
|
@@ -1,426 +1,427 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CssProps,
|
|
3
|
-
getRenderPageProps,
|
|
4
|
-
RefProps,
|
|
5
|
-
DomUtils,
|
|
6
|
-
HtmlVar,
|
|
7
|
-
NotificationColor,
|
|
8
|
-
NotificationMessage,
|
|
9
|
-
formatBytes,
|
|
10
|
-
downloadStream,
|
|
11
|
-
ActionSheetSelectPromise,
|
|
12
|
-
encodeHtml,
|
|
13
|
-
} from 'lupine.components';
|
|
14
|
-
|
|
15
|
-
interface ReleaseListProps {
|
|
16
|
-
result: any;
|
|
17
|
-
onUpdate: () => void;
|
|
18
|
-
onLogClick: (logName: string) => Promise<void>;
|
|
19
|
-
}
|
|
20
|
-
const ReleaseList = (props: ReleaseListProps) => {
|
|
21
|
-
const ref: RefProps = {
|
|
22
|
-
onLoad: async () => {
|
|
23
|
-
const data = JSON.parse(localStorage.getItem('admin-release') || '{}');
|
|
24
|
-
DomUtils.setValue('.from-list', data.fromList || '');
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
return (
|
|
28
|
-
<div ref={ref}>
|
|
29
|
-
<div class='row-box mt-m'>
|
|
30
|
-
<label class='label mr-m release-label'>From:</label>
|
|
31
|
-
<div class='w-50p'>
|
|
32
|
-
<select type='text' class='input-base w-100p from-list'>
|
|
33
|
-
{props.result.appsFrom.map((app: string) => (
|
|
34
|
-
<option key={app} value={app}>
|
|
35
|
-
{app}
|
|
36
|
-
</option>
|
|
37
|
-
))}
|
|
38
|
-
</select>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
<div class='row-box mt-m'>
|
|
42
|
-
<label class='label mr-m release-label'>To:</label>
|
|
43
|
-
<div class='w-50p'>
|
|
44
|
-
<select type='text' class='input-base w-100p to-list'>
|
|
45
|
-
{props.result.apps.map((app: string) => (
|
|
46
|
-
<option key={app} value={app}>
|
|
47
|
-
{app}
|
|
48
|
-
</option>
|
|
49
|
-
))}
|
|
50
|
-
</select>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<div class='row-box mt-m'>
|
|
54
|
-
<label class='label mr-m release-label'>Release:</label>
|
|
55
|
-
<label class='label mr-m' for='chk-server'>
|
|
56
|
-
Server:
|
|
57
|
-
</label>
|
|
58
|
-
<div class='mr-l'>
|
|
59
|
-
<input type='checkbox' class='base-css chk-server' id='chk-server' />
|
|
60
|
-
</div>
|
|
61
|
-
<label class='label mr-m' for='chk-api'>
|
|
62
|
-
Api:
|
|
63
|
-
</label>
|
|
64
|
-
<div class='mr-l'>
|
|
65
|
-
<input type='checkbox' class='base-css chk-api' id='chk-api' />
|
|
66
|
-
</div>
|
|
67
|
-
<label class='label mr-m' for='chk-web'>
|
|
68
|
-
Web:
|
|
69
|
-
</label>
|
|
70
|
-
<div class='mr-l'>
|
|
71
|
-
<input type='checkbox' class='base-css chk-web' id='chk-web' />
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
<label class='label mr-m' for='chk-env'>
|
|
75
|
-
Env:
|
|
76
|
-
</label>
|
|
77
|
-
<div class='mr-l'>
|
|
78
|
-
<input type='checkbox' class='base-css chk-env' id='chk-env' />
|
|
79
|
-
</div>
|
|
80
|
-
<label class='label mr-m' for='chk-backup'>
|
|
81
|
-
( Backup:
|
|
82
|
-
</label>
|
|
83
|
-
<div class=''>
|
|
84
|
-
<input type='checkbox' class='base-css chk-backup' id='chk-backup' /> )
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
<div class='row-box mt-m'>
|
|
88
|
-
<label class='label mr-m release-label'>Web Sub-folder:</label>
|
|
89
|
-
<div class='w-50p mr-l'>
|
|
90
|
-
{/* <input type='text' class='input-base w-100p input-web-sub' placeholder='The Sub-folder you want to update' /> */}
|
|
91
|
-
|
|
92
|
-
{props.result.webSub.map((folder: string) => (
|
|
93
|
-
<div>
|
|
94
|
-
<label>
|
|
95
|
-
<input type='checkbox' class={'chk-web-sub input-' + folder} value={folder} /> {folder}
|
|
96
|
-
</label>
|
|
97
|
-
</div>
|
|
98
|
-
))}
|
|
99
|
-
<label class='label mr-m release-label'>(Skip *.js.map files)</label>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
<LogList logs={props.result.logs} onLogClick={props.onLogClick} />
|
|
103
|
-
<div class='row-box mt-m'>
|
|
104
|
-
<button onClick={props.onUpdate} class='button-base release-update-btn'>
|
|
105
|
-
Update
|
|
106
|
-
</button>
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const LogList = (props: {
|
|
113
|
-
logs: { name: string; size: number; time: string }[];
|
|
114
|
-
onLogClick: (logName: string) => Promise<void>;
|
|
115
|
-
}) => {
|
|
116
|
-
return (
|
|
117
|
-
<div>
|
|
118
|
-
<div class='row-box mt-m'>
|
|
119
|
-
<label class='label mr-m release-label'>Logs:</label>
|
|
120
|
-
<div type='text'>
|
|
121
|
-
{props.logs &&
|
|
122
|
-
props.logs.map((log: { name: string; size: number; time: string }) => (
|
|
123
|
-
<div>
|
|
124
|
-
<label class='release-log' onClick={() => props.onLogClick(log.name)}>{`${log.name}`}</label> (
|
|
125
|
-
{log.time}; {formatBytes(log.size)}){' '}
|
|
126
|
-
</div>
|
|
127
|
-
))}
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
export const AdminReleasePage = () => {
|
|
135
|
-
const fetchData = async (options: { targetUrl: string; accessToken: string; log?: boolean }) => {
|
|
136
|
-
const data = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/check', options);
|
|
137
|
-
console.log('release/check', data);
|
|
138
|
-
return data.json;
|
|
139
|
-
};
|
|
140
|
-
const css: CssProps = {
|
|
141
|
-
'.release-label': {
|
|
142
|
-
width: '130px',
|
|
143
|
-
},
|
|
144
|
-
'.release-log': {
|
|
145
|
-
cursor: 'pointer',
|
|
146
|
-
textDecoration: 'underline',
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
const domLog = new HtmlVar('');
|
|
150
|
-
const domUpdate = new HtmlVar('');
|
|
151
|
-
const getDomData = () => {
|
|
152
|
-
const domFromList = ref.$('.from-list');
|
|
153
|
-
let fromValue = '';
|
|
154
|
-
if (!domFromList) {
|
|
155
|
-
const dataOld = JSON.parse(localStorage.getItem('admin-release') || '{}');
|
|
156
|
-
fromValue = dataOld.fromList;
|
|
157
|
-
} else {
|
|
158
|
-
fromValue = domFromList.value;
|
|
159
|
-
}
|
|
160
|
-
const data = {
|
|
161
|
-
targetUrl: ref.$('.target-url').value,
|
|
162
|
-
accessToken: ref.$('.access-token').value,
|
|
163
|
-
fromList: fromValue,
|
|
164
|
-
};
|
|
165
|
-
localStorage.setItem('admin-release', JSON.stringify(data));
|
|
166
|
-
return data;
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const onUpdate = async () => {
|
|
170
|
-
const data = getDomData();
|
|
171
|
-
if (!data.targetUrl || !data.accessToken) {
|
|
172
|
-
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const fromList = ref.$('.from-list').value;
|
|
177
|
-
const toList = ref.$('.to-list').value;
|
|
178
|
-
const chkServer = ref.$('.chk-server').checked;
|
|
179
|
-
const chkApi = ref.$('.chk-api').checked;
|
|
180
|
-
const chkWeb = ref.$('.chk-web').checked;
|
|
181
|
-
// const webSub = ref.$('.input-web-sub').value;
|
|
182
|
-
const webSubs = ref.$all('.chk-web-sub') as HTMLInputElement[];
|
|
183
|
-
const webSubsChecked = Array.from(webSubs)
|
|
184
|
-
.filter((input) => input.checked)
|
|
185
|
-
.map((input) => input.value);
|
|
186
|
-
const wrongWebSubs = webSubsChecked.filter((s) => !s.startsWith(fromList + '_web/'));
|
|
187
|
-
if (wrongWebSubs.length > 0) {
|
|
188
|
-
NotificationMessage.sendMessage(`Some web sub folder is not under ${fromList}`, NotificationColor.Error);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
const chkEnv = ref.$('.chk-env').checked;
|
|
192
|
-
const chkBackup = ref.$('.chk-backup').checked;
|
|
193
|
-
if (!chkServer && !chkApi &&
|
|
194
|
-
NotificationMessage.sendMessage('Please select the release options', NotificationColor.Error);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (fromList !== toList && !confirm('The From and To are not the same, are you sure?')) {
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (!confirm('Are you sure you want to update the release? (Assets are not copied, so it may cause issues)')) {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
const releaseUpdateBtn = document.querySelector('.release-update-btn') as HTMLButtonElement;
|
|
205
|
-
releaseUpdateBtn.disabled = true;
|
|
206
|
-
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/update', {
|
|
207
|
-
...data,
|
|
208
|
-
fromList,
|
|
209
|
-
toList,
|
|
210
|
-
chkServer,
|
|
211
|
-
chkApi,
|
|
212
|
-
chkWeb,
|
|
213
|
-
// webSub, // will be deprecated
|
|
214
|
-
webSubs: webSubsChecked,
|
|
215
|
-
chkEnv,
|
|
216
|
-
chkBackup,
|
|
217
|
-
});
|
|
218
|
-
const dataResponse = await response.json;
|
|
219
|
-
console.log('release/update', dataResponse);
|
|
220
|
-
releaseUpdateBtn.disabled = false;
|
|
221
|
-
if (!dataResponse || dataResponse.status !== 'ok') {
|
|
222
|
-
NotificationMessage.sendMessage(
|
|
223
|
-
dataResponse.message ||
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
DomUtils.setValue('.
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
<
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
{
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
1
|
+
import {
|
|
2
|
+
CssProps,
|
|
3
|
+
getRenderPageProps,
|
|
4
|
+
RefProps,
|
|
5
|
+
DomUtils,
|
|
6
|
+
HtmlVar,
|
|
7
|
+
NotificationColor,
|
|
8
|
+
NotificationMessage,
|
|
9
|
+
formatBytes,
|
|
10
|
+
downloadStream,
|
|
11
|
+
ActionSheetSelectPromise,
|
|
12
|
+
encodeHtml,
|
|
13
|
+
} from 'lupine.components';
|
|
14
|
+
|
|
15
|
+
interface ReleaseListProps {
|
|
16
|
+
result: any;
|
|
17
|
+
onUpdate: () => void;
|
|
18
|
+
onLogClick: (logName: string) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
const ReleaseList = (props: ReleaseListProps) => {
|
|
21
|
+
const ref: RefProps = {
|
|
22
|
+
onLoad: async () => {
|
|
23
|
+
const data = JSON.parse(localStorage.getItem('admin-release') || '{}');
|
|
24
|
+
DomUtils.setValue('.from-list', data.fromList || '');
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
return (
|
|
28
|
+
<div ref={ref}>
|
|
29
|
+
<div class='row-box mt-m'>
|
|
30
|
+
<label class='label mr-m release-label'>From:</label>
|
|
31
|
+
<div class='w-50p'>
|
|
32
|
+
<select type='text' class='input-base w-100p from-list'>
|
|
33
|
+
{props.result.appsFrom.map((app: string) => (
|
|
34
|
+
<option key={app} value={app}>
|
|
35
|
+
{app}
|
|
36
|
+
</option>
|
|
37
|
+
))}
|
|
38
|
+
</select>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class='row-box mt-m'>
|
|
42
|
+
<label class='label mr-m release-label'>To:</label>
|
|
43
|
+
<div class='w-50p'>
|
|
44
|
+
<select type='text' class='input-base w-100p to-list'>
|
|
45
|
+
{props.result.apps.map((app: string) => (
|
|
46
|
+
<option key={app} value={app}>
|
|
47
|
+
{app}
|
|
48
|
+
</option>
|
|
49
|
+
))}
|
|
50
|
+
</select>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div class='row-box mt-m'>
|
|
54
|
+
<label class='label mr-m release-label'>Release:</label>
|
|
55
|
+
<label class='label mr-m' for='chk-server'>
|
|
56
|
+
Server:
|
|
57
|
+
</label>
|
|
58
|
+
<div class='mr-l'>
|
|
59
|
+
<input type='checkbox' class='base-css chk-server' id='chk-server' />
|
|
60
|
+
</div>
|
|
61
|
+
<label class='label mr-m' for='chk-api'>
|
|
62
|
+
Api:
|
|
63
|
+
</label>
|
|
64
|
+
<div class='mr-l'>
|
|
65
|
+
<input type='checkbox' class='base-css chk-api' id='chk-api' />
|
|
66
|
+
</div>
|
|
67
|
+
{/* <label class='label mr-m' for='chk-web'>
|
|
68
|
+
Web:
|
|
69
|
+
</label>
|
|
70
|
+
<div class='mr-l'>
|
|
71
|
+
<input type='checkbox' class='base-css chk-web' id='chk-web' />
|
|
72
|
+
</div> */}
|
|
73
|
+
|
|
74
|
+
<label class='label mr-m' for='chk-env'>
|
|
75
|
+
Env:
|
|
76
|
+
</label>
|
|
77
|
+
<div class='mr-l'>
|
|
78
|
+
<input type='checkbox' class='base-css chk-env' id='chk-env' />
|
|
79
|
+
</div>
|
|
80
|
+
<label class='label mr-m' for='chk-backup'>
|
|
81
|
+
( Backup:
|
|
82
|
+
</label>
|
|
83
|
+
<div class=''>
|
|
84
|
+
<input type='checkbox' class='base-css chk-backup' id='chk-backup' /> )
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div class='row-box mt-m'>
|
|
88
|
+
<label class='label mr-m release-label'>Web Sub-folder:</label>
|
|
89
|
+
<div class='w-50p mr-l'>
|
|
90
|
+
{/* <input type='text' class='input-base w-100p input-web-sub' placeholder='The Sub-folder you want to update' /> */}
|
|
91
|
+
|
|
92
|
+
{props.result.webSub.map((folder: string) => (
|
|
93
|
+
<div>
|
|
94
|
+
<label>
|
|
95
|
+
<input type='checkbox' class={'chk-web-sub input-' + folder} value={folder} /> {folder}
|
|
96
|
+
</label>
|
|
97
|
+
</div>
|
|
98
|
+
))}
|
|
99
|
+
<label class='label mr-m release-label'>(Skip *.js.map, *.css.map files)</label>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<LogList logs={props.result.logs} onLogClick={props.onLogClick} />
|
|
103
|
+
<div class='row-box mt-m'>
|
|
104
|
+
<button onClick={props.onUpdate} class='button-base release-update-btn'>
|
|
105
|
+
Update
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const LogList = (props: {
|
|
113
|
+
logs: { name: string; size: number; time: string }[];
|
|
114
|
+
onLogClick: (logName: string) => Promise<void>;
|
|
115
|
+
}) => {
|
|
116
|
+
return (
|
|
117
|
+
<div>
|
|
118
|
+
<div class='row-box mt-m'>
|
|
119
|
+
<label class='label mr-m release-label'>Logs:</label>
|
|
120
|
+
<div type='text'>
|
|
121
|
+
{props.logs &&
|
|
122
|
+
props.logs.map((log: { name: string; size: number; time: string }) => (
|
|
123
|
+
<div>
|
|
124
|
+
<label class='release-log' onClick={() => props.onLogClick(log.name)}>{`${log.name}`}</label> (
|
|
125
|
+
{log.time}; {formatBytes(log.size)}){' '}
|
|
126
|
+
</div>
|
|
127
|
+
))}
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const AdminReleasePage = () => {
|
|
135
|
+
const fetchData = async (options: { targetUrl: string; accessToken: string; log?: boolean }) => {
|
|
136
|
+
const data = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/check', options);
|
|
137
|
+
console.log('release/check', data);
|
|
138
|
+
return data.json;
|
|
139
|
+
};
|
|
140
|
+
const css: CssProps = {
|
|
141
|
+
'.release-label': {
|
|
142
|
+
width: '130px',
|
|
143
|
+
},
|
|
144
|
+
'.release-log': {
|
|
145
|
+
cursor: 'pointer',
|
|
146
|
+
textDecoration: 'underline',
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
const domLog = new HtmlVar('');
|
|
150
|
+
const domUpdate = new HtmlVar('');
|
|
151
|
+
const getDomData = () => {
|
|
152
|
+
const domFromList = ref.$('.from-list');
|
|
153
|
+
let fromValue = '';
|
|
154
|
+
if (!domFromList) {
|
|
155
|
+
const dataOld = JSON.parse(localStorage.getItem('admin-release') || '{}');
|
|
156
|
+
fromValue = dataOld.fromList;
|
|
157
|
+
} else {
|
|
158
|
+
fromValue = domFromList.value;
|
|
159
|
+
}
|
|
160
|
+
const data = {
|
|
161
|
+
targetUrl: ref.$('.target-url').value,
|
|
162
|
+
accessToken: ref.$('.access-token').value,
|
|
163
|
+
fromList: fromValue,
|
|
164
|
+
};
|
|
165
|
+
localStorage.setItem('admin-release', JSON.stringify(data));
|
|
166
|
+
return data;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const onUpdate = async () => {
|
|
170
|
+
const data = getDomData();
|
|
171
|
+
if (!data.targetUrl || !data.accessToken) {
|
|
172
|
+
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const fromList = ref.$('.from-list').value;
|
|
177
|
+
const toList = ref.$('.to-list').value;
|
|
178
|
+
const chkServer = ref.$('.chk-server').checked;
|
|
179
|
+
const chkApi = ref.$('.chk-api').checked;
|
|
180
|
+
const chkWeb = true; //ref.$('.chk-web').checked;
|
|
181
|
+
// const webSub = ref.$('.input-web-sub').value;
|
|
182
|
+
const webSubs = ref.$all('.chk-web-sub') as HTMLInputElement[];
|
|
183
|
+
const webSubsChecked = Array.from(webSubs)
|
|
184
|
+
.filter((input) => input.checked)
|
|
185
|
+
.map((input) => input.value);
|
|
186
|
+
const wrongWebSubs = webSubsChecked.filter((s) => !s.startsWith(fromList + '_web/'));
|
|
187
|
+
if (wrongWebSubs.length > 0) {
|
|
188
|
+
NotificationMessage.sendMessage(`Some web sub folder is not under ${fromList}`, NotificationColor.Error);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const chkEnv = ref.$('.chk-env').checked;
|
|
192
|
+
const chkBackup = ref.$('.chk-backup').checked;
|
|
193
|
+
if (!chkServer && !chkApi && webSubsChecked.length < 1 && !chkEnv) {
|
|
194
|
+
NotificationMessage.sendMessage('Please select the release options', NotificationColor.Error);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (fromList !== toList && !confirm('The From and To are not the same, are you sure?')) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (!confirm('Are you sure you want to update the release? (Assets are not copied, so it may cause issues)')) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const releaseUpdateBtn = document.querySelector('.release-update-btn') as HTMLButtonElement;
|
|
205
|
+
releaseUpdateBtn.disabled = true;
|
|
206
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/update', {
|
|
207
|
+
...data,
|
|
208
|
+
fromList,
|
|
209
|
+
toList,
|
|
210
|
+
chkServer,
|
|
211
|
+
chkApi,
|
|
212
|
+
chkWeb,
|
|
213
|
+
// webSub, // will be deprecated
|
|
214
|
+
webSubs: webSubsChecked,
|
|
215
|
+
chkEnv,
|
|
216
|
+
chkBackup,
|
|
217
|
+
});
|
|
218
|
+
const dataResponse = await response.json;
|
|
219
|
+
console.log('release/update', dataResponse);
|
|
220
|
+
releaseUpdateBtn.disabled = false;
|
|
221
|
+
if (!dataResponse || dataResponse.status !== 'ok') {
|
|
222
|
+
NotificationMessage.sendMessage(
|
|
223
|
+
dataResponse.message ||
|
|
224
|
+
'Failed to update release (timeout, possibly backend is runing, please wait and click Check!)',
|
|
225
|
+
NotificationColor.Error
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
NotificationMessage.sendMessage('Release updated successfully', NotificationColor.Success);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const onLogClick = async (logName: string) => {
|
|
233
|
+
const data = getDomData();
|
|
234
|
+
if (!data.targetUrl || !data.accessToken) {
|
|
235
|
+
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const responseText = await getRenderPageProps().renderPageFunctions.fetchData(
|
|
240
|
+
'/api/admin/release/view-log',
|
|
241
|
+
{
|
|
242
|
+
...data,
|
|
243
|
+
logName,
|
|
244
|
+
},
|
|
245
|
+
true
|
|
246
|
+
);
|
|
247
|
+
const blob = await responseText.blob();
|
|
248
|
+
downloadStream(blob, logName);
|
|
249
|
+
};
|
|
250
|
+
const onCheck = async () => {
|
|
251
|
+
const data = getDomData();
|
|
252
|
+
if (!data.targetUrl || !data.accessToken) {
|
|
253
|
+
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const result = await fetchData(data);
|
|
257
|
+
if (!result || result.status !== 'ok') {
|
|
258
|
+
NotificationMessage.sendMessage(result.message || 'Failed to get release list', NotificationColor.Error);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
console.log(result);
|
|
262
|
+
|
|
263
|
+
domUpdate.value = <ReleaseList result={result} onUpdate={onUpdate} onLogClick={onLogClick} />;
|
|
264
|
+
domLog.value = <pre>{JSON.stringify(result, null, 2)}</pre>;
|
|
265
|
+
if (result.releaseProgress) {
|
|
266
|
+
NotificationMessage.sendMessage('Release progress: ' + result.releaseProgress, NotificationColor.Warning);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const onRefreshCacheLocal = async () => {
|
|
271
|
+
return onRefreshCache(true);
|
|
272
|
+
};
|
|
273
|
+
const onRefreshCacheRemote = async () => {
|
|
274
|
+
return onRefreshCache(false);
|
|
275
|
+
};
|
|
276
|
+
const onRefreshCache = async (isLocal?: boolean) => {
|
|
277
|
+
const data = getDomData();
|
|
278
|
+
if (!isLocal) {
|
|
279
|
+
if (!data.targetUrl || !data.accessToken) {
|
|
280
|
+
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/refresh-cache', {
|
|
286
|
+
...data,
|
|
287
|
+
isLocal,
|
|
288
|
+
});
|
|
289
|
+
const dataResponse = await response.json;
|
|
290
|
+
console.log('refresh-cache', dataResponse);
|
|
291
|
+
if (!dataResponse || dataResponse.status !== 'ok') {
|
|
292
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to refresh cache', NotificationColor.Error);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
domLog.value = <pre>{encodeHtml(JSON.stringify(dataResponse, null, 2))}</pre>;
|
|
296
|
+
NotificationMessage.sendMessage('Cache refreshed successfully', NotificationColor.Success);
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const onRestartAppLocal = async () => {
|
|
300
|
+
return onRestartApp(true);
|
|
301
|
+
};
|
|
302
|
+
const onRestartAppRemote = async () => {
|
|
303
|
+
return onRestartApp(false);
|
|
304
|
+
};
|
|
305
|
+
const onRestartApp = async (isLocal?: boolean) => {
|
|
306
|
+
const data = getDomData();
|
|
307
|
+
if (!isLocal) {
|
|
308
|
+
if (!data.targetUrl || !data.accessToken) {
|
|
309
|
+
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const index = await ActionSheetSelectPromise({
|
|
315
|
+
title: 'Restart App (users may get disconnected errors) ?',
|
|
316
|
+
options: ['OK'],
|
|
317
|
+
cancelButtonText: 'Cancel',
|
|
318
|
+
});
|
|
319
|
+
if (index !== 0) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/restart-app', {
|
|
324
|
+
...data,
|
|
325
|
+
isLocal,
|
|
326
|
+
});
|
|
327
|
+
const dataResponse = await response.json;
|
|
328
|
+
console.log('restart-app', dataResponse);
|
|
329
|
+
if (!dataResponse || dataResponse.status !== 'ok') {
|
|
330
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to Restart App', NotificationColor.Error);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
domLog.value = <pre>{JSON.stringify(dataResponse, null, 2)}</pre>;
|
|
334
|
+
NotificationMessage.sendMessage('Restart App successfully', NotificationColor.Success);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const onShellLocal = async () => {
|
|
338
|
+
return onShell(true);
|
|
339
|
+
};
|
|
340
|
+
const onShellRemote = async () => {
|
|
341
|
+
return onShell(false);
|
|
342
|
+
};
|
|
343
|
+
const onShell = async (isLocal?: boolean) => {
|
|
344
|
+
const data = getDomData();
|
|
345
|
+
if (!isLocal) {
|
|
346
|
+
if (!data.targetUrl || !data.accessToken) {
|
|
347
|
+
NotificationMessage.sendMessage('Please fill in all fields', NotificationColor.Error);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const index = await ActionSheetSelectPromise({
|
|
353
|
+
title: 'Run Cmd ?',
|
|
354
|
+
options: ['OK'],
|
|
355
|
+
cancelButtonText: 'Cancel',
|
|
356
|
+
});
|
|
357
|
+
if (index !== 0) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/release/shell', {
|
|
362
|
+
...data,
|
|
363
|
+
isLocal,
|
|
364
|
+
cmd: ref.$('.release-cmd').value,
|
|
365
|
+
});
|
|
366
|
+
const dataResponse = await response.json;
|
|
367
|
+
console.log('shell', dataResponse);
|
|
368
|
+
if (!dataResponse || dataResponse.status !== 'ok') {
|
|
369
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to run cmd', NotificationColor.Error);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
domLog.value = <pre>{encodeHtml(dataResponse.message) + '\r\n<br>' + encodeHtml(dataResponse.result)}</pre>;
|
|
373
|
+
NotificationMessage.sendMessage('Restart App successfully', NotificationColor.Success);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const ref: RefProps = {
|
|
377
|
+
onLoad: async () => {
|
|
378
|
+
const data = JSON.parse(localStorage.getItem('admin-release') || '{}');
|
|
379
|
+
DomUtils.setValue('.target-url', data.targetUrl || '');
|
|
380
|
+
DomUtils.setValue('.access-token', data.accessToken || '');
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
return (
|
|
384
|
+
<div ref={ref} css={css} class='admin-release-top'>
|
|
385
|
+
<div class='row-box mt-m'>
|
|
386
|
+
<label class='label mr-m release-label'>Target Url:</label>
|
|
387
|
+
<div class='w-50p'>
|
|
388
|
+
<input type='text' class='input-base w-100p target-url' placeholder='Target Url' />
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
<div class='row-box mt-m'>
|
|
392
|
+
<label class='label mr-m release-label'>Access token:</label>
|
|
393
|
+
<div class='w-50p'>
|
|
394
|
+
<input type='text' class='input-base w-100p access-token' placeholder='Access token' />
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
<div class='row-box mt-m'>
|
|
398
|
+
<button onClick={onCheck} class='button-base mr-m'>
|
|
399
|
+
Check
|
|
400
|
+
</button>
|
|
401
|
+
<button onClick={onRefreshCacheRemote} class='button-base mr-m'>
|
|
402
|
+
Refresh Cache (Remote)
|
|
403
|
+
</button>
|
|
404
|
+
<button onClick={onRestartAppRemote} class='button-base mr-m color-red'>
|
|
405
|
+
Restart App (Remote)
|
|
406
|
+
</button>
|
|
407
|
+
<button onClick={onRefreshCacheLocal} class='button-base mr-m'>
|
|
408
|
+
Refresh Cache (Local)
|
|
409
|
+
</button>
|
|
410
|
+
<button onClick={onRestartAppLocal} class='button-base color-red'>
|
|
411
|
+
Restart App (Local)
|
|
412
|
+
</button>
|
|
413
|
+
</div>
|
|
414
|
+
<div class='row-box mt-m mb-m'>
|
|
415
|
+
<input type='text' class='input-base w-50p release-cmd mr-m' placeholder='Command' />
|
|
416
|
+
<button onClick={onShellRemote} class='button-base color-red mr-m'>
|
|
417
|
+
Run Cmd (Remote)
|
|
418
|
+
</button>
|
|
419
|
+
<button onClick={onShellLocal} class='button-base'>
|
|
420
|
+
Run Cmd (Local)
|
|
421
|
+
</button>
|
|
422
|
+
</div>
|
|
423
|
+
{domUpdate.node}
|
|
424
|
+
{domLog.node}
|
|
425
|
+
</div>
|
|
426
|
+
);
|
|
427
|
+
};
|