lupine.api 1.1.58 → 1.1.60
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 +706 -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-tokens.tsx
CHANGED
|
@@ -1,338 +1,338 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CssProps,
|
|
3
|
-
getRenderPageProps,
|
|
4
|
-
RefProps,
|
|
5
|
-
EditableLabel,
|
|
6
|
-
HtmlVar,
|
|
7
|
-
ModalWindow,
|
|
8
|
-
NotificationColor,
|
|
9
|
-
NotificationMessage,
|
|
10
|
-
PagingLink,
|
|
11
|
-
} from 'lupine.components';
|
|
12
|
-
|
|
13
|
-
export type TokenProps = {
|
|
14
|
-
token: string;
|
|
15
|
-
description: string;
|
|
16
|
-
timestamp?: number;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const getTokenData = async (pageIndex = 0, searchText: string = '') => {
|
|
20
|
-
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/list', {
|
|
21
|
-
q: searchText,
|
|
22
|
-
});
|
|
23
|
-
const dataResponse = await response.json;
|
|
24
|
-
let pageLimit = 15;
|
|
25
|
-
let tokenList: TokenProps[] = [];
|
|
26
|
-
if (dataResponse && dataResponse.status === 'ok') {
|
|
27
|
-
tokenList = dataResponse.result;
|
|
28
|
-
pageLimit = dataResponse.pageLimit;
|
|
29
|
-
tokenList;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const itemsCount = tokenList.length;
|
|
33
|
-
let maxPages = Math.floor(itemsCount / pageLimit);
|
|
34
|
-
if (itemsCount % pageLimit !== 0) {
|
|
35
|
-
maxPages++;
|
|
36
|
-
}
|
|
37
|
-
if (pageIndex > maxPages - 1) {
|
|
38
|
-
pageIndex = maxPages - 1;
|
|
39
|
-
}
|
|
40
|
-
const offset = pageIndex * pageLimit;
|
|
41
|
-
return {
|
|
42
|
-
status: 'ok',
|
|
43
|
-
itemsCount,
|
|
44
|
-
pageIndex,
|
|
45
|
-
pageLimit,
|
|
46
|
-
result: tokenList.slice(offset, offset + pageLimit),
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
const generateToken = async () => {
|
|
50
|
-
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/generate');
|
|
51
|
-
const dataResponse = await response.json;
|
|
52
|
-
if (dataResponse && dataResponse.status === 'ok') {
|
|
53
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Token generated successfully', NotificationColor.Success);
|
|
54
|
-
} else {
|
|
55
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Failed to generate token', NotificationColor.Error);
|
|
56
|
-
}
|
|
57
|
-
return dataResponse.result;
|
|
58
|
-
};
|
|
59
|
-
const addTokenData = async (item: TokenProps) => {
|
|
60
|
-
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/add', { ...item });
|
|
61
|
-
const dataResponse = await response.json;
|
|
62
|
-
if (dataResponse && dataResponse.status === 'ok') {
|
|
63
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Token added successfully', NotificationColor.Success);
|
|
64
|
-
} else {
|
|
65
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Failed to add token', NotificationColor.Error);
|
|
66
|
-
}
|
|
67
|
-
return { ...item };
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const removeTokenData = async (token: string) => {
|
|
71
|
-
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/remove', { token });
|
|
72
|
-
const dataResponse = await response.json;
|
|
73
|
-
if (dataResponse && dataResponse.status === 'ok') {
|
|
74
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Token removed successfully', NotificationColor.Success);
|
|
75
|
-
} else {
|
|
76
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Failed to remove token', NotificationColor.Error);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
const updateTokenData = async (item: TokenProps) => {
|
|
80
|
-
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/update', { ...item });
|
|
81
|
-
const dataResponse = await response.json;
|
|
82
|
-
if (dataResponse && dataResponse.status === 'ok') {
|
|
83
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Token updated successfully', NotificationColor.Success);
|
|
84
|
-
} else {
|
|
85
|
-
NotificationMessage.sendMessage(dataResponse.message || 'Failed to update token', NotificationColor.Error);
|
|
86
|
-
}
|
|
87
|
-
return { ...item };
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
type TokenDataUpdateProps = {
|
|
91
|
-
save?: (isNew: boolean) => Promise<TokenProps | null>;
|
|
92
|
-
cancel?: () => void;
|
|
93
|
-
};
|
|
94
|
-
// show dialog to edit one item, and call update when save the result
|
|
95
|
-
const showTokenEditItem = async (item: TokenProps, update: (item: TokenProps) => void, isNew: boolean) => {
|
|
96
|
-
const handleClicked = async (index: number) => {
|
|
97
|
-
if (index === 1) {
|
|
98
|
-
updateFn.cancel?.();
|
|
99
|
-
modalClose();
|
|
100
|
-
}
|
|
101
|
-
if (index === 0) {
|
|
102
|
-
const newItem = await updateFn.save?.(isNew);
|
|
103
|
-
if (newItem) {
|
|
104
|
-
update(newItem);
|
|
105
|
-
modalClose();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
const updateFn: TokenDataUpdateProps = {};
|
|
110
|
-
const modalClose = await ModalWindow.show({
|
|
111
|
-
title: 'Edit Token Data',
|
|
112
|
-
buttons: ['Save', 'Cancel'],
|
|
113
|
-
// contentMaxHeight: '400px',
|
|
114
|
-
handleClicked,
|
|
115
|
-
children: <TokenEditItem item={item} update={updateFn}></TokenEditItem>,
|
|
116
|
-
closeWhenClickOutside: false,
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// edit one item
|
|
121
|
-
export const TokenEditItem = (props: { item: TokenProps; update: TokenDataUpdateProps }) => {
|
|
122
|
-
const ref: RefProps = { id: '' };
|
|
123
|
-
const css: CssProps = {
|
|
124
|
-
padding: '10px',
|
|
125
|
-
border: 'solid 1px gray',
|
|
126
|
-
margin: '1px',
|
|
127
|
-
position: 'relative',
|
|
128
|
-
'.lable': {
|
|
129
|
-
width: '90px',
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
props.update.save = async (isNew: boolean) => {
|
|
133
|
-
const token = ref.$('input.token').value;
|
|
134
|
-
const description = ref.$('input.description').value;
|
|
135
|
-
if (token && description) {
|
|
136
|
-
const newItem = isNew
|
|
137
|
-
? await addTokenData({ token, description })
|
|
138
|
-
: await updateTokenData({ token, description });
|
|
139
|
-
props.item.token = newItem.token;
|
|
140
|
-
props.item.description = newItem.description;
|
|
141
|
-
return newItem;
|
|
142
|
-
}
|
|
143
|
-
NotificationMessage.sendMessage('Please input token and description', NotificationColor.Error);
|
|
144
|
-
return null;
|
|
145
|
-
};
|
|
146
|
-
return (
|
|
147
|
-
<div ref={ref} css={css} class='sample-data'>
|
|
148
|
-
<div class='row-box mt-m'>
|
|
149
|
-
<div class='lable'>Token: </div>
|
|
150
|
-
<div>
|
|
151
|
-
<input type='text' class='input-base token' value={props.item.token} readonly={true} />
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
<div class='row-box'>
|
|
155
|
-
<div class='lable'>Description: </div>
|
|
156
|
-
<div>
|
|
157
|
-
<input type='text' class='input-base description' value={props.item.description} />
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
</div>
|
|
161
|
-
);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// show one item
|
|
165
|
-
export const TokenShowItem = (props: { item: TokenProps }) => {
|
|
166
|
-
const ref: RefProps = {};
|
|
167
|
-
const css: CssProps = {
|
|
168
|
-
padding: '10px',
|
|
169
|
-
border: 'solid 1px gray',
|
|
170
|
-
margin: '1px',
|
|
171
|
-
position: 'relative',
|
|
172
|
-
'.control-box': {
|
|
173
|
-
display: 'none',
|
|
174
|
-
position: 'absolute',
|
|
175
|
-
right: '10px',
|
|
176
|
-
top: '10px',
|
|
177
|
-
},
|
|
178
|
-
'&:hover .control-box': {
|
|
179
|
-
display: 'block',
|
|
180
|
-
},
|
|
181
|
-
'.lable': {
|
|
182
|
-
width: '85px',
|
|
183
|
-
},
|
|
184
|
-
'.token': {
|
|
185
|
-
lineBreak: 'anywhere',
|
|
186
|
-
wordBreak: 'break-all',
|
|
187
|
-
},
|
|
188
|
-
};
|
|
189
|
-
const onEdit = (ev: any) => {
|
|
190
|
-
const update = (item: TokenProps) => {
|
|
191
|
-
dom.value = makeDom(item);
|
|
192
|
-
};
|
|
193
|
-
showTokenEditItem(props.item, update, false);
|
|
194
|
-
};
|
|
195
|
-
const onRemove = async (ev: any) => {
|
|
196
|
-
if (!confirm('Are you sure you want to delete this token?')) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
await removeTokenData(props.item.token);
|
|
200
|
-
ref.current.remove();
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const makeDom = (item: TokenProps) => {
|
|
204
|
-
const saveText = (text: string) => {
|
|
205
|
-
item.description = text;
|
|
206
|
-
updateTokenData(item);
|
|
207
|
-
dom.value = makeDom(item);
|
|
208
|
-
};
|
|
209
|
-
return (
|
|
210
|
-
<div>
|
|
211
|
-
<div class='control-box'>
|
|
212
|
-
<button class='button-base button-ss' onClick={onEdit}>
|
|
213
|
-
Edit
|
|
214
|
-
</button>
|
|
215
|
-
<button class='button-base button-ss' onClick={onRemove}>
|
|
216
|
-
Delete
|
|
217
|
-
</button>
|
|
218
|
-
</div>
|
|
219
|
-
<div class='row-box'>
|
|
220
|
-
<div class='lable'>Token: </div>
|
|
221
|
-
<div class='token'>{item.token}</div>
|
|
222
|
-
</div>
|
|
223
|
-
<div class='row-box'>
|
|
224
|
-
<div class='lable'>Description: </div>
|
|
225
|
-
<div class='flex-1'>
|
|
226
|
-
<EditableLabel text={item.description} save={saveText} type='text' />
|
|
227
|
-
</div>
|
|
228
|
-
</div>
|
|
229
|
-
<div class='row-box'>
|
|
230
|
-
<div class='lable'>Timestamp: </div>
|
|
231
|
-
<div>{new Date(item.timestamp || 0).toLocaleString()}</div>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
const dom = new HtmlVar(makeDom(props.item));
|
|
238
|
-
return (
|
|
239
|
-
<div ref={ref} css={css} class='sample-data'>
|
|
240
|
-
{dom.node}
|
|
241
|
-
</div>
|
|
242
|
-
);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// show the list
|
|
246
|
-
const TokenList = () => {
|
|
247
|
-
let currentIndex = 0;
|
|
248
|
-
let searchText = '';
|
|
249
|
-
let maxPages = 0;
|
|
250
|
-
const ref: RefProps = {
|
|
251
|
-
onLoad: async (self: Element) => {
|
|
252
|
-
await makeList(currentIndex);
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
const onAdd = async () => {
|
|
256
|
-
const update = async (item: TokenProps) => {
|
|
257
|
-
// new item is added at the list end, so update the last page
|
|
258
|
-
await makeList(maxPages + 1);
|
|
259
|
-
};
|
|
260
|
-
showTokenEditItem(
|
|
261
|
-
{
|
|
262
|
-
token: await generateToken(),
|
|
263
|
-
description: '',
|
|
264
|
-
},
|
|
265
|
-
update,
|
|
266
|
-
true
|
|
267
|
-
);
|
|
268
|
-
};
|
|
269
|
-
const onSearch = async () => {
|
|
270
|
-
searchText = ref.$('input.search').value.trim();
|
|
271
|
-
await makeList(currentIndex);
|
|
272
|
-
};
|
|
273
|
-
const makeList = async (pageIndex: number) => {
|
|
274
|
-
const onLinkClick = async (index: number) => {
|
|
275
|
-
currentIndex = index;
|
|
276
|
-
await makeList(currentIndex);
|
|
277
|
-
};
|
|
278
|
-
const items = await getTokenData(pageIndex, searchText);
|
|
279
|
-
maxPages = Math.floor((items.itemsCount + 1) / items.pageLimit);
|
|
280
|
-
listDom.value = (
|
|
281
|
-
<div>
|
|
282
|
-
<PagingLink
|
|
283
|
-
itemsCount={items.itemsCount}
|
|
284
|
-
pageIndex={pageIndex}
|
|
285
|
-
pageLimit={items.pageLimit}
|
|
286
|
-
onClick={onLinkClick}
|
|
287
|
-
baseLink=''
|
|
288
|
-
></PagingLink>
|
|
289
|
-
{items.result.map((item) => (
|
|
290
|
-
<TokenShowItem item={item}></TokenShowItem>
|
|
291
|
-
))}
|
|
292
|
-
<PagingLink
|
|
293
|
-
itemsCount={items.itemsCount}
|
|
294
|
-
pageIndex={pageIndex}
|
|
295
|
-
pageLimit={items.pageLimit}
|
|
296
|
-
onClick={onLinkClick}
|
|
297
|
-
baseLink=''
|
|
298
|
-
></PagingLink>
|
|
299
|
-
</div>
|
|
300
|
-
);
|
|
301
|
-
};
|
|
302
|
-
const listDom = new HtmlVar(<div></div>);
|
|
303
|
-
const css: CssProps = {
|
|
304
|
-
display: 'flex',
|
|
305
|
-
flexDirection: 'column',
|
|
306
|
-
'.label': {
|
|
307
|
-
width: '70px',
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
return (
|
|
311
|
-
<div ref={ref} css={css}>
|
|
312
|
-
<div>
|
|
313
|
-
<div class='row-box'>
|
|
314
|
-
<div class='label'>Search: </div>
|
|
315
|
-
<input type='text' class='input-base search' />
|
|
316
|
-
<button class='button-base mr-s' onClick={onSearch}>
|
|
317
|
-
Search
|
|
318
|
-
</button>
|
|
319
|
-
<button class='button-base' onClick={onAdd}>
|
|
320
|
-
Add
|
|
321
|
-
</button>
|
|
322
|
-
</div>
|
|
323
|
-
</div>
|
|
324
|
-
<div class='list'>{listDom.node}</div>
|
|
325
|
-
</div>
|
|
326
|
-
);
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
export const AdminTokensPage = () => {
|
|
330
|
-
return (
|
|
331
|
-
<div>
|
|
332
|
-
<div>
|
|
333
|
-
<div>Tokens management</div>
|
|
334
|
-
<TokenList />
|
|
335
|
-
</div>
|
|
336
|
-
</div>
|
|
337
|
-
);
|
|
338
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
CssProps,
|
|
3
|
+
getRenderPageProps,
|
|
4
|
+
RefProps,
|
|
5
|
+
EditableLabel,
|
|
6
|
+
HtmlVar,
|
|
7
|
+
ModalWindow,
|
|
8
|
+
NotificationColor,
|
|
9
|
+
NotificationMessage,
|
|
10
|
+
PagingLink,
|
|
11
|
+
} from 'lupine.components';
|
|
12
|
+
|
|
13
|
+
export type TokenProps = {
|
|
14
|
+
token: string;
|
|
15
|
+
description: string;
|
|
16
|
+
timestamp?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getTokenData = async (pageIndex = 0, searchText: string = '') => {
|
|
20
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/list', {
|
|
21
|
+
q: searchText,
|
|
22
|
+
});
|
|
23
|
+
const dataResponse = await response.json;
|
|
24
|
+
let pageLimit = 15;
|
|
25
|
+
let tokenList: TokenProps[] = [];
|
|
26
|
+
if (dataResponse && dataResponse.status === 'ok') {
|
|
27
|
+
tokenList = dataResponse.result;
|
|
28
|
+
pageLimit = dataResponse.pageLimit;
|
|
29
|
+
tokenList;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const itemsCount = tokenList.length;
|
|
33
|
+
let maxPages = Math.floor(itemsCount / pageLimit);
|
|
34
|
+
if (itemsCount % pageLimit !== 0) {
|
|
35
|
+
maxPages++;
|
|
36
|
+
}
|
|
37
|
+
if (pageIndex > maxPages - 1) {
|
|
38
|
+
pageIndex = maxPages - 1;
|
|
39
|
+
}
|
|
40
|
+
const offset = pageIndex * pageLimit;
|
|
41
|
+
return {
|
|
42
|
+
status: 'ok',
|
|
43
|
+
itemsCount,
|
|
44
|
+
pageIndex,
|
|
45
|
+
pageLimit,
|
|
46
|
+
result: tokenList.slice(offset, offset + pageLimit),
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
const generateToken = async () => {
|
|
50
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/generate');
|
|
51
|
+
const dataResponse = await response.json;
|
|
52
|
+
if (dataResponse && dataResponse.status === 'ok') {
|
|
53
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Token generated successfully', NotificationColor.Success);
|
|
54
|
+
} else {
|
|
55
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to generate token', NotificationColor.Error);
|
|
56
|
+
}
|
|
57
|
+
return dataResponse.result;
|
|
58
|
+
};
|
|
59
|
+
const addTokenData = async (item: TokenProps) => {
|
|
60
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/add', { ...item });
|
|
61
|
+
const dataResponse = await response.json;
|
|
62
|
+
if (dataResponse && dataResponse.status === 'ok') {
|
|
63
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Token added successfully', NotificationColor.Success);
|
|
64
|
+
} else {
|
|
65
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to add token', NotificationColor.Error);
|
|
66
|
+
}
|
|
67
|
+
return { ...item };
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const removeTokenData = async (token: string) => {
|
|
71
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/remove', { token });
|
|
72
|
+
const dataResponse = await response.json;
|
|
73
|
+
if (dataResponse && dataResponse.status === 'ok') {
|
|
74
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Token removed successfully', NotificationColor.Success);
|
|
75
|
+
} else {
|
|
76
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to remove token', NotificationColor.Error);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const updateTokenData = async (item: TokenProps) => {
|
|
80
|
+
const response = await getRenderPageProps().renderPageFunctions.fetchData('/api/admin/tokens/update', { ...item });
|
|
81
|
+
const dataResponse = await response.json;
|
|
82
|
+
if (dataResponse && dataResponse.status === 'ok') {
|
|
83
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Token updated successfully', NotificationColor.Success);
|
|
84
|
+
} else {
|
|
85
|
+
NotificationMessage.sendMessage(dataResponse.message || 'Failed to update token', NotificationColor.Error);
|
|
86
|
+
}
|
|
87
|
+
return { ...item };
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type TokenDataUpdateProps = {
|
|
91
|
+
save?: (isNew: boolean) => Promise<TokenProps | null>;
|
|
92
|
+
cancel?: () => void;
|
|
93
|
+
};
|
|
94
|
+
// show dialog to edit one item, and call update when save the result
|
|
95
|
+
const showTokenEditItem = async (item: TokenProps, update: (item: TokenProps) => void, isNew: boolean) => {
|
|
96
|
+
const handleClicked = async (index: number) => {
|
|
97
|
+
if (index === 1) {
|
|
98
|
+
updateFn.cancel?.();
|
|
99
|
+
modalClose();
|
|
100
|
+
}
|
|
101
|
+
if (index === 0) {
|
|
102
|
+
const newItem = await updateFn.save?.(isNew);
|
|
103
|
+
if (newItem) {
|
|
104
|
+
update(newItem);
|
|
105
|
+
modalClose();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const updateFn: TokenDataUpdateProps = {};
|
|
110
|
+
const modalClose = await ModalWindow.show({
|
|
111
|
+
title: 'Edit Token Data',
|
|
112
|
+
buttons: ['Save', 'Cancel'],
|
|
113
|
+
// contentMaxHeight: '400px',
|
|
114
|
+
handleClicked,
|
|
115
|
+
children: <TokenEditItem item={item} update={updateFn}></TokenEditItem>,
|
|
116
|
+
closeWhenClickOutside: false,
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// edit one item
|
|
121
|
+
export const TokenEditItem = (props: { item: TokenProps; update: TokenDataUpdateProps }) => {
|
|
122
|
+
const ref: RefProps = { id: '' };
|
|
123
|
+
const css: CssProps = {
|
|
124
|
+
padding: '10px',
|
|
125
|
+
border: 'solid 1px gray',
|
|
126
|
+
margin: '1px',
|
|
127
|
+
position: 'relative',
|
|
128
|
+
'.lable': {
|
|
129
|
+
width: '90px',
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
props.update.save = async (isNew: boolean) => {
|
|
133
|
+
const token = ref.$('input.token').value;
|
|
134
|
+
const description = ref.$('input.description').value;
|
|
135
|
+
if (token && description) {
|
|
136
|
+
const newItem = isNew
|
|
137
|
+
? await addTokenData({ token, description })
|
|
138
|
+
: await updateTokenData({ token, description });
|
|
139
|
+
props.item.token = newItem.token;
|
|
140
|
+
props.item.description = newItem.description;
|
|
141
|
+
return newItem;
|
|
142
|
+
}
|
|
143
|
+
NotificationMessage.sendMessage('Please input token and description', NotificationColor.Error);
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
146
|
+
return (
|
|
147
|
+
<div ref={ref} css={css} class='sample-data'>
|
|
148
|
+
<div class='row-box mt-m'>
|
|
149
|
+
<div class='lable'>Token: </div>
|
|
150
|
+
<div>
|
|
151
|
+
<input type='text' class='input-base token' value={props.item.token} readonly={true} />
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
<div class='row-box'>
|
|
155
|
+
<div class='lable'>Description: </div>
|
|
156
|
+
<div>
|
|
157
|
+
<input type='text' class='input-base description' value={props.item.description} />
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// show one item
|
|
165
|
+
export const TokenShowItem = (props: { item: TokenProps }) => {
|
|
166
|
+
const ref: RefProps = {};
|
|
167
|
+
const css: CssProps = {
|
|
168
|
+
padding: '10px',
|
|
169
|
+
border: 'solid 1px gray',
|
|
170
|
+
margin: '1px',
|
|
171
|
+
position: 'relative',
|
|
172
|
+
'.control-box': {
|
|
173
|
+
display: 'none',
|
|
174
|
+
position: 'absolute',
|
|
175
|
+
right: '10px',
|
|
176
|
+
top: '10px',
|
|
177
|
+
},
|
|
178
|
+
'&:hover .control-box': {
|
|
179
|
+
display: 'block',
|
|
180
|
+
},
|
|
181
|
+
'.lable': {
|
|
182
|
+
width: '85px',
|
|
183
|
+
},
|
|
184
|
+
'.token': {
|
|
185
|
+
lineBreak: 'anywhere',
|
|
186
|
+
wordBreak: 'break-all',
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
const onEdit = (ev: any) => {
|
|
190
|
+
const update = (item: TokenProps) => {
|
|
191
|
+
dom.value = makeDom(item);
|
|
192
|
+
};
|
|
193
|
+
showTokenEditItem(props.item, update, false);
|
|
194
|
+
};
|
|
195
|
+
const onRemove = async (ev: any) => {
|
|
196
|
+
if (!confirm('Are you sure you want to delete this token?')) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
await removeTokenData(props.item.token);
|
|
200
|
+
ref.current.remove();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const makeDom = (item: TokenProps) => {
|
|
204
|
+
const saveText = (text: string) => {
|
|
205
|
+
item.description = text;
|
|
206
|
+
updateTokenData(item);
|
|
207
|
+
dom.value = makeDom(item);
|
|
208
|
+
};
|
|
209
|
+
return (
|
|
210
|
+
<div>
|
|
211
|
+
<div class='control-box'>
|
|
212
|
+
<button class='button-base button-ss' onClick={onEdit}>
|
|
213
|
+
Edit
|
|
214
|
+
</button>
|
|
215
|
+
<button class='button-base button-ss' onClick={onRemove}>
|
|
216
|
+
Delete
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
<div class='row-box'>
|
|
220
|
+
<div class='lable'>Token: </div>
|
|
221
|
+
<div class='token'>{item.token}</div>
|
|
222
|
+
</div>
|
|
223
|
+
<div class='row-box'>
|
|
224
|
+
<div class='lable'>Description: </div>
|
|
225
|
+
<div class='flex-1'>
|
|
226
|
+
<EditableLabel text={item.description} save={saveText} type='text' />
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
<div class='row-box'>
|
|
230
|
+
<div class='lable'>Timestamp: </div>
|
|
231
|
+
<div>{new Date(item.timestamp || 0).toLocaleString()}</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const dom = new HtmlVar(makeDom(props.item));
|
|
238
|
+
return (
|
|
239
|
+
<div ref={ref} css={css} class='sample-data'>
|
|
240
|
+
{dom.node}
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// show the list
|
|
246
|
+
const TokenList = () => {
|
|
247
|
+
let currentIndex = 0;
|
|
248
|
+
let searchText = '';
|
|
249
|
+
let maxPages = 0;
|
|
250
|
+
const ref: RefProps = {
|
|
251
|
+
onLoad: async (self: Element) => {
|
|
252
|
+
await makeList(currentIndex);
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
const onAdd = async () => {
|
|
256
|
+
const update = async (item: TokenProps) => {
|
|
257
|
+
// new item is added at the list end, so update the last page
|
|
258
|
+
await makeList(maxPages + 1);
|
|
259
|
+
};
|
|
260
|
+
showTokenEditItem(
|
|
261
|
+
{
|
|
262
|
+
token: await generateToken(),
|
|
263
|
+
description: '',
|
|
264
|
+
},
|
|
265
|
+
update,
|
|
266
|
+
true
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
const onSearch = async () => {
|
|
270
|
+
searchText = ref.$('input.search').value.trim();
|
|
271
|
+
await makeList(currentIndex);
|
|
272
|
+
};
|
|
273
|
+
const makeList = async (pageIndex: number) => {
|
|
274
|
+
const onLinkClick = async (index: number) => {
|
|
275
|
+
currentIndex = index;
|
|
276
|
+
await makeList(currentIndex);
|
|
277
|
+
};
|
|
278
|
+
const items = await getTokenData(pageIndex, searchText);
|
|
279
|
+
maxPages = Math.floor((items.itemsCount + 1) / items.pageLimit);
|
|
280
|
+
listDom.value = (
|
|
281
|
+
<div>
|
|
282
|
+
<PagingLink
|
|
283
|
+
itemsCount={items.itemsCount}
|
|
284
|
+
pageIndex={pageIndex}
|
|
285
|
+
pageLimit={items.pageLimit}
|
|
286
|
+
onClick={onLinkClick}
|
|
287
|
+
baseLink=''
|
|
288
|
+
></PagingLink>
|
|
289
|
+
{items.result.map((item) => (
|
|
290
|
+
<TokenShowItem item={item}></TokenShowItem>
|
|
291
|
+
))}
|
|
292
|
+
<PagingLink
|
|
293
|
+
itemsCount={items.itemsCount}
|
|
294
|
+
pageIndex={pageIndex}
|
|
295
|
+
pageLimit={items.pageLimit}
|
|
296
|
+
onClick={onLinkClick}
|
|
297
|
+
baseLink=''
|
|
298
|
+
></PagingLink>
|
|
299
|
+
</div>
|
|
300
|
+
);
|
|
301
|
+
};
|
|
302
|
+
const listDom = new HtmlVar(<div></div>);
|
|
303
|
+
const css: CssProps = {
|
|
304
|
+
display: 'flex',
|
|
305
|
+
flexDirection: 'column',
|
|
306
|
+
'.label': {
|
|
307
|
+
width: '70px',
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
return (
|
|
311
|
+
<div ref={ref} css={css}>
|
|
312
|
+
<div>
|
|
313
|
+
<div class='row-box'>
|
|
314
|
+
<div class='label'>Search: </div>
|
|
315
|
+
<input type='text' class='input-base search' />
|
|
316
|
+
<button class='button-base mr-s' onClick={onSearch}>
|
|
317
|
+
Search
|
|
318
|
+
</button>
|
|
319
|
+
<button class='button-base' onClick={onAdd}>
|
|
320
|
+
Add
|
|
321
|
+
</button>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
<div class='list'>{listDom.node}</div>
|
|
325
|
+
</div>
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
export const AdminTokensPage = () => {
|
|
330
|
+
return (
|
|
331
|
+
<div>
|
|
332
|
+
<div>
|
|
333
|
+
<div>Tokens management</div>
|
|
334
|
+
<TokenList />
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
);
|
|
338
|
+
};
|