@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.
Files changed (46) hide show
  1. package/dist/{chunk-LEG4KNFP.cjs → chunk-3JMOWGUU.cjs} +20 -2
  2. package/dist/chunk-3JMOWGUU.cjs.map +1 -0
  3. package/dist/{chunk-LH4Z7QID.js → chunk-6FR25MPC.js} +111 -3
  4. package/dist/chunk-6FR25MPC.js.map +1 -0
  5. package/dist/{chunk-3NVJ6W27.cjs → chunk-DOR2IU73.cjs} +111 -2
  6. package/dist/chunk-DOR2IU73.cjs.map +1 -0
  7. package/dist/{chunk-M6FPVS7E.js → chunk-G5KY3WJV.js} +16 -29
  8. package/dist/chunk-G5KY3WJV.js.map +1 -0
  9. package/dist/{chunk-CDBVZEWR.js → chunk-HSRPDEQQ.js} +20 -2
  10. package/dist/chunk-HSRPDEQQ.js.map +1 -0
  11. package/dist/{chunk-4BJGEGX5.cjs → chunk-IM5SDXOE.cjs} +19 -32
  12. package/dist/chunk-IM5SDXOE.cjs.map +1 -0
  13. package/dist/{chunk-PPUKPNTP.js → chunk-LGC3TNCY.js} +293 -101
  14. package/dist/chunk-LGC3TNCY.js.map +1 -0
  15. package/dist/{chunk-5B3VMVEX.cjs → chunk-NPWWR6RI.cjs} +400 -208
  16. package/dist/chunk-NPWWR6RI.cjs.map +1 -0
  17. package/dist/{chunk-UL32L2KV.cjs → chunk-TRSHFTF6.cjs} +123 -3
  18. package/dist/chunk-TRSHFTF6.cjs.map +1 -0
  19. package/dist/{chunk-XJETEIRU.js → chunk-VSLEA22M.js} +123 -4
  20. package/dist/chunk-VSLEA22M.js.map +1 -0
  21. package/dist/index.cjs +876 -131
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.js +759 -13
  24. package/dist/index.js.map +1 -1
  25. package/dist/middleware.cjs +23 -23
  26. package/dist/middleware.js +2 -2
  27. package/dist/routes.cjs +25 -25
  28. package/dist/routes.js +5 -5
  29. package/dist/services.cjs +25 -21
  30. package/dist/services.js +2 -2
  31. package/dist/utils.cjs +11 -11
  32. package/dist/utils.js +1 -1
  33. package/migrations/006_plugin_system.sql +2 -2
  34. package/migrations/011_config_managed_collections.sql +1 -0
  35. package/migrations/018_settings_table.sql +23 -0
  36. package/package.json +1 -1
  37. package/dist/chunk-3NVJ6W27.cjs.map +0 -1
  38. package/dist/chunk-4BJGEGX5.cjs.map +0 -1
  39. package/dist/chunk-5B3VMVEX.cjs.map +0 -1
  40. package/dist/chunk-CDBVZEWR.js.map +0 -1
  41. package/dist/chunk-LEG4KNFP.cjs.map +0 -1
  42. package/dist/chunk-LH4Z7QID.js.map +0 -1
  43. package/dist/chunk-M6FPVS7E.js.map +0 -1
  44. package/dist/chunk-PPUKPNTP.js.map +0 -1
  45. package/dist/chunk-UL32L2KV.cjs.map +0 -1
  46. package/dist/chunk-XJETEIRU.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var chunk3NVJ6W27_cjs = require('./chunk-3NVJ6W27.cjs');
4
- var chunk4BJGEGX5_cjs = require('./chunk-4BJGEGX5.cjs');
5
- var chunkLEG4KNFP_cjs = require('./chunk-LEG4KNFP.cjs');
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 chunkUL32L2KV_cjs = require('./chunk-UL32L2KV.cjs');
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("/", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
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 || "unknown",
83
+ user?.userId || "system",
84
84
  now,
85
85
  now
86
86
  ).run();
