underpost 2.85.7 → 2.89.1
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/.github/workflows/release.cd.yml +3 -1
- package/README.md +2 -2
- package/bin/build.js +8 -10
- package/bin/index.js +8 -1
- package/cli.md +3 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +50 -50
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/package.json +1 -1
- package/scripts/rpmfusion-ffmpeg-setup.sh +55 -0
- package/src/api/file/file.service.js +29 -3
- package/src/cli/baremetal.js +1 -2
- package/src/cli/index.js +1 -0
- package/src/cli/repository.js +8 -1
- package/src/cli/run.js +104 -36
- package/src/client/components/core/AgGrid.js +42 -3
- package/src/client/components/core/CommonJs.js +4 -0
- package/src/client/components/core/Css.js +95 -48
- package/src/client/components/core/CssCore.js +0 -1
- package/src/client/components/core/LoadingAnimation.js +2 -2
- package/src/client/components/core/Logger.js +2 -9
- package/src/client/components/core/Modal.js +22 -14
- package/src/client/components/core/ObjectLayerEngine.js +300 -9
- package/src/client/components/core/ObjectLayerEngineModal.js +686 -148
- package/src/client/components/core/ObjectLayerEngineViewer.js +1061 -0
- package/src/client/components/core/Pagination.js +57 -12
- package/src/client/components/core/Router.js +37 -1
- package/src/client/components/core/Translate.js +4 -0
- package/src/client/components/core/Worker.js +8 -1
- package/src/client/services/default/default.management.js +86 -16
- package/src/db/mariadb/MariaDB.js +2 -2
- package/src/index.js +1 -1
- package/src/server/client-build.js +57 -2
- package/src/server/object-layer.js +44 -0
- package/src/server/start.js +12 -0
- package/src/ws/IoInterface.js +2 -3
- package/AUTHORS.md +0 -21
- package/src/server/network.js +0 -72
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import { getQueryParams, setQueryParams } from './Router.js';
|
|
1
|
+
import { getQueryParams, setQueryParams, listenQueryParamsChange } from './Router.js';
|
|
2
2
|
|
|
3
3
|
class AgPagination extends HTMLElement {
|
|
4
4
|
constructor() {
|
|
5
5
|
super();
|
|
6
6
|
this.attachShadow({ mode: 'open' });
|
|
7
7
|
this._gridId = null;
|
|
8
|
-
|
|
8
|
+
let queryParams = getQueryParams();
|
|
9
9
|
this._currentPage = parseInt(queryParams.page, 10) || 1;
|
|
10
10
|
this._limit = parseInt(queryParams.limit, 10) || 10;
|
|
11
11
|
this._totalPages = 1;
|
|
12
12
|
this._totalItems = 0;
|
|
13
|
+
this._limitOptions = [10, 20, 50, 100]; // Default options
|
|
13
14
|
this.handlePageChange = this.handlePageChange.bind(this);
|
|
14
15
|
this.handleLimitChange = this.handleLimitChange.bind(this);
|
|
16
|
+
|
|
17
|
+
this.handleQueryParamsChange = this.handleQueryParamsChange.bind(this);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
static get observedAttributes() {
|
|
18
|
-
return ['grid-id', 'current-page', 'total-pages', 'total-items', 'limit'];
|
|
21
|
+
return ['grid-id', 'current-page', 'total-pages', 'total-items', 'limit', 'limit-options'];
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -35,6 +38,16 @@ class AgPagination extends HTMLElement {
|
|
|
35
38
|
case 'limit':
|
|
36
39
|
this._limit = parseInt(newValue, 10) || this._limit;
|
|
37
40
|
break;
|
|
41
|
+
case 'limit-options':
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(newValue);
|
|
44
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
45
|
+
this._limitOptions = parsed.map((v) => parseInt(v, 10)).filter((v) => !isNaN(v) && v > 0);
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.warn('Invalid limit-options format, using defaults');
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
38
51
|
}
|
|
39
52
|
this.update();
|
|
40
53
|
}
|
|
@@ -42,11 +55,43 @@ class AgPagination extends HTMLElement {
|
|
|
42
55
|
connectedCallback() {
|
|
43
56
|
this.render();
|
|
44
57
|
this.addEventListeners();
|
|
45
|
-
|
|
58
|
+
// Subscribe to query parameter changes once the component is connected and _gridId is potentially set.
|
|
59
|
+
listenQueryParamsChange({
|
|
60
|
+
id: `ag-pagination-${this._gridId || 'default'}`, // Use _gridId for a unique listener ID
|
|
61
|
+
event: this.handleQueryParamsChange,
|
|
62
|
+
});
|
|
63
|
+
// The initial state is already set in the constructor from getQueryParams().
|
|
64
|
+
// The `listenQueryParamsChange` in `Router.js` will trigger an immediate (setTimeout) call
|
|
65
|
+
// to `handleQueryParamsChange`, which will re-sync the state if needed and update the component.
|
|
66
|
+
this.update(); // Keep the initial update for rendering based on constructor's initial state.
|
|
46
67
|
}
|
|
47
68
|
|
|
48
69
|
disconnectedCallback() {
|
|
49
|
-
//
|
|
70
|
+
// If Router.js held strong references to queryParamsChangeListeners, you might need to unsubscribe here.
|
|
71
|
+
// However, for this example, we'll assume the component's lifecycle or weak references handle cleanup.
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
handleQueryParamsChange(queryParams) {
|
|
75
|
+
const newPage = parseInt(queryParams.page, 10) || 1;
|
|
76
|
+
const newLimit = parseInt(queryParams.limit, 10) || 10;
|
|
77
|
+
|
|
78
|
+
let shouldUpdate = false;
|
|
79
|
+
|
|
80
|
+
if (newPage !== this._currentPage) {
|
|
81
|
+
this._currentPage = newPage;
|
|
82
|
+
shouldUpdate = true;
|
|
83
|
+
}
|
|
84
|
+
if (newLimit !== this._limit) {
|
|
85
|
+
this._limit = newLimit;
|
|
86
|
+
shouldUpdate = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (shouldUpdate) {
|
|
90
|
+
this.update();
|
|
91
|
+
this.dispatchEvent(
|
|
92
|
+
new CustomEvent('pagination-query-params-change', { detail: { page: this._currentPage, limit: this._limit } }),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
50
95
|
}
|
|
51
96
|
|
|
52
97
|
handlePageChange(newPage) {
|
|
@@ -54,7 +99,7 @@ class AgPagination extends HTMLElement {
|
|
|
54
99
|
return;
|
|
55
100
|
}
|
|
56
101
|
this._currentPage = newPage;
|
|
57
|
-
setQueryParams({ page: newPage, limit: this._limit });
|
|
102
|
+
setQueryParams({ page: newPage, limit: this._limit }, { replace: false }); // Use pushState
|
|
58
103
|
this.dispatchEvent(new CustomEvent('page-change', { detail: { page: newPage } }));
|
|
59
104
|
}
|
|
60
105
|
|
|
@@ -65,7 +110,7 @@ class AgPagination extends HTMLElement {
|
|
|
65
110
|
}
|
|
66
111
|
this._limit = newLimit;
|
|
67
112
|
this._currentPage = 1; // Reset to first page on limit change
|
|
68
|
-
setQueryParams({ page: 1, limit: newLimit });
|
|
113
|
+
setQueryParams({ page: 1, limit: newLimit }, { replace: false }); // Use pushState
|
|
69
114
|
this.dispatchEvent(new CustomEvent('limit-change', { detail: { limit: newLimit } }));
|
|
70
115
|
}
|
|
71
116
|
|
|
@@ -87,7 +132,8 @@ class AgPagination extends HTMLElement {
|
|
|
87
132
|
this.shadowRoot.querySelector('#page-info').textContent = `Page ${this._currentPage} of ${this._totalPages}`;
|
|
88
133
|
|
|
89
134
|
const limitSelector = this.shadowRoot.querySelector('#limit-selector');
|
|
90
|
-
if (limitSelector.value != this._limit) {
|
|
135
|
+
if (limitSelector && limitSelector.value != this._limit) {
|
|
136
|
+
// Added null check for limitSelector
|
|
91
137
|
limitSelector.value = this._limit;
|
|
92
138
|
}
|
|
93
139
|
|
|
@@ -133,6 +179,8 @@ class AgPagination extends HTMLElement {
|
|
|
133
179
|
}
|
|
134
180
|
|
|
135
181
|
render() {
|
|
182
|
+
const limitOptionsHtml = this._limitOptions.map((value) => `<option value="${value}">${value}</option>`).join('');
|
|
183
|
+
|
|
136
184
|
this.shadowRoot.innerHTML = html`
|
|
137
185
|
<style>
|
|
138
186
|
:host {
|
|
@@ -193,10 +241,7 @@ class AgPagination extends HTMLElement {
|
|
|
193
241
|
<button id="next-page">Next</button>
|
|
194
242
|
<button id="last-page">Last</button>
|
|
195
243
|
<select id="limit-selector">
|
|
196
|
-
|
|
197
|
-
<option value="20">20</option>
|
|
198
|
-
<option value="50">50</option>
|
|
199
|
-
<option value="100">100</option>
|
|
244
|
+
${limitOptionsHtml}
|
|
200
245
|
</select>
|
|
201
246
|
`;
|
|
202
247
|
}
|
|
@@ -19,6 +19,13 @@ const logger = loggerFactory(import.meta, { trace: true });
|
|
|
19
19
|
*/
|
|
20
20
|
const RouterEvents = {};
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* @type {Object.<string, function>}\n
|
|
24
|
+
* @description Holds event listeners for query parameter changes.\n
|
|
25
|
+
* @memberof PwaRouter
|
|
26
|
+
*/
|
|
27
|
+
const queryParamsChangeListeners = {};
|
|
28
|
+
|
|
22
29
|
/**
|
|
23
30
|
* @type {string[]}
|
|
24
31
|
* @description Array of core UI component IDs that should not trigger modal close route changes.
|
|
@@ -227,6 +234,22 @@ const listenQueryPathInstance = ({ id, routeId, event }, queryKey = 'cid') => {
|
|
|
227
234
|
});
|
|
228
235
|
};
|
|
229
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Registers a listener for changes to query parameters.\n
|
|
239
|
+
* The provided event callback is triggered with the current query parameters object.\n
|
|
240
|
+
* @param {object} options - The listener options.\n
|
|
241
|
+
* @param {string} options.id - A unique ID for the listener.\n
|
|
242
|
+
* @param {function(Object.<string, string>): void} options.event - The callback function to execute with the new query parameters.\n
|
|
243
|
+
* @memberof PwaRouter
|
|
244
|
+
*/
|
|
245
|
+
const listenQueryParamsChange = ({ id, event }) => {
|
|
246
|
+
queryParamsChangeListeners[id] = event;
|
|
247
|
+
// Immediately call with current query params for initial state
|
|
248
|
+
setTimeout(() => {
|
|
249
|
+
event(getQueryParams());
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
|
|
230
253
|
/**
|
|
231
254
|
* Handles the logic for changing the route when a modal is closed. It determines the next URL
|
|
232
255
|
* based on the remaining open modals or falls back to a home URL.
|
|
@@ -300,7 +323,18 @@ const setQueryParams = (newParams, options = { replace: true }) => {
|
|
|
300
323
|
|
|
301
324
|
const newPath = url.pathname + url.search + url.hash;
|
|
302
325
|
|
|
303
|
-
|
|
326
|
+
if (options.replace) {
|
|
327
|
+
history.replaceState(history.state, '', newPath);
|
|
328
|
+
} else {
|
|
329
|
+
history.pushState(history.state, '', newPath);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const updatedParams = getQueryParams();
|
|
333
|
+
for (const listenerId in queryParamsChangeListeners) {
|
|
334
|
+
if (Object.hasOwnProperty.call(queryParamsChangeListeners, listenerId)) {
|
|
335
|
+
queryParamsChangeListeners[listenerId](updatedParams);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
304
338
|
};
|
|
305
339
|
|
|
306
340
|
export {
|
|
@@ -319,4 +353,6 @@ export {
|
|
|
319
353
|
setPath,
|
|
320
354
|
setQueryParams,
|
|
321
355
|
sanitizeRoute,
|
|
356
|
+
queryParamsChangeListeners,
|
|
357
|
+
listenQueryParamsChange,
|
|
322
358
|
};
|
|
@@ -53,15 +53,22 @@ class PwaWorker {
|
|
|
53
53
|
*/
|
|
54
54
|
constructor() {
|
|
55
55
|
this.title = `${s('title').textContent}`;
|
|
56
|
+
if (!window.renderPayload.dev) {
|
|
57
|
+
console.log = () => null;
|
|
58
|
+
console.error = () => null;
|
|
59
|
+
console.info = () => null;
|
|
60
|
+
console.warn = () => null;
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
/**
|
|
59
65
|
* Checks if the application is running in development mode (localhost or 127.0.0.1).
|
|
66
|
+
* @method devMode
|
|
60
67
|
* @memberof PwaWorker
|
|
61
68
|
* @returns {boolean} True if in development mode.
|
|
62
69
|
*/
|
|
63
70
|
devMode() {
|
|
64
|
-
return location.origin.match('localhost') || location.origin.match('127.0.0.1');
|
|
71
|
+
return window.renderPayload.dev || location.origin.match('localhost') || location.origin.match('127.0.0.1');
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
/**
|
|
@@ -18,7 +18,7 @@ const DefaultOptions = {
|
|
|
18
18
|
serviceId: 'default-management',
|
|
19
19
|
entity: 'default',
|
|
20
20
|
columnDefs: [
|
|
21
|
-
{ field: '0', headerName: '0' },
|
|
21
|
+
{ field: '0', headerName: '0', cellClassRules: { 'row-new-highlight': (params) => true } },
|
|
22
22
|
{ field: '1', headerName: '1' },
|
|
23
23
|
{ field: '2', headerName: '2' },
|
|
24
24
|
{ field: 'createdAt', headerName: 'createdAt', cellDataType: 'date', editable: false },
|
|
@@ -29,6 +29,10 @@ const DefaultOptions = {
|
|
|
29
29
|
permissions: {
|
|
30
30
|
add: true,
|
|
31
31
|
remove: true,
|
|
32
|
+
reload: true,
|
|
33
|
+
},
|
|
34
|
+
paginationOptions: {
|
|
35
|
+
limitOptions: [10, 20, 50, 100],
|
|
32
36
|
},
|
|
33
37
|
};
|
|
34
38
|
|
|
@@ -66,25 +70,36 @@ const DefaultManagement = {
|
|
|
66
70
|
paginationComp.setAttribute('current-page', this.Tokens[id].page);
|
|
67
71
|
paginationComp.setAttribute('total-pages', this.Tokens[id].totalPages);
|
|
68
72
|
paginationComp.setAttribute('total-items', this.Tokens[id].total);
|
|
73
|
+
setTimeout(async () => {
|
|
74
|
+
if (DefaultManagement.Tokens[id].readyRowDataEvent)
|
|
75
|
+
for (const event of Object.keys(DefaultManagement.Tokens[id].readyRowDataEvent))
|
|
76
|
+
await DefaultManagement.Tokens[id].readyRowDataEvent[event](rowDataScope);
|
|
77
|
+
}, 1);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
refreshTable: async function (id) {
|
|
81
|
+
const gridApi = AgGrid.grids[this.Tokens[id].gridId];
|
|
82
|
+
if (gridApi) {
|
|
83
|
+
// Use refreshCells with change detection for optimal performance
|
|
84
|
+
// This is preferred over redrawRows() as it only updates changed cells
|
|
85
|
+
gridApi.refreshCells({
|
|
86
|
+
force: false, // Use change detection - only refresh cells whose values have changed
|
|
87
|
+
suppressFlash: false, // Show flash animation for changed cells (requires enableCellChangeFlash)
|
|
88
|
+
});
|
|
69
89
|
}
|
|
70
90
|
},
|
|
71
91
|
RenderTable: async function (options = DefaultOptions) {
|
|
72
92
|
if (!options) options = DefaultOptions;
|
|
73
|
-
const { serviceId, columnDefs, entity, defaultColKeyFocus, ServiceProvider, permissions } =
|
|
93
|
+
const { serviceId, columnDefs, entity, defaultColKeyFocus, ServiceProvider, permissions, paginationOptions } =
|
|
94
|
+
options;
|
|
74
95
|
logger.info('DefaultManagement RenderTable', options);
|
|
75
96
|
const id = options?.idModal ? options.idModal : getId(this.Tokens, `${serviceId}-`);
|
|
76
97
|
const gridId = `${serviceId}-grid-${id}`;
|
|
77
98
|
const queryParams = getQueryParams();
|
|
78
99
|
const page = parseInt(queryParams.page) || 1;
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
gridId,
|
|
83
|
-
page,
|
|
84
|
-
limit,
|
|
85
|
-
total: 0,
|
|
86
|
-
totalPages: 1,
|
|
87
|
-
};
|
|
100
|
+
const defaultLimit = paginationOptions?.limitOptions?.[0] || 10;
|
|
101
|
+
const limit = parseInt(queryParams.limit) || defaultLimit;
|
|
102
|
+
this.Tokens[id] = { ...this.Tokens[id], ...options, gridId, page, limit, total: 0, totalPages: 1 };
|
|
88
103
|
|
|
89
104
|
setQueryParams({ page, limit });
|
|
90
105
|
setTimeout(async () => {
|
|
@@ -107,7 +122,7 @@ const DefaultManagement = {
|
|
|
107
122
|
label: html`<div class="abs center">
|
|
108
123
|
<i class="fas fa-times"></i>
|
|
109
124
|
</div> `,
|
|
110
|
-
class: `in fll section-mp management-table-btn-mini management-table-btn-remove-${id}-${cellRenderId}`,
|
|
125
|
+
class: `in fll section-mp management-table-btn-mini management-table-btn-remove-${id}-${cellRenderId} ${!params.data._id ? 'hide' : ''}`,
|
|
111
126
|
})}`;
|
|
112
127
|
setTimeout(() => {
|
|
113
128
|
EventsUI.onClick(
|
|
@@ -191,9 +206,16 @@ const DefaultManagement = {
|
|
|
191
206
|
// }
|
|
192
207
|
// }
|
|
193
208
|
s(`.management-table-btn-save-${id}`).onclick = () => {
|
|
209
|
+
s(`.management-table-btn-save-${id}`).classList.add('hide');
|
|
210
|
+
// s(`.management-table-btn-stop-${id}`).classList.add('hide');
|
|
211
|
+
if (permissions.add) s(`.management-table-btn-add-${id}`).classList.remove('hide');
|
|
212
|
+
if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.remove('hide');
|
|
213
|
+
if (permissions.reload) s(`.management-table-btn-reload-${id}`).classList.remove('hide');
|
|
194
214
|
AgGrid.grids[gridId].stopEditing();
|
|
195
215
|
};
|
|
196
216
|
EventsUI.onClick(`.management-table-btn-add-${id}`, async () => {
|
|
217
|
+
if (options.customEvent && options.customEvent.add) return await options.customEvent.add();
|
|
218
|
+
|
|
197
219
|
const rowObj = {};
|
|
198
220
|
for (const def of columnDefs) {
|
|
199
221
|
rowObj[def.field] = '';
|
|
@@ -260,6 +282,11 @@ const DefaultManagement = {
|
|
|
260
282
|
// }
|
|
261
283
|
|
|
262
284
|
setTimeout(() => {
|
|
285
|
+
s(`.management-table-btn-save-${id}`).classList.remove('hide');
|
|
286
|
+
// s(`.management-table-btn-stop-${id}`).classList.remove('hide');
|
|
287
|
+
if (permissions.add) s(`.management-table-btn-add-${id}`).classList.add('hide');
|
|
288
|
+
if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.add('hide');
|
|
289
|
+
if (permissions.reload) s(`.management-table-btn-reload-${id}`).classList.add('hide');
|
|
263
290
|
AgGrid.grids[gridId].startEditingCell({
|
|
264
291
|
rowIndex: 0,
|
|
265
292
|
colKey: defaultColKeyFocus,
|
|
@@ -268,6 +295,15 @@ const DefaultManagement = {
|
|
|
268
295
|
});
|
|
269
296
|
});
|
|
270
297
|
});
|
|
298
|
+
|
|
299
|
+
EventsUI.onClick(`.management-table-btn-stop-${id}`, async () => {
|
|
300
|
+
s(`.management-table-btn-save-${id}`).classList.add('hide');
|
|
301
|
+
// s(`.management-table-btn-stop-${id}`).classList.add('hide');
|
|
302
|
+
if (permissions.add) s(`.management-table-btn-add-${id}`).classList.remove('hide');
|
|
303
|
+
if (permissions.remove) s(`.management-table-btn-clean-${id}`).classList.remove('hide');
|
|
304
|
+
if (permissions.reload) s(`.management-table-btn-reload-${id}`).classList.remove('hide');
|
|
305
|
+
AgGrid.grids[gridId].stopEditing();
|
|
306
|
+
});
|
|
271
307
|
EventsUI.onClick(`.management-table-btn-clean-${id}`, async () => {
|
|
272
308
|
const confirmResult = await Modal.RenderConfirm(
|
|
273
309
|
{
|
|
@@ -292,6 +328,26 @@ const DefaultManagement = {
|
|
|
292
328
|
DefaultManagement.loadTable(id);
|
|
293
329
|
}
|
|
294
330
|
});
|
|
331
|
+
EventsUI.onClick(`.management-table-btn-reload-${id}`, async () => {
|
|
332
|
+
try {
|
|
333
|
+
// Reload data from server
|
|
334
|
+
await DefaultManagement.loadTable(id);
|
|
335
|
+
|
|
336
|
+
// Other option: Refresh cells to update UI
|
|
337
|
+
// DefaultManagement.refreshTable(id);
|
|
338
|
+
|
|
339
|
+
NotificationManager.Push({
|
|
340
|
+
html: Translate.Render('success-reload-data') || 'Data reloaded successfully',
|
|
341
|
+
status: 'success',
|
|
342
|
+
});
|
|
343
|
+
} catch (error) {
|
|
344
|
+
NotificationManager.Push({
|
|
345
|
+
html: error.message || 'Error reloading data',
|
|
346
|
+
status: 'error',
|
|
347
|
+
});
|
|
348
|
+
} finally {
|
|
349
|
+
}
|
|
350
|
+
});
|
|
295
351
|
s(`#ag-pagination-${gridId}`).addEventListener('page-change', async (event) => {
|
|
296
352
|
const token = DefaultManagement.Tokens[id];
|
|
297
353
|
token.page = event.detail.page;
|
|
@@ -318,7 +374,7 @@ const DefaultManagement = {
|
|
|
318
374
|
}
|
|
319
375
|
};
|
|
320
376
|
}, 1);
|
|
321
|
-
return html`<div class="fl">
|
|
377
|
+
return html`<div class="fl management-table-toolbar">
|
|
322
378
|
${await BtnIcon.Render({
|
|
323
379
|
class: `in fll section-mp management-table-btn-mini management-table-btn-add-${id} ${
|
|
324
380
|
permissions.add ? '' : 'hide'
|
|
@@ -327,12 +383,15 @@ const DefaultManagement = {
|
|
|
327
383
|
type: 'button',
|
|
328
384
|
})}
|
|
329
385
|
${await BtnIcon.Render({
|
|
330
|
-
class: `in fll section-mp management-table-btn-mini management-table-btn-save-${id}
|
|
331
|
-
permissions.add ? '' : 'hide'
|
|
332
|
-
}`,
|
|
386
|
+
class: `in fll section-mp management-table-btn-mini management-table-btn-save-${id} hide`,
|
|
333
387
|
label: html`<div class="abs center btn-save-${id}-label"><i class="fas fa-save"></i></div> `,
|
|
334
388
|
type: 'button',
|
|
335
389
|
})}
|
|
390
|
+
${await BtnIcon.Render({
|
|
391
|
+
class: `in fll section-mp management-table-btn-mini management-table-btn-stop-${id} hide`,
|
|
392
|
+
label: html`<div class="abs center btn-save-${id}-label"><i class="fa-solid fa-rectangle-xmark"></i></div> `,
|
|
393
|
+
type: 'button',
|
|
394
|
+
})}
|
|
336
395
|
${await BtnIcon.Render({
|
|
337
396
|
class: `in fll section-mp management-table-btn-mini management-table-btn-clean-${id} ${
|
|
338
397
|
permissions.remove ? '' : 'hide'
|
|
@@ -340,12 +399,21 @@ const DefaultManagement = {
|
|
|
340
399
|
label: html`<div class="abs center btn-clean-${id}-label"><i class="fas fa-broom"></i></div> `,
|
|
341
400
|
type: 'button',
|
|
342
401
|
})}
|
|
402
|
+
${await BtnIcon.Render({
|
|
403
|
+
class: `in fll section-mp management-table-btn-mini management-table-btn-reload-${id} ${
|
|
404
|
+
permissions.reload ? '' : 'hide'
|
|
405
|
+
}`,
|
|
406
|
+
label: html`<div class="abs center btn-reload-${id}-label"><i class="fas fa-sync-alt"></i></div> `,
|
|
407
|
+
type: 'button',
|
|
408
|
+
})}
|
|
343
409
|
</div>
|
|
344
410
|
<div class="in section-mp">
|
|
345
411
|
${await AgGrid.Render({
|
|
346
412
|
id: gridId,
|
|
347
413
|
parentModal: options.idModal,
|
|
348
414
|
usePagination: true,
|
|
415
|
+
paginationOptions,
|
|
416
|
+
customHeightOffset: !permissions.add && !permissions.remove && !permissions.reload ? 50 : 0,
|
|
349
417
|
darkTheme,
|
|
350
418
|
gridOptions: {
|
|
351
419
|
defaultColDef: {
|
|
@@ -425,6 +493,7 @@ const DefaultManagement = {
|
|
|
425
493
|
// rowNode.setData(newRow);
|
|
426
494
|
// }, 2000);
|
|
427
495
|
}
|
|
496
|
+
s(`.management-table-btn-save-${id}`).click();
|
|
428
497
|
}
|
|
429
498
|
} else {
|
|
430
499
|
const body = event.data ? event.data : {};
|
|
@@ -441,6 +510,7 @@ const DefaultManagement = {
|
|
|
441
510
|
}
|
|
442
511
|
}
|
|
443
512
|
},
|
|
513
|
+
...(options.gridOptions ? options.gridOptions : undefined),
|
|
444
514
|
},
|
|
445
515
|
})}
|
|
446
516
|
</div>`;
|
|
@@ -42,9 +42,9 @@ class MariaDBService {
|
|
|
42
42
|
try {
|
|
43
43
|
conn = await pool.getConnection();
|
|
44
44
|
result = await conn.query(query, { supportBigNumbers: true, bigNumberStrings: true });
|
|
45
|
-
logger.info(query
|
|
45
|
+
logger.info('query');
|
|
46
|
+
console.log(result);
|
|
46
47
|
} catch (error) {
|
|
47
|
-
if (error.stack.startsWith('TypeError: Do not know how to serialize a BigInt')) return;
|
|
48
48
|
logger.error(error, error.stack);
|
|
49
49
|
} finally {
|
|
50
50
|
if (conn) conn.release(); // release to pool
|
package/src/index.js
CHANGED
|
@@ -32,6 +32,51 @@ dotenv.config();
|
|
|
32
32
|
|
|
33
33
|
// Static Site Generation (SSG)
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Recursively copies files from source to destination, but only files that don't exist in destination.
|
|
37
|
+
* @function copyNonExistingFiles
|
|
38
|
+
* @param {string} src - Source directory path
|
|
39
|
+
* @param {string} dest - Destination directory path
|
|
40
|
+
* @returns {void}
|
|
41
|
+
* @memberof clientBuild
|
|
42
|
+
*/
|
|
43
|
+
const copyNonExistingFiles = (src, dest) => {
|
|
44
|
+
// Ensure source exists
|
|
45
|
+
if (!fs.existsSync(src)) {
|
|
46
|
+
throw new Error(`Source directory does not exist: ${src}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get stats for source
|
|
50
|
+
const srcStats = fs.statSync(src);
|
|
51
|
+
|
|
52
|
+
// If source is a file, copy only if it doesn't exist in destination
|
|
53
|
+
if (srcStats.isFile()) {
|
|
54
|
+
if (!fs.existsSync(dest)) {
|
|
55
|
+
const destDir = dir.dirname(dest);
|
|
56
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
57
|
+
fs.copyFileSync(src, dest);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// If source is a directory, create destination if it doesn't exist
|
|
63
|
+
if (srcStats.isDirectory()) {
|
|
64
|
+
if (!fs.existsSync(dest)) {
|
|
65
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Read all items in source directory
|
|
69
|
+
const items = fs.readdirSync(src);
|
|
70
|
+
|
|
71
|
+
// Recursively process each item
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
const srcPath = dir.join(src, item);
|
|
74
|
+
const destPath = dir.join(dest, item);
|
|
75
|
+
copyNonExistingFiles(srcPath, destPath);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
35
80
|
/**
|
|
36
81
|
* @async
|
|
37
82
|
* @function buildClient
|
|
@@ -83,6 +128,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
83
128
|
* @param {string} options.publicClientId - Public client ID.
|
|
84
129
|
* @param {boolean} options.iconsBuild - Whether to build icons.
|
|
85
130
|
* @param {Object} options.metadata - Metadata for the client.
|
|
131
|
+
* @param {boolean} options.publicCopyNonExistingFiles - Whether to copy non-existing files from public directory.
|
|
86
132
|
* @returns {Promise<void>} - Promise that resolves when the full build is complete.
|
|
87
133
|
* @throws {Error} - If the full build fails.
|
|
88
134
|
* @memberof clientBuild
|
|
@@ -98,6 +144,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
98
144
|
publicClientId,
|
|
99
145
|
iconsBuild,
|
|
100
146
|
metadata,
|
|
147
|
+
publicCopyNonExistingFiles,
|
|
101
148
|
}) => {
|
|
102
149
|
logger.warn('Full build', rootClientPath);
|
|
103
150
|
|
|
@@ -169,11 +216,15 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
169
216
|
fs.copySync(dist.styles, `${rootClientPath}${dist.public_styles_folder}`);
|
|
170
217
|
}
|
|
171
218
|
}
|
|
219
|
+
|
|
220
|
+
if (publicCopyNonExistingFiles)
|
|
221
|
+
copyNonExistingFiles(`./src/client/public/${publicCopyNonExistingFiles}`, rootClientPath);
|
|
172
222
|
};
|
|
173
223
|
|
|
174
224
|
// { srcBuildPath, publicBuildPath }
|
|
175
225
|
const enableLiveRebuild =
|
|
176
226
|
options && options.liveClientBuildPaths && options.liveClientBuildPaths.length > 0 ? true : false;
|
|
227
|
+
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
177
228
|
|
|
178
229
|
let currentPort = parseInt(process.env.PORT) + 1;
|
|
179
230
|
for (const host of Object.keys(confServer)) {
|
|
@@ -205,7 +256,8 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
205
256
|
} = confServer[host][path];
|
|
206
257
|
if (singleReplica) continue;
|
|
207
258
|
if (!confClient[client]) confClient[client] = {};
|
|
208
|
-
const { components, dists, views, services, metadata, publicRef } =
|
|
259
|
+
const { components, dists, views, services, metadata, publicRef, publicCopyNonExistingFiles } =
|
|
260
|
+
confClient[client];
|
|
209
261
|
let backgroundImage;
|
|
210
262
|
if (metadata) {
|
|
211
263
|
backgroundImage = metadata.backgroundImage;
|
|
@@ -240,6 +292,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
240
292
|
publicClientId,
|
|
241
293
|
iconsBuild,
|
|
242
294
|
metadata,
|
|
295
|
+
publicCopyNonExistingFiles,
|
|
243
296
|
});
|
|
244
297
|
|
|
245
298
|
if (components)
|
|
@@ -520,6 +573,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
520
573
|
apiBaseHost,
|
|
521
574
|
apiBasePath: process.env.BASE_API,
|
|
522
575
|
version: Underpost.version,
|
|
576
|
+
...(isDevelopment ? { dev: true } : undefined),
|
|
523
577
|
},
|
|
524
578
|
renderApi: {
|
|
525
579
|
JSONweb,
|
|
@@ -613,6 +667,7 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
|
|
|
613
667
|
apiBaseHost,
|
|
614
668
|
apiBasePath: process.env.BASE_API,
|
|
615
669
|
version: Underpost.version,
|
|
670
|
+
...(isDevelopment ? { dev: true } : undefined),
|
|
616
671
|
},
|
|
617
672
|
renderApi: {
|
|
618
673
|
JSONweb,
|
|
@@ -687,4 +742,4 @@ ${fs.readFileSync(`${rootClientPath}/sw.js`, 'utf8')}`,
|
|
|
687
742
|
}
|
|
688
743
|
};
|
|
689
744
|
|
|
690
|
-
export { buildClient };
|
|
745
|
+
export { buildClient, copyNonExistingFiles };
|
|
@@ -192,6 +192,49 @@ export class ObjectLayerEngine {
|
|
|
192
192
|
return objectLayerFrameDirections;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
/**
|
|
196
|
+
* @memberof CyberiaObjectLayer
|
|
197
|
+
* @static
|
|
198
|
+
* @description Processes an image file through frameFactory and adds the resulting frame to the render data structure.
|
|
199
|
+
* Updates the color palette and pushes the frame to all keyframe directions corresponding to the given direction code.
|
|
200
|
+
* Initializes colors array, frames object, and direction arrays if they don't exist.
|
|
201
|
+
* @param {Object} renderData - The render data object containing frames and colors.
|
|
202
|
+
* @param {string} imagePath - The path to the image file to process.
|
|
203
|
+
* @param {string} directionCode - The numerical direction code (e.g., '08', '14').
|
|
204
|
+
* @returns {Promise<Object>} - The updated render data object.
|
|
205
|
+
* @memberof CyberiaObjectLayer
|
|
206
|
+
*/
|
|
207
|
+
static async processAndPushFrame(renderData, imagePath, directionCode) {
|
|
208
|
+
// Initialize colors array if it doesn't exist
|
|
209
|
+
if (!renderData.colors) {
|
|
210
|
+
renderData.colors = [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Initialize frames object if it doesn't exist
|
|
214
|
+
if (!renderData.frames) {
|
|
215
|
+
renderData.frames = {};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Process the image and extract frame matrix and updated colors
|
|
219
|
+
const frameFactoryResult = await ObjectLayerEngine.frameFactory(imagePath, renderData.colors);
|
|
220
|
+
|
|
221
|
+
// Update the colors palette
|
|
222
|
+
renderData.colors = frameFactoryResult.colors;
|
|
223
|
+
|
|
224
|
+
// Get all keyframe directions for this direction code
|
|
225
|
+
const keyframeDirections = ObjectLayerEngine.getKeyFramesDirectionsFromNumberFolderDirection(directionCode);
|
|
226
|
+
|
|
227
|
+
// Push the frame to all corresponding directions
|
|
228
|
+
for (const keyframeDirection of keyframeDirections) {
|
|
229
|
+
if (!renderData.frames[keyframeDirection]) {
|
|
230
|
+
renderData.frames[keyframeDirection] = [];
|
|
231
|
+
}
|
|
232
|
+
renderData.frames[keyframeDirection].push(frameFactoryResult.frame);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return renderData;
|
|
236
|
+
}
|
|
237
|
+
|
|
195
238
|
/**
|
|
196
239
|
* @memberof CyberiaObjectLayer
|
|
197
240
|
* @static
|
|
@@ -290,5 +333,6 @@ export const readPngAsync = ObjectLayerEngine.readPngAsync;
|
|
|
290
333
|
export const frameFactory = ObjectLayerEngine.frameFactory;
|
|
291
334
|
export const getKeyFramesDirectionsFromNumberFolderDirection =
|
|
292
335
|
ObjectLayerEngine.getKeyFramesDirectionsFromNumberFolderDirection;
|
|
336
|
+
export const processAndPushFrame = ObjectLayerEngine.processAndPushFrame;
|
|
293
337
|
export const buildImgFromTile = ObjectLayerEngine.buildImgFromTile;
|
|
294
338
|
export const generateRandomStats = ObjectLayerEngine.generateRandomStats;
|
package/src/server/start.js
CHANGED
|
@@ -122,6 +122,12 @@ class UnderpostStartUp {
|
|
|
122
122
|
if (options.build === true) await UnderpostStartUp.API.build(deployId, env);
|
|
123
123
|
if (options.run === true) await UnderpostStartUp.API.run(deployId, env);
|
|
124
124
|
},
|
|
125
|
+
/**
|
|
126
|
+
* Run itc-scripts and builds client bundle.
|
|
127
|
+
* @param {string} deployId - The ID of the deployment.
|
|
128
|
+
* @param {string} env - The environment of the deployment.
|
|
129
|
+
* @memberof UnderpostStartUp
|
|
130
|
+
*/
|
|
125
131
|
async build(deployId = 'dd-default', env = 'development') {
|
|
126
132
|
const buildBasePath = `/home/dd`;
|
|
127
133
|
const repoName = `engine-${deployId.split('-')[1]}`;
|
|
@@ -139,6 +145,12 @@ class UnderpostStartUp {
|
|
|
139
145
|
}
|
|
140
146
|
shellExec(`node bin/deploy build-full-client ${deployId}`);
|
|
141
147
|
},
|
|
148
|
+
/**
|
|
149
|
+
* Runs a deployment.
|
|
150
|
+
* @param {string} deployId - The ID of the deployment.
|
|
151
|
+
* @param {string} env - The environment of the deployment.
|
|
152
|
+
* @memberof UnderpostStartUp
|
|
153
|
+
*/
|
|
142
154
|
async run(deployId = 'dd-default', env = 'development') {
|
|
143
155
|
const runCmd = env === 'production' ? 'run prod-img' : 'run dev-img';
|
|
144
156
|
if (fs.existsSync(`./engine-private/replica`)) {
|