underpost 2.8.844 → 2.8.845
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 +8 -2
- package/cli.md +1 -1
- package/docker-compose.yml +1 -1
- package/manifests/deployment/dd-template-development/deployment.yaml +2 -2
- package/package.json +1 -2
- package/src/client/components/core/Css.js +52 -2
- package/src/client/components/core/Docs.js +9 -10
- package/src/client/components/core/DropDown.js +128 -82
- package/src/client/components/core/Modal.js +25 -38
- package/src/client/components/core/PanelForm.js +12 -4
- package/src/client/components/core/Router.js +63 -2
- package/src/client/components/core/Translate.js +2 -2
- package/src/index.js +1 -1
- package/src/server/client-build-docs.js +205 -0
- package/src/server/client-build.js +11 -120
package/README.md
CHANGED
|
@@ -35,10 +35,13 @@ template
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
38
41
|
<!-- badges -->
|
|
39
42
|
|
|
40
43
|
|
|
41
|
-
[](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [](https://www.npmjs.com/package/underpost) [](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [](https://www.npmjs.com/package/underpost) [](https://socket.dev/npm/package/underpost/overview/2.8.845) [](https://coveralls.io/github/underpostnet/engine?branch=master) [](https://www.npmjs.org/package/underpost) [](https://www.npmjs.com/package/underpost)
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
<!-- end-badges -->
|
|
@@ -56,6 +59,9 @@ template
|
|
|
56
59
|
|
|
57
60
|
|
|
58
61
|
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
59
65
|
</div>
|
|
60
66
|
|
|
61
67
|
<div align="center">
|
|
@@ -100,7 +106,7 @@ Run dev client server
|
|
|
100
106
|
npm run dev
|
|
101
107
|
```
|
|
102
108
|
<!-- -->
|
|
103
|
-
## underpost ci/cd cli v2.8.
|
|
109
|
+
## underpost ci/cd cli v2.8.845
|
|
104
110
|
|
|
105
111
|
### Usage: `underpost [options] [command]`
|
|
106
112
|
```
|
package/cli.md
CHANGED
package/docker-compose.yml
CHANGED
|
@@ -17,7 +17,7 @@ spec:
|
|
|
17
17
|
spec:
|
|
18
18
|
containers:
|
|
19
19
|
- name: dd-template-development-blue
|
|
20
|
-
image: localhost/rockylinux9-underpost:v2.8.
|
|
20
|
+
image: localhost/rockylinux9-underpost:v2.8.845
|
|
21
21
|
# resources:
|
|
22
22
|
# requests:
|
|
23
23
|
# memory: "124Ki"
|
|
@@ -100,7 +100,7 @@ spec:
|
|
|
100
100
|
spec:
|
|
101
101
|
containers:
|
|
102
102
|
- name: dd-template-development-green
|
|
103
|
-
image: localhost/rockylinux9-underpost:v2.8.
|
|
103
|
+
image: localhost/rockylinux9-underpost:v2.8.845
|
|
104
104
|
# resources:
|
|
105
105
|
# requests:
|
|
106
106
|
# memory: "124Ki"
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"main": "src/index.js",
|
|
4
4
|
"name": "underpost",
|
|
5
|
-
"version": "2.8.
|
|
5
|
+
"version": "2.8.845",
|
|
6
6
|
"description": "pwa api rest template",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
|
|
@@ -105,7 +105,6 @@
|
|
|
105
105
|
"rrule": "^2.8.1",
|
|
106
106
|
"sharp": "^0.32.5",
|
|
107
107
|
"shelljs": "^0.8.5",
|
|
108
|
-
"simple-icons": "^13.9.0",
|
|
109
108
|
"sitemap": "^7.1.1",
|
|
110
109
|
"socket.io": "^4.8.0",
|
|
111
110
|
"sortablejs": "^1.15.0",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { getId, newInstance, range, s4, splitEveryXChar } from './CommonJs.js';
|
|
1
|
+
import { getId, newInstance, range, rgbToHex, s4, splitEveryXChar } from './CommonJs.js';
|
|
2
2
|
import { CssCoreDark, CssCoreLight } from './CssCore.js';
|
|
3
3
|
import { DropDown } from './DropDown.js';
|
|
4
4
|
import { Modal } from './Modal.js';
|
|
5
5
|
import { Translate } from './Translate.js';
|
|
6
|
-
import { append, getProxyPath, htmls, s } from './VanillaJs.js';
|
|
6
|
+
import { append, getProxyPath, htmls, s, sa } from './VanillaJs.js';
|
|
7
7
|
|
|
8
8
|
let ThemesScope = [];
|
|
9
9
|
|
|
@@ -12,6 +12,39 @@ let ThemesScope = [];
|
|
|
12
12
|
// https://www.1001fonts.com/
|
|
13
13
|
|
|
14
14
|
const Css = {
|
|
15
|
+
// Menu button container transition styles
|
|
16
|
+
menuButtonContainer: () => css`
|
|
17
|
+
.main-btn-menu {
|
|
18
|
+
transition: all 0.2s ease-in-out;
|
|
19
|
+
position: relative;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.main-btn-menu::after {
|
|
23
|
+
content: '';
|
|
24
|
+
position: absolute;
|
|
25
|
+
bottom: 0;
|
|
26
|
+
left: 50%;
|
|
27
|
+
width: 0;
|
|
28
|
+
height: 2px;
|
|
29
|
+
background: currentColor;
|
|
30
|
+
transition: all 0.2s ease-in-out;
|
|
31
|
+
transform: translateX(-50%);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.main-btn-menu:hover::after {
|
|
35
|
+
width: 60%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.main-btn-menu.active {
|
|
39
|
+
background: rgba(255, 255, 255, 0.1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.main-btn-menu.active::after {
|
|
43
|
+
width: 80%;
|
|
44
|
+
background: currentColor;
|
|
45
|
+
}
|
|
46
|
+
`,
|
|
47
|
+
|
|
15
48
|
loadThemes: async function (themes = []) {
|
|
16
49
|
ThemesScope = [];
|
|
17
50
|
for (const themeOptions of themes) addTheme(themeOptions);
|
|
@@ -31,6 +64,16 @@ const Css = {
|
|
|
31
64
|
Init: async function (options) {
|
|
32
65
|
if (!options) options = ThemesScope[0];
|
|
33
66
|
const { theme } = options;
|
|
67
|
+
|
|
68
|
+
// Inject menu button container styles
|
|
69
|
+
const styleId = 'menu-btn-container-styles';
|
|
70
|
+
if (!document.getElementById(styleId)) {
|
|
71
|
+
const style = document.createElement('style');
|
|
72
|
+
style.id = styleId;
|
|
73
|
+
style.textContent = this.menuButtonContainer();
|
|
74
|
+
document.head.appendChild(style);
|
|
75
|
+
}
|
|
76
|
+
|
|
34
77
|
return await Themes[theme](options);
|
|
35
78
|
},
|
|
36
79
|
RenderSetting: async function () {
|
|
@@ -811,6 +854,12 @@ const imageShimmer = () => html`<div
|
|
|
811
854
|
</div>
|
|
812
855
|
</div>`;
|
|
813
856
|
|
|
857
|
+
const simpleIconsRender = (selector) => {
|
|
858
|
+
sa(selector).forEach((el) => {
|
|
859
|
+
el.src = `https://cdn.simpleicons.org/coveralls/${rgbToHex(window.getComputedStyle(s('html')).color)}`;
|
|
860
|
+
});
|
|
861
|
+
};
|
|
862
|
+
|
|
814
863
|
export {
|
|
815
864
|
Css,
|
|
816
865
|
Themes,
|
|
@@ -843,4 +892,5 @@ export {
|
|
|
843
892
|
renderWave,
|
|
844
893
|
cssEffect,
|
|
845
894
|
imageShimmer,
|
|
895
|
+
simpleIconsRender,
|
|
846
896
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Badge } from './Badge.js';
|
|
2
2
|
import { BtnIcon } from './BtnIcon.js';
|
|
3
3
|
import { rgbToHex } from './CommonJs.js';
|
|
4
|
-
import { Css, darkTheme, dynamicCol, renderCssAttr, ThemeEvents, Themes } from './Css.js';
|
|
4
|
+
import { Css, darkTheme, dynamicCol, renderCssAttr, simpleIconsRender, ThemeEvents, Themes } from './Css.js';
|
|
5
5
|
import { DropDown } from './DropDown.js';
|
|
6
6
|
import { buildBadgeToolTipMenuOption, Modal, renderMenuLabel, renderViewTitle } from './Modal.js';
|
|
7
7
|
import { listenQueryPathInstance, setQueryPath } from './Router.js';
|
|
8
8
|
import { Translate } from './Translate.js';
|
|
9
|
-
import { getProxyPath, getQueryParams, htmls, s } from './VanillaJs.js';
|
|
9
|
+
import { getProxyPath, getQueryParams, htmls, s, sa } from './VanillaJs.js';
|
|
10
10
|
import Sortable from 'sortablejs';
|
|
11
11
|
|
|
12
12
|
// https://mintlify.com/docs/quickstart
|
|
@@ -46,6 +46,11 @@ const Docs = {
|
|
|
46
46
|
Modal.Data[ModalId].onObserverListener[ModalId] = () => {
|
|
47
47
|
if (s(`.iframe-${ModalId}`))
|
|
48
48
|
s(`.iframe-${ModalId}`).style.height = `${s(`.${ModalId}`).offsetHeight - Modal.headerTitleHeight}px`;
|
|
49
|
+
|
|
50
|
+
if (type.match('coverage')) {
|
|
51
|
+
simpleIconsRender(`.doc-icon-coverage`);
|
|
52
|
+
simpleIconsRender(`.doc-icon-coverage-link`);
|
|
53
|
+
}
|
|
49
54
|
};
|
|
50
55
|
Modal.Data[ModalId].onObserverListener[ModalId]();
|
|
51
56
|
},
|
|
@@ -96,10 +101,7 @@ const Docs = {
|
|
|
96
101
|
return `${getProxyPath()}docs/coverage`;
|
|
97
102
|
},
|
|
98
103
|
themeEvent: () => {
|
|
99
|
-
if (s(`.doc-icon-coverage`))
|
|
100
|
-
s(`.doc-icon-coverage`).src = `https://cdn.simpleicons.org/coveralls/${rgbToHex(
|
|
101
|
-
window.getComputedStyle(s('html')).color,
|
|
102
|
-
)}`;
|
|
104
|
+
if (s(`.doc-icon-coverage`)) setTimeout(() => simpleIconsRender(`.doc-icon-coverage`));
|
|
103
105
|
},
|
|
104
106
|
},
|
|
105
107
|
{
|
|
@@ -110,10 +112,7 @@ const Docs = {
|
|
|
110
112
|
return `https://coveralls.io/github/underpostnet/engine`;
|
|
111
113
|
},
|
|
112
114
|
themeEvent: () => {
|
|
113
|
-
if (s(`.doc-icon-coverage-link`))
|
|
114
|
-
s(`.doc-icon-coverage-link`).src = `https://cdn.simpleicons.org/coveralls/${rgbToHex(
|
|
115
|
-
window.getComputedStyle(s('html')).color,
|
|
116
|
-
)}`;
|
|
115
|
+
if (s(`.doc-icon-coverage-link`)) setTimeout(() => simpleIconsRender(`.doc-icon-coverage-link`));
|
|
117
116
|
},
|
|
118
117
|
},
|
|
119
118
|
],
|
|
@@ -8,7 +8,7 @@ const DropDown = {
|
|
|
8
8
|
Tokens: {},
|
|
9
9
|
Render: async function (options) {
|
|
10
10
|
const id = options.id ? options.id : getId(this.Tokens, 'dropdown-');
|
|
11
|
-
this.Tokens[id] = { onClickEvents: {}, lastSelectValue: undefined };
|
|
11
|
+
this.Tokens[id] = { onClickEvents: {}, lastSelectValue: undefined, oncheckvalues: {} };
|
|
12
12
|
|
|
13
13
|
options.data.push({
|
|
14
14
|
value: 'reset',
|
|
@@ -39,6 +39,96 @@ const DropDown = {
|
|
|
39
39
|
else s(`.dropdown-option-${id}`).classList.add('hide');
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
const _render = async (data) => {
|
|
43
|
+
let render = '';
|
|
44
|
+
let index = -1;
|
|
45
|
+
for (const optionData of data) {
|
|
46
|
+
index++;
|
|
47
|
+
const i = index;
|
|
48
|
+
const valueDisplay = optionData.value.trim().replaceAll(' ', '-');
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
const onclick = (e) => {
|
|
51
|
+
if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
|
|
52
|
+
s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.remove(options.lastSelectClass);
|
|
53
|
+
}
|
|
54
|
+
this.Tokens[id].lastSelectValue = valueDisplay;
|
|
55
|
+
if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
|
|
56
|
+
s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.add(options.lastSelectClass);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
!(options && options.disableClose) &&
|
|
61
|
+
(options.type !== 'checkbox' || optionData.value === 'close' || optionData.value === 'reset')
|
|
62
|
+
)
|
|
63
|
+
s(`.dropdown-option-${id}`).classList.add('hide');
|
|
64
|
+
|
|
65
|
+
if (options.type === 'checkbox' && ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`])
|
|
66
|
+
ToggleSwitch.Tokens[`checkbox-role-${valueDisplay}`].click();
|
|
67
|
+
if (optionData.value !== 'close') {
|
|
68
|
+
if (optionData.value !== 'reset')
|
|
69
|
+
htmls(
|
|
70
|
+
`.dropdown-current-${id}`,
|
|
71
|
+
options.type === 'checkbox'
|
|
72
|
+
? data
|
|
73
|
+
.filter((d) => d.checked)
|
|
74
|
+
.map((v, i, a) => `${v.display}${i < a.length - 1 ? ',' : ''}`)
|
|
75
|
+
.join('')
|
|
76
|
+
: optionData.display,
|
|
77
|
+
);
|
|
78
|
+
else htmls(`.dropdown-current-${id}`, '');
|
|
79
|
+
|
|
80
|
+
this.Tokens[id].value =
|
|
81
|
+
options.type === 'checkbox' ? data.filter((d) => d.checked).map((d) => d.data) : optionData.data;
|
|
82
|
+
|
|
83
|
+
console.warn('current value dropdown id:' + id, this.Tokens[id].value);
|
|
84
|
+
|
|
85
|
+
s(`.${id}`).value = this.Tokens[id].value;
|
|
86
|
+
|
|
87
|
+
optionData.onClick(e);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
this.Tokens[id].onClickEvents[`dropdown-option-${id}-${i}`] = onclick;
|
|
92
|
+
this.Tokens[id].onClickEvents[`dropdown-option-${id}-${valueDisplay}`] = onclick;
|
|
93
|
+
this.Tokens[id].onClickEvents[`dropdown-option-${valueDisplay}`] = onclick;
|
|
94
|
+
|
|
95
|
+
s(`.dropdown-option-${id}-${i}`).onclick = onclick;
|
|
96
|
+
});
|
|
97
|
+
render += html`
|
|
98
|
+
<div
|
|
99
|
+
class="in dropdown-option dropdown-option-${id}-${i} dropdown-option-${id}-${valueDisplay} dropdown-option-${valueDisplay} ${valueDisplay ===
|
|
100
|
+
'reset' &&
|
|
101
|
+
options &&
|
|
102
|
+
!(options.resetOption === true)
|
|
103
|
+
? 'hide'
|
|
104
|
+
: ''}"
|
|
105
|
+
>
|
|
106
|
+
${options.type === 'checkbox' && optionData.value !== 'close' && optionData.value !== 'reset'
|
|
107
|
+
? html`
|
|
108
|
+
${await ToggleSwitch.Render({
|
|
109
|
+
id: `checkbox-role-${valueDisplay}`,
|
|
110
|
+
type: 'checkbox',
|
|
111
|
+
disabledOnClick: true,
|
|
112
|
+
checked: optionData.checked,
|
|
113
|
+
on: {
|
|
114
|
+
unchecked: () => {
|
|
115
|
+
optionData.checked = false;
|
|
116
|
+
delete DropDown.Tokens[id].oncheckvalues[valueDisplay];
|
|
117
|
+
},
|
|
118
|
+
checked: () => {
|
|
119
|
+
optionData.checked = true;
|
|
120
|
+
DropDown.Tokens[id].oncheckvalues[valueDisplay] = {};
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
})}
|
|
124
|
+
`
|
|
125
|
+
: ''}${optionData.display}
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
return { render, index };
|
|
130
|
+
};
|
|
131
|
+
|
|
42
132
|
setTimeout(() => {
|
|
43
133
|
if (options.type === 'checkbox')
|
|
44
134
|
options.data.map((optionData) => {
|
|
@@ -53,92 +143,48 @@ const DropDown = {
|
|
|
53
143
|
s(`.dropdown-label-${id}`).onclick = switchOptionsPanel;
|
|
54
144
|
s(`.dropdown-current-${id}`).onclick = switchOptionsPanel;
|
|
55
145
|
if (options && options.open) switchOptionsPanel();
|
|
56
|
-
});
|
|
57
146
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
index++;
|
|
62
|
-
const i = index;
|
|
63
|
-
const valueDisplay = optionData.value.trim().replaceAll(' ', '-');
|
|
64
|
-
setTimeout(() => {
|
|
65
|
-
const onclick = (e) => {
|
|
66
|
-
if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
|
|
67
|
-
s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.remove(options.lastSelectClass);
|
|
68
|
-
}
|
|
69
|
-
this.Tokens[id].lastSelectValue = valueDisplay;
|
|
70
|
-
if (options && options.lastSelectClass && s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`)) {
|
|
71
|
-
s(`.dropdown-option-${this.Tokens[id].lastSelectValue}`).classList.add(options.lastSelectClass);
|
|
72
|
-
}
|
|
147
|
+
const dropDownSearchHandle = async () => {
|
|
148
|
+
const _data = [];
|
|
149
|
+
if (!s(`.search-box-${id}`)) return;
|
|
73
150
|
|
|
151
|
+
let _value = s(`.search-box-${id}`).value.toLowerCase();
|
|
152
|
+
|
|
153
|
+
for (const objData of options.data) {
|
|
154
|
+
const objValue = objData.value.toLowerCase();
|
|
74
155
|
if (
|
|
75
|
-
|
|
76
|
-
(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (optionData.value !== 'close') {
|
|
83
|
-
if (optionData.value !== 'reset')
|
|
84
|
-
htmls(
|
|
85
|
-
`.dropdown-current-${id}`,
|
|
86
|
-
options.type === 'checkbox'
|
|
87
|
-
? options.data
|
|
88
|
-
.filter((d) => d.checked)
|
|
89
|
-
.map((v, i, a) => `${v.display}${i < a.length - 1 ? ',' : ''}`)
|
|
90
|
-
.join('')
|
|
91
|
-
: optionData.display,
|
|
92
|
-
);
|
|
93
|
-
else htmls(`.dropdown-current-${id}`, '');
|
|
94
|
-
|
|
95
|
-
this.Tokens[id].value =
|
|
96
|
-
options.type === 'checkbox' ? options.data.filter((d) => d.checked).map((d) => d.data) : optionData.data;
|
|
97
|
-
|
|
98
|
-
console.warn('current value dropdown id:' + id, this.Tokens[id].value);
|
|
99
|
-
|
|
100
|
-
s(`.${id}`).value = this.Tokens[id].value;
|
|
101
|
-
|
|
102
|
-
optionData.onClick(e);
|
|
156
|
+
objValue.match(_value) ||
|
|
157
|
+
(Translate.Data[objData.value] &&
|
|
158
|
+
Object.keys(Translate.Data[objData.value]).find((t) =>
|
|
159
|
+
Translate.Data[objData.value][t].toLowerCase().match(_value),
|
|
160
|
+
))
|
|
161
|
+
) {
|
|
162
|
+
_data.push(objData);
|
|
103
163
|
}
|
|
104
|
-
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (_data.length > 0) {
|
|
167
|
+
const { render, index } = await _render(_data);
|
|
168
|
+
htmls(`.${id}-render-container`, render);
|
|
169
|
+
} else {
|
|
170
|
+
// const { render, index } = await _render(options.data);
|
|
171
|
+
htmls(
|
|
172
|
+
`.${id}-render-container`,
|
|
173
|
+
html` <div class="inl" style="padding: 10px; color: red">
|
|
174
|
+
<i class="fas fa-exclamation-circle"></i> ${Translate.Render('no-result-found')}
|
|
175
|
+
</div>`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
s(`.search-box-${id}`).oninput = dropDownSearchHandle;
|
|
181
|
+
|
|
182
|
+
// Not use onblur generate bug on input toggle
|
|
183
|
+
// s(`.search-box-${id}`).onblur = dropDownSearchHandle;
|
|
184
|
+
});
|
|
105
185
|
|
|
106
|
-
|
|
107
|
-
this.Tokens[id].onClickEvents[`dropdown-option-${id}-${valueDisplay}`] = onclick;
|
|
108
|
-
this.Tokens[id].onClickEvents[`dropdown-option-${valueDisplay}`] = onclick;
|
|
186
|
+
const { render, index } = await _render(options.data);
|
|
109
187
|
|
|
110
|
-
s(`.dropdown-option-${id}-${i}`).onclick = onclick;
|
|
111
|
-
});
|
|
112
|
-
render += html`
|
|
113
|
-
<div
|
|
114
|
-
class="in dropdown-option dropdown-option-${id}-${i} dropdown-option-${id}-${valueDisplay} dropdown-option-${valueDisplay} ${valueDisplay ===
|
|
115
|
-
'reset' &&
|
|
116
|
-
options &&
|
|
117
|
-
!(options.resetOption === true)
|
|
118
|
-
? 'hide'
|
|
119
|
-
: ''}"
|
|
120
|
-
>
|
|
121
|
-
${options.type === 'checkbox' && optionData.value !== 'close' && optionData.value !== 'reset'
|
|
122
|
-
? html`
|
|
123
|
-
${await ToggleSwitch.Render({
|
|
124
|
-
id: `checkbox-role-${valueDisplay}`,
|
|
125
|
-
type: 'checkbox',
|
|
126
|
-
disabledOnClick: true,
|
|
127
|
-
checked: optionData.checked,
|
|
128
|
-
on: {
|
|
129
|
-
unchecked: () => {
|
|
130
|
-
optionData.checked = false;
|
|
131
|
-
},
|
|
132
|
-
checked: () => {
|
|
133
|
-
optionData.checked = true;
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
})}
|
|
137
|
-
`
|
|
138
|
-
: ''}${optionData.display}
|
|
139
|
-
</div>
|
|
140
|
-
`;
|
|
141
|
-
}
|
|
142
188
|
return html`
|
|
143
189
|
<div class="inl dropdown-container ${id} ${options?.containerClass ? options.containerClass : ''}">
|
|
144
190
|
<div class="in dropdown-option dropdown-label-${id} ${options && options.disableSelectLabel ? 'hide' : ''}">
|
|
@@ -158,7 +204,7 @@ const DropDown = {
|
|
|
158
204
|
placeholder: true,
|
|
159
205
|
})}
|
|
160
206
|
</div>
|
|
161
|
-
${render}
|
|
207
|
+
<div class="${id}-render-container">${render}</div>
|
|
162
208
|
</div>
|
|
163
209
|
</div>
|
|
164
210
|
`;
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
renderStatus,
|
|
25
25
|
renderCssAttr,
|
|
26
26
|
} from './Css.js';
|
|
27
|
-
import { setDocTitle } from './Router.js';
|
|
27
|
+
import { setDocTitle, closeModalRouteChangeEvent, handleModalViewRoute } from './Router.js';
|
|
28
28
|
import { NotificationManager } from './NotificationManager.js';
|
|
29
29
|
import { EventsUI } from './EventsUI.js';
|
|
30
30
|
import { Translate } from './Translate.js';
|
|
@@ -126,19 +126,13 @@ const Modal = {
|
|
|
126
126
|
};
|
|
127
127
|
Responsive.Event[`view-${idModal}`]();
|
|
128
128
|
|
|
129
|
-
//
|
|
130
|
-
if (options.route)
|
|
131
|
-
(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (path !== newPath) {
|
|
137
|
-
// console.warn('SET MODAL URI', newPath);
|
|
138
|
-
setPath(`${newPath}`); // ${location.search}
|
|
139
|
-
setDocTitle({ ...options.RouterInstance, route: options.route });
|
|
140
|
-
}
|
|
141
|
-
})();
|
|
129
|
+
// Handle view mode modal route
|
|
130
|
+
if (options.route) {
|
|
131
|
+
handleModalViewRoute({
|
|
132
|
+
route: options.route,
|
|
133
|
+
RouterInstance: options.RouterInstance,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
142
136
|
|
|
143
137
|
break;
|
|
144
138
|
case 'slide-menu':
|
|
@@ -697,6 +691,7 @@ const Modal = {
|
|
|
697
691
|
Modal.MoveTitleToBar(id);
|
|
698
692
|
|
|
699
693
|
prepend(`.btn-bar-modal-container-${id}`, html`<div class="hide">${inputInfoNode.outerHTML}</div>`);
|
|
694
|
+
if (s(`.slide-menu-top-bar-fix`)) s(`.slide-menu-top-bar-fix`).classList.add('hide');
|
|
700
695
|
}
|
|
701
696
|
};
|
|
702
697
|
|
|
@@ -716,6 +711,7 @@ const Modal = {
|
|
|
716
711
|
} catch (e) {}
|
|
717
712
|
}
|
|
718
713
|
Modal.removeModal(searchBoxHistoryId);
|
|
714
|
+
if (s(`.slide-menu-top-bar-fix`)) s(`.slide-menu-top-bar-fix`).classList.remove('hide');
|
|
719
715
|
};
|
|
720
716
|
s('.top-bar-search-box').onblur = () => {
|
|
721
717
|
hoverFocusCtl.checkDismiss();
|
|
@@ -890,6 +886,9 @@ const Modal = {
|
|
|
890
886
|
],
|
|
891
887
|
eventCallBack: () => {
|
|
892
888
|
if (s(`.top-bar-search-box`)) {
|
|
889
|
+
if (s(`.main-body-btn-ui-close`).classList.contains('hide')) {
|
|
890
|
+
s(`.main-body-btn-ui-open`).click();
|
|
891
|
+
}
|
|
893
892
|
s(`.top-bar-search-box`).blur();
|
|
894
893
|
s(`.top-bar-search-box`).focus();
|
|
895
894
|
s(`.top-bar-search-box`).select();
|
|
@@ -1089,12 +1088,13 @@ const Modal = {
|
|
|
1089
1088
|
icon: html`<i class="fas fa-language mini-title"></i>`,
|
|
1090
1089
|
text: Translate.Render('select lang'),
|
|
1091
1090
|
})}`,
|
|
1092
|
-
html: () => html
|
|
1091
|
+
html: async () => html`${await Translate.RenderSetting('action-drop-modal' + id)}`,
|
|
1093
1092
|
titleClass: 'mini-title',
|
|
1094
1093
|
style: {
|
|
1095
1094
|
resize: 'none',
|
|
1096
|
-
|
|
1097
|
-
height: '
|
|
1095
|
+
width: '100% !important',
|
|
1096
|
+
height: '350px !important',
|
|
1097
|
+
'max-width': '350px !important',
|
|
1098
1098
|
'z-index': 7,
|
|
1099
1099
|
},
|
|
1100
1100
|
dragDisabled: true,
|
|
@@ -1652,27 +1652,14 @@ const Modal = {
|
|
|
1652
1652
|
setTimeout(() => {
|
|
1653
1653
|
if (!s(`.${idModal}`)) return;
|
|
1654
1654
|
this.removeModal(idModal);
|
|
1655
|
-
//
|
|
1656
|
-
if (options.route)
|
|
1657
|
-
(
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
if (this.Data[subIdModal].options.route) {
|
|
1664
|
-
newPath = `${newPath}${this.Data[subIdModal].options.route}`;
|
|
1665
|
-
console.warn('------------> SET MODAL URI', newPath);
|
|
1666
|
-
setPath(newPath);
|
|
1667
|
-
this.setTopModalCallback(subIdModal);
|
|
1668
|
-
return setDocTitle({ ...options.RouterInstance, route: this.Data[subIdModal].options.route });
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
console.warn('-------------> SET MODAL URI', newPath);
|
|
1672
|
-
setPath(`${newPath}${Modal.homeCid ? `?cid=${Modal.homeCid}` : ''}`);
|
|
1673
|
-
return setDocTitle({ ...options.RouterInstance, route: '' });
|
|
1674
|
-
}
|
|
1675
|
-
})();
|
|
1655
|
+
// Handle modal route change
|
|
1656
|
+
if (options.route) {
|
|
1657
|
+
closeModalRouteChangeEvent({
|
|
1658
|
+
route: options.route,
|
|
1659
|
+
RouterInstance: options.RouterInstance,
|
|
1660
|
+
homeCid: Modal.homeCid,
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1676
1663
|
}, 300);
|
|
1677
1664
|
};
|
|
1678
1665
|
s(`.btn-close-${idModal}`).onclick = btnCloseEvent;
|
|
@@ -15,7 +15,7 @@ import { getSrcFromFileData } from './Input.js';
|
|
|
15
15
|
import { imageShimmer, renderCssAttr } from './Css.js';
|
|
16
16
|
import { Translate } from './Translate.js';
|
|
17
17
|
import { Modal } from './Modal.js';
|
|
18
|
-
import { listenQueryPathInstance, setQueryPath } from './Router.js';
|
|
18
|
+
import { closeModalRouteChangeEvents, listenQueryPathInstance, renderTitle, setQueryPath } from './Router.js';
|
|
19
19
|
|
|
20
20
|
const PanelForm = {
|
|
21
21
|
Data: {},
|
|
@@ -425,15 +425,22 @@ const PanelForm = {
|
|
|
425
425
|
});
|
|
426
426
|
let firsUpdateEvent = false;
|
|
427
427
|
let lastCid;
|
|
428
|
-
|
|
428
|
+
let lastUserId;
|
|
429
|
+
closeModalRouteChangeEvents[idPanel] = (newPath) => {
|
|
430
|
+
if (newPath.split('?')[0] === '/' && PanelForm.Data[idPanel].data && PanelForm.Data[idPanel].data.length === 0) {
|
|
431
|
+
this.Data[idPanel].updatePanel();
|
|
432
|
+
}
|
|
433
|
+
};
|
|
429
434
|
this.Data[idPanel].updatePanel = async () => {
|
|
430
|
-
console.warn('-------------> updatePanel 2');
|
|
431
435
|
const cid = getQueryParams().cid ? getQueryParams().cid : '';
|
|
432
|
-
|
|
436
|
+
const forceUpdate = lastUserId !== Elements.Data.user.main.model.user._id;
|
|
437
|
+
if (lastCid === cid && !forceUpdate) return;
|
|
438
|
+
lastUserId = newInstance(Elements.Data.user.main.model.user._id);
|
|
433
439
|
lastCid = cid;
|
|
434
440
|
if (options.route === 'home') Modal.homeCid = newInstance(cid);
|
|
435
441
|
htmls(`.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`, await renderSrrPanelData());
|
|
436
442
|
await getPanelData();
|
|
443
|
+
if (this.Data[idPanel].data.length === 1) renderTitle(this.Data[idPanel].data[0].title);
|
|
437
444
|
htmls(
|
|
438
445
|
`.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`,
|
|
439
446
|
await panelRender({ data: this.Data[idPanel].data }),
|
|
@@ -454,6 +461,7 @@ const PanelForm = {
|
|
|
454
461
|
if (!options.parentIdModal)
|
|
455
462
|
Modal.Data['modal-menu'].onHome[idPanel] = async () => {
|
|
456
463
|
lastCid = undefined;
|
|
464
|
+
lastUserId = undefined;
|
|
457
465
|
setQueryPath({ path: options.route, queryPath: '' });
|
|
458
466
|
await this.Data[idPanel].updatePanel();
|
|
459
467
|
};
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { titleFormatted } from './CommonJs.js';
|
|
2
2
|
import { loggerFactory } from './Logger.js';
|
|
3
3
|
import { getProxyPath, getQueryParams, htmls, s, setPath } from './VanillaJs.js';
|
|
4
|
+
import { Modal } from './Modal.js';
|
|
5
|
+
import { Worker } from './Worker.js';
|
|
4
6
|
|
|
5
7
|
// Router
|
|
6
8
|
|
|
7
9
|
const logger = loggerFactory(import.meta);
|
|
8
10
|
|
|
11
|
+
const renderTitle = (title, nameApp) => htmls('title', html`${title} | ${nameApp ?? Worker.RouterInstance.NameApp}`);
|
|
12
|
+
|
|
9
13
|
const setDocTitle = (options = { Routes: () => {}, route: '', NameApp: '' }) => {
|
|
10
14
|
const { Routes, route, NameApp } = options;
|
|
11
15
|
let title = titleFormatted(Routes()[`/${route}`].title);
|
|
12
16
|
if (Routes()[`/${route}`].upperCase) title = title.toUpperCase();
|
|
13
|
-
|
|
17
|
+
renderTitle(title, NameApp);
|
|
14
18
|
{
|
|
15
19
|
const routeId = route === '' ? 'home' : route;
|
|
16
20
|
if (s(`.main-btn-${routeId}`)) {
|
|
@@ -75,4 +79,61 @@ const listenQueryPathInstance = ({ id, routeId, event }, queryKey = 'cid') => {
|
|
|
75
79
|
});
|
|
76
80
|
};
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
const closeModalRouteChangeEvents = {};
|
|
83
|
+
const triggerCloseModalRouteChangeEvents = (newPath) => {
|
|
84
|
+
console.warn('[closeModalRouteChangeEvent]', newPath);
|
|
85
|
+
for (const event of Object.keys(closeModalRouteChangeEvents)) closeModalRouteChangeEvents[event](newPath);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const closeModalRouteChangeEvent = (options = {}) => {
|
|
89
|
+
const { route, RouterInstance, homeCid } = options;
|
|
90
|
+
if (!route) return;
|
|
91
|
+
|
|
92
|
+
let path = window.location.pathname;
|
|
93
|
+
if (path[path.length - 1] !== '/') path = `${path}/`;
|
|
94
|
+
let newPath = `${getProxyPath()}`;
|
|
95
|
+
|
|
96
|
+
if (path !== newPath) {
|
|
97
|
+
for (const subIdModal of Object.keys(Modal.Data).reverse()) {
|
|
98
|
+
if (Modal.Data[subIdModal]?.options?.route) {
|
|
99
|
+
newPath = `${newPath}${Modal.Data[subIdModal].options.route}`;
|
|
100
|
+
triggerCloseModalRouteChangeEvents(newPath);
|
|
101
|
+
setPath(newPath);
|
|
102
|
+
Modal.setTopModalCallback(subIdModal);
|
|
103
|
+
return setDocTitle({ ...RouterInstance, route: Modal.Data[subIdModal].options.route });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
newPath = `${newPath}${homeCid ? `?cid=${homeCid}` : ''}`;
|
|
107
|
+
triggerCloseModalRouteChangeEvents(newPath);
|
|
108
|
+
setPath(newPath);
|
|
109
|
+
return setDocTitle({ ...RouterInstance, route: '' });
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const handleModalViewRoute = (options = {}) => {
|
|
114
|
+
const { route, RouterInstance } = options;
|
|
115
|
+
if (!route) return;
|
|
116
|
+
|
|
117
|
+
let path = window.location.pathname;
|
|
118
|
+
if (path !== '/' && path[path.length - 1] === '/') path = path.slice(0, -1);
|
|
119
|
+
const proxyPath = getProxyPath();
|
|
120
|
+
const newPath = `${proxyPath}${route}`;
|
|
121
|
+
|
|
122
|
+
if (path !== newPath) {
|
|
123
|
+
setPath(newPath);
|
|
124
|
+
setDocTitle({ ...RouterInstance, route });
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
Router,
|
|
130
|
+
setDocTitle,
|
|
131
|
+
LoadRouter,
|
|
132
|
+
RouterEvents,
|
|
133
|
+
setQueryPath,
|
|
134
|
+
listenQueryPathInstance,
|
|
135
|
+
closeModalRouteChangeEvent,
|
|
136
|
+
handleModalViewRoute,
|
|
137
|
+
closeModalRouteChangeEvents,
|
|
138
|
+
renderTitle,
|
|
139
|
+
};
|
|
@@ -60,10 +60,10 @@ const Translate = {
|
|
|
60
60
|
this.Parse(language);
|
|
61
61
|
if (s(`.action-btn-lang-render`)) htmls(`.action-btn-lang-render`, s('html').lang);
|
|
62
62
|
},
|
|
63
|
-
RenderSetting: async function () {
|
|
63
|
+
RenderSetting: async function (id) {
|
|
64
64
|
return html` <div class="in section-mp">
|
|
65
65
|
${await DropDown.Render({
|
|
66
|
-
id: 'settings-lang',
|
|
66
|
+
id: id ?? 'settings-lang',
|
|
67
67
|
value: s('html').lang ? s('html').lang : 'en',
|
|
68
68
|
label: html`${Translate.Render('lang')}`,
|
|
69
69
|
data: ['en', 'es'].map((language) => {
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import swaggerAutoGen from 'swagger-autogen';
|
|
5
|
+
import { shellExec } from './process.js';
|
|
6
|
+
import { loggerFactory } from './logger.js';
|
|
7
|
+
import { JSONweb } from './client-formatted.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Builds API documentation using Swagger
|
|
11
|
+
* @param {Object} options - Documentation build options
|
|
12
|
+
* @param {string} options.host - The hostname for the API
|
|
13
|
+
* @param {string} options.path - The base path for the API
|
|
14
|
+
* @param {number} options.port - The port number for the API
|
|
15
|
+
* @param {Object} options.metadata - Metadata for the API documentation
|
|
16
|
+
* @param {Array<string>} options.apis - List of API modules to document
|
|
17
|
+
* @param {string} options.publicClientId - Client ID for the public documentation
|
|
18
|
+
* @param {string} options.rootClientPath - Root path for client files
|
|
19
|
+
* @param {Object} options.packageData - Package.json data
|
|
20
|
+
*/
|
|
21
|
+
const buildApiDocs = async ({
|
|
22
|
+
host,
|
|
23
|
+
path,
|
|
24
|
+
port,
|
|
25
|
+
metadata = {},
|
|
26
|
+
apis = [],
|
|
27
|
+
publicClientId,
|
|
28
|
+
rootClientPath,
|
|
29
|
+
packageData,
|
|
30
|
+
}) => {
|
|
31
|
+
const logger = loggerFactory(import.meta);
|
|
32
|
+
const basePath = path === '/' ? `${process.env.BASE_API}` : `/${process.env.BASE_API}`;
|
|
33
|
+
|
|
34
|
+
const doc = {
|
|
35
|
+
info: {
|
|
36
|
+
version: packageData.version,
|
|
37
|
+
title: metadata?.title ? `${metadata.title}` : 'REST API',
|
|
38
|
+
description: metadata?.description ? metadata.description : '',
|
|
39
|
+
},
|
|
40
|
+
servers: [
|
|
41
|
+
{
|
|
42
|
+
url:
|
|
43
|
+
process.env.NODE_ENV === 'development'
|
|
44
|
+
? `http://localhost:${port}${path}${basePath}`
|
|
45
|
+
: `https://${host}${path}${basePath}`,
|
|
46
|
+
description: `${process.env.NODE_ENV} server`,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
tags: [
|
|
50
|
+
{
|
|
51
|
+
name: 'user',
|
|
52
|
+
description: 'User API operations',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
components: {
|
|
56
|
+
schemas: {
|
|
57
|
+
userRequest: {
|
|
58
|
+
username: 'user123',
|
|
59
|
+
password: 'Password123',
|
|
60
|
+
email: 'user@example.com',
|
|
61
|
+
},
|
|
62
|
+
userResponse: {
|
|
63
|
+
status: 'success',
|
|
64
|
+
data: {
|
|
65
|
+
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6IjY2YzM3N2Y1N2Y5OWU1OTY5YjgxZG...',
|
|
66
|
+
user: {
|
|
67
|
+
_id: '66c377f57f99e5969b81de89',
|
|
68
|
+
email: 'user@example.com',
|
|
69
|
+
emailConfirmed: false,
|
|
70
|
+
username: 'user123',
|
|
71
|
+
role: 'user',
|
|
72
|
+
profileImageId: '66c377f57f99e5969b81de87',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
userUpdateResponse: {
|
|
77
|
+
status: 'success',
|
|
78
|
+
data: {
|
|
79
|
+
_id: '66c377f57f99e5969b81de89',
|
|
80
|
+
email: 'user@example.com',
|
|
81
|
+
emailConfirmed: false,
|
|
82
|
+
username: 'user123222',
|
|
83
|
+
role: 'user',
|
|
84
|
+
profileImageId: '66c377f57f99e5969b81de87',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
userGetResponse: {
|
|
88
|
+
status: 'success',
|
|
89
|
+
data: {
|
|
90
|
+
_id: '66c377f57f99e5969b81de89',
|
|
91
|
+
email: 'user@example.com',
|
|
92
|
+
emailConfirmed: false,
|
|
93
|
+
username: 'user123222',
|
|
94
|
+
role: 'user',
|
|
95
|
+
profileImageId: '66c377f57f99e5969b81de87',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
userLogInRequest: {
|
|
99
|
+
email: 'user@example.com',
|
|
100
|
+
password: 'Password123',
|
|
101
|
+
},
|
|
102
|
+
userBadRequestResponse: {
|
|
103
|
+
status: 'error',
|
|
104
|
+
message: 'Bad request. Please check your inputs, and try again',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
securitySchemes: {
|
|
108
|
+
bearerAuth: {
|
|
109
|
+
type: 'http',
|
|
110
|
+
scheme: 'bearer',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
logger.warn('build swagger api docs', doc.info);
|
|
117
|
+
|
|
118
|
+
const outputFile = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
|
|
119
|
+
const routes = [];
|
|
120
|
+
for (const api of apis) {
|
|
121
|
+
if (['user'].includes(api)) routes.push(`./src/api/${api}/${api}.router.js`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Builds JSDoc documentation
|
|
129
|
+
* @param {Object} options - JSDoc build options
|
|
130
|
+
* @param {string} options.host - The hostname for the documentation
|
|
131
|
+
* @param {string} options.path - The base path for the documentation
|
|
132
|
+
* @param {Object} options.metadata - Metadata for the documentation
|
|
133
|
+
*/
|
|
134
|
+
const buildJsDocs = async ({ host, path, metadata = {} }) => {
|
|
135
|
+
const logger = loggerFactory(import.meta);
|
|
136
|
+
const jsDocsConfig = JSON.parse(fs.readFileSync(`./jsdoc.json`, 'utf8'));
|
|
137
|
+
|
|
138
|
+
jsDocsConfig.opts.destination = `./public/${host}${path === '/' ? path : `${path}/`}docs/`;
|
|
139
|
+
jsDocsConfig.opts.theme_opts.title = metadata?.title ? metadata.title : undefined;
|
|
140
|
+
jsDocsConfig.opts.theme_opts.favicon = `./public/${host}${path === '/' ? path : `${path}/favicon.ico`}`;
|
|
141
|
+
|
|
142
|
+
fs.writeFileSync(`./jsdoc.json`, JSON.stringify(jsDocsConfig, null, 4), 'utf8');
|
|
143
|
+
logger.warn('build jsdoc view', jsDocsConfig.opts.destination);
|
|
144
|
+
|
|
145
|
+
shellExec(`npm run docs`, { silent: true });
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Builds test coverage documentation
|
|
150
|
+
* @param {Object} options - Coverage build options
|
|
151
|
+
* @param {string} options.host - The hostname for the coverage
|
|
152
|
+
* @param {string} options.path - The base path for the coverage
|
|
153
|
+
*/
|
|
154
|
+
const buildCoverage = async ({ host, path }) => {
|
|
155
|
+
const logger = loggerFactory(import.meta);
|
|
156
|
+
const jsDocsConfig = JSON.parse(fs.readFileSync(`./jsdoc.json`, 'utf8'));
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(`./coverage`)) {
|
|
159
|
+
shellExec(`npm test`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const coverageBuildPath = `${jsDocsConfig.opts.destination}/coverage`;
|
|
163
|
+
fs.mkdirSync(coverageBuildPath, { recursive: true });
|
|
164
|
+
fs.copySync(`./coverage`, coverageBuildPath);
|
|
165
|
+
|
|
166
|
+
logger.warn('build coverage', coverageBuildPath);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Main function to build all documentation
|
|
171
|
+
* @param {Object} options - Documentation build options
|
|
172
|
+
* @param {string} options.host - The hostname
|
|
173
|
+
* @param {string} options.path - The base path
|
|
174
|
+
* @param {number} options.port - The port number
|
|
175
|
+
* @param {Object} options.metadata - Metadata for the documentation
|
|
176
|
+
* @param {Array<string>} options.apis - List of API modules to document
|
|
177
|
+
* @param {string} options.publicClientId - Client ID for the public documentation
|
|
178
|
+
* @param {string} options.rootClientPath - Root path for client files
|
|
179
|
+
* @param {Object} options.packageData - Package.json data
|
|
180
|
+
*/
|
|
181
|
+
const buildDocs = async ({
|
|
182
|
+
host,
|
|
183
|
+
path,
|
|
184
|
+
port,
|
|
185
|
+
metadata = {},
|
|
186
|
+
apis = [],
|
|
187
|
+
publicClientId,
|
|
188
|
+
rootClientPath,
|
|
189
|
+
packageData,
|
|
190
|
+
}) => {
|
|
191
|
+
await buildJsDocs({ host, path, metadata });
|
|
192
|
+
await buildCoverage({ host, path });
|
|
193
|
+
await buildApiDocs({
|
|
194
|
+
host,
|
|
195
|
+
path,
|
|
196
|
+
port,
|
|
197
|
+
metadata,
|
|
198
|
+
apis,
|
|
199
|
+
publicClientId,
|
|
200
|
+
rootClientPath,
|
|
201
|
+
packageData,
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export { buildDocs };
|
|
@@ -17,11 +17,11 @@ import dotenv from 'dotenv';
|
|
|
17
17
|
import AdmZip from 'adm-zip';
|
|
18
18
|
import * as dir from 'path';
|
|
19
19
|
import { shellExec } from './process.js';
|
|
20
|
-
import swaggerAutoGen from 'swagger-autogen';
|
|
21
20
|
import { SitemapStream, streamToPromise } from 'sitemap';
|
|
22
21
|
import { Readable } from 'stream';
|
|
23
22
|
import { buildIcons, buildTextImg, getBufferPngText } from './client-icons.js';
|
|
24
23
|
import Underpost from '../index.js';
|
|
24
|
+
import { buildDocs } from './client-build-docs.js';
|
|
25
25
|
|
|
26
26
|
dotenv.config();
|
|
27
27
|
|
|
@@ -557,125 +557,16 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
if (!enableLiveRebuild && !process.argv.includes('l') && !process.argv.includes('deploy') && docsBuild) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
shellExec(`npm run docs`, { silent: true });
|
|
571
|
-
|
|
572
|
-
// coverage
|
|
573
|
-
if (!fs.existsSync(`./coverage`)) {
|
|
574
|
-
shellExec(`npm test`);
|
|
575
|
-
}
|
|
576
|
-
const coverageBuildPath = `${jsDocsConfig.opts.destination}/coverage`;
|
|
577
|
-
fs.mkdirSync(coverageBuildPath, { recursive: true });
|
|
578
|
-
fs.copySync(`./coverage`, coverageBuildPath);
|
|
579
|
-
|
|
580
|
-
// https://swagger-autogen.github.io/docs/
|
|
581
|
-
|
|
582
|
-
const basePath = path === '/' ? `${process.env.BASE_API}` : `/${process.env.BASE_API}`;
|
|
583
|
-
|
|
584
|
-
const doc = {
|
|
585
|
-
info: {
|
|
586
|
-
version: packageData.version, // by default: '1.0.0'
|
|
587
|
-
title: metadata?.title ? `${metadata.title}` : 'REST API', // by default: 'REST API'
|
|
588
|
-
description: metadata?.description ? metadata.description : '', // by default: ''
|
|
589
|
-
},
|
|
590
|
-
servers: [
|
|
591
|
-
{
|
|
592
|
-
url:
|
|
593
|
-
process.env.NODE_ENV === 'development'
|
|
594
|
-
? `http://localhost:${port}${path}${basePath}`
|
|
595
|
-
: `https://${host}${path}${basePath}`, // by default: 'http://localhost:3000'
|
|
596
|
-
description: `${process.env.NODE_ENV} server`, // by default: ''
|
|
597
|
-
},
|
|
598
|
-
],
|
|
599
|
-
tags: [
|
|
600
|
-
// by default: empty Array
|
|
601
|
-
{
|
|
602
|
-
name: 'user', // Tag name
|
|
603
|
-
description: 'User API operations', // Tag description
|
|
604
|
-
},
|
|
605
|
-
],
|
|
606
|
-
components: {
|
|
607
|
-
schemas: {
|
|
608
|
-
userRequest: {
|
|
609
|
-
username: 'user123',
|
|
610
|
-
password: 'Password123',
|
|
611
|
-
email: 'user@example.com',
|
|
612
|
-
},
|
|
613
|
-
userResponse: {
|
|
614
|
-
status: 'success',
|
|
615
|
-
data: {
|
|
616
|
-
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7Il9pZCI6IjY2YzM3N2Y1N2Y5OWU1OTY5YjgxZG...',
|
|
617
|
-
user: {
|
|
618
|
-
_id: '66c377f57f99e5969b81de89',
|
|
619
|
-
email: 'user@example.com',
|
|
620
|
-
emailConfirmed: false,
|
|
621
|
-
username: 'user123',
|
|
622
|
-
role: 'user',
|
|
623
|
-
profileImageId: '66c377f57f99e5969b81de87',
|
|
624
|
-
},
|
|
625
|
-
},
|
|
626
|
-
},
|
|
627
|
-
userUpdateResponse: {
|
|
628
|
-
status: 'success',
|
|
629
|
-
data: {
|
|
630
|
-
_id: '66c377f57f99e5969b81de89',
|
|
631
|
-
email: 'user@example.com',
|
|
632
|
-
emailConfirmed: false,
|
|
633
|
-
username: 'user123222',
|
|
634
|
-
role: 'user',
|
|
635
|
-
profileImageId: '66c377f57f99e5969b81de87',
|
|
636
|
-
},
|
|
637
|
-
},
|
|
638
|
-
userGetResponse: {
|
|
639
|
-
status: 'success',
|
|
640
|
-
data: {
|
|
641
|
-
_id: '66c377f57f99e5969b81de89',
|
|
642
|
-
email: 'user@example.com',
|
|
643
|
-
emailConfirmed: false,
|
|
644
|
-
username: 'user123222',
|
|
645
|
-
role: 'user',
|
|
646
|
-
profileImageId: '66c377f57f99e5969b81de87',
|
|
647
|
-
},
|
|
648
|
-
},
|
|
649
|
-
userLogInRequest: {
|
|
650
|
-
email: 'user@example.com',
|
|
651
|
-
password: 'Password123',
|
|
652
|
-
},
|
|
653
|
-
userBadRequestResponse: {
|
|
654
|
-
status: 'error',
|
|
655
|
-
message: 'Bad request. Please check your inputs, and try again',
|
|
656
|
-
},
|
|
657
|
-
},
|
|
658
|
-
securitySchemes: {
|
|
659
|
-
bearerAuth: {
|
|
660
|
-
type: 'http',
|
|
661
|
-
scheme: 'bearer',
|
|
662
|
-
},
|
|
663
|
-
},
|
|
664
|
-
},
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
logger.warn('build swagger api docs', doc.info);
|
|
668
|
-
|
|
669
|
-
const outputFile = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
|
|
670
|
-
const routes = [];
|
|
671
|
-
for (const api of apis) {
|
|
672
|
-
if (['user'].includes(api)) routes.push(`./src/api/${api}/${api}.router.js`);
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/* NOTE: If you are using the express Router, you must pass in the 'routes' only the
|
|
676
|
-
root file where the route starts, such as index.js, app.js, routes.js, etc ... */
|
|
677
|
-
|
|
678
|
-
await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
|
|
560
|
+
await buildDocs({
|
|
561
|
+
host,
|
|
562
|
+
path,
|
|
563
|
+
port,
|
|
564
|
+
metadata,
|
|
565
|
+
apis,
|
|
566
|
+
publicClientId,
|
|
567
|
+
rootClientPath,
|
|
568
|
+
packageData,
|
|
569
|
+
});
|
|
679
570
|
}
|
|
680
571
|
|
|
681
572
|
if (client) {
|