@sonicjs-cms/core 2.0.3 → 2.0.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/dist/{chunk-LEG4KNFP.cjs → chunk-3JMOWGUU.cjs} +20 -2
- package/dist/chunk-3JMOWGUU.cjs.map +1 -0
- package/dist/{chunk-LH4Z7QID.js → chunk-6FR25MPC.js} +111 -3
- package/dist/chunk-6FR25MPC.js.map +1 -0
- package/dist/{chunk-3NVJ6W27.cjs → chunk-DOR2IU73.cjs} +111 -2
- package/dist/chunk-DOR2IU73.cjs.map +1 -0
- package/dist/{chunk-M6FPVS7E.js → chunk-G5KY3WJV.js} +16 -29
- package/dist/chunk-G5KY3WJV.js.map +1 -0
- package/dist/{chunk-CDBVZEWR.js → chunk-HSRPDEQQ.js} +20 -2
- package/dist/chunk-HSRPDEQQ.js.map +1 -0
- package/dist/{chunk-4BJGEGX5.cjs → chunk-IM5SDXOE.cjs} +19 -32
- package/dist/chunk-IM5SDXOE.cjs.map +1 -0
- package/dist/{chunk-PPUKPNTP.js → chunk-LGC3TNCY.js} +293 -101
- package/dist/chunk-LGC3TNCY.js.map +1 -0
- package/dist/{chunk-5B3VMVEX.cjs → chunk-NPWWR6RI.cjs} +400 -208
- package/dist/chunk-NPWWR6RI.cjs.map +1 -0
- package/dist/{chunk-UL32L2KV.cjs → chunk-TRSHFTF6.cjs} +123 -3
- package/dist/chunk-TRSHFTF6.cjs.map +1 -0
- package/dist/{chunk-XJETEIRU.js → chunk-VSLEA22M.js} +123 -4
- package/dist/chunk-VSLEA22M.js.map +1 -0
- package/dist/index.cjs +876 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +759 -13
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +23 -23
- package/dist/middleware.js +2 -2
- package/dist/routes.cjs +25 -25
- package/dist/routes.js +5 -5
- package/dist/services.cjs +25 -21
- package/dist/services.js +2 -2
- package/dist/utils.cjs +11 -11
- package/dist/utils.js +1 -1
- package/migrations/006_plugin_system.sql +2 -2
- package/migrations/011_config_managed_collections.sql +1 -0
- package/migrations/018_settings_table.sql +23 -0
- package/package.json +1 -1
- package/dist/chunk-3NVJ6W27.cjs.map +0 -1
- package/dist/chunk-4BJGEGX5.cjs.map +0 -1
- package/dist/chunk-5B3VMVEX.cjs.map +0 -1
- package/dist/chunk-CDBVZEWR.js.map +0 -1
- package/dist/chunk-LEG4KNFP.cjs.map +0 -1
- package/dist/chunk-LH4Z7QID.js.map +0 -1
- package/dist/chunk-M6FPVS7E.js.map +0 -1
- package/dist/chunk-PPUKPNTP.js.map +0 -1
- package/dist/chunk-UL32L2KV.cjs.map +0 -1
- package/dist/chunk-XJETEIRU.js.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var chunkDOR2IU73_cjs = require('./chunk-DOR2IU73.cjs');
|
|
4
|
+
var chunkIM5SDXOE_cjs = require('./chunk-IM5SDXOE.cjs');
|
|
5
|
+
var chunk3JMOWGUU_cjs = require('./chunk-3JMOWGUU.cjs');
|
|
6
6
|
var chunk3SPQ3J4N_cjs = require('./chunk-3SPQ3J4N.cjs');
|
|
7
|
-
var
|
|
7
|
+
var chunkTRSHFTF6_cjs = require('./chunk-TRSHFTF6.cjs');
|
|
8
8
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
9
9
|
var hono = require('hono');
|
|
10
10
|
var cors = require('hono/cors');
|
|
@@ -43,7 +43,7 @@ apiContentCrudRoutes.get("/:id", async (c) => {
|
|
|
43
43
|
}, 500);
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
|
-
apiContentCrudRoutes.post("/",
|
|
46
|
+
apiContentCrudRoutes.post("/", chunkIM5SDXOE_cjs.requireAuth(), async (c) => {
|
|
47
47
|
try {
|
|
48
48
|
const db = c.env.DB;
|
|
49
49
|
const user = c.get("user");
|
|
@@ -80,11 +80,11 @@ apiContentCrudRoutes.post("/", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
|
|
|
80
80
|
title,
|
|
81
81
|
JSON.stringify(data || {}),
|
|
82
82
|
status || "draft",
|
|
83
|
-
user?.userId || "
|
|
83
|
+
user?.userId || "system",
|
|
84
84
|
now,
|
|
85
85
|
now
|
|
86
86
|
).run();
|
|
87
|
-
const cache =
|
|
87
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.api);
|
|
88
88
|
await cache.invalidate(`content:list:${collectionId}:*`);
|
|
89
89
|
await cache.invalidate("content-filtered:*");
|
|
90
90
|
const getStmt = db.prepare("SELECT * FROM content WHERE id = ?");
|
|
@@ -109,7 +109,7 @@ apiContentCrudRoutes.post("/", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
|
|
|
109
109
|
}, 500);
|
|
110
110
|
}
|
|
111
111
|
});
|
|
112
|
-
apiContentCrudRoutes.put("/:id",
|
|
112
|
+
apiContentCrudRoutes.put("/:id", chunkIM5SDXOE_cjs.requireAuth(), async (c) => {
|
|
113
113
|
try {
|
|
114
114
|
const id = c.req.param("id");
|
|
115
115
|
const db = c.env.DB;
|
|
@@ -147,7 +147,7 @@ apiContentCrudRoutes.put("/:id", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
|
|
|
147
147
|
WHERE id = ?
|
|
148
148
|
`);
|
|
149
149
|
await updateStmt.bind(...params).run();
|
|
150
|
-
const cache =
|
|
150
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.api);
|
|
151
151
|
await cache.delete(cache.generateKey("content", id));
|
|
152
152
|
await cache.invalidate(`content:list:${existing.collection_id}:*`);
|
|
153
153
|
await cache.invalidate("content-filtered:*");
|
|
@@ -173,7 +173,7 @@ apiContentCrudRoutes.put("/:id", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
|
|
|
173
173
|
}, 500);
|
|
174
174
|
}
|
|
175
175
|
});
|
|
176
|
-
apiContentCrudRoutes.delete("/:id",
|
|
176
|
+
apiContentCrudRoutes.delete("/:id", chunkIM5SDXOE_cjs.requireAuth(), async (c) => {
|
|
177
177
|
try {
|
|
178
178
|
const id = c.req.param("id");
|
|
179
179
|
const db = c.env.DB;
|
|
@@ -184,7 +184,7 @@ apiContentCrudRoutes.delete("/:id", chunk4BJGEGX5_cjs.requireAuth(), async (c) =
|
|
|
184
184
|
}
|
|
185
185
|
const deleteStmt = db.prepare("DELETE FROM content WHERE id = ?");
|
|
186
186
|
await deleteStmt.bind(id).run();
|
|
187
|
-
const cache =
|
|
187
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.api);
|
|
188
188
|
await cache.delete(cache.generateKey("content", id));
|
|
189
189
|
await cache.invalidate(`content:list:${existing.collection_id}:*`);
|
|
190
190
|
await cache.invalidate("content-filtered:*");
|
|
@@ -209,7 +209,7 @@ apiRoutes.use("*", async (c, next) => {
|
|
|
209
209
|
c.header("X-Response-Time", `${totalTime}ms`);
|
|
210
210
|
});
|
|
211
211
|
apiRoutes.use("*", async (c, next) => {
|
|
212
|
-
const cacheEnabled = await
|
|
212
|
+
const cacheEnabled = await chunkIM5SDXOE_cjs.isPluginActive(c.env.DB, "core-cache");
|
|
213
213
|
c.set("cacheEnabled", cacheEnabled);
|
|
214
214
|
await next();
|
|
215
215
|
});
|
|
@@ -257,7 +257,7 @@ apiRoutes.get("/collections", async (c) => {
|
|
|
257
257
|
try {
|
|
258
258
|
const db = c.env.DB;
|
|
259
259
|
const cacheEnabled = c.get("cacheEnabled");
|
|
260
|
-
const cache =
|
|
260
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.api);
|
|
261
261
|
const cacheKey = cache.generateKey("collections", "all");
|
|
262
262
|
if (cacheEnabled) {
|
|
263
263
|
const cacheResult = await cache.getWithSource(cacheKey);
|
|
@@ -334,12 +334,12 @@ apiRoutes.get("/content", async (c) => {
|
|
|
334
334
|
});
|
|
335
335
|
}
|
|
336
336
|
}
|
|
337
|
-
const filter =
|
|
337
|
+
const filter = chunkTRSHFTF6_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
338
338
|
if (!filter.limit) {
|
|
339
339
|
filter.limit = 50;
|
|
340
340
|
}
|
|
341
341
|
filter.limit = Math.min(filter.limit, 1e3);
|
|
342
|
-
const builder = new
|
|
342
|
+
const builder = new chunkTRSHFTF6_cjs.QueryFilterBuilder();
|
|
343
343
|
const queryResult = builder.build("content", filter);
|
|
344
344
|
if (queryResult.errors.length > 0) {
|
|
345
345
|
return c.json({
|
|
@@ -348,7 +348,7 @@ apiRoutes.get("/content", async (c) => {
|
|
|
348
348
|
}, 400);
|
|
349
349
|
}
|
|
350
350
|
const cacheEnabled = c.get("cacheEnabled");
|
|
351
|
-
const cache =
|
|
351
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.api);
|
|
352
352
|
const cacheKey = cache.generateKey("content-filtered", JSON.stringify({ filter, query: queryResult.sql }));
|
|
353
353
|
if (cacheEnabled) {
|
|
354
354
|
const cacheResult = await cache.getWithSource(cacheKey);
|
|
@@ -426,7 +426,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
426
426
|
if (!collectionResult) {
|
|
427
427
|
return c.json({ error: "Collection not found" }, 404);
|
|
428
428
|
}
|
|
429
|
-
const filter =
|
|
429
|
+
const filter = chunkTRSHFTF6_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
430
430
|
if (!filter.where) {
|
|
431
431
|
filter.where = { and: [] };
|
|
432
432
|
}
|
|
@@ -442,7 +442,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
442
442
|
filter.limit = 50;
|
|
443
443
|
}
|
|
444
444
|
filter.limit = Math.min(filter.limit, 1e3);
|
|
445
|
-
const builder = new
|
|
445
|
+
const builder = new chunkTRSHFTF6_cjs.QueryFilterBuilder();
|
|
446
446
|
const queryResult = builder.build("content", filter);
|
|
447
447
|
if (queryResult.errors.length > 0) {
|
|
448
448
|
return c.json({
|
|
@@ -451,7 +451,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
451
451
|
}, 400);
|
|
452
452
|
}
|
|
453
453
|
const cacheEnabled = c.get("cacheEnabled");
|
|
454
|
-
const cache =
|
|
454
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.api);
|
|
455
455
|
const cacheKey = cache.generateKey("collection-content-filtered", `${collection}:${JSON.stringify({ filter, query: queryResult.sql })}`);
|
|
456
456
|
if (cacheEnabled) {
|
|
457
457
|
const cacheResult = await cache.getWithSource(cacheKey);
|
|
@@ -567,7 +567,7 @@ var fileValidationSchema = zod.z.object({
|
|
|
567
567
|
// 50MB max
|
|
568
568
|
});
|
|
569
569
|
var apiMediaRoutes = new hono.Hono();
|
|
570
|
-
apiMediaRoutes.use("*",
|
|
570
|
+
apiMediaRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
571
571
|
apiMediaRoutes.post("/upload", async (c) => {
|
|
572
572
|
try {
|
|
573
573
|
const user = c.get("user");
|
|
@@ -673,6 +673,7 @@ apiMediaRoutes.post("/upload", async (c) => {
|
|
|
673
673
|
size: mediaRecord.size,
|
|
674
674
|
width: mediaRecord.width,
|
|
675
675
|
height: mediaRecord.height,
|
|
676
|
+
r2_key: mediaRecord.r2_key,
|
|
676
677
|
publicUrl: mediaRecord.public_url,
|
|
677
678
|
thumbnailUrl: mediaRecord.thumbnail_url,
|
|
678
679
|
uploadedAt: new Date(mediaRecord.uploaded_at * 1e3).toISOString()
|
|
@@ -799,6 +800,7 @@ apiMediaRoutes.post("/upload-multiple", async (c) => {
|
|
|
799
800
|
size: mediaRecord.size,
|
|
800
801
|
width: mediaRecord.width,
|
|
801
802
|
height: mediaRecord.height,
|
|
803
|
+
r2_key: mediaRecord.r2_key,
|
|
802
804
|
publicUrl: mediaRecord.public_url,
|
|
803
805
|
thumbnailUrl: mediaRecord.thumbnail_url,
|
|
804
806
|
uploadedAt: new Date(mediaRecord.uploaded_at * 1e3).toISOString()
|
|
@@ -918,10 +920,17 @@ apiMediaRoutes.post("/create-folder", async (c) => {
|
|
|
918
920
|
}
|
|
919
921
|
const checkStmt = c.env.DB.prepare("SELECT COUNT(*) as count FROM media WHERE folder = ? AND deleted_at IS NULL");
|
|
920
922
|
const existingFolder = await checkStmt.bind(folderName).first();
|
|
923
|
+
if (existingFolder && existingFolder.count > 0) {
|
|
924
|
+
return c.json({
|
|
925
|
+
success: false,
|
|
926
|
+
error: `Folder "${folderName}" already exists`
|
|
927
|
+
}, 400);
|
|
928
|
+
}
|
|
921
929
|
return c.json({
|
|
922
930
|
success: true,
|
|
923
|
-
message: `Folder "${folderName}"
|
|
924
|
-
folder: folderName
|
|
931
|
+
message: `Folder "${folderName}" is ready. Upload files to this folder to make it appear in the media library.`,
|
|
932
|
+
folder: folderName,
|
|
933
|
+
note: "Folders appear automatically when you upload files to them"
|
|
925
934
|
});
|
|
926
935
|
} catch (error) {
|
|
927
936
|
console.error("Create folder error:", error);
|
|
@@ -1302,8 +1311,8 @@ apiSystemRoutes.get("/env", (c) => {
|
|
|
1302
1311
|
});
|
|
1303
1312
|
var api_system_default = apiSystemRoutes;
|
|
1304
1313
|
var adminApiRoutes = new hono.Hono();
|
|
1305
|
-
adminApiRoutes.use("*",
|
|
1306
|
-
adminApiRoutes.use("*",
|
|
1314
|
+
adminApiRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
1315
|
+
adminApiRoutes.use("*", chunkIM5SDXOE_cjs.requireRole(["admin", "editor"]));
|
|
1307
1316
|
adminApiRoutes.get("/stats", async (c) => {
|
|
1308
1317
|
try {
|
|
1309
1318
|
const db = c.env.DB;
|
|
@@ -1435,8 +1444,12 @@ adminApiRoutes.get("/activity", async (c) => {
|
|
|
1435
1444
|
});
|
|
1436
1445
|
var createCollectionSchema = zod.z.object({
|
|
1437
1446
|
name: zod.z.string().min(1).max(255).regex(/^[a-z0-9_]+$/, "Must contain only lowercase letters, numbers, and underscores"),
|
|
1438
|
-
|
|
1447
|
+
displayName: zod.z.string().min(1).max(255).optional(),
|
|
1448
|
+
display_name: zod.z.string().min(1).max(255).optional(),
|
|
1439
1449
|
description: zod.z.string().optional()
|
|
1450
|
+
}).refine((data) => data.displayName || data.display_name, {
|
|
1451
|
+
message: "Either displayName or display_name is required",
|
|
1452
|
+
path: ["displayName"]
|
|
1440
1453
|
});
|
|
1441
1454
|
var updateCollectionSchema = zod.z.object({
|
|
1442
1455
|
display_name: zod.z.string().min(1).max(255).optional(),
|
|
@@ -1523,15 +1536,16 @@ adminApiRoutes.get("/collections/:id", async (c) => {
|
|
|
1523
1536
|
updated_at: Number(row.updated_at)
|
|
1524
1537
|
}));
|
|
1525
1538
|
return c.json({
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1539
|
+
id: collection.id,
|
|
1540
|
+
name: collection.name,
|
|
1541
|
+
display_name: collection.display_name,
|
|
1542
|
+
description: collection.description,
|
|
1543
|
+
is_active: collection.is_active === 1,
|
|
1544
|
+
managed: collection.managed === 1,
|
|
1545
|
+
schema: collection.schema ? JSON.parse(collection.schema) : null,
|
|
1546
|
+
created_at: Number(collection.created_at),
|
|
1547
|
+
updated_at: Number(collection.updated_at),
|
|
1548
|
+
fields
|
|
1535
1549
|
});
|
|
1536
1550
|
} catch (error) {
|
|
1537
1551
|
console.error("Error fetching collection:", error);
|
|
@@ -1540,7 +1554,16 @@ adminApiRoutes.get("/collections/:id", async (c) => {
|
|
|
1540
1554
|
});
|
|
1541
1555
|
adminApiRoutes.post("/collections", async (c) => {
|
|
1542
1556
|
try {
|
|
1543
|
-
const
|
|
1557
|
+
const contentType = c.req.header("Content-Type");
|
|
1558
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
1559
|
+
return c.json({ error: "Content-Type must be application/json" }, 400);
|
|
1560
|
+
}
|
|
1561
|
+
let body;
|
|
1562
|
+
try {
|
|
1563
|
+
body = await c.req.json();
|
|
1564
|
+
} catch (e) {
|
|
1565
|
+
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
1566
|
+
}
|
|
1544
1567
|
const validation = createCollectionSchema.safeParse(body);
|
|
1545
1568
|
if (!validation.success) {
|
|
1546
1569
|
return c.json({ error: "Validation failed", details: validation.error.errors }, 400);
|
|
@@ -1548,6 +1571,7 @@ adminApiRoutes.post("/collections", async (c) => {
|
|
|
1548
1571
|
const validatedData = validation.data;
|
|
1549
1572
|
const db = c.env.DB;
|
|
1550
1573
|
const user = c.get("user");
|
|
1574
|
+
const displayName = validatedData.displayName || validatedData.display_name || "";
|
|
1551
1575
|
const existingStmt = db.prepare("SELECT id FROM collections WHERE name = ?");
|
|
1552
1576
|
const existing = await existingStmt.bind(validatedData.name).first();
|
|
1553
1577
|
if (existing) {
|
|
@@ -1584,7 +1608,7 @@ adminApiRoutes.post("/collections", async (c) => {
|
|
|
1584
1608
|
await insertStmt.bind(
|
|
1585
1609
|
collectionId,
|
|
1586
1610
|
validatedData.name,
|
|
1587
|
-
|
|
1611
|
+
displayName,
|
|
1588
1612
|
validatedData.description || null,
|
|
1589
1613
|
JSON.stringify(basicSchema),
|
|
1590
1614
|
1,
|
|
@@ -1599,13 +1623,11 @@ adminApiRoutes.post("/collections", async (c) => {
|
|
|
1599
1623
|
console.error("Error clearing cache:", e);
|
|
1600
1624
|
}
|
|
1601
1625
|
return c.json({
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
created_at: now
|
|
1608
|
-
}
|
|
1626
|
+
id: collectionId,
|
|
1627
|
+
name: validatedData.name,
|
|
1628
|
+
displayName,
|
|
1629
|
+
description: validatedData.description,
|
|
1630
|
+
created_at: now
|
|
1609
1631
|
}, 201);
|
|
1610
1632
|
} catch (error) {
|
|
1611
1633
|
console.error("Error creating collection:", error);
|
|
@@ -1669,6 +1691,11 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
1669
1691
|
try {
|
|
1670
1692
|
const id = c.req.param("id");
|
|
1671
1693
|
const db = c.env.DB;
|
|
1694
|
+
const collectionStmt = db.prepare("SELECT name FROM collections WHERE id = ?");
|
|
1695
|
+
const collection = await collectionStmt.bind(id).first();
|
|
1696
|
+
if (!collection) {
|
|
1697
|
+
return c.json({ error: "Collection not found" }, 404);
|
|
1698
|
+
}
|
|
1672
1699
|
const contentStmt = db.prepare("SELECT COUNT(*) as count FROM content WHERE collection_id = ?");
|
|
1673
1700
|
const contentResult = await contentStmt.bind(id).first();
|
|
1674
1701
|
if (contentResult && contentResult.count > 0) {
|
|
@@ -1676,17 +1703,13 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
1676
1703
|
error: `Cannot delete collection: it contains ${contentResult.count} content item(s). Delete all content first.`
|
|
1677
1704
|
}, 400);
|
|
1678
1705
|
}
|
|
1679
|
-
const collectionStmt = db.prepare("SELECT name FROM collections WHERE id = ?");
|
|
1680
|
-
const collection = await collectionStmt.bind(id).first();
|
|
1681
1706
|
const deleteFieldsStmt = db.prepare("DELETE FROM content_fields WHERE collection_id = ?");
|
|
1682
1707
|
await deleteFieldsStmt.bind(id).run();
|
|
1683
1708
|
const deleteStmt = db.prepare("DELETE FROM collections WHERE id = ?");
|
|
1684
1709
|
await deleteStmt.bind(id).run();
|
|
1685
1710
|
try {
|
|
1686
1711
|
await c.env.CACHE_KV.delete("cache:collections:all");
|
|
1687
|
-
|
|
1688
|
-
await c.env.CACHE_KV.delete(`cache:collection:${collection.name}`);
|
|
1689
|
-
}
|
|
1712
|
+
await c.env.CACHE_KV.delete(`cache:collection:${collection.name}`);
|
|
1690
1713
|
} catch (e) {
|
|
1691
1714
|
console.error("Error clearing cache:", e);
|
|
1692
1715
|
}
|
|
@@ -2119,7 +2142,7 @@ authRoutes.post(
|
|
|
2119
2142
|
if (existingUser) {
|
|
2120
2143
|
return c.json({ error: "User with this email or username already exists" }, 400);
|
|
2121
2144
|
}
|
|
2122
|
-
const passwordHash = await
|
|
2145
|
+
const passwordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword(password);
|
|
2123
2146
|
const userId = crypto.randomUUID();
|
|
2124
2147
|
const now = /* @__PURE__ */ new Date();
|
|
2125
2148
|
await db.prepare(`
|
|
@@ -2139,7 +2162,7 @@ authRoutes.post(
|
|
|
2139
2162
|
now.getTime(),
|
|
2140
2163
|
now.getTime()
|
|
2141
2164
|
).run();
|
|
2142
|
-
const token = await
|
|
2165
|
+
const token = await chunkIM5SDXOE_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer");
|
|
2143
2166
|
cookie.setCookie(c, "auth_token", token, {
|
|
2144
2167
|
httpOnly: true,
|
|
2145
2168
|
secure: true,
|
|
@@ -2174,7 +2197,7 @@ authRoutes.post("/login", async (c) => {
|
|
|
2174
2197
|
const { email, password } = validation.data;
|
|
2175
2198
|
const db = c.env.DB;
|
|
2176
2199
|
const normalizedEmail = email.toLowerCase();
|
|
2177
|
-
const cache =
|
|
2200
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.user);
|
|
2178
2201
|
let user = await cache.get(cache.generateKey("user", `email:${normalizedEmail}`));
|
|
2179
2202
|
if (!user) {
|
|
2180
2203
|
user = await db.prepare("SELECT * FROM users WHERE email = ? AND is_active = 1").bind(normalizedEmail).first();
|
|
@@ -2186,11 +2209,11 @@ authRoutes.post("/login", async (c) => {
|
|
|
2186
2209
|
if (!user) {
|
|
2187
2210
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2188
2211
|
}
|
|
2189
|
-
const isValidPassword = await
|
|
2212
|
+
const isValidPassword = await chunkIM5SDXOE_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2190
2213
|
if (!isValidPassword) {
|
|
2191
2214
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2192
2215
|
}
|
|
2193
|
-
const token = await
|
|
2216
|
+
const token = await chunkIM5SDXOE_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2194
2217
|
cookie.setCookie(c, "auth_token", token, {
|
|
2195
2218
|
httpOnly: true,
|
|
2196
2219
|
secure: true,
|
|
@@ -2239,7 +2262,7 @@ authRoutes.get("/logout", (c) => {
|
|
|
2239
2262
|
});
|
|
2240
2263
|
return c.redirect("/auth/login?message=You have been logged out successfully");
|
|
2241
2264
|
});
|
|
2242
|
-
authRoutes.get("/me",
|
|
2265
|
+
authRoutes.get("/me", chunkIM5SDXOE_cjs.requireAuth(), async (c) => {
|
|
2243
2266
|
try {
|
|
2244
2267
|
const user = c.get("user");
|
|
2245
2268
|
if (!user) {
|
|
@@ -2256,13 +2279,13 @@ authRoutes.get("/me", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
|
|
|
2256
2279
|
return c.json({ error: "Failed to get user" }, 500);
|
|
2257
2280
|
}
|
|
2258
2281
|
});
|
|
2259
|
-
authRoutes.post("/refresh",
|
|
2282
|
+
authRoutes.post("/refresh", chunkIM5SDXOE_cjs.requireAuth(), async (c) => {
|
|
2260
2283
|
try {
|
|
2261
2284
|
const user = c.get("user");
|
|
2262
2285
|
if (!user) {
|
|
2263
2286
|
return c.json({ error: "Not authenticated" }, 401);
|
|
2264
2287
|
}
|
|
2265
|
-
const token = await
|
|
2288
|
+
const token = await chunkIM5SDXOE_cjs.AuthManager.generateToken(user.userId, user.email, user.role);
|
|
2266
2289
|
cookie.setCookie(c, "auth_token", token, {
|
|
2267
2290
|
httpOnly: true,
|
|
2268
2291
|
secure: true,
|
|
@@ -2312,7 +2335,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2312
2335
|
</div>
|
|
2313
2336
|
`);
|
|
2314
2337
|
}
|
|
2315
|
-
const passwordHash = await
|
|
2338
|
+
const passwordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword(password);
|
|
2316
2339
|
const userId = crypto.randomUUID();
|
|
2317
2340
|
const now = /* @__PURE__ */ new Date();
|
|
2318
2341
|
await db.prepare(`
|
|
@@ -2332,7 +2355,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2332
2355
|
now.getTime(),
|
|
2333
2356
|
now.getTime()
|
|
2334
2357
|
).run();
|
|
2335
|
-
const token = await
|
|
2358
|
+
const token = await chunkIM5SDXOE_cjs.AuthManager.generateToken(userId, normalizedEmail, "admin");
|
|
2336
2359
|
cookie.setCookie(c, "auth_token", token, {
|
|
2337
2360
|
httpOnly: true,
|
|
2338
2361
|
secure: false,
|
|
@@ -2346,7 +2369,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2346
2369
|
Account created successfully! Redirecting to admin dashboard...
|
|
2347
2370
|
<script>
|
|
2348
2371
|
setTimeout(() => {
|
|
2349
|
-
window.location.href = '/admin/
|
|
2372
|
+
window.location.href = '/admin/dashboard';
|
|
2350
2373
|
}, 2000);
|
|
2351
2374
|
</script>
|
|
2352
2375
|
</div>
|
|
@@ -2383,7 +2406,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2383
2406
|
</div>
|
|
2384
2407
|
`);
|
|
2385
2408
|
}
|
|
2386
|
-
const isValidPassword = await
|
|
2409
|
+
const isValidPassword = await chunkIM5SDXOE_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2387
2410
|
if (!isValidPassword) {
|
|
2388
2411
|
return c.html(html.html`
|
|
2389
2412
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
@@ -2391,7 +2414,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2391
2414
|
</div>
|
|
2392
2415
|
`);
|
|
2393
2416
|
}
|
|
2394
|
-
const token = await
|
|
2417
|
+
const token = await chunkIM5SDXOE_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2395
2418
|
cookie.setCookie(c, "auth_token", token, {
|
|
2396
2419
|
httpOnly: true,
|
|
2397
2420
|
secure: false,
|
|
@@ -2414,7 +2437,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2414
2437
|
</div>
|
|
2415
2438
|
<script>
|
|
2416
2439
|
setTimeout(() => {
|
|
2417
|
-
window.location.href = '/admin/
|
|
2440
|
+
window.location.href = '/admin/dashboard';
|
|
2418
2441
|
}, 2000);
|
|
2419
2442
|
</script>
|
|
2420
2443
|
</div>
|
|
@@ -2460,7 +2483,7 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2460
2483
|
}
|
|
2461
2484
|
});
|
|
2462
2485
|
}
|
|
2463
|
-
const passwordHash = await
|
|
2486
|
+
const passwordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword("admin123");
|
|
2464
2487
|
const userId = "admin-user-id";
|
|
2465
2488
|
const now = Date.now();
|
|
2466
2489
|
const adminEmail = "admin@sonicjs.com".toLowerCase();
|
|
@@ -2680,7 +2703,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2680
2703
|
if (existingUsername) {
|
|
2681
2704
|
return c.json({ error: "Username is already taken" }, 400);
|
|
2682
2705
|
}
|
|
2683
|
-
const passwordHash = await
|
|
2706
|
+
const passwordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword(password);
|
|
2684
2707
|
const updateStmt = db.prepare(`
|
|
2685
2708
|
UPDATE users SET
|
|
2686
2709
|
username = ?,
|
|
@@ -2699,7 +2722,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2699
2722
|
Date.now(),
|
|
2700
2723
|
invitedUser.id
|
|
2701
2724
|
).run();
|
|
2702
|
-
const authToken = await
|
|
2725
|
+
const authToken = await chunkIM5SDXOE_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role);
|
|
2703
2726
|
cookie.setCookie(c, "auth_token", authToken, {
|
|
2704
2727
|
httpOnly: true,
|
|
2705
2728
|
secure: true,
|
|
@@ -2707,7 +2730,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2707
2730
|
maxAge: 60 * 60 * 24
|
|
2708
2731
|
// 24 hours
|
|
2709
2732
|
});
|
|
2710
|
-
return c.redirect("/admin/
|
|
2733
|
+
return c.redirect("/admin/dashboard?welcome=true");
|
|
2711
2734
|
} catch (error) {
|
|
2712
2735
|
console.error("Accept invitation error:", error);
|
|
2713
2736
|
return c.json({ error: "Failed to accept invitation" }, 500);
|
|
@@ -2929,7 +2952,7 @@ authRoutes.post("/reset-password", async (c) => {
|
|
|
2929
2952
|
if (Date.now() > user.password_reset_expires) {
|
|
2930
2953
|
return c.json({ error: "Reset token has expired" }, 400);
|
|
2931
2954
|
}
|
|
2932
|
-
const newPasswordHash = await
|
|
2955
|
+
const newPasswordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword(password);
|
|
2933
2956
|
try {
|
|
2934
2957
|
const historyStmt = db.prepare(`
|
|
2935
2958
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
@@ -4734,9 +4757,9 @@ var isPluginActive2 = () => false;
|
|
|
4734
4757
|
|
|
4735
4758
|
// src/routes/admin-content.ts
|
|
4736
4759
|
var adminContentRoutes = new hono.Hono();
|
|
4737
|
-
adminContentRoutes.use("*",
|
|
4760
|
+
adminContentRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
4738
4761
|
async function getCollectionFields(db, collectionId) {
|
|
4739
|
-
const cache =
|
|
4762
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.collection);
|
|
4740
4763
|
return cache.getOrSet(
|
|
4741
4764
|
cache.generateKey("fields", collectionId),
|
|
4742
4765
|
async () => {
|
|
@@ -4760,7 +4783,7 @@ async function getCollectionFields(db, collectionId) {
|
|
|
4760
4783
|
);
|
|
4761
4784
|
}
|
|
4762
4785
|
async function getCollection(db, collectionId) {
|
|
4763
|
-
const cache =
|
|
4786
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.collection);
|
|
4764
4787
|
return cache.getOrSet(
|
|
4765
4788
|
cache.generateKey("collection", collectionId),
|
|
4766
4789
|
async () => {
|
|
@@ -5016,7 +5039,7 @@ adminContentRoutes.get("/:id/edit", async (c) => {
|
|
|
5016
5039
|
const db = c.env.DB;
|
|
5017
5040
|
const url = new URL(c.req.url);
|
|
5018
5041
|
const referrerParams = url.searchParams.get("ref") || "";
|
|
5019
|
-
const cache =
|
|
5042
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.content);
|
|
5020
5043
|
const content = await cache.getOrSet(
|
|
5021
5044
|
cache.generateKey("content", id),
|
|
5022
5045
|
async () => {
|
|
@@ -5185,11 +5208,11 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
5185
5208
|
const now = Date.now();
|
|
5186
5209
|
const insertStmt = db.prepare(`
|
|
5187
5210
|
INSERT INTO content (
|
|
5188
|
-
id, collection_id, slug, title, data, status,
|
|
5211
|
+
id, collection_id, slug, title, data, status,
|
|
5189
5212
|
scheduled_publish_at, scheduled_unpublish_at,
|
|
5190
|
-
meta_title, meta_description, author_id, created_at, updated_at
|
|
5213
|
+
meta_title, meta_description, author_id, created_by, created_at, updated_at
|
|
5191
5214
|
)
|
|
5192
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5215
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5193
5216
|
`);
|
|
5194
5217
|
await insertStmt.bind(
|
|
5195
5218
|
contentId,
|
|
@@ -5203,10 +5226,11 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
5203
5226
|
data.meta_title || null,
|
|
5204
5227
|
data.meta_description || null,
|
|
5205
5228
|
user?.userId || "unknown",
|
|
5229
|
+
user?.userId || "unknown",
|
|
5206
5230
|
now,
|
|
5207
5231
|
now
|
|
5208
5232
|
).run();
|
|
5209
|
-
const cache =
|
|
5233
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.content);
|
|
5210
5234
|
await cache.invalidate(`content:list:${collectionId}:*`);
|
|
5211
5235
|
const versionStmt = db.prepare(`
|
|
5212
5236
|
INSERT INTO content_versions (id, content_id, version, data, author_id, created_at)
|
|
@@ -5354,7 +5378,7 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
5354
5378
|
now,
|
|
5355
5379
|
id
|
|
5356
5380
|
).run();
|
|
5357
|
-
const cache =
|
|
5381
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.content);
|
|
5358
5382
|
await cache.delete(cache.generateKey("content", id));
|
|
5359
5383
|
await cache.invalidate(`content:list:${existingContent.collection_id}:*`);
|
|
5360
5384
|
const existingData = JSON.parse(existingContent.data || "{}");
|
|
@@ -5504,10 +5528,10 @@ adminContentRoutes.post("/duplicate", async (c) => {
|
|
|
5504
5528
|
originalData.title = `${originalData.title || "Untitled"} (Copy)`;
|
|
5505
5529
|
const insertStmt = db.prepare(`
|
|
5506
5530
|
INSERT INTO content (
|
|
5507
|
-
id, collection_id, slug, title, data, status,
|
|
5508
|
-
author_id, created_at, updated_at
|
|
5531
|
+
id, collection_id, slug, title, data, status,
|
|
5532
|
+
author_id, created_by, created_at, updated_at
|
|
5509
5533
|
)
|
|
5510
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5534
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5511
5535
|
`);
|
|
5512
5536
|
await insertStmt.bind(
|
|
5513
5537
|
newId,
|
|
@@ -5518,6 +5542,7 @@ adminContentRoutes.post("/duplicate", async (c) => {
|
|
|
5518
5542
|
"draft",
|
|
5519
5543
|
// Always start as draft
|
|
5520
5544
|
user?.userId || "unknown",
|
|
5545
|
+
user?.userId || "unknown",
|
|
5521
5546
|
now,
|
|
5522
5547
|
now
|
|
5523
5548
|
).run();
|
|
@@ -5647,7 +5672,7 @@ adminContentRoutes.post("/bulk-action", async (c) => {
|
|
|
5647
5672
|
} else {
|
|
5648
5673
|
return c.json({ success: false, error: "Invalid action" });
|
|
5649
5674
|
}
|
|
5650
|
-
const cache =
|
|
5675
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.content);
|
|
5651
5676
|
for (const contentId of ids) {
|
|
5652
5677
|
await cache.delete(cache.generateKey("content", contentId));
|
|
5653
5678
|
}
|
|
@@ -5675,7 +5700,7 @@ adminContentRoutes.delete("/:id", async (c) => {
|
|
|
5675
5700
|
WHERE id = ?
|
|
5676
5701
|
`);
|
|
5677
5702
|
await deleteStmt.bind(now, id).run();
|
|
5678
|
-
const cache =
|
|
5703
|
+
const cache = chunkDOR2IU73_cjs.getCacheService(chunkDOR2IU73_cjs.CACHE_CONFIGS.content);
|
|
5679
5704
|
await cache.delete(cache.generateKey("content", id));
|
|
5680
5705
|
await cache.invalidate("content:list:*");
|
|
5681
5706
|
return c.html(`
|
|
@@ -5868,6 +5893,11 @@ var admin_content_default = adminContentRoutes;
|
|
|
5868
5893
|
|
|
5869
5894
|
// src/templates/pages/admin-profile.template.ts
|
|
5870
5895
|
chunk3SPQ3J4N_cjs.init_admin_layout_catalyst_template();
|
|
5896
|
+
function renderAvatarImage(avatarUrl, firstName, lastName) {
|
|
5897
|
+
return `<div id="avatar-image-container" class="w-24 h-24 rounded-full mx-auto mb-4 overflow-hidden bg-gradient-to-br from-cyan-400 to-purple-400 flex items-center justify-center ring-4 ring-zinc-950/5 dark:ring-white/10">
|
|
5898
|
+
${avatarUrl ? `<img src="${avatarUrl}" alt="Profile picture" class="w-full h-full object-cover">` : `<span class="text-2xl font-bold text-white">${firstName.charAt(0)}${lastName.charAt(0)}</span>`}
|
|
5899
|
+
</div>`;
|
|
5900
|
+
}
|
|
5871
5901
|
function renderProfilePage(data) {
|
|
5872
5902
|
const pageContent = `
|
|
5873
5903
|
<div class="space-y-8">
|
|
@@ -6066,9 +6096,7 @@ function renderProfilePage(data) {
|
|
|
6066
6096
|
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Profile Picture</h3>
|
|
6067
6097
|
|
|
6068
6098
|
<div class="text-center">
|
|
6069
|
-
|
|
6070
|
-
${data.profile.avatar_url ? `<img src="${data.profile.avatar_url}" alt="Profile picture" class="w-full h-full object-cover">` : `<span class="text-2xl font-bold text-white">${data.profile.first_name.charAt(0)}${data.profile.last_name.charAt(0)}</span>`}
|
|
6071
|
-
</div>
|
|
6099
|
+
${renderAvatarImage(data.profile.avatar_url, data.profile.first_name, data.profile.last_name)}
|
|
6072
6100
|
|
|
6073
6101
|
<form id="avatar-form" hx-post="/admin/profile/avatar" hx-target="#avatar-messages" hx-encoding="multipart/form-data">
|
|
6074
6102
|
<input
|
|
@@ -6716,7 +6744,7 @@ function renderUserEditPage(data) {
|
|
|
6716
6744
|
<input
|
|
6717
6745
|
type="text"
|
|
6718
6746
|
name="first_name"
|
|
6719
|
-
value="${
|
|
6747
|
+
value="${chunkTRSHFTF6_cjs.escapeHtml(data.userToEdit.firstName || "")}"
|
|
6720
6748
|
required
|
|
6721
6749
|
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"
|
|
6722
6750
|
/>
|
|
@@ -6727,7 +6755,7 @@ function renderUserEditPage(data) {
|
|
|
6727
6755
|
<input
|
|
6728
6756
|
type="text"
|
|
6729
6757
|
name="last_name"
|
|
6730
|
-
value="${
|
|
6758
|
+
value="${chunkTRSHFTF6_cjs.escapeHtml(data.userToEdit.lastName || "")}"
|
|
6731
6759
|
required
|
|
6732
6760
|
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"
|
|
6733
6761
|
/>
|
|
@@ -6738,7 +6766,7 @@ function renderUserEditPage(data) {
|
|
|
6738
6766
|
<input
|
|
6739
6767
|
type="text"
|
|
6740
6768
|
name="username"
|
|
6741
|
-
value="${
|
|
6769
|
+
value="${chunkTRSHFTF6_cjs.escapeHtml(data.userToEdit.username || "")}"
|
|
6742
6770
|
required
|
|
6743
6771
|
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"
|
|
6744
6772
|
/>
|
|
@@ -6749,7 +6777,7 @@ function renderUserEditPage(data) {
|
|
|
6749
6777
|
<input
|
|
6750
6778
|
type="email"
|
|
6751
6779
|
name="email"
|
|
6752
|
-
value="${
|
|
6780
|
+
value="${chunkTRSHFTF6_cjs.escapeHtml(data.userToEdit.email || "")}"
|
|
6753
6781
|
required
|
|
6754
6782
|
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"
|
|
6755
6783
|
/>
|
|
@@ -6760,7 +6788,7 @@ function renderUserEditPage(data) {
|
|
|
6760
6788
|
<input
|
|
6761
6789
|
type="tel"
|
|
6762
6790
|
name="phone"
|
|
6763
|
-
value="${
|
|
6791
|
+
value="${chunkTRSHFTF6_cjs.escapeHtml(data.userToEdit.phone || "")}"
|
|
6764
6792
|
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"
|
|
6765
6793
|
/>
|
|
6766
6794
|
</div>
|
|
@@ -6774,7 +6802,7 @@ function renderUserEditPage(data) {
|
|
|
6774
6802
|
class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-500/30 dark:outline-zinc-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400 sm:text-sm/6"
|
|
6775
6803
|
>
|
|
6776
6804
|
${data.roles.map((role) => `
|
|
6777
|
-
<option value="${
|
|
6805
|
+
<option value="${chunkTRSHFTF6_cjs.escapeHtml(role.value)}" ${data.userToEdit.role === role.value ? "selected" : ""}>${chunkTRSHFTF6_cjs.escapeHtml(role.label)}</option>
|
|
6778
6806
|
`).join("")}
|
|
6779
6807
|
</select>
|
|
6780
6808
|
<svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-600 dark:text-zinc-400 sm:size-4">
|
|
@@ -6790,7 +6818,7 @@ function renderUserEditPage(data) {
|
|
|
6790
6818
|
name="bio"
|
|
6791
6819
|
rows="3"
|
|
6792
6820
|
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"
|
|
6793
|
-
>${
|
|
6821
|
+
>${chunkTRSHFTF6_cjs.escapeHtml(data.userToEdit.bio || "")}</textarea>
|
|
6794
6822
|
</div>
|
|
6795
6823
|
</div>
|
|
6796
6824
|
|
|
@@ -7690,7 +7718,7 @@ function renderUsersListPage(data) {
|
|
|
7690
7718
|
|
|
7691
7719
|
// src/routes/admin-users.ts
|
|
7692
7720
|
var userRoutes = new hono.Hono();
|
|
7693
|
-
userRoutes.use("*",
|
|
7721
|
+
userRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
7694
7722
|
userRoutes.get("/", (c) => {
|
|
7695
7723
|
return c.redirect("/admin/dashboard");
|
|
7696
7724
|
});
|
|
@@ -7789,12 +7817,12 @@ userRoutes.put("/profile", async (c) => {
|
|
|
7789
7817
|
const db = c.env.DB;
|
|
7790
7818
|
try {
|
|
7791
7819
|
const formData = await c.req.formData();
|
|
7792
|
-
const firstName =
|
|
7793
|
-
const lastName =
|
|
7794
|
-
const username =
|
|
7820
|
+
const firstName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
7821
|
+
const lastName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
7822
|
+
const username = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
7795
7823
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
7796
|
-
const phone =
|
|
7797
|
-
const bio =
|
|
7824
|
+
const phone = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
7825
|
+
const bio = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
7798
7826
|
const timezone = formData.get("timezone")?.toString() || "UTC";
|
|
7799
7827
|
const language = formData.get("language")?.toString() || "en";
|
|
7800
7828
|
const emailNotifications = formData.get("email_notifications") === "1";
|
|
@@ -7845,7 +7873,7 @@ userRoutes.put("/profile", async (c) => {
|
|
|
7845
7873
|
Date.now(),
|
|
7846
7874
|
user.userId
|
|
7847
7875
|
).run();
|
|
7848
|
-
await
|
|
7876
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
7849
7877
|
db,
|
|
7850
7878
|
user.userId,
|
|
7851
7879
|
"profile.update",
|
|
@@ -7904,7 +7932,11 @@ userRoutes.post("/profile/avatar", async (c) => {
|
|
|
7904
7932
|
WHERE id = ?
|
|
7905
7933
|
`);
|
|
7906
7934
|
await updateStmt.bind(avatarUrl, Date.now(), user.userId).run();
|
|
7907
|
-
|
|
7935
|
+
const userStmt = db.prepare(`
|
|
7936
|
+
SELECT first_name, last_name FROM users WHERE id = ?
|
|
7937
|
+
`);
|
|
7938
|
+
const userData = await userStmt.bind(user.userId).first();
|
|
7939
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
7908
7940
|
db,
|
|
7909
7941
|
user.userId,
|
|
7910
7942
|
"profile.avatar_update",
|
|
@@ -7914,11 +7946,18 @@ userRoutes.post("/profile/avatar", async (c) => {
|
|
|
7914
7946
|
c.req.header("x-forwarded-for") || c.req.header("cf-connecting-ip"),
|
|
7915
7947
|
c.req.header("user-agent")
|
|
7916
7948
|
);
|
|
7917
|
-
|
|
7949
|
+
const alertHtml = renderAlert2({
|
|
7918
7950
|
type: "success",
|
|
7919
7951
|
message: "Profile picture updated successfully!",
|
|
7920
7952
|
dismissible: true
|
|
7921
|
-
})
|
|
7953
|
+
});
|
|
7954
|
+
const avatarUrlWithCache = `${avatarUrl}?t=${Date.now()}`;
|
|
7955
|
+
const avatarImageHtml = renderAvatarImage(avatarUrlWithCache, userData.first_name, userData.last_name);
|
|
7956
|
+
const avatarImageWithOob = avatarImageHtml.replace(
|
|
7957
|
+
'id="avatar-image-container"',
|
|
7958
|
+
'id="avatar-image-container" hx-swap-oob="true"'
|
|
7959
|
+
);
|
|
7960
|
+
return c.html(alertHtml + avatarImageWithOob);
|
|
7922
7961
|
} catch (error) {
|
|
7923
7962
|
console.error("Avatar upload error:", error);
|
|
7924
7963
|
return c.html(renderAlert2({
|
|
@@ -7968,7 +8007,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
7968
8007
|
dismissible: true
|
|
7969
8008
|
}));
|
|
7970
8009
|
}
|
|
7971
|
-
const validPassword = await
|
|
8010
|
+
const validPassword = await chunkIM5SDXOE_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
|
|
7972
8011
|
if (!validPassword) {
|
|
7973
8012
|
return c.html(renderAlert2({
|
|
7974
8013
|
type: "error",
|
|
@@ -7976,7 +8015,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
7976
8015
|
dismissible: true
|
|
7977
8016
|
}));
|
|
7978
8017
|
}
|
|
7979
|
-
const newPasswordHash = await
|
|
8018
|
+
const newPasswordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword(newPassword);
|
|
7980
8019
|
const historyStmt = db.prepare(`
|
|
7981
8020
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
7982
8021
|
VALUES (?, ?, ?, ?)
|
|
@@ -7992,7 +8031,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
7992
8031
|
WHERE id = ?
|
|
7993
8032
|
`);
|
|
7994
8033
|
await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
|
|
7995
|
-
await
|
|
8034
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
7996
8035
|
db,
|
|
7997
8036
|
user.userId,
|
|
7998
8037
|
"profile.password_change",
|
|
@@ -8059,7 +8098,7 @@ userRoutes.get("/users", async (c) => {
|
|
|
8059
8098
|
`);
|
|
8060
8099
|
const countResult = await countStmt.bind(...params).first();
|
|
8061
8100
|
const totalUsers = countResult?.total || 0;
|
|
8062
|
-
await
|
|
8101
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8063
8102
|
db,
|
|
8064
8103
|
user.userId,
|
|
8065
8104
|
"users.list_view",
|
|
@@ -8161,12 +8200,12 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
8161
8200
|
const user = c.get("user");
|
|
8162
8201
|
try {
|
|
8163
8202
|
const formData = await c.req.formData();
|
|
8164
|
-
const firstName =
|
|
8165
|
-
const lastName =
|
|
8166
|
-
const username =
|
|
8203
|
+
const firstName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
8204
|
+
const lastName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
8205
|
+
const username = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
8167
8206
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
8168
|
-
const phone =
|
|
8169
|
-
const bio =
|
|
8207
|
+
const phone = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
8208
|
+
const bio = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
8170
8209
|
const role = formData.get("role")?.toString() || "viewer";
|
|
8171
8210
|
const password = formData.get("password")?.toString() || "";
|
|
8172
8211
|
const confirmPassword = formData.get("confirm_password")?.toString() || "";
|
|
@@ -8213,7 +8252,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
8213
8252
|
dismissible: true
|
|
8214
8253
|
}));
|
|
8215
8254
|
}
|
|
8216
|
-
const passwordHash = await
|
|
8255
|
+
const passwordHash = await chunkIM5SDXOE_cjs.AuthManager.hashPassword(password);
|
|
8217
8256
|
const userId = globalThis.crypto.randomUUID();
|
|
8218
8257
|
const createStmt = db.prepare(`
|
|
8219
8258
|
INSERT INTO users (
|
|
@@ -8236,7 +8275,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
8236
8275
|
Date.now(),
|
|
8237
8276
|
Date.now()
|
|
8238
8277
|
).run();
|
|
8239
|
-
await
|
|
8278
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8240
8279
|
db,
|
|
8241
8280
|
user.userId,
|
|
8242
8281
|
"user!.create",
|
|
@@ -8274,7 +8313,7 @@ userRoutes.get("/users/:id", async (c) => {
|
|
|
8274
8313
|
if (!userRecord) {
|
|
8275
8314
|
return c.json({ error: "User not found" }, 404);
|
|
8276
8315
|
}
|
|
8277
|
-
await
|
|
8316
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8278
8317
|
db,
|
|
8279
8318
|
user.userId,
|
|
8280
8319
|
"user!.view",
|
|
@@ -8367,12 +8406,12 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
8367
8406
|
const userId = c.req.param("id");
|
|
8368
8407
|
try {
|
|
8369
8408
|
const formData = await c.req.formData();
|
|
8370
|
-
const firstName =
|
|
8371
|
-
const lastName =
|
|
8372
|
-
const username =
|
|
8409
|
+
const firstName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
8410
|
+
const lastName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
8411
|
+
const username = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
8373
8412
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
8374
|
-
const phone =
|
|
8375
|
-
const bio =
|
|
8413
|
+
const phone = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
8414
|
+
const bio = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
8376
8415
|
const role = formData.get("role")?.toString() || "viewer";
|
|
8377
8416
|
const isActive = formData.get("is_active") === "1";
|
|
8378
8417
|
const emailVerified = formData.get("email_verified") === "1";
|
|
@@ -8423,7 +8462,7 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
8423
8462
|
Date.now(),
|
|
8424
8463
|
userId
|
|
8425
8464
|
).run();
|
|
8426
|
-
await
|
|
8465
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8427
8466
|
db,
|
|
8428
8467
|
user.userId,
|
|
8429
8468
|
"user!.update",
|
|
@@ -8469,7 +8508,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
8469
8508
|
DELETE FROM users WHERE id = ?
|
|
8470
8509
|
`);
|
|
8471
8510
|
await deleteStmt.bind(userId).run();
|
|
8472
|
-
await
|
|
8511
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8473
8512
|
db,
|
|
8474
8513
|
user.userId,
|
|
8475
8514
|
"user!.hard_delete",
|
|
@@ -8488,7 +8527,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
8488
8527
|
UPDATE users SET is_active = 0, updated_at = ? WHERE id = ?
|
|
8489
8528
|
`);
|
|
8490
8529
|
await deleteStmt.bind(Date.now(), userId).run();
|
|
8491
|
-
await
|
|
8530
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8492
8531
|
db,
|
|
8493
8532
|
user.userId,
|
|
8494
8533
|
"user!.soft_delete",
|
|
@@ -8515,8 +8554,8 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
8515
8554
|
const formData = await c.req.formData();
|
|
8516
8555
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
8517
8556
|
const role = formData.get("role")?.toString()?.trim() || "viewer";
|
|
8518
|
-
const firstName =
|
|
8519
|
-
const lastName =
|
|
8557
|
+
const firstName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
8558
|
+
const lastName = chunkTRSHFTF6_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
8520
8559
|
if (!email || !firstName || !lastName) {
|
|
8521
8560
|
return c.json({ error: "Email, first name, and last name are required" }, 400);
|
|
8522
8561
|
}
|
|
@@ -8555,7 +8594,7 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
8555
8594
|
Date.now(),
|
|
8556
8595
|
Date.now()
|
|
8557
8596
|
).run();
|
|
8558
|
-
await
|
|
8597
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8559
8598
|
db,
|
|
8560
8599
|
user.userId,
|
|
8561
8600
|
"user!.invite_sent",
|
|
@@ -8612,7 +8651,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
|
|
|
8612
8651
|
Date.now(),
|
|
8613
8652
|
userId
|
|
8614
8653
|
).run();
|
|
8615
|
-
await
|
|
8654
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8616
8655
|
db,
|
|
8617
8656
|
user.userId,
|
|
8618
8657
|
"user!.invitation_resent",
|
|
@@ -8648,7 +8687,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
|
|
|
8648
8687
|
}
|
|
8649
8688
|
const deleteStmt = db.prepare(`DELETE FROM users WHERE id = ?`);
|
|
8650
8689
|
await deleteStmt.bind(userId).run();
|
|
8651
|
-
await
|
|
8690
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8652
8691
|
db,
|
|
8653
8692
|
user.userId,
|
|
8654
8693
|
"user!.invitation_cancelled",
|
|
@@ -8731,7 +8770,7 @@ userRoutes.get("/activity-logs", async (c) => {
|
|
|
8731
8770
|
...log,
|
|
8732
8771
|
details: log.details ? JSON.parse(log.details) : null
|
|
8733
8772
|
}));
|
|
8734
|
-
await
|
|
8773
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8735
8774
|
db,
|
|
8736
8775
|
user.userId,
|
|
8737
8776
|
"activity.logs_viewed",
|
|
@@ -8838,7 +8877,7 @@ userRoutes.get("/activity-logs/export", async (c) => {
|
|
|
8838
8877
|
csvRows.push(row.join(","));
|
|
8839
8878
|
}
|
|
8840
8879
|
const csvContent = csvRows.join("\n");
|
|
8841
|
-
await
|
|
8880
|
+
await chunkIM5SDXOE_cjs.logActivity(
|
|
8842
8881
|
db,
|
|
8843
8882
|
user.userId,
|
|
8844
8883
|
"activity.logs_exported",
|
|
@@ -9150,8 +9189,10 @@ function renderMediaLibraryPage(data) {
|
|
|
9150
9189
|
</button>
|
|
9151
9190
|
<button
|
|
9152
9191
|
class="w-full text-left px-3 py-2 text-sm text-zinc-700 dark:text-zinc-300 hover:text-zinc-950 dark:hover:text-white hover:bg-zinc-50 dark:hover:bg-zinc-800/50 rounded-lg transition-colors"
|
|
9153
|
-
hx-delete="/media/cleanup"
|
|
9192
|
+
hx-delete="/admin/media/cleanup"
|
|
9154
9193
|
hx-confirm="Delete unused files?"
|
|
9194
|
+
hx-target="body"
|
|
9195
|
+
hx-swap="beforeend"
|
|
9155
9196
|
>
|
|
9156
9197
|
Cleanup Unused
|
|
9157
9198
|
</button>
|
|
@@ -9331,11 +9372,12 @@ function renderMediaLibraryPage(data) {
|
|
|
9331
9372
|
</div>
|
|
9332
9373
|
|
|
9333
9374
|
<!-- Upload Form -->
|
|
9334
|
-
<form
|
|
9375
|
+
<form
|
|
9335
9376
|
id="upload-form"
|
|
9336
9377
|
hx-post="/admin/media/upload"
|
|
9337
9378
|
hx-encoding="multipart/form-data"
|
|
9338
9379
|
hx-target="#upload-results"
|
|
9380
|
+
hx-on::after-request="if(event.detail.successful) { setTimeout(() => { window.location.href = '/admin/media?t=' + Date.now(); }, 1500); }"
|
|
9339
9381
|
class="space-y-4"
|
|
9340
9382
|
>
|
|
9341
9383
|
<!-- Drag and Drop Zone -->
|
|
@@ -10174,7 +10216,7 @@ var fileValidationSchema2 = zod.z.object({
|
|
|
10174
10216
|
// 50MB max
|
|
10175
10217
|
});
|
|
10176
10218
|
var adminMediaRoutes = new hono.Hono();
|
|
10177
|
-
adminMediaRoutes.use("*",
|
|
10219
|
+
adminMediaRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
10178
10220
|
adminMediaRoutes.get("/", async (c) => {
|
|
10179
10221
|
try {
|
|
10180
10222
|
const user = c.get("user");
|
|
@@ -10218,21 +10260,23 @@ adminMediaRoutes.get("/", async (c) => {
|
|
|
10218
10260
|
const { results } = await stmt.bind(...params).all();
|
|
10219
10261
|
const foldersStmt = db.prepare(`
|
|
10220
10262
|
SELECT folder, COUNT(*) as count, SUM(size) as totalSize
|
|
10221
|
-
FROM media
|
|
10222
|
-
|
|
10263
|
+
FROM media
|
|
10264
|
+
WHERE deleted_at IS NULL
|
|
10265
|
+
GROUP BY folder
|
|
10223
10266
|
ORDER BY folder
|
|
10224
10267
|
`);
|
|
10225
10268
|
const { results: folders } = await foldersStmt.all();
|
|
10226
10269
|
const typesStmt = db.prepare(`
|
|
10227
|
-
SELECT
|
|
10228
|
-
CASE
|
|
10270
|
+
SELECT
|
|
10271
|
+
CASE
|
|
10229
10272
|
WHEN mime_type LIKE 'image/%' THEN 'images'
|
|
10230
10273
|
WHEN mime_type LIKE 'video/%' THEN 'videos'
|
|
10231
10274
|
WHEN mime_type IN ('application/pdf', 'text/plain') THEN 'documents'
|
|
10232
10275
|
ELSE 'other'
|
|
10233
10276
|
END as type,
|
|
10234
10277
|
COUNT(*) as count
|
|
10235
|
-
FROM media
|
|
10278
|
+
FROM media
|
|
10279
|
+
WHERE deleted_at IS NULL
|
|
10236
10280
|
GROUP BY type
|
|
10237
10281
|
`);
|
|
10238
10282
|
const { results: types } = await typesStmt.all();
|
|
@@ -10508,6 +10552,18 @@ adminMediaRoutes.post("/upload", async (c) => {
|
|
|
10508
10552
|
}
|
|
10509
10553
|
const uploadResults = [];
|
|
10510
10554
|
const errors = [];
|
|
10555
|
+
console.log("[MEDIA UPLOAD] c.env keys:", Object.keys(c.env));
|
|
10556
|
+
console.log("[MEDIA UPLOAD] MEDIA_BUCKET defined?", !!c.env.MEDIA_BUCKET);
|
|
10557
|
+
console.log("[MEDIA UPLOAD] MEDIA_BUCKET type:", typeof c.env.MEDIA_BUCKET);
|
|
10558
|
+
if (!c.env.MEDIA_BUCKET) {
|
|
10559
|
+
console.error("[MEDIA UPLOAD] MEDIA_BUCKET is not available! Available env keys:", Object.keys(c.env));
|
|
10560
|
+
return c.html(html.html`
|
|
10561
|
+
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
10562
|
+
Media storage (R2) is not configured. Please check your wrangler.toml configuration.
|
|
10563
|
+
<br><small>Debug: Available bindings: ${Object.keys(c.env).join(", ")}</small>
|
|
10564
|
+
</div>
|
|
10565
|
+
`);
|
|
10566
|
+
}
|
|
10511
10567
|
for (const file of files) {
|
|
10512
10568
|
try {
|
|
10513
10569
|
const validation = fileValidationSchema2.safeParse({
|
|
@@ -10739,6 +10795,87 @@ adminMediaRoutes.put("/:id", async (c) => {
|
|
|
10739
10795
|
`);
|
|
10740
10796
|
}
|
|
10741
10797
|
});
|
|
10798
|
+
adminMediaRoutes.delete("/cleanup", chunkIM5SDXOE_cjs.requireRole("admin"), async (c) => {
|
|
10799
|
+
try {
|
|
10800
|
+
const db = c.env.DB;
|
|
10801
|
+
const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
|
|
10802
|
+
const { results: allMedia } = await allMediaStmt.all();
|
|
10803
|
+
const contentStmt = db.prepare("SELECT data FROM content");
|
|
10804
|
+
const { results: contentRecords } = await contentStmt.all();
|
|
10805
|
+
const referencedUrls = /* @__PURE__ */ new Set();
|
|
10806
|
+
for (const record of contentRecords) {
|
|
10807
|
+
if (record.data) {
|
|
10808
|
+
const dataStr = typeof record.data === "string" ? record.data : JSON.stringify(record.data);
|
|
10809
|
+
const urlMatches = dataStr.matchAll(/\/files\/([^\s"',]+)/g);
|
|
10810
|
+
for (const match of urlMatches) {
|
|
10811
|
+
referencedUrls.add(match[1]);
|
|
10812
|
+
}
|
|
10813
|
+
}
|
|
10814
|
+
}
|
|
10815
|
+
const unusedFiles = allMedia.filter((file) => !referencedUrls.has(file.r2_key));
|
|
10816
|
+
if (unusedFiles.length === 0) {
|
|
10817
|
+
return c.html(html.html`
|
|
10818
|
+
<div class="bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded">
|
|
10819
|
+
No unused media files found. All files are referenced in content.
|
|
10820
|
+
</div>
|
|
10821
|
+
<script>
|
|
10822
|
+
setTimeout(() => {
|
|
10823
|
+
window.location.href = '/admin/media?t=' + Date.now();
|
|
10824
|
+
}, 2000);
|
|
10825
|
+
</script>
|
|
10826
|
+
`);
|
|
10827
|
+
}
|
|
10828
|
+
let deletedCount = 0;
|
|
10829
|
+
const errors = [];
|
|
10830
|
+
for (const file of unusedFiles) {
|
|
10831
|
+
try {
|
|
10832
|
+
await c.env.MEDIA_BUCKET.delete(file.r2_key);
|
|
10833
|
+
const deleteStmt = db.prepare("UPDATE media SET deleted_at = ? WHERE id = ?");
|
|
10834
|
+
await deleteStmt.bind(Math.floor(Date.now() / 1e3), file.id).run();
|
|
10835
|
+
deletedCount++;
|
|
10836
|
+
} catch (error) {
|
|
10837
|
+
console.error(`Failed to delete ${file.filename}:`, error);
|
|
10838
|
+
errors.push({
|
|
10839
|
+
filename: file.filename,
|
|
10840
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
10841
|
+
});
|
|
10842
|
+
}
|
|
10843
|
+
}
|
|
10844
|
+
return c.html(html.html`
|
|
10845
|
+
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
|
|
10846
|
+
Successfully cleaned up ${deletedCount} unused media file${deletedCount !== 1 ? "s" : ""}.
|
|
10847
|
+
${errors.length > 0 ? html.html`
|
|
10848
|
+
<br><span class="text-sm">Failed to delete ${errors.length} file${errors.length !== 1 ? "s" : ""}.</span>
|
|
10849
|
+
` : ""}
|
|
10850
|
+
</div>
|
|
10851
|
+
|
|
10852
|
+
${errors.length > 0 ? html.html`
|
|
10853
|
+
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
10854
|
+
<p class="font-medium">Cleanup errors:</p>
|
|
10855
|
+
<ul class="list-disc list-inside mt-2 text-sm">
|
|
10856
|
+
${errors.map((error) => html.html`
|
|
10857
|
+
<li>${error.filename}: ${error.error}</li>
|
|
10858
|
+
`)}
|
|
10859
|
+
</ul>
|
|
10860
|
+
</div>
|
|
10861
|
+
` : ""}
|
|
10862
|
+
|
|
10863
|
+
<script>
|
|
10864
|
+
// Refresh media library after cleanup
|
|
10865
|
+
setTimeout(() => {
|
|
10866
|
+
window.location.href = '/admin/media?t=' + Date.now();
|
|
10867
|
+
}, 2500);
|
|
10868
|
+
</script>
|
|
10869
|
+
`);
|
|
10870
|
+
} catch (error) {
|
|
10871
|
+
console.error("Cleanup error:", error);
|
|
10872
|
+
return c.html(html.html`
|
|
10873
|
+
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
10874
|
+
Cleanup failed: ${error instanceof Error ? error.message : "Unknown error"}
|
|
10875
|
+
</div>
|
|
10876
|
+
`);
|
|
10877
|
+
}
|
|
10878
|
+
});
|
|
10742
10879
|
adminMediaRoutes.delete("/:id", async (c) => {
|
|
10743
10880
|
try {
|
|
10744
10881
|
const user = c.get("user");
|
|
@@ -12194,7 +12331,7 @@ function formatTimestamp(timestamp) {
|
|
|
12194
12331
|
|
|
12195
12332
|
// src/routes/admin-plugins.ts
|
|
12196
12333
|
var adminPluginRoutes = new hono.Hono();
|
|
12197
|
-
adminPluginRoutes.use("*",
|
|
12334
|
+
adminPluginRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
12198
12335
|
adminPluginRoutes.get("/", async (c) => {
|
|
12199
12336
|
try {
|
|
12200
12337
|
const user = c.get("user");
|
|
@@ -12202,7 +12339,7 @@ adminPluginRoutes.get("/", async (c) => {
|
|
|
12202
12339
|
if (user?.role !== "admin") {
|
|
12203
12340
|
return c.text("Access denied", 403);
|
|
12204
12341
|
}
|
|
12205
|
-
const pluginService = new
|
|
12342
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12206
12343
|
let plugins = [];
|
|
12207
12344
|
let stats = { total: 0, active: 0, inactive: 0, errors: 0 };
|
|
12208
12345
|
try {
|
|
@@ -12252,7 +12389,7 @@ adminPluginRoutes.get("/:id", async (c) => {
|
|
|
12252
12389
|
if (user?.role !== "admin") {
|
|
12253
12390
|
return c.redirect("/admin/plugins");
|
|
12254
12391
|
}
|
|
12255
|
-
const pluginService = new
|
|
12392
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12256
12393
|
const plugin = await pluginService.getPlugin(pluginId);
|
|
12257
12394
|
if (!plugin) {
|
|
12258
12395
|
return c.text("Plugin not found", 404);
|
|
@@ -12306,7 +12443,7 @@ adminPluginRoutes.post("/:id/activate", async (c) => {
|
|
|
12306
12443
|
if (user?.role !== "admin") {
|
|
12307
12444
|
return c.json({ error: "Access denied" }, 403);
|
|
12308
12445
|
}
|
|
12309
|
-
const pluginService = new
|
|
12446
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12310
12447
|
await pluginService.activatePlugin(pluginId);
|
|
12311
12448
|
return c.json({ success: true });
|
|
12312
12449
|
} catch (error) {
|
|
@@ -12323,7 +12460,7 @@ adminPluginRoutes.post("/:id/deactivate", async (c) => {
|
|
|
12323
12460
|
if (user?.role !== "admin") {
|
|
12324
12461
|
return c.json({ error: "Access denied" }, 403);
|
|
12325
12462
|
}
|
|
12326
|
-
const pluginService = new
|
|
12463
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12327
12464
|
await pluginService.deactivatePlugin(pluginId);
|
|
12328
12465
|
return c.json({ success: true });
|
|
12329
12466
|
} catch (error) {
|
|
@@ -12340,7 +12477,7 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
12340
12477
|
return c.json({ error: "Access denied" }, 403);
|
|
12341
12478
|
}
|
|
12342
12479
|
const body = await c.req.json();
|
|
12343
|
-
const pluginService = new
|
|
12480
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12344
12481
|
if (body.name === "faq-plugin") {
|
|
12345
12482
|
const faqPlugin = await pluginService.installPlugin({
|
|
12346
12483
|
id: "third-party-faq",
|
|
@@ -12490,7 +12627,7 @@ adminPluginRoutes.post("/:id/uninstall", async (c) => {
|
|
|
12490
12627
|
if (user?.role !== "admin") {
|
|
12491
12628
|
return c.json({ error: "Access denied" }, 403);
|
|
12492
12629
|
}
|
|
12493
|
-
const pluginService = new
|
|
12630
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12494
12631
|
await pluginService.uninstallPlugin(pluginId);
|
|
12495
12632
|
return c.json({ success: true });
|
|
12496
12633
|
} catch (error) {
|
|
@@ -12508,7 +12645,7 @@ adminPluginRoutes.post("/:id/settings", async (c) => {
|
|
|
12508
12645
|
return c.json({ error: "Access denied" }, 403);
|
|
12509
12646
|
}
|
|
12510
12647
|
const settings = await c.req.json();
|
|
12511
|
-
const pluginService = new
|
|
12648
|
+
const pluginService = new chunk3JMOWGUU_cjs.PluginService(db);
|
|
12512
12649
|
await pluginService.updatePluginSettings(pluginId, settings);
|
|
12513
12650
|
return c.json({ success: true });
|
|
12514
12651
|
} catch (error) {
|
|
@@ -13304,11 +13441,11 @@ function renderLogConfigPage(data) {
|
|
|
13304
13441
|
|
|
13305
13442
|
// src/routes/admin-logs.ts
|
|
13306
13443
|
var adminLogsRoutes = new hono.Hono();
|
|
13307
|
-
adminLogsRoutes.use("*",
|
|
13444
|
+
adminLogsRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
13308
13445
|
adminLogsRoutes.get("/", async (c) => {
|
|
13309
13446
|
try {
|
|
13310
13447
|
const user = c.get("user");
|
|
13311
|
-
const logger =
|
|
13448
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13312
13449
|
const query = c.req.query();
|
|
13313
13450
|
const page = parseInt(query.page || "1");
|
|
13314
13451
|
const limit = parseInt(query.limit || "50");
|
|
@@ -13388,7 +13525,7 @@ adminLogsRoutes.get("/:id", async (c) => {
|
|
|
13388
13525
|
try {
|
|
13389
13526
|
const id = c.req.param("id");
|
|
13390
13527
|
const user = c.get("user");
|
|
13391
|
-
const logger =
|
|
13528
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13392
13529
|
const { logs } = await logger.getLogs({
|
|
13393
13530
|
limit: 1,
|
|
13394
13531
|
offset: 0,
|
|
@@ -13425,7 +13562,7 @@ adminLogsRoutes.get("/:id", async (c) => {
|
|
|
13425
13562
|
adminLogsRoutes.get("/config", async (c) => {
|
|
13426
13563
|
try {
|
|
13427
13564
|
const user = c.get("user");
|
|
13428
|
-
const logger =
|
|
13565
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13429
13566
|
const configs = await logger.getAllConfigs();
|
|
13430
13567
|
const pageData = {
|
|
13431
13568
|
configs,
|
|
@@ -13449,7 +13586,7 @@ adminLogsRoutes.post("/config/:category", async (c) => {
|
|
|
13449
13586
|
const level = formData.get("level");
|
|
13450
13587
|
const retention = parseInt(formData.get("retention"));
|
|
13451
13588
|
const maxSize = parseInt(formData.get("max_size"));
|
|
13452
|
-
const logger =
|
|
13589
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13453
13590
|
await logger.updateConfig(category, {
|
|
13454
13591
|
enabled,
|
|
13455
13592
|
level,
|
|
@@ -13478,7 +13615,7 @@ adminLogsRoutes.get("/export", async (c) => {
|
|
|
13478
13615
|
const category = query.category;
|
|
13479
13616
|
const startDate = query.start_date;
|
|
13480
13617
|
const endDate = query.end_date;
|
|
13481
|
-
const logger =
|
|
13618
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13482
13619
|
const filter = {
|
|
13483
13620
|
limit: 1e4,
|
|
13484
13621
|
// Export up to 10k logs
|
|
@@ -13559,7 +13696,7 @@ adminLogsRoutes.post("/cleanup", async (c) => {
|
|
|
13559
13696
|
error: "Unauthorized. Admin access required."
|
|
13560
13697
|
}, 403);
|
|
13561
13698
|
}
|
|
13562
|
-
const logger =
|
|
13699
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13563
13700
|
await logger.cleanupByRetention();
|
|
13564
13701
|
return c.html(html.html`
|
|
13565
13702
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
|
|
@@ -13581,7 +13718,7 @@ adminLogsRoutes.post("/search", async (c) => {
|
|
|
13581
13718
|
const search = formData.get("search");
|
|
13582
13719
|
const level = formData.get("level");
|
|
13583
13720
|
const category = formData.get("category");
|
|
13584
|
-
const logger =
|
|
13721
|
+
const logger = chunkDOR2IU73_cjs.getLogger(c.env.DB);
|
|
13585
13722
|
const filter = {
|
|
13586
13723
|
limit: 20,
|
|
13587
13724
|
offset: 0,
|
|
@@ -16237,9 +16374,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
|
|
|
16237
16374
|
}
|
|
16238
16375
|
|
|
16239
16376
|
// src/routes/admin-dashboard.ts
|
|
16240
|
-
var VERSION =
|
|
16377
|
+
var VERSION = chunkTRSHFTF6_cjs.getCoreVersion();
|
|
16241
16378
|
var router = new hono.Hono();
|
|
16242
|
-
router.use("*",
|
|
16379
|
+
router.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
16243
16380
|
router.get("/", async (c) => {
|
|
16244
16381
|
const user = c.get("user");
|
|
16245
16382
|
try {
|
|
@@ -16278,7 +16415,7 @@ router.get("/stats", async (c) => {
|
|
|
16278
16415
|
}
|
|
16279
16416
|
let contentCount = 0;
|
|
16280
16417
|
try {
|
|
16281
|
-
const contentStmt = db.prepare("SELECT COUNT(*) as count FROM content
|
|
16418
|
+
const contentStmt = db.prepare("SELECT COUNT(*) as count FROM content");
|
|
16282
16419
|
const contentResult = await contentStmt.first();
|
|
16283
16420
|
contentCount = contentResult?.count || 0;
|
|
16284
16421
|
} catch (error) {
|
|
@@ -16302,14 +16439,14 @@ router.get("/stats", async (c) => {
|
|
|
16302
16439
|
} catch (error) {
|
|
16303
16440
|
console.error("Error fetching users count:", error);
|
|
16304
16441
|
}
|
|
16305
|
-
const
|
|
16442
|
+
const html8 = renderStatsCards({
|
|
16306
16443
|
collections: collectionsCount,
|
|
16307
16444
|
contentItems: contentCount,
|
|
16308
16445
|
mediaFiles: mediaCount,
|
|
16309
16446
|
users: usersCount,
|
|
16310
16447
|
mediaSize
|
|
16311
16448
|
});
|
|
16312
|
-
return c.html(
|
|
16449
|
+
return c.html(html8);
|
|
16313
16450
|
} catch (error) {
|
|
16314
16451
|
console.error("Error fetching stats:", error);
|
|
16315
16452
|
return c.html('<div class="text-red-500">Failed to load statistics</div>');
|
|
@@ -16333,8 +16470,8 @@ router.get("/storage", async (c) => {
|
|
|
16333
16470
|
} catch (error) {
|
|
16334
16471
|
console.error("Error fetching media size:", error);
|
|
16335
16472
|
}
|
|
16336
|
-
const
|
|
16337
|
-
return c.html(
|
|
16473
|
+
const html8 = renderStorageUsage(databaseSize, mediaSize);
|
|
16474
|
+
return c.html(html8);
|
|
16338
16475
|
} catch (error) {
|
|
16339
16476
|
console.error("Error fetching storage usage:", error);
|
|
16340
16477
|
return c.html('<div class="text-red-500">Failed to load storage information</div>');
|
|
@@ -16383,12 +16520,12 @@ router.get("/recent-activity", async (c) => {
|
|
|
16383
16520
|
user: userName
|
|
16384
16521
|
};
|
|
16385
16522
|
});
|
|
16386
|
-
const
|
|
16387
|
-
return c.html(
|
|
16523
|
+
const html8 = renderRecentActivity(activities);
|
|
16524
|
+
return c.html(html8);
|
|
16388
16525
|
} catch (error) {
|
|
16389
16526
|
console.error("Error fetching recent activity:", error);
|
|
16390
|
-
const
|
|
16391
|
-
return c.html(
|
|
16527
|
+
const html8 = renderRecentActivity([]);
|
|
16528
|
+
return c.html(html8);
|
|
16392
16529
|
}
|
|
16393
16530
|
});
|
|
16394
16531
|
router.get("/api/metrics", async (c) => {
|
|
@@ -16401,7 +16538,7 @@ router.get("/api/metrics", async (c) => {
|
|
|
16401
16538
|
});
|
|
16402
16539
|
router.get("/system-status", async (c) => {
|
|
16403
16540
|
try {
|
|
16404
|
-
const
|
|
16541
|
+
const html8 = `
|
|
16405
16542
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
16406
16543
|
<div class="relative group">
|
|
16407
16544
|
<div class="absolute inset-0 bg-gradient-to-br from-blue-500/20 to-cyan-500/20 dark:from-blue-500/10 dark:to-cyan-500/10 rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
|
@@ -16456,7 +16593,7 @@ router.get("/system-status", async (c) => {
|
|
|
16456
16593
|
</div>
|
|
16457
16594
|
</div>
|
|
16458
16595
|
`;
|
|
16459
|
-
return c.html(
|
|
16596
|
+
return c.html(html8);
|
|
16460
16597
|
} catch (error) {
|
|
16461
16598
|
console.error("Error fetching system status:", error);
|
|
16462
16599
|
return c.html('<div class="text-red-500">Failed to load system status</div>');
|
|
@@ -17720,7 +17857,7 @@ function renderCollectionFormPage(data) {
|
|
|
17720
17857
|
|
|
17721
17858
|
// src/routes/admin-collections.ts
|
|
17722
17859
|
var adminCollectionsRoutes = new hono.Hono();
|
|
17723
|
-
adminCollectionsRoutes.use("*",
|
|
17860
|
+
adminCollectionsRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
17724
17861
|
adminCollectionsRoutes.get("/", async (c) => {
|
|
17725
17862
|
try {
|
|
17726
17863
|
const user = c.get("user");
|
|
@@ -17874,11 +18011,13 @@ adminCollectionsRoutes.post("/", async (c) => {
|
|
|
17874
18011
|
now,
|
|
17875
18012
|
now
|
|
17876
18013
|
).run();
|
|
17877
|
-
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
|
|
17881
|
-
|
|
18014
|
+
if (c.env.CACHE_KV) {
|
|
18015
|
+
try {
|
|
18016
|
+
await c.env.CACHE_KV.delete("cache:collections:all");
|
|
18017
|
+
await c.env.CACHE_KV.delete(`cache:collection:${name}`);
|
|
18018
|
+
} catch (e) {
|
|
18019
|
+
console.error("Error clearing cache:", e);
|
|
18020
|
+
}
|
|
17882
18021
|
}
|
|
17883
18022
|
if (isHtmx) {
|
|
17884
18023
|
return c.html(html.html`
|
|
@@ -18210,31 +18349,52 @@ function renderSettingsPage(data) {
|
|
|
18210
18349
|
// Initialize tab-specific features on page load
|
|
18211
18350
|
const currentTab = '${activeTab}';
|
|
18212
18351
|
|
|
18213
|
-
function saveAllSettings() {
|
|
18352
|
+
async function saveAllSettings() {
|
|
18214
18353
|
// Collect all form data
|
|
18215
18354
|
const formData = new FormData();
|
|
18216
|
-
|
|
18217
|
-
// Get all form inputs
|
|
18218
|
-
document.querySelectorAll('input, select, textarea').forEach(input => {
|
|
18355
|
+
|
|
18356
|
+
// Get all form inputs in the settings content area
|
|
18357
|
+
document.querySelectorAll('#settings-content input, #settings-content select, #settings-content textarea').forEach(input => {
|
|
18219
18358
|
if (input.type === 'checkbox') {
|
|
18220
|
-
formData.append(input.name, input.checked);
|
|
18359
|
+
formData.append(input.name, input.checked ? 'true' : 'false');
|
|
18221
18360
|
} else if (input.name) {
|
|
18222
18361
|
formData.append(input.name, input.value);
|
|
18223
18362
|
}
|
|
18224
18363
|
});
|
|
18225
|
-
|
|
18364
|
+
|
|
18226
18365
|
// Show loading state
|
|
18227
18366
|
const saveBtn = document.querySelector('button[onclick="saveAllSettings()"]');
|
|
18228
18367
|
const originalText = saveBtn.innerHTML;
|
|
18229
|
-
saveBtn.innerHTML = 'Saving...';
|
|
18368
|
+
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...';
|
|
18230
18369
|
saveBtn.disabled = true;
|
|
18231
|
-
|
|
18232
|
-
|
|
18233
|
-
|
|
18370
|
+
|
|
18371
|
+
try {
|
|
18372
|
+
// Determine which endpoint to call based on current tab
|
|
18373
|
+
let endpoint = '/admin/settings/general';
|
|
18374
|
+
if (currentTab === 'general') {
|
|
18375
|
+
endpoint = '/admin/settings/general';
|
|
18376
|
+
}
|
|
18377
|
+
// Add more endpoints for other tabs when implemented
|
|
18378
|
+
|
|
18379
|
+
const response = await fetch(endpoint, {
|
|
18380
|
+
method: 'POST',
|
|
18381
|
+
body: formData
|
|
18382
|
+
});
|
|
18383
|
+
|
|
18384
|
+
const result = await response.json();
|
|
18385
|
+
|
|
18386
|
+
if (result.success) {
|
|
18387
|
+
showNotification(result.message || 'Settings saved successfully!', 'success');
|
|
18388
|
+
} else {
|
|
18389
|
+
showNotification(result.error || 'Failed to save settings', 'error');
|
|
18390
|
+
}
|
|
18391
|
+
} catch (error) {
|
|
18392
|
+
console.error('Error saving settings:', error);
|
|
18393
|
+
showNotification('Failed to save settings. Please try again.', 'error');
|
|
18394
|
+
} finally {
|
|
18234
18395
|
saveBtn.innerHTML = originalText;
|
|
18235
18396
|
saveBtn.disabled = false;
|
|
18236
|
-
|
|
18237
|
-
}, 1000);
|
|
18397
|
+
}
|
|
18238
18398
|
}
|
|
18239
18399
|
|
|
18240
18400
|
function resetSettings() {
|
|
@@ -19577,7 +19737,7 @@ function renderDatabaseToolsSettings(settings) {
|
|
|
19577
19737
|
|
|
19578
19738
|
// src/routes/admin-settings.ts
|
|
19579
19739
|
var adminSettingsRoutes = new hono.Hono();
|
|
19580
|
-
adminSettingsRoutes.use("*",
|
|
19740
|
+
adminSettingsRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
|
|
19581
19741
|
function getMockSettings(user) {
|
|
19582
19742
|
return {
|
|
19583
19743
|
general: {
|
|
@@ -19639,15 +19799,20 @@ function getMockSettings(user) {
|
|
|
19639
19799
|
adminSettingsRoutes.get("/", (c) => {
|
|
19640
19800
|
return c.redirect("/admin/settings/general");
|
|
19641
19801
|
});
|
|
19642
|
-
adminSettingsRoutes.get("/general", (c) => {
|
|
19802
|
+
adminSettingsRoutes.get("/general", async (c) => {
|
|
19643
19803
|
const user = c.get("user");
|
|
19804
|
+
const db = c.env.DB;
|
|
19805
|
+
const settingsService = new chunkDOR2IU73_cjs.SettingsService(db);
|
|
19806
|
+
const generalSettings = await settingsService.getGeneralSettings(user?.email);
|
|
19807
|
+
const mockSettings = getMockSettings(user);
|
|
19808
|
+
mockSettings.general = generalSettings;
|
|
19644
19809
|
const pageData = {
|
|
19645
19810
|
user: user ? {
|
|
19646
19811
|
name: user.email,
|
|
19647
19812
|
email: user.email,
|
|
19648
19813
|
role: user.role
|
|
19649
19814
|
} : void 0,
|
|
19650
|
-
settings:
|
|
19815
|
+
settings: mockSettings,
|
|
19651
19816
|
activeTab: "general",
|
|
19652
19817
|
version: c.get("appVersion")
|
|
19653
19818
|
};
|
|
@@ -19740,7 +19905,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
|
|
|
19740
19905
|
adminSettingsRoutes.get("/api/migrations/status", async (c) => {
|
|
19741
19906
|
try {
|
|
19742
19907
|
const db = c.env.DB;
|
|
19743
|
-
const migrationService = new
|
|
19908
|
+
const migrationService = new chunk3JMOWGUU_cjs.MigrationService(db);
|
|
19744
19909
|
const status = await migrationService.getMigrationStatus();
|
|
19745
19910
|
return c.json({
|
|
19746
19911
|
success: true,
|
|
@@ -19764,7 +19929,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
19764
19929
|
}, 403);
|
|
19765
19930
|
}
|
|
19766
19931
|
const db = c.env.DB;
|
|
19767
|
-
const migrationService = new
|
|
19932
|
+
const migrationService = new chunk3JMOWGUU_cjs.MigrationService(db);
|
|
19768
19933
|
const result = await migrationService.runPendingMigrations();
|
|
19769
19934
|
return c.json({
|
|
19770
19935
|
success: result.success,
|
|
@@ -19782,7 +19947,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
19782
19947
|
adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
|
|
19783
19948
|
try {
|
|
19784
19949
|
const db = c.env.DB;
|
|
19785
|
-
const migrationService = new
|
|
19950
|
+
const migrationService = new chunk3JMOWGUU_cjs.MigrationService(db);
|
|
19786
19951
|
const validation = await migrationService.validateSchema();
|
|
19787
19952
|
return c.json({
|
|
19788
19953
|
success: true,
|
|
@@ -19928,28 +20093,55 @@ adminSettingsRoutes.post("/api/database-tools/truncate", async (c) => {
|
|
|
19928
20093
|
}, 500);
|
|
19929
20094
|
}
|
|
19930
20095
|
});
|
|
19931
|
-
adminSettingsRoutes.post("/", async (c) => {
|
|
20096
|
+
adminSettingsRoutes.post("/general", async (c) => {
|
|
19932
20097
|
try {
|
|
20098
|
+
const user = c.get("user");
|
|
20099
|
+
if (!user || user.role !== "admin") {
|
|
20100
|
+
return c.json({
|
|
20101
|
+
success: false,
|
|
20102
|
+
error: "Unauthorized. Admin access required."
|
|
20103
|
+
}, 403);
|
|
20104
|
+
}
|
|
19933
20105
|
const formData = await c.req.formData();
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
|
|
19937
|
-
|
|
19938
|
-
|
|
19939
|
-
|
|
19940
|
-
|
|
19941
|
-
|
|
19942
|
-
|
|
19943
|
-
|
|
20106
|
+
const db = c.env.DB;
|
|
20107
|
+
const settingsService = new chunkDOR2IU73_cjs.SettingsService(db);
|
|
20108
|
+
const settings = {
|
|
20109
|
+
siteName: formData.get("siteName"),
|
|
20110
|
+
siteDescription: formData.get("siteDescription"),
|
|
20111
|
+
adminEmail: formData.get("adminEmail"),
|
|
20112
|
+
timezone: formData.get("timezone"),
|
|
20113
|
+
language: formData.get("language"),
|
|
20114
|
+
maintenanceMode: formData.get("maintenanceMode") === "true"
|
|
20115
|
+
};
|
|
20116
|
+
if (!settings.siteName || !settings.siteDescription) {
|
|
20117
|
+
return c.json({
|
|
20118
|
+
success: false,
|
|
20119
|
+
error: "Site name and description are required"
|
|
20120
|
+
}, 400);
|
|
20121
|
+
}
|
|
20122
|
+
const success = await settingsService.saveGeneralSettings(settings);
|
|
20123
|
+
if (success) {
|
|
20124
|
+
return c.json({
|
|
20125
|
+
success: true,
|
|
20126
|
+
message: "General settings saved successfully!"
|
|
20127
|
+
});
|
|
20128
|
+
} else {
|
|
20129
|
+
return c.json({
|
|
20130
|
+
success: false,
|
|
20131
|
+
error: "Failed to save settings"
|
|
20132
|
+
}, 500);
|
|
20133
|
+
}
|
|
19944
20134
|
} catch (error) {
|
|
19945
|
-
console.error("Error saving settings:", error);
|
|
19946
|
-
return c.
|
|
19947
|
-
|
|
19948
|
-
|
|
19949
|
-
|
|
19950
|
-
`);
|
|
20135
|
+
console.error("Error saving general settings:", error);
|
|
20136
|
+
return c.json({
|
|
20137
|
+
success: false,
|
|
20138
|
+
error: "Failed to save settings. Please try again."
|
|
20139
|
+
}, 500);
|
|
19951
20140
|
}
|
|
19952
20141
|
});
|
|
20142
|
+
adminSettingsRoutes.post("/", async (c) => {
|
|
20143
|
+
return c.redirect("/admin/settings/general");
|
|
20144
|
+
});
|
|
19953
20145
|
|
|
19954
20146
|
// src/routes/index.ts
|
|
19955
20147
|
var ROUTES_INFO = {
|
|
@@ -19999,5 +20191,5 @@ exports.api_system_default = api_system_default;
|
|
|
19999
20191
|
exports.auth_default = auth_default;
|
|
20000
20192
|
exports.router = router;
|
|
20001
20193
|
exports.userRoutes = userRoutes;
|
|
20002
|
-
//# sourceMappingURL=chunk-
|
|
20003
|
-
//# sourceMappingURL=chunk-
|
|
20194
|
+
//# sourceMappingURL=chunk-NPWWR6RI.cjs.map
|
|
20195
|
+
//# sourceMappingURL=chunk-NPWWR6RI.cjs.map
|