@sonicjs-cms/core 2.10.1 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/{app-Ozl9agJG.d.cts → app-COElO4Rm.d.cts} +6 -1
  2. package/dist/{app-Ozl9agJG.d.ts → app-COElO4Rm.d.ts} +6 -1
  3. package/dist/{chunk-CJYFSKH7.js → chunk-2MXF4RYZ.js} +3 -3
  4. package/dist/{chunk-CJYFSKH7.js.map → chunk-2MXF4RYZ.js.map} +1 -1
  5. package/dist/{chunk-MNFY6DWY.cjs → chunk-56GUBLJE.cjs} +7 -7
  6. package/dist/{chunk-MNFY6DWY.cjs.map → chunk-56GUBLJE.cjs.map} +1 -1
  7. package/dist/{chunk-YXTFJPMN.js → chunk-6R6LAUR7.js} +3 -3
  8. package/dist/{chunk-YXTFJPMN.js.map → chunk-6R6LAUR7.js.map} +1 -1
  9. package/dist/{chunk-JJS7JZCH.js → chunk-76TX6XND.js} +4 -2
  10. package/dist/chunk-76TX6XND.js.map +1 -0
  11. package/dist/{chunk-EAJJHE5F.cjs → chunk-AG3SIPP7.cjs} +9 -2
  12. package/dist/chunk-AG3SIPP7.cjs.map +1 -0
  13. package/dist/{chunk-74XCYEI7.js → chunk-BWZBKLOC.js} +3 -3
  14. package/dist/{chunk-74XCYEI7.js.map → chunk-BWZBKLOC.js.map} +1 -1
  15. package/dist/{chunk-BUPNX3ZM.js → chunk-H3XXBAMO.js} +50 -2
  16. package/dist/chunk-H3XXBAMO.js.map +1 -0
  17. package/dist/{chunk-LTKV7AE5.cjs → chunk-H4NHRZ6Y.cjs} +4 -2
  18. package/dist/chunk-H4NHRZ6Y.cjs.map +1 -0
  19. package/dist/{chunk-JFMBYQTC.js → chunk-HXIYYE57.js} +6 -6
  20. package/dist/{chunk-JFMBYQTC.js.map → chunk-HXIYYE57.js.map} +1 -1
  21. package/dist/{chunk-TWCQVJ6M.cjs → chunk-I6FFGQIT.cjs} +50 -2
  22. package/dist/chunk-I6FFGQIT.cjs.map +1 -0
  23. package/dist/{chunk-SDAGUFOF.js → chunk-NDFHQOPP.js} +4318 -3820
  24. package/dist/chunk-NDFHQOPP.js.map +1 -0
  25. package/dist/{chunk-LFAQUR7P.cjs → chunk-NZWFCUDA.cjs} +26 -2
  26. package/dist/chunk-NZWFCUDA.cjs.map +1 -0
  27. package/dist/{chunk-KYGRJCZM.cjs → chunk-QTFKZBLC.cjs} +3 -2
  28. package/dist/chunk-QTFKZBLC.cjs.map +1 -0
  29. package/dist/{chunk-LOUJRBXV.js → chunk-QXOZI5Q2.js} +3 -2
  30. package/dist/chunk-QXOZI5Q2.js.map +1 -0
  31. package/dist/{chunk-3G7XX4UI.cjs → chunk-RBXFXT7H.cjs} +9 -9
  32. package/dist/{chunk-3G7XX4UI.cjs.map → chunk-RBXFXT7H.cjs.map} +1 -1
  33. package/dist/{chunk-E2GKK5HX.cjs → chunk-RXNLGINR.cjs} +3 -3
  34. package/dist/{chunk-E2GKK5HX.cjs.map → chunk-RXNLGINR.cjs.map} +1 -1
  35. package/dist/{chunk-VJCLJH3X.js → chunk-TBJY2FF7.js} +26 -2
  36. package/dist/chunk-TBJY2FF7.js.map +1 -0
  37. package/dist/{chunk-FW5CGNM2.js → chunk-U3ZMGBVC.js} +9 -2
  38. package/dist/chunk-U3ZMGBVC.js.map +1 -0
  39. package/dist/{chunk-5GO3AMON.cjs → chunk-VHNTCB2X.cjs} +10 -10
  40. package/dist/{chunk-5GO3AMON.cjs.map → chunk-VHNTCB2X.cjs.map} +1 -1
  41. package/dist/{chunk-HGKBMUYY.cjs → chunk-ZV6ZCJ74.cjs} +4498 -3996
  42. package/dist/chunk-ZV6ZCJ74.cjs.map +1 -0
  43. package/dist/index.cjs +2469 -240
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +175 -7
  46. package/dist/index.d.ts +175 -7
  47. package/dist/index.js +2294 -84
  48. package/dist/index.js.map +1 -1
  49. package/dist/middleware.cjs +29 -29
  50. package/dist/middleware.d.cts +1 -1
  51. package/dist/middleware.d.ts +1 -1
  52. package/dist/middleware.js +3 -3
  53. package/dist/migrations-USSEHJC7.js +4 -0
  54. package/dist/{migrations-ADK6YNM2.js.map → migrations-USSEHJC7.js.map} +1 -1
  55. package/dist/migrations-ZE6IZNLB.cjs +13 -0
  56. package/dist/{migrations-EM2D6EG2.cjs.map → migrations-ZE6IZNLB.cjs.map} +1 -1
  57. package/dist/{plugin-0Xogrln-.d.cts → plugin-DDYetMF-.d.cts} +1 -0
  58. package/dist/{plugin-0Xogrln-.d.ts → plugin-DDYetMF-.d.ts} +1 -0
  59. package/dist/{plugin-bootstrap-CD63DZ-p.d.ts → plugin-bootstrap-CZ1GDum7.d.ts} +803 -2
  60. package/dist/{plugin-bootstrap-B8PXeGj_.d.cts → plugin-bootstrap-DVGLQrcO.d.cts} +803 -2
  61. package/dist/{plugin-manager-GcIeb226.d.cts → plugin-manager-BoM3Q7o7.d.cts} +1 -1
  62. package/dist/{plugin-manager-Clf2gXwj.d.ts → plugin-manager-Efx9RyDX.d.ts} +1 -1
  63. package/dist/plugins.cjs +10 -10
  64. package/dist/plugins.d.cts +2 -2
  65. package/dist/plugins.d.ts +2 -2
  66. package/dist/plugins.js +2 -2
  67. package/dist/routes.cjs +30 -30
  68. package/dist/routes.d.cts +1 -1
  69. package/dist/routes.d.ts +1 -1
  70. package/dist/routes.js +7 -7
  71. package/dist/services.cjs +39 -39
  72. package/dist/services.d.cts +1 -1
  73. package/dist/services.d.ts +1 -1
  74. package/dist/services.js +3 -3
  75. package/dist/templates.cjs +19 -19
  76. package/dist/templates.js +2 -2
  77. package/dist/types.cjs +2 -2
  78. package/dist/types.d.cts +1 -1
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.js +1 -1
  81. package/dist/utils.cjs +11 -11
  82. package/dist/utils.js +1 -1
  83. package/migrations/034_security_audit_plugin.sql +27 -0
  84. package/package.json +1 -1
  85. package/dist/chunk-BUPNX3ZM.js.map +0 -1
  86. package/dist/chunk-EAJJHE5F.cjs.map +0 -1
  87. package/dist/chunk-FW5CGNM2.js.map +0 -1
  88. package/dist/chunk-HGKBMUYY.cjs.map +0 -1
  89. package/dist/chunk-JJS7JZCH.js.map +0 -1
  90. package/dist/chunk-KYGRJCZM.cjs.map +0 -1
  91. package/dist/chunk-LFAQUR7P.cjs.map +0 -1
  92. package/dist/chunk-LOUJRBXV.js.map +0 -1
  93. package/dist/chunk-LTKV7AE5.cjs.map +0 -1
  94. package/dist/chunk-SDAGUFOF.js.map +0 -1
  95. package/dist/chunk-TWCQVJ6M.cjs.map +0 -1
  96. package/dist/chunk-VJCLJH3X.js.map +0 -1
  97. package/dist/migrations-ADK6YNM2.js +0 -4
  98. package/dist/migrations-EM2D6EG2.cjs +0 -13
package/dist/index.cjs CHANGED
@@ -1,19 +1,19 @@
1
1
  'use strict';
2
2
 
3
- var chunkHGKBMUYY_cjs = require('./chunk-HGKBMUYY.cjs');
4
- var chunkLFAQUR7P_cjs = require('./chunk-LFAQUR7P.cjs');
5
- var chunk5GO3AMON_cjs = require('./chunk-5GO3AMON.cjs');
6
- var chunkTWCQVJ6M_cjs = require('./chunk-TWCQVJ6M.cjs');
7
- var chunkEAJJHE5F_cjs = require('./chunk-EAJJHE5F.cjs');
8
- var chunk3G7XX4UI_cjs = require('./chunk-3G7XX4UI.cjs');
9
- var chunkLTKV7AE5_cjs = require('./chunk-LTKV7AE5.cjs');
10
- var chunkMNFY6DWY_cjs = require('./chunk-MNFY6DWY.cjs');
3
+ var chunkZV6ZCJ74_cjs = require('./chunk-ZV6ZCJ74.cjs');
4
+ var chunkNZWFCUDA_cjs = require('./chunk-NZWFCUDA.cjs');
5
+ var chunkVHNTCB2X_cjs = require('./chunk-VHNTCB2X.cjs');
6
+ var chunkI6FFGQIT_cjs = require('./chunk-I6FFGQIT.cjs');
7
+ var chunkAG3SIPP7_cjs = require('./chunk-AG3SIPP7.cjs');
8
+ var chunkRBXFXT7H_cjs = require('./chunk-RBXFXT7H.cjs');
9
+ var chunkH4NHRZ6Y_cjs = require('./chunk-H4NHRZ6Y.cjs');
10
+ var chunk56GUBLJE_cjs = require('./chunk-56GUBLJE.cjs');
11
11
  var chunk6FHNRRJ3_cjs = require('./chunk-6FHNRRJ3.cjs');
12
- var chunkE2GKK5HX_cjs = require('./chunk-E2GKK5HX.cjs');
12
+ var chunkRXNLGINR_cjs = require('./chunk-RXNLGINR.cjs');
13
13
  require('./chunk-P3XDZL6Q.cjs');
14
14
  var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
15
15
  var chunkMNWKYY5E_cjs = require('./chunk-MNWKYY5E.cjs');
16
- var chunkKYGRJCZM_cjs = require('./chunk-KYGRJCZM.cjs');
16
+ var chunkQTFKZBLC_cjs = require('./chunk-QTFKZBLC.cjs');
17
17
  require('./chunk-IGJUBJBW.cjs');
18
18
  var hono = require('hono');
19
19
  var cookie = require('hono/cookie');
@@ -235,7 +235,7 @@ var DatabaseToolsService = class {
235
235
  };
236
236
 
237
237
  // src/templates/pages/admin-database-table.template.ts
238
- chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
238
+ chunkH4NHRZ6Y_cjs.init_admin_layout_catalyst_template();
239
239
  function renderDatabaseTablePage(data) {
240
240
  const totalPages = Math.ceil(data.totalRows / data.pageSize);
241
241
  const startRow = (data.currentPage - 1) * data.pageSize + 1;
@@ -484,7 +484,7 @@ function renderDatabaseTablePage(data) {
484
484
  user: data.user,
485
485
  content: pageContent
486
486
  };
487
- return chunkLTKV7AE5_cjs.renderAdminLayoutCatalyst(layoutData);
487
+ return chunkH4NHRZ6Y_cjs.renderAdminLayoutCatalyst(layoutData);
488
488
  }
