@underpostnet/underpost 2.97.0 → 2.97.5
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 +2 -2
- package/baremetal/commission-workflows.json +33 -3
- package/bin/deploy.js +1 -1
- package/cli.md +7 -2
- package/conf.js +3 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/packer/scripts/fuse-tar-root +3 -3
- package/scripts/disk-clean.sh +23 -23
- package/scripts/gpu-diag.sh +2 -2
- package/scripts/ip-info.sh +11 -11
- package/scripts/maas-upload-boot-resource.sh +1 -1
- package/scripts/nvim.sh +1 -1
- package/scripts/packer-setup.sh +13 -13
- package/scripts/rocky-setup.sh +2 -2
- package/scripts/rpmfusion-ffmpeg-setup.sh +4 -4
- package/scripts/ssl.sh +7 -7
- package/src/api/core/core.service.js +0 -5
- package/src/api/default/default.service.js +7 -5
- package/src/api/document/document.model.js +30 -1
- package/src/api/document/document.router.js +6 -0
- package/src/api/document/document.service.js +423 -51
- package/src/api/file/file.model.js +112 -4
- package/src/api/file/file.ref.json +42 -0
- package/src/api/file/file.service.js +380 -32
- package/src/api/user/user.model.js +38 -1
- package/src/api/user/user.router.js +96 -63
- package/src/api/user/user.service.js +81 -48
- package/src/cli/baremetal.js +689 -329
- package/src/cli/cluster.js +50 -52
- package/src/cli/db.js +424 -166
- package/src/cli/deploy.js +1 -1
- package/src/cli/index.js +12 -1
- package/src/cli/lxd.js +3 -3
- package/src/cli/repository.js +1 -1
- package/src/cli/run.js +2 -1
- package/src/cli/ssh.js +10 -10
- package/src/client/components/core/Account.js +327 -36
- package/src/client/components/core/AgGrid.js +3 -0
- package/src/client/components/core/Auth.js +9 -3
- package/src/client/components/core/Chat.js +2 -2
- package/src/client/components/core/Content.js +159 -78
- package/src/client/components/core/Css.js +16 -2
- package/src/client/components/core/CssCore.js +16 -12
- package/src/client/components/core/FileExplorer.js +115 -8
- package/src/client/components/core/Input.js +204 -11
- package/src/client/components/core/LogIn.js +42 -20
- package/src/client/components/core/Modal.js +257 -177
- package/src/client/components/core/Panel.js +324 -27
- package/src/client/components/core/PanelForm.js +280 -73
- package/src/client/components/core/PublicProfile.js +888 -0
- package/src/client/components/core/Router.js +117 -15
- package/src/client/components/core/SearchBox.js +1117 -0
- package/src/client/components/core/SignUp.js +26 -7
- package/src/client/components/core/SocketIo.js +6 -3
- package/src/client/components/core/Translate.js +98 -0
- package/src/client/components/core/Validator.js +15 -0
- package/src/client/components/core/windowGetDimensions.js +6 -6
- package/src/client/components/default/MenuDefault.js +59 -12
- package/src/client/components/default/RoutesDefault.js +1 -0
- package/src/client/services/core/core.service.js +163 -1
- package/src/client/services/default/default.management.js +451 -64
- package/src/client/services/default/default.service.js +13 -6
- package/src/client/services/document/document.service.js +23 -0
- package/src/client/services/file/file.service.js +43 -16
- package/src/client/services/user/user.service.js +13 -9
- package/src/db/DataBaseProvider.js +1 -1
- package/src/db/mongo/MongooseDB.js +1 -1
- package/src/index.js +1 -1
- package/src/mailer/MailerProvider.js +4 -4
- package/src/runtime/express/Express.js +2 -1
- package/src/runtime/lampp/Lampp.js +2 -2
- package/src/server/auth.js +3 -6
- package/src/server/data-query.js +449 -0
- package/src/server/dns.js +4 -4
- package/src/server/object-layer.js +0 -3
- package/src/ws/IoInterface.js +2 -2
|
@@ -19,7 +19,7 @@ const SignUp = {
|
|
|
19
19
|
{
|
|
20
20
|
model: 'username',
|
|
21
21
|
id: `sign-up-username`,
|
|
22
|
-
rules: [{ type: 'isEmpty' }, { type: 'isLength', options: { min: 2, max: 20 } }],
|
|
22
|
+
rules: [{ type: 'isEmpty' }, { type: 'isLength', options: { min: 2, max: 20 } }, { type: 'isValidUsername' }],
|
|
23
23
|
},
|
|
24
24
|
{ model: 'email', id: `sign-up-email`, rules: [{ type: 'isEmpty' }, { type: 'isEmail' }] },
|
|
25
25
|
{
|
|
@@ -43,23 +43,42 @@ const SignUp = {
|
|
|
43
43
|
if ('model' in inputData) body[inputData.model] = s(`.${inputData.id}`).value;
|
|
44
44
|
}
|
|
45
45
|
const result = await UserService.post({ body });
|
|
46
|
+
const handleSignUpError = (data) => {
|
|
47
|
+
let error = '';
|
|
48
|
+
if (data.message) {
|
|
49
|
+
if (data.message.match('duplicate')) {
|
|
50
|
+
if (data.message.match('username')) error += Translate.Render('error-username-taken');
|
|
51
|
+
if (data.message.match('email')) error += Translate.Render('error-email-taken');
|
|
52
|
+
} else {
|
|
53
|
+
if (data.message.match('username')) error += Translate.Render('error-username-invalid');
|
|
54
|
+
if (data.message.match('email')) error += Translate.Render('error-email-invalid');
|
|
55
|
+
if (data.message.match('password')) error += Translate.Render('error-password-invalid');
|
|
56
|
+
}
|
|
57
|
+
return error;
|
|
58
|
+
}
|
|
59
|
+
return Translate.Render('error-register-user');
|
|
60
|
+
};
|
|
46
61
|
NotificationManager.Push({
|
|
47
62
|
html:
|
|
48
63
|
typeof result.data === 'string'
|
|
49
64
|
? result.data
|
|
50
65
|
: result.status === 'success'
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
? Translate.Render(`success-register-user`)
|
|
67
|
+
: handleSignUpError(result),
|
|
53
68
|
status: result.status,
|
|
54
69
|
});
|
|
55
70
|
if (result.status === 'success') {
|
|
56
71
|
await Auth.sessionIn(result);
|
|
57
|
-
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
if (s(`.btn-close-${options.idModal}`)) s(`.btn-close-${options.idModal}`).click();
|
|
74
|
+
});
|
|
58
75
|
}
|
|
59
76
|
});
|
|
60
|
-
|
|
61
|
-
s(`.
|
|
62
|
-
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
s(`.btn-sign-up-i-have-account`).onclick = () => {
|
|
79
|
+
s(`.main-btn-log-in`).click();
|
|
80
|
+
};
|
|
81
|
+
});
|
|
63
82
|
});
|
|
64
83
|
return html`
|
|
65
84
|
${await BtnIcon.Render({
|
|
@@ -21,9 +21,12 @@ const SocketIo = {
|
|
|
21
21
|
},
|
|
22
22
|
Init: async function (options) {
|
|
23
23
|
if (this.socket) this.socket.disconnect();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const path = getWsBasePath();
|
|
25
|
+
this.host = options.host ? options.host : getWsBaseUrl({ wsBasePath: '' });
|
|
26
|
+
logger.info(`ws host:`, {
|
|
27
|
+
host: this.host,
|
|
28
|
+
path,
|
|
29
|
+
});
|
|
27
30
|
const connectOptions = {
|
|
28
31
|
path: path === '/' ? undefined : path,
|
|
29
32
|
// auth: {
|
|
@@ -111,6 +111,30 @@ const TranslateCore = {
|
|
|
111
111
|
es: 'Este campo no cumple con los requisitos específicos',
|
|
112
112
|
en: 'This field does not meet specific requirements',
|
|
113
113
|
},
|
|
114
|
+
'error-username-invalid': {
|
|
115
|
+
es: 'Nombre de usuario no válido. Debe contener solo letras, números y guiones bajos.',
|
|
116
|
+
en: 'Invalid username. Must contain only letters, numbers, and underscores.',
|
|
117
|
+
},
|
|
118
|
+
'error-email-invalid': {
|
|
119
|
+
es: 'Dirección de correo electrónico no válida.',
|
|
120
|
+
en: 'Invalid email address.',
|
|
121
|
+
},
|
|
122
|
+
'error-password-invalid': {
|
|
123
|
+
es: 'Contraseña no válida. Debe tener al menos 8 caracteres, incluir mayúsculas, minúsculas, números y símbolos.',
|
|
124
|
+
en: 'Invalid password. Must be at least 8 characters long and include uppercase, lowercase, numbers, and symbols.',
|
|
125
|
+
},
|
|
126
|
+
'error-username-taken': {
|
|
127
|
+
es: 'Este nombre de usuario ya está en uso. Por favor, elige otro.',
|
|
128
|
+
en: 'This username is already taken. Please choose another one.',
|
|
129
|
+
},
|
|
130
|
+
'error-email-taken': {
|
|
131
|
+
es: 'Esta dirección de correo electrónico ya está registrada.',
|
|
132
|
+
en: 'This email address is already registered.',
|
|
133
|
+
},
|
|
134
|
+
'error-register-user': {
|
|
135
|
+
es: 'Error al registrar el usuario. Por favor, intenta nuevamente.',
|
|
136
|
+
en: 'Error registering user. Please try again.',
|
|
137
|
+
},
|
|
114
138
|
};
|
|
115
139
|
Translate.Data['isMobilePhone'] = {
|
|
116
140
|
en: 'Invalid mobile phone number. Please check the format and try again.',
|
|
@@ -141,6 +165,16 @@ const TranslateCore = {
|
|
|
141
165
|
Translate.Data['download'] = { en: 'download', es: 'Descargar' };
|
|
142
166
|
Translate.Data['delete'] = { en: 'delete', es: 'Eliminar' };
|
|
143
167
|
Translate.Data['success-delete'] = { en: 'success delete item', es: 'Item eliminado con exito' };
|
|
168
|
+
Translate.Data['document-now-public'] = { en: 'Document is now public', es: 'El documento ahora es público' };
|
|
169
|
+
Translate.Data['document-now-private'] = { en: 'Document is now private', es: 'El documento ahora es privado' };
|
|
170
|
+
Translate.Data['error-toggle-public'] = {
|
|
171
|
+
en: 'Failed to toggle public status',
|
|
172
|
+
es: 'Error al cambiar estado público',
|
|
173
|
+
};
|
|
174
|
+
Translate.Data['confirm-make-public'] = {
|
|
175
|
+
en: 'Are you sure you want to make this document public?',
|
|
176
|
+
es: '¿Estás seguro de que deseas hacer público este documento?',
|
|
177
|
+
};
|
|
144
178
|
Translate.Data['invalid-data'] = { en: 'Invalid data', es: 'Datos invalidos' };
|
|
145
179
|
Translate.Data['upload'] = { en: 'upload', es: 'Subir' };
|
|
146
180
|
Translate.Data['load'] = { en: 'load', es: 'Cargar' };
|
|
@@ -179,6 +213,10 @@ const TranslateCore = {
|
|
|
179
213
|
en: 'Error during user login. Please check your credentials and try again.',
|
|
180
214
|
es: 'Error durante el inicio de sesión del usuario. Por favor, verifica tus credenciales e intenta nuevamente.',
|
|
181
215
|
};
|
|
216
|
+
Translate.Data['error-user-not-authenticated'] = {
|
|
217
|
+
en: 'You must be logged in to perform this action.',
|
|
218
|
+
es: 'Debe iniciar sesión para realizar esta acción.',
|
|
219
|
+
};
|
|
182
220
|
Translate.Data['confirm-logout'] = { en: 'Confirm Logout', es: 'Confirmar cierre de sesión' };
|
|
183
221
|
Translate.Data['success-logout'] = { en: 'Successful session logout', es: 'Cierre de sesión exitoso' };
|
|
184
222
|
Translate.Data['account'] = { en: 'Account', es: 'Cuenta' };
|
|
@@ -531,6 +569,66 @@ const TranslateCore = {
|
|
|
531
569
|
en: 'Require title and content or file',
|
|
532
570
|
es: 'Requiere título y contenido o archivo',
|
|
533
571
|
};
|
|
572
|
+
Translate.Data['public-profile'] = {
|
|
573
|
+
en: 'Public Profile',
|
|
574
|
+
es: 'Perfil Público',
|
|
575
|
+
};
|
|
576
|
+
Translate.Data['private-profile'] = {
|
|
577
|
+
en: 'Private Profile',
|
|
578
|
+
es: 'Perfil Privado',
|
|
579
|
+
};
|
|
580
|
+
Translate.Data['brief-description'] = {
|
|
581
|
+
en: 'Brief Description',
|
|
582
|
+
es: 'Descripción Breve',
|
|
583
|
+
};
|
|
584
|
+
Translate.Data['brief-description-cannot-be-empty'] = {
|
|
585
|
+
en: 'Brief description cannot be empty',
|
|
586
|
+
es: 'La descripción breve no puede estar vacía',
|
|
587
|
+
};
|
|
588
|
+
Translate.Data['user-not-found'] = {
|
|
589
|
+
en: 'User not found',
|
|
590
|
+
es: 'Usuario no encontrado',
|
|
591
|
+
};
|
|
592
|
+
Translate.Data['error-loading-profile'] = {
|
|
593
|
+
en: 'Error loading profile',
|
|
594
|
+
es: 'Error al cargar el perfil',
|
|
595
|
+
};
|
|
596
|
+
Translate.Data['profile-is-private'] = {
|
|
597
|
+
en: 'This profile is private',
|
|
598
|
+
es: 'Este perfil es privado',
|
|
599
|
+
};
|
|
600
|
+
Translate.Data['no-description'] = {
|
|
601
|
+
en: 'No description provided',
|
|
602
|
+
es: 'Sin descripción',
|
|
603
|
+
};
|
|
604
|
+
Translate.Data['member-since'] = {
|
|
605
|
+
en: 'Member since',
|
|
606
|
+
es: 'Miembro desde',
|
|
607
|
+
};
|
|
608
|
+
Translate.Data['followers'] = {
|
|
609
|
+
en: 'Followers',
|
|
610
|
+
es: 'Seguidores',
|
|
611
|
+
};
|
|
612
|
+
Translate.Data['following'] = {
|
|
613
|
+
en: 'Following',
|
|
614
|
+
es: 'Siguiendo',
|
|
615
|
+
};
|
|
616
|
+
Translate.Data['invalid-username-format'] = {
|
|
617
|
+
en: 'Username can only contain letters, numbers, hyphens, and underscores',
|
|
618
|
+
es: 'El nombre de usuario solo puede contener letras, números, guiones e guiones bajos',
|
|
619
|
+
};
|
|
620
|
+
Translate.Data['go-home'] = {
|
|
621
|
+
en: 'Go Home',
|
|
622
|
+
es: 'Ir a Inicio',
|
|
623
|
+
};
|
|
624
|
+
Translate.Data['go-back'] = {
|
|
625
|
+
en: 'Go Back',
|
|
626
|
+
es: 'Volver',
|
|
627
|
+
};
|
|
628
|
+
Translate.Data['public-profile'] = {
|
|
629
|
+
en: 'Public Profile',
|
|
630
|
+
es: 'Perfil Público',
|
|
631
|
+
};
|
|
534
632
|
},
|
|
535
633
|
};
|
|
536
634
|
|
|
@@ -66,6 +66,21 @@ const Validator = {
|
|
|
66
66
|
|
|
67
67
|
break;
|
|
68
68
|
}
|
|
69
|
+
case 'isValidUsername': {
|
|
70
|
+
const username = s(`.${validatorData.id}`).value;
|
|
71
|
+
// Check if username is empty or only whitespace
|
|
72
|
+
if (!username || username.trim() === '') {
|
|
73
|
+
errorMessage += this.renderErrorMessage(undefined, Translate.Render('invalid-username-format'));
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// Check if username contains only valid characters (no spaces or special chars)
|
|
77
|
+
const usernameRegex = /^[a-zA-Z0-9_-]+$/;
|
|
78
|
+
if (!usernameRegex.test(username)) {
|
|
79
|
+
errorMessage += this.renderErrorMessage(undefined, Translate.Render('invalid-username-format'));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
69
84
|
default:
|
|
70
85
|
if (
|
|
71
86
|
validator[rule.type] &&
|
|
@@ -53,7 +53,7 @@ export class PwaWindowDimensions {
|
|
|
53
53
|
// --- Private Static Getters (Encapsulating browser APIs) ---
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* @
|
|
56
|
+
* @method
|
|
57
57
|
* @static
|
|
58
58
|
* Try visualViewport values (most accurate for "what's actually visible").
|
|
59
59
|
* @returns {{height: number|null, width: number|null}}
|
|
@@ -68,7 +68,7 @@ export class PwaWindowDimensions {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* @
|
|
71
|
+
* @method
|
|
72
72
|
* @static
|
|
73
73
|
* Try layout viewport (doctype-root) measurements.
|
|
74
74
|
* document.documentElement.clientHeight/clientWidth are stable and widely used.
|
|
@@ -84,7 +84,7 @@ export class PwaWindowDimensions {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* @
|
|
87
|
+
* @method
|
|
88
88
|
* @static
|
|
89
89
|
* Try window.* measurements (innerHeight/innerWidth are widely supported).
|
|
90
90
|
* @returns {{height: number|null, width: number|null}}
|
|
@@ -98,7 +98,7 @@ export class PwaWindowDimensions {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
* @
|
|
101
|
+
* @method
|
|
102
102
|
* @static
|
|
103
103
|
* Try body measurements (less reliable, used as a fallback).
|
|
104
104
|
* @returns {{height: number|null, width: number|null}}
|
|
@@ -112,7 +112,7 @@ export class PwaWindowDimensions {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
* @
|
|
115
|
+
* @method
|
|
116
116
|
* @static
|
|
117
117
|
* Try screen measurements (physical screen/last-resort fallback).
|
|
118
118
|
* @returns {{height: number|null, width: number|null}}
|
|
@@ -130,7 +130,7 @@ export class PwaWindowDimensions {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
133
|
-
* @
|
|
133
|
+
* @method
|
|
134
134
|
* @static
|
|
135
135
|
* Try outer dimensions (less reliable, sometimes available fallback).
|
|
136
136
|
* @returns {{height: number|null, width: number|null}}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { Account } from '../core/Account.js';
|
|
2
2
|
import { BtnIcon } from '../core/BtnIcon.js';
|
|
3
3
|
import { getId, newInstance } from '../core/CommonJs.js';
|
|
4
|
-
import {
|
|
5
|
-
borderChar,
|
|
6
|
-
boxShadow,
|
|
7
|
-
Css,
|
|
8
|
-
darkTheme,
|
|
9
|
-
extractBackgroundImageUrl,
|
|
10
|
-
renderCssAttr,
|
|
11
|
-
ThemeEvents,
|
|
12
|
-
Themes,
|
|
13
|
-
} from '../core/Css.js';
|
|
4
|
+
import { Css, darkTheme, ThemeEvents, Themes } from '../core/Css.js';
|
|
14
5
|
import { EventsUI } from '../core/EventsUI.js';
|
|
15
6
|
import { LogIn } from '../core/LogIn.js';
|
|
16
7
|
import { LogOut } from '../core/LogOut.js';
|
|
@@ -18,19 +9,19 @@ import { buildBadgeToolTipMenuOption, Modal, renderMenuLabel, renderViewTitle }
|
|
|
18
9
|
import { SignUp } from '../core/SignUp.js';
|
|
19
10
|
import { Translate } from '../core/Translate.js';
|
|
20
11
|
import { htmls, s } from '../core/VanillaJs.js';
|
|
21
|
-
import { getProxyPath } from '../core/Router.js';
|
|
12
|
+
import { extractUsernameFromPath, getProxyPath, getQueryParams } from '../core/Router.js';
|
|
22
13
|
import { ElementsDefault } from './ElementsDefault.js';
|
|
23
14
|
import Sortable from 'sortablejs';
|
|
24
15
|
import { RouterDefault, BannerAppTemplate } from './RoutesDefault.js';
|
|
25
16
|
import { SettingsDefault } from './SettingsDefault.js';
|
|
26
17
|
import { Badge } from '../core/Badge.js';
|
|
27
|
-
import { Docs } from '../core/Docs.js';
|
|
28
18
|
import { Recover } from '../core/Recover.js';
|
|
29
19
|
import { DefaultManagement } from '../../services/default/default.management.js';
|
|
30
20
|
import { Page500 } from '../core/500.js';
|
|
31
21
|
import { Page404 } from '../core/404.js';
|
|
32
22
|
import { PanelForm } from '../core/PanelForm.js';
|
|
33
23
|
import { Chat } from '../core/Chat.js';
|
|
24
|
+
import { PublicProfile } from '../core/PublicProfile.js';
|
|
34
25
|
|
|
35
26
|
const MenuDefault = {
|
|
36
27
|
Data: {},
|
|
@@ -111,6 +102,19 @@ const MenuDefault = {
|
|
|
111
102
|
handleContainerClass: 'handle-btn-container',
|
|
112
103
|
tooltipHtml: await Badge.Render(buildBadgeToolTipMenuOption('account')),
|
|
113
104
|
})}
|
|
105
|
+
${await BtnIcon.Render({
|
|
106
|
+
class: 'in wfa main-btn-menu main-btn-public-profile',
|
|
107
|
+
useMenuBtn: true,
|
|
108
|
+
label: renderMenuLabel({
|
|
109
|
+
icon: html`<i class="fas fa-user-tag"></i>`,
|
|
110
|
+
text: html`<span class="menu-label-text">${Translate.Render('public-profile')}</span>`,
|
|
111
|
+
}),
|
|
112
|
+
style: 'display: none',
|
|
113
|
+
attrs: `data-id="public-profile"`,
|
|
114
|
+
tabHref: `${getProxyPath()}u`,
|
|
115
|
+
handleContainerClass: 'handle-btn-container',
|
|
116
|
+
tooltipHtml: await Badge.Render(buildBadgeToolTipMenuOption('public-profile')),
|
|
117
|
+
})}
|
|
114
118
|
${await BtnIcon.Render({
|
|
115
119
|
class: 'in wfa main-btn-menu main-btn-settings',
|
|
116
120
|
useMenuBtn: true,
|
|
@@ -558,6 +562,49 @@ const MenuDefault = {
|
|
|
558
562
|
});
|
|
559
563
|
});
|
|
560
564
|
|
|
565
|
+
EventsUI.onClick(`.main-btn-public-profile`, async () => {
|
|
566
|
+
const { barConfig } = await Themes[Css.currentTheme]();
|
|
567
|
+
const idModal = 'modal-public-profile';
|
|
568
|
+
const user = ElementsDefault.Data.user.main.model.user;
|
|
569
|
+
|
|
570
|
+
// Check if modal already exists
|
|
571
|
+
const existingModal = s(`.${idModal}`);
|
|
572
|
+
if (existingModal) {
|
|
573
|
+
const usernameFromPath = extractUsernameFromPath();
|
|
574
|
+
const queryParams = getQueryParams();
|
|
575
|
+
const cid = usernameFromPath || queryParams.cid || user.username || null;
|
|
576
|
+
if (cid) {
|
|
577
|
+
await PublicProfile.Update({
|
|
578
|
+
idModal: 'modal-public-profile',
|
|
579
|
+
user: { username: cid },
|
|
580
|
+
});
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
await Modal.Render({
|
|
586
|
+
id: idModal,
|
|
587
|
+
route: 'u',
|
|
588
|
+
barConfig,
|
|
589
|
+
title: '',
|
|
590
|
+
// renderViewTitle({
|
|
591
|
+
// icon: html`<i class="fas fa-user-circle"></i>`,
|
|
592
|
+
// text: Translate.Render('public-profile'),
|
|
593
|
+
// }),
|
|
594
|
+
html: async () =>
|
|
595
|
+
await PublicProfile.Render({
|
|
596
|
+
idModal,
|
|
597
|
+
user,
|
|
598
|
+
}),
|
|
599
|
+
handleType: 'bar',
|
|
600
|
+
maximize: true,
|
|
601
|
+
mode: 'view',
|
|
602
|
+
slideMenu: 'modal-menu',
|
|
603
|
+
RouterInstance,
|
|
604
|
+
observer: true,
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
|
|
561
608
|
EventsUI.onClick(`.main-btn-settings`, async () => {
|
|
562
609
|
const { barConfig } = await Themes[Css.currentTheme]();
|
|
563
610
|
await Modal.Render({
|
|
@@ -34,6 +34,7 @@ const RoutesDefault = () => {
|
|
|
34
34
|
title: 'default-management',
|
|
35
35
|
render: () => s(`.main-btn-default-management`).click(),
|
|
36
36
|
},
|
|
37
|
+
'/u': { title: 'public-profile', render: () => s(`.main-btn-public-profile`).click() },
|
|
37
38
|
'/404': { title: '404 Not Found', render: () => s(`.main-btn-404`).click() },
|
|
38
39
|
'/500': { title: '500 Server Error', render: () => s(`.main-btn-500`).click() },
|
|
39
40
|
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Service Client
|
|
3
|
+
* Provides methods to interact and build URLs for the core API endpoints.
|
|
4
|
+
* @module src/client/services/core/core.service.js
|
|
5
|
+
* @namespace CoreServiceClient
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import { Auth } from '../../components/core/Auth.js';
|
|
2
9
|
import { loggerFactory } from '../../components/core/Logger.js';
|
|
3
10
|
import { getProxyPath } from '../../components/core/Router.js';
|
|
@@ -9,8 +16,23 @@ logger.info('Load service');
|
|
|
9
16
|
const endpoint = 'core';
|
|
10
17
|
|
|
11
18
|
// https://developer.mozilla.org/en-US/docs/Web/API/AbortController
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Gets the base host for API requests.
|
|
22
|
+
* Uses the apiBaseHost from renderPayload if available, otherwise falls back to location.host.
|
|
23
|
+
* @memberof CoreServiceClient
|
|
24
|
+
* @return {string} The base host string.
|
|
25
|
+
*/
|
|
12
26
|
const getBaseHost = () => (window.renderPayload?.apiBaseHost ? window.renderPayload.apiBaseHost : location.host);
|
|
13
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Gets the base path for API requests.
|
|
30
|
+
* Constructs the path using proxyPath and apiBasePath from renderPayload or defaults.
|
|
31
|
+
* @memberof CoreServiceClient
|
|
32
|
+
* @param {Object} [options] - Options for constructing the base path.
|
|
33
|
+
* @param {string} [options.proxyPath] - Custom proxy path to use.
|
|
34
|
+
* @return {string} The constructed API base path.
|
|
35
|
+
*/
|
|
14
36
|
const getApiBasePath = (options) =>
|
|
15
37
|
`${
|
|
16
38
|
options?.proxyPath
|
|
@@ -22,18 +44,51 @@ const getApiBasePath = (options) =>
|
|
|
22
44
|
: getProxyPath()
|
|
23
45
|
}${window.renderPayload?.apiBasePath ? window.renderPayload.apiBasePath : 'api'}/`;
|
|
24
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Constructs the full API base URL for making requests.
|
|
49
|
+
* Combines protocol, host, base path, endpoint, and optional ID.
|
|
50
|
+
* @memberof CoreServiceClient
|
|
51
|
+
* @param {Object} [options={}] - Options for constructing the URL.
|
|
52
|
+
* @param {string} [options.id=''] - Optional resource ID to append to the URL.
|
|
53
|
+
* @param {string} [options.endpoint=''] - API endpoint name.
|
|
54
|
+
* @param {string} [options.proxyPath=''] - Custom proxy path to use.
|
|
55
|
+
* @return {string} The full API base URL.
|
|
56
|
+
*/
|
|
25
57
|
const getApiBaseUrl = (options = { id: '', endpoint: '', proxyPath: '' }) =>
|
|
26
58
|
`${location.protocol}//${getBaseHost()}${getApiBasePath(options)}${options?.endpoint ? options.endpoint : ''}${
|
|
27
59
|
options?.id ? `/${options.id}` : ''
|
|
28
60
|
}`;
|
|
29
61
|
|
|
30
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Gets the base path for WebSocket connections.
|
|
64
|
+
* Constructs the socket.io path using the proxy path.
|
|
65
|
+
* @memberof CoreServiceClient
|
|
66
|
+
* @return {string} The WebSocket base path.
|
|
67
|
+
*/
|
|
68
|
+
const getWsBasePath = () => (getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : '/socket.io/');
|
|
31
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Constructs the full WebSocket base URL for connections.
|
|
72
|
+
* Uses wss: for HTTPS and ws: for HTTP protocols.
|
|
73
|
+
* @memberof CoreServiceClient
|
|
74
|
+
* @param {Object} [options={}] - Options for constructing the WebSocket URL.
|
|
75
|
+
* @param {string} [options.id=''] - Optional resource ID to append to the URL.
|
|
76
|
+
* @param {string} [options.endpoint=''] - WebSocket endpoint name.
|
|
77
|
+
* @param {string} [options.wsBasePath=''] - Custom WebSocket base path to use.
|
|
78
|
+
* @return {string} The full WebSocket base URL.
|
|
79
|
+
*/
|
|
32
80
|
const getWsBaseUrl = (options = { id: '', endpoint: '', wsBasePath: '' }) =>
|
|
33
81
|
`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${getBaseHost()}${
|
|
34
82
|
options?.wsBasePath !== undefined ? options.wsBasePath : getWsBasePath()
|
|
35
83
|
}${options?.endpoint ? options.endpoint : ''}${options?.id ? `/${options.id}` : ''}`;
|
|
36
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Creates HTTP headers for API requests.
|
|
87
|
+
* Includes Authorization header with JWT token and Content-Type based on headerId.
|
|
88
|
+
* @memberof CoreServiceClient
|
|
89
|
+
* @param {string} [headerId=''] - Header type identifier. Use 'file' for file uploads (no Content-Type).
|
|
90
|
+
* @return {Object} Headers object for fetch requests.
|
|
91
|
+
*/
|
|
37
92
|
const headersFactory = (headerId = '') => {
|
|
38
93
|
const headers = {
|
|
39
94
|
Authorization: Auth.getJWT(),
|
|
@@ -48,12 +103,77 @@ const headersFactory = (headerId = '') => {
|
|
|
48
103
|
}
|
|
49
104
|
};
|
|
50
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Prepares the request body payload for API requests.
|
|
108
|
+
* Returns FormData as-is, otherwise stringifies the body as JSON.
|
|
109
|
+
* @memberof CoreServiceClient
|
|
110
|
+
* @param {Object|FormData} body - The request body to process.
|
|
111
|
+
* @return {string|FormData} The processed payload ready for fetch.
|
|
112
|
+
*/
|
|
51
113
|
const payloadFactory = (body) => {
|
|
52
114
|
if (body instanceof FormData) return body;
|
|
53
115
|
return JSON.stringify(body);
|
|
54
116
|
};
|
|
55
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Builds a URL with query parameters for pagination, filtering, and sorting.
|
|
120
|
+
* Supports AG Grid filterModel/sortModel as well as legacy simple sort params.
|
|
121
|
+
* @memberof CoreServiceClient
|
|
122
|
+
* @param {string} baseUrl - The base API URL.
|
|
123
|
+
* @param {Object} [options={}] - Query options.
|
|
124
|
+
* @param {number} [options.page] - Page number for pagination.
|
|
125
|
+
* @param {number} [options.limit] - Items per page for pagination.
|
|
126
|
+
* @param {Object|string} [options.filterModel] - AG Grid filterModel (object or JSON string).
|
|
127
|
+
* @param {Array|string} [options.sortModel] - AG Grid sortModel (array or JSON string).
|
|
128
|
+
* @param {string} [options.sort] - Simple sort field (legacy).
|
|
129
|
+
* @param {string|boolean} [options.asc] - Simple sort direction (legacy).
|
|
130
|
+
* @param {string} [options.order] - Order string, e.g. "field1:asc,field2:desc" (legacy).
|
|
131
|
+
* @return {URL} The URL object with query parameters set.
|
|
132
|
+
*/
|
|
133
|
+
const buildQueryUrl = (baseUrl, options = {}) => {
|
|
134
|
+
const url = new URL(baseUrl);
|
|
135
|
+
const { page, limit, filterModel, sortModel, sort, asc, order } = options;
|
|
136
|
+
|
|
137
|
+
// Add pagination params
|
|
138
|
+
if (page !== undefined) url.searchParams.set('page', page);
|
|
139
|
+
if (limit !== undefined) url.searchParams.set('limit', limit);
|
|
140
|
+
|
|
141
|
+
// Add filter model (AG Grid format) - send as JSON string
|
|
142
|
+
if (filterModel) {
|
|
143
|
+
const filterStr = typeof filterModel === 'string' ? filterModel : JSON.stringify(filterModel);
|
|
144
|
+
if (filterStr && filterStr !== '{}' && filterStr !== 'null') {
|
|
145
|
+
url.searchParams.set('filterModel', filterStr);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Add sort model (AG Grid format) - send as JSON string
|
|
150
|
+
if (sortModel) {
|
|
151
|
+
const sortStr = typeof sortModel === 'string' ? sortModel : JSON.stringify(sortModel);
|
|
152
|
+
if (sortStr && sortStr !== '[]' && sortStr !== 'null') {
|
|
153
|
+
url.searchParams.set('sortModel', sortStr);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add simple sort params for backwards compatibility
|
|
158
|
+
if (sort) url.searchParams.set('sort', sort);
|
|
159
|
+
if (asc !== undefined) url.searchParams.set('asc', asc);
|
|
160
|
+
if (order) url.searchParams.set('order', order);
|
|
161
|
+
|
|
162
|
+
return url;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Core Service object providing CRUD operations for the core API endpoint.
|
|
167
|
+
* @memberof CoreServiceClient
|
|
168
|
+
*/
|
|
56
169
|
const CoreService = {
|
|
170
|
+
/**
|
|
171
|
+
* Performs a raw GET request to fetch content as text.
|
|
172
|
+
* @memberof CoreServiceClient.CoreService
|
|
173
|
+
* @param {Object} [options={}] - Request options.
|
|
174
|
+
* @param {string} [options.url=''] - The full URL to fetch.
|
|
175
|
+
* @return {Promise<string>} A promise that resolves with the response text.
|
|
176
|
+
*/
|
|
57
177
|
getRaw: (options = { url: '' }) =>
|
|
58
178
|
new Promise((resolve, reject) =>
|
|
59
179
|
fetch(options.url, {
|
|
@@ -71,6 +191,15 @@ const CoreService = {
|
|
|
71
191
|
return reject(error);
|
|
72
192
|
}),
|
|
73
193
|
),
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Performs a POST request to create a new resource.
|
|
197
|
+
* @memberof CoreServiceClient.CoreService
|
|
198
|
+
* @param {Object} [options={}] - Request options.
|
|
199
|
+
* @param {string} [options.id=''] - Optional resource ID to append to the URL.
|
|
200
|
+
* @param {Object} [options.body={}] - The request body payload.
|
|
201
|
+
* @return {Promise<Object>} A promise that resolves with the JSON response.
|
|
202
|
+
*/
|
|
74
203
|
post: (options = { id: '', body: {} }) =>
|
|
75
204
|
new Promise((resolve, reject) =>
|
|
76
205
|
fetch(getApiBaseUrl({ id: options.id, endpoint }), {
|
|
@@ -91,6 +220,15 @@ const CoreService = {
|
|
|
91
220
|
return reject(error);
|
|
92
221
|
}),
|
|
93
222
|
),
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Performs a PUT request to update an existing resource.
|
|
226
|
+
* @memberof CoreServiceClient.CoreService
|
|
227
|
+
* @param {Object} [options={}] - Request options.
|
|
228
|
+
* @param {string} [options.id=''] - The resource ID to update.
|
|
229
|
+
* @param {Object} [options.body={}] - The request body payload with updated data.
|
|
230
|
+
* @return {Promise<Object>} A promise that resolves with the JSON response.
|
|
231
|
+
*/
|
|
94
232
|
put: (options = { id: '', body: {} }) =>
|
|
95
233
|
new Promise((resolve, reject) =>
|
|
96
234
|
fetch(getApiBaseUrl({ id: options.id, endpoint }), {
|
|
@@ -111,6 +249,15 @@ const CoreService = {
|
|
|
111
249
|
return reject(error);
|
|
112
250
|
}),
|
|
113
251
|
),
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Performs a GET request to retrieve a resource.
|
|
255
|
+
* @memberof CoreServiceClient.CoreService
|
|
256
|
+
* @param {Object} [options={}] - Request options.
|
|
257
|
+
* @param {string} [options.id=''] - Optional resource ID to retrieve.
|
|
258
|
+
* @param {Object} [options.body={}] - Unused, kept for API consistency.
|
|
259
|
+
* @return {Promise<Object>} A promise that resolves with the JSON response.
|
|
260
|
+
*/
|
|
114
261
|
get: (options = { id: '', body: {} }) =>
|
|
115
262
|
new Promise((resolve, reject) =>
|
|
116
263
|
fetch(getApiBaseUrl({ id: options.id, endpoint }), {
|
|
@@ -130,6 +277,15 @@ const CoreService = {
|
|
|
130
277
|
return reject(error);
|
|
131
278
|
}),
|
|
132
279
|
),
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Performs a DELETE request to remove a resource.
|
|
283
|
+
* @memberof CoreServiceClient.CoreService
|
|
284
|
+
* @param {Object} [options={}] - Request options.
|
|
285
|
+
* @param {string} [options.id=''] - The resource ID to delete.
|
|
286
|
+
* @param {Object} [options.body={}] - Optional request body payload.
|
|
287
|
+
* @return {Promise<Object>} A promise that resolves with the JSON response.
|
|
288
|
+
*/
|
|
133
289
|
delete: (options = { id: '', body: {} }) =>
|
|
134
290
|
new Promise((resolve, reject) =>
|
|
135
291
|
fetch(getApiBaseUrl({ id: options.id, endpoint }), {
|
|
@@ -152,12 +308,18 @@ const CoreService = {
|
|
|
152
308
|
),
|
|
153
309
|
};
|
|
154
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Alias for getApiBaseUrl function.
|
|
313
|
+
* @memberof CoreServiceClient
|
|
314
|
+
* @type {Function}
|
|
315
|
+
*/
|
|
155
316
|
const ApiBase = getApiBaseUrl;
|
|
156
317
|
|
|
157
318
|
export {
|
|
158
319
|
CoreService,
|
|
159
320
|
headersFactory,
|
|
160
321
|
payloadFactory,
|
|
322
|
+
buildQueryUrl,
|
|
161
323
|
getBaseHost,
|
|
162
324
|
getApiBasePath,
|
|
163
325
|
getApiBaseUrl,
|