@sonicjs-cms/core 2.16.1 → 2.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-D9L3mrC-.d.cts → app-C9esKLmh.d.cts} +2 -0
- package/dist/{app-D9L3mrC-.d.ts → app-C9esKLmh.d.ts} +2 -0
- package/dist/{chunk-7HHIZQNE.cjs → chunk-3TVMUQWG.cjs} +155 -17
- package/dist/chunk-3TVMUQWG.cjs.map +1 -0
- package/dist/{chunk-U6FOL6EO.cjs → chunk-47HKH3D6.cjs} +34 -6
- package/dist/chunk-47HKH3D6.cjs.map +1 -0
- package/dist/{chunk-V76ERLX6.cjs → chunk-5EBTBD2Z.cjs} +3 -3
- package/dist/{chunk-V76ERLX6.cjs.map → chunk-5EBTBD2Z.cjs.map} +1 -1
- package/dist/{chunk-MZS33LLH.cjs → chunk-5ITJB5ZT.cjs} +354 -180
- package/dist/chunk-5ITJB5ZT.cjs.map +1 -0
- package/dist/{chunk-JF5RQXPN.js → chunk-7D7SI5P7.js} +3 -3
- package/dist/{chunk-JF5RQXPN.js.map → chunk-7D7SI5P7.js.map} +1 -1
- package/dist/{chunk-W33MHOPA.js → chunk-EUFBU4T4.js} +34 -6
- package/dist/chunk-EUFBU4T4.js.map +1 -0
- package/dist/{chunk-HU4MN74Q.cjs → chunk-I6444XLU.cjs} +2 -2
- package/dist/{chunk-HU4MN74Q.cjs.map → chunk-I6444XLU.cjs.map} +1 -1
- package/dist/{chunk-KYAF33AF.js → chunk-P5IDHMOL.js} +149 -14
- package/dist/chunk-P5IDHMOL.js.map +1 -0
- package/dist/{chunk-TBJY2FF7.js → chunk-QFWHAFEO.js} +22 -2
- package/dist/chunk-QFWHAFEO.js.map +1 -0
- package/dist/{chunk-PUZMLXOJ.js → chunk-QZBZEUZF.js} +2 -2
- package/dist/{chunk-PUZMLXOJ.js.map → chunk-QZBZEUZF.js.map} +1 -1
- package/dist/{chunk-BAMJVG33.js → chunk-UDUHP4PA.js} +229 -55
- package/dist/chunk-UDUHP4PA.js.map +1 -0
- package/dist/{chunk-NZWFCUDA.cjs → chunk-WAEQXGCX.cjs} +22 -2
- package/dist/chunk-WAEQXGCX.cjs.map +1 -0
- package/dist/index.cjs +194 -186
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +30 -22
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +41 -29
- package/dist/middleware.d.cts +38 -4
- package/dist/middleware.d.ts +38 -4
- package/dist/middleware.js +3 -3
- package/dist/migrations-3TVS3HU5.js +4 -0
- package/dist/{migrations-WCEBO5QQ.js.map → migrations-3TVS3HU5.js.map} +1 -1
- package/dist/migrations-VQ4UX4M4.cjs +13 -0
- package/dist/{migrations-MYQI2KAJ.cjs.map → migrations-VQ4UX4M4.cjs.map} +1 -1
- package/dist/routes.cjs +29 -29
- package/dist/routes.d.cts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +6 -6
- package/dist/services.cjs +39 -39
- package/dist/services.d.cts +12 -0
- package/dist/services.d.ts +12 -0
- package/dist/services.js +3 -3
- package/dist/utils.cjs +11 -11
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-7HHIZQNE.cjs.map +0 -1
- package/dist/chunk-BAMJVG33.js.map +0 -1
- package/dist/chunk-KYAF33AF.js.map +0 -1
- package/dist/chunk-MZS33LLH.cjs.map +0 -1
- package/dist/chunk-NZWFCUDA.cjs.map +0 -1
- package/dist/chunk-TBJY2FF7.js.map +0 -1
- package/dist/chunk-U6FOL6EO.cjs.map +0 -1
- package/dist/chunk-W33MHOPA.js.map +0 -1
- package/dist/migrations-MYQI2KAJ.cjs +0 -13
- package/dist/migrations-WCEBO5QQ.js +0 -4
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { getCacheService, CACHE_CONFIGS, SettingsService, getLogger, getAppInstance, buildRouteList, CATEGORY_INFO } from './chunk-
|
|
2
|
-
import { requireAuth, requireRole, isPluginActive, optionalAuth, rateLimit, AuthManager, logActivity, generateCsrfToken } from './chunk-
|
|
3
|
-
import { PluginService, PLUGIN_REGISTRY, findPluginByCodeName, createContentFromSubmission } from './chunk-
|
|
4
|
-
import { MigrationService } from './chunk-
|
|
1
|
+
import { getCacheService, CACHE_CONFIGS, SettingsService, getLogger, getAppInstance, buildRouteList, CATEGORY_INFO } from './chunk-QFWHAFEO.js';
|
|
2
|
+
import { requireAuth, requireRole, isPluginActive, optionalAuth, rateLimit, AuthManager, getJwtExpirySecondsFromDb, getJwtRefreshGraceSecondsFromDb, logActivity, generateCsrfToken } from './chunk-P5IDHMOL.js';
|
|
3
|
+
import { PluginService, PLUGIN_REGISTRY, findPluginByCodeName, createContentFromSubmission } from './chunk-EUFBU4T4.js';
|
|
4
|
+
import { MigrationService } from './chunk-QZBZEUZF.js';
|
|
5
5
|
import { renderDesignPage, renderCheckboxPage, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-XWIA3HVX.js';
|
|
6
6
|
import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-55RDMDOP.js';
|
|
7
7
|
import { PluginBuilder, TurnstileService } from './chunk-EXNEW5US.js';
|
|
8
|
-
import { QueryFilterBuilder, getCoreVersion, getBlocksFieldConfig, parseBlocksValue } from './chunk-
|
|
8
|
+
import { QueryFilterBuilder, getCoreVersion, getBlocksFieldConfig, parseBlocksValue } from './chunk-7D7SI5P7.js';
|
|
9
9
|
import { metricsTracker } from './chunk-FICTAGD4.js';
|
|
10
10
|
import { escapeHtml, sanitizeRichText, sanitizeInput } from './chunk-TQABQWOP.js';
|
|
11
11
|
import { Hono } from 'hono';
|
|
12
12
|
import { cors } from 'hono/cors';
|
|
13
13
|
import { z } from 'zod';
|
|
14
|
-
import { setCookie } from 'hono/cookie';
|
|
14
|
+
import { setCookie, getCookie } from 'hono/cookie';
|
|
15
15
|
import { html, raw } from 'hono/html';
|
|
16
16
|
|
|
17
17
|
// src/schemas/index.ts
|
|
@@ -2351,7 +2351,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
2351
2351
|
});
|
|
2352
2352
|
adminApiRoutes.get("/migrations/status", async (c) => {
|
|
2353
2353
|
try {
|
|
2354
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
2354
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-3TVS3HU5.js');
|
|
2355
2355
|
const db = c.env.DB;
|
|
2356
2356
|
const migrationService = new MigrationService2(db);
|
|
2357
2357
|
const status = await migrationService.getMigrationStatus();
|
|
@@ -2376,7 +2376,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
2376
2376
|
error: "Unauthorized. Admin access required."
|
|
2377
2377
|
}, 403);
|
|
2378
2378
|
}
|
|
2379
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
2379
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-3TVS3HU5.js');
|
|
2380
2380
|
const db = c.env.DB;
|
|
2381
2381
|
const migrationService = new MigrationService2(db);
|
|
2382
2382
|
const result = await migrationService.runPendingMigrations();
|
|
@@ -2398,7 +2398,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
2398
2398
|
});
|
|
2399
2399
|
adminApiRoutes.get("/migrations/validate", async (c) => {
|
|
2400
2400
|
try {
|
|
2401
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
2401
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-3TVS3HU5.js');
|
|
2402
2402
|
const db = c.env.DB;
|
|
2403
2403
|
const migrationService = new MigrationService2(db);
|
|
2404
2404
|
const validation = await migrationService.validateSchema();
|
|
@@ -5148,16 +5148,17 @@ var userProfilesPlugin = createUserProfilesPlugin();
|
|
|
5148
5148
|
|
|
5149
5149
|
// src/routes/auth.ts
|
|
5150
5150
|
var JWT_SECRET_FALLBACK = "your-super-secret-jwt-key-change-in-production";
|
|
5151
|
-
async function setCsrfCookie(c) {
|
|
5151
|
+
async function setCsrfCookie(c, maxAge) {
|
|
5152
5152
|
const secret = c.env?.JWT_SECRET || JWT_SECRET_FALLBACK;
|
|
5153
5153
|
const isDev = c.env?.ENVIRONMENT === "development" || !c.env?.ENVIRONMENT;
|
|
5154
5154
|
const csrfToken = await generateCsrfToken(secret);
|
|
5155
|
+
const cookieMaxAge = await getJwtExpirySecondsFromDb(c.env?.DB, c.env);
|
|
5155
5156
|
setCookie(c, "csrf_token", csrfToken, {
|
|
5156
5157
|
httpOnly: false,
|
|
5157
5158
|
secure: !isDev,
|
|
5158
5159
|
sameSite: "Strict",
|
|
5159
5160
|
path: "/",
|
|
5160
|
-
maxAge:
|
|
5161
|
+
maxAge: cookieMaxAge
|
|
5161
5162
|
});
|
|
5162
5163
|
}
|
|
5163
5164
|
function clearCsrfCookie(c) {
|
|
@@ -5279,13 +5280,13 @@ authRoutes.post(
|
|
|
5279
5280
|
await saveCustomData(db, userId, sanitized);
|
|
5280
5281
|
}
|
|
5281
5282
|
}
|
|
5282
|
-
const
|
|
5283
|
+
const tokenTtl = await getJwtExpirySecondsFromDb(c.env.DB, c.env);
|
|
5284
|
+
const token = await AuthManager.generateToken(userId, normalizedEmail, "viewer", c.env.JWT_SECRET, tokenTtl);
|
|
5283
5285
|
setCookie(c, "auth_token", token, {
|
|
5284
5286
|
httpOnly: true,
|
|
5285
5287
|
secure: true,
|
|
5286
5288
|
sameSite: "Strict",
|
|
5287
|
-
maxAge:
|
|
5288
|
-
// 24 hours
|
|
5289
|
+
maxAge: tokenTtl
|
|
5289
5290
|
});
|
|
5290
5291
|
await setCsrfCookie(c);
|
|
5291
5292
|
return c.json({
|
|
@@ -5348,13 +5349,13 @@ authRoutes.post(
|
|
|
5348
5349
|
console.error("Password rehash failed (non-fatal):", rehashError);
|
|
5349
5350
|
}
|
|
5350
5351
|
}
|
|
5351
|
-
const
|
|
5352
|
+
const tokenTtl = await getJwtExpirySecondsFromDb(c.env.DB, c.env);
|
|
5353
|
+
const token = await AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET, tokenTtl);
|
|
5352
5354
|
setCookie(c, "auth_token", token, {
|
|
5353
5355
|
httpOnly: true,
|
|
5354
5356
|
secure: true,
|
|
5355
5357
|
sameSite: "Strict",
|
|
5356
|
-
maxAge:
|
|
5357
|
-
// 24 hours
|
|
5358
|
+
maxAge: tokenTtl
|
|
5358
5359
|
});
|
|
5359
5360
|
await setCsrfCookie(c);
|
|
5360
5361
|
await db.prepare("UPDATE users SET last_login_at = ? WHERE id = ?").bind((/* @__PURE__ */ new Date()).getTime(), user.id).run();
|
|
@@ -5418,27 +5419,45 @@ authRoutes.get("/me", requireAuth(), async (c) => {
|
|
|
5418
5419
|
return c.json({ error: "Failed to get user" }, 500);
|
|
5419
5420
|
}
|
|
5420
5421
|
});
|
|
5421
|
-
authRoutes.post(
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5422
|
+
authRoutes.post(
|
|
5423
|
+
"/refresh",
|
|
5424
|
+
rateLimit({ max: 60, windowMs: 60 * 1e3, keyPrefix: "refresh" }),
|
|
5425
|
+
async (c) => {
|
|
5426
|
+
try {
|
|
5427
|
+
let token = c.req.header("Authorization")?.replace("Bearer ", "");
|
|
5428
|
+
if (!token) token = getCookie(c, "auth_token");
|
|
5429
|
+
if (!token) {
|
|
5430
|
+
return c.json({ error: "Authentication required" }, 401);
|
|
5431
|
+
}
|
|
5432
|
+
const db = c.env.DB;
|
|
5433
|
+
const grace = await getJwtRefreshGraceSecondsFromDb(db, c.env);
|
|
5434
|
+
const payload = await AuthManager.verifyToken(token, c.env.JWT_SECRET, grace);
|
|
5435
|
+
if (!payload) {
|
|
5436
|
+
return c.json({ error: "Invalid or expired token" }, 401);
|
|
5437
|
+
}
|
|
5438
|
+
const row = await db.prepare("SELECT id, email, role, is_active FROM users WHERE id = ?").bind(payload.userId).first();
|
|
5439
|
+
if (!row || !row.is_active) {
|
|
5440
|
+
return c.json({ error: "User is not active" }, 401);
|
|
5441
|
+
}
|
|
5442
|
+
const tokenTtl = await getJwtExpirySecondsFromDb(db, c.env);
|
|
5443
|
+
const newToken = await AuthManager.generateToken(row.id, row.email, row.role, c.env.JWT_SECRET, tokenTtl);
|
|
5444
|
+
setCookie(c, "auth_token", newToken, {
|
|
5445
|
+
httpOnly: true,
|
|
5446
|
+
secure: true,
|
|
5447
|
+
sameSite: "Strict",
|
|
5448
|
+
maxAge: tokenTtl
|
|
5449
|
+
});
|
|
5450
|
+
await setCsrfCookie(c);
|
|
5451
|
+
return c.json({
|
|
5452
|
+
token: newToken,
|
|
5453
|
+
expiresIn: tokenTtl
|
|
5454
|
+
});
|
|
5455
|
+
} catch (error) {
|
|
5456
|
+
console.error("Token refresh error:", error);
|
|
5457
|
+
return c.json({ error: "Token refresh failed" }, 500);
|
|
5426
5458
|
}
|
|
5427
|
-
const token = await AuthManager.generateToken(user.userId, user.email, user.role, c.env.JWT_SECRET);
|
|
5428
|
-
setCookie(c, "auth_token", token, {
|
|
5429
|
-
httpOnly: true,
|
|
5430
|
-
secure: true,
|
|
5431
|
-
sameSite: "Strict",
|
|
5432
|
-
maxAge: 60 * 60 * 24
|
|
5433
|
-
// 24 hours
|
|
5434
|
-
});
|
|
5435
|
-
await setCsrfCookie(c);
|
|
5436
|
-
return c.json({ token });
|
|
5437
|
-
} catch (error) {
|
|
5438
|
-
console.error("Token refresh error:", error);
|
|
5439
|
-
return c.json({ error: "Token refresh failed" }, 500);
|
|
5440
5459
|
}
|
|
5441
|
-
|
|
5460
|
+
);
|
|
5442
5461
|
authRoutes.post(
|
|
5443
5462
|
"/register/form",
|
|
5444
5463
|
rateLimit({ max: 30, windowMs: 60 * 1e3, keyPrefix: "register" }),
|
|
@@ -5523,14 +5542,14 @@ authRoutes.post(
|
|
|
5523
5542
|
await saveCustomData(db, userId, sanitized);
|
|
5524
5543
|
}
|
|
5525
5544
|
}
|
|
5526
|
-
const
|
|
5545
|
+
const tokenTtl = await getJwtExpirySecondsFromDb(c.env.DB, c.env);
|
|
5546
|
+
const token = await AuthManager.generateToken(userId, normalizedEmail, role, c.env.JWT_SECRET, tokenTtl);
|
|
5527
5547
|
setCookie(c, "auth_token", token, {
|
|
5528
5548
|
httpOnly: true,
|
|
5529
5549
|
secure: false,
|
|
5530
5550
|
// Set to true in production with HTTPS
|
|
5531
5551
|
sameSite: "Strict",
|
|
5532
|
-
maxAge:
|
|
5533
|
-
// 24 hours
|
|
5552
|
+
maxAge: tokenTtl
|
|
5534
5553
|
});
|
|
5535
5554
|
await setCsrfCookie(c);
|
|
5536
5555
|
const redirectUrl = role === "admin" ? "/admin/dashboard" : "/admin/dashboard";
|
|
@@ -5596,14 +5615,14 @@ authRoutes.post(
|
|
|
5596
5615
|
console.error("Password rehash failed (non-fatal):", rehashError);
|
|
5597
5616
|
}
|
|
5598
5617
|
}
|
|
5599
|
-
const
|
|
5618
|
+
const tokenTtl = await getJwtExpirySecondsFromDb(c.env.DB, c.env);
|
|
5619
|
+
const token = await AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET, tokenTtl);
|
|
5600
5620
|
setCookie(c, "auth_token", token, {
|
|
5601
5621
|
httpOnly: true,
|
|
5602
5622
|
secure: false,
|
|
5603
5623
|
// Set to true in production with HTTPS
|
|
5604
5624
|
sameSite: "Strict",
|
|
5605
|
-
maxAge:
|
|
5606
|
-
// 24 hours
|
|
5625
|
+
maxAge: tokenTtl
|
|
5607
5626
|
});
|
|
5608
5627
|
await setCsrfCookie(c);
|
|
5609
5628
|
await db.prepare("UPDATE users SET last_login_at = ? WHERE id = ?").bind((/* @__PURE__ */ new Date()).getTime(), user.id).run();
|
|
@@ -5912,13 +5931,13 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
5912
5931
|
Date.now(),
|
|
5913
5932
|
invitedUser.id
|
|
5914
5933
|
).run();
|
|
5915
|
-
const
|
|
5934
|
+
const tokenTtl = await getJwtExpirySecondsFromDb(c.env.DB, c.env);
|
|
5935
|
+
const authToken = await AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role, c.env.JWT_SECRET, tokenTtl);
|
|
5916
5936
|
setCookie(c, "auth_token", authToken, {
|
|
5917
5937
|
httpOnly: true,
|
|
5918
5938
|
secure: true,
|
|
5919
5939
|
sameSite: "Strict",
|
|
5920
|
-
maxAge:
|
|
5921
|
-
// 24 hours
|
|
5940
|
+
maxAge: tokenTtl
|
|
5922
5941
|
});
|
|
5923
5942
|
await setCsrfCookie(c);
|
|
5924
5943
|
return c.redirect("/admin/dashboard?welcome=true");
|
|
@@ -11627,7 +11646,6 @@ function renderUserEditPage(data) {
|
|
|
11627
11646
|
type="text"
|
|
11628
11647
|
name="first_name"
|
|
11629
11648
|
value="${escapeHtml(data.userToEdit.firstName || "")}"
|
|
11630
|
-
required
|
|
11631
11649
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
11632
11650
|
/>
|
|
11633
11651
|
</div>
|
|
@@ -11638,7 +11656,6 @@ function renderUserEditPage(data) {
|
|
|
11638
11656
|
type="text"
|
|
11639
11657
|
name="last_name"
|
|
11640
11658
|
value="${escapeHtml(data.userToEdit.lastName || "")}"
|
|
11641
|
-
required
|
|
11642
11659
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
11643
11660
|
/>
|
|
11644
11661
|
</div>
|
|
@@ -13479,10 +13496,10 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
13479
13496
|
const merged = { ...existingCustom, ...sanitized };
|
|
13480
13497
|
customDataJson = JSON.stringify(merged);
|
|
13481
13498
|
}
|
|
13482
|
-
if (!
|
|
13499
|
+
if (!username || !email) {
|
|
13483
13500
|
return c.html(renderAlert2({
|
|
13484
13501
|
type: "error",
|
|
13485
|
-
message: "
|
|
13502
|
+
message: "Username and email are required.",
|
|
13486
13503
|
dismissible: true
|
|
13487
13504
|
}));
|
|
13488
13505
|
}
|
|
@@ -24239,6 +24256,43 @@ function renderSettingsPage(data) {
|
|
|
24239
24256
|
}
|
|
24240
24257
|
}
|
|
24241
24258
|
|
|
24259
|
+
async function saveSecuritySettings() {
|
|
24260
|
+
const formData = new FormData();
|
|
24261
|
+
const expiry = document.getElementById('jwtExpiresIn');
|
|
24262
|
+
const grace = document.getElementById('jwtRefreshGraceSeconds');
|
|
24263
|
+
if (expiry) formData.append('jwtExpiresIn', expiry.value);
|
|
24264
|
+
if (grace) formData.append('jwtRefreshGraceSeconds', grace.value);
|
|
24265
|
+
|
|
24266
|
+
const saveBtn = document.querySelector('button[onclick="saveSecuritySettings()"]');
|
|
24267
|
+
const originalText = saveBtn ? saveBtn.innerHTML : '';
|
|
24268
|
+
if (saveBtn) {
|
|
24269
|
+
saveBtn.innerHTML = '<svg class="animate-spin -ml-0.5 mr-1.5 h-5 w-5 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg>Saving...';
|
|
24270
|
+
saveBtn.disabled = true;
|
|
24271
|
+
}
|
|
24272
|
+
|
|
24273
|
+
try {
|
|
24274
|
+
const response = await fetch('/admin/settings/security', {
|
|
24275
|
+
method: 'POST',
|
|
24276
|
+
body: formData
|
|
24277
|
+
});
|
|
24278
|
+
const result = await response.json();
|
|
24279
|
+
if (result.success) {
|
|
24280
|
+
showNotification(result.message || 'Security settings saved successfully!', 'success');
|
|
24281
|
+
} else {
|
|
24282
|
+
showNotification(result.error || 'Failed to save security settings', 'error');
|
|
24283
|
+
}
|
|
24284
|
+
} catch (error) {
|
|
24285
|
+
console.error('Error saving security settings:', error);
|
|
24286
|
+
showNotification('Failed to save security settings. Please try again.', 'error');
|
|
24287
|
+
} finally {
|
|
24288
|
+
if (saveBtn) {
|
|
24289
|
+
saveBtn.innerHTML = originalText;
|
|
24290
|
+
saveBtn.disabled = false;
|
|
24291
|
+
}
|
|
24292
|
+
}
|
|
24293
|
+
}
|
|
24294
|
+
window.saveSecuritySettings = saveSecuritySettings;
|
|
24295
|
+
|
|
24242
24296
|
// Migration functions
|
|
24243
24297
|
window.refreshMigrationStatus = async function() {
|
|
24244
24298
|
try {
|
|
@@ -24821,9 +24875,71 @@ function renderAppearanceSettings(settings) {
|
|
|
24821
24875
|
`;
|
|
24822
24876
|
}
|
|
24823
24877
|
function renderSecuritySettings(settings) {
|
|
24878
|
+
const jwtExpiresIn = settings?.jwtExpiresIn ?? "30d";
|
|
24879
|
+
const jwtRefreshGraceSeconds = typeof settings?.jwtRefreshGraceSeconds === "number" ? settings.jwtRefreshGraceSeconds : 60 * 60 * 24 * 7;
|
|
24824
24880
|
return `
|
|
24825
24881
|
<div class="space-y-6">
|
|
24826
|
-
<!--
|
|
24882
|
+
<!-- Session / JWT card (live) -->
|
|
24883
|
+
<div class="rounded-lg bg-white dark:bg-white/5 p-6 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/10">
|
|
24884
|
+
<h3 class="text-lg/7 font-semibold text-zinc-950 dark:text-white">Session / JWT</h3>
|
|
24885
|
+
<p class="mt-1 text-sm/6 text-zinc-500 dark:text-zinc-400">
|
|
24886
|
+
Configure how long a signed-in session lasts and how long an expired token can still be refreshed.
|
|
24887
|
+
The <code class="text-xs">JWT_EXPIRES_IN</code> and <code class="text-xs">JWT_REFRESH_GRACE_SECONDS</code>
|
|
24888
|
+
environment variables, when set, override the values below.
|
|
24889
|
+
</p>
|
|
24890
|
+
|
|
24891
|
+
<div class="mt-6 grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
24892
|
+
<div>
|
|
24893
|
+
<label for="jwtExpiresIn" class="block text-sm/6 font-medium text-zinc-950 dark:text-white mb-2">
|
|
24894
|
+
JWT Expiration
|
|
24895
|
+
</label>
|
|
24896
|
+
<input
|
|
24897
|
+
type="text"
|
|
24898
|
+
id="jwtExpiresIn"
|
|
24899
|
+
name="jwtExpiresIn"
|
|
24900
|
+
value="${jwtExpiresIn}"
|
|
24901
|
+
placeholder="30d"
|
|
24902
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm/6 text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
24903
|
+
/>
|
|
24904
|
+
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
|
|
24905
|
+
Accepts <code>30d</code>, <code>12h</code>, <code>3600s</code>, or bare seconds. Default: 30 days.
|
|
24906
|
+
</p>
|
|
24907
|
+
</div>
|
|
24908
|
+
|
|
24909
|
+
<div>
|
|
24910
|
+
<label for="jwtRefreshGraceSeconds" class="block text-sm/6 font-medium text-zinc-950 dark:text-white mb-2">
|
|
24911
|
+
Refresh Grace Window (seconds)
|
|
24912
|
+
</label>
|
|
24913
|
+
<input
|
|
24914
|
+
type="number"
|
|
24915
|
+
id="jwtRefreshGraceSeconds"
|
|
24916
|
+
name="jwtRefreshGraceSeconds"
|
|
24917
|
+
value="${jwtRefreshGraceSeconds}"
|
|
24918
|
+
min="0"
|
|
24919
|
+
max="7776000"
|
|
24920
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm/6 text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-500 dark:placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 dark:focus:ring-indigo-400"
|
|
24921
|
+
/>
|
|
24922
|
+
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
|
|
24923
|
+
How long an expired token can still be exchanged at <code>/auth/refresh</code>. Default: 604800 (7 days).
|
|
24924
|
+
</p>
|
|
24925
|
+
</div>
|
|
24926
|
+
</div>
|
|
24927
|
+
|
|
24928
|
+
<div class="mt-6 pt-4 border-t border-zinc-950/5 dark:border-white/10 flex justify-end">
|
|
24929
|
+
<button
|
|
24930
|
+
type="button"
|
|
24931
|
+
onclick="saveSecuritySettings()"
|
|
24932
|
+
class="inline-flex items-center justify-center rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm"
|
|
24933
|
+
>
|
|
24934
|
+
<svg class="-ml-0.5 mr-1.5 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
24935
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
|
24936
|
+
</svg>
|
|
24937
|
+
Save Session Settings
|
|
24938
|
+
</button>
|
|
24939
|
+
</div>
|
|
24940
|
+
</div>
|
|
24941
|
+
|
|
24942
|
+
<!-- WIP Notice for remaining fields -->
|
|
24827
24943
|
<div class="rounded-lg bg-blue-50 dark:bg-blue-950/20 p-6 ring-1 ring-inset ring-blue-600/20 dark:ring-blue-500/30">
|
|
24828
24944
|
<div class="flex items-start space-x-3">
|
|
24829
24945
|
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -24832,7 +24948,7 @@ function renderSecuritySettings(settings) {
|
|
|
24832
24948
|
<div class="flex-1">
|
|
24833
24949
|
<h4 class="text-base/7 font-semibold text-blue-900 dark:text-blue-300">Work in Progress</h4>
|
|
24834
24950
|
<p class="mt-1 text-sm/6 text-blue-700 dark:text-blue-200">
|
|
24835
|
-
|
|
24951
|
+
The fields below are under development and provided for reference and design feedback only. Changes made here will not be saved.
|
|
24836
24952
|
</p>
|
|
24837
24953
|
</div>
|
|
24838
24954
|
</div>
|
|
@@ -25717,15 +25833,24 @@ adminSettingsRoutes.get("/appearance", (c) => {
|
|
|
25717
25833
|
};
|
|
25718
25834
|
return c.html(renderSettingsPage(pageData));
|
|
25719
25835
|
});
|
|
25720
|
-
adminSettingsRoutes.get("/security", (c) => {
|
|
25836
|
+
adminSettingsRoutes.get("/security", async (c) => {
|
|
25721
25837
|
const user = c.get("user");
|
|
25838
|
+
const db = c.env.DB;
|
|
25839
|
+
const settingsService = new SettingsService(db);
|
|
25840
|
+
const persisted = await settingsService.getSecuritySettings();
|
|
25841
|
+
const mockSettings = getMockSettings(user);
|
|
25842
|
+
mockSettings.security = {
|
|
25843
|
+
...mockSettings.security,
|
|
25844
|
+
jwtExpiresIn: persisted.jwtExpiresIn,
|
|
25845
|
+
jwtRefreshGraceSeconds: persisted.jwtRefreshGraceSeconds
|
|
25846
|
+
};
|
|
25722
25847
|
const pageData = {
|
|
25723
25848
|
user: user ? {
|
|
25724
25849
|
name: user.email,
|
|
25725
25850
|
email: user.email,
|
|
25726
25851
|
role: user.role
|
|
25727
25852
|
} : void 0,
|
|
25728
|
-
settings:
|
|
25853
|
+
settings: mockSettings,
|
|
25729
25854
|
activeTab: "security",
|
|
25730
25855
|
version: c.get("appVersion")
|
|
25731
25856
|
};
|
|
@@ -26036,6 +26161,55 @@ adminSettingsRoutes.post("/general", async (c) => {
|
|
|
26036
26161
|
}, 500);
|
|
26037
26162
|
}
|
|
26038
26163
|
});
|
|
26164
|
+
adminSettingsRoutes.post("/security", async (c) => {
|
|
26165
|
+
try {
|
|
26166
|
+
const user = c.get("user");
|
|
26167
|
+
if (!user || user.role !== "admin") {
|
|
26168
|
+
return c.json({
|
|
26169
|
+
success: false,
|
|
26170
|
+
error: "Unauthorized. Admin access required."
|
|
26171
|
+
}, 403);
|
|
26172
|
+
}
|
|
26173
|
+
const formData = await c.req.formData();
|
|
26174
|
+
const db = c.env.DB;
|
|
26175
|
+
const settingsService = new SettingsService(db);
|
|
26176
|
+
const jwtExpiresInRaw = formData.get("jwtExpiresIn")?.trim() || "";
|
|
26177
|
+
const graceRaw = formData.get("jwtRefreshGraceSeconds")?.trim() || "";
|
|
26178
|
+
if (!/^\d+(?:s|m|h|d)?$/i.test(jwtExpiresInRaw)) {
|
|
26179
|
+
return c.json({
|
|
26180
|
+
success: false,
|
|
26181
|
+
error: "JWT expiration must be a number optionally suffixed with s/m/h/d (e.g. 30d, 12h, 3600)."
|
|
26182
|
+
}, 400);
|
|
26183
|
+
}
|
|
26184
|
+
const graceSeconds = Number.parseInt(graceRaw, 10);
|
|
26185
|
+
if (!Number.isFinite(graceSeconds) || graceSeconds < 0 || graceSeconds > 60 * 60 * 24 * 90) {
|
|
26186
|
+
return c.json({
|
|
26187
|
+
success: false,
|
|
26188
|
+
error: "Refresh grace must be an integer between 0 and 7776000 seconds (90 days)."
|
|
26189
|
+
}, 400);
|
|
26190
|
+
}
|
|
26191
|
+
const success = await settingsService.saveSecuritySettings({
|
|
26192
|
+
jwtExpiresIn: jwtExpiresInRaw,
|
|
26193
|
+
jwtRefreshGraceSeconds: graceSeconds
|
|
26194
|
+
});
|
|
26195
|
+
if (success) {
|
|
26196
|
+
return c.json({
|
|
26197
|
+
success: true,
|
|
26198
|
+
message: "Security settings saved successfully!"
|
|
26199
|
+
});
|
|
26200
|
+
}
|
|
26201
|
+
return c.json({
|
|
26202
|
+
success: false,
|
|
26203
|
+
error: "Failed to save settings"
|
|
26204
|
+
}, 500);
|
|
26205
|
+
} catch (error) {
|
|
26206
|
+
console.error("Error saving security settings:", error);
|
|
26207
|
+
return c.json({
|
|
26208
|
+
success: false,
|
|
26209
|
+
error: "Failed to save settings. Please try again."
|
|
26210
|
+
}, 500);
|
|
26211
|
+
}
|
|
26212
|
+
});
|
|
26039
26213
|
adminSettingsRoutes.post("/", async (c) => {
|
|
26040
26214
|
return c.redirect("/admin/settings/general");
|
|
26041
26215
|
});
|
|
@@ -28960,5 +29134,5 @@ var ROUTES_INFO = {
|
|
|
28960
29134
|
};
|
|
28961
29135
|
|
|
28962
29136
|
export { ROUTES_INFO, adminCheckboxRoutes, adminCollectionsRoutes, adminDesignRoutes, adminFormsRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_code_examples_default, admin_content_default, admin_testimonials_default, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, createUserProfilesPlugin, defineUserProfile, getConfirmationDialogScript2 as getConfirmationDialogScript, getUserProfileConfig, public_forms_default, renderConfirmationDialog2 as renderConfirmationDialog, router, router2, test_cleanup_default, userProfilesPlugin, userRoutes };
|
|
28963
|
-
//# sourceMappingURL=chunk-
|
|
28964
|
-
//# sourceMappingURL=chunk-
|
|
29137
|
+
//# sourceMappingURL=chunk-UDUHP4PA.js.map
|
|
29138
|
+
//# sourceMappingURL=chunk-UDUHP4PA.js.map
|