underpost 2.7.94 → 2.8.0
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/.vscode/settings.json +3 -0
- package/bin/deploy.js +39 -0
- package/bin/index.js +1 -1
- package/docker-compose.yml +1 -1
- package/package.json +4 -3
- package/src/api/user/user.model.js +1 -1
- package/src/api/user/user.service.js +22 -21
- package/src/client/components/core/Account.js +142 -124
- package/src/client/components/core/Auth.js +94 -1
- package/src/client/components/core/Css.js +1 -1
- package/src/client/components/core/Docs.js +1 -1
- package/src/client/components/core/LogIn.js +8 -1
- package/src/client/components/core/LogOut.js +3 -2
- package/src/client/components/core/Modal.js +7 -3
- package/src/client/components/core/Panel.js +16 -3
- package/src/client/components/core/PanelForm.js +1 -0
- package/src/client/components/core/SignUp.js +2 -5
- package/src/client/components/core/SocketIo.js +2 -0
- package/src/client/components/core/Validator.js +9 -2
- package/src/client/components/core/VanillaJs.js +4 -1
- package/src/client/components/default/LogInDefault.js +2 -23
- package/src/client/components/default/LogOutDefault.js +3 -5
- package/src/client/services/user/user.service.js +9 -1
- package/src/client/ssr/body/CacheControl.js +1 -1
- package/src/client/sw/default.sw.js +10 -4
- package/src/server/client-build.js +22 -21
- package/src/server/client-icons.js +12 -2
- package/src/server/conf.js +2 -1
- package/src/server/network.js +4 -0
- package/src/server/ssl.js +2 -2
- package/src/server/valkey.js +125 -0
package/.vscode/settings.json
CHANGED
package/bin/deploy.js
CHANGED
|
@@ -35,6 +35,7 @@ import { Lampp } from '../src/runtime/lampp/Lampp.js';
|
|
|
35
35
|
import { DefaultConf } from '../conf.js';
|
|
36
36
|
import { JSONweb } from '../src/server/client-formatted.js';
|
|
37
37
|
import ejs from 'easy-json-schema';
|
|
38
|
+
import { Xampp } from '../src/runtime/xampp/Xampp.js';
|
|
38
39
|
|
|
39
40
|
const logger = loggerFactory(import.meta);
|
|
40
41
|
|
|
@@ -283,6 +284,28 @@ try {
|
|
|
283
284
|
}
|
|
284
285
|
break;
|
|
285
286
|
|
|
287
|
+
case 'xampp': {
|
|
288
|
+
const directory = 'c:/xampp/htdocs';
|
|
289
|
+
const host = 'localhost';
|
|
290
|
+
const port = 80;
|
|
291
|
+
Xampp.removeRouter();
|
|
292
|
+
Xampp.appendRouter(` Listen ${port}
|
|
293
|
+
<VirtualHost *:${port}>
|
|
294
|
+
DocumentRoot "${directory}"
|
|
295
|
+
ServerName ${host}:${port}
|
|
296
|
+
|
|
297
|
+
<Directory "${directory}">
|
|
298
|
+
Options Indexes FollowSymLinks MultiViews
|
|
299
|
+
AllowOverride All
|
|
300
|
+
Require all granted
|
|
301
|
+
</Directory>
|
|
302
|
+
|
|
303
|
+
</VirtualHost>
|
|
304
|
+
`);
|
|
305
|
+
if (Xampp.enabled() && Xampp.router) Xampp.initService({ daemon: true });
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
|
|
286
309
|
case 'adminer': {
|
|
287
310
|
const directory = '/dd/engine/public/adminer';
|
|
288
311
|
// const host = '127.0.0.1';
|
|
@@ -998,6 +1021,22 @@ ${uniqueArray(logs.all.map((log) => `- ${log.author_name} ([${log.author_email}]
|
|
|
998
1021
|
break;
|
|
999
1022
|
}
|
|
1000
1023
|
|
|
1024
|
+
case 'valkey': {
|
|
1025
|
+
if (!process.argv.includes('server')) {
|
|
1026
|
+
shellExec(`cd /dd && git clone https://github.com/valkey-io/valkey.git`);
|
|
1027
|
+
shellExec(`cd /dd/valkey && make`);
|
|
1028
|
+
shellExec(`apt install valkey-tools`); // valkey-cli
|
|
1029
|
+
}
|
|
1030
|
+
shellExec(`cd /dd/valkey && ./src/valkey-server`);
|
|
1031
|
+
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
case 'valkey-service': {
|
|
1036
|
+
shellExec(`pm2 start bin/deploy.js --node-args=\"--max-old-space-size=8192\" --name valkey -- valkey server`);
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1001
1040
|
default:
|
|
1002
1041
|
break;
|
|
1003
1042
|
}
|
package/bin/index.js
CHANGED
package/docker-compose.yml
CHANGED
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.
|
|
5
|
+
"version": "2.8.0",
|
|
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",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"pm2-delete": "pm2 delete engine",
|
|
12
12
|
"build": "node bin/deploy build-full-client",
|
|
13
13
|
"build-production": "env-cmd -f .env.production node bin/deploy build-full-client",
|
|
14
|
-
"dev": "env-cmd -f .env.development node src/client.dev
|
|
14
|
+
"dev": "env-cmd -f .env.development node src/client.dev",
|
|
15
15
|
"dev-api": "env-cmd -f .env.development nodemon --watch src --ignore src/client src/api",
|
|
16
16
|
"docs": "jsdoc -c jsdoc.json",
|
|
17
17
|
"backup": "node bin/db default.net/ export",
|
|
@@ -117,7 +117,8 @@
|
|
|
117
117
|
"systeminformation": "^5.21.17",
|
|
118
118
|
"uglify-js": "^3.17.4",
|
|
119
119
|
"validator": "^13.11.0",
|
|
120
|
-
"winston": "^3.11.0"
|
|
120
|
+
"winston": "^3.11.0",
|
|
121
|
+
"iovalkey": "^0.2.1"
|
|
121
122
|
},
|
|
122
123
|
"devDependencies": {
|
|
123
124
|
"clean-jsdoc-theme": "^4.3.0",
|
|
@@ -6,24 +6,13 @@ import { CoreWsEmit } from '../../ws/core/core.ws.emit.js';
|
|
|
6
6
|
import { CoreWsMailerChannel } from '../../ws/core/channels/core.ws.mailer.js';
|
|
7
7
|
import validator from 'validator';
|
|
8
8
|
import { DataBaseProvider } from '../../db/DataBaseProvider.js';
|
|
9
|
-
import { s4 } from '../../client/components/core/CommonJs.js';
|
|
10
9
|
import { FileFactory } from '../file/file.service.js';
|
|
11
|
-
import fs from 'fs-extra';
|
|
12
|
-
import { svg, png, png3x } from 'font-awesome-assets';
|
|
13
10
|
import { UserDto } from './user.model.js';
|
|
14
|
-
import
|
|
11
|
+
import { selectDtoFactory, ValkeyAPI } from '../../server/valkey.js';
|
|
12
|
+
import { getDefaultProfileImageId } from '../../server/client-icons.js';
|
|
15
13
|
|
|
16
14
|
const logger = loggerFactory(import.meta);
|
|
17
15
|
|
|
18
|
-
const getDefaultProfileImageId = async (File) => {
|
|
19
|
-
const faId = 'user';
|
|
20
|
-
const tmpFilePath = `./tmp/${faId}-${s4() + s4()}.svg`;
|
|
21
|
-
fs.writeFileSync(tmpFilePath, svg(faId, '#f5f5f5d1'), 'utf8');
|
|
22
|
-
const file = await new File(FileFactory.svg(fs.readFileSync(tmpFilePath), `${faId}.svg`)).save();
|
|
23
|
-
fs.removeSync(tmpFilePath);
|
|
24
|
-
return file._id;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
16
|
const UserService = {
|
|
28
17
|
post: async (req, res, options) => {
|
|
29
18
|
/** @type {import('./user.model.js').UserModel} */
|
|
@@ -235,6 +224,15 @@ const UserService = {
|
|
|
235
224
|
}
|
|
236
225
|
} else throw new Error('invalid email or password');
|
|
237
226
|
|
|
227
|
+
case 'guest': {
|
|
228
|
+
const user = await ValkeyAPI.valkeyObjectFactory('user', options);
|
|
229
|
+
await ValkeyAPI.setValkeyObject(user.email, user);
|
|
230
|
+
return {
|
|
231
|
+
token: hashJWT({ user: UserDto.auth.payload(user) }),
|
|
232
|
+
user: selectDtoFactory(user, UserDto.select.get()),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
238
236
|
default: {
|
|
239
237
|
const validatePassword = validatePasswordMiddleware(req.body.password);
|
|
240
238
|
if (validatePassword.status === 'error') throw new Error(validatePassword.message);
|
|
@@ -327,13 +325,15 @@ const UserService = {
|
|
|
327
325
|
return await User.find().select(UserDto.select.getAll());
|
|
328
326
|
|
|
329
327
|
case 'auth': {
|
|
330
|
-
const user = await
|
|
331
|
-
|
|
332
|
-
|
|
328
|
+
const user = (await ValkeyAPI.getValkeyObject(req.auth.user.email))
|
|
329
|
+
? await ValkeyAPI.getValkeyObject(req.auth.user.email)
|
|
330
|
+
: await User.findOne({
|
|
331
|
+
_id: req.auth.user._id,
|
|
332
|
+
});
|
|
333
333
|
|
|
334
334
|
const file = await File.findOne({ _id: user.profileImageId });
|
|
335
335
|
|
|
336
|
-
if (!file) {
|
|
336
|
+
if (!file && !(await ValkeyAPI.getValkeyObject(req.auth.user.email))) {
|
|
337
337
|
await User.findByIdAndUpdate(
|
|
338
338
|
user._id,
|
|
339
339
|
{ profileImageId: await getDefaultProfileImageId(File) },
|
|
@@ -342,10 +342,11 @@ const UserService = {
|
|
|
342
342
|
},
|
|
343
343
|
);
|
|
344
344
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
345
|
+
return (await ValkeyAPI.getValkeyObject(req.auth.user.email))
|
|
346
|
+
? selectDtoFactory(await ValkeyAPI.getValkeyObject(req.auth.user.email), UserDto.select.get())
|
|
347
|
+
: await User.findOne({
|
|
348
|
+
_id: req.auth.user._id,
|
|
349
|
+
}).select(UserDto.select.get());
|
|
349
350
|
}
|
|
350
351
|
|
|
351
352
|
default: {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { UserService } from '../../services/user/user.service.js';
|
|
2
|
+
import { Auth } from './Auth.js';
|
|
2
3
|
import { BtnIcon } from './BtnIcon.js';
|
|
3
4
|
import { newInstance, s4 } from './CommonJs.js';
|
|
4
5
|
import { renderStatus, renderWave } from './Css.js';
|
|
5
6
|
import { EventsUI } from './EventsUI.js';
|
|
6
7
|
import { fileFormDataFactory, Input } from './Input.js';
|
|
7
8
|
import { LogIn } from './LogIn.js';
|
|
8
|
-
import { LogOut } from './LogOut.js';
|
|
9
9
|
import { Modal } from './Modal.js';
|
|
10
10
|
import { NotificationManager } from './NotificationManager.js';
|
|
11
11
|
import { Translate } from './Translate.js';
|
|
@@ -21,19 +21,19 @@ const Account = {
|
|
|
21
21
|
const waveAnimationId = 'account-wave';
|
|
22
22
|
const profileFileAccept = ['image/png', 'image/jpeg'];
|
|
23
23
|
setTimeout(async () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
src="${LogIn.Scope.user.main.model.user.profileImage.imageSrc}"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
append(
|
|
25
|
+
`.wave-animation-container-${waveAnimationId}`,
|
|
26
|
+
html` <div class="abs center account-profile-image-container">
|
|
27
|
+
<img
|
|
28
|
+
class="abs center account-profile-image"
|
|
29
|
+
style="opacity: 1"
|
|
30
|
+
${LogIn.Scope.user.main.model.user.profileImage
|
|
31
|
+
? `src="${LogIn.Scope.user.main.model.user.profileImage.imageSrc}"`
|
|
32
|
+
: ''}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="abs center account-profile-image-loading" style="color: white"></div>`,
|
|
36
|
+
);
|
|
37
37
|
|
|
38
38
|
const formData = [
|
|
39
39
|
{
|
|
@@ -49,133 +49,141 @@ const Account = {
|
|
|
49
49
|
rules: [{ type: 'isStrongPassword' }],
|
|
50
50
|
},
|
|
51
51
|
];
|
|
52
|
-
const validators = await Validator.instance(formData);
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const submit = async () => {
|
|
60
|
-
lastUser = newInstance(user);
|
|
61
|
-
const { errorMessage } = await validators();
|
|
62
|
-
if (errorMessage) return;
|
|
63
|
-
const body = {};
|
|
53
|
+
this.formData = formData;
|
|
54
|
+
|
|
55
|
+
this.instanceModalUiEvents = async ({ user }) => {
|
|
56
|
+
const validators = await Validator.instance(formData);
|
|
57
|
+
|
|
64
58
|
for (const inputData of formData) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
body[inputData.model] = s(`.${inputData.id}`).value;
|
|
68
|
-
user[inputData.model] = s(`.${inputData.id}`).value;
|
|
69
|
-
}
|
|
59
|
+
s(`.${inputData.id}`).value =
|
|
60
|
+
!user[inputData.model] && inputData.defaultValue ? inputData.defaultValue : user[inputData.model];
|
|
70
61
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
html:
|
|
74
|
-
result.status === 'error' && result.message
|
|
75
|
-
? result.message
|
|
76
|
-
: Translate.Render(`${result.status}-update-user`),
|
|
77
|
-
status: result.status,
|
|
78
|
-
});
|
|
79
|
-
if (result.status === 'success') {
|
|
80
|
-
user = result.data;
|
|
81
|
-
this.triggerUpdateEvent({ user });
|
|
82
|
-
if (lastUser.emailConfirmed !== user.emailConfirmed) {
|
|
83
|
-
this.renderVerifyEmailStatus(user);
|
|
84
|
-
}
|
|
62
|
+
let lastUser;
|
|
63
|
+
const submit = async () => {
|
|
85
64
|
lastUser = newInstance(user);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (s(`.btn-confirm-email`))
|
|
98
|
-
EventsUI.onClick(`.btn-confirm-email`, async (e) => {
|
|
99
|
-
e.preventDefault();
|
|
100
|
-
const result = await UserService.post({
|
|
101
|
-
id: 'mailer/verify-email',
|
|
102
|
-
body: { email: s(`.account-email`).value },
|
|
103
|
-
});
|
|
65
|
+
const { successKeys } = await validators();
|
|
66
|
+
if (successKeys.length === 0) return;
|
|
67
|
+
const body = {};
|
|
68
|
+
for (const inputData of formData) {
|
|
69
|
+
if (!s(`.${inputData.id}`).value || s(`.${inputData.id}`).value === 'undefined') continue;
|
|
70
|
+
if ('model' in inputData && successKeys.includes(inputData.id)) {
|
|
71
|
+
body[inputData.model] = s(`.${inputData.id}`).value;
|
|
72
|
+
user[inputData.model] = s(`.${inputData.id}`).value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const result = await UserService.put({ id: user._id, body });
|
|
104
76
|
NotificationManager.Push({
|
|
105
|
-
html:
|
|
77
|
+
html:
|
|
78
|
+
result.status === 'error' && result.message
|
|
79
|
+
? result.message
|
|
80
|
+
: Translate.Render(`${result.status}-update-user`),
|
|
106
81
|
status: result.status,
|
|
107
82
|
});
|
|
83
|
+
if (result.status === 'success') {
|
|
84
|
+
user = result.data;
|
|
85
|
+
this.triggerUpdateEvent({ user });
|
|
86
|
+
if (lastUser.emailConfirmed !== user.emailConfirmed) {
|
|
87
|
+
this.renderVerifyEmailStatus(user);
|
|
88
|
+
}
|
|
89
|
+
lastUser = newInstance(user);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
EventsUI.onClick(`.btn-account`, async (e) => {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
await submit();
|
|
108
95
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
s(`.${waveAnimationId}`).style.cursor = 'pointer';
|
|
112
|
-
s(`.${waveAnimationId}`).onclick = async (e) => {
|
|
113
|
-
e.preventDefault();
|
|
114
|
-
s(`.account-profile-image-input`).click();
|
|
115
|
-
};
|
|
116
|
-
EventsUI.onChange(
|
|
117
|
-
`.account-profile-image-input`,
|
|
118
|
-
async (e) => {
|
|
96
|
+
EventsUI.onClick(`.btn-account-update-username`, async (e) => {
|
|
119
97
|
e.preventDefault();
|
|
120
|
-
|
|
121
|
-
|
|
98
|
+
await submit();
|
|
99
|
+
});
|
|
122
100
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
101
|
+
if (s(`.btn-confirm-email`))
|
|
102
|
+
EventsUI.onClick(`.btn-confirm-email`, async (e) => {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
const result = await UserService.post({
|
|
105
|
+
id: 'mailer/verify-email',
|
|
106
|
+
body: { email: s(`.account-email`).value },
|
|
107
|
+
});
|
|
108
|
+
NotificationManager.Push({
|
|
109
|
+
html: result.status === 'error' ? result.message : Translate.Render(`email send`),
|
|
110
|
+
status: result.status,
|
|
111
|
+
});
|
|
127
112
|
});
|
|
113
|
+
this.renderVerifyEmailStatus(user);
|
|
128
114
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
115
|
+
s(`.${waveAnimationId}`).style.cursor = 'pointer';
|
|
116
|
+
s(`.${waveAnimationId}`).onclick = async (e) => {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
s(`.account-profile-image-input`).click();
|
|
119
|
+
};
|
|
120
|
+
EventsUI.onChange(
|
|
121
|
+
`.account-profile-image-input`,
|
|
122
|
+
async (e) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
s(`.account-profile-image`).style.opacity = 0;
|
|
125
|
+
const formFile = fileFormDataFactory(e, profileFileAccept);
|
|
126
|
+
|
|
127
|
+
const { status, data } = await UserService.put({
|
|
128
|
+
id: `profile-image/${user._id}`,
|
|
129
|
+
body: formFile,
|
|
130
|
+
headerId: 'file',
|
|
138
131
|
});
|
|
139
|
-
}
|
|
140
132
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return html`
|
|
155
|
-
<div class="in section-mp" style="text-align: center">${Translate.Render('confirm-delete-account')}</div>
|
|
156
|
-
`;
|
|
133
|
+
if (status === 'success') {
|
|
134
|
+
user.profileImageId = data.profileImageId;
|
|
135
|
+
delete LogIn.Scope.user.main.model.user.profileImage;
|
|
136
|
+
await LogIn.Trigger({ user });
|
|
137
|
+
s(`.account-profile-image`).src = LogIn.Scope.user.main.model.user.profileImage.imageSrc;
|
|
138
|
+
} else {
|
|
139
|
+
NotificationManager.Push({
|
|
140
|
+
html: Translate.Render('file-upload-failed'),
|
|
141
|
+
status: 'error',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
s(`.account-profile-image`).style.opacity = 1;
|
|
157
146
|
},
|
|
158
|
-
|
|
147
|
+
{ loadingContainer: `.account-profile-image-loading` },
|
|
148
|
+
);
|
|
149
|
+
s(`.btn-account-change-password`).onclick = (e) => {
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
// s(`.btn-close-modal-account`).click();
|
|
152
|
+
s(`.main-btn-recover`).click();
|
|
153
|
+
};
|
|
154
|
+
s(`.btn-account-delete-confirm`).onclick = async (e) => {
|
|
155
|
+
e.preventDefault();
|
|
156
|
+
const confirmResult = await Modal.RenderConfirm({
|
|
157
|
+
html: async () => {
|
|
158
|
+
return html`
|
|
159
|
+
<div class="in section-mp" style="text-align: center">
|
|
160
|
+
${Translate.Render('confirm-delete-account')}
|
|
161
|
+
</div>
|
|
162
|
+
`;
|
|
163
|
+
},
|
|
164
|
+
id: 'delete-account-modal',
|
|
165
|
+
});
|
|
166
|
+
if (confirmResult.status === 'cancelled') return;
|
|
167
|
+
s(`.btn-account-delete-confirm`).classList.add('hide');
|
|
168
|
+
s(`.btn-account-delete`).classList.remove('hide');
|
|
169
|
+
s(`.btn-account-delete`).click();
|
|
170
|
+
};
|
|
171
|
+
EventsUI.onClick(`.btn-account-delete`, async (e) => {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
const result = await UserService.delete({ id: user._id });
|
|
174
|
+
NotificationManager.Push({
|
|
175
|
+
html: result.status === 'error' ? result.message : Translate.Render(`success-delete-account`),
|
|
176
|
+
status: result.status,
|
|
177
|
+
});
|
|
178
|
+
s(`.btn-account-delete-confirm`).classList.remove('hide');
|
|
179
|
+
s(`.btn-account-delete`).classList.add('hide');
|
|
180
|
+
if (result.status === 'success') {
|
|
181
|
+
s(`.main-btn-home`).click();
|
|
182
|
+
await Auth.sessionOut();
|
|
183
|
+
}
|
|
159
184
|
});
|
|
160
|
-
if (confirmResult.status === 'cancelled') return;
|
|
161
|
-
s(`.btn-account-delete-confirm`).classList.add('hide');
|
|
162
|
-
s(`.btn-account-delete`).classList.remove('hide');
|
|
163
|
-
s(`.btn-account-delete`).click();
|
|
164
185
|
};
|
|
165
|
-
|
|
166
|
-
e.preventDefault();
|
|
167
|
-
const result = await UserService.delete({ id: user._id });
|
|
168
|
-
NotificationManager.Push({
|
|
169
|
-
html: result.status === 'error' ? result.message : Translate.Render(`success-delete-account`),
|
|
170
|
-
status: result.status,
|
|
171
|
-
});
|
|
172
|
-
s(`.btn-account-delete-confirm`).classList.remove('hide');
|
|
173
|
-
s(`.btn-account-delete`).classList.add('hide');
|
|
174
|
-
if (result.status === 'success') {
|
|
175
|
-
LogOut.Trigger();
|
|
176
|
-
s(`.main-btn-home`).click();
|
|
177
|
-
}
|
|
178
|
-
});
|
|
186
|
+
await this.instanceModalUiEvents({ user });
|
|
179
187
|
});
|
|
180
188
|
return html`
|
|
181
189
|
<input type="file" accept="${profileFileAccept.join(', ')}" class="account-profile-image-input hide" />
|
|
@@ -285,6 +293,16 @@ const Account = {
|
|
|
285
293
|
if (user.emailConfirmed === true) s(`.account-email`).setAttribute('disabled', '');
|
|
286
294
|
}
|
|
287
295
|
},
|
|
296
|
+
instanceModalUiEvents: async (user) => null,
|
|
297
|
+
updateForm: async function (user) {
|
|
298
|
+
if (!s(`.modal-account`)) return;
|
|
299
|
+
await this.instanceModalUiEvents({ user });
|
|
300
|
+
s(`.account-profile-image`).style.opacity = 0;
|
|
301
|
+
for (const inputData of this.formData)
|
|
302
|
+
if (s(`.${inputData.id}`)) s(`.${inputData.id}`).value = user[inputData.model];
|
|
303
|
+
s(`.account-profile-image`).src = LogIn.Scope.user.main.model.user.profileImage.imageSrc;
|
|
304
|
+
s(`.account-profile-image`).style.opacity = 1;
|
|
305
|
+
},
|
|
288
306
|
};
|
|
289
307
|
|
|
290
308
|
export { Account };
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import { UserMock, UserService } from '../../services/user/user.service.js';
|
|
2
|
+
import { Account } from './Account.js';
|
|
3
|
+
import { loggerFactory } from './Logger.js';
|
|
4
|
+
import { LogIn } from './LogIn.js';
|
|
5
|
+
import { LogOut } from './LogOut.js';
|
|
6
|
+
import { SignUp } from './SignUp.js';
|
|
7
|
+
|
|
8
|
+
const logger = loggerFactory(import.meta);
|
|
9
|
+
|
|
1
10
|
const token = Symbol('token');
|
|
2
11
|
|
|
12
|
+
const guestToken = Symbol('guestToken');
|
|
13
|
+
|
|
3
14
|
const Auth = {
|
|
4
15
|
[token]: '',
|
|
16
|
+
[guestToken]: '',
|
|
5
17
|
setToken: function (value = '') {
|
|
6
18
|
return (this[token] = value);
|
|
7
19
|
},
|
|
@@ -11,8 +23,89 @@ const Auth = {
|
|
|
11
23
|
getToken: function () {
|
|
12
24
|
return this[token];
|
|
13
25
|
},
|
|
26
|
+
setGuestToken: function (value = '') {
|
|
27
|
+
return (this[guestToken] = value);
|
|
28
|
+
},
|
|
29
|
+
deleteGuestToken: function () {
|
|
30
|
+
return (this[guestToken] = '');
|
|
31
|
+
},
|
|
32
|
+
getGuestToken: function () {
|
|
33
|
+
return this[guestToken];
|
|
34
|
+
},
|
|
35
|
+
// jwt
|
|
14
36
|
getJWT: function () {
|
|
15
|
-
return `Bearer ${this.getToken()}`;
|
|
37
|
+
return `Bearer ${this.getToken() ? this.getToken() : this.getGuestToken()}`;
|
|
38
|
+
},
|
|
39
|
+
signUpToken: async function (
|
|
40
|
+
result = {
|
|
41
|
+
data: {
|
|
42
|
+
token: '',
|
|
43
|
+
user: null,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
) {
|
|
47
|
+
try {
|
|
48
|
+
localStorage.setItem('jwt', result.data.token);
|
|
49
|
+
await Auth.sessionIn();
|
|
50
|
+
await SignUp.Trigger(result.data);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error(error);
|
|
53
|
+
localStorage.removeItem('jwt');
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
sessionIn: async function (userServicePayload) {
|
|
57
|
+
try {
|
|
58
|
+
const token = userServicePayload?.data?.token ? userServicePayload.data.token : localStorage.getItem('jwt');
|
|
59
|
+
|
|
60
|
+
if (token) {
|
|
61
|
+
this.setToken(token);
|
|
62
|
+
const result = userServicePayload
|
|
63
|
+
? userServicePayload
|
|
64
|
+
: await (async () => {
|
|
65
|
+
const _result = await UserService.get({ id: 'auth' });
|
|
66
|
+
return {
|
|
67
|
+
status: _result.status,
|
|
68
|
+
data: {
|
|
69
|
+
user: _result.data,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
})();
|
|
73
|
+
const { status, data, message } = result;
|
|
74
|
+
if (status === 'success') {
|
|
75
|
+
localStorage.setItem('jwt', token);
|
|
76
|
+
await LogIn.Trigger({ user: data.user });
|
|
77
|
+
await Account.updateForm(data.user);
|
|
78
|
+
return { user: data.user };
|
|
79
|
+
} else throw new Error(message);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// anon guest session
|
|
83
|
+
this.deleteToken();
|
|
84
|
+
localStorage.removeItem('jwt');
|
|
85
|
+
let guestToken = localStorage.getItem('jwt.g');
|
|
86
|
+
|
|
87
|
+
if (!guestToken) {
|
|
88
|
+
const result = await UserService.post({ id: 'guest' });
|
|
89
|
+
localStorage.setItem('jwt.g', result.data.token);
|
|
90
|
+
guestToken = result.data.token;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.setGuestToken(guestToken);
|
|
94
|
+
let { data, status, message } = await UserService.get({ id: 'auth' });
|
|
95
|
+
if (status === 'error') throw new Error(message);
|
|
96
|
+
await Account.updateForm(data);
|
|
97
|
+
return { user: data };
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.error(error);
|
|
100
|
+
localStorage.removeItem('jwt');
|
|
101
|
+
localStorage.removeItem('jwt.g');
|
|
102
|
+
return { user: UserMock.default };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
sessionOut: async function () {
|
|
106
|
+
this.deleteToken();
|
|
107
|
+
localStorage.removeItem('jwt');
|
|
108
|
+
await LogOut.Trigger(await this.sessionIn());
|
|
16
109
|
},
|
|
17
110
|
};
|
|
18
111
|
|
|
@@ -344,7 +344,7 @@ const renderBubbleDialog = async function (
|
|
|
344
344
|
}
|
|
345
345
|
if (options.trianglePositionCss) cssTrianglePosition = options.trianglePositionCss;
|
|
346
346
|
return html` <div
|
|
347
|
-
class="${options?.classSelectors ? options.classSelectors : '
|
|
347
|
+
class="${options?.classSelectors ? options.classSelectors : 'in'} bubble-dialog bubble-dialog-${id}"
|
|
348
348
|
${options.bubbleCss ? `style='${options.bubbleCss}'` : ''}
|
|
349
349
|
>
|
|
350
350
|
<style class="style-bubble-dialog-triangle-${id}">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CoreService } from '../../services/core/core.service.js';
|
|
2
2
|
import { FileService } from '../../services/file/file.service.js';
|
|
3
3
|
import { UserService } from '../../services/user/user.service.js';
|
|
4
|
+
import { Auth } from './Auth.js';
|
|
4
5
|
import { BtnIcon } from './BtnIcon.js';
|
|
5
6
|
import { EventsUI } from './EventsUI.js';
|
|
6
7
|
import { Input } from './Input.js';
|
|
@@ -102,7 +103,13 @@ const LogIn = {
|
|
|
102
103
|
if ('model' in inputData) body[inputData.model] = s(`.${inputData.id}`).value;
|
|
103
104
|
}
|
|
104
105
|
const result = await UserService.post({ id: 'auth', body });
|
|
105
|
-
if (result.status === 'success')
|
|
106
|
+
if (result.status === 'success') {
|
|
107
|
+
await Auth.sessionIn(result);
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
if (s(`.modal-log-in`)) s(`.btn-close-modal-log-in`).click();
|
|
110
|
+
if (s(`.modal-sign-up`)) s(`.btn-close-modal-sign-up`).click();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
106
113
|
if (result.status === 'error' && result.message.match('attempts')) {
|
|
107
114
|
htmls(`.login-attempt-warn-value`, result.message.split(':')[1]);
|
|
108
115
|
s(`.login-attempt-warn-container`).classList.remove('hide');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Auth } from './Auth.js';
|
|
1
2
|
import { BtnIcon } from './BtnIcon.js';
|
|
2
3
|
import { LogIn } from './LogIn.js';
|
|
3
4
|
import { Translate } from './Translate.js';
|
|
@@ -37,9 +38,9 @@ const LogOut = {
|
|
|
37
38
|
},
|
|
38
39
|
Render: async function () {
|
|
39
40
|
setTimeout(() => {
|
|
40
|
-
s('.btn-log-out').onclick = (e) => {
|
|
41
|
+
s('.btn-log-out').onclick = async (e) => {
|
|
41
42
|
e.preventDefault();
|
|
42
|
-
|
|
43
|
+
await Auth.sessionOut();
|
|
43
44
|
};
|
|
44
45
|
});
|
|
45
46
|
// Translate.Render('confirm-logout')
|
|
@@ -87,6 +87,8 @@ const Modal = {
|
|
|
87
87
|
onObserverListener: {},
|
|
88
88
|
onClickListener: {},
|
|
89
89
|
onExpandUiListener: {},
|
|
90
|
+
onBarUiOpen: {},
|
|
91
|
+
onBarUiClose: {},
|
|
90
92
|
query: options.query ? `${window.location.search}` : undefined,
|
|
91
93
|
};
|
|
92
94
|
if (options && 'mode' in options) {
|
|
@@ -286,6 +288,8 @@ const Modal = {
|
|
|
286
288
|
s(`.modal-menu`).style.top = '0px';
|
|
287
289
|
s(`.main-body-btn-container`).style.top = '50px';
|
|
288
290
|
s(`.main-body`).style.top = '0px';
|
|
291
|
+
for (const event of Object.keys(Modal.Data[idModal].onBarUiClose))
|
|
292
|
+
Modal.Data[idModal].onBarUiClose[event]();
|
|
289
293
|
} else {
|
|
290
294
|
s(`.main-body-btn-ui-close`).classList.remove('hide');
|
|
291
295
|
s(`.main-body-btn-ui-open`).classList.add('hide');
|
|
@@ -296,6 +300,8 @@ const Modal = {
|
|
|
296
300
|
s(`.slide-menu-top-bar`).classList.remove('hide');
|
|
297
301
|
s(`.bottom-bar`).classList.remove('hide');
|
|
298
302
|
s(`.main-body`).style.top = `${options.heightTopBar}px`;
|
|
303
|
+
for (const event of Object.keys(Modal.Data[idModal].onBarUiOpen))
|
|
304
|
+
Modal.Data[idModal].onBarUiOpen[event]();
|
|
299
305
|
}
|
|
300
306
|
Responsive.Event[`slide-menu-modal-menu`]();
|
|
301
307
|
Object.keys(this.Data).map((_idModal) => {
|
|
@@ -1321,7 +1327,6 @@ const Modal = {
|
|
|
1321
1327
|
case 'slide-menu-right':
|
|
1322
1328
|
case 'slide-menu-left':
|
|
1323
1329
|
const backMenuButtonEvent = async () => {
|
|
1324
|
-
if (location.pathname !== getProxyPath()) setPath(getProxyPath());
|
|
1325
1330
|
if (s(`.menu-btn-container-children`)) htmls(`.menu-btn-container-children`, '');
|
|
1326
1331
|
// htmls(`.nav-title-display-${'modal-menu'}`, html`<i class="fas fa-home"></i> ${Translate.Render('home')}`);
|
|
1327
1332
|
htmls(`.nav-title-display-${'modal-menu'}`, html``);
|
|
@@ -1340,8 +1345,7 @@ const Modal = {
|
|
|
1340
1345
|
backMenuButtonEvent();
|
|
1341
1346
|
}
|
|
1342
1347
|
s(`.btn-close-modal-menu`).click();
|
|
1343
|
-
|
|
1344
|
-
setPath(getProxyPath());
|
|
1348
|
+
setPath(getProxyPath());
|
|
1345
1349
|
setDocTitle({ ...options.RouterInstance, route: '' });
|
|
1346
1350
|
};
|
|
1347
1351
|
EventsUI.onClick(`.btn-icon-menu-back`, backMenuButtonEvent);
|
|
@@ -372,13 +372,26 @@ const Panel = {
|
|
|
372
372
|
} else {
|
|
373
373
|
Responsive.Event[`${idPanel}-responsive`] = () => {
|
|
374
374
|
if (s(`.${idPanel}-form-container`))
|
|
375
|
-
s(`.${idPanel}-form-container`).style.maxHeight =
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
s(`.${idPanel}-form-container`).style.maxHeight =
|
|
376
|
+
options.route === 'home' &&
|
|
377
|
+
s(`.${idPanel}-form-body`) &&
|
|
378
|
+
!s(`.${idPanel}-form-body`).classList.contains('hide') &&
|
|
379
|
+
!s(`.main-body-btn-ui-open`).classList.contains('hide')
|
|
380
|
+
? `${window.innerHeight}px`
|
|
381
|
+
: `${window.innerHeight - heightTopBar - heightBottomBar}px`;
|
|
378
382
|
};
|
|
379
383
|
Responsive.Event[`${idPanel}-responsive`]();
|
|
380
384
|
}
|
|
381
385
|
};
|
|
386
|
+
if (options.route === 'home') {
|
|
387
|
+
Modal.Data['modal-menu'].onBarUiClose[`${idPanel}-responsive`] = () => {
|
|
388
|
+
resizeParentModal();
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
Modal.Data['modal-menu'].onBarUiOpen[`${idPanel}-responsive`] = () => {
|
|
392
|
+
resizeParentModal();
|
|
393
|
+
};
|
|
394
|
+
}
|
|
382
395
|
setTimeout(resizeParentModal);
|
|
383
396
|
if (options.route) {
|
|
384
397
|
RouterEvents[options.parentIdModal] = ({ route }) => {
|
|
@@ -113,6 +113,7 @@ const PanelForm = {
|
|
|
113
113
|
filesData: () => PanelForm.Data[idPanel].filesData,
|
|
114
114
|
scrollClassContainer: options.scrollClassContainer ? options.scrollClassContainer : 'main-body',
|
|
115
115
|
titleIcon,
|
|
116
|
+
route: options.route,
|
|
116
117
|
formContainerClass: 'session-in-log-in',
|
|
117
118
|
onClick: async function ({ payload }) {
|
|
118
119
|
if (options.route) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { UserService } from '../../services/user/user.service.js';
|
|
2
|
+
import { Auth } from './Auth.js';
|
|
2
3
|
import { BtnIcon } from './BtnIcon.js';
|
|
3
4
|
import { EventsUI } from './EventsUI.js';
|
|
4
5
|
import { Input } from './Input.js';
|
|
5
|
-
import { LogIn } from './LogIn.js';
|
|
6
6
|
import { NotificationManager } from './NotificationManager.js';
|
|
7
7
|
import { Translate } from './Translate.js';
|
|
8
8
|
import { Validator } from './Validator.js';
|
|
@@ -52,10 +52,7 @@ const SignUp = {
|
|
|
52
52
|
: Translate.Render(`no-valid-register`),
|
|
53
53
|
status: result.status,
|
|
54
54
|
});
|
|
55
|
-
if (result.status === 'success')
|
|
56
|
-
await this.Trigger(result.data);
|
|
57
|
-
await LogIn.Trigger(result.data);
|
|
58
|
-
}
|
|
55
|
+
if (result.status === 'success') await Auth.signUpToken(result);
|
|
59
56
|
});
|
|
60
57
|
s(`.btn-sign-up-i-have-account`).onclick = () => {
|
|
61
58
|
s(`.main-btn-log-in`).click();
|
|
@@ -35,6 +35,8 @@ const SocketIo = {
|
|
|
35
35
|
// forceNew: true,
|
|
36
36
|
// reconnectionAttempts: 'Infinity',
|
|
37
37
|
// timeout: 10000,
|
|
38
|
+
// withCredentials: true,
|
|
39
|
+
// autoConnect: 5000,
|
|
38
40
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
39
41
|
};
|
|
40
42
|
// logger.error(`connect options:`, JSON.stringify(connectOptions, null, 4));
|
|
@@ -88,11 +88,18 @@ const Validator = {
|
|
|
88
88
|
|
|
89
89
|
return async () => {
|
|
90
90
|
let errorMessage = '';
|
|
91
|
+
const errorKeys = [];
|
|
92
|
+
const successKeys = [];
|
|
91
93
|
for (const validatorKey of Object.keys(validatorFunction)) {
|
|
92
94
|
const result = await validatorFunction[validatorKey]();
|
|
93
|
-
if (result && result.errorMessage)
|
|
95
|
+
if (result && result.errorMessage) {
|
|
96
|
+
errorMessage += result.errorMessage;
|
|
97
|
+
errorKeys.push(validatorKey);
|
|
98
|
+
} else {
|
|
99
|
+
successKeys.push(validatorKey);
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
|
-
return { errorMessage };
|
|
102
|
+
return { errorMessage, errorKeys, successKeys };
|
|
96
103
|
};
|
|
97
104
|
},
|
|
98
105
|
};
|
|
@@ -148,7 +148,10 @@ const pasteData = () => new Promise((resolve) => navigator.clipboard.readText().
|
|
|
148
148
|
* history.
|
|
149
149
|
* @memberof VanillaJS
|
|
150
150
|
*/
|
|
151
|
-
const setPath = (path = '/', stateStorage = {}, title = '') =>
|
|
151
|
+
const setPath = (path = '/', stateStorage = {}, title = '') => {
|
|
152
|
+
if (window.location.pathname === path || window.location.pathname === `${path}/`) return;
|
|
153
|
+
return history.pushState(stateStorage, title, path);
|
|
154
|
+
};
|
|
152
155
|
|
|
153
156
|
/**
|
|
154
157
|
* The function `getQueryParams` extracts query parameters from the current URL and returns them as an
|
|
@@ -7,35 +7,14 @@ import { ElementsDefault } from './ElementsDefault.js';
|
|
|
7
7
|
const LogInDefault = async function () {
|
|
8
8
|
LogIn.Event['LogInDefault'] = async (options) => {
|
|
9
9
|
const { token, user } = options;
|
|
10
|
-
|
|
11
|
-
if (token) {
|
|
12
|
-
localStorage.setItem('jwt', token);
|
|
13
|
-
Auth.setToken(token);
|
|
14
|
-
}
|
|
15
10
|
ElementsDefault.Data.user.main.model.user = user;
|
|
16
|
-
|
|
17
11
|
s(`.main-btn-log-in`).style.display = 'none';
|
|
18
12
|
s(`.main-btn-sign-up`).style.display = 'none';
|
|
19
13
|
s(`.main-btn-log-out`).style.display = null;
|
|
20
14
|
s(`.main-btn-account`).style.display = null;
|
|
21
|
-
|
|
22
|
-
if (s(`.modal-log-in`)) s(`.btn-close-modal-log-in`).click();
|
|
23
|
-
if (s(`.modal-sign-up`)) s(`.btn-close-modal-sign-up`).click();
|
|
24
15
|
};
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
Auth.setToken(token);
|
|
28
|
-
const result = await UserService.get({ id: 'auth' });
|
|
29
|
-
if (result.status === 'success' && result.data) {
|
|
30
|
-
const user = result.data;
|
|
31
|
-
await LogIn.Trigger({
|
|
32
|
-
token,
|
|
33
|
-
user,
|
|
34
|
-
});
|
|
35
|
-
} else localStorage.removeItem('jwt');
|
|
36
|
-
} else {
|
|
37
|
-
// Anon
|
|
38
|
-
}
|
|
16
|
+
const { user } = await Auth.sessionIn();
|
|
17
|
+
ElementsDefault.Data.user.main.model.user = user;
|
|
39
18
|
};
|
|
40
19
|
|
|
41
20
|
export { LogInDefault };
|
|
@@ -6,8 +6,9 @@ import { s } from '../core/VanillaJs.js';
|
|
|
6
6
|
import { ElementsDefault } from './ElementsDefault.js';
|
|
7
7
|
|
|
8
8
|
const LogOutDefault = async function () {
|
|
9
|
-
LogOut.Event['LogOutDefault'] = async () => {
|
|
10
|
-
|
|
9
|
+
LogOut.Event['LogOutDefault'] = async (result = { user: { _id: '' } }) => {
|
|
10
|
+
ElementsDefault.Data.user.main.model.user = result.user;
|
|
11
|
+
|
|
11
12
|
s(`.main-btn-log-out`).style.display = 'none';
|
|
12
13
|
s(`.main-btn-account`).style.display = 'none';
|
|
13
14
|
s(`.main-btn-log-in`).style.display = null;
|
|
@@ -15,9 +16,6 @@ const LogOutDefault = async function () {
|
|
|
15
16
|
if (s(`.modal-log-out`)) s(`.btn-close-modal-log-out`).click();
|
|
16
17
|
if (s(`.modal-account`)) s(`.btn-close-modal-account`).click();
|
|
17
18
|
|
|
18
|
-
ElementsDefault.Data.user.main.model.user = { _id: '' };
|
|
19
|
-
Auth.deleteToken();
|
|
20
|
-
|
|
21
19
|
NotificationManager.Push({
|
|
22
20
|
html: Translate.Render(`success-logout`),
|
|
23
21
|
status: 'success',
|
|
@@ -8,6 +8,14 @@ logger.info('Load service');
|
|
|
8
8
|
|
|
9
9
|
const endpoint = 'user';
|
|
10
10
|
|
|
11
|
+
const UserMock = {
|
|
12
|
+
default: {
|
|
13
|
+
username: 'guest',
|
|
14
|
+
email: `guest@${location.hostname}`,
|
|
15
|
+
role: 'guest',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
11
19
|
const UserService = {
|
|
12
20
|
post: (options = { id: '', body: {} }) =>
|
|
13
21
|
new Promise((resolve, reject) =>
|
|
@@ -86,4 +94,4 @@ const UserService = {
|
|
|
86
94
|
),
|
|
87
95
|
};
|
|
88
96
|
|
|
89
|
-
export { UserService };
|
|
97
|
+
export { UserService, UserMock };
|
|
@@ -109,6 +109,6 @@ SrrComponent = ({ ttiLoadTimeLimit }) => {
|
|
|
109
109
|
const CacheControl = ${CacheControl};
|
|
110
110
|
CacheControl({ ttiLoadTimeLimit: ${ttiLoadTimeLimit ? ttiLoadTimeLimit : 1000 * 70 * 1} });
|
|
111
111
|
</script>
|
|
112
|
-
<div class="clean-cache-container">v2.
|
|
112
|
+
<div class="clean-cache-container">v2.8.0</div>
|
|
113
113
|
`;
|
|
114
114
|
};
|
|
@@ -64,10 +64,16 @@ self.addEventListener('fetch', (event) => {
|
|
|
64
64
|
} catch (error) {
|
|
65
65
|
console.error('Error opening cache for pre cached page', event.request.url, error);
|
|
66
66
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
if (event.request.method.toUpperCase() === 'GET') {
|
|
68
|
+
const cache = await caches.open(CACHE_NAME);
|
|
69
|
+
const preCachedResponse = await cache.match(`${PROXY_PATH === '/' ? '' : PROXY_PATH}/offline/index.html`);
|
|
70
|
+
if (!preCachedResponse) throw new Error(error.message);
|
|
71
|
+
return preCachedResponse;
|
|
72
|
+
}
|
|
73
|
+
const response = new Response(JSON.stringify({ status: 'error', message: 'offline test response' }));
|
|
74
|
+
// response.status = 200;
|
|
75
|
+
response.headers.set('Content-Type', 'application/json');
|
|
76
|
+
return response;
|
|
71
77
|
} catch (error) {
|
|
72
78
|
console.error('Error opening cache for offline page', event.request.url, error);
|
|
73
79
|
const response = new Response(JSON.stringify({ status: 'error', message: error.message }));
|
|
@@ -700,28 +700,7 @@ root file where the route starts, such as index.js, app.js, routes.js, etc ... *
|
|
|
700
700
|
|
|
701
701
|
await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
|
|
702
702
|
}
|
|
703
|
-
if (!enableLiveRebuild && process.argv.includes('zip')) {
|
|
704
|
-
logger.warn('build zip', rootClientPath);
|
|
705
|
-
|
|
706
|
-
if (!fs.existsSync('./build')) fs.mkdirSync('./build');
|
|
707
|
-
|
|
708
|
-
const zip = new AdmZip();
|
|
709
|
-
const files = await fs.readdir(rootClientPath, { recursive: true });
|
|
710
|
-
|
|
711
|
-
for (const relativePath of files) {
|
|
712
|
-
const filePath = dir.resolve(`${rootClientPath}/${relativePath}`);
|
|
713
|
-
if (!fs.lstatSync(filePath).isDirectory()) {
|
|
714
|
-
const folder = dir.relative(`public/${host}${path}`, dir.dirname(filePath));
|
|
715
|
-
zip.addLocalFile(filePath, folder);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
const buildId = `${host}-${path.replaceAll('/', '')}`;
|
|
720
703
|
|
|
721
|
-
logger.warn('write zip', `./build/${buildId}.zip`);
|
|
722
|
-
|
|
723
|
-
zip.writeZip(`./build/${buildId}.zip`);
|
|
724
|
-
}
|
|
725
704
|
if (client) {
|
|
726
705
|
let PRE_CACHED_RESOURCES = [];
|
|
727
706
|
|
|
@@ -792,6 +771,28 @@ root file where the route starts, such as index.js, app.js, routes.js, etc ... *
|
|
|
792
771
|
);
|
|
793
772
|
}
|
|
794
773
|
}
|
|
774
|
+
if (!enableLiveRebuild && process.argv.includes('zip')) {
|
|
775
|
+
logger.warn('build zip', rootClientPath);
|
|
776
|
+
|
|
777
|
+
if (!fs.existsSync('./build')) fs.mkdirSync('./build');
|
|
778
|
+
|
|
779
|
+
const zip = new AdmZip();
|
|
780
|
+
const files = await fs.readdir(rootClientPath, { recursive: true });
|
|
781
|
+
|
|
782
|
+
for (const relativePath of files) {
|
|
783
|
+
const filePath = dir.resolve(`${rootClientPath}/${relativePath}`);
|
|
784
|
+
if (!fs.lstatSync(filePath).isDirectory()) {
|
|
785
|
+
const folder = dir.relative(`public/${host}${path}`, dir.dirname(filePath));
|
|
786
|
+
zip.addLocalFile(filePath, folder);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const buildId = `${host}-${path.replaceAll('/', '')}`;
|
|
791
|
+
|
|
792
|
+
logger.warn('write zip', `./build/${buildId}.zip`);
|
|
793
|
+
|
|
794
|
+
zip.writeZip(`./build/${buildId}.zip`);
|
|
795
|
+
}
|
|
795
796
|
}
|
|
796
797
|
}
|
|
797
798
|
};
|
|
@@ -3,8 +3,9 @@ import { favicons } from 'favicons';
|
|
|
3
3
|
// import textToImage from 'text-to-image';
|
|
4
4
|
import { loggerFactory } from './logger.js';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
|
-
import { png3x } from 'font-awesome-assets';
|
|
7
6
|
import { getCapVariableName, s4 } from '../client/components/core/CommonJs.js';
|
|
7
|
+
import { FileFactory } from '../api/file/file.service.js';
|
|
8
|
+
import { svg, png, png3x } from 'font-awesome-assets';
|
|
8
9
|
|
|
9
10
|
const logger = loggerFactory(import.meta);
|
|
10
11
|
|
|
@@ -148,4 +149,13 @@ const buildIcons = async ({
|
|
|
148
149
|
}
|
|
149
150
|
};
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
const getDefaultProfileImageId = async (File) => {
|
|
153
|
+
const faId = 'user';
|
|
154
|
+
const tmpFilePath = `./tmp/${faId}-${s4() + s4()}.svg`;
|
|
155
|
+
fs.writeFileSync(tmpFilePath, svg(faId, '#f5f5f5d1'), 'utf8');
|
|
156
|
+
const file = await new File(FileFactory.svg(fs.readFileSync(tmpFilePath), `${faId}.svg`)).save();
|
|
157
|
+
fs.removeSync(tmpFilePath);
|
|
158
|
+
return file._id;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export { buildIcons, buildTextImg, defaultBaseTextImgOptions, faBase64Png, getBufferPngText, getDefaultProfileImageId };
|
package/src/server/conf.js
CHANGED
|
@@ -583,7 +583,7 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
583
583
|
const confServer = DefaultConf.server[host][path];
|
|
584
584
|
const confClient = DefaultConf.client[client];
|
|
585
585
|
const confSsr = DefaultConf.ssr[ssr];
|
|
586
|
-
const clients = Object.keys(confClient).concat(['core', 'test', 'default']);
|
|
586
|
+
const clients = Object.keys(confClient).concat(['core', 'test', 'default', 'user']);
|
|
587
587
|
|
|
588
588
|
if (absolutePath.match('src/api') && !confServer.apis.find((p) => absolutePath.match(`src/api/${p}/`))) {
|
|
589
589
|
return false;
|
|
@@ -935,6 +935,7 @@ const maintenanceMiddleware = (req, res, port, proxyRouter) => {
|
|
|
935
935
|
|
|
936
936
|
const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
|
|
937
937
|
shellExec(`pm2 kill`);
|
|
938
|
+
shellExec(`node bin/deploy valkey-service`);
|
|
938
939
|
const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
|
|
939
940
|
shellExec(`node bin/deploy conf ${proxyDeployId} production`);
|
|
940
941
|
shellExec(`node bin/deploy run ${proxyDeployId} maintenance`);
|
package/src/server/network.js
CHANGED
|
@@ -74,6 +74,10 @@ const saveRuntimeRouter = async () => {
|
|
|
74
74
|
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
75
75
|
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
76
76
|
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
77
|
+
if (!deployId || !host || !path) {
|
|
78
|
+
logger.warn('default deploy instance not found');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
77
81
|
const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
|
|
78
82
|
const { db } = confServer[host][path];
|
|
79
83
|
|
package/src/server/ssl.js
CHANGED
|
@@ -50,8 +50,8 @@ const buildSSL = async (host) => {
|
|
|
50
50
|
|
|
51
51
|
fs.writeFileSync(`./engine-private/ssl/${host}/_ca_bundle.crt`, ca, 'utf8');
|
|
52
52
|
fs.writeFileSync(`./engine-private/ssl/${host}/_ca_full_bundle.crt`, caFull, 'utf8');
|
|
53
|
-
|
|
54
|
-
fs.removeSync(`${sslPath}/${folderHost}`);
|
|
53
|
+
// TODO: no repeat folderHost match
|
|
54
|
+
// fs.removeSync(`${sslPath}/${folderHost}`);
|
|
55
55
|
return true;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import Valkey from 'iovalkey';
|
|
2
|
+
import mongoose from 'mongoose';
|
|
3
|
+
import { hashPassword } from './auth.js';
|
|
4
|
+
import { loggerFactory } from './logger.js';
|
|
5
|
+
|
|
6
|
+
const logger = loggerFactory(import.meta);
|
|
7
|
+
|
|
8
|
+
let valkeyEnabled = true;
|
|
9
|
+
|
|
10
|
+
const disableValkeyErrorMessage = 'valkey is not enabled';
|
|
11
|
+
|
|
12
|
+
const isValkeyEnable = () => valkeyEnabled;
|
|
13
|
+
|
|
14
|
+
const selectDtoFactory = (payload, select) => {
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const key of Object.keys(select)) {
|
|
17
|
+
if (select[key] === 1 && key in payload) result[key] = payload[key];
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const valkeyClientFactory = async () => {
|
|
23
|
+
const valkey = new Valkey({
|
|
24
|
+
retryStrategy: (attempt) => {
|
|
25
|
+
if (attempt === 1) {
|
|
26
|
+
valkey.disconnect();
|
|
27
|
+
valkeyEnabled = false;
|
|
28
|
+
logger.warn('Valkey service not enabled', { valkeyEnabled });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
return 1000; // 1 second interval attempt
|
|
32
|
+
},
|
|
33
|
+
}); // Connect to 127.0.0.1:6379
|
|
34
|
+
// new Valkey(6380); // 127.0.0.1:6380
|
|
35
|
+
// new Valkey(6379, '192.168.1.1'); // 192.168.1.1:6379
|
|
36
|
+
// new Valkey('/tmp/redis.sock');
|
|
37
|
+
// new Valkey({
|
|
38
|
+
// port: 6379, // Valkey port
|
|
39
|
+
// host: '127.0.0.1', // Valkey host
|
|
40
|
+
// username: 'default', // needs Valkey >= 6
|
|
41
|
+
// password: 'my-top-secret',
|
|
42
|
+
// db: 0, // Defaults to 0
|
|
43
|
+
// });
|
|
44
|
+
return valkey;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const getValkeyObject = async (key = '') => {
|
|
48
|
+
if (!valkeyEnabled) {
|
|
49
|
+
logger.warn(disableValkeyErrorMessage + ' get', key);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const object = await valkey.get(key);
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(object);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return object;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const setValkeyObject = async (key = '', payload = {}) => {
|
|
61
|
+
if (!valkeyEnabled) throw new Error(disableValkeyErrorMessage);
|
|
62
|
+
return await valkey.set(key, JSON.stringify(payload));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const updateValkeyObject = async (key = '', payload = {}) => {
|
|
66
|
+
if (!valkeyEnabled) throw new Error(disableValkeyErrorMessage);
|
|
67
|
+
const object = await getValkeyObject(key, valkey);
|
|
68
|
+
object.updatedAt = new Date().toISOString();
|
|
69
|
+
return await valkey.set(key, JSON.stringify({ ...object, ...payload }));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const valkeyObjectFactory = async (module = '', options = { host: 'localhost', object: {} }) => {
|
|
73
|
+
if (!valkeyEnabled) throw new Error(disableValkeyErrorMessage);
|
|
74
|
+
const idoDate = new Date().toISOString();
|
|
75
|
+
options.object = options.object || {};
|
|
76
|
+
const { object } = options;
|
|
77
|
+
const _id = new mongoose.Types.ObjectId().toString();
|
|
78
|
+
object._id = _id;
|
|
79
|
+
object.createdAt = idoDate;
|
|
80
|
+
object.updatedAt = idoDate;
|
|
81
|
+
switch (module) {
|
|
82
|
+
case 'user': {
|
|
83
|
+
const role = 'guest';
|
|
84
|
+
object._id = `${role}${_id}`;
|
|
85
|
+
return {
|
|
86
|
+
...object,
|
|
87
|
+
username: `${role}${_id.slice(-5)}`,
|
|
88
|
+
email: `${_id}@${options.host}`,
|
|
89
|
+
password: hashPassword(process.env.JWT_SECRET),
|
|
90
|
+
role,
|
|
91
|
+
failedLoginAttempts: 0,
|
|
92
|
+
phoneNumbers: [],
|
|
93
|
+
publicKey: [],
|
|
94
|
+
profileImageId: null,
|
|
95
|
+
emailConfirmed: false,
|
|
96
|
+
recoverTimeOut: null,
|
|
97
|
+
lastLoginDate: null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
throw new Error(`module schema not found: ${module}`);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const ValkeyAPI = {
|
|
106
|
+
valkeyClientFactory,
|
|
107
|
+
selectDtoFactory,
|
|
108
|
+
getValkeyObject,
|
|
109
|
+
setValkeyObject,
|
|
110
|
+
valkeyObjectFactory,
|
|
111
|
+
updateValkeyObject,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const valkey = await ValkeyAPI.valkeyClientFactory();
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
valkeyClientFactory,
|
|
118
|
+
selectDtoFactory,
|
|
119
|
+
getValkeyObject,
|
|
120
|
+
setValkeyObject,
|
|
121
|
+
valkeyObjectFactory,
|
|
122
|
+
updateValkeyObject,
|
|
123
|
+
isValkeyEnable,
|
|
124
|
+
ValkeyAPI,
|
|
125
|
+
};
|