489
489
  function generatePageNumbers(currentPage, totalPages) {
490
490
  const pages = [];
@@ -559,7 +559,7 @@ function formatCellValue(value) {
559
559
  // src/plugins/core-plugins/database-tools-plugin/admin-routes.ts
560
560
  function createDatabaseToolsAdminRoutes() {
561
561
  const router3 = new hono.Hono();
562
- router3.use("*", chunk5GO3AMON_cjs.requireAuth());
562
+ router3.use("*", chunkVHNTCB2X_cjs.requireAuth());
563
563
  router3.get("/api/stats", async (c) => {
564
564
  try {
565
565
  const user = c.get("user");
@@ -1776,7 +1776,7 @@ function createOTPLoginPlugin() {
1776
1776
  console.warn("Failed to parse OTP plugin settings, using defaults");
1777
1777
  }
1778
1778
  }
1779
- const settingsService = new chunkLFAQUR7P_cjs.SettingsService(db);
1779
+ const settingsService = new chunkNZWFCUDA_cjs.SettingsService(db);
1780
1780
  const generalSettings = await settingsService.getGeneralSettings();
1781
1781
  const siteName = generalSettings.siteName;
1782
1782
  const canRequest = await otpService.checkRateLimit(normalizedEmail, settings);
@@ -1822,7 +1822,8 @@ function createOTPLoginPlugin() {
1822
1822
  email: normalizedEmail,
1823
1823
  ipAddress,
1824
1824
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1825
- appName: siteName
1825
+ appName: siteName,
1826
+ logoUrl: settings.logoUrl || ""
1826
1827
  });
1827
1828
  const emailPlugin2 = await db.prepare(`
1828
1829
  SELECT settings FROM plugins WHERE id = 'email'
@@ -1925,7 +1926,7 @@ function createOTPLoginPlugin() {
1925
1926
  error: "Account is deactivated"
1926
1927
  }, 403);
1927
1928
  }
1928
- const token = await chunk5GO3AMON_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
1929
+ const token = await chunkVHNTCB2X_cjs.AuthManager.generateToken(user.id, user.email, user.role, c.env.JWT_SECRET);
1929
1930
  cookie.setCookie(c, "auth_token", token, {
1930
1931
  httpOnly: true,
1931
1932
  secure: true,
@@ -1997,6 +1998,567 @@ function createOTPLoginPlugin() {
1997
1998
  }
1998
1999
  var otpLoginPlugin = createOTPLoginPlugin();
1999
2000
 
2001
+ // src/plugins/core-plugins/oauth-providers/oauth-service.ts
2002
+ var GITHUB_PROVIDER = {
2003
+ id: "github",
2004
+ name: "GitHub",
2005
+ authorizeUrl: "https://github.com/login/oauth/authorize",
2006
+ tokenUrl: "https://github.com/login/oauth/access_token",
2007
+ userInfoUrl: "https://api.github.com/user",
2008
+ scopes: ["read:user", "user:email"],
2009
+ mapProfile: (profile) => ({
2010
+ providerAccountId: String(profile.id),
2011
+ email: profile.email || "",
2012
+ name: profile.name || profile.login || "",
2013
+ avatar: profile.avatar_url || void 0
2014
+ })
2015
+ };
2016
+ var GOOGLE_PROVIDER = {
2017
+ id: "google",
2018
+ name: "Google",
2019
+ authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
2020
+ tokenUrl: "https://oauth2.googleapis.com/token",
2021
+ userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
2022
+ scopes: ["openid", "email", "profile"],
2023
+ mapProfile: (profile) => ({
2024
+ providerAccountId: String(profile.id),
2025
+ email: profile.email || "",
2026
+ name: profile.name || "",
2027
+ avatar: profile.picture || void 0
2028
+ })
2029
+ };
2030
+ var BUILT_IN_PROVIDERS = {
2031
+ github: GITHUB_PROVIDER,
2032
+ google: GOOGLE_PROVIDER
2033
+ };
2034
+ var OAuthService = class {
2035
+ constructor(db) {
2036
+ this.db = db;
2037
+ }
2038
+ /**
2039
+ * Build the authorization redirect URL for a provider.
2040
+ */
2041
+ buildAuthorizeUrl(provider, clientId, redirectUri, state) {
2042
+ const params = new URLSearchParams({
2043
+ client_id: clientId,
2044
+ redirect_uri: redirectUri,
2045
+ response_type: "code",
2046
+ scope: provider.scopes.join(" "),
2047
+ state
2048
+ });
2049
+ if (provider.id === "google") {
2050
+ params.set("access_type", "offline");
2051
+ params.set("prompt", "consent");
2052
+ }
2053
+ return `${provider.authorizeUrl}?${params.toString()}`;
2054
+ }
2055
+ /**
2056
+ * Exchange authorization code for tokens using native fetch.
2057
+ */
2058
+ async exchangeCode(provider, clientId, clientSecret, code, redirectUri) {
2059
+ const body = {
2060
+ client_id: clientId,
2061
+ client_secret: clientSecret,
2062
+ code,
2063
+ redirect_uri: redirectUri,
2064
+ grant_type: "authorization_code"
2065
+ };
2066
+ const response = await fetch(provider.tokenUrl, {
2067
+ method: "POST",
2068
+ headers: {
2069
+ "Content-Type": "application/x-www-form-urlencoded",
2070
+ "Accept": "application/json"
2071
+ },
2072
+ body: new URLSearchParams(body).toString()
2073
+ });
2074
+ if (!response.ok) {
2075
+ const errorText = await response.text();
2076
+ throw new Error(`Token exchange failed (${response.status}): ${errorText}`);
2077
+ }
2078
+ const data = await response.json();
2079
+ if (data.error) {
2080
+ throw new Error(`Token exchange error: ${data.error_description || data.error}`);
2081
+ }
2082
+ return {
2083
+ access_token: data.access_token,
2084
+ refresh_token: data.refresh_token,
2085
+ expires_in: data.expires_in ? Number(data.expires_in) : void 0
2086
+ };
2087
+ }
2088
+ /**
2089
+ * Fetch user profile from the provider's userinfo endpoint.
2090
+ */
2091
+ async fetchUserProfile(provider, accessToken) {
2092
+ const headers = {
2093
+ "Authorization": `Bearer ${accessToken}`,
2094
+ "Accept": "application/json"
2095
+ };
2096
+ if (provider.id === "github") {
2097
+ headers["Authorization"] = `token ${accessToken}`;
2098
+ }
2099
+ const response = await fetch(provider.userInfoUrl, { headers });
2100
+ if (!response.ok) {
2101
+ throw new Error(`Failed to fetch user profile (${response.status})`);
2102
+ }
2103
+ const profile = await response.json();
2104
+ if (provider.id === "github" && !profile.email) {
2105
+ const emailResponse = await fetch("https://api.github.com/user/emails", {
2106
+ headers: {
2107
+ "Authorization": `token ${accessToken}`,
2108
+ "Accept": "application/json"
2109
+ }
2110
+ });
2111
+ if (emailResponse.ok) {
2112
+ const emails = await emailResponse.json();
2113
+ const primaryEmail = emails.find((e) => e.primary && e.verified);
2114
+ if (primaryEmail) {
2115
+ profile.email = primaryEmail.email;
2116
+ }
2117
+ }
2118
+ }
2119
+ return provider.mapProfile(profile);
2120
+ }
2121
+ // ─── Database Operations ────────────────────────────────────────────────
2122
+ /**
2123
+ * Find an existing OAuth account link.
2124
+ */
2125
+ async findOAuthAccount(provider, providerAccountId) {
2126
+ return await this.db.prepare(`
2127
+ SELECT * FROM oauth_accounts
2128
+ WHERE provider = ? AND provider_account_id = ?
2129
+ `).bind(provider, providerAccountId).first();
2130
+ }
2131
+ /**
2132
+ * Find all OAuth accounts for a user.
2133
+ */
2134
+ async findUserOAuthAccounts(userId) {
2135
+ const result = await this.db.prepare(`
2136
+ SELECT * FROM oauth_accounts WHERE user_id = ?
2137
+ `).bind(userId).all();
2138
+ return result.results || [];
2139
+ }
2140
+ /**
2141
+ * Create a new OAuth account link.
2142
+ */
2143
+ async createOAuthAccount(params) {
2144
+ const id = crypto.randomUUID();
2145
+ const now = Date.now();
2146
+ await this.db.prepare(`
2147
+ INSERT INTO oauth_accounts (
2148
+ id, user_id, provider, provider_account_id,
2149
+ access_token, refresh_token, token_expires_at,
2150
+ profile_data, created_at, updated_at
2151
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2152
+ `).bind(
2153
+ id,
2154
+ params.userId,
2155
+ params.provider,
2156
+ params.providerAccountId,
2157
+ params.accessToken,
2158
+ params.refreshToken || null,
2159
+ params.tokenExpiresAt || null,
2160
+ params.profileData || null,
2161
+ now,
2162
+ now
2163
+ ).run();
2164
+ return {
2165
+ id,
2166
+ user_id: params.userId,
2167
+ provider: params.provider,
2168
+ provider_account_id: params.providerAccountId,
2169
+ access_token: params.accessToken,
2170
+ refresh_token: params.refreshToken || null,
2171
+ token_expires_at: params.tokenExpiresAt || null,
2172
+ profile_data: params.profileData || null,
2173
+ created_at: now,
2174
+ updated_at: now
2175
+ };
2176
+ }
2177
+ /**
2178
+ * Update tokens for an existing OAuth account.
2179
+ */
2180
+ async updateOAuthTokens(id, accessToken, refreshToken, tokenExpiresAt) {
2181
+ await this.db.prepare(`
2182
+ UPDATE oauth_accounts
2183
+ SET access_token = ?, refresh_token = ?, token_expires_at = ?, updated_at = ?
2184
+ WHERE id = ?
2185
+ `).bind(accessToken, refreshToken || null, tokenExpiresAt || null, Date.now(), id).run();
2186
+ }
2187
+ /**
2188
+ * Unlink an OAuth account from a user (only if they have another auth method).
2189
+ */
2190
+ async unlinkOAuthAccount(userId, provider) {
2191
+ const user = await this.db.prepare(`
2192
+ SELECT password_hash FROM users WHERE id = ?
2193
+ `).bind(userId).first();
2194
+ const otherLinks = await this.db.prepare(`
2195
+ SELECT COUNT(*) as count FROM oauth_accounts
2196
+ WHERE user_id = ? AND provider != ?
2197
+ `).bind(userId, provider).first();
2198
+ const hasPassword = !!user?.password_hash;
2199
+ const hasOtherLinks = (otherLinks?.count || 0) > 0;
2200
+ if (!hasPassword && !hasOtherLinks) {
2201
+ return false;
2202
+ }
2203
+ await this.db.prepare(`
2204
+ DELETE FROM oauth_accounts WHERE user_id = ? AND provider = ?
2205
+ `).bind(userId, provider).run();
2206
+ return true;
2207
+ }
2208
+ /**
2209
+ * Find a user by email.
2210
+ */
2211
+ async findUserByEmail(email) {
2212
+ return await this.db.prepare(`
2213
+ SELECT id, email, role, is_active, first_name, last_name
2214
+ FROM users WHERE email = ?
2215
+ `).bind(email.toLowerCase()).first();
2216
+ }
2217
+ /**
2218
+ * Create a new user from an OAuth profile.
2219
+ */
2220
+ async createUserFromOAuth(profile) {
2221
+ const id = crypto.randomUUID();
2222
+ const now = Date.now();
2223
+ const email = profile.email.toLowerCase();
2224
+ const nameParts = (profile.name || email.split("@")[0] || "User").split(" ");
2225
+ const firstName = nameParts[0] || "User";
2226
+ const lastName = nameParts.slice(1).join(" ") || "";
2227
+ const username = email.split("@")[0] || id.substring(0, 8);
2228
+ const existing = await this.db.prepare(
2229
+ "SELECT id FROM users WHERE username = ?"
2230
+ ).bind(username).first();
2231
+ const finalUsername = existing ? `${username}-${id.substring(0, 6)}` : username;
2232
+ await this.db.prepare(`
2233
+ INSERT INTO users (
2234
+ id, email, username, first_name, last_name,
2235
+ password_hash, role, avatar, is_active, created_at, updated_at
2236
+ ) VALUES (?, ?, ?, ?, ?, NULL, 'viewer', ?, 1, ?, ?)
2237
+ `).bind(
2238
+ id,
2239
+ email,
2240
+ finalUsername,
2241
+ firstName,
2242
+ lastName,
2243
+ profile.avatar || null,
2244
+ now,
2245
+ now
2246
+ ).run();
2247
+ return id;
2248
+ }
2249
+ /**
2250
+ * Generate a cryptographically random state parameter for CSRF protection.
2251
+ */
2252
+ generateState() {
2253
+ const bytes = new Uint8Array(32);
2254
+ crypto.getRandomValues(bytes);
2255
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2256
+ }
2257
+ };
2258
+
2259
+ // src/plugins/core-plugins/oauth-providers/index.ts
2260
+ var STATE_COOKIE_NAME = "oauth_state";
2261
+ var STATE_COOKIE_MAX_AGE = 600;
2262
+ function createOAuthProvidersPlugin() {
2263
+ const builder = chunk6FHNRRJ3_cjs.PluginBuilder.create({
2264
+ name: "oauth-providers",
2265
+ version: "1.0.0-beta.1",
2266
+ description: "OAuth2/OIDC social login with GitHub, Google, and more"
2267
+ });
2268
+ builder.metadata({
2269
+ author: {
2270
+ name: "SonicJS Team",
2271
+ email: "team@sonicjs.com"
2272
+ },
2273
+ license: "MIT",
2274
+ compatibility: "^2.0.0"
2275
+ });
2276
+ function getCallbackUrl(c, provider) {
2277
+ const proto = c.req.header("x-forwarded-proto") || "https";
2278
+ const host = c.req.header("host") || "localhost";
2279
+ return `${proto}://${host}/auth/oauth/${provider}/callback`;
2280
+ }
2281
+ async function loadSettings(db) {
2282
+ const row = await db.prepare(
2283
+ `SELECT settings FROM plugins WHERE id = 'oauth-providers'`
2284
+ ).first();
2285
+ if (!row?.settings) return null;
2286
+ try {
2287
+ return JSON.parse(row.settings);
2288
+ } catch {
2289
+ return null;
2290
+ }
2291
+ }
2292
+ function getProviderCredentials(settings, providerId) {
2293
+ if (!settings?.providers?.[providerId]) return null;
2294
+ const p = settings.providers[providerId];
2295
+ if (!p.enabled || !p.clientId || !p.clientSecret) return null;
2296
+ return { clientId: p.clientId, clientSecret: p.clientSecret };
2297
+ }
2298
+ const oauthAPI = new hono.Hono();
2299
+ oauthAPI.get("/:provider", async (c) => {
2300
+ try {
2301
+ const providerId = c.req.param("provider");
2302
+ const providerConfig = BUILT_IN_PROVIDERS[providerId];
2303
+ if (!providerConfig) {
2304
+ return c.json({ error: `Unknown OAuth provider: ${providerId}` }, 400);
2305
+ }
2306
+ const db = c.env.DB;
2307
+ const settings = await loadSettings(db);
2308
+ const creds = getProviderCredentials(settings, providerId);
2309
+ if (!creds) {
2310
+ return c.json({
2311
+ error: `OAuth provider "${providerId}" is not configured or not enabled`
2312
+ }, 400);
2313
+ }
2314
+ const oauthService = new OAuthService(db);
2315
+ const state = oauthService.generateState();
2316
+ const redirectUri = getCallbackUrl(c, providerId);
2317
+ cookie.setCookie(c, STATE_COOKIE_NAME, state, {
2318
+ httpOnly: true,
2319
+ secure: true,
2320
+ sameSite: "Lax",
2321
+ // Lax required for OAuth redirect flow
2322
+ maxAge: STATE_COOKIE_MAX_AGE,
2323
+ path: "/auth/oauth"
2324
+ });
2325
+ const authorizeUrl = oauthService.buildAuthorizeUrl(
2326
+ providerConfig,
2327
+ creds.clientId,
2328
+ redirectUri,
2329
+ state
2330
+ );
2331
+ return c.redirect(authorizeUrl);
2332
+ } catch (error) {
2333
+ console.error("OAuth authorize error:", error);
2334
+ return c.json({ error: "Failed to initiate OAuth flow" }, 500);
2335
+ }
2336
+ });
2337
+ oauthAPI.get("/:provider/callback", async (c) => {
2338
+ try {
2339
+ const providerId = c.req.param("provider");
2340
+ const providerConfig = BUILT_IN_PROVIDERS[providerId];
2341
+ if (!providerConfig) {
2342
+ return c.redirect("/auth/login?error=Unknown OAuth provider");
2343
+ }
2344
+ const stateParam = c.req.query("state");
2345
+ const stateCookie = cookie.getCookie(c, STATE_COOKIE_NAME);
2346
+ if (!stateParam || !stateCookie || stateParam !== stateCookie) {
2347
+ return c.redirect("/auth/login?error=Invalid OAuth state. Please try again.");
2348
+ }
2349
+ cookie.setCookie(c, STATE_COOKIE_NAME, "", {
2350
+ httpOnly: true,
2351
+ secure: true,
2352
+ sameSite: "Lax",
2353
+ maxAge: 0,
2354
+ path: "/auth/oauth"
2355
+ });
2356
+ const errorParam = c.req.query("error");
2357
+ if (errorParam) {
2358
+ const errorDesc = c.req.query("error_description") || errorParam;
2359
+ return c.redirect(`/auth/login?error=${encodeURIComponent(errorDesc)}`);
2360
+ }
2361
+ const code = c.req.query("code");
2362
+ if (!code) {
2363
+ return c.redirect("/auth/login?error=No authorization code received");
2364
+ }
2365
+ const db = c.env.DB;
2366
+ const settings = await loadSettings(db);
2367
+ const creds = getProviderCredentials(settings, providerId);
2368
+ if (!creds) {
2369
+ return c.redirect("/auth/login?error=OAuth provider not configured");
2370
+ }
2371
+ const oauthService = new OAuthService(db);
2372
+ const redirectUri = getCallbackUrl(c, providerId);
2373
+ const tokens = await oauthService.exchangeCode(
2374
+ providerConfig,
2375
+ creds.clientId,
2376
+ creds.clientSecret,
2377
+ code,
2378
+ redirectUri
2379
+ );
2380
+ const profile = await oauthService.fetchUserProfile(providerConfig, tokens.access_token);
2381
+ if (!profile.email) {
2382
+ return c.redirect("/auth/login?error=Could not retrieve email from OAuth provider. Please ensure your email is public or grant email permission.");
2383
+ }
2384
+ const tokenExpiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : null;
2385
+ const existingOAuth = await oauthService.findOAuthAccount(providerId, profile.providerAccountId);
2386
+ if (existingOAuth) {
2387
+ await oauthService.updateOAuthTokens(
2388
+ existingOAuth.id,
2389
+ tokens.access_token,
2390
+ tokens.refresh_token,
2391
+ tokenExpiresAt ?? void 0
2392
+ );
2393
+ const user = await db.prepare(
2394
+ "SELECT id, email, role, is_active FROM users WHERE id = ?"
2395
+ ).bind(existingOAuth.user_id).first();
2396
+ if (!user || !user.is_active) {
2397
+ return c.redirect("/auth/login?error=Account is deactivated");
2398
+ }
2399
+ const jwt2 = await chunkVHNTCB2X_cjs.AuthManager.generateToken(
2400
+ user.id,
2401
+ user.email,
2402
+ user.role,
2403
+ c.env.JWT_SECRET
2404
+ );
2405
+ chunkVHNTCB2X_cjs.AuthManager.setAuthCookie(c, jwt2, { sameSite: "Lax" });
2406
+ return c.redirect("/admin");
2407
+ }
2408
+ const existingUser = await oauthService.findUserByEmail(profile.email);
2409
+ if (existingUser) {
2410
+ if (!existingUser.is_active) {
2411
+ return c.redirect("/auth/login?error=Account is deactivated");
2412
+ }
2413
+ await oauthService.createOAuthAccount({
2414
+ userId: existingUser.id,
2415
+ provider: providerId,
2416
+ providerAccountId: profile.providerAccountId,
2417
+ accessToken: tokens.access_token,
2418
+ refreshToken: tokens.refresh_token,
2419
+ tokenExpiresAt: tokenExpiresAt ?? void 0,
2420
+ profileData: JSON.stringify(profile)
2421
+ });
2422
+ const jwt2 = await chunkVHNTCB2X_cjs.AuthManager.generateToken(
2423
+ existingUser.id,
2424
+ existingUser.email,
2425
+ existingUser.role,
2426
+ c.env.JWT_SECRET
2427
+ );
2428
+ chunkVHNTCB2X_cjs.AuthManager.setAuthCookie(c, jwt2, { sameSite: "Lax" });
2429
+ return c.redirect("/admin");
2430
+ }
2431
+ const newUserId = await oauthService.createUserFromOAuth(profile);
2432
+ await oauthService.createOAuthAccount({
2433
+ userId: newUserId,
2434
+ provider: providerId,
2435
+ providerAccountId: profile.providerAccountId,
2436
+ accessToken: tokens.access_token,
2437
+ refreshToken: tokens.refresh_token,
2438
+ tokenExpiresAt: tokenExpiresAt ?? void 0,
2439
+ profileData: JSON.stringify(profile)
2440
+ });
2441
+ const jwt = await chunkVHNTCB2X_cjs.AuthManager.generateToken(
2442
+ newUserId,
2443
+ profile.email.toLowerCase(),
2444
+ "viewer",
2445
+ c.env.JWT_SECRET
2446
+ );
2447
+ chunkVHNTCB2X_cjs.AuthManager.setAuthCookie(c, jwt, { sameSite: "Lax" });
2448
+ return c.redirect("/admin");
2449
+ } catch (error) {
2450
+ console.error("OAuth callback error:", error);
2451
+ const message = error instanceof Error ? error.message : "OAuth authentication failed";
2452
+ return c.redirect(`/auth/login?error=${encodeURIComponent(message)}`);
2453
+ }
2454
+ });
2455
+ oauthAPI.post("/link", async (c) => {
2456
+ try {
2457
+ const user = c.get("user");
2458
+ if (!user) {
2459
+ return c.json({ error: "Authentication required" }, 401);
2460
+ }
2461
+ const body = await c.req.json();
2462
+ const { provider } = body;
2463
+ if (!provider || !BUILT_IN_PROVIDERS[provider]) {
2464
+ return c.json({ error: "Invalid provider" }, 400);
2465
+ }
2466
+ const db = c.env.DB;
2467
+ const settings = await loadSettings(db);
2468
+ const creds = getProviderCredentials(settings, provider);
2469
+ if (!creds) {
2470
+ return c.json({ error: `OAuth provider "${provider}" is not configured` }, 400);
2471
+ }
2472
+ const oauthService = new OAuthService(db);
2473
+ const state = oauthService.generateState();
2474
+ const redirectUri = getCallbackUrl(c, provider);
2475
+ cookie.setCookie(c, STATE_COOKIE_NAME, state, {
2476
+ httpOnly: true,
2477
+ secure: true,
2478
+ sameSite: "Lax",
2479
+ maxAge: STATE_COOKIE_MAX_AGE,
2480
+ path: "/auth/oauth"
2481
+ });
2482
+ const authorizeUrl = oauthService.buildAuthorizeUrl(
2483
+ BUILT_IN_PROVIDERS[provider],
2484
+ creds.clientId,
2485
+ redirectUri,
2486
+ state
2487
+ );
2488
+ return c.json({ redirectUrl: authorizeUrl });
2489
+ } catch (error) {
2490
+ console.error("OAuth link error:", error);
2491
+ return c.json({ error: "Failed to initiate account linking" }, 500);
2492
+ }
2493
+ });
2494
+ oauthAPI.post("/unlink", async (c) => {
2495
+ try {
2496
+ const user = c.get("user");
2497
+ if (!user) {
2498
+ return c.json({ error: "Authentication required" }, 401);
2499
+ }
2500
+ const body = await c.req.json();
2501
+ const { provider } = body;
2502
+ if (!provider) {
2503
+ return c.json({ error: "Provider is required" }, 400);
2504
+ }
2505
+ const db = c.env.DB;
2506
+ const oauthService = new OAuthService(db);
2507
+ const success = await oauthService.unlinkOAuthAccount(user.userId, provider);
2508
+ if (!success) {
2509
+ return c.json({
2510
+ error: "Cannot unlink the only authentication method. Set a password first."
2511
+ }, 400);
2512
+ }
2513
+ return c.json({ success: true, message: `${provider} account unlinked` });
2514
+ } catch (error) {
2515
+ console.error("OAuth unlink error:", error);
2516
+ return c.json({ error: "Failed to unlink account" }, 500);
2517
+ }
2518
+ });
2519
+ oauthAPI.get("/accounts", async (c) => {
2520
+ try {
2521
+ const user = c.get("user");
2522
+ if (!user) {
2523
+ return c.json({ error: "Authentication required" }, 401);
2524
+ }
2525
+ const db = c.env.DB;
2526
+ const oauthService = new OAuthService(db);
2527
+ const accounts = await oauthService.findUserOAuthAccounts(user.userId);
2528
+ return c.json({
2529
+ accounts: accounts.map((a) => ({
2530
+ provider: a.provider,
2531
+ providerAccountId: a.provider_account_id,
2532
+ linkedAt: a.created_at
2533
+ }))
2534
+ });
2535
+ } catch (error) {
2536
+ console.error("OAuth accounts error:", error);
2537
+ return c.json({ error: "Failed to fetch linked accounts" }, 500);
2538
+ }
2539
+ });
2540
+ builder.addRoute("/auth/oauth", oauthAPI, {
2541
+ description: "OAuth2 social login endpoints",
2542
+ requiresAuth: false,
2543
+ priority: 100
2544
+ });
2545
+ builder.addMenuItem("OAuth Providers", "/admin/plugins/oauth-providers", {
2546
+ icon: "shield",
2547
+ order: 86,
2548
+ permissions: ["oauth:manage"]
2549
+ });
2550
+ builder.lifecycle({
2551
+ activate: async () => {
2552
+ console.info("\u2705 OAuth Providers plugin activated");
2553
+ },
2554
+ deactivate: async () => {
2555
+ console.info("\u274C OAuth Providers plugin deactivated");
2556
+ }
2557
+ });
2558
+ return builder.build();
2559
+ }
2560
+ var oauthProvidersPlugin = createOAuthProvidersPlugin();
2561
+
2000
2562
  // src/plugins/core-plugins/ai-search-plugin/services/embedding.service.ts
2001
2563
  var EmbeddingService = class {
2002
2564
  constructor(ai) {
@@ -3564,7 +4126,7 @@ function renderSettingsPage(data) {
3564
4126
  }, 30000);
3565
4127
  </script>
3566
4128
  `;
3567
- return chunkLTKV7AE5_cjs.renderAdminLayout({
4129
+ return chunkH4NHRZ6Y_cjs.renderAdminLayout({
3568
4130
  title: "AI Search Settings",
3569
4131
  pageTitle: "AI Search Settings",
3570
4132
  currentPath: "/admin/plugins/ai-search/settings",
@@ -3575,7 +4137,7 @@ function renderSettingsPage(data) {
3575
4137
 
3576
4138
  // src/plugins/core-plugins/ai-search-plugin/routes/admin.ts
3577
4139
  var adminRoutes = new hono.Hono();
3578
- adminRoutes.use("*", chunk5GO3AMON_cjs.requireAuth());
4140
+ adminRoutes.use("*", chunkVHNTCB2X_cjs.requireAuth());
3579
4141
  adminRoutes.get("/", async (c) => {
3580
4142
  try {
3581
4143
  const user = c.get("user");
@@ -3976,13 +4538,13 @@ function createMagicLinkAuthPlugin() {
3976
4538
  SET used = 1, used_at = ?
3977
4539
  WHERE id = ?
3978
4540
  `).bind(Date.now(), magicLink.id).run();
3979
- const jwtToken = await chunk5GO3AMON_cjs.AuthManager.generateToken(
4541
+ const jwtToken = await chunkVHNTCB2X_cjs.AuthManager.generateToken(
3980
4542
  user.id,
3981
4543
  user.email,
3982
4544
  user.role,
3983
4545
  c.env.JWT_SECRET
3984
4546
  );
3985
- chunk5GO3AMON_cjs.AuthManager.setAuthCookie(c, jwtToken);
4547
+ chunkVHNTCB2X_cjs.AuthManager.setAuthCookie(c, jwtToken);
3986
4548
  await db.prepare(`
3987
4549
  UPDATE users SET last_login_at = ? WHERE id = ?
3988
4550
  `).bind(Date.now(), user.id).run();
@@ -4127,74 +4689,1704 @@ function renderMagicLinkEmail(magicLink, expiryMinutes) {
4127
4689
  }
4128
4690
  createMagicLinkAuthPlugin();
4129
4691
 
4130
- // src/plugins/cache/services/cache-config.ts
4131
- var CACHE_CONFIGS = {
4132
- // Content (high read, low write)
4133
- content: {
4134
- ttl: 3600,
4135
- // 1 hour
4136
- kvEnabled: true,
4137
- memoryEnabled: true,
4138
- namespace: "content",
4139
- invalidateOn: ["content.update", "content.delete", "content.publish"],
4140
- version: "v1"
4141
- },
4142
- // User data (medium read, medium write)
4143
- user: {
4144
- ttl: 900,
4145
- // 15 minutes
4146
- kvEnabled: true,
4147
- memoryEnabled: true,
4148
- namespace: "user",
4149
- invalidateOn: ["user.update", "user.delete", "auth.login"],
4150
- version: "v1"
4151
- },
4152
- // Configuration (high read, very low write)
4153
- config: {
4154
- ttl: 7200,
4155
- // 2 hours
4156
- kvEnabled: true,
4157
- memoryEnabled: true,
4158
- namespace: "config",
4159
- invalidateOn: ["config.update", "plugin.activate", "plugin.deactivate"],
4160
- version: "v1"
4161
- },
4162
- // Media metadata (high read, low write)
4163
- media: {
4164
- ttl: 3600,
4165
- // 1 hour
4166
- kvEnabled: true,
4167
- memoryEnabled: true,
4168
- namespace: "media",
4169
- invalidateOn: ["media.upload", "media.delete", "media.update"],
4170
- version: "v1"
4171
- },
4172
- // API responses (very high read, low write)
4173
- api: {
4174
- ttl: 300,
4175
- // 5 minutes
4176
- kvEnabled: true,
4177
- memoryEnabled: true,
4178
- namespace: "api",
4179
- invalidateOn: ["content.update", "content.publish"],
4180
- version: "v1"
4692
+ // src/plugins/core-plugins/security-audit-plugin/types.ts
4693
+ var DEFAULT_SETTINGS2 = {
4694
+ retention: {
4695
+ daysToKeep: 90,
4696
+ maxEvents: 1e5,
4697
+ autoPurge: true
4181
4698
  },
4182
- // Session data (very high read, medium write)
4183
- session: {
4184
- ttl: 1800,
4185
- // 30 minutes
4186
- kvEnabled: false,
4187
- // Only in-memory for sessions
4188
- memoryEnabled: true,
4189
- namespace: "session",
4190
- invalidateOn: ["auth.logout"],
4191
- version: "v1"
4699
+ bruteForce: {
4700
+ enabled: true,
4701
+ maxFailedAttemptsPerIP: 10,
4702
+ maxFailedAttemptsPerEmail: 5,
4703
+ windowMinutes: 15,
4704
+ lockoutDurationMinutes: 30,
4705
+ alertThreshold: 20
4192
4706
  },
4193
- // Plugin data
4194
- plugin: {
4195
- ttl: 3600,
4196
- // 1 hour
4197
- kvEnabled: true,
4707
+ logging: {
4708
+ logSuccessfulLogins: true,
4709
+ logLogouts: true,
4710
+ logRegistrations: true,
4711
+ logPasswordResets: true,
4712
+ logPermissionDenied: true
4713
+ }
4714
+ };
4715
+
4716
+ // src/plugins/core-plugins/security-audit-plugin/services/security-audit-service.ts
4717
+ var SecurityAuditService = class {
4718
+ constructor(db, settings = DEFAULT_SETTINGS2) {
4719
+ this.db = db;
4720
+ this.settings = settings;
4721
+ }
4722
+ async logEvent(event) {
4723
+ const id = crypto.randomUUID();
4724
+ const now = Date.now();
4725
+ await this.db.prepare(`
4726
+ INSERT INTO security_events (id, event_type, severity, user_id, email, ip_address, user_agent, country_code, request_path, request_method, details, fingerprint, blocked, created_at)
4727
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4728
+ `).bind(
4729
+ id,
4730
+ event.eventType,
4731
+ event.severity || "info",
4732
+ event.userId || null,
4733
+ event.email || null,
4734
+ event.ipAddress || null,
4735
+ event.userAgent || null,
4736
+ event.countryCode || null,
4737
+ event.requestPath || null,
4738
+ event.requestMethod || null,
4739
+ event.details ? JSON.stringify(event.details) : null,
4740
+ event.fingerprint || null,
4741
+ event.blocked ? 1 : 0,
4742
+ now
4743
+ ).run();
4744
+ return id;
4745
+ }
4746
+ async getEvents(filters = {}) {
4747
+ const conditions = [];
4748
+ const params = [];
4749
+ if (filters.eventType) {
4750
+ if (Array.isArray(filters.eventType)) {
4751
+ conditions.push(`event_type IN (${filters.eventType.map(() => "?").join(",")})`);
4752
+ params.push(...filters.eventType);
4753
+ } else {
4754
+ conditions.push("event_type = ?");
4755
+ params.push(filters.eventType);
4756
+ }
4757
+ }
4758
+ if (filters.severity) {
4759
+ if (Array.isArray(filters.severity)) {
4760
+ conditions.push(`severity IN (${filters.severity.map(() => "?").join(",")})`);
4761
+ params.push(...filters.severity);
4762
+ } else {
4763
+ conditions.push("severity = ?");
4764
+ params.push(filters.severity);
4765
+ }
4766
+ }
4767
+ if (filters.email) {
4768
+ conditions.push("email LIKE ?");
4769
+ params.push(`%${filters.email}%`);
4770
+ }
4771
+ if (filters.ipAddress) {
4772
+ conditions.push("ip_address LIKE ?");
4773
+ params.push(`%${filters.ipAddress}%`);
4774
+ }
4775
+ if (filters.search) {
4776
+ conditions.push("(email LIKE ? OR ip_address LIKE ? OR details LIKE ?)");
4777
+ params.push(`%${filters.search}%`, `%${filters.search}%`, `%${filters.search}%`);
4778
+ }
4779
+ if (filters.startDate) {
4780
+ conditions.push("created_at >= ?");
4781
+ params.push(filters.startDate);
4782
+ }
4783
+ if (filters.endDate) {
4784
+ conditions.push("created_at <= ?");
4785
+ params.push(filters.endDate);
4786
+ }
4787
+ if (filters.blocked !== void 0) {
4788
+ conditions.push("blocked = ?");
4789
+ params.push(filters.blocked ? 1 : 0);
4790
+ }
4791
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4792
+ const sortBy = filters.sortBy || "created_at";
4793
+ const sortOrder = filters.sortOrder || "desc";
4794
+ const page = filters.page || 1;
4795
+ const limit = filters.limit || 50;
4796
+ const offset = (page - 1) * limit;
4797
+ const countResult = await this.db.prepare(
4798
+ `SELECT COUNT(*) as count FROM security_events ${where}`
4799
+ ).bind(...params).first();
4800
+ const total = countResult?.count || 0;
4801
+ const results = await this.db.prepare(
4802
+ `SELECT * FROM security_events ${where} ORDER BY ${sortBy} ${sortOrder} LIMIT ? OFFSET ?`
4803
+ ).bind(...params, limit, offset).all();
4804
+ const events = (results.results || []).map((row) => ({
4805
+ id: row.id,
4806
+ eventType: row.event_type,
4807
+ severity: row.severity,
4808
+ userId: row.user_id,
4809
+ email: row.email,
4810
+ ipAddress: row.ip_address,
4811
+ userAgent: row.user_agent,
4812
+ countryCode: row.country_code,
4813
+ requestPath: row.request_path,
4814
+ requestMethod: row.request_method,
4815
+ details: row.details ? JSON.parse(row.details) : null,
4816
+ fingerprint: row.fingerprint,
4817
+ blocked: !!row.blocked,
4818
+ createdAt: row.created_at
4819
+ }));
4820
+ return { events, total };
4821
+ }
4822
+ async getEvent(id) {
4823
+ const row = await this.db.prepare(
4824
+ "SELECT * FROM security_events WHERE id = ?"
4825
+ ).bind(id).first();
4826
+ if (!row) return null;
4827
+ return {
4828
+ id: row.id,
4829
+ eventType: row.event_type,
4830
+ severity: row.severity,
4831
+ userId: row.user_id,
4832
+ email: row.email,
4833
+ ipAddress: row.ip_address,
4834
+ userAgent: row.user_agent,
4835
+ countryCode: row.country_code,
4836
+ requestPath: row.request_path,
4837
+ requestMethod: row.request_method,
4838
+ details: row.details ? JSON.parse(row.details) : null,
4839
+ fingerprint: row.fingerprint,
4840
+ blocked: !!row.blocked,
4841
+ createdAt: row.created_at
4842
+ };
4843
+ }
4844
+ async getStats() {
4845
+ const now = Date.now();
4846
+ const h24 = now - 24 * 60 * 60 * 1e3;
4847
+ const h48 = now - 48 * 60 * 60 * 1e3;
4848
+ const totalResult = await this.db.prepare(
4849
+ "SELECT COUNT(*) as count FROM security_events"
4850
+ ).first();
4851
+ const failed24hResult = await this.db.prepare(
4852
+ "SELECT COUNT(*) as count FROM security_events WHERE event_type = 'login_failure' AND created_at >= ?"
4853
+ ).bind(h24).first();
4854
+ const failedPrior24hResult = await this.db.prepare(
4855
+ "SELECT COUNT(*) as count FROM security_events WHERE event_type = 'login_failure' AND created_at >= ? AND created_at < ?"
4856
+ ).bind(h48, h24).first();
4857
+ const failed24h = failed24hResult?.count || 0;
4858
+ const failedPrior24h = failedPrior24hResult?.count || 0;
4859
+ const trend = failedPrior24h > 0 ? Math.round((failed24h - failedPrior24h) / failedPrior24h * 100) : failed24h > 0 ? 100 : 0;
4860
+ const lockoutWindow = now - this.settings.bruteForce.lockoutDurationMinutes * 60 * 1e3;
4861
+ const lockoutsResult = await this.db.prepare(
4862
+ "SELECT COUNT(DISTINCT ip_address) as count FROM security_events WHERE event_type = 'account_lockout' AND created_at >= ?"
4863
+ ).bind(lockoutWindow).first();
4864
+ const windowStart = now - this.settings.bruteForce.windowMinutes * 60 * 1e3;
4865
+ const flaggedResult = await this.db.prepare(
4866
+ `SELECT COUNT(*) as count FROM (
4867
+ SELECT ip_address FROM security_events
4868
+ WHERE event_type = 'login_failure' AND created_at >= ?
4869
+ GROUP BY ip_address HAVING COUNT(*) >= ?
4870
+ )`
4871
+ ).bind(windowStart, this.settings.bruteForce.maxFailedAttemptsPerIP).first();
4872
+ const typeResults = await this.db.prepare(
4873
+ "SELECT event_type, COUNT(*) as count FROM security_events WHERE created_at >= ? GROUP BY event_type"
4874
+ ).bind(h24).all();
4875
+ const eventsByType = {};
4876
+ for (const row of typeResults.results || []) {
4877
+ eventsByType[row.event_type] = row.count;
4878
+ }
4879
+ const severityResults = await this.db.prepare(
4880
+ "SELECT severity, COUNT(*) as count FROM security_events WHERE created_at >= ? GROUP BY severity"
4881
+ ).bind(h24).all();
4882
+ const eventsBySeverity = {};
4883
+ for (const row of severityResults.results || []) {
4884
+ eventsBySeverity[row.severity] = row.count;
4885
+ }
4886
+ return {
4887
+ totalEvents: totalResult?.count || 0,
4888
+ failedLogins24h: failed24h,
4889
+ failedLoginsTrend: trend,
4890
+ activeLockouts: lockoutsResult?.count || 0,
4891
+ flaggedIPs: flaggedResult?.count || 0,
4892
+ eventsByType,
4893
+ eventsBySeverity
4894
+ };
4895
+ }
4896
+ async getTopIPs(limit = 10) {
4897
+ const now = Date.now();
4898
+ const h24 = now - 24 * 60 * 60 * 1e3;
4899
+ const results = await this.db.prepare(`
4900
+ SELECT
4901
+ ip_address,
4902
+ country_code,
4903
+ COUNT(*) as failed_attempts,
4904
+ MAX(created_at) as last_seen
4905
+ FROM security_events
4906
+ WHERE event_type = 'login_failure' AND created_at >= ?
4907
+ GROUP BY ip_address
4908
+ ORDER BY failed_attempts DESC
4909
+ LIMIT ?
4910
+ `).bind(h24, limit).all();
4911
+ const lockoutWindow = now - this.settings.bruteForce.lockoutDurationMinutes * 60 * 1e3;
4912
+ const lockoutResults = await this.db.prepare(
4913
+ "SELECT DISTINCT ip_address FROM security_events WHERE event_type = 'account_lockout' AND created_at >= ?"
4914
+ ).bind(lockoutWindow).all();
4915
+ const lockedIPs = new Set((lockoutResults.results || []).map((r) => r.ip_address));
4916
+ return (results.results || []).map((row) => ({
4917
+ ipAddress: row.ip_address,
4918
+ countryCode: row.country_code,
4919
+ failedAttempts: row.failed_attempts,
4920
+ lastSeen: row.last_seen,
4921
+ locked: lockedIPs.has(row.ip_address)
4922
+ }));
4923
+ }
4924
+ async getHourlyTrend(hours = 24) {
4925
+ const now = Date.now();
4926
+ const start = now - hours * 60 * 60 * 1e3;
4927
+ const buckets = [];
4928
+ for (let i = 0; i < hours; i++) {
4929
+ const bucketStart = start + i * 60 * 60 * 1e3;
4930
+ const date = new Date(bucketStart);
4931
+ buckets.push({
4932
+ hour: `${date.getUTCHours().toString().padStart(2, "0")}:00`,
4933
+ count: 0
4934
+ });
4935
+ }
4936
+ const results = await this.db.prepare(`
4937
+ SELECT
4938
+ CAST((created_at - ?) / 3600000 AS INTEGER) as bucket,
4939
+ COUNT(*) as count
4940
+ FROM security_events
4941
+ WHERE event_type = 'login_failure' AND created_at >= ?
4942
+ GROUP BY bucket
4943
+ ORDER BY bucket
4944
+ `).bind(start, start).all();
4945
+ for (const row of results.results || []) {
4946
+ const idx = row.bucket;
4947
+ if (idx >= 0 && idx < buckets.length) {
4948
+ buckets[idx].count = row.count;
4949
+ }
4950
+ }
4951
+ return buckets;
4952
+ }
4953
+ async purgeOldEvents(daysToKeep) {
4954
+ const days = daysToKeep || this.settings.retention.daysToKeep;
4955
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
4956
+ const result = await this.db.prepare(
4957
+ "DELETE FROM security_events WHERE created_at < ?"
4958
+ ).bind(cutoff).run();
4959
+ return result.meta?.changes || 0;
4960
+ }
4961
+ async getRecentCriticalEvents(limit = 20) {
4962
+ const results = await this.db.prepare(
4963
+ "SELECT * FROM security_events WHERE severity = 'critical' ORDER BY created_at DESC LIMIT ?"
4964
+ ).bind(limit).all();
4965
+ return (results.results || []).map((row) => ({
4966
+ id: row.id,
4967
+ eventType: row.event_type,
4968
+ severity: row.severity,
4969
+ userId: row.user_id,
4970
+ email: row.email,
4971
+ ipAddress: row.ip_address,
4972
+ userAgent: row.user_agent,
4973
+ countryCode: row.country_code,
4974
+ requestPath: row.request_path,
4975
+ requestMethod: row.request_method,
4976
+ details: row.details ? JSON.parse(row.details) : null,
4977
+ fingerprint: row.fingerprint,
4978
+ blocked: !!row.blocked,
4979
+ createdAt: row.created_at
4980
+ }));
4981
+ }
4982
+ };
4983
+
4984
+ // src/plugins/core-plugins/security-audit-plugin/components/dashboard-page.ts
4985
+ chunkH4NHRZ6Y_cjs.init_admin_layout_catalyst_template();
4986
+ function formatTimestamp(ts) {
4987
+ const date = new Date(ts);
4988
+ const now = Date.now();
4989
+ const diff = now - ts;
4990
+ if (diff < 6e4) return "just now";
4991
+ if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
4992
+ if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
4993
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
4994
+ }
4995
+ function severityBadge(severity) {
4996
+ const colors = {
4997
+ info: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
4998
+ warning: "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400",
4999
+ critical: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
5000
+ };
5001
+ return `<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ${colors[severity] || colors.info}">${severity}</span>`;
5002
+ }
5003
+ function eventTypeBadge(type) {
5004
+ const labels = {
5005
+ login_success: "Login OK",
5006
+ login_failure: "Login Failed",
5007
+ registration: "Registration",
5008
+ account_lockout: "Lockout",
5009
+ suspicious_activity: "Suspicious",
5010
+ logout: "Logout",
5011
+ password_reset_request: "Password Reset",
5012
+ permission_denied: "Access Denied"
5013
+ };
5014
+ return labels[type] || type;
5015
+ }
5016
+ function trendArrow(trend) {
5017
+ if (trend > 0) return `<span class="text-red-500">+${trend}%</span>`;
5018
+ if (trend < 0) return `<span class="text-emerald-500">${trend}%</span>`;
5019
+ return `<span class="text-zinc-400">0%</span>`;
5020
+ }
5021
+ function renderBarChart(data) {
5022
+ if (data.length === 0) return '<p class="text-zinc-500 text-sm">No data available</p>';
5023
+ const max = Math.max(...data.map((d) => d.count), 1);
5024
+ const bars = data.map((d) => {
5025
+ const height = Math.max(d.count / max * 100, 2);
5026
+ const color = d.count === 0 ? "bg-zinc-200 dark:bg-zinc-700" : d.count >= max * 0.75 ? "bg-red-500" : d.count >= max * 0.5 ? "bg-amber-500" : "bg-cyan-500";
5027
+ return `
5028
+ <div class="flex flex-col items-center flex-1 min-w-0 group relative">
5029
+ <div class="w-full flex flex-col items-center justify-end" style="height: 120px">
5030
+ <div class="absolute bottom-8 hidden group-hover:block bg-zinc-900 text-white text-xs rounded px-2 py-1 whitespace-nowrap z-10">
5031
+ ${d.hour}: ${d.count} failed
5032
+ </div>
5033
+ <div class="${color} w-full max-w-[12px] rounded-t transition-all" style="height: ${height}%"></div>
5034
+ </div>
5035
+ <span class="text-[9px] text-zinc-400 mt-1 ${data.length > 12 ? "hidden sm:block" : ""}">${d.hour}</span>
5036
+ </div>
5037
+ `;
5038
+ }).join("");
5039
+ return `<div class="flex items-end gap-px">${bars}</div>`;
5040
+ }
5041
+ function renderSecurityDashboard(data) {
5042
+ const { stats, topIPs, hourlyTrend, recentCritical, user, version, dynamicMenuItems } = data;
5043
+ const content2 = `
5044
+ <div>
5045
+ <div class="sm:flex sm:items-center sm:justify-between mb-6">
5046
+ <div class="sm:flex-auto">
5047
+ <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">Security Dashboard</h1>
5048
+ <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
5049
+ Monitor login attempts, brute-force detection, and security events.
5050
+ </p>
5051
+ </div>
5052
+ <div class="mt-4 sm:mt-0 sm:ml-16 flex gap-x-2">
5053
+ <a href="/admin/plugins/security-audit/events"
5054
+ class="inline-flex items-center justify-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white hover:bg-zinc-50 dark:hover:bg-zinc-700 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 transition-colors shadow-sm">
5055
+ View Event Log
5056
+ </a>
5057
+ <a href="/api/security-audit/export?format=csv"
5058
+ class="inline-flex items-center justify-center rounded-lg bg-zinc-950 dark:bg-white px-3.5 py-2.5 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors shadow-sm">
5059
+ Export CSV
5060
+ </a>
5061
+ </div>
5062
+ </div>
5063
+
5064
+ <!-- Summary Cards -->
5065
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4 mb-6">
5066
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5067
+ <p class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Total Events</p>
5068
+ <p class="mt-2 text-3xl font-bold text-zinc-950 dark:text-white">${stats.totalEvents.toLocaleString()}</p>
5069
+ </div>
5070
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5071
+ <p class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Failed Logins (24h)</p>
5072
+ <p class="mt-2 text-3xl font-bold text-zinc-950 dark:text-white">
5073
+ ${stats.failedLogins24h}
5074
+ <span class="ml-2 text-sm font-normal">${trendArrow(stats.failedLoginsTrend)}</span>
5075
+ </p>
5076
+ </div>
5077
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5078
+ <p class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Active Lockouts</p>
5079
+ <p class="mt-2 text-3xl font-bold ${stats.activeLockouts > 0 ? "text-red-600 dark:text-red-400" : "text-zinc-950 dark:text-white"}">${stats.activeLockouts}</p>
5080
+ </div>
5081
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5082
+ <p class="text-sm font-medium text-zinc-500 dark:text-zinc-400">Flagged IPs</p>
5083
+ <p class="mt-2 text-3xl font-bold ${stats.flaggedIPs > 0 ? "text-amber-600 dark:text-amber-400" : "text-zinc-950 dark:text-white"}">${stats.flaggedIPs}</p>
5084
+ </div>
5085
+ </div>
5086
+
5087
+ <!-- Charts Row -->
5088
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
5089
+ <!-- Failed Login Trend -->
5090
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5091
+ <h2 class="text-sm font-semibold text-zinc-950 dark:text-white mb-4">Failed Login Attempts (24h)</h2>
5092
+ ${renderBarChart(hourlyTrend)}
5093
+ </div>
5094
+
5095
+ <!-- Events by Type -->
5096
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5097
+ <h2 class="text-sm font-semibold text-zinc-950 dark:text-white mb-4">Events by Type (24h)</h2>
5098
+ <div class="space-y-3">
5099
+ ${Object.entries(stats.eventsByType).length === 0 ? '<p class="text-zinc-500 text-sm">No events in the last 24 hours</p>' : Object.entries(stats.eventsByType).sort(([, a], [, b]) => b - a).map(([type, count]) => {
5100
+ const total = Object.values(stats.eventsByType).reduce((s, v) => s + v, 0);
5101
+ const pct = total > 0 ? Math.round(count / total * 100) : 0;
5102
+ return `
5103
+ <div>
5104
+ <div class="flex justify-between text-sm mb-1">
5105
+ <span class="text-zinc-600 dark:text-zinc-300">${eventTypeBadge(type)}</span>
5106
+ <span class="text-zinc-500 dark:text-zinc-400">${count}</span>
5107
+ </div>
5108
+ <div class="w-full bg-zinc-100 dark:bg-zinc-800 rounded-full h-1.5">
5109
+ <div class="h-1.5 rounded-full ${type === "login_failure" ? "bg-red-500" : type === "login_success" ? "bg-emerald-500" : "bg-cyan-500"}" style="width: ${pct}%"></div>
5110
+ </div>
5111
+ </div>
5112
+ `;
5113
+ }).join("")}
5114
+ </div>
5115
+ </div>
5116
+ </div>
5117
+
5118
+ <!-- Top IPs and Recent Critical -->
5119
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
5120
+ <!-- Top IPs -->
5121
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm overflow-hidden">
5122
+ <div class="p-5 border-b border-zinc-100 dark:border-zinc-800">
5123
+ <h2 class="text-sm font-semibold text-zinc-950 dark:text-white">Top IPs by Failed Logins (24h)</h2>
5124
+ </div>
5125
+ ${topIPs.length === 0 ? '<div class="p-5"><p class="text-zinc-500 text-sm">No failed login attempts</p></div>' : `<table class="min-w-full">
5126
+ <thead>
5127
+ <tr class="border-b border-zinc-100 dark:border-zinc-800">
5128
+ <th class="px-5 py-3 text-left text-xs font-medium text-zinc-500 uppercase">IP Address</th>
5129
+ <th class="px-5 py-3 text-left text-xs font-medium text-zinc-500 uppercase">Country</th>
5130
+ <th class="px-5 py-3 text-right text-xs font-medium text-zinc-500 uppercase">Attempts</th>
5131
+ <th class="px-5 py-3 text-right text-xs font-medium text-zinc-500 uppercase">Status</th>
5132
+ </tr>
5133
+ </thead>
5134
+ <tbody>
5135
+ ${topIPs.map((ip) => `
5136
+ <tr class="border-b border-zinc-50 dark:border-zinc-800/50 hover:bg-zinc-50 dark:hover:bg-zinc-800/50">
5137
+ <td class="px-5 py-3 text-sm font-mono text-zinc-900 dark:text-zinc-100">${ip.ipAddress}</td>
5138
+ <td class="px-5 py-3 text-sm text-zinc-600 dark:text-zinc-400">${ip.countryCode || "-"}</td>
5139
+ <td class="px-5 py-3 text-sm text-right font-semibold ${ip.failedAttempts >= 10 ? "text-red-600 dark:text-red-400" : "text-zinc-900 dark:text-zinc-100"}">${ip.failedAttempts}</td>
5140
+ <td class="px-5 py-3 text-sm text-right">
5141
+ ${ip.locked ? '<span class="inline-flex items-center rounded-md bg-red-100 dark:bg-red-900/30 px-2 py-1 text-xs font-medium text-red-700 dark:text-red-400">Locked</span>' : '<span class="inline-flex items-center rounded-md bg-emerald-100 dark:bg-emerald-900/30 px-2 py-1 text-xs font-medium text-emerald-700 dark:text-emerald-400">Active</span>'}
5142
+ </td>
5143
+ </tr>
5144
+ `).join("")}
5145
+ </tbody>
5146
+ </table>`}
5147
+ </div>
5148
+
5149
+ <!-- Recent Critical Events -->
5150
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm overflow-hidden">
5151
+ <div class="p-5 border-b border-zinc-100 dark:border-zinc-800">
5152
+ <h2 class="text-sm font-semibold text-zinc-950 dark:text-white">Recent Critical Events</h2>
5153
+ </div>
5154
+ ${recentCritical.length === 0 ? '<div class="p-5"><p class="text-zinc-500 text-sm">No critical events</p></div>' : `<div class="divide-y divide-zinc-100 dark:divide-zinc-800">
5155
+ ${recentCritical.slice(0, 10).map((event) => `
5156
+ <div class="px-5 py-3 hover:bg-zinc-50 dark:hover:bg-zinc-800/50">
5157
+ <div class="flex items-center justify-between">
5158
+ <div class="flex items-center gap-2">
5159
+ ${severityBadge(event.severity)}
5160
+ <span class="text-sm font-medium text-zinc-900 dark:text-zinc-100">${eventTypeBadge(event.eventType)}</span>
5161
+ </div>
5162
+ <span class="text-xs text-zinc-400">${formatTimestamp(event.createdAt)}</span>
5163
+ </div>
5164
+ <div class="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
5165
+ ${event.ipAddress ? `IP: ${event.ipAddress}` : ""}
5166
+ ${event.email ? ` | ${event.email}` : ""}
5167
+ </div>
5168
+ </div>
5169
+ `).join("")}
5170
+ </div>`}
5171
+ </div>
5172
+ </div>
5173
+ </div>
5174
+ `;
5175
+ const layoutData = {
5176
+ title: "Security Dashboard",
5177
+ pageTitle: "Security Dashboard",
5178
+ currentPath: "/admin/plugins/security-audit",
5179
+ user,
5180
+ content: content2,
5181
+ version,
5182
+ dynamicMenuItems
5183
+ };
5184
+ return chunkH4NHRZ6Y_cjs.renderAdminLayoutCatalyst(layoutData);
5185
+ }
5186
+
5187
+ // src/plugins/core-plugins/security-audit-plugin/components/event-log-page.ts
5188
+ chunkH4NHRZ6Y_cjs.init_admin_layout_catalyst_template();
5189
+ function formatTimestamp2(ts) {
5190
+ const date = new Date(ts);
5191
+ return date.toLocaleDateString("en-US", {
5192
+ month: "short",
5193
+ day: "numeric",
5194
+ year: "numeric",
5195
+ hour: "2-digit",
5196
+ minute: "2-digit",
5197
+ second: "2-digit"
5198
+ });
5199
+ }
5200
+ function severityBadge2(severity) {
5201
+ const colors = {
5202
+ info: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
5203
+ warning: "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400",
5204
+ critical: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
5205
+ };
5206
+ return `<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ${colors[severity] || colors.info}">${severity}</span>`;
5207
+ }
5208
+ function eventTypeBadge2(type) {
5209
+ const colors = {
5210
+ login_success: "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400",
5211
+ login_failure: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400",
5212
+ registration: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
5213
+ account_lockout: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400",
5214
+ suspicious_activity: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400",
5215
+ logout: "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-400",
5216
+ password_reset_request: "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400",
5217
+ permission_denied: "bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400"
5218
+ };
5219
+ const labels = {
5220
+ login_success: "Login OK",
5221
+ login_failure: "Login Failed",
5222
+ registration: "Registration",
5223
+ account_lockout: "Lockout",
5224
+ suspicious_activity: "Suspicious",
5225
+ logout: "Logout",
5226
+ password_reset_request: "Password Reset",
5227
+ permission_denied: "Access Denied"
5228
+ };
5229
+ const color = colors[type] || "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-400";
5230
+ return `<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ${color}">${labels[type] || type}</span>`;
5231
+ }
5232
+ function buildFilterUrl(filters, overrides = {}) {
5233
+ const params = new URLSearchParams();
5234
+ if (filters.eventType && !overrides.type) params.set("type", String(filters.eventType));
5235
+ if (filters.severity && !overrides.severity) params.set("severity", String(filters.severity));
5236
+ if (filters.email && !overrides.email) params.set("email", filters.email);
5237
+ if (filters.ipAddress && !overrides.ip) params.set("ip", filters.ipAddress);
5238
+ if (filters.search && !overrides.search) params.set("search", filters.search);
5239
+ for (const [key, value] of Object.entries(overrides)) {
5240
+ if (value) params.set(key, value);
5241
+ }
5242
+ const qs = params.toString();
5243
+ return `/admin/plugins/security-audit/events${qs ? "?" + qs : ""}`;
5244
+ }
5245
+ function renderEventLogPage(data) {
5246
+ const { events, pagination, filters, user, version, dynamicMenuItems } = data;
5247
+ const content2 = `
5248
+ <div>
5249
+ <div class="sm:flex sm:items-center sm:justify-between mb-6">
5250
+ <div class="sm:flex-auto">
5251
+ <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">Security Event Log</h1>
5252
+ <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
5253
+ Browse and filter all security events. Showing ${pagination.startItem}-${pagination.endItem} of ${pagination.totalItems}.
5254
+ </p>
5255
+ </div>
5256
+ <div class="mt-4 sm:mt-0 sm:ml-16 flex gap-x-2">
5257
+ <a href="/admin/plugins/security-audit"
5258
+ class="inline-flex items-center justify-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white hover:bg-zinc-50 dark:hover:bg-zinc-700 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 transition-colors shadow-sm">
5259
+ Dashboard
5260
+ </a>
5261
+ </div>
5262
+ </div>
5263
+
5264
+ <!-- Filters -->
5265
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-5 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm mb-6">
5266
+ <form method="GET" action="/admin/plugins/security-audit/events" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
5267
+ <div>
5268
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Event Type</label>
5269
+ <select name="type" class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5270
+ <option value="">All Types</option>
5271
+ <option value="login_success" ${filters.eventType === "login_success" ? "selected" : ""}>Login Success</option>
5272
+ <option value="login_failure" ${filters.eventType === "login_failure" ? "selected" : ""}>Login Failure</option>
5273
+ <option value="registration" ${filters.eventType === "registration" ? "selected" : ""}>Registration</option>
5274
+ <option value="account_lockout" ${filters.eventType === "account_lockout" ? "selected" : ""}>Account Lockout</option>
5275
+ <option value="suspicious_activity" ${filters.eventType === "suspicious_activity" ? "selected" : ""}>Suspicious Activity</option>
5276
+ <option value="logout" ${filters.eventType === "logout" ? "selected" : ""}>Logout</option>
5277
+ </select>
5278
+ </div>
5279
+ <div>
5280
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Severity</label>
5281
+ <select name="severity" class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5282
+ <option value="">All Severities</option>
5283
+ <option value="info" ${filters.severity === "info" ? "selected" : ""}>Info</option>
5284
+ <option value="warning" ${filters.severity === "warning" ? "selected" : ""}>Warning</option>
5285
+ <option value="critical" ${filters.severity === "critical" ? "selected" : ""}>Critical</option>
5286
+ </select>
5287
+ </div>
5288
+ <div>
5289
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Email</label>
5290
+ <input type="text" name="email" value="${filters.email || ""}" placeholder="Filter by email"
5291
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500 placeholder:text-zinc-400">
5292
+ </div>
5293
+ <div>
5294
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">IP Address</label>
5295
+ <input type="text" name="ip" value="${filters.ipAddress || ""}" placeholder="Filter by IP"
5296
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500 placeholder:text-zinc-400">
5297
+ </div>
5298
+ <div class="flex items-end gap-2">
5299
+ <button type="submit"
5300
+ class="flex-1 rounded-lg bg-cyan-600 px-3 py-2 text-sm font-semibold text-white hover:bg-cyan-500 transition-colors shadow-sm">
5301
+ Filter
5302
+ </button>
5303
+ <a href="/admin/plugins/security-audit/events"
5304
+ class="rounded-lg bg-zinc-100 dark:bg-zinc-800 px-3 py-2 text-sm font-medium text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors">
5305
+ Clear
5306
+ </a>
5307
+ </div>
5308
+ </form>
5309
+ </div>
5310
+
5311
+ <!-- Events Table -->
5312
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm overflow-hidden">
5313
+ ${events.length === 0 ? `<div class="p-12 text-center">
5314
+ <svg class="mx-auto h-12 w-12 text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
5315
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/>
5316
+ </svg>
5317
+ <h3 class="mt-2 text-sm font-semibold text-zinc-900 dark:text-white">No events found</h3>
5318
+ <p class="mt-1 text-sm text-zinc-500">No security events match your current filters.</p>
5319
+ </div>` : `<div class="overflow-x-auto">
5320
+ <table class="min-w-full divide-y divide-zinc-200 dark:divide-zinc-800">
5321
+ <thead>
5322
+ <tr>
5323
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">Time</th>
5324
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">Type</th>
5325
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">Severity</th>
5326
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">Email</th>
5327
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">IP Address</th>
5328
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">Country</th>
5329
+ <th class="px-4 py-3 text-left text-xs font-medium text-zinc-500 uppercase tracking-wider">Status</th>
5330
+ </tr>
5331
+ </thead>
5332
+ <tbody class="divide-y divide-zinc-100 dark:divide-zinc-800">
5333
+ ${events.map((event) => `
5334
+ <tr class="hover:bg-zinc-50 dark:hover:bg-zinc-800/50 cursor-pointer" onclick="this.querySelector('.event-details').classList.toggle('hidden')">
5335
+ <td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300 whitespace-nowrap">${formatTimestamp2(event.createdAt)}</td>
5336
+ <td class="px-4 py-3">${eventTypeBadge2(event.eventType)}</td>
5337
+ <td class="px-4 py-3">${severityBadge2(event.severity)}</td>
5338
+ <td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300 max-w-[200px] truncate">${event.email || "-"}</td>
5339
+ <td class="px-4 py-3 text-sm font-mono text-zinc-600 dark:text-zinc-300">${event.ipAddress || "-"}</td>
5340
+ <td class="px-4 py-3 text-sm text-zinc-600 dark:text-zinc-300">${event.countryCode || "-"}</td>
5341
+ <td class="px-4 py-3">
5342
+ ${event.blocked ? '<span class="inline-flex items-center rounded-md bg-red-100 dark:bg-red-900/30 px-2 py-1 text-xs font-medium text-red-700 dark:text-red-400">Blocked</span>' : '<span class="inline-flex items-center rounded-md bg-emerald-100 dark:bg-emerald-900/30 px-2 py-1 text-xs font-medium text-emerald-700 dark:text-emerald-400">Allowed</span>'}
5343
+ </td>
5344
+ </tr>
5345
+ <tr class="event-details hidden">
5346
+ <td colspan="7" class="px-4 py-3 bg-zinc-50 dark:bg-zinc-800/30">
5347
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4 text-xs">
5348
+ <div><span class="font-medium text-zinc-500">Event ID:</span> <span class="text-zinc-700 dark:text-zinc-300 font-mono">${event.id.substring(0, 8)}...</span></div>
5349
+ <div><span class="font-medium text-zinc-500">User Agent:</span> <span class="text-zinc-700 dark:text-zinc-300 truncate block max-w-[300px]">${event.userAgent || "-"}</span></div>
5350
+ <div><span class="font-medium text-zinc-500">Path:</span> <span class="text-zinc-700 dark:text-zinc-300">${event.requestPath || "-"}</span></div>
5351
+ <div><span class="font-medium text-zinc-500">Fingerprint:</span> <span class="text-zinc-700 dark:text-zinc-300 font-mono">${event.fingerprint || "-"}</span></div>
5352
+ ${event.details ? `<div class="col-span-full"><span class="font-medium text-zinc-500">Details:</span> <pre class="text-zinc-700 dark:text-zinc-300 mt-1 bg-zinc-100 dark:bg-zinc-900 rounded p-2 overflow-x-auto">${JSON.stringify(event.details, null, 2)}</pre></div>` : ""}
5353
+ </div>
5354
+ </td>
5355
+ </tr>
5356
+ `).join("")}
5357
+ </tbody>
5358
+ </table>
5359
+ </div>`}
5360
+
5361
+ <!-- Pagination -->
5362
+ ${pagination.totalPages > 1 ? `
5363
+ <div class="flex items-center justify-between border-t border-zinc-200 dark:border-zinc-800 px-4 py-3">
5364
+ <div class="text-sm text-zinc-500">
5365
+ Page ${pagination.currentPage} of ${pagination.totalPages}
5366
+ </div>
5367
+ <div class="flex gap-1">
5368
+ ${pagination.currentPage > 1 ? `
5369
+ <a href="${buildFilterUrl(filters, { page: String(pagination.currentPage - 1) })}"
5370
+ class="rounded-lg px-3 py-1.5 text-sm font-medium text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">
5371
+ Previous
5372
+ </a>
5373
+ ` : ""}
5374
+ ${pagination.currentPage < pagination.totalPages ? `
5375
+ <a href="${buildFilterUrl(filters, { page: String(pagination.currentPage + 1) })}"
5376
+ class="rounded-lg px-3 py-1.5 text-sm font-medium text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">
5377
+ Next
5378
+ </a>
5379
+ ` : ""}
5380
+ </div>
5381
+ </div>
5382
+ ` : ""}
5383
+ </div>
5384
+ </div>
5385
+ `;
5386
+ const layoutData = {
5387
+ title: "Security Event Log",
5388
+ pageTitle: "Security Event Log",
5389
+ currentPath: "/admin/plugins/security-audit/events",
5390
+ user,
5391
+ content: content2,
5392
+ version,
5393
+ dynamicMenuItems
5394
+ };
5395
+ return chunkH4NHRZ6Y_cjs.renderAdminLayoutCatalyst(layoutData);
5396
+ }
5397
+
5398
+ // src/plugins/core-plugins/security-audit-plugin/components/settings-page.ts
5399
+ chunkH4NHRZ6Y_cjs.init_admin_layout_catalyst_template();
5400
+ function renderSecuritySettingsPage(data) {
5401
+ const { settings, user, version, message, dynamicMenuItems } = data;
5402
+ const content2 = `
5403
+ <div>
5404
+ <div class="sm:flex sm:items-center sm:justify-between mb-6">
5405
+ <div class="sm:flex-auto">
5406
+ <h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">Security Audit Settings</h1>
5407
+ <p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
5408
+ Configure brute-force detection thresholds, event logging, and data retention.
5409
+ </p>
5410
+ </div>
5411
+ <div class="mt-4 sm:mt-0 sm:ml-16 flex gap-x-2">
5412
+ <a href="/admin/plugins/security-audit"
5413
+ class="inline-flex items-center justify-center rounded-lg bg-white dark:bg-zinc-800 px-3.5 py-2.5 text-sm font-semibold text-zinc-950 dark:text-white hover:bg-zinc-50 dark:hover:bg-zinc-700 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 transition-colors shadow-sm">
5414
+ Dashboard
5415
+ </a>
5416
+ </div>
5417
+ </div>
5418
+
5419
+ ${message ? `
5420
+ <div class="mb-6 rounded-lg bg-emerald-50 dark:bg-emerald-900/20 p-4 ring-1 ring-emerald-200 dark:ring-emerald-800">
5421
+ <p class="text-sm text-emerald-800 dark:text-emerald-300">${message}</p>
5422
+ </div>
5423
+ ` : ""}
5424
+
5425
+ <form method="POST" action="/admin/plugins/security-audit/settings"
5426
+ class="space-y-6"
5427
+ hx-post="/admin/plugins/security-audit/settings"
5428
+ hx-swap="none"
5429
+ hx-on::after-request="if(event.detail.successful) { window.showNotification && window.showNotification('Settings saved', 'success'); } else { window.showNotification && window.showNotification('Failed to save', 'error'); }">
5430
+
5431
+ <!-- Brute Force Detection -->
5432
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-6 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5433
+ <h2 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Brute-Force Detection</h2>
5434
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
5435
+ <div>
5436
+ <label class="flex items-center gap-2 mb-4">
5437
+ <input type="checkbox" name="bruteForce.enabled" value="true" ${settings.bruteForce.enabled ? "checked" : ""}
5438
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5439
+ <span class="text-sm font-medium text-zinc-700 dark:text-zinc-300">Enable brute-force detection</span>
5440
+ </label>
5441
+ </div>
5442
+ <div></div><div></div>
5443
+ <div>
5444
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Max Failed Attempts per IP</label>
5445
+ <input type="number" name="bruteForce.maxFailedAttemptsPerIP" value="${settings.bruteForce.maxFailedAttemptsPerIP}" min="1" max="100"
5446
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5447
+ </div>
5448
+ <div>
5449
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Max Failed Attempts per Email</label>
5450
+ <input type="number" name="bruteForce.maxFailedAttemptsPerEmail" value="${settings.bruteForce.maxFailedAttemptsPerEmail}" min="1" max="100"
5451
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5452
+ </div>
5453
+ <div>
5454
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Window (minutes)</label>
5455
+ <input type="number" name="bruteForce.windowMinutes" value="${settings.bruteForce.windowMinutes}" min="1" max="1440"
5456
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5457
+ </div>
5458
+ <div>
5459
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Lockout Duration (minutes)</label>
5460
+ <input type="number" name="bruteForce.lockoutDurationMinutes" value="${settings.bruteForce.lockoutDurationMinutes}" min="1" max="1440"
5461
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5462
+ </div>
5463
+ <div>
5464
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Alert Threshold</label>
5465
+ <input type="number" name="bruteForce.alertThreshold" value="${settings.bruteForce.alertThreshold}" min="1" max="1000"
5466
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5467
+ <p class="mt-1 text-xs text-zinc-400">Events above this count trigger critical severity</p>
5468
+ </div>
5469
+ </div>
5470
+ </div>
5471
+
5472
+ <!-- Event Logging -->
5473
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-6 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5474
+ <h2 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Event Logging</h2>
5475
+ <div class="space-y-3">
5476
+ <label class="flex items-center gap-2">
5477
+ <input type="checkbox" name="logging.logSuccessfulLogins" value="true" ${settings.logging.logSuccessfulLogins ? "checked" : ""}
5478
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5479
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">Log successful logins</span>
5480
+ </label>
5481
+ <label class="flex items-center gap-2">
5482
+ <input type="checkbox" name="logging.logLogouts" value="true" ${settings.logging.logLogouts ? "checked" : ""}
5483
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5484
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">Log logouts</span>
5485
+ </label>
5486
+ <label class="flex items-center gap-2">
5487
+ <input type="checkbox" name="logging.logRegistrations" value="true" ${settings.logging.logRegistrations ? "checked" : ""}
5488
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5489
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">Log registrations</span>
5490
+ </label>
5491
+ <label class="flex items-center gap-2">
5492
+ <input type="checkbox" name="logging.logPasswordResets" value="true" ${settings.logging.logPasswordResets ? "checked" : ""}
5493
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5494
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">Log password resets</span>
5495
+ </label>
5496
+ <label class="flex items-center gap-2">
5497
+ <input type="checkbox" name="logging.logPermissionDenied" value="true" ${settings.logging.logPermissionDenied ? "checked" : ""}
5498
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5499
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">Log permission denied events</span>
5500
+ </label>
5501
+ </div>
5502
+ </div>
5503
+
5504
+ <!-- Data Retention -->
5505
+ <div class="rounded-xl bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl p-6 ring-1 ring-zinc-950/5 dark:ring-white/10 shadow-sm">
5506
+ <h2 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Data Retention</h2>
5507
+ <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
5508
+ <div>
5509
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Days to Keep</label>
5510
+ <input type="number" name="retention.daysToKeep" value="${settings.retention.daysToKeep}" min="1" max="365"
5511
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5512
+ </div>
5513
+ <div>
5514
+ <label class="block text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1">Max Events</label>
5515
+ <input type="number" name="retention.maxEvents" value="${settings.retention.maxEvents}" min="1000" max="1000000"
5516
+ class="w-full rounded-lg border-0 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-white ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 focus:ring-2 focus:ring-cyan-500">
5517
+ </div>
5518
+ <div>
5519
+ <label class="flex items-center gap-2 mt-5">
5520
+ <input type="checkbox" name="retention.autoPurge" value="true" ${settings.retention.autoPurge ? "checked" : ""}
5521
+ class="rounded border-zinc-300 text-cyan-600 focus:ring-cyan-500">
5522
+ <span class="text-sm text-zinc-700 dark:text-zinc-300">Auto-purge old events</span>
5523
+ </label>
5524
+ </div>
5525
+ </div>
5526
+ </div>
5527
+
5528
+ <!-- Actions -->
5529
+ <div class="flex items-center justify-between">
5530
+ <button type="button"
5531
+ onclick="if(confirm('Purge events older than retention period?')) fetch('/api/security-audit/events/purge', {method:'POST',headers:{'Content-Type':'application/json'}}).then(r=>r.json()).then(d=>window.showNotification && window.showNotification('Purged '+d.deleted+' events','success'))"
5532
+ class="rounded-lg bg-red-50 dark:bg-red-900/20 px-4 py-2.5 text-sm font-medium text-red-700 dark:text-red-400 hover:bg-red-100 dark:hover:bg-red-900/40 ring-1 ring-red-200 dark:ring-red-800 transition-colors">
5533
+ Purge Old Events
5534
+ </button>
5535
+ <button type="submit"
5536
+ class="rounded-lg bg-cyan-600 px-6 py-2.5 text-sm font-semibold text-white hover:bg-cyan-500 transition-colors shadow-sm">
5537
+ Save Settings
5538
+ </button>
5539
+ </div>
5540
+ </form>
5541
+ </div>
5542
+ `;
5543
+ const layoutData = {
5544
+ title: "Security Audit Settings",
5545
+ pageTitle: "Security Audit Settings",
5546
+ currentPath: "/admin/plugins/security-audit/settings",
5547
+ user,
5548
+ content: content2,
5549
+ version,
5550
+ dynamicMenuItems
5551
+ };
5552
+ return chunkH4NHRZ6Y_cjs.renderAdminLayoutCatalyst(layoutData);
5553
+ }
5554
+
5555
+ // src/plugins/core-plugins/security-audit-plugin/routes/admin.ts
5556
+ var adminRoutes2 = new hono.Hono();
5557
+ adminRoutes2.use("*", chunkVHNTCB2X_cjs.requireAuth());
5558
+ adminRoutes2.use("*", async (c, next) => {
5559
+ const user = c.get("user");
5560
+ if (user?.role !== "admin") {
5561
+ return c.text("Access denied", 403);
5562
+ }
5563
+ return next();
5564
+ });
5565
+ async function getSettings(db) {
5566
+ try {
5567
+ const pluginService = new chunkI6FFGQIT_cjs.PluginService(db);
5568
+ const plugin2 = await pluginService.getPlugin("security-audit");
5569
+ if (plugin2?.settings) {
5570
+ const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
5571
+ return { ...DEFAULT_SETTINGS2, ...settings };
5572
+ }
5573
+ } catch {
5574
+ }
5575
+ return DEFAULT_SETTINGS2;
5576
+ }
5577
+ adminRoutes2.get("/", async (c) => {
5578
+ const db = c.env.DB;
5579
+ const user = c.get("user");
5580
+ const settings = await getSettings(db);
5581
+ const service = new SecurityAuditService(db, settings);
5582
+ const [stats, topIPs, hourlyTrend, recentCritical] = await Promise.all([
5583
+ service.getStats(),
5584
+ service.getTopIPs(10),
5585
+ service.getHourlyTrend(24),
5586
+ service.getRecentCriticalEvents(20)
5587
+ ]);
5588
+ const pageData = {
5589
+ stats,
5590
+ topIPs,
5591
+ hourlyTrend,
5592
+ recentCritical,
5593
+ user: user ? { name: user.email, email: user.email, role: user.role } : void 0,
5594
+ version: c.get("appVersion"),
5595
+ dynamicMenuItems: c.get("pluginMenuItems")
5596
+ };
5597
+ return c.html(renderSecurityDashboard(pageData));
5598
+ });
5599
+ adminRoutes2.get("/events", async (c) => {
5600
+ const db = c.env.DB;
5601
+ const user = c.get("user");
5602
+ const settings = await getSettings(db);
5603
+ const service = new SecurityAuditService(db, settings);
5604
+ const page = parseInt(c.req.query("page") || "1");
5605
+ const limit = 50;
5606
+ const filters = {
5607
+ eventType: c.req.query("type") || void 0,
5608
+ severity: c.req.query("severity") || void 0,
5609
+ email: c.req.query("email") || void 0,
5610
+ ipAddress: c.req.query("ip") || void 0,
5611
+ search: c.req.query("search") || void 0,
5612
+ page,
5613
+ limit
5614
+ };
5615
+ const { events, total } = await service.getEvents(filters);
5616
+ const totalPages = Math.ceil(total / limit);
5617
+ const pageData = {
5618
+ events,
5619
+ pagination: {
5620
+ currentPage: page,
5621
+ totalPages,
5622
+ totalItems: total,
5623
+ itemsPerPage: limit,
5624
+ startItem: total === 0 ? 0 : (page - 1) * limit + 1,
5625
+ endItem: Math.min(page * limit, total)
5626
+ },
5627
+ filters,
5628
+ user: user ? { name: user.email, email: user.email, role: user.role } : void 0,
5629
+ version: c.get("appVersion"),
5630
+ dynamicMenuItems: c.get("pluginMenuItems")
5631
+ };
5632
+ return c.html(renderEventLogPage(pageData));
5633
+ });
5634
+ adminRoutes2.get("/settings", async (c) => {
5635
+ const db = c.env.DB;
5636
+ const user = c.get("user");
5637
+ const settings = await getSettings(db);
5638
+ const pageData = {
5639
+ settings,
5640
+ user: user ? { name: user.email, email: user.email, role: user.role } : void 0,
5641
+ version: c.get("appVersion"),
5642
+ message: c.req.query("message") || void 0,
5643
+ dynamicMenuItems: c.get("pluginMenuItems")
5644
+ };
5645
+ return c.html(renderSecuritySettingsPage(pageData));
5646
+ });
5647
+ adminRoutes2.post("/settings", async (c) => {
5648
+ const db = c.env.DB;
5649
+ const body = await c.req.parseBody();
5650
+ const settings = {
5651
+ bruteForce: {
5652
+ enabled: body["bruteForce.enabled"] === "true",
5653
+ maxFailedAttemptsPerIP: parseInt(body["bruteForce.maxFailedAttemptsPerIP"]) || 10,
5654
+ maxFailedAttemptsPerEmail: parseInt(body["bruteForce.maxFailedAttemptsPerEmail"]) || 5,
5655
+ windowMinutes: parseInt(body["bruteForce.windowMinutes"]) || 15,
5656
+ lockoutDurationMinutes: parseInt(body["bruteForce.lockoutDurationMinutes"]) || 30,
5657
+ alertThreshold: parseInt(body["bruteForce.alertThreshold"]) || 20
5658
+ },
5659
+ logging: {
5660
+ logSuccessfulLogins: body["logging.logSuccessfulLogins"] === "true",
5661
+ logLogouts: body["logging.logLogouts"] === "true",
5662
+ logRegistrations: body["logging.logRegistrations"] === "true",
5663
+ logPasswordResets: body["logging.logPasswordResets"] === "true",
5664
+ logPermissionDenied: body["logging.logPermissionDenied"] === "true"
5665
+ },
5666
+ retention: {
5667
+ daysToKeep: parseInt(body["retention.daysToKeep"]) || 90,
5668
+ maxEvents: parseInt(body["retention.maxEvents"]) || 1e5,
5669
+ autoPurge: body["retention.autoPurge"] === "true"
5670
+ }
5671
+ };
5672
+ const pluginService = new chunkI6FFGQIT_cjs.PluginService(db);
5673
+ await pluginService.updatePluginSettings("security-audit", settings);
5674
+ if (c.req.header("HX-Request")) {
5675
+ return c.json({ success: true });
5676
+ }
5677
+ return c.redirect("/admin/plugins/security-audit/settings?message=Settings saved successfully");
5678
+ });
5679
+
5680
+ // src/plugins/core-plugins/security-audit-plugin/services/brute-force-detector.ts
5681
+ var KV_PREFIX = "security:bf:";
5682
+ var LOCK_PREFIX = "security:locked:";
5683
+ var BruteForceDetector = class {
5684
+ constructor(kv, settings) {
5685
+ this.kv = kv;
5686
+ this.settings = settings || DEFAULT_SETTINGS2.bruteForce;
5687
+ }
5688
+ settings;
5689
+ async recordFailedAttempt(ip, email) {
5690
+ if (!this.settings.enabled) {
5691
+ return { ipCount: 0, emailCount: 0, shouldLockIP: false, shouldLockEmail: false, isSuspicious: false };
5692
+ }
5693
+ const windowMs = this.settings.windowMinutes * 60 * 1e3;
5694
+ const ipKey = `${KV_PREFIX}ip:${ip}`;
5695
+ const ipCount = await this.incrementCounter(ipKey, windowMs);
5696
+ const emailKey = `${KV_PREFIX}email:${email}`;
5697
+ const emailCount = await this.incrementCounter(emailKey, windowMs);
5698
+ const ipEmailsKey = `${KV_PREFIX}ip-emails:${ip}`;
5699
+ await this.addToSet(ipEmailsKey, email, windowMs);
5700
+ const emailsFromIP = await this.getSetSize(ipEmailsKey);
5701
+ const isSuspicious = emailsFromIP >= 5;
5702
+ const shouldLockIP = ipCount >= this.settings.maxFailedAttemptsPerIP;
5703
+ const shouldLockEmail = emailCount >= this.settings.maxFailedAttemptsPerEmail;
5704
+ return { ipCount, emailCount, shouldLockIP, shouldLockEmail, isSuspicious };
5705
+ }
5706
+ async isLocked(ip, email) {
5707
+ if (!this.settings.enabled) {
5708
+ return { locked: false };
5709
+ }
5710
+ const ipLocked = await this.kv.get(`${LOCK_PREFIX}ip:${ip}`);
5711
+ if (ipLocked) {
5712
+ return { locked: true, reason: "IP address temporarily locked due to excessive failed login attempts" };
5713
+ }
5714
+ const emailLocked = await this.kv.get(`${LOCK_PREFIX}email:${email}`);
5715
+ if (emailLocked) {
5716
+ return { locked: true, reason: "Account temporarily locked due to excessive failed login attempts" };
5717
+ }
5718
+ return { locked: false };
5719
+ }
5720
+ async lockIP(ip) {
5721
+ const ttl = this.settings.lockoutDurationMinutes * 60;
5722
+ await this.kv.put(`${LOCK_PREFIX}ip:${ip}`, JSON.stringify({
5723
+ lockedAt: Date.now(),
5724
+ reason: "brute_force_ip"
5725
+ }), { expirationTtl: ttl });
5726
+ }
5727
+ async lockEmail(email) {
5728
+ const ttl = this.settings.lockoutDurationMinutes * 60;
5729
+ await this.kv.put(`${LOCK_PREFIX}email:${email}`, JSON.stringify({
5730
+ lockedAt: Date.now(),
5731
+ reason: "brute_force_email"
5732
+ }), { expirationTtl: ttl });
5733
+ }
5734
+ async unlockIP(ip) {
5735
+ await this.kv.delete(`${LOCK_PREFIX}ip:${ip}`);
5736
+ }
5737
+ async unlockEmail(email) {
5738
+ await this.kv.delete(`${LOCK_PREFIX}email:${email}`);
5739
+ }
5740
+ async getActiveLockouts() {
5741
+ const ipLocks = await this.kv.list({ prefix: `${LOCK_PREFIX}ip:` });
5742
+ const emailLocks = await this.kv.list({ prefix: `${LOCK_PREFIX}email:` });
5743
+ const lockouts = [];
5744
+ for (const key of ipLocks.keys) {
5745
+ const data = await this.kv.get(key.name);
5746
+ if (data) {
5747
+ const parsed = JSON.parse(data);
5748
+ lockouts.push({
5749
+ key: key.name,
5750
+ type: "ip",
5751
+ value: key.name.replace(`${LOCK_PREFIX}ip:`, ""),
5752
+ lockedAt: parsed.lockedAt
5753
+ });
5754
+ }
5755
+ }
5756
+ for (const key of emailLocks.keys) {
5757
+ const data = await this.kv.get(key.name);
5758
+ if (data) {
5759
+ const parsed = JSON.parse(data);
5760
+ lockouts.push({
5761
+ key: key.name,
5762
+ type: "email",
5763
+ value: key.name.replace(`${LOCK_PREFIX}email:`, ""),
5764
+ lockedAt: parsed.lockedAt
5765
+ });
5766
+ }
5767
+ }
5768
+ return lockouts;
5769
+ }
5770
+ async releaseLockout(key) {
5771
+ await this.kv.delete(key);
5772
+ }
5773
+ isAboveAlertThreshold(count) {
5774
+ return count >= this.settings.alertThreshold;
5775
+ }
5776
+ async incrementCounter(key, windowMs) {
5777
+ const existing = await this.kv.get(key);
5778
+ const now = Date.now();
5779
+ let entries = [];
5780
+ if (existing) {
5781
+ try {
5782
+ entries = JSON.parse(existing);
5783
+ } catch {
5784
+ entries = [];
5785
+ }
5786
+ }
5787
+ const cutoff = now - windowMs;
5788
+ entries = entries.filter((ts) => ts > cutoff);
5789
+ entries.push(now);
5790
+ const ttlSeconds = Math.ceil(windowMs / 1e3);
5791
+ await this.kv.put(key, JSON.stringify(entries), { expirationTtl: ttlSeconds });
5792
+ return entries.length;
5793
+ }
5794
+ async addToSet(key, value, windowMs) {
5795
+ const existing = await this.kv.get(key);
5796
+ let set = {};
5797
+ const now = Date.now();
5798
+ const cutoff = now - windowMs;
5799
+ if (existing) {
5800
+ try {
5801
+ set = JSON.parse(existing);
5802
+ } catch {
5803
+ set = {};
5804
+ }
5805
+ }
5806
+ for (const [k, ts] of Object.entries(set)) {
5807
+ if (ts < cutoff) delete set[k];
5808
+ }
5809
+ set[value] = now;
5810
+ const ttlSeconds = Math.ceil(windowMs / 1e3);
5811
+ await this.kv.put(key, JSON.stringify(set), { expirationTtl: ttlSeconds });
5812
+ }
5813
+ async getSetSize(key) {
5814
+ const existing = await this.kv.get(key);
5815
+ if (!existing) return 0;
5816
+ try {
5817
+ const set = JSON.parse(existing);
5818
+ return Object.keys(set).length;
5819
+ } catch {
5820
+ return 0;
5821
+ }
5822
+ }
5823
+ };
5824
+
5825
+ // src/plugins/core-plugins/security-audit-plugin/routes/api.ts
5826
+ var apiRoutes2 = new hono.Hono();
5827
+ apiRoutes2.use("*", chunkVHNTCB2X_cjs.requireAuth());
5828
+ apiRoutes2.use("*", async (c, next) => {
5829
+ const user = c.get("user");
5830
+ if (user?.role !== "admin") {
5831
+ return c.json({ error: "Access denied" }, 403);
5832
+ }
5833
+ return next();
5834
+ });
5835
+ async function getSettings2(db) {
5836
+ try {
5837
+ const pluginService = new chunkI6FFGQIT_cjs.PluginService(db);
5838
+ const plugin2 = await pluginService.getPlugin("security-audit");
5839
+ if (plugin2?.settings) {
5840
+ const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
5841
+ return { ...DEFAULT_SETTINGS2, ...settings };
5842
+ }
5843
+ } catch {
5844
+ }
5845
+ return DEFAULT_SETTINGS2;
5846
+ }
5847
+ apiRoutes2.get("/events", async (c) => {
5848
+ const db = c.env.DB;
5849
+ const settings = await getSettings2(db);
5850
+ const service = new SecurityAuditService(db, settings);
5851
+ const filters = {
5852
+ eventType: c.req.query("type"),
5853
+ severity: c.req.query("severity"),
5854
+ email: c.req.query("email") || void 0,
5855
+ ipAddress: c.req.query("ip") || void 0,
5856
+ search: c.req.query("search") || void 0,
5857
+ startDate: c.req.query("start") ? parseInt(c.req.query("start")) : void 0,
5858
+ endDate: c.req.query("end") ? parseInt(c.req.query("end")) : void 0,
5859
+ page: c.req.query("page") ? parseInt(c.req.query("page")) : 1,
5860
+ limit: c.req.query("limit") ? Math.min(parseInt(c.req.query("limit")), 100) : 50,
5861
+ sortBy: c.req.query("sortBy") || "created_at",
5862
+ sortOrder: c.req.query("sortOrder") || "desc"
5863
+ };
5864
+ const result = await service.getEvents(filters);
5865
+ return c.json(result);
5866
+ });
5867
+ apiRoutes2.get("/events/:id", async (c) => {
5868
+ const db = c.env.DB;
5869
+ const settings = await getSettings2(db);
5870
+ const service = new SecurityAuditService(db, settings);
5871
+ const event = await service.getEvent(c.req.param("id"));
5872
+ if (!event) {
5873
+ return c.json({ error: "Event not found" }, 404);
5874
+ }
5875
+ return c.json(event);
5876
+ });
5877
+ apiRoutes2.get("/stats", async (c) => {
5878
+ const db = c.env.DB;
5879
+ const settings = await getSettings2(db);
5880
+ const service = new SecurityAuditService(db, settings);
5881
+ const stats = await service.getStats();
5882
+ return c.json(stats);
5883
+ });
5884
+ apiRoutes2.get("/stats/ips", async (c) => {
5885
+ const db = c.env.DB;
5886
+ const settings = await getSettings2(db);
5887
+ const service = new SecurityAuditService(db, settings);
5888
+ const limit = c.req.query("limit") ? parseInt(c.req.query("limit")) : 10;
5889
+ const ips = await service.getTopIPs(limit);
5890
+ return c.json(ips);
5891
+ });
5892
+ apiRoutes2.get("/stats/trend", async (c) => {
5893
+ const db = c.env.DB;
5894
+ const settings = await getSettings2(db);
5895
+ const service = new SecurityAuditService(db, settings);
5896
+ const hours = c.req.query("hours") ? parseInt(c.req.query("hours")) : 24;
5897
+ const trend = await service.getHourlyTrend(hours);
5898
+ return c.json(trend);
5899
+ });
5900
+ apiRoutes2.get("/lockouts", async (c) => {
5901
+ const kv = c.env.CACHE_KV;
5902
+ const db = c.env.DB;
5903
+ const settings = await getSettings2(db);
5904
+ const detector = new BruteForceDetector(kv, settings.bruteForce);
5905
+ const lockouts = await detector.getActiveLockouts();
5906
+ return c.json(lockouts);
5907
+ });
5908
+ apiRoutes2.delete("/lockouts/:key", async (c) => {
5909
+ const kv = c.env.CACHE_KV;
5910
+ const key = decodeURIComponent(c.req.param("key"));
5911
+ const db = c.env.DB;
5912
+ const settings = await getSettings2(db);
5913
+ const detector = new BruteForceDetector(kv, settings.bruteForce);
5914
+ await detector.releaseLockout(key);
5915
+ return c.json({ success: true });
5916
+ });
5917
+ apiRoutes2.post("/events/purge", async (c) => {
5918
+ const db = c.env.DB;
5919
+ const settings = await getSettings2(db);
5920
+ const service = new SecurityAuditService(db, settings);
5921
+ const body = await c.req.json().catch(() => ({}));
5922
+ const deleted = await service.purgeOldEvents(body.daysToKeep);
5923
+ return c.json({ success: true, deleted });
5924
+ });
5925
+ apiRoutes2.get("/export", async (c) => {
5926
+ const db = c.env.DB;
5927
+ const settings = await getSettings2(db);
5928
+ const service = new SecurityAuditService(db, settings);
5929
+ const format = c.req.query("format") || "json";
5930
+ const filters = {
5931
+ eventType: c.req.query("type"),
5932
+ severity: c.req.query("severity"),
5933
+ startDate: c.req.query("start") ? parseInt(c.req.query("start")) : void 0,
5934
+ endDate: c.req.query("end") ? parseInt(c.req.query("end")) : void 0,
5935
+ limit: 1e4,
5936
+ page: 1
5937
+ };
5938
+ const { events } = await service.getEvents(filters);
5939
+ if (format === "csv") {
5940
+ const headers = ["id", "event_type", "severity", "email", "ip_address", "country_code", "blocked", "created_at"];
5941
+ const csvRows = [headers.join(",")];
5942
+ for (const event of events) {
5943
+ csvRows.push([
5944
+ event.id,
5945
+ event.eventType,
5946
+ event.severity,
5947
+ event.email || "",
5948
+ event.ipAddress || "",
5949
+ event.countryCode || "",
5950
+ event.blocked ? "1" : "0",
5951
+ new Date(event.createdAt).toISOString()
5952
+ ].map((v) => `"${String(v).replace(/"/g, '""')}"`).join(","));
5953
+ }
5954
+ return new Response(csvRows.join("\n"), {
5955
+ headers: {
5956
+ "Content-Type": "text/csv",
5957
+ "Content-Disposition": `attachment; filename="security-events-${Date.now()}.csv"`
5958
+ }
5959
+ });
5960
+ }
5961
+ return c.json(events);
5962
+ });
5963
+
5964
+ // src/plugins/core-plugins/security-audit-plugin/middleware/audit-middleware.ts
5965
+ function extractRequestInfo(c) {
5966
+ const ip = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for")?.split(",")[0]?.trim() || "unknown";
5967
+ const userAgent = c.req.header("user-agent") || "unknown";
5968
+ const countryCode = c.req.header("cf-ipcountry") || null;
5969
+ const path = new URL(c.req.url).pathname;
5970
+ const method = c.req.method;
5971
+ return { ip, userAgent, countryCode, path, method };
5972
+ }
5973
+ function generateFingerprint(ip, userAgent) {
5974
+ const str = `${ip}:${userAgent}`;
5975
+ let hash = 0;
5976
+ for (let i = 0; i < str.length; i++) {
5977
+ const char = str.charCodeAt(i);
5978
+ hash = (hash << 5) - hash + char;
5979
+ hash |= 0;
5980
+ }
5981
+ return Math.abs(hash).toString(36);
5982
+ }
5983
+ async function getPluginSettings(db) {
5984
+ try {
5985
+ const pluginService = new chunkI6FFGQIT_cjs.PluginService(db);
5986
+ const plugin2 = await pluginService.getPlugin("security-audit");
5987
+ if (plugin2?.settings) {
5988
+ const settings = typeof plugin2.settings === "string" ? JSON.parse(plugin2.settings) : plugin2.settings;
5989
+ return { ...DEFAULT_SETTINGS2, ...settings };
5990
+ }
5991
+ } catch {
5992
+ }
5993
+ return DEFAULT_SETTINGS2;
5994
+ }
5995
+ async function isPluginActive2(db) {
5996
+ try {
5997
+ const result = await db.prepare(
5998
+ "SELECT status FROM plugins WHERE id = 'security-audit'"
5999
+ ).first();
6000
+ return result?.status === "active";
6001
+ } catch {
6002
+ return false;
6003
+ }
6004
+ }
6005
+ function securityAuditMiddleware() {
6006
+ return async (c, next) => {
6007
+ const path = new URL(c.req.url).pathname;
6008
+ if (!path.startsWith("/auth/")) {
6009
+ return next();
6010
+ }
6011
+ const db = c.env.DB;
6012
+ if (!await isPluginActive2(db)) {
6013
+ return next();
6014
+ }
6015
+ const settings = await getPluginSettings(db);
6016
+ const { ip, userAgent, countryCode, method } = extractRequestInfo(c);
6017
+ const fingerprint = generateFingerprint(ip, userAgent);
6018
+ const isLoginPost = (path === "/auth/login" || path === "/auth/login/form") && method === "POST";
6019
+ let preExtractedEmail = "";
6020
+ if (isLoginPost) {
6021
+ try {
6022
+ if (path === "/auth/login/form") {
6023
+ const clonedReq = c.req.raw.clone();
6024
+ const formData = await clonedReq.formData();
6025
+ preExtractedEmail = (formData.get("email") || "").toLowerCase();
6026
+ } else {
6027
+ const body = await c.req.json();
6028
+ preExtractedEmail = body?.email?.toLowerCase() || "";
6029
+ }
6030
+ } catch {
6031
+ }
6032
+ if (preExtractedEmail && settings.bruteForce.enabled) {
6033
+ const detector = new BruteForceDetector(c.env.CACHE_KV, settings.bruteForce);
6034
+ const lockStatus = await detector.isLocked(ip, preExtractedEmail);
6035
+ if (lockStatus.locked) {
6036
+ const service = new SecurityAuditService(db, settings);
6037
+ const logPromise2 = service.logEvent({
6038
+ eventType: "login_failure",
6039
+ severity: "warning",
6040
+ email: preExtractedEmail,
6041
+ ipAddress: ip,
6042
+ userAgent,
6043
+ countryCode: countryCode || void 0,
6044
+ requestPath: path,
6045
+ requestMethod: method,
6046
+ fingerprint,
6047
+ blocked: true,
6048
+ details: { reason: lockStatus.reason }
6049
+ });
6050
+ if (c.executionCtx?.waitUntil) {
6051
+ c.executionCtx.waitUntil(logPromise2);
6052
+ }
6053
+ return c.json({
6054
+ error: lockStatus.reason || "Too many failed attempts. Please try again later."
6055
+ }, 429);
6056
+ }
6057
+ }
6058
+ }
6059
+ await next();
6060
+ const logPromise = logAuthEvent(c, db, settings, ip, userAgent, countryCode, fingerprint, path, method, preExtractedEmail);
6061
+ if (c.executionCtx?.waitUntil) {
6062
+ c.executionCtx.waitUntil(logPromise);
6063
+ }
6064
+ };
6065
+ }
6066
+ async function logAuthEvent(c, db, settings, ip, userAgent, countryCode, fingerprint, path, method, preExtractedEmail = "") {
6067
+ try {
6068
+ const service = new SecurityAuditService(db, settings);
6069
+ const status = c.res.status;
6070
+ const isLoginPost = (path === "/auth/login" || path === "/auth/login/form") && method === "POST";
6071
+ const isFormLogin = path === "/auth/login/form";
6072
+ if (isLoginPost) {
6073
+ let loginSucceeded;
6074
+ if (isFormLogin) {
6075
+ const hxRedirect = c.res.headers.get("HX-Redirect");
6076
+ const setCookieHeader = c.res.headers.get("set-cookie") || "";
6077
+ loginSucceeded = !!(hxRedirect?.includes("/admin") || setCookieHeader.includes("auth_token"));
6078
+ } else {
6079
+ loginSucceeded = status === 200;
6080
+ }
6081
+ if (loginSucceeded) {
6082
+ if (!settings.logging.logSuccessfulLogins) return;
6083
+ let email = preExtractedEmail;
6084
+ let userId = "";
6085
+ if (!isFormLogin) {
6086
+ try {
6087
+ const cloned = c.res.clone();
6088
+ const body = await cloned.json();
6089
+ email = body?.user?.email || email;
6090
+ userId = body?.user?.id || "";
6091
+ } catch {
6092
+ }
6093
+ }
6094
+ await service.logEvent({
6095
+ eventType: "login_success",
6096
+ severity: "info",
6097
+ userId: userId || void 0,
6098
+ email: email || void 0,
6099
+ ipAddress: ip,
6100
+ userAgent,
6101
+ countryCode: countryCode || void 0,
6102
+ requestPath: path,
6103
+ requestMethod: method,
6104
+ fingerprint
6105
+ });
6106
+ } else {
6107
+ const email = preExtractedEmail;
6108
+ await service.logEvent({
6109
+ eventType: "login_failure",
6110
+ severity: "warning",
6111
+ email: email || void 0,
6112
+ ipAddress: ip,
6113
+ userAgent,
6114
+ countryCode: countryCode || void 0,
6115
+ requestPath: path,
6116
+ requestMethod: method,
6117
+ fingerprint,
6118
+ details: { statusCode: status }
6119
+ });
6120
+ if (email && settings.bruteForce.enabled) {
6121
+ const detector = new BruteForceDetector(c.env.CACHE_KV, settings.bruteForce);
6122
+ const result = await detector.recordFailedAttempt(ip, email);
6123
+ if (result.shouldLockIP) {
6124
+ await detector.lockIP(ip);
6125
+ await service.logEvent({
6126
+ eventType: "account_lockout",
6127
+ severity: "critical",
6128
+ email,
6129
+ ipAddress: ip,
6130
+ userAgent,
6131
+ countryCode: countryCode || void 0,
6132
+ requestPath: path,
6133
+ requestMethod: method,
6134
+ fingerprint,
6135
+ details: { reason: "brute_force_ip", attemptCount: result.ipCount }
6136
+ });
6137
+ }
6138
+ if (result.shouldLockEmail) {
6139
+ await detector.lockEmail(email);
6140
+ await service.logEvent({
6141
+ eventType: "account_lockout",
6142
+ severity: "critical",
6143
+ email,
6144
+ ipAddress: ip,
6145
+ userAgent,
6146
+ countryCode: countryCode || void 0,
6147
+ requestPath: path,
6148
+ requestMethod: method,
6149
+ fingerprint,
6150
+ details: { reason: "brute_force_email", attemptCount: result.emailCount }
6151
+ });
6152
+ }
6153
+ if (result.isSuspicious) {
6154
+ await service.logEvent({
6155
+ eventType: "suspicious_activity",
6156
+ severity: "critical",
6157
+ ipAddress: ip,
6158
+ userAgent,
6159
+ countryCode: countryCode || void 0,
6160
+ requestPath: path,
6161
+ requestMethod: method,
6162
+ fingerprint,
6163
+ details: { reason: "multiple_emails_from_ip", ipCount: result.ipCount }
6164
+ });
6165
+ }
6166
+ }
6167
+ }
6168
+ }
6169
+ if (path === "/auth/register" && method === "POST" && settings.logging.logRegistrations) {
6170
+ if (status === 201 || status === 200) {
6171
+ let email = "";
6172
+ let userId = "";
6173
+ try {
6174
+ const cloned = c.res.clone();
6175
+ const body = await cloned.json();
6176
+ email = body?.user?.email || "";
6177
+ userId = body?.user?.id || "";
6178
+ } catch {
6179
+ }
6180
+ await service.logEvent({
6181
+ eventType: "registration",
6182
+ severity: "info",
6183
+ userId: userId || void 0,
6184
+ email: email || void 0,
6185
+ ipAddress: ip,
6186
+ userAgent,
6187
+ countryCode: countryCode || void 0,
6188
+ requestPath: path,
6189
+ requestMethod: method,
6190
+ fingerprint
6191
+ });
6192
+ }
6193
+ }
6194
+ if (path === "/auth/logout" && settings.logging.logLogouts) {
6195
+ const user = c.get("user");
6196
+ await service.logEvent({
6197
+ eventType: "logout",
6198
+ severity: "info",
6199
+ userId: user?.userId,
6200
+ email: user?.email,
6201
+ ipAddress: ip,
6202
+ userAgent,
6203
+ countryCode: countryCode || void 0,
6204
+ requestPath: path,
6205
+ requestMethod: method,
6206
+ fingerprint
6207
+ });
6208
+ }
6209
+ } catch (error) {
6210
+ console.error("[SecurityAudit] Error logging auth event:", error);
6211
+ }
6212
+ }
6213
+
6214
+ // src/plugins/core-plugins/security-audit-plugin/index.ts
6215
+ function createSecurityAuditPlugin() {
6216
+ const builder = chunk6FHNRRJ3_cjs.PluginBuilder.create({
6217
+ name: "security-audit",
6218
+ version: "1.0.0-beta.1",
6219
+ description: "Security event logging, brute-force detection, and analytics dashboard"
6220
+ });
6221
+ builder.metadata({
6222
+ author: { name: "SonicJS Team" },
6223
+ license: "MIT"
6224
+ });
6225
+ builder.addRoute("/admin/plugins/security-audit", adminRoutes2, {
6226
+ description: "Security audit dashboard and admin pages",
6227
+ requiresAuth: true,
6228
+ priority: 50
6229
+ });
6230
+ builder.addRoute("/api/security-audit", apiRoutes2, {
6231
+ description: "Security audit API endpoints",
6232
+ requiresAuth: true,
6233
+ priority: 50
6234
+ });
6235
+ builder.addMenuItem("Security", "/admin/plugins/security-audit", {
6236
+ icon: `<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/></svg>`,
6237
+ order: 85
6238
+ });
6239
+ builder.lifecycle({
6240
+ install: async (context) => {
6241
+ console.log("[SecurityAudit] Plugin installed");
6242
+ },
6243
+ activate: async (context) => {
6244
+ console.log("[SecurityAudit] Plugin activated");
6245
+ },
6246
+ deactivate: async (context) => {
6247
+ console.log("[SecurityAudit] Plugin deactivated");
6248
+ },
6249
+ uninstall: async (context) => {
6250
+ console.log("[SecurityAudit] Plugin uninstalled");
6251
+ }
6252
+ });
6253
+ return builder.build();
6254
+ }
6255
+ var securityAuditPlugin = createSecurityAuditPlugin();
6256
+
6257
+ // src/middleware/plugin-menu.ts
6258
+ var MENU_PLUGINS = [
6259
+ securityAuditPlugin
6260
+ ];
6261
+ var MARKER = "<!-- DYNAMIC_PLUGIN_MENU -->";
6262
+ function renderMenuItem(item, currentPath) {
6263
+ const isActive = currentPath === item.path || currentPath.startsWith(item.path);
6264
+ const fallbackIcon = `<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>`;
6265
+ return `
6266
+ <span class="relative">
6267
+ ${isActive ? '<span class="absolute inset-y-2 -left-4 w-0.5 rounded-full bg-cyan-500 dark:bg-cyan-400"></span>' : ""}
6268
+ <a
6269
+ href="${item.path}"
6270
+ class="flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-sm/5 font-medium ${isActive ? "text-zinc-950 dark:text-white" : "text-zinc-950 hover:bg-zinc-950/5 dark:text-white dark:hover:bg-white/5"}"
6271
+ ${isActive ? 'data-current="true"' : ""}
6272
+ >
6273
+ <span class="shrink-0 ${isActive ? "fill-zinc-950 dark:fill-white" : "fill-zinc-500 dark:fill-zinc-400"}">
6274
+ ${item.icon || fallbackIcon}
6275
+ </span>
6276
+ <span class="truncate">${item.label}</span>
6277
+ </a>
6278
+ </span>`;
6279
+ }
6280
+ function pluginMenuMiddleware() {
6281
+ return async (c, next) => {
6282
+ const path = new URL(c.req.url).pathname;
6283
+ if (!path.startsWith("/admin")) {
6284
+ return next();
6285
+ }
6286
+ let activeMenuItems = [];
6287
+ try {
6288
+ const db = c.env.DB;
6289
+ const pluginNames = MENU_PLUGINS.map((p) => p.name);
6290
+ if (pluginNames.length > 0) {
6291
+ const placeholders = pluginNames.map(() => "?").join(",");
6292
+ const result = await db.prepare(
6293
+ `SELECT name FROM plugins WHERE name IN (${placeholders}) AND status = 'active'`
6294
+ ).bind(...pluginNames).all();
6295
+ const activeNames = new Set((result.results || []).map((r) => r.name));
6296
+ for (const plugin2 of MENU_PLUGINS) {
6297
+ if (activeNames.has(plugin2.name) && plugin2.menuItems) {
6298
+ activeMenuItems.push(...plugin2.menuItems);
6299
+ }
6300
+ }
6301
+ activeMenuItems.sort((a, b) => (a.order || 0) - (b.order || 0));
6302
+ }
6303
+ } catch {
6304
+ }
6305
+ c.set("pluginMenuItems", activeMenuItems.map((m) => ({ label: m.label, path: m.path, icon: m.icon || "" })));
6306
+ await next();
6307
+ if (activeMenuItems.length > 0 && c.res.headers.get("content-type")?.includes("text/html")) {
6308
+ const status = c.res.status;
6309
+ const headers = new Headers(c.res.headers);
6310
+ const html = await c.res.text();
6311
+ if (html.includes(MARKER)) {
6312
+ const renderedItems = activeMenuItems.map((item) => renderMenuItem(item, path)).join("");
6313
+ const newHtml = html.split(MARKER).join(renderedItems);
6314
+ c.res = new Response(newHtml, { status, headers });
6315
+ } else {
6316
+ c.res = new Response(html, { status, headers });
6317
+ }
6318
+ }
6319
+ };
6320
+ }
6321
+
6322
+ // src/plugins/cache/services/cache-config.ts
6323
+ var CACHE_CONFIGS = {
6324
+ // Content (high read, low write)
6325
+ content: {
6326
+ ttl: 3600,
6327
+ // 1 hour
6328
+ kvEnabled: true,
6329
+ memoryEnabled: true,
6330
+ namespace: "content",
6331
+ invalidateOn: ["content.update", "content.delete", "content.publish"],
6332
+ version: "v1"
6333
+ },
6334
+ // User data (medium read, medium write)
6335
+ user: {
6336
+ ttl: 900,
6337
+ // 15 minutes
6338
+ kvEnabled: true,
6339
+ memoryEnabled: true,
6340
+ namespace: "user",
6341
+ invalidateOn: ["user.update", "user.delete", "auth.login"],
6342
+ version: "v1"
6343
+ },
6344
+ // Configuration (high read, very low write)
6345
+ config: {
6346
+ ttl: 7200,
6347
+ // 2 hours
6348
+ kvEnabled: true,
6349
+ memoryEnabled: true,
6350
+ namespace: "config",
6351
+ invalidateOn: ["config.update", "plugin.activate", "plugin.deactivate"],
6352
+ version: "v1"
6353
+ },
6354
+ // Media metadata (high read, low write)
6355
+ media: {
6356
+ ttl: 3600,
6357
+ // 1 hour
6358
+ kvEnabled: true,
6359
+ memoryEnabled: true,
6360
+ namespace: "media",
6361
+ invalidateOn: ["media.upload", "media.delete", "media.update"],
6362
+ version: "v1"
6363
+ },
6364
+ // API responses (very high read, low write)
6365
+ api: {
6366
+ ttl: 300,
6367
+ // 5 minutes
6368
+ kvEnabled: true,
6369
+ memoryEnabled: true,
6370
+ namespace: "api",
6371
+ invalidateOn: ["content.update", "content.publish"],
6372
+ version: "v1"
6373
+ },
6374
+ // Session data (very high read, medium write)
6375
+ session: {
6376
+ ttl: 1800,
6377
+ // 30 minutes
6378
+ kvEnabled: false,
6379
+ // Only in-memory for sessions
6380
+ memoryEnabled: true,
6381
+ namespace: "session",
6382
+ invalidateOn: ["auth.logout"],
6383
+ version: "v1"
6384
+ },
6385
+ // Plugin data
6386
+ plugin: {
6387
+ ttl: 3600,
6388
+ // 1 hour
6389
+ kvEnabled: true,
4198
6390
  memoryEnabled: true,
4199
6391
  namespace: "plugin",
4200
6392
  invalidateOn: ["plugin.activate", "plugin.deactivate", "plugin.update"],
@@ -5089,7 +7281,7 @@ async function warmNamespace(namespace, entries) {
5089
7281
  }
5090
7282
 
5091
7283
  // src/templates/pages/admin-cache.template.ts
5092
- chunkLTKV7AE5_cjs.init_admin_layout_catalyst_template();
7284
+ chunkH4NHRZ6Y_cjs.init_admin_layout_catalyst_template();
5093
7285
  function renderCacheDashboard(data) {
5094
7286
  const pageContent = `
5095
7287
  <div class="space-y-6">
@@ -5268,7 +7460,7 @@ function renderCacheDashboard(data) {
5268
7460
  </script>
5269
7461
 
5270
7462
  <!-- Confirmation Dialogs -->
5271
- ${chunkHGKBMUYY_cjs.renderConfirmationDialog({
7463
+ ${chunkZV6ZCJ74_cjs.renderConfirmationDialog({
5272
7464
  id: "clear-all-cache-confirm",
5273
7465
  title: "Clear All Cache",
5274
7466
  message: "Are you sure you want to clear all cache entries? This cannot be undone.",
@@ -5279,7 +7471,7 @@ function renderCacheDashboard(data) {
5279
7471
  onConfirm: "performClearAllCaches()"
5280
7472
  })}
5281
7473
 
5282
- ${chunkHGKBMUYY_cjs.renderConfirmationDialog({
7474
+ ${chunkZV6ZCJ74_cjs.renderConfirmationDialog({
5283
7475
  id: "clear-namespace-cache-confirm",
5284
7476
  title: "Clear Namespace Cache",
5285
7477
  message: "Clear cache for this namespace?",
@@ -5290,7 +7482,7 @@ function renderCacheDashboard(data) {
5290
7482
  onConfirm: "performClearNamespaceCache()"
5291
7483
  })}
5292
7484
 
5293
- ${chunkHGKBMUYY_cjs.getConfirmationDialogScript()}
7485
+ ${chunkZV6ZCJ74_cjs.getConfirmationDialogScript()}
5294
7486
  `;
5295
7487
  const layoutData = {
5296
7488
  title: "Cache System",
@@ -5300,7 +7492,7 @@ function renderCacheDashboard(data) {
5300
7492
  version: data.version,
5301
7493
  content: pageContent
5302
7494
  };
5303
- return chunkLTKV7AE5_cjs.renderAdminLayoutCatalyst(layoutData);
7495
+ return chunkH4NHRZ6Y_cjs.renderAdminLayoutCatalyst(layoutData);
5304
7496
  }
5305
7497
  function renderStatCard(label, value, color, icon, colorOverride) {
5306
7498
  const finalColor = colorOverride || color;
@@ -5976,14 +8168,14 @@ var faviconSvg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
5976
8168
  // src/app.ts
5977
8169
  function createSonicJSApp(config = {}) {
5978
8170
  const app2 = new hono.Hono();
5979
- const appVersion = config.version || chunkE2GKK5HX_cjs.getCoreVersion();
8171
+ const appVersion = config.version || chunkRXNLGINR_cjs.getCoreVersion();
5980
8172
  const appName = config.name || "SonicJS AI";
5981
8173
  app2.use("*", async (c, next) => {
5982
8174
  c.set("appVersion", appVersion);
5983
8175
  await next();
5984
8176
  });
5985
- app2.use("*", chunk5GO3AMON_cjs.metricsMiddleware());
5986
- app2.use("*", chunk5GO3AMON_cjs.bootstrapMiddleware(config));
8177
+ app2.use("*", chunkVHNTCB2X_cjs.metricsMiddleware());
8178
+ app2.use("*", chunkVHNTCB2X_cjs.bootstrapMiddleware(config));
5987
8179
  if (config.middleware?.beforeAuth) {
5988
8180
  for (const middleware of config.middleware.beforeAuth) {
5989
8181
  app2.use("*", middleware);
@@ -5992,44 +8184,61 @@ function createSonicJSApp(config = {}) {
5992
8184
  app2.use("*", async (_c, next) => {
5993
8185
  await next();
5994
8186
  });
5995
- app2.use("*", chunk5GO3AMON_cjs.securityHeadersMiddleware());
5996
- app2.use("*", chunk5GO3AMON_cjs.csrfProtection());
8187
+ app2.use("*", chunkVHNTCB2X_cjs.securityHeadersMiddleware());
8188
+ app2.use("*", chunkVHNTCB2X_cjs.csrfProtection());
5997
8189
  if (config.middleware?.afterAuth) {
5998
8190
  for (const middleware of config.middleware.afterAuth) {
5999
8191
  app2.use("*", middleware);
6000
8192
  }
6001
8193
  }
6002
- app2.route("/api", chunkHGKBMUYY_cjs.api_default);
6003
- app2.route("/api/media", chunkHGKBMUYY_cjs.api_media_default);
6004
- app2.route("/api/system", chunkHGKBMUYY_cjs.api_system_default);
6005
- app2.route("/admin/api", chunkHGKBMUYY_cjs.admin_api_default);
6006
- app2.route("/admin/dashboard", chunkHGKBMUYY_cjs.router);
6007
- app2.route("/admin/collections", chunkHGKBMUYY_cjs.adminCollectionsRoutes);
6008
- app2.route("/admin/forms", chunkHGKBMUYY_cjs.adminFormsRoutes);
6009
- app2.route("/admin/settings", chunkHGKBMUYY_cjs.adminSettingsRoutes);
6010
- app2.route("/forms", chunkHGKBMUYY_cjs.public_forms_default);
6011
- app2.route("/api/forms", chunkHGKBMUYY_cjs.public_forms_default);
6012
- app2.route("/admin/api-reference", chunkHGKBMUYY_cjs.router2);
8194
+ app2.use("/admin/*", pluginMenuMiddleware());
8195
+ app2.route("/api", chunkZV6ZCJ74_cjs.api_default);
8196
+ app2.route("/api/media", chunkZV6ZCJ74_cjs.api_media_default);
8197
+ app2.route("/api/system", chunkZV6ZCJ74_cjs.api_system_default);
8198
+ app2.route("/admin/api", chunkZV6ZCJ74_cjs.admin_api_default);
8199
+ app2.route("/admin/dashboard", chunkZV6ZCJ74_cjs.router);
8200
+ app2.route("/admin/collections", chunkZV6ZCJ74_cjs.adminCollectionsRoutes);
8201
+ app2.route("/admin/forms", chunkZV6ZCJ74_cjs.adminFormsRoutes);
8202
+ app2.route("/admin/settings", chunkZV6ZCJ74_cjs.adminSettingsRoutes);
8203
+ app2.route("/forms", chunkZV6ZCJ74_cjs.public_forms_default);
8204
+ app2.route("/api/forms", chunkZV6ZCJ74_cjs.public_forms_default);
8205
+ app2.route("/admin/api-reference", chunkZV6ZCJ74_cjs.router2);
6013
8206
  app2.route("/admin/database-tools", createDatabaseToolsAdminRoutes());
6014
8207
  app2.route("/admin/seed-data", createSeedDataAdminRoutes());
6015
- app2.route("/admin/content", chunkHGKBMUYY_cjs.admin_content_default);
6016
- app2.route("/admin/media", chunkHGKBMUYY_cjs.adminMediaRoutes);
8208
+ app2.route("/admin/content", chunkZV6ZCJ74_cjs.admin_content_default);
8209
+ app2.route("/admin/media", chunkZV6ZCJ74_cjs.adminMediaRoutes);
8210
+ app2.use("/auth/*", securityAuditMiddleware());
8211
+ if (securityAuditPlugin.routes && securityAuditPlugin.routes.length > 0) {
8212
+ for (const route of securityAuditPlugin.routes) {
8213
+ app2.route(route.path, route.handler);
8214
+ }
8215
+ }
6017
8216
  if (aiSearchPlugin.routes && aiSearchPlugin.routes.length > 0) {
6018
8217
  for (const route of aiSearchPlugin.routes) {
6019
8218
  app2.route(route.path, route.handler);
6020
8219
  }
6021
8220
  }
6022
8221
  app2.route("/admin/cache", cache_default.getRoutes());
8222
+ if (oauthProvidersPlugin.routes && oauthProvidersPlugin.routes.length > 0) {
8223
+ for (const route of oauthProvidersPlugin.routes) {
8224
+ app2.route(route.path, route.handler);
8225
+ }
8226
+ }
8227
+ if (chunkZV6ZCJ74_cjs.userProfilesPlugin.routes && chunkZV6ZCJ74_cjs.userProfilesPlugin.routes.length > 0) {
8228
+ for (const route of chunkZV6ZCJ74_cjs.userProfilesPlugin.routes) {
8229
+ app2.route(route.path, route.handler);
8230
+ }
8231
+ }
6023
8232
  if (otpLoginPlugin.routes && otpLoginPlugin.routes.length > 0) {
6024
8233
  for (const route of otpLoginPlugin.routes) {
6025
8234
  app2.route(route.path, route.handler);
6026
8235
  }
6027
8236
  }
6028
- app2.route("/admin/plugins", chunkHGKBMUYY_cjs.adminPluginRoutes);
6029
- app2.route("/admin/logs", chunkHGKBMUYY_cjs.adminLogsRoutes);
6030
- app2.route("/admin", chunkHGKBMUYY_cjs.userRoutes);
6031
- app2.route("/auth", chunkHGKBMUYY_cjs.auth_default);
6032
- app2.route("/", chunkHGKBMUYY_cjs.test_cleanup_default);
8237
+ app2.route("/admin/plugins", chunkZV6ZCJ74_cjs.adminPluginRoutes);
8238
+ app2.route("/admin/logs", chunkZV6ZCJ74_cjs.adminLogsRoutes);
8239
+ app2.route("/admin", chunkZV6ZCJ74_cjs.userRoutes);
8240
+ app2.route("/auth", chunkZV6ZCJ74_cjs.auth_default);
8241
+ app2.route("/", chunkZV6ZCJ74_cjs.test_cleanup_default);
6033
8242
  if (emailPlugin.routes && emailPlugin.routes.length > 0) {
6034
8243
  for (const route of emailPlugin.routes) {
6035
8244
  app2.route(route.path, route.handler);
@@ -6092,7 +8301,7 @@ function createSonicJSApp(config = {}) {
6092
8301
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6093
8302
  });
6094
8303
  });
6095
- chunkLFAQUR7P_cjs.setAppInstance(app2);
8304
+ chunkNZWFCUDA_cjs.setAppInstance(app2);
6096
8305
  app2.notFound((c) => {
6097
8306
  return c.json({ error: "Not Found", status: 404 }, 404);
6098
8307
  });
@@ -6109,471 +8318,487 @@ function setupCoreRoutes(_app) {
6109
8318
  console.warn("setupCoreRoutes is deprecated. Use createSonicJSApp() instead.");
6110
8319
  }
6111
8320
  function createDb(d1$1) {
6112
- return d1.drizzle(d1$1, { schema: chunkLFAQUR7P_cjs.schema_exports });
8321
+ return d1.drizzle(d1$1, { schema: chunkNZWFCUDA_cjs.schema_exports });
6113
8322
  }
6114
8323
 
6115
8324
  // src/index.ts
6116
- var VERSION = chunkE2GKK5HX_cjs.package_default.version;
8325
+ var VERSION = chunkRXNLGINR_cjs.package_default.version;
6117
8326
 
6118
8327
  Object.defineProperty(exports, "ROUTES_INFO", {
6119
8328
  enumerable: true,
6120
- get: function () { return chunkHGKBMUYY_cjs.ROUTES_INFO; }
8329
+ get: function () { return chunkZV6ZCJ74_cjs.ROUTES_INFO; }
6121
8330
  });
6122
8331
  Object.defineProperty(exports, "adminApiRoutes", {
6123
8332
  enumerable: true,
6124
- get: function () { return chunkHGKBMUYY_cjs.admin_api_default; }
8333
+ get: function () { return chunkZV6ZCJ74_cjs.admin_api_default; }
6125
8334
  });
6126
8335
  Object.defineProperty(exports, "adminCheckboxRoutes", {
6127
8336
  enumerable: true,
6128
- get: function () { return chunkHGKBMUYY_cjs.adminCheckboxRoutes; }
8337
+ get: function () { return chunkZV6ZCJ74_cjs.adminCheckboxRoutes; }
6129
8338
  });
6130
8339
  Object.defineProperty(exports, "adminCodeExamplesRoutes", {
6131
8340
  enumerable: true,
6132
- get: function () { return chunkHGKBMUYY_cjs.admin_code_examples_default; }
8341
+ get: function () { return chunkZV6ZCJ74_cjs.admin_code_examples_default; }
6133
8342
  });
6134
8343
  Object.defineProperty(exports, "adminCollectionsRoutes", {
6135
8344
  enumerable: true,
6136
- get: function () { return chunkHGKBMUYY_cjs.adminCollectionsRoutes; }
8345
+ get: function () { return chunkZV6ZCJ74_cjs.adminCollectionsRoutes; }
6137
8346
  });
6138
8347
  Object.defineProperty(exports, "adminContentRoutes", {
6139
8348
  enumerable: true,
6140
- get: function () { return chunkHGKBMUYY_cjs.admin_content_default; }
8349
+ get: function () { return chunkZV6ZCJ74_cjs.admin_content_default; }
6141
8350
  });
6142
8351
  Object.defineProperty(exports, "adminDashboardRoutes", {
6143
8352
  enumerable: true,
6144
- get: function () { return chunkHGKBMUYY_cjs.router; }
8353
+ get: function () { return chunkZV6ZCJ74_cjs.router; }
6145
8354
  });
6146
8355
  Object.defineProperty(exports, "adminDesignRoutes", {
6147
8356
  enumerable: true,
6148
- get: function () { return chunkHGKBMUYY_cjs.adminDesignRoutes; }
8357
+ get: function () { return chunkZV6ZCJ74_cjs.adminDesignRoutes; }
6149
8358
  });
6150
8359
  Object.defineProperty(exports, "adminLogsRoutes", {
6151
8360
  enumerable: true,
6152
- get: function () { return chunkHGKBMUYY_cjs.adminLogsRoutes; }
8361
+ get: function () { return chunkZV6ZCJ74_cjs.adminLogsRoutes; }
6153
8362
  });
6154
8363
  Object.defineProperty(exports, "adminMediaRoutes", {
6155
8364
  enumerable: true,
6156
- get: function () { return chunkHGKBMUYY_cjs.adminMediaRoutes; }
8365
+ get: function () { return chunkZV6ZCJ74_cjs.adminMediaRoutes; }
6157
8366
  });
6158
8367
  Object.defineProperty(exports, "adminPluginRoutes", {
6159
8368
  enumerable: true,
6160
- get: function () { return chunkHGKBMUYY_cjs.adminPluginRoutes; }
8369
+ get: function () { return chunkZV6ZCJ74_cjs.adminPluginRoutes; }
6161
8370
  });
6162
8371
  Object.defineProperty(exports, "adminSettingsRoutes", {
6163
8372
  enumerable: true,
6164
- get: function () { return chunkHGKBMUYY_cjs.adminSettingsRoutes; }
8373
+ get: function () { return chunkZV6ZCJ74_cjs.adminSettingsRoutes; }
6165
8374
  });
6166
8375
  Object.defineProperty(exports, "adminTestimonialsRoutes", {
6167
8376
  enumerable: true,
6168
- get: function () { return chunkHGKBMUYY_cjs.admin_testimonials_default; }
8377
+ get: function () { return chunkZV6ZCJ74_cjs.admin_testimonials_default; }
6169
8378
  });
6170
8379
  Object.defineProperty(exports, "adminUsersRoutes", {
6171
8380
  enumerable: true,
6172
- get: function () { return chunkHGKBMUYY_cjs.userRoutes; }
8381
+ get: function () { return chunkZV6ZCJ74_cjs.userRoutes; }
6173
8382
  });
6174
8383
  Object.defineProperty(exports, "apiContentCrudRoutes", {
6175
8384
  enumerable: true,
6176
- get: function () { return chunkHGKBMUYY_cjs.api_content_crud_default; }
8385
+ get: function () { return chunkZV6ZCJ74_cjs.api_content_crud_default; }
6177
8386
  });
6178
8387
  Object.defineProperty(exports, "apiMediaRoutes", {
6179
8388
  enumerable: true,
6180
- get: function () { return chunkHGKBMUYY_cjs.api_media_default; }
8389
+ get: function () { return chunkZV6ZCJ74_cjs.api_media_default; }
6181
8390
  });
6182
8391
  Object.defineProperty(exports, "apiRoutes", {
6183
8392
  enumerable: true,
6184
- get: function () { return chunkHGKBMUYY_cjs.api_default; }
8393
+ get: function () { return chunkZV6ZCJ74_cjs.api_default; }
6185
8394
  });
6186
8395
  Object.defineProperty(exports, "apiSystemRoutes", {
6187
8396
  enumerable: true,
6188
- get: function () { return chunkHGKBMUYY_cjs.api_system_default; }
8397
+ get: function () { return chunkZV6ZCJ74_cjs.api_system_default; }
6189
8398
  });
6190
8399
  Object.defineProperty(exports, "authRoutes", {
6191
8400
  enumerable: true,
6192
- get: function () { return chunkHGKBMUYY_cjs.auth_default; }
8401
+ get: function () { return chunkZV6ZCJ74_cjs.auth_default; }
8402
+ });
8403
+ Object.defineProperty(exports, "createUserProfilesPlugin", {
8404
+ enumerable: true,
8405
+ get: function () { return chunkZV6ZCJ74_cjs.createUserProfilesPlugin; }
8406
+ });
8407
+ Object.defineProperty(exports, "defineUserProfile", {
8408
+ enumerable: true,
8409
+ get: function () { return chunkZV6ZCJ74_cjs.defineUserProfile; }
8410
+ });
8411
+ Object.defineProperty(exports, "getUserProfileConfig", {
8412
+ enumerable: true,
8413
+ get: function () { return chunkZV6ZCJ74_cjs.getUserProfileConfig; }
8414
+ });
8415
+ Object.defineProperty(exports, "userProfilesPlugin", {
8416
+ enumerable: true,
8417
+ get: function () { return chunkZV6ZCJ74_cjs.userProfilesPlugin; }
6193
8418
  });
6194
8419
  Object.defineProperty(exports, "Logger", {
6195
8420
  enumerable: true,
6196
- get: function () { return chunkLFAQUR7P_cjs.Logger; }
8421
+ get: function () { return chunkNZWFCUDA_cjs.Logger; }
6197
8422
  });
6198
8423
  Object.defineProperty(exports, "apiTokens", {
6199
8424
  enumerable: true,
6200
- get: function () { return chunkLFAQUR7P_cjs.apiTokens; }
8425
+ get: function () { return chunkNZWFCUDA_cjs.apiTokens; }
6201
8426
  });
6202
8427
  Object.defineProperty(exports, "collections", {
6203
8428
  enumerable: true,
6204
- get: function () { return chunkLFAQUR7P_cjs.collections; }
8429
+ get: function () { return chunkNZWFCUDA_cjs.collections; }
6205
8430
  });
6206
8431
  Object.defineProperty(exports, "content", {
6207
8432
  enumerable: true,
6208
- get: function () { return chunkLFAQUR7P_cjs.content; }
8433
+ get: function () { return chunkNZWFCUDA_cjs.content; }
6209
8434
  });
6210
8435
  Object.defineProperty(exports, "contentVersions", {
6211
8436
  enumerable: true,
6212
- get: function () { return chunkLFAQUR7P_cjs.contentVersions; }
8437
+ get: function () { return chunkNZWFCUDA_cjs.contentVersions; }
6213
8438
  });
6214
8439
  Object.defineProperty(exports, "getLogger", {
6215
8440
  enumerable: true,
6216
- get: function () { return chunkLFAQUR7P_cjs.getLogger; }
8441
+ get: function () { return chunkNZWFCUDA_cjs.getLogger; }
6217
8442
  });
6218
8443
  Object.defineProperty(exports, "initLogger", {
6219
8444
  enumerable: true,
6220
- get: function () { return chunkLFAQUR7P_cjs.initLogger; }
8445
+ get: function () { return chunkNZWFCUDA_cjs.initLogger; }
6221
8446
  });
6222
8447
  Object.defineProperty(exports, "insertCollectionSchema", {
6223
8448
  enumerable: true,
6224
- get: function () { return chunkLFAQUR7P_cjs.insertCollectionSchema; }
8449
+ get: function () { return chunkNZWFCUDA_cjs.insertCollectionSchema; }
6225
8450
  });
6226
8451
  Object.defineProperty(exports, "insertContentSchema", {
6227
8452
  enumerable: true,
6228
- get: function () { return chunkLFAQUR7P_cjs.insertContentSchema; }
8453
+ get: function () { return chunkNZWFCUDA_cjs.insertContentSchema; }
6229
8454
  });
6230
8455
  Object.defineProperty(exports, "insertLogConfigSchema", {
6231
8456
  enumerable: true,
6232
- get: function () { return chunkLFAQUR7P_cjs.insertLogConfigSchema; }
8457
+ get: function () { return chunkNZWFCUDA_cjs.insertLogConfigSchema; }
6233
8458
  });
6234
8459
  Object.defineProperty(exports, "insertMediaSchema", {
6235
8460
  enumerable: true,
6236
- get: function () { return chunkLFAQUR7P_cjs.insertMediaSchema; }
8461
+ get: function () { return chunkNZWFCUDA_cjs.insertMediaSchema; }
6237
8462
  });
6238
8463
  Object.defineProperty(exports, "insertPluginActivityLogSchema", {
6239
8464
  enumerable: true,
6240
- get: function () { return chunkLFAQUR7P_cjs.insertPluginActivityLogSchema; }
8465
+ get: function () { return chunkNZWFCUDA_cjs.insertPluginActivityLogSchema; }
6241
8466
  });
6242
8467
  Object.defineProperty(exports, "insertPluginAssetSchema", {
6243
8468
  enumerable: true,
6244
- get: function () { return chunkLFAQUR7P_cjs.insertPluginAssetSchema; }
8469
+ get: function () { return chunkNZWFCUDA_cjs.insertPluginAssetSchema; }
6245
8470
  });
6246
8471
  Object.defineProperty(exports, "insertPluginHookSchema", {
6247
8472
  enumerable: true,
6248
- get: function () { return chunkLFAQUR7P_cjs.insertPluginHookSchema; }
8473
+ get: function () { return chunkNZWFCUDA_cjs.insertPluginHookSchema; }
6249
8474
  });
6250
8475
  Object.defineProperty(exports, "insertPluginRouteSchema", {
6251
8476
  enumerable: true,
6252
- get: function () { return chunkLFAQUR7P_cjs.insertPluginRouteSchema; }
8477
+ get: function () { return chunkNZWFCUDA_cjs.insertPluginRouteSchema; }
6253
8478
  });
6254
8479
  Object.defineProperty(exports, "insertPluginSchema", {
6255
8480
  enumerable: true,
6256
- get: function () { return chunkLFAQUR7P_cjs.insertPluginSchema; }
8481
+ get: function () { return chunkNZWFCUDA_cjs.insertPluginSchema; }
6257
8482
  });
6258
8483
  Object.defineProperty(exports, "insertSystemLogSchema", {
6259
8484
  enumerable: true,
6260
- get: function () { return chunkLFAQUR7P_cjs.insertSystemLogSchema; }
8485
+ get: function () { return chunkNZWFCUDA_cjs.insertSystemLogSchema; }
6261
8486
  });
6262
8487
  Object.defineProperty(exports, "insertUserSchema", {
6263
8488
  enumerable: true,
6264
- get: function () { return chunkLFAQUR7P_cjs.insertUserSchema; }
8489
+ get: function () { return chunkNZWFCUDA_cjs.insertUserSchema; }
6265
8490
  });
6266
8491
  Object.defineProperty(exports, "insertWorkflowHistorySchema", {
6267
8492
  enumerable: true,
6268
- get: function () { return chunkLFAQUR7P_cjs.insertWorkflowHistorySchema; }
8493
+ get: function () { return chunkNZWFCUDA_cjs.insertWorkflowHistorySchema; }
6269
8494
  });
6270
8495
  Object.defineProperty(exports, "logConfig", {
6271
8496
  enumerable: true,
6272
- get: function () { return chunkLFAQUR7P_cjs.logConfig; }
8497
+ get: function () { return chunkNZWFCUDA_cjs.logConfig; }
6273
8498
  });
6274
8499
  Object.defineProperty(exports, "media", {
6275
8500
  enumerable: true,
6276
- get: function () { return chunkLFAQUR7P_cjs.media; }
8501
+ get: function () { return chunkNZWFCUDA_cjs.media; }
6277
8502
  });
6278
8503
  Object.defineProperty(exports, "pluginActivityLog", {
6279
8504
  enumerable: true,
6280
- get: function () { return chunkLFAQUR7P_cjs.pluginActivityLog; }
8505
+ get: function () { return chunkNZWFCUDA_cjs.pluginActivityLog; }
6281
8506
  });
6282
8507
  Object.defineProperty(exports, "pluginAssets", {
6283
8508
  enumerable: true,
6284
- get: function () { return chunkLFAQUR7P_cjs.pluginAssets; }
8509
+ get: function () { return chunkNZWFCUDA_cjs.pluginAssets; }
6285
8510
  });
6286
8511
  Object.defineProperty(exports, "pluginHooks", {
6287
8512
  enumerable: true,
6288
- get: function () { return chunkLFAQUR7P_cjs.pluginHooks; }
8513
+ get: function () { return chunkNZWFCUDA_cjs.pluginHooks; }
6289
8514
  });
6290
8515
  Object.defineProperty(exports, "pluginRoutes", {
6291
8516
  enumerable: true,
6292
- get: function () { return chunkLFAQUR7P_cjs.pluginRoutes; }
8517
+ get: function () { return chunkNZWFCUDA_cjs.pluginRoutes; }
6293
8518
  });
6294
8519
  Object.defineProperty(exports, "plugins", {
6295
8520
  enumerable: true,
6296
- get: function () { return chunkLFAQUR7P_cjs.plugins; }
8521
+ get: function () { return chunkNZWFCUDA_cjs.plugins; }
6297
8522
  });
6298
8523
  Object.defineProperty(exports, "selectCollectionSchema", {
6299
8524
  enumerable: true,
6300
- get: function () { return chunkLFAQUR7P_cjs.selectCollectionSchema; }
8525
+ get: function () { return chunkNZWFCUDA_cjs.selectCollectionSchema; }
6301
8526
  });
6302
8527
  Object.defineProperty(exports, "selectContentSchema", {
6303
8528
  enumerable: true,
6304
- get: function () { return chunkLFAQUR7P_cjs.selectContentSchema; }
8529
+ get: function () { return chunkNZWFCUDA_cjs.selectContentSchema; }
6305
8530
  });
6306
8531
  Object.defineProperty(exports, "selectLogConfigSchema", {
6307
8532
  enumerable: true,
6308
- get: function () { return chunkLFAQUR7P_cjs.selectLogConfigSchema; }
8533
+ get: function () { return chunkNZWFCUDA_cjs.selectLogConfigSchema; }
6309
8534
  });
6310
8535
  Object.defineProperty(exports, "selectMediaSchema", {
6311
8536
  enumerable: true,
6312
- get: function () { return chunkLFAQUR7P_cjs.selectMediaSchema; }
8537
+ get: function () { return chunkNZWFCUDA_cjs.selectMediaSchema; }
6313
8538
  });
6314
8539
  Object.defineProperty(exports, "selectPluginActivityLogSchema", {
6315
8540
  enumerable: true,
6316
- get: function () { return chunkLFAQUR7P_cjs.selectPluginActivityLogSchema; }
8541
+ get: function () { return chunkNZWFCUDA_cjs.selectPluginActivityLogSchema; }
6317
8542
  });
6318
8543
  Object.defineProperty(exports, "selectPluginAssetSchema", {
6319
8544
  enumerable: true,
6320
- get: function () { return chunkLFAQUR7P_cjs.selectPluginAssetSchema; }
8545
+ get: function () { return chunkNZWFCUDA_cjs.selectPluginAssetSchema; }
6321
8546
  });
6322
8547
  Object.defineProperty(exports, "selectPluginHookSchema", {
6323
8548
  enumerable: true,
6324
- get: function () { return chunkLFAQUR7P_cjs.selectPluginHookSchema; }
8549
+ get: function () { return chunkNZWFCUDA_cjs.selectPluginHookSchema; }
6325
8550
  });
6326
8551
  Object.defineProperty(exports, "selectPluginRouteSchema", {
6327
8552
  enumerable: true,
6328
- get: function () { return chunkLFAQUR7P_cjs.selectPluginRouteSchema; }
8553
+ get: function () { return chunkNZWFCUDA_cjs.selectPluginRouteSchema; }
6329
8554
  });
6330
8555
  Object.defineProperty(exports, "selectPluginSchema", {
6331
8556
  enumerable: true,
6332
- get: function () { return chunkLFAQUR7P_cjs.selectPluginSchema; }
8557
+ get: function () { return chunkNZWFCUDA_cjs.selectPluginSchema; }
6333
8558
  });
6334
8559
  Object.defineProperty(exports, "selectSystemLogSchema", {
6335
8560
  enumerable: true,
6336
- get: function () { return chunkLFAQUR7P_cjs.selectSystemLogSchema; }
8561
+ get: function () { return chunkNZWFCUDA_cjs.selectSystemLogSchema; }
6337
8562
  });
6338
8563
  Object.defineProperty(exports, "selectUserSchema", {
6339
8564
  enumerable: true,
6340
- get: function () { return chunkLFAQUR7P_cjs.selectUserSchema; }
8565
+ get: function () { return chunkNZWFCUDA_cjs.selectUserSchema; }
6341
8566
  });
6342
8567
  Object.defineProperty(exports, "selectWorkflowHistorySchema", {
6343
8568
  enumerable: true,
6344
- get: function () { return chunkLFAQUR7P_cjs.selectWorkflowHistorySchema; }
8569
+ get: function () { return chunkNZWFCUDA_cjs.selectWorkflowHistorySchema; }
6345
8570
  });
6346
8571
  Object.defineProperty(exports, "systemLogs", {
6347
8572
  enumerable: true,
6348
- get: function () { return chunkLFAQUR7P_cjs.systemLogs; }
8573
+ get: function () { return chunkNZWFCUDA_cjs.systemLogs; }
6349
8574
  });
6350
8575
  Object.defineProperty(exports, "users", {
6351
8576
  enumerable: true,
6352
- get: function () { return chunkLFAQUR7P_cjs.users; }
8577
+ get: function () { return chunkNZWFCUDA_cjs.users; }
6353
8578
  });
6354
8579
  Object.defineProperty(exports, "workflowHistory", {
6355
8580
  enumerable: true,
6356
- get: function () { return chunkLFAQUR7P_cjs.workflowHistory; }
8581
+ get: function () { return chunkNZWFCUDA_cjs.workflowHistory; }
6357
8582
  });
6358
8583
  Object.defineProperty(exports, "AuthManager", {
6359
8584
  enumerable: true,
6360
- get: function () { return chunk5GO3AMON_cjs.AuthManager; }
8585
+ get: function () { return chunkVHNTCB2X_cjs.AuthManager; }
6361
8586
  });
6362
8587
  Object.defineProperty(exports, "PermissionManager", {
6363
8588
  enumerable: true,
6364
- get: function () { return chunk5GO3AMON_cjs.PermissionManager; }
8589
+ get: function () { return chunkVHNTCB2X_cjs.PermissionManager; }
6365
8590
  });
6366
8591
  Object.defineProperty(exports, "bootstrapMiddleware", {
6367
8592
  enumerable: true,
6368
- get: function () { return chunk5GO3AMON_cjs.bootstrapMiddleware; }
8593
+ get: function () { return chunkVHNTCB2X_cjs.bootstrapMiddleware; }
6369
8594
  });
6370
8595
  Object.defineProperty(exports, "cacheHeaders", {
6371
8596
  enumerable: true,
6372
- get: function () { return chunk5GO3AMON_cjs.cacheHeaders; }
8597
+ get: function () { return chunkVHNTCB2X_cjs.cacheHeaders; }
6373
8598
  });
6374
8599
  Object.defineProperty(exports, "compressionMiddleware", {
6375
8600
  enumerable: true,
6376
- get: function () { return chunk5GO3AMON_cjs.compressionMiddleware; }
8601
+ get: function () { return chunkVHNTCB2X_cjs.compressionMiddleware; }
6377
8602
  });
6378
8603
  Object.defineProperty(exports, "detailedLoggingMiddleware", {
6379
8604
  enumerable: true,
6380
- get: function () { return chunk5GO3AMON_cjs.detailedLoggingMiddleware; }
8605
+ get: function () { return chunkVHNTCB2X_cjs.detailedLoggingMiddleware; }
6381
8606
  });
6382
8607
  Object.defineProperty(exports, "getActivePlugins", {
6383
8608
  enumerable: true,
6384
- get: function () { return chunk5GO3AMON_cjs.getActivePlugins; }
8609
+ get: function () { return chunkVHNTCB2X_cjs.getActivePlugins; }
6385
8610
  });
6386
8611
  Object.defineProperty(exports, "isPluginActive", {
6387
8612
  enumerable: true,
6388
- get: function () { return chunk5GO3AMON_cjs.isPluginActive; }
8613
+ get: function () { return chunkVHNTCB2X_cjs.isPluginActive; }
6389
8614
  });
6390
8615
  Object.defineProperty(exports, "logActivity", {
6391
8616
  enumerable: true,
6392
- get: function () { return chunk5GO3AMON_cjs.logActivity; }
8617
+ get: function () { return chunkVHNTCB2X_cjs.logActivity; }
6393
8618
  });
6394
8619
  Object.defineProperty(exports, "loggingMiddleware", {
6395
8620
  enumerable: true,
6396
- get: function () { return chunk5GO3AMON_cjs.loggingMiddleware; }
8621
+ get: function () { return chunkVHNTCB2X_cjs.loggingMiddleware; }
6397
8622
  });
6398
8623
  Object.defineProperty(exports, "optionalAuth", {
6399
8624
  enumerable: true,
6400
- get: function () { return chunk5GO3AMON_cjs.optionalAuth; }
8625
+ get: function () { return chunkVHNTCB2X_cjs.optionalAuth; }
6401
8626
  });
6402
8627
  Object.defineProperty(exports, "performanceLoggingMiddleware", {
6403
8628
  enumerable: true,
6404
- get: function () { return chunk5GO3AMON_cjs.performanceLoggingMiddleware; }
8629
+ get: function () { return chunkVHNTCB2X_cjs.performanceLoggingMiddleware; }
6405
8630
  });
6406
8631
  Object.defineProperty(exports, "requireActivePlugin", {
6407
8632
  enumerable: true,
6408
- get: function () { return chunk5GO3AMON_cjs.requireActivePlugin; }
8633
+ get: function () { return chunkVHNTCB2X_cjs.requireActivePlugin; }
6409
8634
  });
6410
8635
  Object.defineProperty(exports, "requireActivePlugins", {
6411
8636
  enumerable: true,
6412
- get: function () { return chunk5GO3AMON_cjs.requireActivePlugins; }
8637
+ get: function () { return chunkVHNTCB2X_cjs.requireActivePlugins; }
6413
8638
  });
6414
8639
  Object.defineProperty(exports, "requireAnyPermission", {
6415
8640
  enumerable: true,
6416
- get: function () { return chunk5GO3AMON_cjs.requireAnyPermission; }
8641
+ get: function () { return chunkVHNTCB2X_cjs.requireAnyPermission; }
6417
8642
  });
6418
8643
  Object.defineProperty(exports, "requireAuth", {
6419
8644
  enumerable: true,
6420
- get: function () { return chunk5GO3AMON_cjs.requireAuth; }
8645
+ get: function () { return chunkVHNTCB2X_cjs.requireAuth; }
6421
8646
  });
6422
8647
  Object.defineProperty(exports, "requirePermission", {
6423
8648
  enumerable: true,
6424
- get: function () { return chunk5GO3AMON_cjs.requirePermission; }
8649
+ get: function () { return chunkVHNTCB2X_cjs.requirePermission; }
6425
8650
  });
6426
8651
  Object.defineProperty(exports, "requireRole", {
6427
8652
  enumerable: true,
6428
- get: function () { return chunk5GO3AMON_cjs.requireRole; }
8653
+ get: function () { return chunkVHNTCB2X_cjs.requireRole; }
6429
8654
  });
6430
8655
  Object.defineProperty(exports, "securityHeaders", {
6431
8656
  enumerable: true,
6432
- get: function () { return chunk5GO3AMON_cjs.securityHeadersMiddleware; }
8657
+ get: function () { return chunkVHNTCB2X_cjs.securityHeadersMiddleware; }
6433
8658
  });
6434
8659
  Object.defineProperty(exports, "securityLoggingMiddleware", {
6435
8660
  enumerable: true,
6436
- get: function () { return chunk5GO3AMON_cjs.securityLoggingMiddleware; }
8661
+ get: function () { return chunkVHNTCB2X_cjs.securityLoggingMiddleware; }
6437
8662
  });
6438
8663
  Object.defineProperty(exports, "PluginBootstrapService", {
6439
8664
  enumerable: true,
6440
- get: function () { return chunkTWCQVJ6M_cjs.PluginBootstrapService; }
8665
+ get: function () { return chunkI6FFGQIT_cjs.PluginBootstrapService; }
6441
8666
  });
6442
8667
  Object.defineProperty(exports, "PluginServiceClass", {
6443
8668
  enumerable: true,
6444
- get: function () { return chunkTWCQVJ6M_cjs.PluginService; }
8669
+ get: function () { return chunkI6FFGQIT_cjs.PluginService; }
6445
8670
  });
6446
8671
  Object.defineProperty(exports, "backfillFormSubmissions", {
6447
8672
  enumerable: true,
6448
- get: function () { return chunkTWCQVJ6M_cjs.backfillFormSubmissions; }
8673
+ get: function () { return chunkI6FFGQIT_cjs.backfillFormSubmissions; }
6449
8674
  });
6450
8675
  Object.defineProperty(exports, "cleanupRemovedCollections", {
6451
8676
  enumerable: true,
6452
- get: function () { return chunkTWCQVJ6M_cjs.cleanupRemovedCollections; }
8677
+ get: function () { return chunkI6FFGQIT_cjs.cleanupRemovedCollections; }
6453
8678
  });
6454
8679
  Object.defineProperty(exports, "createContentFromSubmission", {
6455
8680
  enumerable: true,
6456
- get: function () { return chunkTWCQVJ6M_cjs.createContentFromSubmission; }
8681
+ get: function () { return chunkI6FFGQIT_cjs.createContentFromSubmission; }
6457
8682
  });
6458
8683
  Object.defineProperty(exports, "deriveCollectionSchemaFromFormio", {
6459
8684
  enumerable: true,
6460
- get: function () { return chunkTWCQVJ6M_cjs.deriveCollectionSchemaFromFormio; }
8685
+ get: function () { return chunkI6FFGQIT_cjs.deriveCollectionSchemaFromFormio; }
6461
8686
  });
6462
8687
  Object.defineProperty(exports, "deriveSubmissionTitle", {
6463
8688
  enumerable: true,
6464
- get: function () { return chunkTWCQVJ6M_cjs.deriveSubmissionTitle; }
8689
+ get: function () { return chunkI6FFGQIT_cjs.deriveSubmissionTitle; }
6465
8690
  });
6466
8691
  Object.defineProperty(exports, "fullCollectionSync", {
6467
8692
  enumerable: true,
6468
- get: function () { return chunkTWCQVJ6M_cjs.fullCollectionSync; }
8693
+ get: function () { return chunkI6FFGQIT_cjs.fullCollectionSync; }
6469
8694
  });
6470
8695
  Object.defineProperty(exports, "getAvailableCollectionNames", {
6471
8696
  enumerable: true,
6472
- get: function () { return chunkTWCQVJ6M_cjs.getAvailableCollectionNames; }
8697
+ get: function () { return chunkI6FFGQIT_cjs.getAvailableCollectionNames; }
6473
8698
  });
6474
8699
  Object.defineProperty(exports, "getManagedCollections", {
6475
8700
  enumerable: true,
6476
- get: function () { return chunkTWCQVJ6M_cjs.getManagedCollections; }
8701
+ get: function () { return chunkI6FFGQIT_cjs.getManagedCollections; }
6477
8702
  });
6478
8703
  Object.defineProperty(exports, "isCollectionManaged", {
6479
8704
  enumerable: true,
6480
- get: function () { return chunkTWCQVJ6M_cjs.isCollectionManaged; }
8705
+ get: function () { return chunkI6FFGQIT_cjs.isCollectionManaged; }
6481
8706
  });
6482
8707
  Object.defineProperty(exports, "loadCollectionConfig", {
6483
8708
  enumerable: true,
6484
- get: function () { return chunkTWCQVJ6M_cjs.loadCollectionConfig; }
8709
+ get: function () { return chunkI6FFGQIT_cjs.loadCollectionConfig; }
6485
8710
  });
6486
8711
  Object.defineProperty(exports, "loadCollectionConfigs", {
6487
8712
  enumerable: true,
6488
- get: function () { return chunkTWCQVJ6M_cjs.loadCollectionConfigs; }
8713
+ get: function () { return chunkI6FFGQIT_cjs.loadCollectionConfigs; }
6489
8714
  });
6490
8715
  Object.defineProperty(exports, "mapFormStatusToContentStatus", {
6491
8716
  enumerable: true,
6492
- get: function () { return chunkTWCQVJ6M_cjs.mapFormStatusToContentStatus; }
8717
+ get: function () { return chunkI6FFGQIT_cjs.mapFormStatusToContentStatus; }
6493
8718
  });
6494
8719
  Object.defineProperty(exports, "registerCollections", {
6495
8720
  enumerable: true,
6496
- get: function () { return chunkTWCQVJ6M_cjs.registerCollections; }
8721
+ get: function () { return chunkI6FFGQIT_cjs.registerCollections; }
6497
8722
  });
6498
8723
  Object.defineProperty(exports, "syncAllFormCollections", {
6499
8724
  enumerable: true,
6500
- get: function () { return chunkTWCQVJ6M_cjs.syncAllFormCollections; }
8725
+ get: function () { return chunkI6FFGQIT_cjs.syncAllFormCollections; }
6501
8726
  });
6502
8727
  Object.defineProperty(exports, "syncCollection", {
6503
8728
  enumerable: true,
6504
- get: function () { return chunkTWCQVJ6M_cjs.syncCollection; }
8729
+ get: function () { return chunkI6FFGQIT_cjs.syncCollection; }
6505
8730
  });
6506
8731
  Object.defineProperty(exports, "syncCollections", {
6507
8732
  enumerable: true,
6508
- get: function () { return chunkTWCQVJ6M_cjs.syncCollections; }
8733
+ get: function () { return chunkI6FFGQIT_cjs.syncCollections; }
6509
8734
  });
6510
8735
  Object.defineProperty(exports, "syncFormCollection", {
6511
8736
  enumerable: true,
6512
- get: function () { return chunkTWCQVJ6M_cjs.syncFormCollection; }
8737
+ get: function () { return chunkI6FFGQIT_cjs.syncFormCollection; }
6513
8738
  });
6514
8739
  Object.defineProperty(exports, "validateCollectionConfig", {
6515
8740
  enumerable: true,
6516
- get: function () { return chunkTWCQVJ6M_cjs.validateCollectionConfig; }
8741
+ get: function () { return chunkI6FFGQIT_cjs.validateCollectionConfig; }
6517
8742
  });
6518
8743
  Object.defineProperty(exports, "MigrationService", {
6519
8744
  enumerable: true,
6520
- get: function () { return chunkEAJJHE5F_cjs.MigrationService; }
8745
+ get: function () { return chunkAG3SIPP7_cjs.MigrationService; }
6521
8746
  });
6522
8747
  Object.defineProperty(exports, "renderFilterBar", {
6523
8748
  enumerable: true,
6524
- get: function () { return chunk3G7XX4UI_cjs.renderFilterBar; }
8749
+ get: function () { return chunkRBXFXT7H_cjs.renderFilterBar; }
6525
8750
  });
6526
8751
  Object.defineProperty(exports, "getConfirmationDialogScript", {
6527
8752
  enumerable: true,
6528
- get: function () { return chunkLTKV7AE5_cjs.getConfirmationDialogScript; }
8753
+ get: function () { return chunkH4NHRZ6Y_cjs.getConfirmationDialogScript; }
6529
8754
  });
6530
8755
  Object.defineProperty(exports, "renderAlert", {
6531
8756
  enumerable: true,
6532
- get: function () { return chunkLTKV7AE5_cjs.renderAlert; }
8757
+ get: function () { return chunkH4NHRZ6Y_cjs.renderAlert; }
6533
8758
  });
6534
8759
  Object.defineProperty(exports, "renderConfirmationDialog", {
6535
8760
  enumerable: true,
6536
- get: function () { return chunkLTKV7AE5_cjs.renderConfirmationDialog; }
8761
+ get: function () { return chunkH4NHRZ6Y_cjs.renderConfirmationDialog; }
6537
8762
  });
6538
8763
  Object.defineProperty(exports, "renderForm", {
6539
8764
  enumerable: true,
6540
- get: function () { return chunkLTKV7AE5_cjs.renderForm; }
8765
+ get: function () { return chunkH4NHRZ6Y_cjs.renderForm; }
6541
8766
  });
6542
8767
  Object.defineProperty(exports, "renderFormField", {
6543
8768
  enumerable: true,
6544
- get: function () { return chunkLTKV7AE5_cjs.renderFormField; }
8769
+ get: function () { return chunkH4NHRZ6Y_cjs.renderFormField; }
6545
8770
  });
6546
8771
  Object.defineProperty(exports, "renderPagination", {
6547
8772
  enumerable: true,
6548
- get: function () { return chunkLTKV7AE5_cjs.renderPagination; }
8773
+ get: function () { return chunkH4NHRZ6Y_cjs.renderPagination; }
6549
8774
  });
6550
8775
  Object.defineProperty(exports, "renderTable", {
6551
8776
  enumerable: true,
6552
- get: function () { return chunkLTKV7AE5_cjs.renderTable; }
8777
+ get: function () { return chunkH4NHRZ6Y_cjs.renderTable; }
6553
8778
  });
6554
8779
  Object.defineProperty(exports, "HookSystemImpl", {
6555
8780
  enumerable: true,
6556
- get: function () { return chunkMNFY6DWY_cjs.HookSystemImpl; }
8781
+ get: function () { return chunk56GUBLJE_cjs.HookSystemImpl; }
6557
8782
  });
6558
8783
  Object.defineProperty(exports, "HookUtils", {
6559
8784
  enumerable: true,
6560
- get: function () { return chunkMNFY6DWY_cjs.HookUtils; }
8785
+ get: function () { return chunk56GUBLJE_cjs.HookUtils; }
6561
8786
  });
6562
8787
  Object.defineProperty(exports, "PluginManagerClass", {
6563
8788
  enumerable: true,
6564
- get: function () { return chunkMNFY6DWY_cjs.PluginManager; }
8789
+ get: function () { return chunk56GUBLJE_cjs.PluginManager; }
6565
8790
  });
6566
8791
  Object.defineProperty(exports, "PluginRegistryImpl", {
6567
8792
  enumerable: true,
6568
- get: function () { return chunkMNFY6DWY_cjs.PluginRegistryImpl; }
8793
+ get: function () { return chunk56GUBLJE_cjs.PluginRegistryImpl; }
6569
8794
  });
6570
8795
  Object.defineProperty(exports, "PluginValidatorClass", {
6571
8796
  enumerable: true,
6572
- get: function () { return chunkMNFY6DWY_cjs.PluginValidator; }
8797
+ get: function () { return chunk56GUBLJE_cjs.PluginValidator; }
6573
8798
  });
6574
8799
  Object.defineProperty(exports, "ScopedHookSystemClass", {
6575
8800
  enumerable: true,
6576
- get: function () { return chunkMNFY6DWY_cjs.ScopedHookSystem; }
8801
+ get: function () { return chunk56GUBLJE_cjs.ScopedHookSystem; }
6577
8802
  });
6578
8803
  Object.defineProperty(exports, "PluginBuilder", {
6579
8804
  enumerable: true,
@@ -6585,31 +8810,31 @@ Object.defineProperty(exports, "PluginHelpers", {
6585
8810
  });
6586
8811
  Object.defineProperty(exports, "QueryFilterBuilder", {
6587
8812
  enumerable: true,
6588
- get: function () { return chunkE2GKK5HX_cjs.QueryFilterBuilder; }
8813
+ get: function () { return chunkRXNLGINR_cjs.QueryFilterBuilder; }
6589
8814
  });
6590
8815
  Object.defineProperty(exports, "SONICJS_VERSION", {
6591
8816
  enumerable: true,
6592
- get: function () { return chunkE2GKK5HX_cjs.SONICJS_VERSION; }
8817
+ get: function () { return chunkRXNLGINR_cjs.SONICJS_VERSION; }
6593
8818
  });
6594
8819
  Object.defineProperty(exports, "TemplateRenderer", {
6595
8820
  enumerable: true,
6596
- get: function () { return chunkE2GKK5HX_cjs.TemplateRenderer; }
8821
+ get: function () { return chunkRXNLGINR_cjs.TemplateRenderer; }
6597
8822
  });
6598
8823
  Object.defineProperty(exports, "buildQuery", {
6599
8824
  enumerable: true,
6600
- get: function () { return chunkE2GKK5HX_cjs.buildQuery; }
8825
+ get: function () { return chunkRXNLGINR_cjs.buildQuery; }
6601
8826
  });
6602
8827
  Object.defineProperty(exports, "getCoreVersion", {
6603
8828
  enumerable: true,
6604
- get: function () { return chunkE2GKK5HX_cjs.getCoreVersion; }
8829
+ get: function () { return chunkRXNLGINR_cjs.getCoreVersion; }
6605
8830
  });
6606
8831
  Object.defineProperty(exports, "renderTemplate", {
6607
8832
  enumerable: true,
6608
- get: function () { return chunkE2GKK5HX_cjs.renderTemplate; }
8833
+ get: function () { return chunkRXNLGINR_cjs.renderTemplate; }
6609
8834
  });
6610
8835
  Object.defineProperty(exports, "templateRenderer", {
6611
8836
  enumerable: true,
6612
- get: function () { return chunkE2GKK5HX_cjs.templateRenderer; }
8837
+ get: function () { return chunkRXNLGINR_cjs.templateRenderer; }
6613
8838
  });
6614
8839
  Object.defineProperty(exports, "metricsTracker", {
6615
8840
  enumerable: true,
@@ -6629,11 +8854,15 @@ Object.defineProperty(exports, "sanitizeObject", {
6629
8854
  });
6630
8855
  Object.defineProperty(exports, "HOOKS", {
6631
8856
  enumerable: true,
6632
- get: function () { return chunkKYGRJCZM_cjs.HOOKS; }
8857
+ get: function () { return chunkQTFKZBLC_cjs.HOOKS; }
6633
8858
  });
8859
+ exports.BUILT_IN_PROVIDERS = BUILT_IN_PROVIDERS;
8860
+ exports.OAuthService = OAuthService;
6634
8861
  exports.VERSION = VERSION;
6635
8862
  exports.createDb = createDb;
8863
+ exports.createOAuthProvidersPlugin = createOAuthProvidersPlugin;
6636
8864
  exports.createSonicJSApp = createSonicJSApp;
8865
+ exports.oauthProvidersPlugin = oauthProvidersPlugin;
6637
8866
  exports.setupCoreMiddleware = setupCoreMiddleware;
6638
8867
  exports.setupCoreRoutes = setupCoreRoutes;
6639
8868
  //# sourceMappingURL=index.cjs.map