@sonicjs-cms/core 2.3.13 → 2.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-F56JKQTA.js → chunk-AVPUX57O.js} +3 -3
- package/dist/{chunk-F56JKQTA.js.map → chunk-AVPUX57O.js.map} +1 -1
- package/dist/{chunk-MF7DWI5P.cjs → chunk-AZLU3ROK.cjs} +4 -2
- package/dist/chunk-AZLU3ROK.cjs.map +1 -0
- package/dist/{chunk-VMEBHBYY.js → chunk-CAJOP354.js} +2 -2
- package/dist/{chunk-VMEBHBYY.js.map → chunk-CAJOP354.js.map} +1 -1
- package/dist/{chunk-ARLXQU2S.cjs → chunk-D4PJFFOV.cjs} +560 -465
- package/dist/chunk-D4PJFFOV.cjs.map +1 -0
- package/dist/{chunk-W4CE7XME.cjs → chunk-ETS5XSAG.cjs} +2 -2
- package/dist/{chunk-W4CE7XME.cjs.map → chunk-ETS5XSAG.cjs.map} +1 -1
- package/dist/{chunk-2NTBZ2Y7.js → chunk-H34L445M.js} +3 -3
- package/dist/{chunk-2NTBZ2Y7.js.map → chunk-H34L445M.js.map} +1 -1
- package/dist/{chunk-FHCN7KR2.js → chunk-SKPETEM5.js} +3 -3
- package/dist/{chunk-FHCN7KR2.js.map → chunk-SKPETEM5.js.map} +1 -1
- package/dist/{chunk-W2IAEG4W.cjs → chunk-SZE3XVET.cjs} +3 -3
- package/dist/{chunk-W2IAEG4W.cjs.map → chunk-SZE3XVET.cjs.map} +1 -1
- package/dist/{chunk-RP66TPEJ.js → chunk-T4XRPNX2.js} +415 -320
- package/dist/chunk-T4XRPNX2.js.map +1 -0
- package/dist/{chunk-DN45O5XV.js → chunk-V5LBQN3I.js} +4 -2
- package/dist/chunk-V5LBQN3I.js.map +1 -0
- package/dist/{chunk-5NCBFP37.cjs → chunk-XWPGIFS7.cjs} +4 -4
- package/dist/{chunk-5NCBFP37.cjs.map → chunk-XWPGIFS7.cjs.map} +1 -1
- package/dist/{chunk-XR6XACXJ.cjs → chunk-YIXSSJWD.cjs} +5 -5
- package/dist/{chunk-XR6XACXJ.cjs.map → chunk-YIXSSJWD.cjs.map} +1 -1
- package/dist/index.cjs +1080 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1003 -10
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +23 -23
- package/dist/middleware.js +2 -2
- package/dist/migrations-3A53GREK.cjs +13 -0
- package/dist/{migrations-ZAYXZXON.cjs.map → migrations-3A53GREK.cjs.map} +1 -1
- package/dist/migrations-WF6VIVU2.js +4 -0
- package/dist/{migrations-43GTELB5.js.map → migrations-WF6VIVU2.js.map} +1 -1
- package/dist/routes.cjs +25 -25
- package/dist/routes.js +5 -5
- package/dist/services.cjs +2 -2
- package/dist/services.js +1 -1
- package/dist/templates.cjs +17 -17
- package/dist/templates.js +2 -2
- package/dist/utils.cjs +11 -11
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ARLXQU2S.cjs.map +0 -1
- package/dist/chunk-DN45O5XV.js.map +0 -1
- package/dist/chunk-MF7DWI5P.cjs.map +0 -1
- package/dist/chunk-RP66TPEJ.js.map +0 -1
- package/dist/migrations-43GTELB5.js +0 -4
- package/dist/migrations-ZAYXZXON.cjs +0 -13
package/dist/index.cjs
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkD4PJFFOV_cjs = require('./chunk-D4PJFFOV.cjs');
|
|
4
4
|
var chunk7FOAMNTI_cjs = require('./chunk-7FOAMNTI.cjs');
|
|
5
|
-
var
|
|
5
|
+
var chunkXWPGIFS7_cjs = require('./chunk-XWPGIFS7.cjs');
|
|
6
6
|
var chunkILZ3DP4I_cjs = require('./chunk-ILZ3DP4I.cjs');
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
7
|
+
var chunkETS5XSAG_cjs = require('./chunk-ETS5XSAG.cjs');
|
|
8
|
+
var chunkYIXSSJWD_cjs = require('./chunk-YIXSSJWD.cjs');
|
|
9
|
+
var chunkAZLU3ROK_cjs = require('./chunk-AZLU3ROK.cjs');
|
|
10
10
|
var chunkDTLB6UIH_cjs = require('./chunk-DTLB6UIH.cjs');
|
|
11
|
-
var
|
|
11
|
+
var chunkSZE3XVET_cjs = require('./chunk-SZE3XVET.cjs');
|
|
12
12
|
require('./chunk-P3XDZL6Q.cjs');
|
|
13
13
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
14
14
|
var chunkKYGRJCZM_cjs = require('./chunk-KYGRJCZM.cjs');
|
|
15
15
|
require('./chunk-IGJUBJBW.cjs');
|
|
16
16
|
var hono = require('hono');
|
|
17
17
|
var html = require('hono/html');
|
|
18
|
+
var zod = require('zod');
|
|
18
19
|
var d1 = require('drizzle-orm/d1');
|
|
19
20
|
|
|
20
21
|
// src/plugins/core-plugins/database-tools-plugin/services/database-service.ts
|
|
@@ -232,7 +233,7 @@ var DatabaseToolsService = class {
|
|
|
232
233
|
};
|
|
233
234
|
|
|
234
235
|
// src/templates/pages/admin-database-table.template.ts
|
|
235
|
-
|
|
236
|
+
chunkAZLU3ROK_cjs.init_admin_layout_catalyst_template();
|
|
236
237
|
function renderDatabaseTablePage(data) {
|
|
237
238
|
const totalPages = Math.ceil(data.totalRows / data.pageSize);
|
|
238
239
|
const startRow = (data.currentPage - 1) * data.pageSize + 1;
|
|
@@ -481,7 +482,7 @@ function renderDatabaseTablePage(data) {
|
|
|
481
482
|
user: data.user,
|
|
482
483
|
content: pageContent
|
|
483
484
|
};
|
|
484
|
-
return
|
|
485
|
+
return chunkAZLU3ROK_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
485
486
|
}
|
|
486
487
|
function generatePageNumbers(currentPage, totalPages) {
|
|
487
488
|
const pages = [];
|
|
@@ -556,7 +557,7 @@ function formatCellValue(value) {
|
|
|
556
557
|
// src/plugins/core-plugins/database-tools-plugin/admin-routes.ts
|
|
557
558
|
function createDatabaseToolsAdminRoutes() {
|
|
558
559
|
const router2 = new hono.Hono();
|
|
559
|
-
router2.use("*",
|
|
560
|
+
router2.use("*", chunkXWPGIFS7_cjs.requireAuth());
|
|
560
561
|
router2.get("/api/stats", async (c) => {
|
|
561
562
|
try {
|
|
562
563
|
const user = c.get("user");
|
|
@@ -737,7 +738,7 @@ function createDatabaseToolsAdminRoutes() {
|
|
|
737
738
|
return router2;
|
|
738
739
|
}
|
|
739
740
|
function createEmailPlugin() {
|
|
740
|
-
const builder =
|
|
741
|
+
const builder = chunkD4PJFFOV_cjs.PluginBuilder.create({
|
|
741
742
|
name: "email",
|
|
742
743
|
version: "1.0.0-beta.1",
|
|
743
744
|
description: "Send transactional emails using Resend"
|
|
@@ -990,7 +991,7 @@ function createEmailPlugin() {
|
|
|
990
991
|
role: user.role ?? "admin"
|
|
991
992
|
} : void 0;
|
|
992
993
|
return c.html(
|
|
993
|
-
|
|
994
|
+
chunkAZLU3ROK_cjs.renderAdminLayout({
|
|
994
995
|
title: "Email Settings",
|
|
995
996
|
content: contentHTML,
|
|
996
997
|
user: templateUser,
|
|
@@ -1110,17 +1111,998 @@ function createEmailPlugin() {
|
|
|
1110
1111
|
}
|
|
1111
1112
|
var emailPlugin = createEmailPlugin();
|
|
1112
1113
|
|
|
1114
|
+
// src/plugins/core-plugins/otp-login-plugin/otp-service.ts
|
|
1115
|
+
var OTPService = class {
|
|
1116
|
+
constructor(db) {
|
|
1117
|
+
this.db = db;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Generate a secure random OTP code
|
|
1121
|
+
*/
|
|
1122
|
+
generateCode(length = 6) {
|
|
1123
|
+
const digits = "0123456789";
|
|
1124
|
+
let code = "";
|
|
1125
|
+
for (let i = 0; i < length; i++) {
|
|
1126
|
+
const randomValues = new Uint8Array(1);
|
|
1127
|
+
crypto.getRandomValues(randomValues);
|
|
1128
|
+
const randomValue = randomValues[0] ?? 0;
|
|
1129
|
+
code += digits[randomValue % digits.length];
|
|
1130
|
+
}
|
|
1131
|
+
return code;
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Create and store a new OTP code
|
|
1135
|
+
*/
|
|
1136
|
+
async createOTPCode(email, settings, ipAddress, userAgent) {
|
|
1137
|
+
const code = this.generateCode(settings.codeLength);
|
|
1138
|
+
const id = crypto.randomUUID();
|
|
1139
|
+
const now = Date.now();
|
|
1140
|
+
const expiresAt = now + settings.codeExpiryMinutes * 60 * 1e3;
|
|
1141
|
+
const otpCode = {
|
|
1142
|
+
id,
|
|
1143
|
+
user_email: email.toLowerCase(),
|
|
1144
|
+
code,
|
|
1145
|
+
expires_at: expiresAt,
|
|
1146
|
+
used: 0,
|
|
1147
|
+
used_at: null,
|
|
1148
|
+
ip_address: ipAddress || null,
|
|
1149
|
+
user_agent: userAgent || null,
|
|
1150
|
+
attempts: 0,
|
|
1151
|
+
created_at: now
|
|
1152
|
+
};
|
|
1153
|
+
await this.db.prepare(`
|
|
1154
|
+
INSERT INTO otp_codes (
|
|
1155
|
+
id, user_email, code, expires_at, used, used_at,
|
|
1156
|
+
ip_address, user_agent, attempts, created_at
|
|
1157
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1158
|
+
`).bind(
|
|
1159
|
+
otpCode.id,
|
|
1160
|
+
otpCode.user_email,
|
|
1161
|
+
otpCode.code,
|
|
1162
|
+
otpCode.expires_at,
|
|
1163
|
+
otpCode.used,
|
|
1164
|
+
otpCode.used_at,
|
|
1165
|
+
otpCode.ip_address,
|
|
1166
|
+
otpCode.user_agent,
|
|
1167
|
+
otpCode.attempts,
|
|
1168
|
+
otpCode.created_at
|
|
1169
|
+
).run();
|
|
1170
|
+
return otpCode;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Verify an OTP code
|
|
1174
|
+
*/
|
|
1175
|
+
async verifyCode(email, code, settings) {
|
|
1176
|
+
const normalizedEmail = email.toLowerCase();
|
|
1177
|
+
const now = Date.now();
|
|
1178
|
+
const otpCode = await this.db.prepare(`
|
|
1179
|
+
SELECT * FROM otp_codes
|
|
1180
|
+
WHERE user_email = ? AND code = ? AND used = 0
|
|
1181
|
+
ORDER BY created_at DESC
|
|
1182
|
+
LIMIT 1
|
|
1183
|
+
`).bind(normalizedEmail, code).first();
|
|
1184
|
+
if (!otpCode) {
|
|
1185
|
+
return { valid: false, error: "Invalid or expired code" };
|
|
1186
|
+
}
|
|
1187
|
+
if (now > otpCode.expires_at) {
|
|
1188
|
+
return { valid: false, error: "Code has expired" };
|
|
1189
|
+
}
|
|
1190
|
+
if (otpCode.attempts >= settings.maxAttempts) {
|
|
1191
|
+
return { valid: false, error: "Maximum attempts exceeded" };
|
|
1192
|
+
}
|
|
1193
|
+
await this.db.prepare(`
|
|
1194
|
+
UPDATE otp_codes
|
|
1195
|
+
SET used = 1, used_at = ?, attempts = attempts + 1
|
|
1196
|
+
WHERE id = ?
|
|
1197
|
+
`).bind(now, otpCode.id).run();
|
|
1198
|
+
return { valid: true };
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Increment failed attempt count
|
|
1202
|
+
*/
|
|
1203
|
+
async incrementAttempts(email, code) {
|
|
1204
|
+
const normalizedEmail = email.toLowerCase();
|
|
1205
|
+
const result = await this.db.prepare(`
|
|
1206
|
+
UPDATE otp_codes
|
|
1207
|
+
SET attempts = attempts + 1
|
|
1208
|
+
WHERE user_email = ? AND code = ? AND used = 0
|
|
1209
|
+
RETURNING attempts
|
|
1210
|
+
`).bind(normalizedEmail, code).first();
|
|
1211
|
+
return result?.attempts || 0;
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Check rate limiting
|
|
1215
|
+
*/
|
|
1216
|
+
async checkRateLimit(email, settings) {
|
|
1217
|
+
const normalizedEmail = email.toLowerCase();
|
|
1218
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1e3;
|
|
1219
|
+
const result = await this.db.prepare(`
|
|
1220
|
+
SELECT COUNT(*) as count
|
|
1221
|
+
FROM otp_codes
|
|
1222
|
+
WHERE user_email = ? AND created_at > ?
|
|
1223
|
+
`).bind(normalizedEmail, oneHourAgo).first();
|
|
1224
|
+
const count = result?.count || 0;
|
|
1225
|
+
return count < settings.rateLimitPerHour;
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Get recent OTP requests for activity log
|
|
1229
|
+
*/
|
|
1230
|
+
async getRecentRequests(limit = 50) {
|
|
1231
|
+
const result = await this.db.prepare(`
|
|
1232
|
+
SELECT * FROM otp_codes
|
|
1233
|
+
ORDER BY created_at DESC
|
|
1234
|
+
LIMIT ?
|
|
1235
|
+
`).bind(limit).all();
|
|
1236
|
+
const rows = result.results || [];
|
|
1237
|
+
return rows.map((row) => this.mapRowToOTP(row));
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Clean up expired codes (for maintenance)
|
|
1241
|
+
*/
|
|
1242
|
+
async cleanupExpiredCodes() {
|
|
1243
|
+
const now = Date.now();
|
|
1244
|
+
const result = await this.db.prepare(`
|
|
1245
|
+
DELETE FROM otp_codes
|
|
1246
|
+
WHERE expires_at < ? OR (used = 1 AND used_at < ?)
|
|
1247
|
+
`).bind(now, now - 30 * 24 * 60 * 60 * 1e3).run();
|
|
1248
|
+
return result.meta.changes || 0;
|
|
1249
|
+
}
|
|
1250
|
+
mapRowToOTP(row) {
|
|
1251
|
+
return {
|
|
1252
|
+
id: String(row.id),
|
|
1253
|
+
user_email: String(row.user_email),
|
|
1254
|
+
code: String(row.code),
|
|
1255
|
+
expires_at: Number(row.expires_at ?? Date.now()),
|
|
1256
|
+
used: Number(row.used ?? 0),
|
|
1257
|
+
used_at: row.used_at === null || row.used_at === void 0 ? null : Number(row.used_at),
|
|
1258
|
+
ip_address: typeof row.ip_address === "string" ? row.ip_address : null,
|
|
1259
|
+
user_agent: typeof row.user_agent === "string" ? row.user_agent : null,
|
|
1260
|
+
attempts: Number(row.attempts ?? 0),
|
|
1261
|
+
created_at: Number(row.created_at ?? Date.now())
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Get OTP statistics
|
|
1266
|
+
*/
|
|
1267
|
+
async getStats(days = 7) {
|
|
1268
|
+
const since = Date.now() - days * 24 * 60 * 60 * 1e3;
|
|
1269
|
+
const stats = await this.db.prepare(`
|
|
1270
|
+
SELECT
|
|
1271
|
+
COUNT(*) as total,
|
|
1272
|
+
SUM(CASE WHEN used = 1 THEN 1 ELSE 0 END) as successful,
|
|
1273
|
+
SUM(CASE WHEN attempts >= 3 AND used = 0 THEN 1 ELSE 0 END) as failed,
|
|
1274
|
+
SUM(CASE WHEN expires_at < ? AND used = 0 THEN 1 ELSE 0 END) as expired
|
|
1275
|
+
FROM otp_codes
|
|
1276
|
+
WHERE created_at > ?
|
|
1277
|
+
`).bind(Date.now(), since).first();
|
|
1278
|
+
return {
|
|
1279
|
+
total: stats?.total || 0,
|
|
1280
|
+
successful: stats?.successful || 0,
|
|
1281
|
+
failed: stats?.failed || 0,
|
|
1282
|
+
expired: stats?.expired || 0
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
// src/plugins/core-plugins/otp-login-plugin/email-templates.ts
|
|
1288
|
+
function renderOTPEmailHTML(data) {
|
|
1289
|
+
return `<!DOCTYPE html>
|
|
1290
|
+
<html>
|
|
1291
|
+
<head>
|
|
1292
|
+
<meta charset="utf-8">
|
|
1293
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1294
|
+
<title>Your Login Code</title>
|
|
1295
|
+
</head>
|
|
1296
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f5f5f5;">
|
|
1297
|
+
|
|
1298
|
+
<div style="background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
|
|
1299
|
+
|
|
1300
|
+
${data.logoUrl ? `
|
|
1301
|
+
<div style="text-align: center; padding: 30px 20px 20px;">
|
|
1302
|
+
<img src="${data.logoUrl}" alt="Logo" style="max-width: 150px; height: auto;">
|
|
1303
|
+
</div>
|
|
1304
|
+
` : ""}
|
|
1305
|
+
|
|
1306
|
+
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 30px; text-align: center;">
|
|
1307
|
+
<h1 style="margin: 0 0 10px 0; font-size: 32px; font-weight: 600;">Your Login Code</h1>
|
|
1308
|
+
<p style="margin: 0; opacity: 0.95; font-size: 16px;">Enter this code to sign in to ${data.appName}</p>
|
|
1309
|
+
</div>
|
|
1310
|
+
|
|
1311
|
+
<div style="padding: 40px 30px;">
|
|
1312
|
+
<div style="background: #f8f9fa; border: 2px dashed #667eea; border-radius: 12px; padding: 30px; text-align: center; margin: 0 0 30px 0;">
|
|
1313
|
+
<div style="font-size: 56px; font-weight: bold; letter-spacing: 12px; color: #667eea; font-family: 'Courier New', Courier, monospace; line-height: 1;">
|
|
1314
|
+
${data.code}
|
|
1315
|
+
</div>
|
|
1316
|
+
</div>
|
|
1317
|
+
|
|
1318
|
+
<div style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 16px 20px; margin: 0 0 30px 0; border-radius: 6px;">
|
|
1319
|
+
<p style="margin: 0; font-size: 14px; color: #856404;">
|
|
1320
|
+
<strong>\u26A0\uFE0F This code expires in ${data.expiryMinutes} minutes</strong>
|
|
1321
|
+
</p>
|
|
1322
|
+
</div>
|
|
1323
|
+
|
|
1324
|
+
<div style="margin: 0 0 30px 0;">
|
|
1325
|
+
<h3 style="color: #333; margin: 0 0 15px 0; font-size: 18px;">Quick Tips:</h3>
|
|
1326
|
+
<ul style="color: #666; font-size: 14px; line-height: 1.8; margin: 0; padding-left: 20px;">
|
|
1327
|
+
<li>Enter the code exactly as shown (${data.codeLength} digits)</li>
|
|
1328
|
+
<li>The code can only be used once</li>
|
|
1329
|
+
<li>You have ${data.maxAttempts} attempts to enter the correct code</li>
|
|
1330
|
+
<li>Request a new code if this one expires</li>
|
|
1331
|
+
</ul>
|
|
1332
|
+
</div>
|
|
1333
|
+
|
|
1334
|
+
<div style="background: #e8f4ff; border-radius: 8px; padding: 20px; margin: 0 0 30px 0;">
|
|
1335
|
+
<p style="margin: 0 0 10px 0; font-size: 14px; color: #0066cc; font-weight: 600;">
|
|
1336
|
+
\u{1F512} Security Notice
|
|
1337
|
+
</p>
|
|
1338
|
+
<p style="margin: 0; font-size: 13px; color: #004080; line-height: 1.6;">
|
|
1339
|
+
Never share this code with anyone. ${data.appName} will never ask you for this code via phone, email, or social media.
|
|
1340
|
+
</p>
|
|
1341
|
+
</div>
|
|
1342
|
+
</div>
|
|
1343
|
+
|
|
1344
|
+
<div style="border-top: 1px solid #eee; padding: 30px; background: #f8f9fa;">
|
|
1345
|
+
<p style="margin: 0 0 15px 0; font-size: 14px; color: #666; text-align: center;">
|
|
1346
|
+
<strong>Didn't request this code?</strong><br>
|
|
1347
|
+
Someone may have entered your email by mistake. You can safely ignore this email.
|
|
1348
|
+
</p>
|
|
1349
|
+
|
|
1350
|
+
<div style="text-align: center; color: #999; font-size: 12px; line-height: 1.6;">
|
|
1351
|
+
<p style="margin: 5px 0;">This email was sent to ${data.email}</p>
|
|
1352
|
+
${data.ipAddress ? `<p style="margin: 5px 0;">IP Address: ${data.ipAddress}</p>` : ""}
|
|
1353
|
+
<p style="margin: 5px 0;">Time: ${data.timestamp}</p>
|
|
1354
|
+
</div>
|
|
1355
|
+
</div>
|
|
1356
|
+
|
|
1357
|
+
</div>
|
|
1358
|
+
|
|
1359
|
+
<div style="text-align: center; padding: 20px; color: #999; font-size: 12px;">
|
|
1360
|
+
<p style="margin: 0;">© ${(/* @__PURE__ */ new Date()).getFullYear()} ${data.appName}. All rights reserved.</p>
|
|
1361
|
+
</div>
|
|
1362
|
+
|
|
1363
|
+
</body>
|
|
1364
|
+
</html>`;
|
|
1365
|
+
}
|
|
1366
|
+
function renderOTPEmailText(data) {
|
|
1367
|
+
return `Your Login Code for ${data.appName}
|
|
1368
|
+
|
|
1369
|
+
Your one-time verification code is:
|
|
1370
|
+
|
|
1371
|
+
${data.code}
|
|
1372
|
+
|
|
1373
|
+
This code expires in ${data.expiryMinutes} minutes.
|
|
1374
|
+
|
|
1375
|
+
Quick Tips:
|
|
1376
|
+
\u2022 Enter the code exactly as shown (${data.codeLength} digits)
|
|
1377
|
+
\u2022 The code can only be used once
|
|
1378
|
+
\u2022 You have ${data.maxAttempts} attempts to enter the correct code
|
|
1379
|
+
\u2022 Request a new code if this one expires
|
|
1380
|
+
|
|
1381
|
+
Security Notice:
|
|
1382
|
+
Never share this code with anyone. ${data.appName} will never ask you for this code via phone, email, or social media.
|
|
1383
|
+
|
|
1384
|
+
Didn't request this code?
|
|
1385
|
+
Someone may have entered your email by mistake. You can safely ignore this email.
|
|
1386
|
+
|
|
1387
|
+
---
|
|
1388
|
+
This email was sent to ${data.email}
|
|
1389
|
+
${data.ipAddress ? `IP Address: ${data.ipAddress}` : ""}
|
|
1390
|
+
Time: ${data.timestamp}
|
|
1391
|
+
|
|
1392
|
+
\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${data.appName}. All rights reserved.`;
|
|
1393
|
+
}
|
|
1394
|
+
function renderOTPEmail(data) {
|
|
1395
|
+
return {
|
|
1396
|
+
html: renderOTPEmailHTML(data),
|
|
1397
|
+
text: renderOTPEmailText(data)
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// src/plugins/core-plugins/otp-login-plugin/index.ts
|
|
1402
|
+
var otpRequestSchema = zod.z.object({
|
|
1403
|
+
email: zod.z.string().email("Valid email is required")
|
|
1404
|
+
});
|
|
1405
|
+
var otpVerifySchema = zod.z.object({
|
|
1406
|
+
email: zod.z.string().email("Valid email is required"),
|
|
1407
|
+
code: zod.z.string().min(4).max(8)
|
|
1408
|
+
});
|
|
1409
|
+
var DEFAULT_SETTINGS = {
|
|
1410
|
+
codeLength: 6,
|
|
1411
|
+
codeExpiryMinutes: 10,
|
|
1412
|
+
maxAttempts: 3,
|
|
1413
|
+
rateLimitPerHour: 5,
|
|
1414
|
+
allowNewUserRegistration: false,
|
|
1415
|
+
appName: "SonicJS"
|
|
1416
|
+
};
|
|
1417
|
+
function createOTPLoginPlugin() {
|
|
1418
|
+
const builder = chunkD4PJFFOV_cjs.PluginBuilder.create({
|
|
1419
|
+
name: "otp-login",
|
|
1420
|
+
version: "1.0.0-beta.1",
|
|
1421
|
+
description: "Passwordless authentication via email one-time codes"
|
|
1422
|
+
});
|
|
1423
|
+
builder.metadata({
|
|
1424
|
+
author: {
|
|
1425
|
+
name: "SonicJS Team",
|
|
1426
|
+
email: "team@sonicjs.com"
|
|
1427
|
+
},
|
|
1428
|
+
license: "MIT",
|
|
1429
|
+
compatibility: "^2.0.0"
|
|
1430
|
+
});
|
|
1431
|
+
const otpAPI = new hono.Hono();
|
|
1432
|
+
otpAPI.post("/request", async (c) => {
|
|
1433
|
+
try {
|
|
1434
|
+
const body = await c.req.json();
|
|
1435
|
+
const validation = otpRequestSchema.safeParse(body);
|
|
1436
|
+
if (!validation.success) {
|
|
1437
|
+
return c.json({
|
|
1438
|
+
error: "Validation failed",
|
|
1439
|
+
details: validation.error.issues
|
|
1440
|
+
}, 400);
|
|
1441
|
+
}
|
|
1442
|
+
const { email } = validation.data;
|
|
1443
|
+
const normalizedEmail = email.toLowerCase();
|
|
1444
|
+
const db = c.env.DB;
|
|
1445
|
+
const otpService = new OTPService(db);
|
|
1446
|
+
const settings = { ...DEFAULT_SETTINGS };
|
|
1447
|
+
const canRequest = await otpService.checkRateLimit(normalizedEmail, settings);
|
|
1448
|
+
if (!canRequest) {
|
|
1449
|
+
return c.json({
|
|
1450
|
+
error: "Too many requests. Please try again in an hour."
|
|
1451
|
+
}, 429);
|
|
1452
|
+
}
|
|
1453
|
+
const user = await db.prepare(`
|
|
1454
|
+
SELECT id, email, role, is_active
|
|
1455
|
+
FROM users
|
|
1456
|
+
WHERE email = ?
|
|
1457
|
+
`).bind(normalizedEmail).first();
|
|
1458
|
+
if (!user && !settings.allowNewUserRegistration) {
|
|
1459
|
+
return c.json({
|
|
1460
|
+
message: "If an account exists for this email, you will receive a verification code shortly.",
|
|
1461
|
+
expiresIn: settings.codeExpiryMinutes * 60
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
if (user && !user.is_active) {
|
|
1465
|
+
return c.json({
|
|
1466
|
+
error: "This account has been deactivated."
|
|
1467
|
+
}, 403);
|
|
1468
|
+
}
|
|
1469
|
+
const ipAddress = c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown";
|
|
1470
|
+
const userAgent = c.req.header("user-agent") || "unknown";
|
|
1471
|
+
const otpCode = await otpService.createOTPCode(
|
|
1472
|
+
normalizedEmail,
|
|
1473
|
+
settings,
|
|
1474
|
+
ipAddress,
|
|
1475
|
+
userAgent
|
|
1476
|
+
);
|
|
1477
|
+
try {
|
|
1478
|
+
const isDevMode = c.env.ENVIRONMENT === "development";
|
|
1479
|
+
if (isDevMode) {
|
|
1480
|
+
console.log(`[DEV] OTP Code for ${normalizedEmail}: ${otpCode.code}`);
|
|
1481
|
+
}
|
|
1482
|
+
const emailContent = renderOTPEmail({
|
|
1483
|
+
code: otpCode.code,
|
|
1484
|
+
expiryMinutes: settings.codeExpiryMinutes,
|
|
1485
|
+
codeLength: settings.codeLength,
|
|
1486
|
+
maxAttempts: settings.maxAttempts,
|
|
1487
|
+
email: normalizedEmail,
|
|
1488
|
+
ipAddress,
|
|
1489
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1490
|
+
appName: settings.appName
|
|
1491
|
+
});
|
|
1492
|
+
const response = {
|
|
1493
|
+
message: "If an account exists for this email, you will receive a verification code shortly.",
|
|
1494
|
+
expiresIn: settings.codeExpiryMinutes * 60
|
|
1495
|
+
};
|
|
1496
|
+
if (isDevMode) {
|
|
1497
|
+
response.dev_code = otpCode.code;
|
|
1498
|
+
}
|
|
1499
|
+
return c.json(response);
|
|
1500
|
+
} catch (emailError) {
|
|
1501
|
+
console.error("Error sending OTP email:", emailError);
|
|
1502
|
+
return c.json({
|
|
1503
|
+
error: "Failed to send verification code. Please try again."
|
|
1504
|
+
}, 500);
|
|
1505
|
+
}
|
|
1506
|
+
} catch (error) {
|
|
1507
|
+
console.error("OTP request error:", error);
|
|
1508
|
+
return c.json({
|
|
1509
|
+
error: "An error occurred. Please try again."
|
|
1510
|
+
}, 500);
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
otpAPI.post("/verify", async (c) => {
|
|
1514
|
+
try {
|
|
1515
|
+
const body = await c.req.json();
|
|
1516
|
+
const validation = otpVerifySchema.safeParse(body);
|
|
1517
|
+
if (!validation.success) {
|
|
1518
|
+
return c.json({
|
|
1519
|
+
error: "Validation failed",
|
|
1520
|
+
details: validation.error.issues
|
|
1521
|
+
}, 400);
|
|
1522
|
+
}
|
|
1523
|
+
const { email, code } = validation.data;
|
|
1524
|
+
const normalizedEmail = email.toLowerCase();
|
|
1525
|
+
const db = c.env.DB;
|
|
1526
|
+
const otpService = new OTPService(db);
|
|
1527
|
+
const settings = { ...DEFAULT_SETTINGS };
|
|
1528
|
+
const verification = await otpService.verifyCode(normalizedEmail, code, settings);
|
|
1529
|
+
if (!verification.valid) {
|
|
1530
|
+
await otpService.incrementAttempts(normalizedEmail, code);
|
|
1531
|
+
return c.json({
|
|
1532
|
+
error: verification.error || "Invalid code",
|
|
1533
|
+
attemptsRemaining: verification.attemptsRemaining
|
|
1534
|
+
}, 401);
|
|
1535
|
+
}
|
|
1536
|
+
const user = await db.prepare(`
|
|
1537
|
+
SELECT id, email, role, is_active
|
|
1538
|
+
FROM users
|
|
1539
|
+
WHERE email = ?
|
|
1540
|
+
`).bind(normalizedEmail).first();
|
|
1541
|
+
if (!user) {
|
|
1542
|
+
return c.json({
|
|
1543
|
+
error: "User not found"
|
|
1544
|
+
}, 404);
|
|
1545
|
+
}
|
|
1546
|
+
if (!user.is_active) {
|
|
1547
|
+
return c.json({
|
|
1548
|
+
error: "Account is deactivated"
|
|
1549
|
+
}, 403);
|
|
1550
|
+
}
|
|
1551
|
+
return c.json({
|
|
1552
|
+
success: true,
|
|
1553
|
+
user: {
|
|
1554
|
+
id: user.id,
|
|
1555
|
+
email: user.email,
|
|
1556
|
+
role: user.role
|
|
1557
|
+
},
|
|
1558
|
+
message: "Authentication successful"
|
|
1559
|
+
});
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
console.error("OTP verify error:", error);
|
|
1562
|
+
return c.json({
|
|
1563
|
+
error: "An error occurred. Please try again."
|
|
1564
|
+
}, 500);
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
otpAPI.post("/resend", async (c) => {
|
|
1568
|
+
try {
|
|
1569
|
+
const body = await c.req.json();
|
|
1570
|
+
const validation = otpRequestSchema.safeParse(body);
|
|
1571
|
+
if (!validation.success) {
|
|
1572
|
+
return c.json({
|
|
1573
|
+
error: "Validation failed",
|
|
1574
|
+
details: validation.error.issues
|
|
1575
|
+
}, 400);
|
|
1576
|
+
}
|
|
1577
|
+
return otpAPI.fetch(
|
|
1578
|
+
new Request(c.req.url.replace("/resend", "/request"), {
|
|
1579
|
+
method: "POST",
|
|
1580
|
+
headers: c.req.raw.headers,
|
|
1581
|
+
body: JSON.stringify({ email: validation.data.email })
|
|
1582
|
+
}),
|
|
1583
|
+
c.env
|
|
1584
|
+
);
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
console.error("OTP resend error:", error);
|
|
1587
|
+
return c.json({
|
|
1588
|
+
error: "An error occurred. Please try again."
|
|
1589
|
+
}, 500);
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
builder.addRoute("/auth/otp", otpAPI, {
|
|
1593
|
+
description: "OTP authentication endpoints",
|
|
1594
|
+
requiresAuth: false,
|
|
1595
|
+
priority: 100
|
|
1596
|
+
});
|
|
1597
|
+
const adminRoutes = new hono.Hono();
|
|
1598
|
+
adminRoutes.get("/settings", async (c) => {
|
|
1599
|
+
const user = c.get("user");
|
|
1600
|
+
const contentHTML = await html.html`
|
|
1601
|
+
<div class="p-8">
|
|
1602
|
+
<div class="mb-8">
|
|
1603
|
+
<h1 class="text-3xl font-bold mb-2">OTP Login Settings</h1>
|
|
1604
|
+
<p class="text-zinc-600 dark:text-zinc-400">Configure passwordless authentication via email codes</p>
|
|
1605
|
+
</div>
|
|
1606
|
+
|
|
1607
|
+
<div class="max-w-3xl">
|
|
1608
|
+
<div class="backdrop-blur-md bg-black/20 border border-white/10 shadow-xl rounded-xl p-6 mb-6">
|
|
1609
|
+
<h2 class="text-xl font-semibold mb-4">Code Settings</h2>
|
|
1610
|
+
|
|
1611
|
+
<form id="otpSettingsForm" class="space-y-6">
|
|
1612
|
+
<div>
|
|
1613
|
+
<label for="codeLength" class="block text-sm font-medium mb-2">
|
|
1614
|
+
Code Length
|
|
1615
|
+
</label>
|
|
1616
|
+
<input
|
|
1617
|
+
type="number"
|
|
1618
|
+
id="codeLength"
|
|
1619
|
+
name="codeLength"
|
|
1620
|
+
min="4"
|
|
1621
|
+
max="8"
|
|
1622
|
+
value="6"
|
|
1623
|
+
class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
|
|
1624
|
+
/>
|
|
1625
|
+
<p class="text-xs text-zinc-500 mt-1">Number of digits in OTP code (4-8)</p>
|
|
1626
|
+
</div>
|
|
1627
|
+
|
|
1628
|
+
<div>
|
|
1629
|
+
<label for="codeExpiryMinutes" class="block text-sm font-medium mb-2">
|
|
1630
|
+
Code Expiry (minutes)
|
|
1631
|
+
</label>
|
|
1632
|
+
<input
|
|
1633
|
+
type="number"
|
|
1634
|
+
id="codeExpiryMinutes"
|
|
1635
|
+
name="codeExpiryMinutes"
|
|
1636
|
+
min="5"
|
|
1637
|
+
max="60"
|
|
1638
|
+
value="10"
|
|
1639
|
+
class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
|
|
1640
|
+
/>
|
|
1641
|
+
<p class="text-xs text-zinc-500 mt-1">How long codes remain valid (5-60 minutes)</p>
|
|
1642
|
+
</div>
|
|
1643
|
+
|
|
1644
|
+
<div>
|
|
1645
|
+
<label for="maxAttempts" class="block text-sm font-medium mb-2">
|
|
1646
|
+
Maximum Attempts
|
|
1647
|
+
</label>
|
|
1648
|
+
<input
|
|
1649
|
+
type="number"
|
|
1650
|
+
id="maxAttempts"
|
|
1651
|
+
name="maxAttempts"
|
|
1652
|
+
min="3"
|
|
1653
|
+
max="10"
|
|
1654
|
+
value="3"
|
|
1655
|
+
class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
|
|
1656
|
+
/>
|
|
1657
|
+
<p class="text-xs text-zinc-500 mt-1">Max verification attempts before invalidation</p>
|
|
1658
|
+
</div>
|
|
1659
|
+
|
|
1660
|
+
<div>
|
|
1661
|
+
<label for="rateLimitPerHour" class="block text-sm font-medium mb-2">
|
|
1662
|
+
Rate Limit (per hour)
|
|
1663
|
+
</label>
|
|
1664
|
+
<input
|
|
1665
|
+
type="number"
|
|
1666
|
+
id="rateLimitPerHour"
|
|
1667
|
+
name="rateLimitPerHour"
|
|
1668
|
+
min="3"
|
|
1669
|
+
max="20"
|
|
1670
|
+
value="5"
|
|
1671
|
+
class="w-full px-4 py-2 rounded-lg bg-white/5 border border-white/10 focus:border-blue-500 focus:outline-none"
|
|
1672
|
+
/>
|
|
1673
|
+
<p class="text-xs text-zinc-500 mt-1">Max code requests per email per hour</p>
|
|
1674
|
+
</div>
|
|
1675
|
+
|
|
1676
|
+
<div class="flex items-center">
|
|
1677
|
+
<input
|
|
1678
|
+
type="checkbox"
|
|
1679
|
+
id="allowNewUserRegistration"
|
|
1680
|
+
name="allowNewUserRegistration"
|
|
1681
|
+
class="w-4 h-4 rounded border-white/10"
|
|
1682
|
+
/>
|
|
1683
|
+
<label for="allowNewUserRegistration" class="ml-2 text-sm">
|
|
1684
|
+
Allow new user registration via OTP
|
|
1685
|
+
</label>
|
|
1686
|
+
</div>
|
|
1687
|
+
|
|
1688
|
+
<div class="flex gap-3 pt-4">
|
|
1689
|
+
<button
|
|
1690
|
+
type="submit"
|
|
1691
|
+
class="px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-lg font-medium hover:from-blue-600 hover:to-purple-700 transition-all"
|
|
1692
|
+
>
|
|
1693
|
+
Save Settings
|
|
1694
|
+
</button>
|
|
1695
|
+
<button
|
|
1696
|
+
type="button"
|
|
1697
|
+
id="testOTPBtn"
|
|
1698
|
+
class="px-6 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg font-medium transition-all"
|
|
1699
|
+
>
|
|
1700
|
+
Send Test Code
|
|
1701
|
+
</button>
|
|
1702
|
+
</div>
|
|
1703
|
+
</form>
|
|
1704
|
+
</div>
|
|
1705
|
+
|
|
1706
|
+
<div id="statusMessage" class="hidden backdrop-blur-md bg-black/20 border border-white/10 rounded-xl p-4 mb-6"></div>
|
|
1707
|
+
|
|
1708
|
+
<div class="backdrop-blur-md bg-blue-500/10 border border-blue-500/20 rounded-xl p-6">
|
|
1709
|
+
<h3 class="font-semibold text-blue-400 mb-3">
|
|
1710
|
+
🔢 Features
|
|
1711
|
+
</h3>
|
|
1712
|
+
<ul class="text-sm text-blue-200 space-y-2">
|
|
1713
|
+
<li>✓ Passwordless authentication</li>
|
|
1714
|
+
<li>✓ Secure random code generation</li>
|
|
1715
|
+
<li>✓ Rate limiting protection</li>
|
|
1716
|
+
<li>✓ Brute force prevention</li>
|
|
1717
|
+
<li>✓ Mobile-friendly UX</li>
|
|
1718
|
+
</ul>
|
|
1719
|
+
</div>
|
|
1720
|
+
</div>
|
|
1721
|
+
</div>
|
|
1722
|
+
|
|
1723
|
+
<script>
|
|
1724
|
+
document.getElementById('otpSettingsForm').addEventListener('submit', async (e) => {
|
|
1725
|
+
e.preventDefault()
|
|
1726
|
+
const statusEl = document.getElementById('statusMessage')
|
|
1727
|
+
statusEl.className = 'backdrop-blur-md bg-green-500/20 border border-green-500/30 rounded-xl p-4 mb-6'
|
|
1728
|
+
statusEl.innerHTML = '✅ Settings saved successfully!'
|
|
1729
|
+
statusEl.classList.remove('hidden')
|
|
1730
|
+
setTimeout(() => statusEl.classList.add('hidden'), 3000)
|
|
1731
|
+
})
|
|
1732
|
+
|
|
1733
|
+
document.getElementById('testOTPBtn').addEventListener('click', async () => {
|
|
1734
|
+
const email = prompt('Enter email address for test:')
|
|
1735
|
+
if (!email) return
|
|
1736
|
+
|
|
1737
|
+
const statusEl = document.getElementById('statusMessage')
|
|
1738
|
+
statusEl.className = 'backdrop-blur-md bg-blue-500/20 border border-blue-500/30 rounded-xl p-4 mb-6'
|
|
1739
|
+
statusEl.innerHTML = '📧 Sending test code...'
|
|
1740
|
+
statusEl.classList.remove('hidden')
|
|
1741
|
+
|
|
1742
|
+
try {
|
|
1743
|
+
const response = await fetch('/auth/otp/request', {
|
|
1744
|
+
method: 'POST',
|
|
1745
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1746
|
+
body: JSON.stringify({ email })
|
|
1747
|
+
})
|
|
1748
|
+
|
|
1749
|
+
const data = await response.json()
|
|
1750
|
+
|
|
1751
|
+
if (response.ok) {
|
|
1752
|
+
statusEl.className = 'backdrop-blur-md bg-green-500/20 border border-green-500/30 rounded-xl p-4 mb-6'
|
|
1753
|
+
statusEl.innerHTML = '✅ Test code sent!' + (data.dev_code ? \` Code: <strong>\${data.dev_code}</strong>\` : '')
|
|
1754
|
+
} else {
|
|
1755
|
+
throw new Error(data.error || 'Failed')
|
|
1756
|
+
}
|
|
1757
|
+
} catch (error) {
|
|
1758
|
+
statusEl.className = 'backdrop-blur-md bg-red-500/20 border border-red-500/30 rounded-xl p-4 mb-6'
|
|
1759
|
+
statusEl.innerHTML = '❌ Failed to send test code'
|
|
1760
|
+
}
|
|
1761
|
+
})
|
|
1762
|
+
</script>
|
|
1763
|
+
`;
|
|
1764
|
+
const templateUser = user ? {
|
|
1765
|
+
name: user.name ?? user.email ?? "Admin",
|
|
1766
|
+
email: user.email ?? "admin@sonicjs.com",
|
|
1767
|
+
role: user.role ?? "admin"
|
|
1768
|
+
} : void 0;
|
|
1769
|
+
return c.html(
|
|
1770
|
+
chunkAZLU3ROK_cjs.adminLayoutV2({
|
|
1771
|
+
title: "OTP Login Settings",
|
|
1772
|
+
content: contentHTML,
|
|
1773
|
+
user: templateUser,
|
|
1774
|
+
currentPath: "/admin/plugins/otp-login/settings"
|
|
1775
|
+
})
|
|
1776
|
+
);
|
|
1777
|
+
});
|
|
1778
|
+
builder.addRoute("/admin/plugins/otp-login", adminRoutes, {
|
|
1779
|
+
description: "OTP login admin interface",
|
|
1780
|
+
requiresAuth: true,
|
|
1781
|
+
priority: 85
|
|
1782
|
+
});
|
|
1783
|
+
builder.addMenuItem("OTP Login", "/admin/plugins/otp-login/settings", {
|
|
1784
|
+
icon: "key",
|
|
1785
|
+
order: 85,
|
|
1786
|
+
permissions: ["otp:manage"]
|
|
1787
|
+
});
|
|
1788
|
+
builder.lifecycle({
|
|
1789
|
+
activate: async () => {
|
|
1790
|
+
console.info("\u2705 OTP Login plugin activated");
|
|
1791
|
+
},
|
|
1792
|
+
deactivate: async () => {
|
|
1793
|
+
console.info("\u274C OTP Login plugin deactivated");
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
return builder.build();
|
|
1797
|
+
}
|
|
1798
|
+
var otpLoginPlugin = createOTPLoginPlugin();
|
|
1799
|
+
var magicLinkRequestSchema = zod.z.object({
|
|
1800
|
+
email: zod.z.string().email("Valid email is required")
|
|
1801
|
+
});
|
|
1802
|
+
function createMagicLinkAuthPlugin() {
|
|
1803
|
+
const magicLinkRoutes = new hono.Hono();
|
|
1804
|
+
magicLinkRoutes.post("/request", async (c) => {
|
|
1805
|
+
try {
|
|
1806
|
+
const body = await c.req.json();
|
|
1807
|
+
const validation = magicLinkRequestSchema.safeParse(body);
|
|
1808
|
+
if (!validation.success) {
|
|
1809
|
+
return c.json({
|
|
1810
|
+
error: "Validation failed",
|
|
1811
|
+
details: validation.error.issues
|
|
1812
|
+
}, 400);
|
|
1813
|
+
}
|
|
1814
|
+
const { email } = validation.data;
|
|
1815
|
+
const normalizedEmail = email.toLowerCase();
|
|
1816
|
+
const db = c.env.DB;
|
|
1817
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1e3;
|
|
1818
|
+
const recentLinks = await db.prepare(`
|
|
1819
|
+
SELECT COUNT(*) as count
|
|
1820
|
+
FROM magic_links
|
|
1821
|
+
WHERE user_email = ? AND created_at > ?
|
|
1822
|
+
`).bind(normalizedEmail, oneHourAgo).first();
|
|
1823
|
+
const rateLimitPerHour = 5;
|
|
1824
|
+
if (recentLinks && recentLinks.count >= rateLimitPerHour) {
|
|
1825
|
+
return c.json({
|
|
1826
|
+
error: "Too many requests. Please try again later."
|
|
1827
|
+
}, 429);
|
|
1828
|
+
}
|
|
1829
|
+
const user = await db.prepare(`
|
|
1830
|
+
SELECT id, email, role, is_active
|
|
1831
|
+
FROM users
|
|
1832
|
+
WHERE email = ?
|
|
1833
|
+
`).bind(normalizedEmail).first();
|
|
1834
|
+
const allowNewUsers = false;
|
|
1835
|
+
if (!user && !allowNewUsers) {
|
|
1836
|
+
return c.json({
|
|
1837
|
+
message: "If an account exists for this email, you will receive a magic link shortly."
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
if (user && !user.is_active) {
|
|
1841
|
+
return c.json({
|
|
1842
|
+
error: "This account has been deactivated."
|
|
1843
|
+
}, 403);
|
|
1844
|
+
}
|
|
1845
|
+
const token = crypto.randomUUID() + "-" + crypto.randomUUID();
|
|
1846
|
+
const tokenId = crypto.randomUUID();
|
|
1847
|
+
const linkExpiryMinutes = 15;
|
|
1848
|
+
const expiresAt = Date.now() + linkExpiryMinutes * 60 * 1e3;
|
|
1849
|
+
await db.prepare(`
|
|
1850
|
+
INSERT INTO magic_links (
|
|
1851
|
+
id, user_email, token, expires_at, used, created_at, ip_address, user_agent
|
|
1852
|
+
) VALUES (?, ?, ?, ?, 0, ?, ?, ?)
|
|
1853
|
+
`).bind(
|
|
1854
|
+
tokenId,
|
|
1855
|
+
normalizedEmail,
|
|
1856
|
+
token,
|
|
1857
|
+
expiresAt,
|
|
1858
|
+
Date.now(),
|
|
1859
|
+
c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown",
|
|
1860
|
+
c.req.header("user-agent") || "unknown"
|
|
1861
|
+
).run();
|
|
1862
|
+
const baseUrl = new URL(c.req.url).origin;
|
|
1863
|
+
const magicLink = `${baseUrl}/auth/magic-link/verify?token=${token}`;
|
|
1864
|
+
try {
|
|
1865
|
+
const emailPlugin2 = c.env.plugins?.get("email");
|
|
1866
|
+
if (emailPlugin2 && emailPlugin2.sendEmail) {
|
|
1867
|
+
await emailPlugin2.sendEmail({
|
|
1868
|
+
to: normalizedEmail,
|
|
1869
|
+
subject: "Your Magic Link to Sign In",
|
|
1870
|
+
html: renderMagicLinkEmail(magicLink, linkExpiryMinutes)
|
|
1871
|
+
});
|
|
1872
|
+
} else {
|
|
1873
|
+
console.error("Email plugin not available");
|
|
1874
|
+
console.log(`Magic link for ${normalizedEmail}: ${magicLink}`);
|
|
1875
|
+
}
|
|
1876
|
+
} catch (error) {
|
|
1877
|
+
console.error("Failed to send magic link email:", error);
|
|
1878
|
+
return c.json({
|
|
1879
|
+
error: "Failed to send email. Please try again later."
|
|
1880
|
+
}, 500);
|
|
1881
|
+
}
|
|
1882
|
+
return c.json({
|
|
1883
|
+
message: "If an account exists for this email, you will receive a magic link shortly.",
|
|
1884
|
+
// For development only - remove in production
|
|
1885
|
+
...c.env.ENVIRONMENT === "development" && { dev_link: magicLink }
|
|
1886
|
+
});
|
|
1887
|
+
} catch (error) {
|
|
1888
|
+
console.error("Magic link request error:", error);
|
|
1889
|
+
return c.json({ error: "Failed to process request" }, 500);
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
magicLinkRoutes.get("/verify", async (c) => {
|
|
1893
|
+
try {
|
|
1894
|
+
const token = c.req.query("token");
|
|
1895
|
+
if (!token) {
|
|
1896
|
+
return c.redirect("/auth/login?error=Invalid magic link");
|
|
1897
|
+
}
|
|
1898
|
+
const db = c.env.DB;
|
|
1899
|
+
const magicLink = await db.prepare(`
|
|
1900
|
+
SELECT * FROM magic_links
|
|
1901
|
+
WHERE token = ? AND used = 0
|
|
1902
|
+
`).bind(token).first();
|
|
1903
|
+
if (!magicLink) {
|
|
1904
|
+
return c.redirect("/auth/login?error=Invalid or expired magic link");
|
|
1905
|
+
}
|
|
1906
|
+
if (magicLink.expires_at < Date.now()) {
|
|
1907
|
+
return c.redirect("/auth/login?error=This magic link has expired");
|
|
1908
|
+
}
|
|
1909
|
+
let user = await db.prepare(`
|
|
1910
|
+
SELECT * FROM users WHERE email = ? AND is_active = 1
|
|
1911
|
+
`).bind(magicLink.user_email).first();
|
|
1912
|
+
const allowNewUsers = false;
|
|
1913
|
+
if (!user && allowNewUsers) {
|
|
1914
|
+
const userId = crypto.randomUUID();
|
|
1915
|
+
const username = magicLink.user_email.split("@")[0];
|
|
1916
|
+
const now = Date.now();
|
|
1917
|
+
await db.prepare(`
|
|
1918
|
+
INSERT INTO users (
|
|
1919
|
+
id, email, username, first_name, last_name,
|
|
1920
|
+
password_hash, role, is_active, created_at, updated_at
|
|
1921
|
+
) VALUES (?, ?, ?, ?, ?, NULL, 'viewer', 1, ?, ?)
|
|
1922
|
+
`).bind(
|
|
1923
|
+
userId,
|
|
1924
|
+
magicLink.user_email,
|
|
1925
|
+
username,
|
|
1926
|
+
username,
|
|
1927
|
+
"",
|
|
1928
|
+
now,
|
|
1929
|
+
now
|
|
1930
|
+
).run();
|
|
1931
|
+
user = {
|
|
1932
|
+
id: userId,
|
|
1933
|
+
email: magicLink.user_email,
|
|
1934
|
+
username,
|
|
1935
|
+
role: "viewer"
|
|
1936
|
+
};
|
|
1937
|
+
} else if (!user) {
|
|
1938
|
+
return c.redirect("/auth/login?error=No account found for this email");
|
|
1939
|
+
}
|
|
1940
|
+
await db.prepare(`
|
|
1941
|
+
UPDATE magic_links
|
|
1942
|
+
SET used = 1, used_at = ?
|
|
1943
|
+
WHERE id = ?
|
|
1944
|
+
`).bind(Date.now(), magicLink.id).run();
|
|
1945
|
+
const jwtToken = await chunkXWPGIFS7_cjs.AuthManager.generateToken(
|
|
1946
|
+
user.id,
|
|
1947
|
+
user.email,
|
|
1948
|
+
user.role
|
|
1949
|
+
);
|
|
1950
|
+
chunkXWPGIFS7_cjs.AuthManager.setAuthCookie(c, jwtToken);
|
|
1951
|
+
await db.prepare(`
|
|
1952
|
+
UPDATE users SET last_login_at = ? WHERE id = ?
|
|
1953
|
+
`).bind(Date.now(), user.id).run();
|
|
1954
|
+
return c.redirect("/admin/dashboard?message=Successfully signed in");
|
|
1955
|
+
} catch (error) {
|
|
1956
|
+
console.error("Magic link verification error:", error);
|
|
1957
|
+
return c.redirect("/auth/login?error=Authentication failed");
|
|
1958
|
+
}
|
|
1959
|
+
});
|
|
1960
|
+
return {
|
|
1961
|
+
name: "magic-link-auth",
|
|
1962
|
+
version: "1.0.0",
|
|
1963
|
+
description: "Passwordless authentication via email magic links",
|
|
1964
|
+
author: {
|
|
1965
|
+
name: "SonicJS Team",
|
|
1966
|
+
email: "team@sonicjs.com"
|
|
1967
|
+
},
|
|
1968
|
+
dependencies: ["email"],
|
|
1969
|
+
routes: [{
|
|
1970
|
+
path: "/auth/magic-link",
|
|
1971
|
+
handler: magicLinkRoutes,
|
|
1972
|
+
description: "Magic link authentication endpoints",
|
|
1973
|
+
requiresAuth: false
|
|
1974
|
+
}],
|
|
1975
|
+
async install(context) {
|
|
1976
|
+
console.log("Installing magic-link-auth plugin...");
|
|
1977
|
+
},
|
|
1978
|
+
async activate(context) {
|
|
1979
|
+
console.log("Magic link authentication activated");
|
|
1980
|
+
console.log("Users can now sign in via /auth/magic-link/request");
|
|
1981
|
+
},
|
|
1982
|
+
async deactivate(context) {
|
|
1983
|
+
console.log("Magic link authentication deactivated");
|
|
1984
|
+
},
|
|
1985
|
+
async uninstall(context) {
|
|
1986
|
+
console.log("Uninstalling magic-link-auth plugin...");
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function renderMagicLinkEmail(magicLink, expiryMinutes) {
|
|
1991
|
+
return `
|
|
1992
|
+
<!DOCTYPE html>
|
|
1993
|
+
<html>
|
|
1994
|
+
<head>
|
|
1995
|
+
<meta charset="utf-8">
|
|
1996
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1997
|
+
<title>Your Magic Link</title>
|
|
1998
|
+
<style>
|
|
1999
|
+
body {
|
|
2000
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
2001
|
+
line-height: 1.6;
|
|
2002
|
+
color: #333;
|
|
2003
|
+
max-width: 600px;
|
|
2004
|
+
margin: 0 auto;
|
|
2005
|
+
padding: 20px;
|
|
2006
|
+
}
|
|
2007
|
+
.container {
|
|
2008
|
+
background: #ffffff;
|
|
2009
|
+
border-radius: 8px;
|
|
2010
|
+
padding: 40px;
|
|
2011
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
2012
|
+
}
|
|
2013
|
+
.header {
|
|
2014
|
+
text-align: center;
|
|
2015
|
+
margin-bottom: 30px;
|
|
2016
|
+
}
|
|
2017
|
+
.header h1 {
|
|
2018
|
+
color: #0ea5e9;
|
|
2019
|
+
margin: 0;
|
|
2020
|
+
font-size: 24px;
|
|
2021
|
+
}
|
|
2022
|
+
.content {
|
|
2023
|
+
margin-bottom: 30px;
|
|
2024
|
+
}
|
|
2025
|
+
.button {
|
|
2026
|
+
display: inline-block;
|
|
2027
|
+
padding: 14px 32px;
|
|
2028
|
+
background: linear-gradient(135deg, #0ea5e9 0%, #06b6d4 100%);
|
|
2029
|
+
color: #ffffff !important;
|
|
2030
|
+
text-decoration: none;
|
|
2031
|
+
border-radius: 6px;
|
|
2032
|
+
font-weight: 600;
|
|
2033
|
+
text-align: center;
|
|
2034
|
+
margin: 20px 0;
|
|
2035
|
+
}
|
|
2036
|
+
.button:hover {
|
|
2037
|
+
opacity: 0.9;
|
|
2038
|
+
}
|
|
2039
|
+
.expiry {
|
|
2040
|
+
color: #ef4444;
|
|
2041
|
+
font-size: 14px;
|
|
2042
|
+
margin-top: 20px;
|
|
2043
|
+
}
|
|
2044
|
+
.footer {
|
|
2045
|
+
margin-top: 40px;
|
|
2046
|
+
padding-top: 20px;
|
|
2047
|
+
border-top: 1px solid #e5e7eb;
|
|
2048
|
+
font-size: 12px;
|
|
2049
|
+
color: #6b7280;
|
|
2050
|
+
text-align: center;
|
|
2051
|
+
}
|
|
2052
|
+
.security-note {
|
|
2053
|
+
background: #fef3c7;
|
|
2054
|
+
border-left: 4px solid #f59e0b;
|
|
2055
|
+
padding: 12px 16px;
|
|
2056
|
+
margin-top: 20px;
|
|
2057
|
+
border-radius: 4px;
|
|
2058
|
+
font-size: 14px;
|
|
2059
|
+
}
|
|
2060
|
+
</style>
|
|
2061
|
+
</head>
|
|
2062
|
+
<body>
|
|
2063
|
+
<div class="container">
|
|
2064
|
+
<div class="header">
|
|
2065
|
+
<h1>\u{1F517} Your Magic Link</h1>
|
|
2066
|
+
</div>
|
|
2067
|
+
|
|
2068
|
+
<div class="content">
|
|
2069
|
+
<p>Hello!</p>
|
|
2070
|
+
<p>You requested a magic link to sign in to your account. Click the button below to continue:</p>
|
|
2071
|
+
|
|
2072
|
+
<div style="text-align: center;">
|
|
2073
|
+
<a href="${magicLink}" class="button">Sign In</a>
|
|
2074
|
+
</div>
|
|
2075
|
+
|
|
2076
|
+
<p class="expiry">\u23F0 This link expires in ${expiryMinutes} minutes</p>
|
|
2077
|
+
|
|
2078
|
+
<div class="security-note">
|
|
2079
|
+
<strong>Security Notice:</strong> If you didn't request this link, you can safely ignore this email.
|
|
2080
|
+
Someone may have entered your email address by mistake.
|
|
2081
|
+
</div>
|
|
2082
|
+
</div>
|
|
2083
|
+
|
|
2084
|
+
<div class="footer">
|
|
2085
|
+
<p>This is an automated email from SonicJS.</p>
|
|
2086
|
+
<p>For security, this link can only be used once.</p>
|
|
2087
|
+
</div>
|
|
2088
|
+
</div>
|
|
2089
|
+
</body>
|
|
2090
|
+
</html>
|
|
2091
|
+
`;
|
|
2092
|
+
}
|
|
2093
|
+
createMagicLinkAuthPlugin();
|
|
2094
|
+
|
|
1113
2095
|
// src/app.ts
|
|
1114
2096
|
function createSonicJSApp(config = {}) {
|
|
1115
2097
|
const app = new hono.Hono();
|
|
1116
|
-
const appVersion = config.version ||
|
|
2098
|
+
const appVersion = config.version || chunkSZE3XVET_cjs.getCoreVersion();
|
|
1117
2099
|
const appName = config.name || "SonicJS AI";
|
|
1118
2100
|
app.use("*", async (c, next) => {
|
|
1119
2101
|
c.set("appVersion", appVersion);
|
|
1120
2102
|
await next();
|
|
1121
2103
|
});
|
|
1122
|
-
app.use("*",
|
|
1123
|
-
app.use("*",
|
|
2104
|
+
app.use("*", chunkXWPGIFS7_cjs.metricsMiddleware());
|
|
2105
|
+
app.use("*", chunkXWPGIFS7_cjs.bootstrapMiddleware(config));
|
|
1124
2106
|
if (config.middleware?.beforeAuth) {
|
|
1125
2107
|
for (const middleware of config.middleware.beforeAuth) {
|
|
1126
2108
|
app.use("*", middleware);
|
|
@@ -1137,26 +2119,37 @@ function createSonicJSApp(config = {}) {
|
|
|
1137
2119
|
app.use("*", middleware);
|
|
1138
2120
|
}
|
|
1139
2121
|
}
|
|
1140
|
-
app.route("/api",
|
|
1141
|
-
app.route("/api/media",
|
|
1142
|
-
app.route("/api/system",
|
|
1143
|
-
app.route("/admin/api",
|
|
1144
|
-
app.route("/admin/dashboard",
|
|
1145
|
-
app.route("/admin/collections",
|
|
1146
|
-
app.route("/admin/settings",
|
|
2122
|
+
app.route("/api", chunkD4PJFFOV_cjs.api_default);
|
|
2123
|
+
app.route("/api/media", chunkD4PJFFOV_cjs.api_media_default);
|
|
2124
|
+
app.route("/api/system", chunkD4PJFFOV_cjs.api_system_default);
|
|
2125
|
+
app.route("/admin/api", chunkD4PJFFOV_cjs.admin_api_default);
|
|
2126
|
+
app.route("/admin/dashboard", chunkD4PJFFOV_cjs.router);
|
|
2127
|
+
app.route("/admin/collections", chunkD4PJFFOV_cjs.adminCollectionsRoutes);
|
|
2128
|
+
app.route("/admin/settings", chunkD4PJFFOV_cjs.adminSettingsRoutes);
|
|
1147
2129
|
app.route("/admin/database-tools", createDatabaseToolsAdminRoutes());
|
|
1148
|
-
app.route("/admin/content",
|
|
1149
|
-
app.route("/admin/media",
|
|
1150
|
-
app.route("/admin/plugins",
|
|
1151
|
-
app.route("/admin/logs",
|
|
1152
|
-
app.route("/admin",
|
|
1153
|
-
app.route("/auth",
|
|
1154
|
-
app.route("/",
|
|
2130
|
+
app.route("/admin/content", chunkD4PJFFOV_cjs.admin_content_default);
|
|
2131
|
+
app.route("/admin/media", chunkD4PJFFOV_cjs.adminMediaRoutes);
|
|
2132
|
+
app.route("/admin/plugins", chunkD4PJFFOV_cjs.adminPluginRoutes);
|
|
2133
|
+
app.route("/admin/logs", chunkD4PJFFOV_cjs.adminLogsRoutes);
|
|
2134
|
+
app.route("/admin", chunkD4PJFFOV_cjs.userRoutes);
|
|
2135
|
+
app.route("/auth", chunkD4PJFFOV_cjs.auth_default);
|
|
2136
|
+
app.route("/", chunkD4PJFFOV_cjs.test_cleanup_default);
|
|
1155
2137
|
if (emailPlugin.routes && emailPlugin.routes.length > 0) {
|
|
1156
2138
|
for (const route of emailPlugin.routes) {
|
|
1157
2139
|
app.route(route.path, route.handler);
|
|
1158
2140
|
}
|
|
1159
2141
|
}
|
|
2142
|
+
if (otpLoginPlugin.routes && otpLoginPlugin.routes.length > 0) {
|
|
2143
|
+
for (const route of otpLoginPlugin.routes) {
|
|
2144
|
+
app.route(route.path, route.handler);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
const magicLinkPlugin = createMagicLinkAuthPlugin();
|
|
2148
|
+
if (magicLinkPlugin.routes && magicLinkPlugin.routes.length > 0) {
|
|
2149
|
+
for (const route of magicLinkPlugin.routes) {
|
|
2150
|
+
app.route(route.path, route.handler);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
1160
2153
|
app.get("/files/*", async (c) => {
|
|
1161
2154
|
try {
|
|
1162
2155
|
const url = new URL(c.req.url);
|
|
@@ -1220,83 +2213,83 @@ function createDb(d1$1) {
|
|
|
1220
2213
|
}
|
|
1221
2214
|
|
|
1222
2215
|
// src/index.ts
|
|
1223
|
-
var VERSION =
|
|
2216
|
+
var VERSION = chunkSZE3XVET_cjs.package_default.version;
|
|
1224
2217
|
|
|
1225
2218
|
Object.defineProperty(exports, "ROUTES_INFO", {
|
|
1226
2219
|
enumerable: true,
|
|
1227
|
-
get: function () { return
|
|
2220
|
+
get: function () { return chunkD4PJFFOV_cjs.ROUTES_INFO; }
|
|
1228
2221
|
});
|
|
1229
2222
|
Object.defineProperty(exports, "adminApiRoutes", {
|
|
1230
2223
|
enumerable: true,
|
|
1231
|
-
get: function () { return
|
|
2224
|
+
get: function () { return chunkD4PJFFOV_cjs.admin_api_default; }
|
|
1232
2225
|
});
|
|
1233
2226
|
Object.defineProperty(exports, "adminCheckboxRoutes", {
|
|
1234
2227
|
enumerable: true,
|
|
1235
|
-
get: function () { return
|
|
2228
|
+
get: function () { return chunkD4PJFFOV_cjs.adminCheckboxRoutes; }
|
|
1236
2229
|
});
|
|
1237
2230
|
Object.defineProperty(exports, "adminCodeExamplesRoutes", {
|
|
1238
2231
|
enumerable: true,
|
|
1239
|
-
get: function () { return
|
|
2232
|
+
get: function () { return chunkD4PJFFOV_cjs.admin_code_examples_default; }
|
|
1240
2233
|
});
|
|
1241
2234
|
Object.defineProperty(exports, "adminCollectionsRoutes", {
|
|
1242
2235
|
enumerable: true,
|
|
1243
|
-
get: function () { return
|
|
2236
|
+
get: function () { return chunkD4PJFFOV_cjs.adminCollectionsRoutes; }
|
|
1244
2237
|
});
|
|
1245
2238
|
Object.defineProperty(exports, "adminContentRoutes", {
|
|
1246
2239
|
enumerable: true,
|
|
1247
|
-
get: function () { return
|
|
2240
|
+
get: function () { return chunkD4PJFFOV_cjs.admin_content_default; }
|
|
1248
2241
|
});
|
|
1249
2242
|
Object.defineProperty(exports, "adminDashboardRoutes", {
|
|
1250
2243
|
enumerable: true,
|
|
1251
|
-
get: function () { return
|
|
2244
|
+
get: function () { return chunkD4PJFFOV_cjs.router; }
|
|
1252
2245
|
});
|
|
1253
2246
|
Object.defineProperty(exports, "adminDesignRoutes", {
|
|
1254
2247
|
enumerable: true,
|
|
1255
|
-
get: function () { return
|
|
2248
|
+
get: function () { return chunkD4PJFFOV_cjs.adminDesignRoutes; }
|
|
1256
2249
|
});
|
|
1257
2250
|
Object.defineProperty(exports, "adminLogsRoutes", {
|
|
1258
2251
|
enumerable: true,
|
|
1259
|
-
get: function () { return
|
|
2252
|
+
get: function () { return chunkD4PJFFOV_cjs.adminLogsRoutes; }
|
|
1260
2253
|
});
|
|
1261
2254
|
Object.defineProperty(exports, "adminMediaRoutes", {
|
|
1262
2255
|
enumerable: true,
|
|
1263
|
-
get: function () { return
|
|
2256
|
+
get: function () { return chunkD4PJFFOV_cjs.adminMediaRoutes; }
|
|
1264
2257
|
});
|
|
1265
2258
|
Object.defineProperty(exports, "adminPluginRoutes", {
|
|
1266
2259
|
enumerable: true,
|
|
1267
|
-
get: function () { return
|
|
2260
|
+
get: function () { return chunkD4PJFFOV_cjs.adminPluginRoutes; }
|
|
1268
2261
|
});
|
|
1269
2262
|
Object.defineProperty(exports, "adminSettingsRoutes", {
|
|
1270
2263
|
enumerable: true,
|
|
1271
|
-
get: function () { return
|
|
2264
|
+
get: function () { return chunkD4PJFFOV_cjs.adminSettingsRoutes; }
|
|
1272
2265
|
});
|
|
1273
2266
|
Object.defineProperty(exports, "adminTestimonialsRoutes", {
|
|
1274
2267
|
enumerable: true,
|
|
1275
|
-
get: function () { return
|
|
2268
|
+
get: function () { return chunkD4PJFFOV_cjs.admin_testimonials_default; }
|
|
1276
2269
|
});
|
|
1277
2270
|
Object.defineProperty(exports, "adminUsersRoutes", {
|
|
1278
2271
|
enumerable: true,
|
|
1279
|
-
get: function () { return
|
|
2272
|
+
get: function () { return chunkD4PJFFOV_cjs.userRoutes; }
|
|
1280
2273
|
});
|
|
1281
2274
|
Object.defineProperty(exports, "apiContentCrudRoutes", {
|
|
1282
2275
|
enumerable: true,
|
|
1283
|
-
get: function () { return
|
|
2276
|
+
get: function () { return chunkD4PJFFOV_cjs.api_content_crud_default; }
|
|
1284
2277
|
});
|
|
1285
2278
|
Object.defineProperty(exports, "apiMediaRoutes", {
|
|
1286
2279
|
enumerable: true,
|
|
1287
|
-
get: function () { return
|
|
2280
|
+
get: function () { return chunkD4PJFFOV_cjs.api_media_default; }
|
|
1288
2281
|
});
|
|
1289
2282
|
Object.defineProperty(exports, "apiRoutes", {
|
|
1290
2283
|
enumerable: true,
|
|
1291
|
-
get: function () { return
|
|
2284
|
+
get: function () { return chunkD4PJFFOV_cjs.api_default; }
|
|
1292
2285
|
});
|
|
1293
2286
|
Object.defineProperty(exports, "apiSystemRoutes", {
|
|
1294
2287
|
enumerable: true,
|
|
1295
|
-
get: function () { return
|
|
2288
|
+
get: function () { return chunkD4PJFFOV_cjs.api_system_default; }
|
|
1296
2289
|
});
|
|
1297
2290
|
Object.defineProperty(exports, "authRoutes", {
|
|
1298
2291
|
enumerable: true,
|
|
1299
|
-
get: function () { return
|
|
2292
|
+
get: function () { return chunkD4PJFFOV_cjs.auth_default; }
|
|
1300
2293
|
});
|
|
1301
2294
|
Object.defineProperty(exports, "Logger", {
|
|
1302
2295
|
enumerable: true,
|
|
@@ -1464,83 +2457,83 @@ Object.defineProperty(exports, "workflowHistory", {
|
|
|
1464
2457
|
});
|
|
1465
2458
|
Object.defineProperty(exports, "AuthManager", {
|
|
1466
2459
|
enumerable: true,
|
|
1467
|
-
get: function () { return
|
|
2460
|
+
get: function () { return chunkXWPGIFS7_cjs.AuthManager; }
|
|
1468
2461
|
});
|
|
1469
2462
|
Object.defineProperty(exports, "PermissionManager", {
|
|
1470
2463
|
enumerable: true,
|
|
1471
|
-
get: function () { return
|
|
2464
|
+
get: function () { return chunkXWPGIFS7_cjs.PermissionManager; }
|
|
1472
2465
|
});
|
|
1473
2466
|
Object.defineProperty(exports, "bootstrapMiddleware", {
|
|
1474
2467
|
enumerable: true,
|
|
1475
|
-
get: function () { return
|
|
2468
|
+
get: function () { return chunkXWPGIFS7_cjs.bootstrapMiddleware; }
|
|
1476
2469
|
});
|
|
1477
2470
|
Object.defineProperty(exports, "cacheHeaders", {
|
|
1478
2471
|
enumerable: true,
|
|
1479
|
-
get: function () { return
|
|
2472
|
+
get: function () { return chunkXWPGIFS7_cjs.cacheHeaders; }
|
|
1480
2473
|
});
|
|
1481
2474
|
Object.defineProperty(exports, "compressionMiddleware", {
|
|
1482
2475
|
enumerable: true,
|
|
1483
|
-
get: function () { return
|
|
2476
|
+
get: function () { return chunkXWPGIFS7_cjs.compressionMiddleware; }
|
|
1484
2477
|
});
|
|
1485
2478
|
Object.defineProperty(exports, "detailedLoggingMiddleware", {
|
|
1486
2479
|
enumerable: true,
|
|
1487
|
-
get: function () { return
|
|
2480
|
+
get: function () { return chunkXWPGIFS7_cjs.detailedLoggingMiddleware; }
|
|
1488
2481
|
});
|
|
1489
2482
|
Object.defineProperty(exports, "getActivePlugins", {
|
|
1490
2483
|
enumerable: true,
|
|
1491
|
-
get: function () { return
|
|
2484
|
+
get: function () { return chunkXWPGIFS7_cjs.getActivePlugins; }
|
|
1492
2485
|
});
|
|
1493
2486
|
Object.defineProperty(exports, "isPluginActive", {
|
|
1494
2487
|
enumerable: true,
|
|
1495
|
-
get: function () { return
|
|
2488
|
+
get: function () { return chunkXWPGIFS7_cjs.isPluginActive; }
|
|
1496
2489
|
});
|
|
1497
2490
|
Object.defineProperty(exports, "logActivity", {
|
|
1498
2491
|
enumerable: true,
|
|
1499
|
-
get: function () { return
|
|
2492
|
+
get: function () { return chunkXWPGIFS7_cjs.logActivity; }
|
|
1500
2493
|
});
|
|
1501
2494
|
Object.defineProperty(exports, "loggingMiddleware", {
|
|
1502
2495
|
enumerable: true,
|
|
1503
|
-
get: function () { return
|
|
2496
|
+
get: function () { return chunkXWPGIFS7_cjs.loggingMiddleware; }
|
|
1504
2497
|
});
|
|
1505
2498
|
Object.defineProperty(exports, "optionalAuth", {
|
|
1506
2499
|
enumerable: true,
|
|
1507
|
-
get: function () { return
|
|
2500
|
+
get: function () { return chunkXWPGIFS7_cjs.optionalAuth; }
|
|
1508
2501
|
});
|
|
1509
2502
|
Object.defineProperty(exports, "performanceLoggingMiddleware", {
|
|
1510
2503
|
enumerable: true,
|
|
1511
|
-
get: function () { return
|
|
2504
|
+
get: function () { return chunkXWPGIFS7_cjs.performanceLoggingMiddleware; }
|
|
1512
2505
|
});
|
|
1513
2506
|
Object.defineProperty(exports, "requireActivePlugin", {
|
|
1514
2507
|
enumerable: true,
|
|
1515
|
-
get: function () { return
|
|
2508
|
+
get: function () { return chunkXWPGIFS7_cjs.requireActivePlugin; }
|
|
1516
2509
|
});
|
|
1517
2510
|
Object.defineProperty(exports, "requireActivePlugins", {
|
|
1518
2511
|
enumerable: true,
|
|
1519
|
-
get: function () { return
|
|
2512
|
+
get: function () { return chunkXWPGIFS7_cjs.requireActivePlugins; }
|
|
1520
2513
|
});
|
|
1521
2514
|
Object.defineProperty(exports, "requireAnyPermission", {
|
|
1522
2515
|
enumerable: true,
|
|
1523
|
-
get: function () { return
|
|
2516
|
+
get: function () { return chunkXWPGIFS7_cjs.requireAnyPermission; }
|
|
1524
2517
|
});
|
|
1525
2518
|
Object.defineProperty(exports, "requireAuth", {
|
|
1526
2519
|
enumerable: true,
|
|
1527
|
-
get: function () { return
|
|
2520
|
+
get: function () { return chunkXWPGIFS7_cjs.requireAuth; }
|
|
1528
2521
|
});
|
|
1529
2522
|
Object.defineProperty(exports, "requirePermission", {
|
|
1530
2523
|
enumerable: true,
|
|
1531
|
-
get: function () { return
|
|
2524
|
+
get: function () { return chunkXWPGIFS7_cjs.requirePermission; }
|
|
1532
2525
|
});
|
|
1533
2526
|
Object.defineProperty(exports, "requireRole", {
|
|
1534
2527
|
enumerable: true,
|
|
1535
|
-
get: function () { return
|
|
2528
|
+
get: function () { return chunkXWPGIFS7_cjs.requireRole; }
|
|
1536
2529
|
});
|
|
1537
2530
|
Object.defineProperty(exports, "securityHeaders", {
|
|
1538
2531
|
enumerable: true,
|
|
1539
|
-
get: function () { return
|
|
2532
|
+
get: function () { return chunkXWPGIFS7_cjs.securityHeaders; }
|
|
1540
2533
|
});
|
|
1541
2534
|
Object.defineProperty(exports, "securityLoggingMiddleware", {
|
|
1542
2535
|
enumerable: true,
|
|
1543
|
-
get: function () { return
|
|
2536
|
+
get: function () { return chunkXWPGIFS7_cjs.securityLoggingMiddleware; }
|
|
1544
2537
|
});
|
|
1545
2538
|
Object.defineProperty(exports, "PluginBootstrapService", {
|
|
1546
2539
|
enumerable: true,
|
|
@@ -1596,39 +2589,39 @@ Object.defineProperty(exports, "validateCollectionConfig", {
|
|
|
1596
2589
|
});
|
|
1597
2590
|
Object.defineProperty(exports, "MigrationService", {
|
|
1598
2591
|
enumerable: true,
|
|
1599
|
-
get: function () { return
|
|
2592
|
+
get: function () { return chunkETS5XSAG_cjs.MigrationService; }
|
|
1600
2593
|
});
|
|
1601
2594
|
Object.defineProperty(exports, "renderFilterBar", {
|
|
1602
2595
|
enumerable: true,
|
|
1603
|
-
get: function () { return
|
|
2596
|
+
get: function () { return chunkYIXSSJWD_cjs.renderFilterBar; }
|
|
1604
2597
|
});
|
|
1605
2598
|
Object.defineProperty(exports, "getConfirmationDialogScript", {
|
|
1606
2599
|
enumerable: true,
|
|
1607
|
-
get: function () { return
|
|
2600
|
+
get: function () { return chunkAZLU3ROK_cjs.getConfirmationDialogScript; }
|
|
1608
2601
|
});
|
|
1609
2602
|
Object.defineProperty(exports, "renderAlert", {
|
|
1610
2603
|
enumerable: true,
|
|
1611
|
-
get: function () { return
|
|
2604
|
+
get: function () { return chunkAZLU3ROK_cjs.renderAlert; }
|
|
1612
2605
|
});
|
|
1613
2606
|
Object.defineProperty(exports, "renderConfirmationDialog", {
|
|
1614
2607
|
enumerable: true,
|
|
1615
|
-
get: function () { return
|
|
2608
|
+
get: function () { return chunkAZLU3ROK_cjs.renderConfirmationDialog; }
|
|
1616
2609
|
});
|
|
1617
2610
|
Object.defineProperty(exports, "renderForm", {
|
|
1618
2611
|
enumerable: true,
|
|
1619
|
-
get: function () { return
|
|
2612
|
+
get: function () { return chunkAZLU3ROK_cjs.renderForm; }
|
|
1620
2613
|
});
|
|
1621
2614
|
Object.defineProperty(exports, "renderFormField", {
|
|
1622
2615
|
enumerable: true,
|
|
1623
|
-
get: function () { return
|
|
2616
|
+
get: function () { return chunkAZLU3ROK_cjs.renderFormField; }
|
|
1624
2617
|
});
|
|
1625
2618
|
Object.defineProperty(exports, "renderPagination", {
|
|
1626
2619
|
enumerable: true,
|
|
1627
|
-
get: function () { return
|
|
2620
|
+
get: function () { return chunkAZLU3ROK_cjs.renderPagination; }
|
|
1628
2621
|
});
|
|
1629
2622
|
Object.defineProperty(exports, "renderTable", {
|
|
1630
2623
|
enumerable: true,
|
|
1631
|
-
get: function () { return
|
|
2624
|
+
get: function () { return chunkAZLU3ROK_cjs.renderTable; }
|
|
1632
2625
|
});
|
|
1633
2626
|
Object.defineProperty(exports, "HookSystemImpl", {
|
|
1634
2627
|
enumerable: true,
|
|
@@ -1656,43 +2649,43 @@ Object.defineProperty(exports, "ScopedHookSystemClass", {
|
|
|
1656
2649
|
});
|
|
1657
2650
|
Object.defineProperty(exports, "QueryFilterBuilder", {
|
|
1658
2651
|
enumerable: true,
|
|
1659
|
-
get: function () { return
|
|
2652
|
+
get: function () { return chunkSZE3XVET_cjs.QueryFilterBuilder; }
|
|
1660
2653
|
});
|
|
1661
2654
|
Object.defineProperty(exports, "SONICJS_VERSION", {
|
|
1662
2655
|
enumerable: true,
|
|
1663
|
-
get: function () { return
|
|
2656
|
+
get: function () { return chunkSZE3XVET_cjs.SONICJS_VERSION; }
|
|
1664
2657
|
});
|
|
1665
2658
|
Object.defineProperty(exports, "TemplateRenderer", {
|
|
1666
2659
|
enumerable: true,
|
|
1667
|
-
get: function () { return
|
|
2660
|
+
get: function () { return chunkSZE3XVET_cjs.TemplateRenderer; }
|
|
1668
2661
|
});
|
|
1669
2662
|
Object.defineProperty(exports, "buildQuery", {
|
|
1670
2663
|
enumerable: true,
|
|
1671
|
-
get: function () { return
|
|
2664
|
+
get: function () { return chunkSZE3XVET_cjs.buildQuery; }
|
|
1672
2665
|
});
|
|
1673
2666
|
Object.defineProperty(exports, "escapeHtml", {
|
|
1674
2667
|
enumerable: true,
|
|
1675
|
-
get: function () { return
|
|
2668
|
+
get: function () { return chunkSZE3XVET_cjs.escapeHtml; }
|
|
1676
2669
|
});
|
|
1677
2670
|
Object.defineProperty(exports, "getCoreVersion", {
|
|
1678
2671
|
enumerable: true,
|
|
1679
|
-
get: function () { return
|
|
2672
|
+
get: function () { return chunkSZE3XVET_cjs.getCoreVersion; }
|
|
1680
2673
|
});
|
|
1681
2674
|
Object.defineProperty(exports, "renderTemplate", {
|
|
1682
2675
|
enumerable: true,
|
|
1683
|
-
get: function () { return
|
|
2676
|
+
get: function () { return chunkSZE3XVET_cjs.renderTemplate; }
|
|
1684
2677
|
});
|
|
1685
2678
|
Object.defineProperty(exports, "sanitizeInput", {
|
|
1686
2679
|
enumerable: true,
|
|
1687
|
-
get: function () { return
|
|
2680
|
+
get: function () { return chunkSZE3XVET_cjs.sanitizeInput; }
|
|
1688
2681
|
});
|
|
1689
2682
|
Object.defineProperty(exports, "sanitizeObject", {
|
|
1690
2683
|
enumerable: true,
|
|
1691
|
-
get: function () { return
|
|
2684
|
+
get: function () { return chunkSZE3XVET_cjs.sanitizeObject; }
|
|
1692
2685
|
});
|
|
1693
2686
|
Object.defineProperty(exports, "templateRenderer", {
|
|
1694
2687
|
enumerable: true,
|
|
1695
|
-
get: function () { return
|
|
2688
|
+
get: function () { return chunkSZE3XVET_cjs.templateRenderer; }
|
|
1696
2689
|
});
|
|
1697
2690
|
Object.defineProperty(exports, "metricsTracker", {
|
|
1698
2691
|
enumerable: true,
|