@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.
- package/dist/{app-Ozl9agJG.d.cts → app-COElO4Rm.d.cts} +6 -1
- package/dist/{app-Ozl9agJG.d.ts → app-COElO4Rm.d.ts} +6 -1
- package/dist/{chunk-CJYFSKH7.js → chunk-2MXF4RYZ.js} +3 -3
- package/dist/{chunk-CJYFSKH7.js.map → chunk-2MXF4RYZ.js.map} +1 -1
- package/dist/{chunk-MNFY6DWY.cjs → chunk-56GUBLJE.cjs} +7 -7
- package/dist/{chunk-MNFY6DWY.cjs.map → chunk-56GUBLJE.cjs.map} +1 -1
- package/dist/{chunk-YXTFJPMN.js → chunk-6R6LAUR7.js} +3 -3
- package/dist/{chunk-YXTFJPMN.js.map → chunk-6R6LAUR7.js.map} +1 -1
- package/dist/{chunk-JJS7JZCH.js → chunk-76TX6XND.js} +4 -2
- package/dist/chunk-76TX6XND.js.map +1 -0
- package/dist/{chunk-EAJJHE5F.cjs → chunk-AG3SIPP7.cjs} +9 -2
- package/dist/chunk-AG3SIPP7.cjs.map +1 -0
- package/dist/{chunk-74XCYEI7.js → chunk-BWZBKLOC.js} +3 -3
- package/dist/{chunk-74XCYEI7.js.map → chunk-BWZBKLOC.js.map} +1 -1
- package/dist/{chunk-BUPNX3ZM.js → chunk-H3XXBAMO.js} +50 -2
- package/dist/chunk-H3XXBAMO.js.map +1 -0
- package/dist/{chunk-LTKV7AE5.cjs → chunk-H4NHRZ6Y.cjs} +4 -2
- package/dist/chunk-H4NHRZ6Y.cjs.map +1 -0
- package/dist/{chunk-JFMBYQTC.js → chunk-HXIYYE57.js} +6 -6
- package/dist/{chunk-JFMBYQTC.js.map → chunk-HXIYYE57.js.map} +1 -1
- package/dist/{chunk-TWCQVJ6M.cjs → chunk-I6FFGQIT.cjs} +50 -2
- package/dist/chunk-I6FFGQIT.cjs.map +1 -0
- package/dist/{chunk-SDAGUFOF.js → chunk-NDFHQOPP.js} +4318 -3820
- package/dist/chunk-NDFHQOPP.js.map +1 -0
- package/dist/{chunk-LFAQUR7P.cjs → chunk-NZWFCUDA.cjs} +26 -2
- package/dist/chunk-NZWFCUDA.cjs.map +1 -0
- package/dist/{chunk-KYGRJCZM.cjs → chunk-QTFKZBLC.cjs} +3 -2
- package/dist/chunk-QTFKZBLC.cjs.map +1 -0
- package/dist/{chunk-LOUJRBXV.js → chunk-QXOZI5Q2.js} +3 -2
- package/dist/chunk-QXOZI5Q2.js.map +1 -0
- package/dist/{chunk-3G7XX4UI.cjs → chunk-RBXFXT7H.cjs} +9 -9
- package/dist/{chunk-3G7XX4UI.cjs.map → chunk-RBXFXT7H.cjs.map} +1 -1
- package/dist/{chunk-E2GKK5HX.cjs → chunk-RXNLGINR.cjs} +3 -3
- package/dist/{chunk-E2GKK5HX.cjs.map → chunk-RXNLGINR.cjs.map} +1 -1
- package/dist/{chunk-VJCLJH3X.js → chunk-TBJY2FF7.js} +26 -2
- package/dist/chunk-TBJY2FF7.js.map +1 -0
- package/dist/{chunk-FW5CGNM2.js → chunk-U3ZMGBVC.js} +9 -2
- package/dist/chunk-U3ZMGBVC.js.map +1 -0
- package/dist/{chunk-5GO3AMON.cjs → chunk-VHNTCB2X.cjs} +10 -10
- package/dist/{chunk-5GO3AMON.cjs.map → chunk-VHNTCB2X.cjs.map} +1 -1
- package/dist/{chunk-HGKBMUYY.cjs → chunk-ZV6ZCJ74.cjs} +4498 -3996
- package/dist/chunk-ZV6ZCJ74.cjs.map +1 -0
- package/dist/index.cjs +2469 -240
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +175 -7
- package/dist/index.d.ts +175 -7
- package/dist/index.js +2294 -84
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +29 -29
- package/dist/middleware.d.cts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +3 -3
- package/dist/migrations-USSEHJC7.js +4 -0
- package/dist/{migrations-ADK6YNM2.js.map → migrations-USSEHJC7.js.map} +1 -1
- package/dist/migrations-ZE6IZNLB.cjs +13 -0
- package/dist/{migrations-EM2D6EG2.cjs.map → migrations-ZE6IZNLB.cjs.map} +1 -1
- package/dist/{plugin-0Xogrln-.d.cts → plugin-DDYetMF-.d.cts} +1 -0
- package/dist/{plugin-0Xogrln-.d.ts → plugin-DDYetMF-.d.ts} +1 -0
- package/dist/{plugin-bootstrap-CD63DZ-p.d.ts → plugin-bootstrap-CZ1GDum7.d.ts} +803 -2
- package/dist/{plugin-bootstrap-B8PXeGj_.d.cts → plugin-bootstrap-DVGLQrcO.d.cts} +803 -2
- package/dist/{plugin-manager-GcIeb226.d.cts → plugin-manager-BoM3Q7o7.d.cts} +1 -1
- package/dist/{plugin-manager-Clf2gXwj.d.ts → plugin-manager-Efx9RyDX.d.ts} +1 -1
- package/dist/plugins.cjs +10 -10
- package/dist/plugins.d.cts +2 -2
- package/dist/plugins.d.ts +2 -2
- package/dist/plugins.js +2 -2
- package/dist/routes.cjs +30 -30
- package/dist/routes.d.cts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +7 -7
- package/dist/services.cjs +39 -39
- package/dist/services.d.cts +1 -1
- package/dist/services.d.ts +1 -1
- package/dist/services.js +3 -3
- package/dist/templates.cjs +19 -19
- package/dist/templates.js +2 -2
- package/dist/types.cjs +2 -2
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.cjs +11 -11
- package/dist/utils.js +1 -1
- package/migrations/034_security_audit_plugin.sql +27 -0
- package/package.json +1 -1
- package/dist/chunk-BUPNX3ZM.js.map +0 -1
- package/dist/chunk-EAJJHE5F.cjs.map +0 -1
- package/dist/chunk-FW5CGNM2.js.map +0 -1
- package/dist/chunk-HGKBMUYY.cjs.map +0 -1
- package/dist/chunk-JJS7JZCH.js.map +0 -1
- package/dist/chunk-KYGRJCZM.cjs.map +0 -1
- package/dist/chunk-LFAQUR7P.cjs.map +0 -1
- package/dist/chunk-LOUJRBXV.js.map +0 -1
- package/dist/chunk-LTKV7AE5.cjs.map +0 -1
- package/dist/chunk-SDAGUFOF.js.map +0 -1
- package/dist/chunk-TWCQVJ6M.cjs.map +0 -1
- package/dist/chunk-VJCLJH3X.js.map +0 -1
- package/dist/migrations-ADK6YNM2.js +0 -4
- package/dist/migrations-EM2D6EG2.cjs +0 -13
package/dist/index.cjs
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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("*",
|
|
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
|
|
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
|
|
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
|
|
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("*",
|
|
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
|
|
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
|
-
|
|
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/
|
|
4131
|
-
var
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
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
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
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
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
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
|
-
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
-
${
|
|
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
|
|
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 ||
|
|
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("*",
|
|
5986
|
-
app2.use("*",
|
|
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("*",
|
|
5996
|
-
app2.use("*",
|
|
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.
|
|
6003
|
-
app2.route("/api
|
|
6004
|
-
app2.route("/api/
|
|
6005
|
-
app2.route("/
|
|
6006
|
-
app2.route("/admin/
|
|
6007
|
-
app2.route("/admin/
|
|
6008
|
-
app2.route("/admin/
|
|
6009
|
-
app2.route("/admin/
|
|
6010
|
-
app2.route("/
|
|
6011
|
-
app2.route("/
|
|
6012
|
-
app2.route("/
|
|
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",
|
|
6016
|
-
app2.route("/admin/media",
|
|
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",
|
|
6029
|
-
app2.route("/admin/logs",
|
|
6030
|
-
app2.route("/admin",
|
|
6031
|
-
app2.route("/auth",
|
|
6032
|
-
app2.route("/",
|
|
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
|
-
|
|
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:
|
|
8321
|
+
return d1.drizzle(d1$1, { schema: chunkNZWFCUDA_cjs.schema_exports });
|
|
6113
8322
|
}
|
|
6114
8323
|
|
|
6115
8324
|
// src/index.ts
|
|
6116
|
-
var 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
|
|
8329
|
+
get: function () { return chunkZV6ZCJ74_cjs.ROUTES_INFO; }
|
|
6121
8330
|
});
|
|
6122
8331
|
Object.defineProperty(exports, "adminApiRoutes", {
|
|
6123
8332
|
enumerable: true,
|
|
6124
|
-
get: function () { return
|
|
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
|
|
8337
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminCheckboxRoutes; }
|
|
6129
8338
|
});
|
|
6130
8339
|
Object.defineProperty(exports, "adminCodeExamplesRoutes", {
|
|
6131
8340
|
enumerable: true,
|
|
6132
|
-
get: function () { return
|
|
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
|
|
8345
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminCollectionsRoutes; }
|
|
6137
8346
|
});
|
|
6138
8347
|
Object.defineProperty(exports, "adminContentRoutes", {
|
|
6139
8348
|
enumerable: true,
|
|
6140
|
-
get: function () { return
|
|
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
|
|
8353
|
+
get: function () { return chunkZV6ZCJ74_cjs.router; }
|
|
6145
8354
|
});
|
|
6146
8355
|
Object.defineProperty(exports, "adminDesignRoutes", {
|
|
6147
8356
|
enumerable: true,
|
|
6148
|
-
get: function () { return
|
|
8357
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminDesignRoutes; }
|
|
6149
8358
|
});
|
|
6150
8359
|
Object.defineProperty(exports, "adminLogsRoutes", {
|
|
6151
8360
|
enumerable: true,
|
|
6152
|
-
get: function () { return
|
|
8361
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminLogsRoutes; }
|
|
6153
8362
|
});
|
|
6154
8363
|
Object.defineProperty(exports, "adminMediaRoutes", {
|
|
6155
8364
|
enumerable: true,
|
|
6156
|
-
get: function () { return
|
|
8365
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminMediaRoutes; }
|
|
6157
8366
|
});
|
|
6158
8367
|
Object.defineProperty(exports, "adminPluginRoutes", {
|
|
6159
8368
|
enumerable: true,
|
|
6160
|
-
get: function () { return
|
|
8369
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminPluginRoutes; }
|
|
6161
8370
|
});
|
|
6162
8371
|
Object.defineProperty(exports, "adminSettingsRoutes", {
|
|
6163
8372
|
enumerable: true,
|
|
6164
|
-
get: function () { return
|
|
8373
|
+
get: function () { return chunkZV6ZCJ74_cjs.adminSettingsRoutes; }
|
|
6165
8374
|
});
|
|
6166
8375
|
Object.defineProperty(exports, "adminTestimonialsRoutes", {
|
|
6167
8376
|
enumerable: true,
|
|
6168
|
-
get: function () { return
|
|
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
|
|
8381
|
+
get: function () { return chunkZV6ZCJ74_cjs.userRoutes; }
|
|
6173
8382
|
});
|
|
6174
8383
|
Object.defineProperty(exports, "apiContentCrudRoutes", {
|
|
6175
8384
|
enumerable: true,
|
|
6176
|
-
get: function () { return
|
|
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
|
|
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
|
|
8393
|
+
get: function () { return chunkZV6ZCJ74_cjs.api_default; }
|
|
6185
8394
|
});
|
|
6186
8395
|
Object.defineProperty(exports, "apiSystemRoutes", {
|
|
6187
8396
|
enumerable: true,
|
|
6188
|
-
get: function () { return
|
|
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
|
|
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
|
|
8421
|
+
get: function () { return chunkNZWFCUDA_cjs.Logger; }
|
|
6197
8422
|
});
|
|
6198
8423
|
Object.defineProperty(exports, "apiTokens", {
|
|
6199
8424
|
enumerable: true,
|
|
6200
|
-
get: function () { return
|
|
8425
|
+
get: function () { return chunkNZWFCUDA_cjs.apiTokens; }
|
|
6201
8426
|
});
|
|
6202
8427
|
Object.defineProperty(exports, "collections", {
|
|
6203
8428
|
enumerable: true,
|
|
6204
|
-
get: function () { return
|
|
8429
|
+
get: function () { return chunkNZWFCUDA_cjs.collections; }
|
|
6205
8430
|
});
|
|
6206
8431
|
Object.defineProperty(exports, "content", {
|
|
6207
8432
|
enumerable: true,
|
|
6208
|
-
get: function () { return
|
|
8433
|
+
get: function () { return chunkNZWFCUDA_cjs.content; }
|
|
6209
8434
|
});
|
|
6210
8435
|
Object.defineProperty(exports, "contentVersions", {
|
|
6211
8436
|
enumerable: true,
|
|
6212
|
-
get: function () { return
|
|
8437
|
+
get: function () { return chunkNZWFCUDA_cjs.contentVersions; }
|
|
6213
8438
|
});
|
|
6214
8439
|
Object.defineProperty(exports, "getLogger", {
|
|
6215
8440
|
enumerable: true,
|
|
6216
|
-
get: function () { return
|
|
8441
|
+
get: function () { return chunkNZWFCUDA_cjs.getLogger; }
|
|
6217
8442
|
});
|
|
6218
8443
|
Object.defineProperty(exports, "initLogger", {
|
|
6219
8444
|
enumerable: true,
|
|
6220
|
-
get: function () { return
|
|
8445
|
+
get: function () { return chunkNZWFCUDA_cjs.initLogger; }
|
|
6221
8446
|
});
|
|
6222
8447
|
Object.defineProperty(exports, "insertCollectionSchema", {
|
|
6223
8448
|
enumerable: true,
|
|
6224
|
-
get: function () { return
|
|
8449
|
+
get: function () { return chunkNZWFCUDA_cjs.insertCollectionSchema; }
|
|
6225
8450
|
});
|
|
6226
8451
|
Object.defineProperty(exports, "insertContentSchema", {
|
|
6227
8452
|
enumerable: true,
|
|
6228
|
-
get: function () { return
|
|
8453
|
+
get: function () { return chunkNZWFCUDA_cjs.insertContentSchema; }
|
|
6229
8454
|
});
|
|
6230
8455
|
Object.defineProperty(exports, "insertLogConfigSchema", {
|
|
6231
8456
|
enumerable: true,
|
|
6232
|
-
get: function () { return
|
|
8457
|
+
get: function () { return chunkNZWFCUDA_cjs.insertLogConfigSchema; }
|
|
6233
8458
|
});
|
|
6234
8459
|
Object.defineProperty(exports, "insertMediaSchema", {
|
|
6235
8460
|
enumerable: true,
|
|
6236
|
-
get: function () { return
|
|
8461
|
+
get: function () { return chunkNZWFCUDA_cjs.insertMediaSchema; }
|
|
6237
8462
|
});
|
|
6238
8463
|
Object.defineProperty(exports, "insertPluginActivityLogSchema", {
|
|
6239
8464
|
enumerable: true,
|
|
6240
|
-
get: function () { return
|
|
8465
|
+
get: function () { return chunkNZWFCUDA_cjs.insertPluginActivityLogSchema; }
|
|
6241
8466
|
});
|
|
6242
8467
|
Object.defineProperty(exports, "insertPluginAssetSchema", {
|
|
6243
8468
|
enumerable: true,
|
|
6244
|
-
get: function () { return
|
|
8469
|
+
get: function () { return chunkNZWFCUDA_cjs.insertPluginAssetSchema; }
|
|
6245
8470
|
});
|
|
6246
8471
|
Object.defineProperty(exports, "insertPluginHookSchema", {
|
|
6247
8472
|
enumerable: true,
|
|
6248
|
-
get: function () { return
|
|
8473
|
+
get: function () { return chunkNZWFCUDA_cjs.insertPluginHookSchema; }
|
|
6249
8474
|
});
|
|
6250
8475
|
Object.defineProperty(exports, "insertPluginRouteSchema", {
|
|
6251
8476
|
enumerable: true,
|
|
6252
|
-
get: function () { return
|
|
8477
|
+
get: function () { return chunkNZWFCUDA_cjs.insertPluginRouteSchema; }
|
|
6253
8478
|
});
|
|
6254
8479
|
Object.defineProperty(exports, "insertPluginSchema", {
|
|
6255
8480
|
enumerable: true,
|
|
6256
|
-
get: function () { return
|
|
8481
|
+
get: function () { return chunkNZWFCUDA_cjs.insertPluginSchema; }
|
|
6257
8482
|
});
|
|
6258
8483
|
Object.defineProperty(exports, "insertSystemLogSchema", {
|
|
6259
8484
|
enumerable: true,
|
|
6260
|
-
get: function () { return
|
|
8485
|
+
get: function () { return chunkNZWFCUDA_cjs.insertSystemLogSchema; }
|
|
6261
8486
|
});
|
|
6262
8487
|
Object.defineProperty(exports, "insertUserSchema", {
|
|
6263
8488
|
enumerable: true,
|
|
6264
|
-
get: function () { return
|
|
8489
|
+
get: function () { return chunkNZWFCUDA_cjs.insertUserSchema; }
|
|
6265
8490
|
});
|
|
6266
8491
|
Object.defineProperty(exports, "insertWorkflowHistorySchema", {
|
|
6267
8492
|
enumerable: true,
|
|
6268
|
-
get: function () { return
|
|
8493
|
+
get: function () { return chunkNZWFCUDA_cjs.insertWorkflowHistorySchema; }
|
|
6269
8494
|
});
|
|
6270
8495
|
Object.defineProperty(exports, "logConfig", {
|
|
6271
8496
|
enumerable: true,
|
|
6272
|
-
get: function () { return
|
|
8497
|
+
get: function () { return chunkNZWFCUDA_cjs.logConfig; }
|
|
6273
8498
|
});
|
|
6274
8499
|
Object.defineProperty(exports, "media", {
|
|
6275
8500
|
enumerable: true,
|
|
6276
|
-
get: function () { return
|
|
8501
|
+
get: function () { return chunkNZWFCUDA_cjs.media; }
|
|
6277
8502
|
});
|
|
6278
8503
|
Object.defineProperty(exports, "pluginActivityLog", {
|
|
6279
8504
|
enumerable: true,
|
|
6280
|
-
get: function () { return
|
|
8505
|
+
get: function () { return chunkNZWFCUDA_cjs.pluginActivityLog; }
|
|
6281
8506
|
});
|
|
6282
8507
|
Object.defineProperty(exports, "pluginAssets", {
|
|
6283
8508
|
enumerable: true,
|
|
6284
|
-
get: function () { return
|
|
8509
|
+
get: function () { return chunkNZWFCUDA_cjs.pluginAssets; }
|
|
6285
8510
|
});
|
|
6286
8511
|
Object.defineProperty(exports, "pluginHooks", {
|
|
6287
8512
|
enumerable: true,
|
|
6288
|
-
get: function () { return
|
|
8513
|
+
get: function () { return chunkNZWFCUDA_cjs.pluginHooks; }
|
|
6289
8514
|
});
|
|
6290
8515
|
Object.defineProperty(exports, "pluginRoutes", {
|
|
6291
8516
|
enumerable: true,
|
|
6292
|
-
get: function () { return
|
|
8517
|
+
get: function () { return chunkNZWFCUDA_cjs.pluginRoutes; }
|
|
6293
8518
|
});
|
|
6294
8519
|
Object.defineProperty(exports, "plugins", {
|
|
6295
8520
|
enumerable: true,
|
|
6296
|
-
get: function () { return
|
|
8521
|
+
get: function () { return chunkNZWFCUDA_cjs.plugins; }
|
|
6297
8522
|
});
|
|
6298
8523
|
Object.defineProperty(exports, "selectCollectionSchema", {
|
|
6299
8524
|
enumerable: true,
|
|
6300
|
-
get: function () { return
|
|
8525
|
+
get: function () { return chunkNZWFCUDA_cjs.selectCollectionSchema; }
|
|
6301
8526
|
});
|
|
6302
8527
|
Object.defineProperty(exports, "selectContentSchema", {
|
|
6303
8528
|
enumerable: true,
|
|
6304
|
-
get: function () { return
|
|
8529
|
+
get: function () { return chunkNZWFCUDA_cjs.selectContentSchema; }
|
|
6305
8530
|
});
|
|
6306
8531
|
Object.defineProperty(exports, "selectLogConfigSchema", {
|
|
6307
8532
|
enumerable: true,
|
|
6308
|
-
get: function () { return
|
|
8533
|
+
get: function () { return chunkNZWFCUDA_cjs.selectLogConfigSchema; }
|
|
6309
8534
|
});
|
|
6310
8535
|
Object.defineProperty(exports, "selectMediaSchema", {
|
|
6311
8536
|
enumerable: true,
|
|
6312
|
-
get: function () { return
|
|
8537
|
+
get: function () { return chunkNZWFCUDA_cjs.selectMediaSchema; }
|
|
6313
8538
|
});
|
|
6314
8539
|
Object.defineProperty(exports, "selectPluginActivityLogSchema", {
|
|
6315
8540
|
enumerable: true,
|
|
6316
|
-
get: function () { return
|
|
8541
|
+
get: function () { return chunkNZWFCUDA_cjs.selectPluginActivityLogSchema; }
|
|
6317
8542
|
});
|
|
6318
8543
|
Object.defineProperty(exports, "selectPluginAssetSchema", {
|
|
6319
8544
|
enumerable: true,
|
|
6320
|
-
get: function () { return
|
|
8545
|
+
get: function () { return chunkNZWFCUDA_cjs.selectPluginAssetSchema; }
|
|
6321
8546
|
});
|
|
6322
8547
|
Object.defineProperty(exports, "selectPluginHookSchema", {
|
|
6323
8548
|
enumerable: true,
|
|
6324
|
-
get: function () { return
|
|
8549
|
+
get: function () { return chunkNZWFCUDA_cjs.selectPluginHookSchema; }
|
|
6325
8550
|
});
|
|
6326
8551
|
Object.defineProperty(exports, "selectPluginRouteSchema", {
|
|
6327
8552
|
enumerable: true,
|
|
6328
|
-
get: function () { return
|
|
8553
|
+
get: function () { return chunkNZWFCUDA_cjs.selectPluginRouteSchema; }
|
|
6329
8554
|
});
|
|
6330
8555
|
Object.defineProperty(exports, "selectPluginSchema", {
|
|
6331
8556
|
enumerable: true,
|
|
6332
|
-
get: function () { return
|
|
8557
|
+
get: function () { return chunkNZWFCUDA_cjs.selectPluginSchema; }
|
|
6333
8558
|
});
|
|
6334
8559
|
Object.defineProperty(exports, "selectSystemLogSchema", {
|
|
6335
8560
|
enumerable: true,
|
|
6336
|
-
get: function () { return
|
|
8561
|
+
get: function () { return chunkNZWFCUDA_cjs.selectSystemLogSchema; }
|
|
6337
8562
|
});
|
|
6338
8563
|
Object.defineProperty(exports, "selectUserSchema", {
|
|
6339
8564
|
enumerable: true,
|
|
6340
|
-
get: function () { return
|
|
8565
|
+
get: function () { return chunkNZWFCUDA_cjs.selectUserSchema; }
|
|
6341
8566
|
});
|
|
6342
8567
|
Object.defineProperty(exports, "selectWorkflowHistorySchema", {
|
|
6343
8568
|
enumerable: true,
|
|
6344
|
-
get: function () { return
|
|
8569
|
+
get: function () { return chunkNZWFCUDA_cjs.selectWorkflowHistorySchema; }
|
|
6345
8570
|
});
|
|
6346
8571
|
Object.defineProperty(exports, "systemLogs", {
|
|
6347
8572
|
enumerable: true,
|
|
6348
|
-
get: function () { return
|
|
8573
|
+
get: function () { return chunkNZWFCUDA_cjs.systemLogs; }
|
|
6349
8574
|
});
|
|
6350
8575
|
Object.defineProperty(exports, "users", {
|
|
6351
8576
|
enumerable: true,
|
|
6352
|
-
get: function () { return
|
|
8577
|
+
get: function () { return chunkNZWFCUDA_cjs.users; }
|
|
6353
8578
|
});
|
|
6354
8579
|
Object.defineProperty(exports, "workflowHistory", {
|
|
6355
8580
|
enumerable: true,
|
|
6356
|
-
get: function () { return
|
|
8581
|
+
get: function () { return chunkNZWFCUDA_cjs.workflowHistory; }
|
|
6357
8582
|
});
|
|
6358
8583
|
Object.defineProperty(exports, "AuthManager", {
|
|
6359
8584
|
enumerable: true,
|
|
6360
|
-
get: function () { return
|
|
8585
|
+
get: function () { return chunkVHNTCB2X_cjs.AuthManager; }
|
|
6361
8586
|
});
|
|
6362
8587
|
Object.defineProperty(exports, "PermissionManager", {
|
|
6363
8588
|
enumerable: true,
|
|
6364
|
-
get: function () { return
|
|
8589
|
+
get: function () { return chunkVHNTCB2X_cjs.PermissionManager; }
|
|
6365
8590
|
});
|
|
6366
8591
|
Object.defineProperty(exports, "bootstrapMiddleware", {
|
|
6367
8592
|
enumerable: true,
|
|
6368
|
-
get: function () { return
|
|
8593
|
+
get: function () { return chunkVHNTCB2X_cjs.bootstrapMiddleware; }
|
|
6369
8594
|
});
|
|
6370
8595
|
Object.defineProperty(exports, "cacheHeaders", {
|
|
6371
8596
|
enumerable: true,
|
|
6372
|
-
get: function () { return
|
|
8597
|
+
get: function () { return chunkVHNTCB2X_cjs.cacheHeaders; }
|
|
6373
8598
|
});
|
|
6374
8599
|
Object.defineProperty(exports, "compressionMiddleware", {
|
|
6375
8600
|
enumerable: true,
|
|
6376
|
-
get: function () { return
|
|
8601
|
+
get: function () { return chunkVHNTCB2X_cjs.compressionMiddleware; }
|
|
6377
8602
|
});
|
|
6378
8603
|
Object.defineProperty(exports, "detailedLoggingMiddleware", {
|
|
6379
8604
|
enumerable: true,
|
|
6380
|
-
get: function () { return
|
|
8605
|
+
get: function () { return chunkVHNTCB2X_cjs.detailedLoggingMiddleware; }
|
|
6381
8606
|
});
|
|
6382
8607
|
Object.defineProperty(exports, "getActivePlugins", {
|
|
6383
8608
|
enumerable: true,
|
|
6384
|
-
get: function () { return
|
|
8609
|
+
get: function () { return chunkVHNTCB2X_cjs.getActivePlugins; }
|
|
6385
8610
|
});
|
|
6386
8611
|
Object.defineProperty(exports, "isPluginActive", {
|
|
6387
8612
|
enumerable: true,
|
|
6388
|
-
get: function () { return
|
|
8613
|
+
get: function () { return chunkVHNTCB2X_cjs.isPluginActive; }
|
|
6389
8614
|
});
|
|
6390
8615
|
Object.defineProperty(exports, "logActivity", {
|
|
6391
8616
|
enumerable: true,
|
|
6392
|
-
get: function () { return
|
|
8617
|
+
get: function () { return chunkVHNTCB2X_cjs.logActivity; }
|
|
6393
8618
|
});
|
|
6394
8619
|
Object.defineProperty(exports, "loggingMiddleware", {
|
|
6395
8620
|
enumerable: true,
|
|
6396
|
-
get: function () { return
|
|
8621
|
+
get: function () { return chunkVHNTCB2X_cjs.loggingMiddleware; }
|
|
6397
8622
|
});
|
|
6398
8623
|
Object.defineProperty(exports, "optionalAuth", {
|
|
6399
8624
|
enumerable: true,
|
|
6400
|
-
get: function () { return
|
|
8625
|
+
get: function () { return chunkVHNTCB2X_cjs.optionalAuth; }
|
|
6401
8626
|
});
|
|
6402
8627
|
Object.defineProperty(exports, "performanceLoggingMiddleware", {
|
|
6403
8628
|
enumerable: true,
|
|
6404
|
-
get: function () { return
|
|
8629
|
+
get: function () { return chunkVHNTCB2X_cjs.performanceLoggingMiddleware; }
|
|
6405
8630
|
});
|
|
6406
8631
|
Object.defineProperty(exports, "requireActivePlugin", {
|
|
6407
8632
|
enumerable: true,
|
|
6408
|
-
get: function () { return
|
|
8633
|
+
get: function () { return chunkVHNTCB2X_cjs.requireActivePlugin; }
|
|
6409
8634
|
});
|
|
6410
8635
|
Object.defineProperty(exports, "requireActivePlugins", {
|
|
6411
8636
|
enumerable: true,
|
|
6412
|
-
get: function () { return
|
|
8637
|
+
get: function () { return chunkVHNTCB2X_cjs.requireActivePlugins; }
|
|
6413
8638
|
});
|
|
6414
8639
|
Object.defineProperty(exports, "requireAnyPermission", {
|
|
6415
8640
|
enumerable: true,
|
|
6416
|
-
get: function () { return
|
|
8641
|
+
get: function () { return chunkVHNTCB2X_cjs.requireAnyPermission; }
|
|
6417
8642
|
});
|
|
6418
8643
|
Object.defineProperty(exports, "requireAuth", {
|
|
6419
8644
|
enumerable: true,
|
|
6420
|
-
get: function () { return
|
|
8645
|
+
get: function () { return chunkVHNTCB2X_cjs.requireAuth; }
|
|
6421
8646
|
});
|
|
6422
8647
|
Object.defineProperty(exports, "requirePermission", {
|
|
6423
8648
|
enumerable: true,
|
|
6424
|
-
get: function () { return
|
|
8649
|
+
get: function () { return chunkVHNTCB2X_cjs.requirePermission; }
|
|
6425
8650
|
});
|
|
6426
8651
|
Object.defineProperty(exports, "requireRole", {
|
|
6427
8652
|
enumerable: true,
|
|
6428
|
-
get: function () { return
|
|
8653
|
+
get: function () { return chunkVHNTCB2X_cjs.requireRole; }
|
|
6429
8654
|
});
|
|
6430
8655
|
Object.defineProperty(exports, "securityHeaders", {
|
|
6431
8656
|
enumerable: true,
|
|
6432
|
-
get: function () { return
|
|
8657
|
+
get: function () { return chunkVHNTCB2X_cjs.securityHeadersMiddleware; }
|
|
6433
8658
|
});
|
|
6434
8659
|
Object.defineProperty(exports, "securityLoggingMiddleware", {
|
|
6435
8660
|
enumerable: true,
|
|
6436
|
-
get: function () { return
|
|
8661
|
+
get: function () { return chunkVHNTCB2X_cjs.securityLoggingMiddleware; }
|
|
6437
8662
|
});
|
|
6438
8663
|
Object.defineProperty(exports, "PluginBootstrapService", {
|
|
6439
8664
|
enumerable: true,
|
|
6440
|
-
get: function () { return
|
|
8665
|
+
get: function () { return chunkI6FFGQIT_cjs.PluginBootstrapService; }
|
|
6441
8666
|
});
|
|
6442
8667
|
Object.defineProperty(exports, "PluginServiceClass", {
|
|
6443
8668
|
enumerable: true,
|
|
6444
|
-
get: function () { return
|
|
8669
|
+
get: function () { return chunkI6FFGQIT_cjs.PluginService; }
|
|
6445
8670
|
});
|
|
6446
8671
|
Object.defineProperty(exports, "backfillFormSubmissions", {
|
|
6447
8672
|
enumerable: true,
|
|
6448
|
-
get: function () { return
|
|
8673
|
+
get: function () { return chunkI6FFGQIT_cjs.backfillFormSubmissions; }
|
|
6449
8674
|
});
|
|
6450
8675
|
Object.defineProperty(exports, "cleanupRemovedCollections", {
|
|
6451
8676
|
enumerable: true,
|
|
6452
|
-
get: function () { return
|
|
8677
|
+
get: function () { return chunkI6FFGQIT_cjs.cleanupRemovedCollections; }
|
|
6453
8678
|
});
|
|
6454
8679
|
Object.defineProperty(exports, "createContentFromSubmission", {
|
|
6455
8680
|
enumerable: true,
|
|
6456
|
-
get: function () { return
|
|
8681
|
+
get: function () { return chunkI6FFGQIT_cjs.createContentFromSubmission; }
|
|
6457
8682
|
});
|
|
6458
8683
|
Object.defineProperty(exports, "deriveCollectionSchemaFromFormio", {
|
|
6459
8684
|
enumerable: true,
|
|
6460
|
-
get: function () { return
|
|
8685
|
+
get: function () { return chunkI6FFGQIT_cjs.deriveCollectionSchemaFromFormio; }
|
|
6461
8686
|
});
|
|
6462
8687
|
Object.defineProperty(exports, "deriveSubmissionTitle", {
|
|
6463
8688
|
enumerable: true,
|
|
6464
|
-
get: function () { return
|
|
8689
|
+
get: function () { return chunkI6FFGQIT_cjs.deriveSubmissionTitle; }
|
|
6465
8690
|
});
|
|
6466
8691
|
Object.defineProperty(exports, "fullCollectionSync", {
|
|
6467
8692
|
enumerable: true,
|
|
6468
|
-
get: function () { return
|
|
8693
|
+
get: function () { return chunkI6FFGQIT_cjs.fullCollectionSync; }
|
|
6469
8694
|
});
|
|
6470
8695
|
Object.defineProperty(exports, "getAvailableCollectionNames", {
|
|
6471
8696
|
enumerable: true,
|
|
6472
|
-
get: function () { return
|
|
8697
|
+
get: function () { return chunkI6FFGQIT_cjs.getAvailableCollectionNames; }
|
|
6473
8698
|
});
|
|
6474
8699
|
Object.defineProperty(exports, "getManagedCollections", {
|
|
6475
8700
|
enumerable: true,
|
|
6476
|
-
get: function () { return
|
|
8701
|
+
get: function () { return chunkI6FFGQIT_cjs.getManagedCollections; }
|
|
6477
8702
|
});
|
|
6478
8703
|
Object.defineProperty(exports, "isCollectionManaged", {
|
|
6479
8704
|
enumerable: true,
|
|
6480
|
-
get: function () { return
|
|
8705
|
+
get: function () { return chunkI6FFGQIT_cjs.isCollectionManaged; }
|
|
6481
8706
|
});
|
|
6482
8707
|
Object.defineProperty(exports, "loadCollectionConfig", {
|
|
6483
8708
|
enumerable: true,
|
|
6484
|
-
get: function () { return
|
|
8709
|
+
get: function () { return chunkI6FFGQIT_cjs.loadCollectionConfig; }
|
|
6485
8710
|
});
|
|
6486
8711
|
Object.defineProperty(exports, "loadCollectionConfigs", {
|
|
6487
8712
|
enumerable: true,
|
|
6488
|
-
get: function () { return
|
|
8713
|
+
get: function () { return chunkI6FFGQIT_cjs.loadCollectionConfigs; }
|
|
6489
8714
|
});
|
|
6490
8715
|
Object.defineProperty(exports, "mapFormStatusToContentStatus", {
|
|
6491
8716
|
enumerable: true,
|
|
6492
|
-
get: function () { return
|
|
8717
|
+
get: function () { return chunkI6FFGQIT_cjs.mapFormStatusToContentStatus; }
|
|
6493
8718
|
});
|
|
6494
8719
|
Object.defineProperty(exports, "registerCollections", {
|
|
6495
8720
|
enumerable: true,
|
|
6496
|
-
get: function () { return
|
|
8721
|
+
get: function () { return chunkI6FFGQIT_cjs.registerCollections; }
|
|
6497
8722
|
});
|
|
6498
8723
|
Object.defineProperty(exports, "syncAllFormCollections", {
|
|
6499
8724
|
enumerable: true,
|
|
6500
|
-
get: function () { return
|
|
8725
|
+
get: function () { return chunkI6FFGQIT_cjs.syncAllFormCollections; }
|
|
6501
8726
|
});
|
|
6502
8727
|
Object.defineProperty(exports, "syncCollection", {
|
|
6503
8728
|
enumerable: true,
|
|
6504
|
-
get: function () { return
|
|
8729
|
+
get: function () { return chunkI6FFGQIT_cjs.syncCollection; }
|
|
6505
8730
|
});
|
|
6506
8731
|
Object.defineProperty(exports, "syncCollections", {
|
|
6507
8732
|
enumerable: true,
|
|
6508
|
-
get: function () { return
|
|
8733
|
+
get: function () { return chunkI6FFGQIT_cjs.syncCollections; }
|
|
6509
8734
|
});
|
|
6510
8735
|
Object.defineProperty(exports, "syncFormCollection", {
|
|
6511
8736
|
enumerable: true,
|
|
6512
|
-
get: function () { return
|
|
8737
|
+
get: function () { return chunkI6FFGQIT_cjs.syncFormCollection; }
|
|
6513
8738
|
});
|
|
6514
8739
|
Object.defineProperty(exports, "validateCollectionConfig", {
|
|
6515
8740
|
enumerable: true,
|
|
6516
|
-
get: function () { return
|
|
8741
|
+
get: function () { return chunkI6FFGQIT_cjs.validateCollectionConfig; }
|
|
6517
8742
|
});
|
|
6518
8743
|
Object.defineProperty(exports, "MigrationService", {
|
|
6519
8744
|
enumerable: true,
|
|
6520
|
-
get: function () { return
|
|
8745
|
+
get: function () { return chunkAG3SIPP7_cjs.MigrationService; }
|
|
6521
8746
|
});
|
|
6522
8747
|
Object.defineProperty(exports, "renderFilterBar", {
|
|
6523
8748
|
enumerable: true,
|
|
6524
|
-
get: function () { return
|
|
8749
|
+
get: function () { return chunkRBXFXT7H_cjs.renderFilterBar; }
|
|
6525
8750
|
});
|
|
6526
8751
|
Object.defineProperty(exports, "getConfirmationDialogScript", {
|
|
6527
8752
|
enumerable: true,
|
|
6528
|
-
get: function () { return
|
|
8753
|
+
get: function () { return chunkH4NHRZ6Y_cjs.getConfirmationDialogScript; }
|
|
6529
8754
|
});
|
|
6530
8755
|
Object.defineProperty(exports, "renderAlert", {
|
|
6531
8756
|
enumerable: true,
|
|
6532
|
-
get: function () { return
|
|
8757
|
+
get: function () { return chunkH4NHRZ6Y_cjs.renderAlert; }
|
|
6533
8758
|
});
|
|
6534
8759
|
Object.defineProperty(exports, "renderConfirmationDialog", {
|
|
6535
8760
|
enumerable: true,
|
|
6536
|
-
get: function () { return
|
|
8761
|
+
get: function () { return chunkH4NHRZ6Y_cjs.renderConfirmationDialog; }
|
|
6537
8762
|
});
|
|
6538
8763
|
Object.defineProperty(exports, "renderForm", {
|
|
6539
8764
|
enumerable: true,
|
|
6540
|
-
get: function () { return
|
|
8765
|
+
get: function () { return chunkH4NHRZ6Y_cjs.renderForm; }
|
|
6541
8766
|
});
|
|
6542
8767
|
Object.defineProperty(exports, "renderFormField", {
|
|
6543
8768
|
enumerable: true,
|
|
6544
|
-
get: function () { return
|
|
8769
|
+
get: function () { return chunkH4NHRZ6Y_cjs.renderFormField; }
|
|
6545
8770
|
});
|
|
6546
8771
|
Object.defineProperty(exports, "renderPagination", {
|
|
6547
8772
|
enumerable: true,
|
|
6548
|
-
get: function () { return
|
|
8773
|
+
get: function () { return chunkH4NHRZ6Y_cjs.renderPagination; }
|
|
6549
8774
|
});
|
|
6550
8775
|
Object.defineProperty(exports, "renderTable", {
|
|
6551
8776
|
enumerable: true,
|
|
6552
|
-
get: function () { return
|
|
8777
|
+
get: function () { return chunkH4NHRZ6Y_cjs.renderTable; }
|
|
6553
8778
|
});
|
|
6554
8779
|
Object.defineProperty(exports, "HookSystemImpl", {
|
|
6555
8780
|
enumerable: true,
|
|
6556
|
-
get: function () { return
|
|
8781
|
+
get: function () { return chunk56GUBLJE_cjs.HookSystemImpl; }
|
|
6557
8782
|
});
|
|
6558
8783
|
Object.defineProperty(exports, "HookUtils", {
|
|
6559
8784
|
enumerable: true,
|
|
6560
|
-
get: function () { return
|
|
8785
|
+
get: function () { return chunk56GUBLJE_cjs.HookUtils; }
|
|
6561
8786
|
});
|
|
6562
8787
|
Object.defineProperty(exports, "PluginManagerClass", {
|
|
6563
8788
|
enumerable: true,
|
|
6564
|
-
get: function () { return
|
|
8789
|
+
get: function () { return chunk56GUBLJE_cjs.PluginManager; }
|
|
6565
8790
|
});
|
|
6566
8791
|
Object.defineProperty(exports, "PluginRegistryImpl", {
|
|
6567
8792
|
enumerable: true,
|
|
6568
|
-
get: function () { return
|
|
8793
|
+
get: function () { return chunk56GUBLJE_cjs.PluginRegistryImpl; }
|
|
6569
8794
|
});
|
|
6570
8795
|
Object.defineProperty(exports, "PluginValidatorClass", {
|
|
6571
8796
|
enumerable: true,
|
|
6572
|
-
get: function () { return
|
|
8797
|
+
get: function () { return chunk56GUBLJE_cjs.PluginValidator; }
|
|
6573
8798
|
});
|
|
6574
8799
|
Object.defineProperty(exports, "ScopedHookSystemClass", {
|
|
6575
8800
|
enumerable: true,
|
|
6576
|
-
get: function () { return
|
|
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
|
|
8813
|
+
get: function () { return chunkRXNLGINR_cjs.QueryFilterBuilder; }
|
|
6589
8814
|
});
|
|
6590
8815
|
Object.defineProperty(exports, "SONICJS_VERSION", {
|
|
6591
8816
|
enumerable: true,
|
|
6592
|
-
get: function () { return
|
|
8817
|
+
get: function () { return chunkRXNLGINR_cjs.SONICJS_VERSION; }
|
|
6593
8818
|
});
|
|
6594
8819
|
Object.defineProperty(exports, "TemplateRenderer", {
|
|
6595
8820
|
enumerable: true,
|
|
6596
|
-
get: function () { return
|
|
8821
|
+
get: function () { return chunkRXNLGINR_cjs.TemplateRenderer; }
|
|
6597
8822
|
});
|
|
6598
8823
|
Object.defineProperty(exports, "buildQuery", {
|
|
6599
8824
|
enumerable: true,
|
|
6600
|
-
get: function () { return
|
|
8825
|
+
get: function () { return chunkRXNLGINR_cjs.buildQuery; }
|
|
6601
8826
|
});
|
|
6602
8827
|
Object.defineProperty(exports, "getCoreVersion", {
|
|
6603
8828
|
enumerable: true,
|
|
6604
|
-
get: function () { return
|
|
8829
|
+
get: function () { return chunkRXNLGINR_cjs.getCoreVersion; }
|
|
6605
8830
|
});
|
|
6606
8831
|
Object.defineProperty(exports, "renderTemplate", {
|
|
6607
8832
|
enumerable: true,
|
|
6608
|
-
get: function () { return
|
|
8833
|
+
get: function () { return chunkRXNLGINR_cjs.renderTemplate; }
|
|
6609
8834
|
});
|
|
6610
8835
|
Object.defineProperty(exports, "templateRenderer", {
|
|
6611
8836
|
enumerable: true,
|
|
6612
|
-
get: function () { return
|
|
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
|
|
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
|