87
- const cache = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.api);
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", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.api);
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", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.api);
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 chunk4BJGEGX5_cjs.isPluginActive(c.env.DB, "core-cache");
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.api);
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 = chunkUL32L2KV_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
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 chunkUL32L2KV_cjs.QueryFilterBuilder();
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.api);
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 = chunkUL32L2KV_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
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 chunkUL32L2KV_cjs.QueryFilterBuilder();
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.api);
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("*", chunk4BJGEGX5_cjs.requireAuth());
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}" created successfully`,
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("*", chunk4BJGEGX5_cjs.requireAuth());
1306
- adminApiRoutes.use("*", chunk4BJGEGX5_cjs.requireRole(["admin", "editor"]));
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
- display_name: zod.z.string().min(1).max(255),
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
- data: {
1527
- ...collection,
1528
- is_active: collection.is_active === 1,
1529
- managed: collection.managed === 1,
1530
- schema: collection.schema ? JSON.parse(collection.schema) : null,
1531
- created_at: Number(collection.created_at),
1532
- updated_at: Number(collection.updated_at),
1533
- fields
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 body = await c.req.json();
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
- validatedData.display_name,
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
- data: {
1603
- id: collectionId,
1604
- name: validatedData.name,
1605
- display_name: validatedData.display_name,
1606
- description: validatedData.description,
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
- if (collection) {
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword(password);
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 chunk4BJGEGX5_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer");
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.user);
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 chunk4BJGEGX5_cjs.AuthManager.verifyPassword(password, user.password_hash);
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 chunk4BJGEGX5_cjs.AuthManager.generateToken(user.id, user.email, user.role);
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", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
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", chunk4BJGEGX5_cjs.requireAuth(), async (c) => {
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 chunk4BJGEGX5_cjs.AuthManager.generateToken(user.userId, user.email, user.role);
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword(password);
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 chunk4BJGEGX5_cjs.AuthManager.generateToken(userId, normalizedEmail, "admin");
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/content';
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 chunk4BJGEGX5_cjs.AuthManager.verifyPassword(password, user.password_hash);
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 chunk4BJGEGX5_cjs.AuthManager.generateToken(user.id, user.email, user.role);
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/content';
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword("admin123");
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword(password);
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 chunk4BJGEGX5_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role);
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/content?welcome=true");
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword(password);
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("*", chunk4BJGEGX5_cjs.requireAuth());
4760
+ adminContentRoutes.use("*", chunkIM5SDXOE_cjs.requireAuth());
4738
4761
  async function getCollectionFields(db, collectionId) {
4739
- const cache = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.collection);
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.collection);
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.content);
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.content);
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.content);
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.content);
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 = chunk3NVJ6W27_cjs.getCacheService(chunk3NVJ6W27_cjs.CACHE_CONFIGS.content);
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
- <div 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">
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="${chunkUL32L2KV_cjs.escapeHtml(data.userToEdit.firstName || "")}"
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="${chunkUL32L2KV_cjs.escapeHtml(data.userToEdit.lastName || "")}"
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="${chunkUL32L2KV_cjs.escapeHtml(data.userToEdit.username || "")}"
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="${chunkUL32L2KV_cjs.escapeHtml(data.userToEdit.email || "")}"
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="${chunkUL32L2KV_cjs.escapeHtml(data.userToEdit.phone || "")}"
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="${chunkUL32L2KV_cjs.escapeHtml(role.value)}" ${data.userToEdit.role === role.value ? "selected" : ""}>${chunkUL32L2KV_cjs.escapeHtml(role.label)}</option>
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
- >${chunkUL32L2KV_cjs.escapeHtml(data.userToEdit.bio || "")}</textarea>
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("*", chunk4BJGEGX5_cjs.requireAuth());
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("first_name")?.toString());
7793
- const lastName = chunkUL32L2KV_cjs.sanitizeInput(formData.get("last_name")?.toString());
7794
- const username = chunkUL32L2KV_cjs.sanitizeInput(formData.get("username")?.toString());
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
7797
- const bio = chunkUL32L2KV_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
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 chunk4BJGEGX5_cjs.logActivity(
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
- await chunk4BJGEGX5_cjs.logActivity(
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
- return c.html(renderAlert2({
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 chunk4BJGEGX5_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword(newPassword);
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("first_name")?.toString());
8165
- const lastName = chunkUL32L2KV_cjs.sanitizeInput(formData.get("last_name")?.toString());
8166
- const username = chunkUL32L2KV_cjs.sanitizeInput(formData.get("username")?.toString());
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
8169
- const bio = chunkUL32L2KV_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
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 chunk4BJGEGX5_cjs.AuthManager.hashPassword(password);
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("first_name")?.toString());
8371
- const lastName = chunkUL32L2KV_cjs.sanitizeInput(formData.get("last_name")?.toString());
8372
- const username = chunkUL32L2KV_cjs.sanitizeInput(formData.get("username")?.toString());
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
8375
- const bio = chunkUL32L2KV_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 = chunkUL32L2KV_cjs.sanitizeInput(formData.get("first_name")?.toString());
8519
- const lastName = chunkUL32L2KV_cjs.sanitizeInput(formData.get("last_name")?.toString());
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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 chunk4BJGEGX5_cjs.logActivity(
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("*", chunk4BJGEGX5_cjs.requireAuth());
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
- GROUP BY folder
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("*", chunk4BJGEGX5_cjs.requireAuth());
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 chunkLEG4KNFP_cjs.PluginService(db);
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 chunkLEG4KNFP_cjs.PluginService(db);
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 chunkLEG4KNFP_cjs.PluginService(db);
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 chunkLEG4KNFP_cjs.PluginService(db);
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 chunkLEG4KNFP_cjs.PluginService(db);
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 chunkLEG4KNFP_cjs.PluginService(db);
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 chunkLEG4KNFP_cjs.PluginService(db);
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("*", chunk4BJGEGX5_cjs.requireAuth());
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunk3NVJ6W27_cjs.getLogger(c.env.DB);
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 = chunkUL32L2KV_cjs.getCoreVersion();
16377
+ var VERSION = chunkTRSHFTF6_cjs.getCoreVersion();
16241
16378
  var router = new hono.Hono();
16242
- router.use("*", chunk4BJGEGX5_cjs.requireAuth());
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 WHERE deleted_at IS NULL");
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 html9 = renderStatsCards({
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(html9);
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 html9 = renderStorageUsage(databaseSize, mediaSize);
16337
- return c.html(html9);
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 html9 = renderRecentActivity(activities);
16387
- return c.html(html9);
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 html9 = renderRecentActivity([]);
16391
- return c.html(html9);
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 html9 = `
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(html9);
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("*", chunk4BJGEGX5_cjs.requireAuth());
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
- try {
17878
- await c.env.CACHE_KV.delete("cache:collections:all");
17879
- await c.env.CACHE_KV.delete(`cache:collection:${name}`);
17880
- } catch (e) {
17881
- console.error("Error clearing cache:", e);
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
- // Simulate save (replace with actual API call)
18233
- setTimeout(() => {
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
- showNotification('Settings saved successfully!', 'success');
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("*", chunk4BJGEGX5_cjs.requireAuth());
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: getMockSettings(user),
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 chunkLEG4KNFP_cjs.MigrationService(db);
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 chunkLEG4KNFP_cjs.MigrationService(db);
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 chunkLEG4KNFP_cjs.MigrationService(db);
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
- return c.html(html.html`
19935
- <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
19936
- Settings saved successfully!
19937
- <script>
19938
- setTimeout(() => {
19939
- showNotification('Settings saved successfully!', 'success');
19940
- }, 100);
19941
- </script>
19942
- </div>
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.html(html.html`
19947
- <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
19948
- Failed to save settings. Please try again.
19949
- </div>
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-5B3VMVEX.cjs.map
20003
- //# sourceMappingURL=chunk-5B3VMVEX.cjs.map
20194
+ //# sourceMappingURL=chunk-NPWWR6RI.cjs.map
20195
+ //# sourceMappingURL=chunk-NPWWR6RI.cjs.map