@yunsoft/yuncms-api 0.1.11 → 0.1.13
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/package.json +2 -2
- package/src/error-response.js +1 -0
- package/src/routes/auth.js +88 -6
- package/src/routes/studio-settings.js +4 -0
- package/studio-dist/assets/{index-sB3p3QRk.css → index-DyyGvOze.css} +1 -1
- package/studio-dist/assets/index-SpwZMXar.js +11 -0
- package/studio-dist/index.html +2 -2
- package/studio-dist/assets/index-B5vuZY2c.js +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yunsoft/yuncms-api",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Express API runtime and bundled Studio server for YunCMS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@modelcontextprotocol/node": "2.0.0",
|
|
36
36
|
"@modelcontextprotocol/server": "2.0.0",
|
|
37
37
|
"@node-saml/node-saml": "5.1.0",
|
|
38
|
-
"@yunsoft/yuncms-core": "0.1.
|
|
38
|
+
"@yunsoft/yuncms-core": "0.1.13",
|
|
39
39
|
"express": "5.2.1",
|
|
40
40
|
"ldapts": "9.0.0",
|
|
41
41
|
"openid-client": "6.8.7",
|
package/src/error-response.js
CHANGED
package/src/routes/auth.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createSystemAccountability } from '@yunsoft/yuncms-core';
|
|
1
2
|
import express from 'express';
|
|
2
3
|
|
|
3
4
|
import { createFixedWindowRateLimit } from '../rate-limit.js';
|
|
@@ -7,10 +8,20 @@ function service(req, name) {
|
|
|
7
8
|
const Service = req.context.services[name];
|
|
8
9
|
return new Service(serviceOptionsFromRequest(req));
|
|
9
10
|
}
|
|
11
|
+
function systemService(req, name) {
|
|
12
|
+
const Service = req.context.services[name];
|
|
13
|
+
return new Service({
|
|
14
|
+
...serviceOptionsFromRequest(req),
|
|
15
|
+
accountability: createSystemAccountability(),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
10
18
|
function authService(req) { return service(req, 'AuthService'); }
|
|
11
19
|
function authTokensService(req) { return service(req, 'AuthTokensService'); }
|
|
20
|
+
function systemAuthTokensService(req) { return systemService(req, 'AuthTokensService'); }
|
|
12
21
|
function apiTokensService(req) { return service(req, 'ApiTokensService'); }
|
|
13
22
|
function usersService(req) { return service(req, 'UsersService'); }
|
|
23
|
+
function systemUsersService(req) { return systemService(req, 'UsersService'); }
|
|
24
|
+
function studioSettingsService(req) { return service(req, 'StudioSettingsService'); }
|
|
14
25
|
function externalAuthService(req, registry) {
|
|
15
26
|
const Service = req.context.services.ExternalAuthService;
|
|
16
27
|
return new Service({
|
|
@@ -45,6 +56,14 @@ function actionUrl(config, action, token) {
|
|
|
45
56
|
return `${config.auth.publicUrl}/?auth_action=${encodeURIComponent(action)}&token=${encodeURIComponent(token)}`;
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
function verificationMessage(config, token) {
|
|
60
|
+
const url = actionUrl(config, 'verify', token);
|
|
61
|
+
return {
|
|
62
|
+
subject: 'Verify your YunCMS email',
|
|
63
|
+
text: `Verify your YunCMS email address by opening this link:\n${url}\n\nIf you did not request this, you can ignore this message.`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
48
67
|
function noStore(req, res, next) {
|
|
49
68
|
res.set('cache-control', 'no-store');
|
|
50
69
|
res.set('pragma', 'no-cache');
|
|
@@ -97,6 +116,43 @@ export function createAuthRouter({
|
|
|
97
116
|
res.json({ data: externalAuthRegistry?.publicProviders?.() ?? [] });
|
|
98
117
|
});
|
|
99
118
|
|
|
119
|
+
router.post('/register', actionLimit, async (req, res) => {
|
|
120
|
+
const settings = await studioSettingsService(req).readPublic();
|
|
121
|
+
const verificationRequired = settings.public_registration_require_email_verification === true;
|
|
122
|
+
const transport = verificationRequired ? requireMailer(mailer) : null;
|
|
123
|
+
const data = await usersService(req).registerPublic(req.body ?? {});
|
|
124
|
+
|
|
125
|
+
if (verificationRequired) {
|
|
126
|
+
try {
|
|
127
|
+
const result = await systemAuthTokensService(req).createEmailVerification(data.id);
|
|
128
|
+
const message = verificationMessage(config, result.token);
|
|
129
|
+
await transport.send({ to: data.email, ...message }, {
|
|
130
|
+
accountability: req.accountability,
|
|
131
|
+
requestId: req.id,
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
try {
|
|
135
|
+
await systemUsersService(req).deleteOne(data.id);
|
|
136
|
+
} catch (cleanupError) {
|
|
137
|
+
logger.error?.('YunCMS public registration cleanup failed', {
|
|
138
|
+
requestId: req.id,
|
|
139
|
+
user: data.id,
|
|
140
|
+
code: cleanupError?.code,
|
|
141
|
+
message: cleanupError?.message,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
res.status(201).json({
|
|
149
|
+
data: {
|
|
150
|
+
...data,
|
|
151
|
+
email_verification_required: verificationRequired,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
100
156
|
router.post('/login', loginLimit, async (req, res) => {
|
|
101
157
|
const result = await authService(req).login({ email: req.body?.email, password: req.body?.password, ip: req.ip ?? null, userAgent: req.get('user-agent') ?? null });
|
|
102
158
|
res.json({ data: result });
|
|
@@ -210,12 +266,35 @@ export function createAuthRouter({
|
|
|
210
266
|
res.status(204).end();
|
|
211
267
|
});
|
|
212
268
|
router.post('/email-verification/request', actionLimit, async (req, res) => {
|
|
213
|
-
const transport = requireMailer(mailer);
|
|
214
269
|
if (!req.accountability?.user) {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
270
|
+
const settings = await studioSettingsService(req).readPublic();
|
|
271
|
+
if (settings.public_registration_require_email_verification !== true) {
|
|
272
|
+
res.status(202).json({ data: { accepted: true } });
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const transport = requireMailer(mailer);
|
|
277
|
+
const result = await systemAuthTokensService(req).requestEmailVerification(req.body?.email);
|
|
278
|
+
if (result) {
|
|
279
|
+
try {
|
|
280
|
+
const message = verificationMessage(config, result.token);
|
|
281
|
+
await transport.send({ to: result.user.email, ...message }, {
|
|
282
|
+
accountability: req.accountability,
|
|
283
|
+
requestId: req.id,
|
|
284
|
+
});
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.error?.('YunCMS public email verification mail delivery failed', {
|
|
287
|
+
requestId: req.id,
|
|
288
|
+
code: error?.code,
|
|
289
|
+
message: error?.message,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
res.status(202).json({ data: { accepted: true } });
|
|
294
|
+
return;
|
|
218
295
|
}
|
|
296
|
+
|
|
297
|
+
const transport = requireMailer(mailer);
|
|
219
298
|
const userId = req.body?.user ?? req.accountability.user;
|
|
220
299
|
const user = await usersService(req).readOne(userId);
|
|
221
300
|
if (!user) {
|
|
@@ -224,8 +303,11 @@ export function createAuthRouter({
|
|
|
224
303
|
throw error;
|
|
225
304
|
}
|
|
226
305
|
const result = await authTokensService(req).createEmailVerification(userId);
|
|
227
|
-
const
|
|
228
|
-
await transport.send({ to: user.email,
|
|
306
|
+
const message = verificationMessage(config, result.token);
|
|
307
|
+
await transport.send({ to: user.email, ...message }, {
|
|
308
|
+
accountability: req.accountability,
|
|
309
|
+
requestId: req.id,
|
|
310
|
+
});
|
|
229
311
|
res.status(202).json({ data: { accepted: true } });
|
|
230
312
|
});
|
|
231
313
|
router.post('/email-verification/confirm', actionLimit, async (req, res) => {
|
|
@@ -26,6 +26,10 @@ export function createStudioSettingsRouter() {
|
|
|
26
26
|
sendImageAsset(res, await settingsService(req).readFaviconContent());
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
router.get('/admin', async (req, res) => {
|
|
30
|
+
res.json({ data: await settingsService(req).readOne() });
|
|
31
|
+
});
|
|
32
|
+
|
|
29
33
|
router.get('/', async (req, res) => {
|
|
30
34
|
res.json({ data: await settingsService(req).readPublic() });
|
|
31
35
|
});
|