@sonicjs-cms/core 2.5.0 → 2.6.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-Db0AfT5F.d.cts → app-DV27cjPy.d.cts} +1 -1
- package/dist/{app-Db0AfT5F.d.ts → app-DV27cjPy.d.ts} +1 -1
- package/dist/{chunk-YIXSSJWD.cjs → chunk-63K7XXRX.cjs} +5 -5
- package/dist/{chunk-YIXSSJWD.cjs.map → chunk-63K7XXRX.cjs.map} +1 -1
- package/dist/{chunk-BHNDALCA.js → chunk-7DL5SPPX.js} +6 -4
- package/dist/chunk-7DL5SPPX.js.map +1 -0
- package/dist/{chunk-AZLU3ROK.cjs → chunk-BZC4FYW7.cjs} +4 -4
- package/dist/chunk-BZC4FYW7.cjs.map +1 -0
- package/dist/chunk-CLIH2T74.js +403 -0
- package/dist/chunk-CLIH2T74.js.map +1 -0
- package/dist/{chunk-VEL7QRYI.js → chunk-EVZOVYLO.js} +9 -2
- package/dist/chunk-EVZOVYLO.js.map +1 -0
- package/dist/{chunk-TJTWRO4G.js → chunk-EYWR6UA2.js} +4 -4
- package/dist/{chunk-TJTWRO4G.js.map → chunk-EYWR6UA2.js.map} +1 -1
- package/dist/{chunk-3YUHXWSG.js → chunk-F332TENF.js} +3 -3
- package/dist/{chunk-3YUHXWSG.js.map → chunk-F332TENF.js.map} +1 -1
- package/dist/{chunk-OJZ45OJD.js → chunk-F6GZURXJ.js} +2243 -539
- package/dist/chunk-F6GZURXJ.js.map +1 -0
- package/dist/{chunk-I4V3VZWF.cjs → chunk-IIRVZSP2.cjs} +9 -2
- package/dist/chunk-IIRVZSP2.cjs.map +1 -0
- package/dist/{chunk-V5LBQN3I.js → chunk-KA2PDJNB.js} +4 -4
- package/dist/chunk-KA2PDJNB.js.map +1 -0
- package/dist/{chunk-AVPUX57O.js → chunk-KAOWRIFD.js} +3 -3
- package/dist/{chunk-AVPUX57O.js.map → chunk-KAOWRIFD.js.map} +1 -1
- package/dist/{chunk-ILZ3DP4I.cjs → chunk-MPT5PA6U.cjs} +24 -2
- package/dist/chunk-MPT5PA6U.cjs.map +1 -0
- package/dist/{chunk-UAQL2VWX.cjs → chunk-N7TDLOUE.cjs} +2406 -703
- package/dist/chunk-N7TDLOUE.cjs.map +1 -0
- package/dist/{chunk-YYV3XQOQ.cjs → chunk-T3YIKW2A.cjs} +7 -7
- package/dist/{chunk-YYV3XQOQ.cjs.map → chunk-T3YIKW2A.cjs.map} +1 -1
- package/dist/{chunk-LWG2MWDA.cjs → chunk-Y72M3MVX.cjs} +4 -4
- package/dist/{chunk-LWG2MWDA.cjs.map → chunk-Y72M3MVX.cjs.map} +1 -1
- package/dist/{chunk-SGAG6FD3.js → chunk-YFJJU26H.js} +24 -2
- package/dist/chunk-YFJJU26H.js.map +1 -0
- package/dist/chunk-YHW27CBV.cjs +406 -0
- package/dist/chunk-YHW27CBV.cjs.map +1 -0
- package/dist/{chunk-ZWV3EBZ7.cjs → chunk-YMTTGHEK.cjs} +6 -4
- package/dist/chunk-YMTTGHEK.cjs.map +1 -0
- package/dist/{collection-config-B6gMPunn.d.cts → collection-config-BF95LgQb.d.cts} +1 -1
- package/dist/{collection-config-B6gMPunn.d.ts → collection-config-BF95LgQb.d.ts} +1 -1
- package/dist/index.cjs +2156 -300
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +503 -8
- package/dist/index.d.ts +503 -8
- package/dist/index.js +1893 -44
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +24 -24
- package/dist/middleware.d.cts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +3 -3
- package/dist/migrations-QNYAWQLB.cjs +13 -0
- package/dist/{migrations-NIEUFG44.cjs.map → migrations-QNYAWQLB.cjs.map} +1 -1
- package/dist/migrations-R6NQBKQV.js +4 -0
- package/dist/{migrations-TGZKJKV4.js.map → migrations-R6NQBKQV.js.map} +1 -1
- package/dist/{plugin-bootstrap-dYhD9fQR.d.ts → plugin-bootstrap-CB-xaBfK.d.ts} +2 -2
- package/dist/{plugin-bootstrap-SHsdjE6X.d.cts → plugin-bootstrap-U-cw9jn3.d.cts} +2 -2
- package/dist/plugins.cjs +11 -11
- package/dist/plugins.js +2 -2
- package/dist/routes.cjs +27 -27
- package/dist/routes.d.cts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +7 -7
- package/dist/services.cjs +16 -16
- package/dist/services.d.cts +2 -2
- package/dist/services.d.ts +2 -2
- package/dist/services.js +2 -2
- package/dist/templates.cjs +17 -17
- package/dist/templates.js +2 -2
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils.cjs +14 -14
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/migrations/029_ai_search_plugin.sql +45 -0
- package/package.json +4 -2
- package/dist/chunk-AI2JJIJX.cjs +0 -211
- package/dist/chunk-AI2JJIJX.cjs.map +0 -1
- package/dist/chunk-AZLU3ROK.cjs.map +0 -1
- package/dist/chunk-BHNDALCA.js.map +0 -1
- package/dist/chunk-I4V3VZWF.cjs.map +0 -1
- package/dist/chunk-ILZ3DP4I.cjs.map +0 -1
- package/dist/chunk-OJZ45OJD.js.map +0 -1
- package/dist/chunk-QDBNW7KQ.js +0 -209
- package/dist/chunk-QDBNW7KQ.js.map +0 -1
- package/dist/chunk-SGAG6FD3.js.map +0 -1
- package/dist/chunk-UAQL2VWX.cjs.map +0 -1
- package/dist/chunk-V5LBQN3I.js.map +0 -1
- package/dist/chunk-VEL7QRYI.js.map +0 -1
- package/dist/chunk-ZWV3EBZ7.cjs.map +0 -1
- package/dist/migrations-NIEUFG44.cjs +0 -13
- package/dist/migrations-TGZKJKV4.js +0 -4
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkN7TDLOUE_cjs = require('./chunk-N7TDLOUE.cjs');
|
|
4
4
|
var chunk7FOAMNTI_cjs = require('./chunk-7FOAMNTI.cjs');
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
var
|
|
5
|
+
var chunkT3YIKW2A_cjs = require('./chunk-T3YIKW2A.cjs');
|
|
6
|
+
var chunkMPT5PA6U_cjs = require('./chunk-MPT5PA6U.cjs');
|
|
7
|
+
var chunkIIRVZSP2_cjs = require('./chunk-IIRVZSP2.cjs');
|
|
8
|
+
var chunk63K7XXRX_cjs = require('./chunk-63K7XXRX.cjs');
|
|
9
|
+
var chunkBZC4FYW7_cjs = require('./chunk-BZC4FYW7.cjs');
|
|
10
|
+
var chunkY72M3MVX_cjs = require('./chunk-Y72M3MVX.cjs');
|
|
11
|
+
var chunkYHW27CBV_cjs = require('./chunk-YHW27CBV.cjs');
|
|
12
|
+
var chunkYMTTGHEK_cjs = require('./chunk-YMTTGHEK.cjs');
|
|
13
13
|
require('./chunk-P3XDZL6Q.cjs');
|
|
14
14
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
15
15
|
var chunkKYGRJCZM_cjs = require('./chunk-KYGRJCZM.cjs');
|
|
@@ -20,33 +20,6 @@ var cookie = require('hono/cookie');
|
|
|
20
20
|
var zod = require('zod');
|
|
21
21
|
var d1 = require('drizzle-orm/d1');
|
|
22
22
|
|
|
23
|
-
// src/middleware/admin-setup.ts
|
|
24
|
-
function adminSetupMiddleware() {
|
|
25
|
-
return async (c, next) => {
|
|
26
|
-
const path = new URL(c.req.url).pathname;
|
|
27
|
-
if (path.startsWith("/auth/")) {
|
|
28
|
-
return next();
|
|
29
|
-
}
|
|
30
|
-
if (path.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/)) {
|
|
31
|
-
return next();
|
|
32
|
-
}
|
|
33
|
-
if (path === "/health") {
|
|
34
|
-
return next();
|
|
35
|
-
}
|
|
36
|
-
if (path.startsWith("/api/")) {
|
|
37
|
-
return next();
|
|
38
|
-
}
|
|
39
|
-
const db = c.env.DB;
|
|
40
|
-
const adminExists = await chunkUAQL2VWX_cjs.checkAdminUserExists(db);
|
|
41
|
-
if (!adminExists) {
|
|
42
|
-
if (path.startsWith("/admin")) {
|
|
43
|
-
return c.redirect("/auth/register?setup=true");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return next();
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
23
|
// src/plugins/core-plugins/database-tools-plugin/services/database-service.ts
|
|
51
24
|
var DatabaseToolsService = class {
|
|
52
25
|
constructor(db) {
|
|
@@ -262,7 +235,7 @@ var DatabaseToolsService = class {
|
|
|
262
235
|
};
|
|
263
236
|
|
|
264
237
|
// src/templates/pages/admin-database-table.template.ts
|
|
265
|
-
|
|
238
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
266
239
|
function renderDatabaseTablePage(data) {
|
|
267
240
|
const totalPages = Math.ceil(data.totalRows / data.pageSize);
|
|
268
241
|
const startRow = (data.currentPage - 1) * data.pageSize + 1;
|
|
@@ -511,7 +484,7 @@ function renderDatabaseTablePage(data) {
|
|
|
511
484
|
user: data.user,
|
|
512
485
|
content: pageContent
|
|
513
486
|
};
|
|
514
|
-
return
|
|
487
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
515
488
|
}
|
|
516
489
|
function generatePageNumbers(currentPage, totalPages) {
|
|
517
490
|
const pages = [];
|
|
@@ -586,7 +559,7 @@ function formatCellValue(value) {
|
|
|
586
559
|
// src/plugins/core-plugins/database-tools-plugin/admin-routes.ts
|
|
587
560
|
function createDatabaseToolsAdminRoutes() {
|
|
588
561
|
const router2 = new hono.Hono();
|
|
589
|
-
router2.use("*",
|
|
562
|
+
router2.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
590
563
|
router2.get("/api/stats", async (c) => {
|
|
591
564
|
try {
|
|
592
565
|
const user = c.get("user");
|
|
@@ -1334,7 +1307,7 @@ function createSeedDataAdminRoutes() {
|
|
|
1334
1307
|
return routes;
|
|
1335
1308
|
}
|
|
1336
1309
|
function createEmailPlugin() {
|
|
1337
|
-
const builder =
|
|
1310
|
+
const builder = chunkYHW27CBV_cjs.PluginBuilder.create({
|
|
1338
1311
|
name: "email",
|
|
1339
1312
|
version: "1.0.0-beta.1",
|
|
1340
1313
|
description: "Send transactional emails using Resend"
|
|
@@ -1587,7 +1560,7 @@ function createEmailPlugin() {
|
|
|
1587
1560
|
role: user.role ?? "admin"
|
|
1588
1561
|
} : void 0;
|
|
1589
1562
|
return c.html(
|
|
1590
|
-
|
|
1563
|
+
chunkBZC4FYW7_cjs.renderAdminLayout({
|
|
1591
1564
|
title: "Email Settings",
|
|
1592
1565
|
content: contentHTML,
|
|
1593
1566
|
user: templateUser,
|
|
@@ -2011,7 +1984,7 @@ var DEFAULT_SETTINGS = {
|
|
|
2011
1984
|
appName: "SonicJS"
|
|
2012
1985
|
};
|
|
2013
1986
|
function createOTPLoginPlugin() {
|
|
2014
|
-
const builder =
|
|
1987
|
+
const builder = chunkYHW27CBV_cjs.PluginBuilder.create({
|
|
2015
1988
|
name: "otp-login",
|
|
2016
1989
|
version: "1.0.0-beta.1",
|
|
2017
1990
|
description: "Passwordless authentication via email one-time codes"
|
|
@@ -2175,7 +2148,7 @@ function createOTPLoginPlugin() {
|
|
|
2175
2148
|
error: "Account is deactivated"
|
|
2176
2149
|
}, 403);
|
|
2177
2150
|
}
|
|
2178
|
-
const token = await
|
|
2151
|
+
const token = await chunkT3YIKW2A_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2179
2152
|
cookie.setCookie(c, "auth_token", token, {
|
|
2180
2153
|
httpOnly: true,
|
|
2181
2154
|
secure: true,
|
|
@@ -2230,8 +2203,8 @@ function createOTPLoginPlugin() {
|
|
|
2230
2203
|
requiresAuth: false,
|
|
2231
2204
|
priority: 100
|
|
2232
2205
|
});
|
|
2233
|
-
const
|
|
2234
|
-
|
|
2206
|
+
const adminRoutes2 = new hono.Hono();
|
|
2207
|
+
adminRoutes2.get("/settings", async (c) => {
|
|
2235
2208
|
const user = c.get("user");
|
|
2236
2209
|
const contentHTML = await html.html`
|
|
2237
2210
|
<div class="p-8">
|
|
@@ -2403,7 +2376,7 @@ function createOTPLoginPlugin() {
|
|
|
2403
2376
|
role: user.role ?? "admin"
|
|
2404
2377
|
} : void 0;
|
|
2405
2378
|
return c.html(
|
|
2406
|
-
|
|
2379
|
+
chunkBZC4FYW7_cjs.adminLayoutV2({
|
|
2407
2380
|
title: "OTP Login Settings",
|
|
2408
2381
|
content: contentHTML,
|
|
2409
2382
|
user: templateUser,
|
|
@@ -2411,7 +2384,7 @@ function createOTPLoginPlugin() {
|
|
|
2411
2384
|
})
|
|
2412
2385
|
);
|
|
2413
2386
|
});
|
|
2414
|
-
builder.addRoute("/admin/plugins/otp-login",
|
|
2387
|
+
builder.addRoute("/admin/plugins/otp-login", adminRoutes2, {
|
|
2415
2388
|
description: "OTP login admin interface",
|
|
2416
2389
|
requiresAuth: true,
|
|
2417
2390
|
priority: 85
|
|
@@ -2432,182 +2405,2016 @@ function createOTPLoginPlugin() {
|
|
|
2432
2405
|
return builder.build();
|
|
2433
2406
|
}
|
|
2434
2407
|
var otpLoginPlugin = createOTPLoginPlugin();
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2408
|
+
|
|
2409
|
+
// src/plugins/core-plugins/ai-search-plugin/services/embedding.service.ts
|
|
2410
|
+
var EmbeddingService = class {
|
|
2411
|
+
constructor(ai) {
|
|
2412
|
+
this.ai = ai;
|
|
2413
|
+
}
|
|
2414
|
+
/**
|
|
2415
|
+
* Generate embedding for a single text
|
|
2416
|
+
*/
|
|
2417
|
+
async generateEmbedding(text) {
|
|
2441
2418
|
try {
|
|
2442
|
-
const
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
details: validation.error.issues
|
|
2448
|
-
}, 400);
|
|
2419
|
+
const response = await this.ai.run("@cf/baai/bge-base-en-v1.5", {
|
|
2420
|
+
text: this.preprocessText(text)
|
|
2421
|
+
});
|
|
2422
|
+
if (response.data && response.data.length > 0) {
|
|
2423
|
+
return response.data[0];
|
|
2449
2424
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2425
|
+
throw new Error("No embedding data returned");
|
|
2426
|
+
} catch (error) {
|
|
2427
|
+
console.error("[EmbeddingService] Error generating embedding:", error);
|
|
2428
|
+
throw error;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* Generate embeddings for multiple texts (batch processing)
|
|
2433
|
+
*/
|
|
2434
|
+
async generateBatch(texts) {
|
|
2435
|
+
try {
|
|
2436
|
+
const batchSize = 10;
|
|
2437
|
+
const batches = [];
|
|
2438
|
+
for (let i = 0; i < texts.length; i += batchSize) {
|
|
2439
|
+
batches.push(texts.slice(i, i + batchSize));
|
|
2464
2440
|
}
|
|
2465
|
-
const
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2441
|
+
const allEmbeddings = [];
|
|
2442
|
+
for (const batch of batches) {
|
|
2443
|
+
const batchEmbeddings = await Promise.all(
|
|
2444
|
+
batch.map((text) => this.generateEmbedding(text))
|
|
2445
|
+
);
|
|
2446
|
+
allEmbeddings.push(...batchEmbeddings);
|
|
2447
|
+
}
|
|
2448
|
+
return allEmbeddings;
|
|
2449
|
+
} catch (error) {
|
|
2450
|
+
console.error("[EmbeddingService] Error generating batch embeddings:", error);
|
|
2451
|
+
throw error;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Preprocess text before generating embedding
|
|
2456
|
+
* - Trim whitespace
|
|
2457
|
+
* - Limit length to avoid token limits
|
|
2458
|
+
* - Remove special characters that might cause issues
|
|
2459
|
+
*/
|
|
2460
|
+
preprocessText(text) {
|
|
2461
|
+
if (!text) return "";
|
|
2462
|
+
let processed = text.trim().replace(/\s+/g, " ");
|
|
2463
|
+
if (processed.length > 8e3) {
|
|
2464
|
+
processed = processed.substring(0, 8e3);
|
|
2465
|
+
}
|
|
2466
|
+
return processed;
|
|
2467
|
+
}
|
|
2468
|
+
/**
|
|
2469
|
+
* Calculate cosine similarity between two embeddings
|
|
2470
|
+
*/
|
|
2471
|
+
cosineSimilarity(a, b) {
|
|
2472
|
+
if (a.length !== b.length) {
|
|
2473
|
+
throw new Error("Embeddings must have same dimensions");
|
|
2474
|
+
}
|
|
2475
|
+
let dotProduct = 0;
|
|
2476
|
+
let normA = 0;
|
|
2477
|
+
let normB = 0;
|
|
2478
|
+
for (let i = 0; i < a.length; i++) {
|
|
2479
|
+
const aVal = a[i] ?? 0;
|
|
2480
|
+
const bVal = b[i] ?? 0;
|
|
2481
|
+
dotProduct += aVal * bVal;
|
|
2482
|
+
normA += aVal * aVal;
|
|
2483
|
+
normB += bVal * bVal;
|
|
2484
|
+
}
|
|
2485
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
|
|
2489
|
+
// src/plugins/core-plugins/ai-search-plugin/services/chunking.service.ts
|
|
2490
|
+
var ChunkingService = class {
|
|
2491
|
+
// Default chunk size (in approximate tokens)
|
|
2492
|
+
CHUNK_SIZE = 500;
|
|
2493
|
+
CHUNK_OVERLAP = 50;
|
|
2494
|
+
/**
|
|
2495
|
+
* Chunk a single content item
|
|
2496
|
+
*/
|
|
2497
|
+
chunkContent(contentId, collectionId, title, data, metadata = {}) {
|
|
2498
|
+
const text = this.extractText(data);
|
|
2499
|
+
if (!text || text.trim().length === 0) {
|
|
2500
|
+
console.warn(`[ChunkingService] No text found for content ${contentId}`);
|
|
2501
|
+
return [];
|
|
2502
|
+
}
|
|
2503
|
+
const textChunks = this.splitIntoChunks(text);
|
|
2504
|
+
return textChunks.map((chunkText, index) => ({
|
|
2505
|
+
id: `${contentId}_chunk_${index}`,
|
|
2506
|
+
content_id: contentId,
|
|
2507
|
+
collection_id: collectionId,
|
|
2508
|
+
title,
|
|
2509
|
+
text: chunkText,
|
|
2510
|
+
chunk_index: index,
|
|
2511
|
+
metadata: {
|
|
2512
|
+
...metadata,
|
|
2513
|
+
total_chunks: textChunks.length
|
|
2514
|
+
}
|
|
2515
|
+
}));
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Chunk multiple content items
|
|
2519
|
+
*/
|
|
2520
|
+
chunkContentBatch(items) {
|
|
2521
|
+
const allChunks = [];
|
|
2522
|
+
for (const item of items) {
|
|
2523
|
+
const chunks = this.chunkContent(
|
|
2524
|
+
item.id,
|
|
2525
|
+
item.collection_id,
|
|
2526
|
+
item.title,
|
|
2527
|
+
item.data,
|
|
2528
|
+
item.metadata
|
|
2529
|
+
);
|
|
2530
|
+
allChunks.push(...chunks);
|
|
2531
|
+
}
|
|
2532
|
+
return allChunks;
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* Extract all text from content data
|
|
2536
|
+
*/
|
|
2537
|
+
extractText(data) {
|
|
2538
|
+
const parts = [];
|
|
2539
|
+
if (data.title) parts.push(String(data.title));
|
|
2540
|
+
if (data.name) parts.push(String(data.name));
|
|
2541
|
+
if (data.description) parts.push(String(data.description));
|
|
2542
|
+
if (data.content) parts.push(String(data.content));
|
|
2543
|
+
if (data.body) parts.push(String(data.body));
|
|
2544
|
+
if (data.text) parts.push(String(data.text));
|
|
2545
|
+
if (data.summary) parts.push(String(data.summary));
|
|
2546
|
+
const extractRecursive = (obj) => {
|
|
2547
|
+
if (typeof obj === "string") {
|
|
2548
|
+
if (obj.length > 10 && !obj.startsWith("http")) {
|
|
2549
|
+
parts.push(obj);
|
|
2550
|
+
}
|
|
2551
|
+
} else if (Array.isArray(obj)) {
|
|
2552
|
+
obj.forEach(extractRecursive);
|
|
2553
|
+
} else if (obj && typeof obj === "object") {
|
|
2554
|
+
const skipKeys = ["id", "slug", "url", "image", "thumbnail", "metadata"];
|
|
2555
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
2556
|
+
if (!skipKeys.includes(key.toLowerCase())) {
|
|
2557
|
+
extractRecursive(value);
|
|
2558
|
+
}
|
|
2474
2559
|
});
|
|
2475
2560
|
}
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2561
|
+
};
|
|
2562
|
+
extractRecursive(data);
|
|
2563
|
+
return parts.join("\n\n").trim();
|
|
2564
|
+
}
|
|
2565
|
+
/**
|
|
2566
|
+
* Split text into overlapping chunks
|
|
2567
|
+
*/
|
|
2568
|
+
splitIntoChunks(text) {
|
|
2569
|
+
const words = text.split(/\s+/);
|
|
2570
|
+
if (words.length <= this.CHUNK_SIZE) {
|
|
2571
|
+
return [text];
|
|
2572
|
+
}
|
|
2573
|
+
const chunks = [];
|
|
2574
|
+
let startIndex = 0;
|
|
2575
|
+
while (startIndex < words.length) {
|
|
2576
|
+
const endIndex = Math.min(startIndex + this.CHUNK_SIZE, words.length);
|
|
2577
|
+
const chunk = words.slice(startIndex, endIndex).join(" ");
|
|
2578
|
+
chunks.push(chunk);
|
|
2579
|
+
startIndex += this.CHUNK_SIZE - this.CHUNK_OVERLAP;
|
|
2580
|
+
if (startIndex >= words.length - this.CHUNK_OVERLAP) {
|
|
2581
|
+
break;
|
|
2480
2582
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2583
|
+
}
|
|
2584
|
+
return chunks;
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* Get optimal chunk size based on content type
|
|
2588
|
+
*/
|
|
2589
|
+
getOptimalChunkSize(contentType) {
|
|
2590
|
+
switch (contentType) {
|
|
2591
|
+
case "blog_posts":
|
|
2592
|
+
case "articles":
|
|
2593
|
+
return 600;
|
|
2594
|
+
// Larger chunks for long-form content
|
|
2595
|
+
case "products":
|
|
2596
|
+
case "pages":
|
|
2597
|
+
return 400;
|
|
2598
|
+
// Medium chunks for structured content
|
|
2599
|
+
case "messages":
|
|
2600
|
+
case "comments":
|
|
2601
|
+
return 200;
|
|
2602
|
+
// Small chunks for short content
|
|
2603
|
+
default:
|
|
2604
|
+
return this.CHUNK_SIZE;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
|
|
2609
|
+
// src/plugins/core-plugins/ai-search-plugin/services/custom-rag.service.ts
|
|
2610
|
+
var CustomRAGService = class {
|
|
2611
|
+
constructor(db, ai, vectorize) {
|
|
2612
|
+
this.db = db;
|
|
2613
|
+
this.ai = ai;
|
|
2614
|
+
this.vectorize = vectorize;
|
|
2615
|
+
this.embeddingService = new EmbeddingService(ai);
|
|
2616
|
+
this.chunkingService = new ChunkingService();
|
|
2617
|
+
}
|
|
2618
|
+
embeddingService;
|
|
2619
|
+
chunkingService;
|
|
2620
|
+
/**
|
|
2621
|
+
* Index all content from a collection
|
|
2622
|
+
*/
|
|
2623
|
+
async indexCollection(collectionId) {
|
|
2624
|
+
console.log(`[CustomRAG] Starting indexing for collection: ${collectionId}`);
|
|
2625
|
+
try {
|
|
2626
|
+
const { results: contentItems } = await this.db.prepare(`
|
|
2627
|
+
SELECT c.id, c.title, c.data, c.collection_id, c.status,
|
|
2628
|
+
c.created_at, c.updated_at, c.author_id,
|
|
2629
|
+
col.name as collection_name, col.display_name as collection_display_name
|
|
2630
|
+
FROM content c
|
|
2631
|
+
JOIN collections col ON c.collection_id = col.id
|
|
2632
|
+
WHERE c.collection_id = ? AND c.status = 'published'
|
|
2633
|
+
`).bind(collectionId).all();
|
|
2634
|
+
const totalItems = contentItems?.length || 0;
|
|
2635
|
+
if (totalItems === 0) {
|
|
2636
|
+
console.log(`[CustomRAG] No content found in collection ${collectionId}`);
|
|
2637
|
+
return { total_items: 0, total_chunks: 0, indexed_chunks: 0, errors: 0 };
|
|
2638
|
+
}
|
|
2639
|
+
const items = (contentItems || []).map((item) => ({
|
|
2640
|
+
id: item.id,
|
|
2641
|
+
collection_id: item.collection_id,
|
|
2642
|
+
title: item.title || "Untitled",
|
|
2643
|
+
data: typeof item.data === "string" ? JSON.parse(item.data) : item.data,
|
|
2644
|
+
metadata: {
|
|
2645
|
+
status: item.status,
|
|
2646
|
+
created_at: item.created_at,
|
|
2647
|
+
updated_at: item.updated_at,
|
|
2648
|
+
author_id: item.author_id,
|
|
2649
|
+
collection_name: item.collection_name,
|
|
2650
|
+
collection_display_name: item.collection_display_name
|
|
2651
|
+
}
|
|
2652
|
+
}));
|
|
2653
|
+
const chunks = this.chunkingService.chunkContentBatch(items);
|
|
2654
|
+
const totalChunks = chunks.length;
|
|
2655
|
+
console.log(`[CustomRAG] Generated ${totalChunks} chunks from ${totalItems} items`);
|
|
2656
|
+
const embeddings = await this.embeddingService.generateBatch(
|
|
2657
|
+
chunks.map((c) => `${c.title}
|
|
2658
|
+
|
|
2659
|
+
${c.text}`)
|
|
2660
|
+
);
|
|
2661
|
+
console.log(`[CustomRAG] Generated ${embeddings.length} embeddings`);
|
|
2662
|
+
let indexedChunks = 0;
|
|
2663
|
+
let errors = 0;
|
|
2664
|
+
const batchSize = 100;
|
|
2665
|
+
for (let i = 0; i < chunks.length; i += batchSize) {
|
|
2666
|
+
const chunkBatch = chunks.slice(i, i + batchSize);
|
|
2667
|
+
const embeddingBatch = embeddings.slice(i, i + batchSize);
|
|
2668
|
+
try {
|
|
2669
|
+
await this.vectorize.upsert(
|
|
2670
|
+
chunkBatch.map((chunk, idx) => ({
|
|
2671
|
+
id: chunk.id,
|
|
2672
|
+
values: embeddingBatch[idx],
|
|
2673
|
+
metadata: {
|
|
2674
|
+
content_id: chunk.content_id,
|
|
2675
|
+
collection_id: chunk.collection_id,
|
|
2676
|
+
title: chunk.title,
|
|
2677
|
+
text: chunk.text.substring(0, 500),
|
|
2678
|
+
// Store snippet for display
|
|
2679
|
+
chunk_index: chunk.chunk_index,
|
|
2680
|
+
...chunk.metadata
|
|
2681
|
+
}
|
|
2682
|
+
}))
|
|
2683
|
+
);
|
|
2684
|
+
indexedChunks += chunkBatch.length;
|
|
2685
|
+
console.log(`[CustomRAG] Indexed batch ${i / batchSize + 1}: ${chunkBatch.length} chunks`);
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
console.error(`[CustomRAG] Error indexing batch ${i / batchSize + 1}:`, error);
|
|
2688
|
+
errors += chunkBatch.length;
|
|
2511
2689
|
}
|
|
2512
|
-
} catch (error) {
|
|
2513
|
-
console.error("Failed to send magic link email:", error);
|
|
2514
|
-
return c.json({
|
|
2515
|
-
error: "Failed to send email. Please try again later."
|
|
2516
|
-
}, 500);
|
|
2517
2690
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2691
|
+
console.log(`[CustomRAG] Indexing complete: ${indexedChunks}/${totalChunks} chunks indexed`);
|
|
2692
|
+
return {
|
|
2693
|
+
total_items: totalItems,
|
|
2694
|
+
total_chunks: totalChunks,
|
|
2695
|
+
indexed_chunks: indexedChunks,
|
|
2696
|
+
errors
|
|
2697
|
+
};
|
|
2523
2698
|
} catch (error) {
|
|
2524
|
-
console.error(
|
|
2525
|
-
|
|
2699
|
+
console.error(`[CustomRAG] Error indexing collection ${collectionId}:`, error);
|
|
2700
|
+
throw error;
|
|
2526
2701
|
}
|
|
2527
|
-
}
|
|
2528
|
-
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Search using RAG (semantic search with Vectorize)
|
|
2705
|
+
*/
|
|
2706
|
+
async search(query, settings) {
|
|
2707
|
+
const startTime = Date.now();
|
|
2529
2708
|
try {
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2709
|
+
console.log(`[CustomRAG] Searching for: "${query.query}"`);
|
|
2710
|
+
const queryEmbedding = await this.embeddingService.generateEmbedding(query.query);
|
|
2711
|
+
const filter = {};
|
|
2712
|
+
if (query.filters?.collections && query.filters.collections.length > 0) {
|
|
2713
|
+
filter.collection_id = { $in: query.filters.collections };
|
|
2714
|
+
} else if (settings.selected_collections.length > 0) {
|
|
2715
|
+
filter.collection_id = { $in: settings.selected_collections };
|
|
2533
2716
|
}
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
SELECT * FROM magic_links
|
|
2537
|
-
WHERE token = ? AND used = 0
|
|
2538
|
-
`).bind(token).first();
|
|
2539
|
-
if (!magicLink) {
|
|
2540
|
-
return c.redirect("/auth/login?error=Invalid or expired magic link");
|
|
2717
|
+
if (query.filters?.status && query.filters.status.length > 0) {
|
|
2718
|
+
filter.status = { $in: query.filters.status };
|
|
2541
2719
|
}
|
|
2542
|
-
|
|
2543
|
-
|
|
2720
|
+
const vectorResults = await this.vectorize.query(queryEmbedding, {
|
|
2721
|
+
topK: 50,
|
|
2722
|
+
// Max allowed with returnMetadata: true
|
|
2723
|
+
returnMetadata: true
|
|
2724
|
+
});
|
|
2725
|
+
let filteredMatches = vectorResults.matches || [];
|
|
2726
|
+
if (filter.collection_id?.$in && Array.isArray(filter.collection_id.$in)) {
|
|
2727
|
+
const allowedCollections = filter.collection_id.$in;
|
|
2728
|
+
filteredMatches = filteredMatches.filter(
|
|
2729
|
+
(match) => allowedCollections.includes(match.metadata?.collection_id)
|
|
2730
|
+
);
|
|
2544
2731
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
magicLink.user_email,
|
|
2561
|
-
username,
|
|
2562
|
-
username,
|
|
2563
|
-
"",
|
|
2564
|
-
now,
|
|
2565
|
-
now
|
|
2566
|
-
).run();
|
|
2567
|
-
user = {
|
|
2568
|
-
id: userId,
|
|
2569
|
-
email: magicLink.user_email,
|
|
2570
|
-
username,
|
|
2571
|
-
role: "viewer"
|
|
2732
|
+
if (filter.status?.$in && Array.isArray(filter.status.$in)) {
|
|
2733
|
+
const allowedStatuses = filter.status.$in;
|
|
2734
|
+
filteredMatches = filteredMatches.filter(
|
|
2735
|
+
(match) => allowedStatuses.includes(match.metadata?.status)
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
const topK = query.limit || settings.results_limit || 20;
|
|
2739
|
+
filteredMatches = filteredMatches.slice(0, topK);
|
|
2740
|
+
vectorResults.matches = filteredMatches;
|
|
2741
|
+
if (!vectorResults.matches || vectorResults.matches.length === 0) {
|
|
2742
|
+
return {
|
|
2743
|
+
results: [],
|
|
2744
|
+
total: 0,
|
|
2745
|
+
query_time_ms: Date.now() - startTime,
|
|
2746
|
+
mode: "ai"
|
|
2572
2747
|
};
|
|
2573
|
-
} else if (!user) {
|
|
2574
|
-
return c.redirect("/auth/login?error=No account found for this email");
|
|
2575
2748
|
}
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2749
|
+
const contentIds = [...new Set(
|
|
2750
|
+
vectorResults.matches.map((m) => m.metadata.content_id)
|
|
2751
|
+
)];
|
|
2752
|
+
const placeholders = contentIds.map(() => "?").join(",");
|
|
2753
|
+
const { results: contentItems } = await this.db.prepare(`
|
|
2754
|
+
SELECT c.id, c.title, c.slug, c.collection_id, c.status,
|
|
2755
|
+
c.created_at, c.updated_at, c.author_id,
|
|
2756
|
+
col.display_name as collection_name
|
|
2757
|
+
FROM content c
|
|
2758
|
+
JOIN collections col ON c.collection_id = col.id
|
|
2759
|
+
WHERE c.id IN (${placeholders})
|
|
2760
|
+
`).bind(...contentIds).all();
|
|
2761
|
+
const searchResults = (contentItems || []).map((item) => {
|
|
2762
|
+
const matchingChunks = vectorResults.matches.filter(
|
|
2763
|
+
(m) => m.metadata.content_id === item.id
|
|
2764
|
+
);
|
|
2765
|
+
const bestMatch = matchingChunks.reduce(
|
|
2766
|
+
(best, current) => current.score > (best?.score || 0) ? current : best,
|
|
2767
|
+
null
|
|
2768
|
+
);
|
|
2769
|
+
return {
|
|
2770
|
+
id: item.id,
|
|
2771
|
+
title: item.title || "Untitled",
|
|
2772
|
+
slug: item.slug || "",
|
|
2773
|
+
collection_id: item.collection_id,
|
|
2774
|
+
collection_name: item.collection_name,
|
|
2775
|
+
snippet: bestMatch?.metadata?.text || "",
|
|
2776
|
+
relevance_score: bestMatch?.score || 0,
|
|
2777
|
+
status: item.status,
|
|
2778
|
+
created_at: item.created_at,
|
|
2779
|
+
updated_at: item.updated_at
|
|
2780
|
+
};
|
|
2781
|
+
});
|
|
2782
|
+
searchResults.sort((a, b) => (b.relevance_score || 0) - (a.relevance_score || 0));
|
|
2783
|
+
const queryTime = Date.now() - startTime;
|
|
2784
|
+
console.log(`[CustomRAG] Search completed in ${queryTime}ms, ${searchResults.length} results`);
|
|
2785
|
+
return {
|
|
2786
|
+
results: searchResults,
|
|
2787
|
+
total: searchResults.length,
|
|
2788
|
+
query_time_ms: queryTime,
|
|
2789
|
+
mode: "ai"
|
|
2790
|
+
};
|
|
2791
|
+
} catch (error) {
|
|
2792
|
+
console.error("[CustomRAG] Search error:", error);
|
|
2793
|
+
throw error;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Update index for a single content item
|
|
2798
|
+
*/
|
|
2799
|
+
async updateContentIndex(contentId) {
|
|
2800
|
+
try {
|
|
2801
|
+
const content2 = await this.db.prepare(`
|
|
2802
|
+
SELECT c.id, c.title, c.data, c.collection_id, c.status,
|
|
2803
|
+
c.created_at, c.updated_at, c.author_id,
|
|
2804
|
+
col.name as collection_name, col.display_name as collection_display_name
|
|
2805
|
+
FROM content c
|
|
2806
|
+
JOIN collections col ON c.collection_id = col.id
|
|
2807
|
+
WHERE c.id = ?
|
|
2808
|
+
`).bind(contentId).first();
|
|
2809
|
+
if (!content2) {
|
|
2810
|
+
console.warn(`[CustomRAG] Content ${contentId} not found`);
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
if (content2.status !== "published") {
|
|
2814
|
+
await this.removeContentFromIndex(contentId);
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
const chunks = this.chunkingService.chunkContent(
|
|
2818
|
+
content2.id,
|
|
2819
|
+
content2.collection_id,
|
|
2820
|
+
content2.title || "Untitled",
|
|
2821
|
+
typeof content2.data === "string" ? JSON.parse(content2.data) : content2.data,
|
|
2822
|
+
{
|
|
2823
|
+
status: content2.status,
|
|
2824
|
+
created_at: content2.created_at,
|
|
2825
|
+
updated_at: content2.updated_at,
|
|
2826
|
+
author_id: content2.author_id,
|
|
2827
|
+
collection_name: content2.collection_name,
|
|
2828
|
+
collection_display_name: content2.collection_display_name
|
|
2829
|
+
}
|
|
2585
2830
|
);
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2831
|
+
const embeddings = await this.embeddingService.generateBatch(
|
|
2832
|
+
chunks.map((c) => `${c.title}
|
|
2833
|
+
|
|
2834
|
+
${c.text}`)
|
|
2835
|
+
);
|
|
2836
|
+
await this.vectorize.upsert(
|
|
2837
|
+
chunks.map((chunk, idx) => ({
|
|
2838
|
+
id: chunk.id,
|
|
2839
|
+
values: embeddings[idx],
|
|
2840
|
+
metadata: {
|
|
2841
|
+
content_id: chunk.content_id,
|
|
2842
|
+
collection_id: chunk.collection_id,
|
|
2843
|
+
title: chunk.title,
|
|
2844
|
+
text: chunk.text.substring(0, 500),
|
|
2845
|
+
chunk_index: chunk.chunk_index,
|
|
2846
|
+
...chunk.metadata
|
|
2847
|
+
}
|
|
2848
|
+
}))
|
|
2849
|
+
);
|
|
2850
|
+
console.log(`[CustomRAG] Updated index for content ${contentId}: ${chunks.length} chunks`);
|
|
2591
2851
|
} catch (error) {
|
|
2592
|
-
console.error(
|
|
2593
|
-
|
|
2852
|
+
console.error(`[CustomRAG] Error updating index for ${contentId}:`, error);
|
|
2853
|
+
throw error;
|
|
2594
2854
|
}
|
|
2595
|
-
}
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2855
|
+
}
|
|
2856
|
+
/**
|
|
2857
|
+
* Remove content from index
|
|
2858
|
+
*/
|
|
2859
|
+
async removeContentFromIndex(contentId) {
|
|
2860
|
+
try {
|
|
2861
|
+
console.log(`[CustomRAG] Removing content ${contentId} from index`);
|
|
2862
|
+
} catch (error) {
|
|
2863
|
+
console.error(`[CustomRAG] Error removing content ${contentId}:`, error);
|
|
2864
|
+
throw error;
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Get search suggestions based on query
|
|
2869
|
+
*/
|
|
2870
|
+
async getSuggestions(partialQuery, limit = 5) {
|
|
2871
|
+
try {
|
|
2872
|
+
const queryEmbedding = await this.embeddingService.generateEmbedding(partialQuery);
|
|
2873
|
+
const results = await this.vectorize.query(queryEmbedding, {
|
|
2874
|
+
topK: limit * 2,
|
|
2875
|
+
// Get more to filter
|
|
2876
|
+
returnMetadata: true
|
|
2877
|
+
});
|
|
2878
|
+
const suggestions = [...new Set(
|
|
2879
|
+
results.matches?.map((m) => m.metadata.title).filter(Boolean) || []
|
|
2880
|
+
)].slice(0, limit);
|
|
2881
|
+
return suggestions;
|
|
2882
|
+
} catch (error) {
|
|
2883
|
+
console.error("[CustomRAG] Error getting suggestions:", error);
|
|
2884
|
+
return [];
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
/**
|
|
2888
|
+
* Check if Vectorize is available and configured
|
|
2889
|
+
*/
|
|
2890
|
+
isAvailable() {
|
|
2891
|
+
return !!this.vectorize && !!this.ai;
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
|
|
2895
|
+
// src/plugins/core-plugins/ai-search-plugin/services/ai-search.ts
|
|
2896
|
+
var AISearchService = class {
|
|
2897
|
+
constructor(db, ai, vectorize) {
|
|
2898
|
+
this.db = db;
|
|
2899
|
+
this.ai = ai;
|
|
2900
|
+
this.vectorize = vectorize;
|
|
2901
|
+
if (this.ai && this.vectorize) {
|
|
2902
|
+
this.customRAG = new CustomRAGService(db, ai, vectorize);
|
|
2903
|
+
console.log("[AISearchService] Custom RAG initialized");
|
|
2904
|
+
} else {
|
|
2905
|
+
console.log("[AISearchService] Custom RAG not available, using keyword search only");
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
customRAG;
|
|
2909
|
+
/**
|
|
2910
|
+
* Get plugin settings
|
|
2911
|
+
*/
|
|
2912
|
+
async getSettings() {
|
|
2913
|
+
try {
|
|
2914
|
+
const plugin = await this.db.prepare(`SELECT settings FROM plugins WHERE id = ? LIMIT 1`).bind("ai-search").first();
|
|
2915
|
+
if (!plugin || !plugin.settings) {
|
|
2916
|
+
return this.getDefaultSettings();
|
|
2917
|
+
}
|
|
2918
|
+
return JSON.parse(plugin.settings);
|
|
2919
|
+
} catch (error) {
|
|
2920
|
+
console.error("Error fetching AI Search settings:", error);
|
|
2921
|
+
return this.getDefaultSettings();
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Get default settings
|
|
2926
|
+
*/
|
|
2927
|
+
getDefaultSettings() {
|
|
2928
|
+
return {
|
|
2929
|
+
enabled: true,
|
|
2930
|
+
ai_mode_enabled: true,
|
|
2931
|
+
selected_collections: [],
|
|
2932
|
+
dismissed_collections: [],
|
|
2933
|
+
autocomplete_enabled: true,
|
|
2934
|
+
cache_duration: 1,
|
|
2935
|
+
results_limit: 20,
|
|
2936
|
+
index_media: false
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
/**
|
|
2940
|
+
* Update plugin settings
|
|
2941
|
+
*/
|
|
2942
|
+
async updateSettings(settings) {
|
|
2943
|
+
const existing = await this.getSettings();
|
|
2944
|
+
const updated = {
|
|
2945
|
+
...existing,
|
|
2946
|
+
...settings
|
|
2947
|
+
};
|
|
2948
|
+
try {
|
|
2949
|
+
await this.db.prepare(`
|
|
2950
|
+
UPDATE plugins
|
|
2951
|
+
SET settings = ?,
|
|
2952
|
+
updated_at = unixepoch()
|
|
2953
|
+
WHERE id = 'ai-search'
|
|
2954
|
+
`).bind(JSON.stringify(updated)).run();
|
|
2955
|
+
return updated;
|
|
2956
|
+
} catch (error) {
|
|
2957
|
+
console.error("Error updating AI Search settings:", error);
|
|
2958
|
+
throw error;
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
/**
|
|
2962
|
+
* Detect new collections that aren't indexed or dismissed
|
|
2963
|
+
*/
|
|
2964
|
+
async detectNewCollections() {
|
|
2965
|
+
try {
|
|
2966
|
+
const collectionsStmt = this.db.prepare(
|
|
2967
|
+
"SELECT id, name, display_name, description FROM collections WHERE is_active = 1"
|
|
2968
|
+
);
|
|
2969
|
+
const { results: allCollections } = await collectionsStmt.all();
|
|
2970
|
+
const collections2 = (allCollections || []).filter(
|
|
2971
|
+
(col) => {
|
|
2972
|
+
if (!col.name) return false;
|
|
2973
|
+
const name = col.name.toLowerCase();
|
|
2974
|
+
return !name.startsWith("test_") && !name.endsWith("_test") && name !== "test_collection" && !name.includes("_test_") && name !== "large_payload_test" && name !== "concurrent_test";
|
|
2975
|
+
}
|
|
2976
|
+
);
|
|
2977
|
+
const settings = await this.getSettings();
|
|
2978
|
+
const selected = settings?.selected_collections || [];
|
|
2979
|
+
const dismissed = settings?.dismissed_collections || [];
|
|
2980
|
+
const notifications = [];
|
|
2981
|
+
for (const collection of collections2 || []) {
|
|
2982
|
+
const collectionId = String(collection.id);
|
|
2983
|
+
if (selected.includes(collectionId) || dismissed.includes(collectionId)) {
|
|
2984
|
+
continue;
|
|
2985
|
+
}
|
|
2986
|
+
const countStmt = this.db.prepare(
|
|
2987
|
+
"SELECT COUNT(*) as count FROM content WHERE collection_id = ?"
|
|
2988
|
+
);
|
|
2989
|
+
const countResult = await countStmt.bind(collectionId).first();
|
|
2990
|
+
const itemCount = countResult?.count || 0;
|
|
2991
|
+
notifications.push({
|
|
2992
|
+
collection: {
|
|
2993
|
+
id: collectionId,
|
|
2994
|
+
name: collection.name,
|
|
2995
|
+
display_name: collection.display_name,
|
|
2996
|
+
description: collection.description,
|
|
2997
|
+
item_count: itemCount,
|
|
2998
|
+
is_indexed: false,
|
|
2999
|
+
is_dismissed: false,
|
|
3000
|
+
is_new: true
|
|
3001
|
+
},
|
|
3002
|
+
message: `New collection "${collection.display_name}" with ${itemCount} items available for indexing`
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
return notifications;
|
|
3006
|
+
} catch (error) {
|
|
3007
|
+
console.error("Error detecting new collections:", error);
|
|
3008
|
+
return [];
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
/**
|
|
3012
|
+
* Get all collections with indexing status
|
|
3013
|
+
*/
|
|
3014
|
+
async getAllCollections() {
|
|
3015
|
+
try {
|
|
3016
|
+
const collectionsStmt = this.db.prepare(
|
|
3017
|
+
"SELECT id, name, display_name, description FROM collections WHERE is_active = 1 ORDER BY display_name"
|
|
3018
|
+
);
|
|
3019
|
+
const { results: allCollections } = await collectionsStmt.all();
|
|
3020
|
+
console.log("[AISearchService.getAllCollections] Raw collections from DB:", allCollections?.length || 0);
|
|
3021
|
+
const firstCollection = allCollections?.[0];
|
|
3022
|
+
if (firstCollection) {
|
|
3023
|
+
console.log("[AISearchService.getAllCollections] Sample collection:", {
|
|
3024
|
+
id: firstCollection.id,
|
|
3025
|
+
name: firstCollection.name,
|
|
3026
|
+
display_name: firstCollection.display_name
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3029
|
+
const collections2 = (allCollections || []).filter(
|
|
3030
|
+
(col) => col.id && col.name
|
|
3031
|
+
);
|
|
3032
|
+
console.log("[AISearchService.getAllCollections] After filtering test collections:", collections2.length);
|
|
3033
|
+
console.log("[AISearchService.getAllCollections] Remaining collections:", collections2.map((c) => c.name).join(", "));
|
|
3034
|
+
const settings = await this.getSettings();
|
|
3035
|
+
const selected = settings?.selected_collections || [];
|
|
3036
|
+
const dismissed = settings?.dismissed_collections || [];
|
|
3037
|
+
console.log("[AISearchService.getAllCollections] Settings:", {
|
|
3038
|
+
selected_count: selected.length,
|
|
3039
|
+
dismissed_count: dismissed.length,
|
|
3040
|
+
selected
|
|
3041
|
+
});
|
|
3042
|
+
const collectionInfos = [];
|
|
3043
|
+
for (const collection of collections2) {
|
|
3044
|
+
if (!collection.id || !collection.name) continue;
|
|
3045
|
+
const collectionId = String(collection.id);
|
|
3046
|
+
if (!collectionId) {
|
|
3047
|
+
console.warn("[AISearchService] Skipping invalid collection:", collection);
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
const countStmt = this.db.prepare(
|
|
3051
|
+
"SELECT COUNT(*) as count FROM content WHERE collection_id = ?"
|
|
3052
|
+
);
|
|
3053
|
+
const countResult = await countStmt.bind(collectionId).first();
|
|
3054
|
+
const itemCount = countResult?.count || 0;
|
|
3055
|
+
collectionInfos.push({
|
|
3056
|
+
id: collectionId,
|
|
3057
|
+
name: collection.name,
|
|
3058
|
+
display_name: collection.display_name || collection.name,
|
|
3059
|
+
description: collection.description,
|
|
3060
|
+
item_count: itemCount,
|
|
3061
|
+
is_indexed: selected.includes(collectionId),
|
|
3062
|
+
is_dismissed: dismissed.includes(collectionId),
|
|
3063
|
+
is_new: !selected.includes(collectionId) && !dismissed.includes(collectionId)
|
|
3064
|
+
});
|
|
3065
|
+
}
|
|
3066
|
+
console.log("[AISearchService.getAllCollections] Returning collectionInfos:", collectionInfos.length);
|
|
3067
|
+
const firstInfo = collectionInfos[0];
|
|
3068
|
+
if (collectionInfos.length > 0 && firstInfo) {
|
|
3069
|
+
console.log("[AISearchService.getAllCollections] First collectionInfo:", {
|
|
3070
|
+
id: firstInfo.id,
|
|
3071
|
+
name: firstInfo.name,
|
|
3072
|
+
display_name: firstInfo.display_name,
|
|
3073
|
+
item_count: firstInfo.item_count
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3076
|
+
return collectionInfos;
|
|
3077
|
+
} catch (error) {
|
|
3078
|
+
console.error("[AISearchService] Error fetching collections:", error);
|
|
3079
|
+
return [];
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
/**
|
|
3083
|
+
* Execute search query
|
|
3084
|
+
*/
|
|
3085
|
+
async search(query) {
|
|
3086
|
+
const settings = await this.getSettings();
|
|
3087
|
+
if (!settings?.enabled) {
|
|
3088
|
+
return {
|
|
3089
|
+
results: [],
|
|
3090
|
+
total: 0,
|
|
3091
|
+
query_time_ms: 0,
|
|
3092
|
+
mode: query.mode
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
if (query.mode === "ai" && settings.ai_mode_enabled && this.customRAG?.isAvailable()) {
|
|
3096
|
+
return this.searchAI(query, settings);
|
|
3097
|
+
}
|
|
3098
|
+
return this.searchKeyword(query, settings);
|
|
3099
|
+
}
|
|
3100
|
+
/**
|
|
3101
|
+
* AI-powered semantic search using Custom RAG
|
|
3102
|
+
*/
|
|
3103
|
+
async searchAI(query, settings) {
|
|
3104
|
+
try {
|
|
3105
|
+
if (!this.customRAG) {
|
|
3106
|
+
console.warn("[AISearchService] CustomRAG not available, falling back to keyword search");
|
|
3107
|
+
return this.searchKeyword(query, settings);
|
|
3108
|
+
}
|
|
3109
|
+
const result = await this.customRAG.search(query, settings);
|
|
3110
|
+
return result;
|
|
3111
|
+
} catch (error) {
|
|
3112
|
+
console.error("[AISearchService] AI search error, falling back to keyword:", error);
|
|
3113
|
+
return this.searchKeyword(query, settings);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Traditional keyword search
|
|
3118
|
+
*/
|
|
3119
|
+
async searchKeyword(query, settings) {
|
|
3120
|
+
const startTime = Date.now();
|
|
3121
|
+
try {
|
|
3122
|
+
const conditions = [];
|
|
3123
|
+
const params = [];
|
|
3124
|
+
if (query.query) {
|
|
3125
|
+
conditions.push("(c.title LIKE ? OR c.slug LIKE ? OR c.data LIKE ?)");
|
|
3126
|
+
const searchTerm = `%${query.query}%`;
|
|
3127
|
+
params.push(searchTerm, searchTerm, searchTerm);
|
|
3128
|
+
}
|
|
3129
|
+
if (query.filters?.collections && query.filters.collections.length > 0) {
|
|
3130
|
+
const placeholders = query.filters.collections.map(() => "?").join(",");
|
|
3131
|
+
conditions.push(`c.collection_id IN (${placeholders})`);
|
|
3132
|
+
params.push(...query.filters.collections);
|
|
3133
|
+
} else if (settings.selected_collections.length > 0) {
|
|
3134
|
+
const placeholders = settings.selected_collections.map(() => "?").join(",");
|
|
3135
|
+
conditions.push(`c.collection_id IN (${placeholders})`);
|
|
3136
|
+
params.push(...settings.selected_collections);
|
|
3137
|
+
}
|
|
3138
|
+
if (query.filters?.status && query.filters.status.length > 0) {
|
|
3139
|
+
const placeholders = query.filters.status.map(() => "?").join(",");
|
|
3140
|
+
conditions.push(`c.status IN (${placeholders})`);
|
|
3141
|
+
params.push(...query.filters.status);
|
|
3142
|
+
} else {
|
|
3143
|
+
conditions.push("c.status != 'deleted'");
|
|
3144
|
+
}
|
|
3145
|
+
if (query.filters?.dateRange) {
|
|
3146
|
+
const field = query.filters.dateRange.field || "created_at";
|
|
3147
|
+
if (query.filters.dateRange.start) {
|
|
3148
|
+
conditions.push(`c.${field} >= ?`);
|
|
3149
|
+
params.push(query.filters.dateRange.start.getTime());
|
|
3150
|
+
}
|
|
3151
|
+
if (query.filters.dateRange.end) {
|
|
3152
|
+
conditions.push(`c.${field} <= ?`);
|
|
3153
|
+
params.push(query.filters.dateRange.end.getTime());
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
if (query.filters?.author) {
|
|
3157
|
+
conditions.push("c.author_id = ?");
|
|
3158
|
+
params.push(query.filters.author);
|
|
3159
|
+
}
|
|
3160
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3161
|
+
const countStmt = this.db.prepare(`
|
|
3162
|
+
SELECT COUNT(*) as count
|
|
3163
|
+
FROM content c
|
|
3164
|
+
${whereClause}
|
|
3165
|
+
`);
|
|
3166
|
+
const countResult = await countStmt.bind(...params).first();
|
|
3167
|
+
const total = countResult?.count || 0;
|
|
3168
|
+
const limit = query.limit || settings.results_limit;
|
|
3169
|
+
const offset = query.offset || 0;
|
|
3170
|
+
const resultsStmt = this.db.prepare(`
|
|
3171
|
+
SELECT
|
|
3172
|
+
c.id, c.title, c.slug, c.collection_id, c.status,
|
|
3173
|
+
c.created_at, c.updated_at, c.author_id, c.data,
|
|
3174
|
+
col.name as collection_name, col.display_name as collection_display_name,
|
|
3175
|
+
u.email as author_email
|
|
3176
|
+
FROM content c
|
|
3177
|
+
JOIN collections col ON c.collection_id = col.id
|
|
3178
|
+
LEFT JOIN users u ON c.author_id = u.id
|
|
3179
|
+
${whereClause}
|
|
3180
|
+
ORDER BY c.updated_at DESC
|
|
3181
|
+
LIMIT ? OFFSET ?
|
|
3182
|
+
`);
|
|
3183
|
+
const { results } = await resultsStmt.bind(...params, limit, offset).all();
|
|
3184
|
+
const searchResults = (results || []).map((row) => ({
|
|
3185
|
+
id: String(row.id),
|
|
3186
|
+
title: row.title || "Untitled",
|
|
3187
|
+
slug: row.slug || "",
|
|
3188
|
+
collection_id: String(row.collection_id),
|
|
3189
|
+
collection_name: row.collection_display_name || row.collection_name,
|
|
3190
|
+
snippet: this.extractSnippet(row.data, query.query),
|
|
3191
|
+
status: row.status,
|
|
3192
|
+
created_at: Number(row.created_at),
|
|
3193
|
+
updated_at: Number(row.updated_at),
|
|
3194
|
+
author_name: row.author_email
|
|
3195
|
+
}));
|
|
3196
|
+
const queryTime = Date.now() - startTime;
|
|
3197
|
+
await this.logSearch(query.query, query.mode, searchResults.length);
|
|
3198
|
+
return {
|
|
3199
|
+
results: searchResults,
|
|
3200
|
+
total,
|
|
3201
|
+
query_time_ms: queryTime,
|
|
3202
|
+
mode: query.mode
|
|
3203
|
+
};
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
console.error("Keyword search error:", error);
|
|
3206
|
+
return {
|
|
3207
|
+
results: [],
|
|
3208
|
+
total: 0,
|
|
3209
|
+
query_time_ms: Date.now() - startTime,
|
|
3210
|
+
mode: query.mode
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
/**
|
|
3215
|
+
* Extract snippet from content data
|
|
3216
|
+
*/
|
|
3217
|
+
extractSnippet(data, query) {
|
|
3218
|
+
try {
|
|
3219
|
+
const parsed = typeof data === "string" ? JSON.parse(data) : data;
|
|
3220
|
+
const text = JSON.stringify(parsed).toLowerCase();
|
|
3221
|
+
const queryLower = query.toLowerCase();
|
|
3222
|
+
const index = text.indexOf(queryLower);
|
|
3223
|
+
if (index === -1) {
|
|
3224
|
+
return JSON.stringify(parsed).substring(0, 200) + "...";
|
|
3225
|
+
}
|
|
3226
|
+
const start = Math.max(0, index - 50);
|
|
3227
|
+
const end = Math.min(text.length, index + query.length + 50);
|
|
3228
|
+
return text.substring(start, end) + "...";
|
|
3229
|
+
} catch {
|
|
3230
|
+
return data.substring(0, 200) + "...";
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Get search suggestions (autocomplete)
|
|
3235
|
+
*/
|
|
3236
|
+
async getSearchSuggestions(partial) {
|
|
3237
|
+
try {
|
|
3238
|
+
const settings = await this.getSettings();
|
|
3239
|
+
if (!settings?.autocomplete_enabled) {
|
|
3240
|
+
return [];
|
|
3241
|
+
}
|
|
3242
|
+
if (this.customRAG?.isAvailable()) {
|
|
3243
|
+
try {
|
|
3244
|
+
const aiSuggestions = await this.customRAG.getSuggestions(partial, 5);
|
|
3245
|
+
if (aiSuggestions.length > 0) {
|
|
3246
|
+
return aiSuggestions;
|
|
3247
|
+
}
|
|
3248
|
+
} catch (error) {
|
|
3249
|
+
console.error("[AISearchService] Error getting AI suggestions:", error);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
const stmt = this.db.prepare(`
|
|
3253
|
+
SELECT DISTINCT query
|
|
3254
|
+
FROM ai_search_history
|
|
3255
|
+
WHERE query LIKE ?
|
|
3256
|
+
ORDER BY created_at DESC
|
|
3257
|
+
LIMIT 10
|
|
3258
|
+
`);
|
|
3259
|
+
const { results } = await stmt.bind(`%${partial}%`).all();
|
|
3260
|
+
return (results || []).map((r) => r.query);
|
|
3261
|
+
} catch (error) {
|
|
3262
|
+
console.error("Error getting suggestions:", error);
|
|
3263
|
+
return [];
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
/**
|
|
3267
|
+
* Log search query to history
|
|
3268
|
+
*/
|
|
3269
|
+
async logSearch(query, mode, resultsCount) {
|
|
3270
|
+
try {
|
|
3271
|
+
const stmt = this.db.prepare(`
|
|
3272
|
+
INSERT INTO ai_search_history (query, mode, results_count, created_at)
|
|
3273
|
+
VALUES (?, ?, ?, ?)
|
|
3274
|
+
`);
|
|
3275
|
+
await stmt.bind(query, mode, resultsCount, Date.now()).run();
|
|
3276
|
+
} catch (error) {
|
|
3277
|
+
console.error("Error logging search:", error);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
/**
|
|
3281
|
+
* Get search analytics
|
|
3282
|
+
*/
|
|
3283
|
+
async getSearchAnalytics() {
|
|
3284
|
+
try {
|
|
3285
|
+
const totalStmt = this.db.prepare(`
|
|
3286
|
+
SELECT COUNT(*) as count
|
|
3287
|
+
FROM ai_search_history
|
|
3288
|
+
WHERE created_at >= ?
|
|
3289
|
+
`);
|
|
3290
|
+
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1e3;
|
|
3291
|
+
const totalResult = await totalStmt.bind(thirtyDaysAgo).first();
|
|
3292
|
+
const modeStmt = this.db.prepare(`
|
|
3293
|
+
SELECT mode, COUNT(*) as count
|
|
3294
|
+
FROM ai_search_history
|
|
3295
|
+
WHERE created_at >= ?
|
|
3296
|
+
GROUP BY mode
|
|
3297
|
+
`);
|
|
3298
|
+
const { results: modeResults } = await modeStmt.bind(thirtyDaysAgo).all();
|
|
3299
|
+
const aiCount = modeResults?.find((r) => r.mode === "ai")?.count || 0;
|
|
3300
|
+
const keywordCount = modeResults?.find((r) => r.mode === "keyword")?.count || 0;
|
|
3301
|
+
const popularStmt = this.db.prepare(`
|
|
3302
|
+
SELECT query, COUNT(*) as count
|
|
3303
|
+
FROM ai_search_history
|
|
3304
|
+
WHERE created_at >= ?
|
|
3305
|
+
GROUP BY query
|
|
3306
|
+
ORDER BY count DESC
|
|
3307
|
+
LIMIT 10
|
|
3308
|
+
`);
|
|
3309
|
+
const { results: popularResults } = await popularStmt.bind(thirtyDaysAgo).all();
|
|
3310
|
+
return {
|
|
3311
|
+
total_queries: totalResult?.count || 0,
|
|
3312
|
+
ai_queries: aiCount,
|
|
3313
|
+
keyword_queries: keywordCount,
|
|
3314
|
+
popular_queries: (popularResults || []).map((r) => ({
|
|
3315
|
+
query: r.query,
|
|
3316
|
+
count: r.count
|
|
3317
|
+
})),
|
|
3318
|
+
average_query_time: 0
|
|
3319
|
+
// TODO: Track query times
|
|
3320
|
+
};
|
|
3321
|
+
} catch (error) {
|
|
3322
|
+
console.error("Error getting analytics:", error);
|
|
3323
|
+
return {
|
|
3324
|
+
total_queries: 0,
|
|
3325
|
+
ai_queries: 0,
|
|
3326
|
+
keyword_queries: 0,
|
|
3327
|
+
popular_queries: [],
|
|
3328
|
+
average_query_time: 0
|
|
3329
|
+
};
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
/**
|
|
3333
|
+
* Verify Custom RAG is available
|
|
3334
|
+
*/
|
|
3335
|
+
verifyBinding() {
|
|
3336
|
+
return this.customRAG?.isAvailable() ?? false;
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Get Custom RAG service instance (for indexer)
|
|
3340
|
+
*/
|
|
3341
|
+
getCustomRAG() {
|
|
3342
|
+
return this.customRAG;
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
|
|
3346
|
+
// src/plugins/core-plugins/ai-search-plugin/services/indexer.ts
|
|
3347
|
+
var IndexManager = class {
|
|
3348
|
+
constructor(db, ai, vectorize) {
|
|
3349
|
+
this.db = db;
|
|
3350
|
+
this.ai = ai;
|
|
3351
|
+
this.vectorize = vectorize;
|
|
3352
|
+
if (this.ai && this.vectorize) {
|
|
3353
|
+
this.customRAG = new CustomRAGService(db, ai, vectorize);
|
|
3354
|
+
console.log("[IndexManager] Custom RAG initialized");
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
customRAG;
|
|
3358
|
+
/**
|
|
3359
|
+
* Index all content items within a collection using Custom RAG
|
|
3360
|
+
*/
|
|
3361
|
+
async indexCollection(collectionId) {
|
|
3362
|
+
try {
|
|
3363
|
+
const collectionStmt = this.db.prepare(
|
|
3364
|
+
"SELECT id, name, display_name FROM collections WHERE id = ?"
|
|
3365
|
+
);
|
|
3366
|
+
const collection = await collectionStmt.bind(collectionId).first();
|
|
3367
|
+
if (!collection) {
|
|
3368
|
+
throw new Error(`Collection ${collectionId} not found`);
|
|
3369
|
+
}
|
|
3370
|
+
await this.updateIndexStatus(collectionId, {
|
|
3371
|
+
collection_id: collectionId,
|
|
3372
|
+
collection_name: collection.display_name,
|
|
3373
|
+
total_items: 0,
|
|
3374
|
+
indexed_items: 0,
|
|
3375
|
+
status: "indexing"
|
|
3376
|
+
});
|
|
3377
|
+
if (this.customRAG?.isAvailable()) {
|
|
3378
|
+
console.log(`[IndexManager] Using Custom RAG to index collection ${collectionId}`);
|
|
3379
|
+
const result = await this.customRAG.indexCollection(collectionId);
|
|
3380
|
+
const finalStatus = {
|
|
3381
|
+
collection_id: collectionId,
|
|
3382
|
+
collection_name: collection.display_name,
|
|
3383
|
+
total_items: result.total_items,
|
|
3384
|
+
indexed_items: result.indexed_chunks,
|
|
3385
|
+
last_sync_at: Date.now(),
|
|
3386
|
+
status: result.errors > 0 ? "error" : "completed",
|
|
3387
|
+
error_message: result.errors > 0 ? `${result.errors} errors during indexing` : void 0
|
|
3388
|
+
};
|
|
3389
|
+
await this.updateIndexStatus(collectionId, finalStatus);
|
|
3390
|
+
return finalStatus;
|
|
3391
|
+
}
|
|
3392
|
+
console.warn(`[IndexManager] Custom RAG not available, skipping indexing for ${collectionId}`);
|
|
3393
|
+
const fallbackStatus = {
|
|
3394
|
+
collection_id: collectionId,
|
|
3395
|
+
collection_name: collection.display_name,
|
|
3396
|
+
total_items: 0,
|
|
3397
|
+
indexed_items: 0,
|
|
3398
|
+
last_sync_at: Date.now(),
|
|
3399
|
+
status: "completed",
|
|
3400
|
+
error_message: "Custom RAG not available - using keyword search only"
|
|
3401
|
+
};
|
|
3402
|
+
await this.updateIndexStatus(collectionId, fallbackStatus);
|
|
3403
|
+
return fallbackStatus;
|
|
3404
|
+
} catch (error) {
|
|
3405
|
+
console.error(`[IndexManager] Error indexing collection ${collectionId}:`, error);
|
|
3406
|
+
const errorStatus = {
|
|
3407
|
+
collection_id: collectionId,
|
|
3408
|
+
collection_name: "Unknown",
|
|
3409
|
+
total_items: 0,
|
|
3410
|
+
indexed_items: 0,
|
|
3411
|
+
status: "error",
|
|
3412
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
3413
|
+
};
|
|
3414
|
+
await this.updateIndexStatus(collectionId, errorStatus);
|
|
3415
|
+
return errorStatus;
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
/**
|
|
3419
|
+
* Index a single content item
|
|
3420
|
+
*/
|
|
3421
|
+
async indexContentItem(item, collectionId) {
|
|
3422
|
+
try {
|
|
3423
|
+
let parsedData = {};
|
|
3424
|
+
try {
|
|
3425
|
+
parsedData = typeof item.data === "string" ? JSON.parse(item.data) : item.data;
|
|
3426
|
+
} catch {
|
|
3427
|
+
parsedData = {};
|
|
3428
|
+
}
|
|
3429
|
+
const document = {
|
|
3430
|
+
id: `content_${item.id}`,
|
|
3431
|
+
title: item.title || "Untitled",
|
|
3432
|
+
slug: item.slug || "",
|
|
3433
|
+
content: this.extractSearchableText(parsedData),
|
|
3434
|
+
metadata: {
|
|
3435
|
+
collection_id: collectionId,
|
|
3436
|
+
collection_name: item.collection_name,
|
|
3437
|
+
collection_display_name: item.collection_display_name,
|
|
3438
|
+
status: item.status,
|
|
3439
|
+
created_at: item.created_at,
|
|
3440
|
+
updated_at: item.updated_at,
|
|
3441
|
+
author_id: item.author_id
|
|
3442
|
+
}
|
|
3443
|
+
};
|
|
3444
|
+
console.log(`Indexed content item: ${item.id}`);
|
|
3445
|
+
} catch (error) {
|
|
3446
|
+
console.error(`Error indexing content item ${item.id}:`, error);
|
|
3447
|
+
throw error;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Extract searchable text from content data
|
|
3452
|
+
*/
|
|
3453
|
+
extractSearchableText(data) {
|
|
3454
|
+
const parts = [];
|
|
3455
|
+
if (data.title) parts.push(String(data.title));
|
|
3456
|
+
if (data.name) parts.push(String(data.name));
|
|
3457
|
+
if (data.description) parts.push(String(data.description));
|
|
3458
|
+
if (data.content) parts.push(String(data.content));
|
|
3459
|
+
if (data.body) parts.push(String(data.body));
|
|
3460
|
+
if (data.text) parts.push(String(data.text));
|
|
3461
|
+
const extractStrings = (obj) => {
|
|
3462
|
+
if (typeof obj === "string") {
|
|
3463
|
+
parts.push(obj);
|
|
3464
|
+
} else if (Array.isArray(obj)) {
|
|
3465
|
+
obj.forEach(extractStrings);
|
|
3466
|
+
} else if (obj && typeof obj === "object") {
|
|
3467
|
+
Object.values(obj).forEach(extractStrings);
|
|
3468
|
+
}
|
|
3469
|
+
};
|
|
3470
|
+
extractStrings(data);
|
|
3471
|
+
return parts.join(" ");
|
|
3472
|
+
}
|
|
3473
|
+
/**
|
|
3474
|
+
* Update a single content item in the index
|
|
3475
|
+
*/
|
|
3476
|
+
async updateIndex(collectionId, contentId) {
|
|
3477
|
+
try {
|
|
3478
|
+
const stmt = this.db.prepare(`
|
|
3479
|
+
SELECT
|
|
3480
|
+
c.id, c.title, c.slug, c.data, c.status,
|
|
3481
|
+
c.created_at, c.updated_at, c.author_id,
|
|
3482
|
+
col.name as collection_name, col.display_name as collection_display_name
|
|
3483
|
+
FROM content c
|
|
3484
|
+
JOIN collections col ON c.collection_id = col.id
|
|
3485
|
+
WHERE c.id = ? AND c.collection_id = ?
|
|
3486
|
+
`);
|
|
3487
|
+
const item = await stmt.bind(contentId, collectionId).first();
|
|
3488
|
+
if (!item) {
|
|
3489
|
+
throw new Error(`Content item ${contentId} not found`);
|
|
3490
|
+
}
|
|
3491
|
+
await this.indexContentItem(item, String(collectionId));
|
|
3492
|
+
const status = await this.getIndexStatus(String(collectionId));
|
|
3493
|
+
if (status) {
|
|
3494
|
+
await this.updateIndexStatus(String(collectionId), {
|
|
3495
|
+
...status,
|
|
3496
|
+
last_sync_at: Date.now()
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
3499
|
+
} catch (error) {
|
|
3500
|
+
console.error(`Error updating index for content ${contentId}:`, error);
|
|
3501
|
+
throw error;
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
/**
|
|
3505
|
+
* Remove a content item from the index using Custom RAG
|
|
3506
|
+
*/
|
|
3507
|
+
async removeFromIndex(collectionId, contentId) {
|
|
3508
|
+
try {
|
|
3509
|
+
if (this.customRAG?.isAvailable()) {
|
|
3510
|
+
console.log(`[IndexManager] Removing content ${contentId} from index`);
|
|
3511
|
+
await this.customRAG.removeContentFromIndex(contentId);
|
|
3512
|
+
} else {
|
|
3513
|
+
console.warn(`[IndexManager] Custom RAG not available, skipping removal for ${contentId}`);
|
|
3514
|
+
}
|
|
3515
|
+
} catch (error) {
|
|
3516
|
+
console.error(`[IndexManager] Error removing content ${contentId} from index:`, error);
|
|
3517
|
+
throw error;
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
/**
|
|
3521
|
+
* Get indexing status for a collection
|
|
3522
|
+
*/
|
|
3523
|
+
async getIndexStatus(collectionId) {
|
|
3524
|
+
try {
|
|
3525
|
+
const stmt = this.db.prepare(
|
|
3526
|
+
"SELECT * FROM ai_search_index_meta WHERE collection_id = ?"
|
|
3527
|
+
);
|
|
3528
|
+
const result = await stmt.bind(collectionId).first();
|
|
3529
|
+
if (!result) {
|
|
3530
|
+
return null;
|
|
3531
|
+
}
|
|
3532
|
+
return {
|
|
3533
|
+
collection_id: String(result.collection_id),
|
|
3534
|
+
collection_name: result.collection_name,
|
|
3535
|
+
total_items: result.total_items,
|
|
3536
|
+
indexed_items: result.indexed_items,
|
|
3537
|
+
last_sync_at: result.last_sync_at,
|
|
3538
|
+
status: result.status,
|
|
3539
|
+
error_message: result.error_message
|
|
3540
|
+
};
|
|
3541
|
+
} catch (error) {
|
|
3542
|
+
console.error(`Error getting index status for collection ${collectionId}:`, error);
|
|
3543
|
+
return null;
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
/**
|
|
3547
|
+
* Get indexing status for all collections
|
|
3548
|
+
*/
|
|
3549
|
+
async getAllIndexStatus() {
|
|
3550
|
+
try {
|
|
3551
|
+
const stmt = this.db.prepare("SELECT * FROM ai_search_index_meta");
|
|
3552
|
+
const { results } = await stmt.all();
|
|
3553
|
+
const statusMap = {};
|
|
3554
|
+
for (const row of results || []) {
|
|
3555
|
+
const collectionId = String(row.collection_id);
|
|
3556
|
+
statusMap[collectionId] = {
|
|
3557
|
+
collection_id: collectionId,
|
|
3558
|
+
collection_name: row.collection_name,
|
|
3559
|
+
total_items: row.total_items,
|
|
3560
|
+
indexed_items: row.indexed_items,
|
|
3561
|
+
last_sync_at: row.last_sync_at,
|
|
3562
|
+
status: row.status,
|
|
3563
|
+
error_message: row.error_message
|
|
3564
|
+
};
|
|
3565
|
+
}
|
|
3566
|
+
return statusMap;
|
|
3567
|
+
} catch (error) {
|
|
3568
|
+
console.error("Error getting all index status:", error);
|
|
3569
|
+
return {};
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
/**
|
|
3573
|
+
* Update index status in database
|
|
3574
|
+
*/
|
|
3575
|
+
async updateIndexStatus(collectionId, status) {
|
|
3576
|
+
try {
|
|
3577
|
+
const checkStmt = this.db.prepare(
|
|
3578
|
+
"SELECT id FROM ai_search_index_meta WHERE collection_id = ?"
|
|
3579
|
+
);
|
|
3580
|
+
const existing = await checkStmt.bind(collectionId).first();
|
|
3581
|
+
if (existing) {
|
|
3582
|
+
const stmt = this.db.prepare(`
|
|
3583
|
+
UPDATE ai_search_index_meta
|
|
3584
|
+
SET collection_name = ?,
|
|
3585
|
+
total_items = ?,
|
|
3586
|
+
indexed_items = ?,
|
|
3587
|
+
last_sync_at = ?,
|
|
3588
|
+
status = ?,
|
|
3589
|
+
error_message = ?
|
|
3590
|
+
WHERE collection_id = ?
|
|
3591
|
+
`);
|
|
3592
|
+
await stmt.bind(
|
|
3593
|
+
status.collection_name,
|
|
3594
|
+
status.total_items,
|
|
3595
|
+
status.indexed_items,
|
|
3596
|
+
status.last_sync_at || null,
|
|
3597
|
+
status.status,
|
|
3598
|
+
status.error_message || null,
|
|
3599
|
+
String(collectionId)
|
|
3600
|
+
).run();
|
|
3601
|
+
} else {
|
|
3602
|
+
const stmt = this.db.prepare(`
|
|
3603
|
+
INSERT INTO ai_search_index_meta (
|
|
3604
|
+
collection_id, collection_name, total_items, indexed_items,
|
|
3605
|
+
last_sync_at, status, error_message
|
|
3606
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
3607
|
+
`);
|
|
3608
|
+
await stmt.bind(
|
|
3609
|
+
String(status.collection_id),
|
|
3610
|
+
status.collection_name,
|
|
3611
|
+
status.total_items,
|
|
3612
|
+
status.indexed_items,
|
|
3613
|
+
status.last_sync_at || null,
|
|
3614
|
+
status.status,
|
|
3615
|
+
status.error_message || null
|
|
3616
|
+
).run();
|
|
3617
|
+
}
|
|
3618
|
+
} catch (error) {
|
|
3619
|
+
console.error(`Error updating index status for collection ${collectionId}:`, error);
|
|
3620
|
+
throw error;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
/**
|
|
3624
|
+
* Sync all selected collections
|
|
3625
|
+
*/
|
|
3626
|
+
async syncAll(selectedCollections) {
|
|
3627
|
+
for (const collectionId of selectedCollections) {
|
|
3628
|
+
try {
|
|
3629
|
+
await this.indexCollection(collectionId);
|
|
3630
|
+
} catch (error) {
|
|
3631
|
+
console.error(`Error syncing collection ${collectionId}:`, error);
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
};
|
|
3636
|
+
|
|
3637
|
+
// src/plugins/core-plugins/ai-search-plugin/components/settings-page.ts
|
|
3638
|
+
function renderSettingsPage(data) {
|
|
3639
|
+
const settings = data.settings || {
|
|
3640
|
+
enabled: false,
|
|
3641
|
+
ai_mode_enabled: true,
|
|
3642
|
+
selected_collections: [],
|
|
3643
|
+
dismissed_collections: [],
|
|
3644
|
+
autocomplete_enabled: true,
|
|
3645
|
+
cache_duration: 1,
|
|
3646
|
+
results_limit: 20,
|
|
3647
|
+
index_media: false
|
|
3648
|
+
};
|
|
3649
|
+
const selectedCollections = Array.isArray(settings.selected_collections) ? settings.selected_collections : [];
|
|
3650
|
+
const dismissedCollections = Array.isArray(settings.dismissed_collections) ? settings.dismissed_collections : [];
|
|
3651
|
+
const enabled = settings.enabled === true;
|
|
3652
|
+
const aiModeEnabled = settings.ai_mode_enabled !== false;
|
|
3653
|
+
const autocompleteEnabled = settings.autocomplete_enabled !== false;
|
|
3654
|
+
const indexMedia = settings.index_media === true;
|
|
3655
|
+
const selectedCollectionIds = new Set(selectedCollections.map((id) => String(id)));
|
|
3656
|
+
const dismissedCollectionIds = new Set(dismissedCollections.map((id) => String(id)));
|
|
3657
|
+
const collections2 = Array.isArray(data.collections) ? data.collections : [];
|
|
3658
|
+
console.log("[SettingsPage Template] Collections received:", collections2.length);
|
|
3659
|
+
if (collections2.length > 0) {
|
|
3660
|
+
console.log("[SettingsPage Template] First collection:", collections2[0]);
|
|
3661
|
+
}
|
|
3662
|
+
const content2 = `
|
|
3663
|
+
<div class="w-full px-4 sm:px-6 lg:px-8 py-6">
|
|
3664
|
+
<!-- Header with Back Button -->
|
|
3665
|
+
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-6">
|
|
3666
|
+
<div>
|
|
3667
|
+
<h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">\u{1F50D} AI Search Settings</h1>
|
|
3668
|
+
<p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">
|
|
3669
|
+
Configure advanced search with Cloudflare AI Search. Select collections to index and manage search preferences.
|
|
3670
|
+
</p>
|
|
3671
|
+
</div>
|
|
3672
|
+
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
|
|
3673
|
+
<a href="/admin/plugins" 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 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors shadow-sm">
|
|
3674
|
+
<svg class="-ml-0.5 mr-1.5 h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3675
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
|
|
3676
|
+
</svg>
|
|
3677
|
+
Back to Plugins
|
|
3678
|
+
</a>
|
|
3679
|
+
</div>
|
|
3680
|
+
</div>
|
|
3681
|
+
|
|
3682
|
+
|
|
3683
|
+
<!-- Main Settings Card -->
|
|
3684
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6 mb-6">
|
|
3685
|
+
<form id="settingsForm" class="space-y-6">
|
|
3686
|
+
<!-- Enable Search Section -->
|
|
3687
|
+
<div>
|
|
3688
|
+
<h2 class="text-xl font-semibold text-zinc-950 dark:text-white mb-4">\u{1F50D} Search Settings</h2>
|
|
3689
|
+
<div class="space-y-3">
|
|
3690
|
+
<div class="flex items-center gap-3 p-4 border border-indigo-200 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg">
|
|
3691
|
+
<input type="checkbox" id="enabled" name="enabled" ${enabled ? "checked" : ""} class="w-5 h-5 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer">
|
|
3692
|
+
<div class="flex-1">
|
|
3693
|
+
<label for="enabled" class="text-base font-semibold text-zinc-900 dark:text-white select-none cursor-pointer block">Enable AI Search</label>
|
|
3694
|
+
<p class="text-xs text-zinc-600 dark:text-zinc-400 mt-0.5">Turn on advanced search capabilities across your content</p>
|
|
3695
|
+
</div>
|
|
3696
|
+
</div>
|
|
3697
|
+
|
|
3698
|
+
<div class="flex items-center gap-3 p-4 border border-blue-200 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
|
3699
|
+
<input type="checkbox" id="ai_mode_enabled" name="ai_mode_enabled" ${aiModeEnabled ? "checked" : ""} class="w-5 h-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500 cursor-pointer">
|
|
3700
|
+
<div class="flex-1">
|
|
3701
|
+
<label for="ai_mode_enabled" class="text-base font-semibold text-zinc-900 dark:text-white select-none cursor-pointer block">\u{1F916} AI/Semantic Search</label>
|
|
3702
|
+
<p class="text-xs text-zinc-600 dark:text-zinc-400 mt-0.5">
|
|
3703
|
+
Enable natural language queries (requires Cloudflare Workers AI binding)
|
|
3704
|
+
<a href="https://developers.cloudflare.com/workers-ai/" target="_blank" class="text-blue-600 dark:text-blue-400 hover:underline ml-1">\u2192 Setup Guide</a>
|
|
3705
|
+
</p>
|
|
3706
|
+
<p class="text-xs text-amber-600 dark:text-amber-400 mt-1">
|
|
3707
|
+
\u26A0\uFE0F If AI binding unavailable, will fallback to keyword search
|
|
3708
|
+
</p>
|
|
3709
|
+
</div>
|
|
3710
|
+
</div>
|
|
3711
|
+
</div>
|
|
3712
|
+
</div>
|
|
3713
|
+
|
|
3714
|
+
<hr class="border-zinc-200 dark:border-zinc-800">
|
|
3715
|
+
|
|
3716
|
+
<!-- Collections Section -->
|
|
3717
|
+
<div>
|
|
3718
|
+
<div class="flex items-start justify-between mb-4">
|
|
3719
|
+
<div>
|
|
3720
|
+
<h2 class="text-xl font-semibold text-zinc-950 dark:text-white">\u{1F4DA} Collections to Index</h2>
|
|
3721
|
+
<p class="text-sm text-zinc-600 dark:text-zinc-400 mt-1">
|
|
3722
|
+
Select which content collections should be indexed and searchable. Only checked collections will be included in search results.
|
|
3723
|
+
</p>
|
|
3724
|
+
</div>
|
|
3725
|
+
</div>
|
|
3726
|
+
<div class="space-y-3 max-h-96 overflow-y-auto border-2 border-zinc-300 dark:border-zinc-700 rounded-lg p-4 bg-white dark:bg-zinc-800" id="collections-list">
|
|
3727
|
+
${collections2.length === 0 ? '<p class="text-sm text-zinc-500 dark:text-zinc-400 p-4">No collections available. Create collections first.</p>' : collections2.map((collection) => {
|
|
3728
|
+
const collectionId = String(collection.id);
|
|
3729
|
+
const isChecked = selectedCollectionIds.has(collectionId);
|
|
3730
|
+
const isDismissed = dismissedCollectionIds.has(collectionId);
|
|
3731
|
+
const indexStatusMap = data.indexStatus || {};
|
|
3732
|
+
const status = indexStatusMap[collectionId];
|
|
3733
|
+
const isNew = collection.is_new === true && !isDismissed && !status;
|
|
3734
|
+
const statusBadge = status && isChecked ? `<span class="ml-2 px-2 py-1 text-xs rounded-full ${status.status === "completed" ? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300" : status.status === "indexing" ? "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300" : status.status === "error" ? "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300" : "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300"}">${status.status}</span>` : "";
|
|
3735
|
+
return `<div class="flex items-start gap-3 p-3 rounded-lg border border-zinc-200 dark:border-zinc-700 ${isNew ? "bg-blue-50 dark:bg-blue-900/10 border-blue-200 dark:border-blue-800" : "hover:bg-zinc-50 dark:hover:bg-zinc-800"}">
|
|
3736
|
+
<input
|
|
3737
|
+
type="checkbox"
|
|
3738
|
+
id="collection_${collectionId}"
|
|
3739
|
+
name="selected_collections"
|
|
3740
|
+
value="${collectionId}"
|
|
3741
|
+
${isChecked ? "checked" : ""}
|
|
3742
|
+
class="mt-1 w-5 h-5 text-indigo-600 bg-white border-gray-300 rounded focus:ring-indigo-500 focus:ring-2 cursor-pointer"
|
|
3743
|
+
style="cursor: pointer; flex-shrink: 0;"
|
|
3744
|
+
/>
|
|
3745
|
+
<div class="flex-1 min-w-0">
|
|
3746
|
+
<label for="collection_${collectionId}" class="text-sm font-medium text-zinc-950 dark:text-white select-none cursor-pointer flex items-center">
|
|
3747
|
+
${collection.display_name || collection.name || "Unnamed Collection"}
|
|
3748
|
+
${isNew ? '<span class="ml-2 px-2 py-0.5 text-xs rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300">NEW</span>' : ""}
|
|
3749
|
+
${statusBadge}
|
|
3750
|
+
</label>
|
|
3751
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
3752
|
+
${collection.description || collection.name || "No description"} \u2022 ${collection.item_count || 0} items
|
|
3753
|
+
${status ? ` \u2022 ${status.indexed_items}/${status.total_items} indexed` : ""}
|
|
3754
|
+
</p>
|
|
3755
|
+
${status && status.status === "indexing" ? `<div class="mt-2 w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700">
|
|
3756
|
+
<div class="bg-blue-600 h-2 rounded-full" style="width: ${status.indexed_items / status.total_items * 100}%"></div>
|
|
3757
|
+
</div>` : ""}
|
|
3758
|
+
</div>
|
|
3759
|
+
${isChecked ? `
|
|
3760
|
+
<button
|
|
3761
|
+
type="button"
|
|
3762
|
+
onclick="reindexCollection('${collectionId}')"
|
|
3763
|
+
class="px-3 py-1.5 text-xs font-medium text-white bg-indigo-600 hover:bg-indigo-700 rounded-md transition-colors flex items-center gap-1.5 whitespace-nowrap"
|
|
3764
|
+
${status && status.status === "indexing" ? "disabled" : ""}
|
|
3765
|
+
>
|
|
3766
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3767
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
3768
|
+
</svg>
|
|
3769
|
+
Re-index
|
|
3770
|
+
</button>
|
|
3771
|
+
` : ""}
|
|
3772
|
+
</div>`;
|
|
3773
|
+
}).join("")}
|
|
3774
|
+
</div>
|
|
3775
|
+
</div>
|
|
3776
|
+
|
|
3777
|
+
<hr class="border-zinc-200 dark:border-zinc-800">
|
|
3778
|
+
|
|
3779
|
+
<!-- Advanced Options -->
|
|
3780
|
+
<div>
|
|
3781
|
+
<h2 class="text-xl font-semibold text-zinc-950 dark:text-white mb-4">\u2699\uFE0F Advanced Options</h2>
|
|
3782
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
3783
|
+
<div class="flex items-start gap-3 p-3 border border-zinc-200 dark:border-zinc-700 rounded-lg">
|
|
3784
|
+
<input type="checkbox" id="autocomplete_enabled" name="autocomplete_enabled" ${autocompleteEnabled ? "checked" : ""} class="mt-0.5 w-5 h-5 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer">
|
|
3785
|
+
<div>
|
|
3786
|
+
<label for="autocomplete_enabled" class="text-sm font-medium text-zinc-950 dark:text-white select-none cursor-pointer block">Autocomplete Suggestions</label>
|
|
3787
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5">Show search suggestions as users type</p>
|
|
3788
|
+
</div>
|
|
3789
|
+
</div>
|
|
3790
|
+
|
|
3791
|
+
<div class="flex items-start gap-3 p-3 border border-zinc-200 dark:border-zinc-700 rounded-lg">
|
|
3792
|
+
<input type="checkbox" id="index_media" name="index_media" ${indexMedia ? "checked" : ""} class="mt-0.5 w-5 h-5 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer">
|
|
3793
|
+
<div>
|
|
3794
|
+
<label for="index_media" class="text-sm font-medium text-zinc-950 dark:text-white select-none cursor-pointer block">Index Media Metadata</label>
|
|
3795
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5">Include media files in search results</p>
|
|
3796
|
+
</div>
|
|
3797
|
+
</div>
|
|
3798
|
+
|
|
3799
|
+
<div>
|
|
3800
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Cache Duration (hours)</label>
|
|
3801
|
+
<input type="number" id="cache_duration" name="cache_duration" value="${settings.cache_duration || 1}" min="0" max="24" class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 focus:ring-2 focus:ring-indigo-500">
|
|
3802
|
+
</div>
|
|
3803
|
+
<div>
|
|
3804
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Results Per Page</label>
|
|
3805
|
+
<input type="number" id="results_limit" name="results_limit" value="${settings.results_limit || 20}" min="10" max="100" class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 focus:ring-2 focus:ring-indigo-500">
|
|
3806
|
+
</div>
|
|
3807
|
+
</div>
|
|
3808
|
+
</div>
|
|
3809
|
+
|
|
3810
|
+
<!-- Save Button -->
|
|
3811
|
+
<div class="flex items-center justify-between pt-4 border-t border-zinc-200 dark:border-zinc-800">
|
|
3812
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400">
|
|
3813
|
+
\u{1F4A1} Collections marked as <span class="px-1.5 py-0.5 text-xs font-medium rounded-full bg-blue-500 text-white">NEW</span> haven't been indexed yet
|
|
3814
|
+
</p>
|
|
3815
|
+
<button type="submit" class="inline-flex items-center justify-center rounded-lg bg-indigo-600 text-white px-6 py-2.5 text-sm font-semibold hover:bg-indigo-500 shadow-sm transition-colors">
|
|
3816
|
+
\u{1F4BE} Save Settings
|
|
3817
|
+
</button>
|
|
3818
|
+
</div>
|
|
3819
|
+
</form>
|
|
3820
|
+
</div>
|
|
3821
|
+
|
|
3822
|
+
|
|
3823
|
+
<!-- Search Analytics -->
|
|
3824
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 p-6">
|
|
3825
|
+
<h2 class="text-xl font-semibold text-zinc-950 dark:text-white mb-4">\u{1F4CA} Search Analytics</h2>
|
|
3826
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
3827
|
+
<div class="p-4 rounded-lg bg-zinc-50 dark:bg-zinc-800">
|
|
3828
|
+
<div class="text-sm text-zinc-500 dark:text-zinc-400">Total Queries</div>
|
|
3829
|
+
<div class="text-2xl font-bold text-zinc-950 dark:text-white mt-1">${data.analytics.total_queries}</div>
|
|
3830
|
+
</div>
|
|
3831
|
+
<div class="p-4 rounded-lg bg-zinc-50 dark:bg-zinc-800">
|
|
3832
|
+
<div class="text-sm text-zinc-500 dark:text-zinc-400">AI Queries</div>
|
|
3833
|
+
<div class="text-2xl font-bold text-blue-600 dark:text-blue-400 mt-1">${data.analytics.ai_queries}</div>
|
|
3834
|
+
</div>
|
|
3835
|
+
<div class="p-4 rounded-lg bg-zinc-50 dark:bg-zinc-800">
|
|
3836
|
+
<div class="text-sm text-zinc-500 dark:text-zinc-400">Keyword Queries</div>
|
|
3837
|
+
<div class="text-2xl font-bold text-indigo-600 dark:text-indigo-400 mt-1">${data.analytics.keyword_queries}</div>
|
|
3838
|
+
</div>
|
|
3839
|
+
</div>
|
|
3840
|
+
${data.analytics.popular_queries.length > 0 ? `
|
|
3841
|
+
<div>
|
|
3842
|
+
<h3 class="text-sm font-semibold text-zinc-950 dark:text-white mb-2">Popular Searches</h3>
|
|
3843
|
+
<div class="space-y-1">
|
|
3844
|
+
${data.analytics.popular_queries.map(
|
|
3845
|
+
(item) => `
|
|
3846
|
+
<div class="flex items-center justify-between text-sm">
|
|
3847
|
+
<span class="text-zinc-700 dark:text-zinc-300">"${item.query}"</span>
|
|
3848
|
+
<span class="text-zinc-500 dark:text-zinc-400">${item.count} times</span>
|
|
3849
|
+
</div>
|
|
3850
|
+
`
|
|
3851
|
+
).join("")}
|
|
3852
|
+
</div>
|
|
3853
|
+
</div>
|
|
3854
|
+
` : '<p class="text-sm text-zinc-500 dark:text-zinc-400">No search history yet.</p>'}
|
|
3855
|
+
</div>
|
|
3856
|
+
|
|
3857
|
+
<!-- Success Message -->
|
|
3858
|
+
<div id="msg" class="hidden fixed bottom-4 right-4 p-4 rounded-lg bg-green-50 text-green-900 border border-green-200 dark:bg-green-900/20 dark:text-green-100 dark:border-green-800 shadow-lg z-50">
|
|
3859
|
+
<div class="flex items-center gap-2">
|
|
3860
|
+
<span class="text-xl">\u2705</span>
|
|
3861
|
+
<span class="font-semibold">Settings Saved Successfully!</span>
|
|
3862
|
+
</div>
|
|
3863
|
+
</div>
|
|
3864
|
+
</div>
|
|
3865
|
+
<script>
|
|
3866
|
+
// Form submission with error handling
|
|
3867
|
+
document.getElementById('settingsForm').addEventListener('submit', async (e) => {
|
|
3868
|
+
e.preventDefault();
|
|
3869
|
+
console.log('[AI Search Client] Form submitted');
|
|
3870
|
+
|
|
3871
|
+
try {
|
|
3872
|
+
const btn = e.submitter;
|
|
3873
|
+
btn.innerText = 'Saving...';
|
|
3874
|
+
btn.disabled = true;
|
|
3875
|
+
|
|
3876
|
+
const formData = new FormData(e.target);
|
|
3877
|
+
const selectedCollections = Array.from(formData.getAll('selected_collections')).map(String);
|
|
3878
|
+
|
|
3879
|
+
const data = {
|
|
3880
|
+
enabled: document.getElementById('enabled').checked,
|
|
3881
|
+
ai_mode_enabled: document.getElementById('ai_mode_enabled').checked,
|
|
3882
|
+
selected_collections: selectedCollections,
|
|
3883
|
+
autocomplete_enabled: document.getElementById('autocomplete_enabled').checked,
|
|
3884
|
+
cache_duration: Number(formData.get('cache_duration')),
|
|
3885
|
+
results_limit: Number(formData.get('results_limit')),
|
|
3886
|
+
index_media: document.getElementById('index_media').checked,
|
|
3887
|
+
};
|
|
3888
|
+
|
|
3889
|
+
console.log('[AI Search Client] Sending data:', data);
|
|
3890
|
+
console.log('[AI Search Client] Selected collections:', selectedCollections);
|
|
3891
|
+
|
|
3892
|
+
const res = await fetch('/admin/plugins/ai-search', {
|
|
3893
|
+
method: 'POST',
|
|
3894
|
+
headers: {'Content-Type': 'application/json'},
|
|
3895
|
+
body: JSON.stringify(data)
|
|
3896
|
+
});
|
|
3897
|
+
|
|
3898
|
+
console.log('[AI Search Client] Response status:', res.status);
|
|
3899
|
+
|
|
3900
|
+
if (res.ok) {
|
|
3901
|
+
const result = await res.json();
|
|
3902
|
+
console.log('[AI Search Client] Save successful:', result);
|
|
3903
|
+
document.getElementById('msg').classList.remove('hidden');
|
|
3904
|
+
setTimeout(() => {
|
|
3905
|
+
document.getElementById('msg').classList.add('hidden');
|
|
3906
|
+
location.reload();
|
|
3907
|
+
}, 2000);
|
|
3908
|
+
} else {
|
|
3909
|
+
const error = await res.text();
|
|
3910
|
+
console.error('[AI Search Client] Save failed:', error);
|
|
3911
|
+
alert('Failed to save settings: ' + error);
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3914
|
+
btn.innerText = 'Save Settings';
|
|
3915
|
+
btn.disabled = false;
|
|
3916
|
+
} catch (error) {
|
|
3917
|
+
console.error('[AI Search Client] Error:', error);
|
|
3918
|
+
alert('Error saving settings: ' + error.message);
|
|
3919
|
+
}
|
|
3920
|
+
});
|
|
3921
|
+
|
|
3922
|
+
// Add collection to index
|
|
3923
|
+
async function addCollectionToIndex(collectionId) {
|
|
3924
|
+
const form = document.getElementById('settingsForm');
|
|
3925
|
+
const checkbox = document.getElementById('collection_' + collectionId);
|
|
3926
|
+
if (checkbox) {
|
|
3927
|
+
checkbox.checked = true;
|
|
3928
|
+
form.dispatchEvent(new Event('submit'));
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
|
|
3932
|
+
// Dismiss collection
|
|
3933
|
+
async function dismissCollection(collectionId) {
|
|
3934
|
+
const res = await fetch('/admin/plugins/ai-search', {
|
|
3935
|
+
method: 'POST',
|
|
3936
|
+
headers: {'Content-Type': 'application/json'},
|
|
3937
|
+
body: JSON.stringify({
|
|
3938
|
+
dismissed_collections: [collectionId]
|
|
3939
|
+
})
|
|
3940
|
+
});
|
|
3941
|
+
if (res.ok) {
|
|
3942
|
+
location.reload();
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
// Re-index collection
|
|
3947
|
+
async function reindexCollection(collectionId) {
|
|
3948
|
+
const res = await fetch('/admin/plugins/ai-search/api/reindex', {
|
|
3949
|
+
method: 'POST',
|
|
3950
|
+
headers: {'Content-Type': 'application/json'},
|
|
3951
|
+
body: JSON.stringify({ collection_id: collectionId })
|
|
3952
|
+
});
|
|
3953
|
+
if (res.ok) {
|
|
3954
|
+
alert('Re-indexing started. Page will refresh in a moment.');
|
|
3955
|
+
setTimeout(() => location.reload(), 2000);
|
|
3956
|
+
} else {
|
|
3957
|
+
alert('Failed to start re-indexing. Please try again.');
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
// Poll for index status updates
|
|
3962
|
+
setInterval(async () => {
|
|
3963
|
+
const res = await fetch('/admin/plugins/ai-search/api/status');
|
|
3964
|
+
if (res.ok) {
|
|
3965
|
+
const { data } = await res.json();
|
|
3966
|
+
// Update status indicators if needed
|
|
3967
|
+
// For now, just reload every 30 seconds if indexing is in progress
|
|
3968
|
+
const hasIndexing = Object.values(data).some((s) => s.status === 'indexing');
|
|
3969
|
+
if (hasIndexing) {
|
|
3970
|
+
location.reload();
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
}, 30000);
|
|
3974
|
+
</script>
|
|
3975
|
+
`;
|
|
3976
|
+
return chunkBZC4FYW7_cjs.renderAdminLayout({
|
|
3977
|
+
title: "AI Search Settings",
|
|
3978
|
+
pageTitle: "AI Search Settings",
|
|
3979
|
+
currentPath: "/admin/plugins/ai-search/settings",
|
|
3980
|
+
user: data.user,
|
|
3981
|
+
content: content2
|
|
3982
|
+
});
|
|
3983
|
+
}
|
|
3984
|
+
|
|
3985
|
+
// src/plugins/core-plugins/ai-search-plugin/routes/admin.ts
|
|
3986
|
+
var adminRoutes = new hono.Hono();
|
|
3987
|
+
adminRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
3988
|
+
adminRoutes.get("/", async (c) => {
|
|
3989
|
+
try {
|
|
3990
|
+
const user = c.get("user");
|
|
3991
|
+
const db = c.env.DB;
|
|
3992
|
+
const ai = c.env.AI;
|
|
3993
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
3994
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
3995
|
+
const indexer = new IndexManager(db, ai, vectorize);
|
|
3996
|
+
const settings = await service.getSettings();
|
|
3997
|
+
console.log("[AI Search Settings Route] Settings loaded:", !!settings);
|
|
3998
|
+
const collections2 = await service.getAllCollections();
|
|
3999
|
+
console.log("[AI Search Settings Route] Collections returned:", collections2.length);
|
|
4000
|
+
if (collections2.length === 0) {
|
|
4001
|
+
const directQuery = await db.prepare("SELECT id, name, display_name FROM collections WHERE is_active = 1").all();
|
|
4002
|
+
console.log("[AI Search Settings Route] Direct DB query found:", directQuery.results?.length || 0, "collections");
|
|
4003
|
+
if (directQuery.results && directQuery.results.length > 0) {
|
|
4004
|
+
console.log("[AI Search Settings Route] Sample from DB:", directQuery.results[0]);
|
|
4005
|
+
}
|
|
4006
|
+
} else if (collections2.length > 0 && collections2[0]) {
|
|
4007
|
+
console.log("[AI Search Settings Route] First collection:", {
|
|
4008
|
+
id: collections2[0].id,
|
|
4009
|
+
name: collections2[0].name,
|
|
4010
|
+
display_name: collections2[0].display_name
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
const newCollections = await service.detectNewCollections();
|
|
4014
|
+
console.log("AI Search: New collections:", newCollections.length);
|
|
4015
|
+
const indexStatus = await indexer.getAllIndexStatus();
|
|
4016
|
+
console.log("AI Search: Index status:", Object.keys(indexStatus).length);
|
|
4017
|
+
const analytics = await service.getSearchAnalytics();
|
|
4018
|
+
return c.html(
|
|
4019
|
+
renderSettingsPage({
|
|
4020
|
+
settings,
|
|
4021
|
+
collections: collections2 || [],
|
|
4022
|
+
newCollections: newCollections || [],
|
|
4023
|
+
indexStatus: indexStatus || {},
|
|
4024
|
+
analytics,
|
|
4025
|
+
user: {
|
|
4026
|
+
name: user.email,
|
|
4027
|
+
email: user.email,
|
|
4028
|
+
role: user.role
|
|
4029
|
+
}
|
|
4030
|
+
})
|
|
4031
|
+
);
|
|
4032
|
+
} catch (error) {
|
|
4033
|
+
console.error("Error rendering AI Search settings:", error);
|
|
4034
|
+
return c.html(`<p>Error loading settings: ${error instanceof Error ? error.message : String(error)}</p>`, 500);
|
|
4035
|
+
}
|
|
4036
|
+
});
|
|
4037
|
+
adminRoutes.post("/", async (c) => {
|
|
4038
|
+
try {
|
|
4039
|
+
const db = c.env.DB;
|
|
4040
|
+
const ai = c.env.AI;
|
|
4041
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4042
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
4043
|
+
const indexer = new IndexManager(db, ai, vectorize);
|
|
4044
|
+
const body = await c.req.json();
|
|
4045
|
+
console.log("[AI Search POST] Received body:", JSON.stringify(body, null, 2));
|
|
4046
|
+
const currentSettings = await service.getSettings();
|
|
4047
|
+
console.log("[AI Search POST] Current settings selected_collections:", currentSettings?.selected_collections);
|
|
4048
|
+
const updatedSettings = {
|
|
4049
|
+
enabled: body.enabled !== void 0 ? Boolean(body.enabled) : currentSettings?.enabled,
|
|
4050
|
+
ai_mode_enabled: body.ai_mode_enabled !== void 0 ? Boolean(body.ai_mode_enabled) : currentSettings?.ai_mode_enabled,
|
|
4051
|
+
selected_collections: Array.isArray(body.selected_collections) ? body.selected_collections.map(String) : currentSettings?.selected_collections || [],
|
|
4052
|
+
dismissed_collections: Array.isArray(body.dismissed_collections) ? body.dismissed_collections.map(String) : currentSettings?.dismissed_collections || [],
|
|
4053
|
+
autocomplete_enabled: body.autocomplete_enabled !== void 0 ? Boolean(body.autocomplete_enabled) : currentSettings?.autocomplete_enabled,
|
|
4054
|
+
cache_duration: body.cache_duration ? Number(body.cache_duration) : currentSettings?.cache_duration,
|
|
4055
|
+
results_limit: body.results_limit ? Number(body.results_limit) : currentSettings?.results_limit,
|
|
4056
|
+
index_media: body.index_media !== void 0 ? Boolean(body.index_media) : currentSettings?.index_media
|
|
4057
|
+
};
|
|
4058
|
+
console.log("[AI Search POST] Updated settings selected_collections:", updatedSettings.selected_collections);
|
|
4059
|
+
const collectionsChanged = JSON.stringify(updatedSettings.selected_collections) !== JSON.stringify(currentSettings?.selected_collections || []);
|
|
4060
|
+
const saved = await service.updateSettings(updatedSettings);
|
|
4061
|
+
console.log("[AI Search POST] Settings saved, selected_collections:", saved.selected_collections);
|
|
4062
|
+
if (collectionsChanged && updatedSettings.selected_collections) {
|
|
4063
|
+
console.log("[AI Search POST] Collections changed, starting background indexing");
|
|
4064
|
+
c.executionCtx.waitUntil(
|
|
4065
|
+
indexer.syncAll(updatedSettings.selected_collections).then(() => console.log("[AI Search POST] Background indexing completed")).catch((error) => console.error("[AI Search POST] Background indexing error:", error))
|
|
4066
|
+
);
|
|
4067
|
+
}
|
|
4068
|
+
return c.json({ success: true, settings: saved });
|
|
4069
|
+
} catch (error) {
|
|
4070
|
+
console.error("Error updating AI Search settings:", error);
|
|
4071
|
+
return c.json({ error: "Failed to update settings" }, 500);
|
|
4072
|
+
}
|
|
4073
|
+
});
|
|
4074
|
+
adminRoutes.get("/api/settings", async (c) => {
|
|
4075
|
+
try {
|
|
4076
|
+
const db = c.env.DB;
|
|
4077
|
+
const ai = c.env.AI;
|
|
4078
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4079
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
4080
|
+
const settings = await service.getSettings();
|
|
4081
|
+
return c.json({ success: true, data: settings });
|
|
4082
|
+
} catch (error) {
|
|
4083
|
+
console.error("Error fetching settings:", error);
|
|
4084
|
+
return c.json({ error: "Failed to fetch settings" }, 500);
|
|
4085
|
+
}
|
|
4086
|
+
});
|
|
4087
|
+
adminRoutes.get("/api/new-collections", async (c) => {
|
|
4088
|
+
try {
|
|
4089
|
+
const db = c.env.DB;
|
|
4090
|
+
const ai = c.env.AI;
|
|
4091
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4092
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
4093
|
+
const notifications = await service.detectNewCollections();
|
|
4094
|
+
return c.json({ success: true, data: notifications });
|
|
4095
|
+
} catch (error) {
|
|
4096
|
+
console.error("Error detecting new collections:", error);
|
|
4097
|
+
return c.json({ error: "Failed to detect new collections" }, 500);
|
|
4098
|
+
}
|
|
4099
|
+
});
|
|
4100
|
+
adminRoutes.get("/api/status", async (c) => {
|
|
4101
|
+
try {
|
|
4102
|
+
const db = c.env.DB;
|
|
4103
|
+
const ai = c.env.AI;
|
|
4104
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4105
|
+
const indexer = new IndexManager(db, ai, vectorize);
|
|
4106
|
+
const status = await indexer.getAllIndexStatus();
|
|
4107
|
+
return c.json({ success: true, data: status });
|
|
4108
|
+
} catch (error) {
|
|
4109
|
+
console.error("Error fetching index status:", error);
|
|
4110
|
+
return c.json({ error: "Failed to fetch status" }, 500);
|
|
4111
|
+
}
|
|
4112
|
+
});
|
|
4113
|
+
adminRoutes.post("/api/reindex", async (c) => {
|
|
4114
|
+
try {
|
|
4115
|
+
const db = c.env.DB;
|
|
4116
|
+
const ai = c.env.AI;
|
|
4117
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4118
|
+
const indexer = new IndexManager(db, ai, vectorize);
|
|
4119
|
+
const body = await c.req.json();
|
|
4120
|
+
const collectionIdRaw = body.collection_id;
|
|
4121
|
+
const collectionId = collectionIdRaw ? String(collectionIdRaw) : "";
|
|
4122
|
+
if (!collectionId || collectionId === "undefined" || collectionId === "null") {
|
|
4123
|
+
return c.json({ error: "collection_id is required" }, 400);
|
|
4124
|
+
}
|
|
4125
|
+
c.executionCtx.waitUntil(
|
|
4126
|
+
indexer.indexCollection(collectionId).then(() => console.log(`[AI Search Reindex] Completed for collection ${collectionId}`)).catch((error) => console.error(`[AI Search Reindex] Error for collection ${collectionId}:`, error))
|
|
4127
|
+
);
|
|
4128
|
+
return c.json({ success: true, message: "Re-indexing started" });
|
|
4129
|
+
} catch (error) {
|
|
4130
|
+
console.error("Error starting re-index:", error);
|
|
4131
|
+
return c.json({ error: "Failed to start re-indexing" }, 500);
|
|
4132
|
+
}
|
|
4133
|
+
});
|
|
4134
|
+
var admin_default = adminRoutes;
|
|
4135
|
+
var apiRoutes = new hono.Hono();
|
|
4136
|
+
apiRoutes.post("/", async (c) => {
|
|
4137
|
+
try {
|
|
4138
|
+
const db = c.env.DB;
|
|
4139
|
+
const ai = c.env.AI;
|
|
4140
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4141
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
4142
|
+
const body = await c.req.json();
|
|
4143
|
+
const query = {
|
|
4144
|
+
query: body.query || "",
|
|
4145
|
+
mode: body.mode || "keyword",
|
|
4146
|
+
filters: body.filters || {},
|
|
4147
|
+
limit: body.limit ? Number(body.limit) : void 0,
|
|
4148
|
+
offset: body.offset ? Number(body.offset) : void 0
|
|
4149
|
+
};
|
|
4150
|
+
if (query.filters?.dateRange) {
|
|
4151
|
+
if (typeof query.filters.dateRange.start === "string") {
|
|
4152
|
+
query.filters.dateRange.start = new Date(query.filters.dateRange.start);
|
|
4153
|
+
}
|
|
4154
|
+
if (typeof query.filters.dateRange.end === "string") {
|
|
4155
|
+
query.filters.dateRange.end = new Date(query.filters.dateRange.end);
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
const results = await service.search(query);
|
|
4159
|
+
return c.json({
|
|
4160
|
+
success: true,
|
|
4161
|
+
data: results
|
|
4162
|
+
});
|
|
4163
|
+
} catch (error) {
|
|
4164
|
+
console.error("Search error:", error);
|
|
4165
|
+
return c.json(
|
|
4166
|
+
{
|
|
4167
|
+
success: false,
|
|
4168
|
+
error: "Search failed",
|
|
4169
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4170
|
+
},
|
|
4171
|
+
500
|
|
4172
|
+
);
|
|
4173
|
+
}
|
|
4174
|
+
});
|
|
4175
|
+
apiRoutes.get("/suggest", async (c) => {
|
|
4176
|
+
try {
|
|
4177
|
+
const db = c.env.DB;
|
|
4178
|
+
const ai = c.env.AI;
|
|
4179
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4180
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
4181
|
+
const query = c.req.query("q") || "";
|
|
4182
|
+
if (!query || query.length < 2) {
|
|
4183
|
+
return c.json({ success: true, data: [] });
|
|
4184
|
+
}
|
|
4185
|
+
const suggestions = await service.getSearchSuggestions(query);
|
|
4186
|
+
return c.json({
|
|
4187
|
+
success: true,
|
|
4188
|
+
data: suggestions
|
|
4189
|
+
});
|
|
4190
|
+
} catch (error) {
|
|
4191
|
+
console.error("Suggestions error:", error);
|
|
4192
|
+
return c.json(
|
|
4193
|
+
{
|
|
4194
|
+
success: false,
|
|
4195
|
+
error: "Failed to get suggestions"
|
|
4196
|
+
},
|
|
4197
|
+
500
|
|
4198
|
+
);
|
|
4199
|
+
}
|
|
4200
|
+
});
|
|
4201
|
+
apiRoutes.get("/analytics", async (c) => {
|
|
4202
|
+
try {
|
|
4203
|
+
const db = c.env.DB;
|
|
4204
|
+
const ai = c.env.AI;
|
|
4205
|
+
const vectorize = c.env.VECTORIZE_INDEX;
|
|
4206
|
+
const service = new AISearchService(db, ai, vectorize);
|
|
4207
|
+
const analytics = await service.getSearchAnalytics();
|
|
4208
|
+
return c.json({
|
|
4209
|
+
success: true,
|
|
4210
|
+
data: analytics
|
|
4211
|
+
});
|
|
4212
|
+
} catch (error) {
|
|
4213
|
+
console.error("Analytics error:", error);
|
|
4214
|
+
return c.json(
|
|
4215
|
+
{
|
|
4216
|
+
success: false,
|
|
4217
|
+
error: "Failed to get analytics"
|
|
4218
|
+
},
|
|
4219
|
+
500
|
|
4220
|
+
);
|
|
4221
|
+
}
|
|
4222
|
+
});
|
|
4223
|
+
var api_default2 = apiRoutes;
|
|
4224
|
+
|
|
4225
|
+
// src/plugins/core-plugins/ai-search-plugin/manifest.json
|
|
4226
|
+
var manifest_default = {
|
|
4227
|
+
name: "AI Search",
|
|
4228
|
+
description: "Advanced search with Cloudflare AI Search. Full-text search, semantic search, and advanced filtering across all content collections.",
|
|
4229
|
+
version: "1.0.0",
|
|
4230
|
+
author: "SonicJS"};
|
|
4231
|
+
|
|
4232
|
+
// src/plugins/core-plugins/ai-search-plugin/index.ts
|
|
4233
|
+
var aiSearchPlugin = new chunkYHW27CBV_cjs.PluginBuilder({
|
|
4234
|
+
name: manifest_default.name,
|
|
4235
|
+
version: manifest_default.version,
|
|
4236
|
+
description: manifest_default.description,
|
|
4237
|
+
author: { name: manifest_default.author }
|
|
4238
|
+
}).metadata({
|
|
4239
|
+
description: manifest_default.description,
|
|
4240
|
+
author: { name: manifest_default.author }
|
|
4241
|
+
}).addService("aiSearch", AISearchService).addService("indexManager", IndexManager).addRoute("/admin/plugins/ai-search", admin_default).addRoute("/api/search", api_default2).build();
|
|
4242
|
+
var magicLinkRequestSchema = zod.z.object({
|
|
4243
|
+
email: zod.z.string().email("Valid email is required")
|
|
4244
|
+
});
|
|
4245
|
+
function createMagicLinkAuthPlugin() {
|
|
4246
|
+
const magicLinkRoutes = new hono.Hono();
|
|
4247
|
+
magicLinkRoutes.post("/request", async (c) => {
|
|
4248
|
+
try {
|
|
4249
|
+
const body = await c.req.json();
|
|
4250
|
+
const validation = magicLinkRequestSchema.safeParse(body);
|
|
4251
|
+
if (!validation.success) {
|
|
4252
|
+
return c.json({
|
|
4253
|
+
error: "Validation failed",
|
|
4254
|
+
details: validation.error.issues
|
|
4255
|
+
}, 400);
|
|
4256
|
+
}
|
|
4257
|
+
const { email } = validation.data;
|
|
4258
|
+
const normalizedEmail = email.toLowerCase();
|
|
4259
|
+
const db = c.env.DB;
|
|
4260
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1e3;
|
|
4261
|
+
const recentLinks = await db.prepare(`
|
|
4262
|
+
SELECT COUNT(*) as count
|
|
4263
|
+
FROM magic_links
|
|
4264
|
+
WHERE user_email = ? AND created_at > ?
|
|
4265
|
+
`).bind(normalizedEmail, oneHourAgo).first();
|
|
4266
|
+
const rateLimitPerHour = 5;
|
|
4267
|
+
if (recentLinks && recentLinks.count >= rateLimitPerHour) {
|
|
4268
|
+
return c.json({
|
|
4269
|
+
error: "Too many requests. Please try again later."
|
|
4270
|
+
}, 429);
|
|
4271
|
+
}
|
|
4272
|
+
const user = await db.prepare(`
|
|
4273
|
+
SELECT id, email, role, is_active
|
|
4274
|
+
FROM users
|
|
4275
|
+
WHERE email = ?
|
|
4276
|
+
`).bind(normalizedEmail).first();
|
|
4277
|
+
const allowNewUsers = false;
|
|
4278
|
+
if (!user && !allowNewUsers) {
|
|
4279
|
+
return c.json({
|
|
4280
|
+
message: "If an account exists for this email, you will receive a magic link shortly."
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
if (user && !user.is_active) {
|
|
4284
|
+
return c.json({
|
|
4285
|
+
error: "This account has been deactivated."
|
|
4286
|
+
}, 403);
|
|
4287
|
+
}
|
|
4288
|
+
const token = crypto.randomUUID() + "-" + crypto.randomUUID();
|
|
4289
|
+
const tokenId = crypto.randomUUID();
|
|
4290
|
+
const linkExpiryMinutes = 15;
|
|
4291
|
+
const expiresAt = Date.now() + linkExpiryMinutes * 60 * 1e3;
|
|
4292
|
+
await db.prepare(`
|
|
4293
|
+
INSERT INTO magic_links (
|
|
4294
|
+
id, user_email, token, expires_at, used, created_at, ip_address, user_agent
|
|
4295
|
+
) VALUES (?, ?, ?, ?, 0, ?, ?, ?)
|
|
4296
|
+
`).bind(
|
|
4297
|
+
tokenId,
|
|
4298
|
+
normalizedEmail,
|
|
4299
|
+
token,
|
|
4300
|
+
expiresAt,
|
|
4301
|
+
Date.now(),
|
|
4302
|
+
c.req.header("cf-connecting-ip") || c.req.header("x-forwarded-for") || "unknown",
|
|
4303
|
+
c.req.header("user-agent") || "unknown"
|
|
4304
|
+
).run();
|
|
4305
|
+
const baseUrl = new URL(c.req.url).origin;
|
|
4306
|
+
const magicLink = `${baseUrl}/auth/magic-link/verify?token=${token}`;
|
|
4307
|
+
try {
|
|
4308
|
+
const emailPlugin2 = c.env.plugins?.get("email");
|
|
4309
|
+
if (emailPlugin2 && emailPlugin2.sendEmail) {
|
|
4310
|
+
await emailPlugin2.sendEmail({
|
|
4311
|
+
to: normalizedEmail,
|
|
4312
|
+
subject: "Your Magic Link to Sign In",
|
|
4313
|
+
html: renderMagicLinkEmail(magicLink, linkExpiryMinutes)
|
|
4314
|
+
});
|
|
4315
|
+
} else {
|
|
4316
|
+
console.error("Email plugin not available");
|
|
4317
|
+
console.log(`Magic link for ${normalizedEmail}: ${magicLink}`);
|
|
4318
|
+
}
|
|
4319
|
+
} catch (error) {
|
|
4320
|
+
console.error("Failed to send magic link email:", error);
|
|
4321
|
+
return c.json({
|
|
4322
|
+
error: "Failed to send email. Please try again later."
|
|
4323
|
+
}, 500);
|
|
4324
|
+
}
|
|
4325
|
+
return c.json({
|
|
4326
|
+
message: "If an account exists for this email, you will receive a magic link shortly.",
|
|
4327
|
+
// For development only - remove in production
|
|
4328
|
+
...c.env.ENVIRONMENT === "development" && { dev_link: magicLink }
|
|
4329
|
+
});
|
|
4330
|
+
} catch (error) {
|
|
4331
|
+
console.error("Magic link request error:", error);
|
|
4332
|
+
return c.json({ error: "Failed to process request" }, 500);
|
|
4333
|
+
}
|
|
4334
|
+
});
|
|
4335
|
+
magicLinkRoutes.get("/verify", async (c) => {
|
|
4336
|
+
try {
|
|
4337
|
+
const token = c.req.query("token");
|
|
4338
|
+
if (!token) {
|
|
4339
|
+
return c.redirect("/auth/login?error=Invalid magic link");
|
|
4340
|
+
}
|
|
4341
|
+
const db = c.env.DB;
|
|
4342
|
+
const magicLink = await db.prepare(`
|
|
4343
|
+
SELECT * FROM magic_links
|
|
4344
|
+
WHERE token = ? AND used = 0
|
|
4345
|
+
`).bind(token).first();
|
|
4346
|
+
if (!magicLink) {
|
|
4347
|
+
return c.redirect("/auth/login?error=Invalid or expired magic link");
|
|
4348
|
+
}
|
|
4349
|
+
if (magicLink.expires_at < Date.now()) {
|
|
4350
|
+
return c.redirect("/auth/login?error=This magic link has expired");
|
|
4351
|
+
}
|
|
4352
|
+
let user = await db.prepare(`
|
|
4353
|
+
SELECT * FROM users WHERE email = ? AND is_active = 1
|
|
4354
|
+
`).bind(magicLink.user_email).first();
|
|
4355
|
+
const allowNewUsers = false;
|
|
4356
|
+
if (!user && allowNewUsers) {
|
|
4357
|
+
const userId = crypto.randomUUID();
|
|
4358
|
+
const username = magicLink.user_email.split("@")[0];
|
|
4359
|
+
const now = Date.now();
|
|
4360
|
+
await db.prepare(`
|
|
4361
|
+
INSERT INTO users (
|
|
4362
|
+
id, email, username, first_name, last_name,
|
|
4363
|
+
password_hash, role, is_active, created_at, updated_at
|
|
4364
|
+
) VALUES (?, ?, ?, ?, ?, NULL, 'viewer', 1, ?, ?)
|
|
4365
|
+
`).bind(
|
|
4366
|
+
userId,
|
|
4367
|
+
magicLink.user_email,
|
|
4368
|
+
username,
|
|
4369
|
+
username,
|
|
4370
|
+
"",
|
|
4371
|
+
now,
|
|
4372
|
+
now
|
|
4373
|
+
).run();
|
|
4374
|
+
user = {
|
|
4375
|
+
id: userId,
|
|
4376
|
+
email: magicLink.user_email,
|
|
4377
|
+
username,
|
|
4378
|
+
role: "viewer"
|
|
4379
|
+
};
|
|
4380
|
+
} else if (!user) {
|
|
4381
|
+
return c.redirect("/auth/login?error=No account found for this email");
|
|
4382
|
+
}
|
|
4383
|
+
await db.prepare(`
|
|
4384
|
+
UPDATE magic_links
|
|
4385
|
+
SET used = 1, used_at = ?
|
|
4386
|
+
WHERE id = ?
|
|
4387
|
+
`).bind(Date.now(), magicLink.id).run();
|
|
4388
|
+
const jwtToken = await chunkT3YIKW2A_cjs.AuthManager.generateToken(
|
|
4389
|
+
user.id,
|
|
4390
|
+
user.email,
|
|
4391
|
+
user.role
|
|
4392
|
+
);
|
|
4393
|
+
chunkT3YIKW2A_cjs.AuthManager.setAuthCookie(c, jwtToken);
|
|
4394
|
+
await db.prepare(`
|
|
4395
|
+
UPDATE users SET last_login_at = ? WHERE id = ?
|
|
4396
|
+
`).bind(Date.now(), user.id).run();
|
|
4397
|
+
return c.redirect("/admin/dashboard?message=Successfully signed in");
|
|
4398
|
+
} catch (error) {
|
|
4399
|
+
console.error("Magic link verification error:", error);
|
|
4400
|
+
return c.redirect("/auth/login?error=Authentication failed");
|
|
4401
|
+
}
|
|
4402
|
+
});
|
|
4403
|
+
return {
|
|
4404
|
+
name: "magic-link-auth",
|
|
4405
|
+
version: "1.0.0",
|
|
4406
|
+
description: "Passwordless authentication via email magic links",
|
|
4407
|
+
author: {
|
|
4408
|
+
name: "SonicJS Team",
|
|
4409
|
+
email: "team@sonicjs.com"
|
|
4410
|
+
},
|
|
4411
|
+
dependencies: ["email"],
|
|
4412
|
+
routes: [{
|
|
4413
|
+
path: "/auth/magic-link",
|
|
4414
|
+
handler: magicLinkRoutes,
|
|
4415
|
+
description: "Magic link authentication endpoints",
|
|
4416
|
+
requiresAuth: false
|
|
4417
|
+
}],
|
|
2611
4418
|
async install(context) {
|
|
2612
4419
|
console.log("Installing magic-link-auth plugin...");
|
|
2613
4420
|
},
|
|
@@ -2728,18 +4535,46 @@ function renderMagicLinkEmail(magicLink, expiryMinutes) {
|
|
|
2728
4535
|
}
|
|
2729
4536
|
createMagicLinkAuthPlugin();
|
|
2730
4537
|
|
|
4538
|
+
// src/assets/favicon.ts
|
|
4539
|
+
var faviconSvg = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
4540
|
+
<svg
|
|
4541
|
+
version="1.1"
|
|
4542
|
+
id="Layer_1"
|
|
4543
|
+
x="0px"
|
|
4544
|
+
y="0px"
|
|
4545
|
+
viewBox="380 1300 257.89001 278.8855"
|
|
4546
|
+
xml:space="preserve"
|
|
4547
|
+
width="257.89001"
|
|
4548
|
+
height="278.8855"
|
|
4549
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
4550
|
+
<g
|
|
4551
|
+
id="g10"
|
|
4552
|
+
transform="translate(-383.935,-60.555509)">
|
|
4553
|
+
<g
|
|
4554
|
+
id="g9">
|
|
4555
|
+
<path
|
|
4556
|
+
fill="#f1f2f2"
|
|
4557
|
+
d="m 974.78,1398.211 c -5.016,6.574 -10.034,13.146 -15.048,19.721 -1.828,2.398 -3.657,4.796 -5.487,7.194 1.994,1.719 3.958,3.51 5.873,5.424 18.724,18.731 28.089,41.216 28.089,67.459 0,26.251 -9.366,48.658 -28.089,67.237 -18.731,18.579 -41.215,27.868 -67.459,27.868 -9.848,0 -19.156,-1.308 -27.923,-3.923 l -4.185,3.354 c -8.587,6.885 -17.154,13.796 -25.725,20.702 17.52,8.967 36.86,13.487 58.054,13.487 35.533,0 65.91,-12.608 91.124,-37.821 25.214,-25.215 37.821,-55.584 37.821,-91.125 0,-35.534 -12.607,-65.911 -37.821,-91.126 -3,-2.999 -6.078,-5.808 -9.224,-8.451 z"
|
|
4558
|
+
id="path2" />
|
|
4559
|
+
<path
|
|
4560
|
+
fill="#34d399"
|
|
4561
|
+
d="m 854.024,1585.195 20.001,-16.028 c 16.616,-13.507 33.04,-27.265 50.086,-40.251 1.13,-0.861 2.9,-1.686 2.003,-3.516 -0.843,-1.716 -2.481,-2.302 -4.484,-2.123 -8.514,0.765 -17.016,-0.538 -25.537,-0.353 -1.124,0.024 -2.768,0.221 -3.163,-1.25 -0.371,-1.369 1.088,-2.063 1.919,-2.894 6.26,-6.242 12.574,-12.43 18.816,-18.691 9.303,-9.327 18.565,-18.714 27.851,-28.066 1.848,-1.859 3.701,-3.713 5.549,-5.572 2.655,-2.661 5.309,-5.315 7.958,-7.982 0.574,-0.579 1.259,-1.141 1.246,-1.94 -0.004,-0.257 -0.078,-0.538 -0.254,-0.853 -0.556,-0.981 -1.441,-1.1 -2.469,-0.957 -0.658,0.096 -1.315,0.185 -1.973,0.275 -3.844,0.538 -7.689,1.076 -11.533,1.608 -3.641,0.505 -7.281,1.02 -10.922,1.529 -4.162,0.582 -8.324,1.158 -12.486,1.748 -1.142,0.161 -2.409,1.662 -3.354,0.508 -0.419,-0.508 -0.431,-1.028 -0.251,-1.531 0.269,-0.741 0.957,-1.441 1.387,-2.021 3.414,-4.58 6.882,-9.124 10.356,-13.662 1.74,-2.272 3.48,-4.544 5.214,-6.822 4.682,-6.141 9.369,-12.281 14.051,-18.422 0.09,-0.119 0.181,-0.237 0.271,-0.355 6.848,-8.98 13.7,-17.958 20.553,-26.936 0.488,-0.64 0.977,-1.28 1.465,-1.92 2.159,-2.828 4.315,-5.658 6.476,-8.486 4.197,-5.501 8.454,-10.954 12.67,-16.442 0.263,-0.347 0.538,-0.718 0.717,-1.106 0.269,-0.586 0.299,-1.196 -0.335,-1.776 -0.825,-0.753 -1.8,-0.15 -2.595,0.419 -0.67,0.472 -1.333,0.957 -1.955,1.489 -2.206,1.889 -4.401,3.797 -6.595,5.698 -3.958,3.438 -7.922,6.876 -11.976,10.194 -2.443,2.003 -4.865,4.028 -7.301,6.038 -18.689,-10.581 -39.53,-15.906 -62.549,-15.906 -35.54,0 -65.911,12.607 -91.125,37.82 -25.214,25.215 -37.821,55.592 -37.821,91.126 0,35.54 12.607,65.91 37.821,91.125 4.146,4.146 8.445,7.916 12.87,11.381 -9.015,11.14 -18.036,22.277 -27.034,33.429 -1.208,1.489 -3.755,3.151 -2.745,4.891 0.078,0.144 0.173,0.281 0.305,0.425 1.321,1.429 3.492,-1.303 4.933,-2.457 6.673,-5.333 13.333,-10.685 19.982,-16.042 3.707,-2.984 7.417,-5.965 11.124,-8.952 1.474,-1.188 2.951,-2.373 4.425,-3.561 6.41,-5.164 12.816,-10.333 19.238,-15.481 z m -56.472,-87.186 c 0,-26.243 9.29,-48.728 27.868,-67.459 18.579,-18.723 40.987,-28.089 67.238,-28.089 12.273,0 23.712,2.075 34.34,6.171 -3.37,2.905 -6.734,5.816 -10.069,8.762 -6.075,5.351 -12.365,10.469 -18.667,15.564 -4.179,3.378 -8.371,6.744 -12.514,10.164 -7.54,6.23 -15.037,12.52 -22.529,18.804 -7.091,5.955 -14.182,11.904 -21.19,17.949 -1.136,0.974 -3.055,1.907 -2.135,3.94 0.831,1.836 2.774,1.417 4.341,1.578 l 12.145,-0.599 14.151,-0.698 c 1.031,-0.102 2.192,-0.257 2.89,0.632 0.034,0.044 0.073,0.078 0.106,0.127 1.017,1.561 -0.67,2.105 -1.387,2.942 -6.308,7.318 -12.616,14.637 -18.978,21.907 -8.161,9.339 -16.353,18.649 -24.544,27.958 -2.146,2.433 -4.275,4.879 -6.422,7.312 -1.034,1.172 -2.129,2.272 -1.238,3.922 0.933,1.728 2.685,1.752 4.323,1.602 4.134,-0.367 8.263,-0.489 12.396,-0.492 0.242,0 0.485,-0.01 0.728,0 2.711,0.01 5.422,0.068 8.134,0.145 2.582,0.074 5.166,0.165 7.752,0.249 0.275,1.62 -0.879,2.356 -1.62,3.259 -1.333,1.626 -2.667,3.247 -4,4.867 -4.315,5.252 -8.62,10.514 -12.928,15.772 -3.562,-2.725 -7.007,-5.733 -10.324,-9.051 -18.577,-18.576 -27.867,-40.983 -27.867,-67.234 z"
|
|
4562
|
+
id="path9" />
|
|
4563
|
+
</g>
|
|
4564
|
+
</g>
|
|
4565
|
+
</svg>`;
|
|
4566
|
+
|
|
2731
4567
|
// src/app.ts
|
|
2732
4568
|
function createSonicJSApp(config = {}) {
|
|
2733
4569
|
const app = new hono.Hono();
|
|
2734
|
-
const appVersion = config.version ||
|
|
4570
|
+
const appVersion = config.version || chunkYMTTGHEK_cjs.getCoreVersion();
|
|
2735
4571
|
const appName = config.name || "SonicJS AI";
|
|
2736
4572
|
app.use("*", async (c, next) => {
|
|
2737
4573
|
c.set("appVersion", appVersion);
|
|
2738
4574
|
await next();
|
|
2739
4575
|
});
|
|
2740
|
-
app.use("*",
|
|
2741
|
-
app.use("*",
|
|
2742
|
-
app.use("*", adminSetupMiddleware());
|
|
4576
|
+
app.use("*", chunkT3YIKW2A_cjs.metricsMiddleware());
|
|
4577
|
+
app.use("*", chunkT3YIKW2A_cjs.bootstrapMiddleware(config));
|
|
2743
4578
|
if (config.middleware?.beforeAuth) {
|
|
2744
4579
|
for (const middleware of config.middleware.beforeAuth) {
|
|
2745
4580
|
app.use("*", middleware);
|
|
@@ -2756,22 +4591,27 @@ function createSonicJSApp(config = {}) {
|
|
|
2756
4591
|
app.use("*", middleware);
|
|
2757
4592
|
}
|
|
2758
4593
|
}
|
|
2759
|
-
app.route("/api",
|
|
2760
|
-
app.route("/api/media",
|
|
2761
|
-
app.route("/api/system",
|
|
2762
|
-
app.route("/admin/api",
|
|
2763
|
-
app.route("/admin/dashboard",
|
|
2764
|
-
app.route("/admin/collections",
|
|
2765
|
-
app.route("/admin/settings",
|
|
4594
|
+
app.route("/api", chunkN7TDLOUE_cjs.api_default);
|
|
4595
|
+
app.route("/api/media", chunkN7TDLOUE_cjs.api_media_default);
|
|
4596
|
+
app.route("/api/system", chunkN7TDLOUE_cjs.api_system_default);
|
|
4597
|
+
app.route("/admin/api", chunkN7TDLOUE_cjs.admin_api_default);
|
|
4598
|
+
app.route("/admin/dashboard", chunkN7TDLOUE_cjs.router);
|
|
4599
|
+
app.route("/admin/collections", chunkN7TDLOUE_cjs.adminCollectionsRoutes);
|
|
4600
|
+
app.route("/admin/settings", chunkN7TDLOUE_cjs.adminSettingsRoutes);
|
|
2766
4601
|
app.route("/admin/database-tools", createDatabaseToolsAdminRoutes());
|
|
2767
4602
|
app.route("/admin/seed-data", createSeedDataAdminRoutes());
|
|
2768
|
-
app.route("/admin/content",
|
|
2769
|
-
app.route("/admin/media",
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
4603
|
+
app.route("/admin/content", chunkN7TDLOUE_cjs.admin_content_default);
|
|
4604
|
+
app.route("/admin/media", chunkN7TDLOUE_cjs.adminMediaRoutes);
|
|
4605
|
+
if (aiSearchPlugin.routes && aiSearchPlugin.routes.length > 0) {
|
|
4606
|
+
for (const route of aiSearchPlugin.routes) {
|
|
4607
|
+
app.route(route.path, route.handler);
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
app.route("/admin/plugins", chunkN7TDLOUE_cjs.adminPluginRoutes);
|
|
4611
|
+
app.route("/admin/logs", chunkN7TDLOUE_cjs.adminLogsRoutes);
|
|
4612
|
+
app.route("/admin", chunkN7TDLOUE_cjs.userRoutes);
|
|
4613
|
+
app.route("/auth", chunkN7TDLOUE_cjs.auth_default);
|
|
4614
|
+
app.route("/", chunkN7TDLOUE_cjs.test_cleanup_default);
|
|
2775
4615
|
if (emailPlugin.routes && emailPlugin.routes.length > 0) {
|
|
2776
4616
|
for (const route of emailPlugin.routes) {
|
|
2777
4617
|
app.route(route.path, route.handler);
|
|
@@ -2788,6 +4628,14 @@ function createSonicJSApp(config = {}) {
|
|
|
2788
4628
|
app.route(route.path, route.handler);
|
|
2789
4629
|
}
|
|
2790
4630
|
}
|
|
4631
|
+
app.get("/favicon.svg", (c) => {
|
|
4632
|
+
return new Response(faviconSvg, {
|
|
4633
|
+
headers: {
|
|
4634
|
+
"Content-Type": "image/svg+xml",
|
|
4635
|
+
"Cache-Control": "public, max-age=31536000"
|
|
4636
|
+
}
|
|
4637
|
+
});
|
|
4638
|
+
});
|
|
2791
4639
|
app.get("/files/*", async (c) => {
|
|
2792
4640
|
try {
|
|
2793
4641
|
const url = new URL(c.req.url);
|
|
@@ -2851,83 +4699,83 @@ function createDb(d1$1) {
|
|
|
2851
4699
|
}
|
|
2852
4700
|
|
|
2853
4701
|
// src/index.ts
|
|
2854
|
-
var VERSION =
|
|
4702
|
+
var VERSION = chunkYMTTGHEK_cjs.package_default.version;
|
|
2855
4703
|
|
|
2856
4704
|
Object.defineProperty(exports, "ROUTES_INFO", {
|
|
2857
4705
|
enumerable: true,
|
|
2858
|
-
get: function () { return
|
|
4706
|
+
get: function () { return chunkN7TDLOUE_cjs.ROUTES_INFO; }
|
|
2859
4707
|
});
|
|
2860
4708
|
Object.defineProperty(exports, "adminApiRoutes", {
|
|
2861
4709
|
enumerable: true,
|
|
2862
|
-
get: function () { return
|
|
4710
|
+
get: function () { return chunkN7TDLOUE_cjs.admin_api_default; }
|
|
2863
4711
|
});
|
|
2864
4712
|
Object.defineProperty(exports, "adminCheckboxRoutes", {
|
|
2865
4713
|
enumerable: true,
|
|
2866
|
-
get: function () { return
|
|
4714
|
+
get: function () { return chunkN7TDLOUE_cjs.adminCheckboxRoutes; }
|
|
2867
4715
|
});
|
|
2868
4716
|
Object.defineProperty(exports, "adminCodeExamplesRoutes", {
|
|
2869
4717
|
enumerable: true,
|
|
2870
|
-
get: function () { return
|
|
4718
|
+
get: function () { return chunkN7TDLOUE_cjs.admin_code_examples_default; }
|
|
2871
4719
|
});
|
|
2872
4720
|
Object.defineProperty(exports, "adminCollectionsRoutes", {
|
|
2873
4721
|
enumerable: true,
|
|
2874
|
-
get: function () { return
|
|
4722
|
+
get: function () { return chunkN7TDLOUE_cjs.adminCollectionsRoutes; }
|
|
2875
4723
|
});
|
|
2876
4724
|
Object.defineProperty(exports, "adminContentRoutes", {
|
|
2877
4725
|
enumerable: true,
|
|
2878
|
-
get: function () { return
|
|
4726
|
+
get: function () { return chunkN7TDLOUE_cjs.admin_content_default; }
|
|
2879
4727
|
});
|
|
2880
4728
|
Object.defineProperty(exports, "adminDashboardRoutes", {
|
|
2881
4729
|
enumerable: true,
|
|
2882
|
-
get: function () { return
|
|
4730
|
+
get: function () { return chunkN7TDLOUE_cjs.router; }
|
|
2883
4731
|
});
|
|
2884
4732
|
Object.defineProperty(exports, "adminDesignRoutes", {
|
|
2885
4733
|
enumerable: true,
|
|
2886
|
-
get: function () { return
|
|
4734
|
+
get: function () { return chunkN7TDLOUE_cjs.adminDesignRoutes; }
|
|
2887
4735
|
});
|
|
2888
4736
|
Object.defineProperty(exports, "adminLogsRoutes", {
|
|
2889
4737
|
enumerable: true,
|
|
2890
|
-
get: function () { return
|
|
4738
|
+
get: function () { return chunkN7TDLOUE_cjs.adminLogsRoutes; }
|
|
2891
4739
|
});
|
|
2892
4740
|
Object.defineProperty(exports, "adminMediaRoutes", {
|
|
2893
4741
|
enumerable: true,
|
|
2894
|
-
get: function () { return
|
|
4742
|
+
get: function () { return chunkN7TDLOUE_cjs.adminMediaRoutes; }
|
|
2895
4743
|
});
|
|
2896
4744
|
Object.defineProperty(exports, "adminPluginRoutes", {
|
|
2897
4745
|
enumerable: true,
|
|
2898
|
-
get: function () { return
|
|
4746
|
+
get: function () { return chunkN7TDLOUE_cjs.adminPluginRoutes; }
|
|
2899
4747
|
});
|
|
2900
4748
|
Object.defineProperty(exports, "adminSettingsRoutes", {
|
|
2901
4749
|
enumerable: true,
|
|
2902
|
-
get: function () { return
|
|
4750
|
+
get: function () { return chunkN7TDLOUE_cjs.adminSettingsRoutes; }
|
|
2903
4751
|
});
|
|
2904
4752
|
Object.defineProperty(exports, "adminTestimonialsRoutes", {
|
|
2905
4753
|
enumerable: true,
|
|
2906
|
-
get: function () { return
|
|
4754
|
+
get: function () { return chunkN7TDLOUE_cjs.admin_testimonials_default; }
|
|
2907
4755
|
});
|
|
2908
4756
|
Object.defineProperty(exports, "adminUsersRoutes", {
|
|
2909
4757
|
enumerable: true,
|
|
2910
|
-
get: function () { return
|
|
4758
|
+
get: function () { return chunkN7TDLOUE_cjs.userRoutes; }
|
|
2911
4759
|
});
|
|
2912
4760
|
Object.defineProperty(exports, "apiContentCrudRoutes", {
|
|
2913
4761
|
enumerable: true,
|
|
2914
|
-
get: function () { return
|
|
4762
|
+
get: function () { return chunkN7TDLOUE_cjs.api_content_crud_default; }
|
|
2915
4763
|
});
|
|
2916
4764
|
Object.defineProperty(exports, "apiMediaRoutes", {
|
|
2917
4765
|
enumerable: true,
|
|
2918
|
-
get: function () { return
|
|
4766
|
+
get: function () { return chunkN7TDLOUE_cjs.api_media_default; }
|
|
2919
4767
|
});
|
|
2920
4768
|
Object.defineProperty(exports, "apiRoutes", {
|
|
2921
4769
|
enumerable: true,
|
|
2922
|
-
get: function () { return
|
|
4770
|
+
get: function () { return chunkN7TDLOUE_cjs.api_default; }
|
|
2923
4771
|
});
|
|
2924
4772
|
Object.defineProperty(exports, "apiSystemRoutes", {
|
|
2925
4773
|
enumerable: true,
|
|
2926
|
-
get: function () { return
|
|
4774
|
+
get: function () { return chunkN7TDLOUE_cjs.api_system_default; }
|
|
2927
4775
|
});
|
|
2928
4776
|
Object.defineProperty(exports, "authRoutes", {
|
|
2929
4777
|
enumerable: true,
|
|
2930
|
-
get: function () { return
|
|
4778
|
+
get: function () { return chunkN7TDLOUE_cjs.auth_default; }
|
|
2931
4779
|
});
|
|
2932
4780
|
Object.defineProperty(exports, "Logger", {
|
|
2933
4781
|
enumerable: true,
|
|
@@ -3095,235 +4943,243 @@ Object.defineProperty(exports, "workflowHistory", {
|
|
|
3095
4943
|
});
|
|
3096
4944
|
Object.defineProperty(exports, "AuthManager", {
|
|
3097
4945
|
enumerable: true,
|
|
3098
|
-
get: function () { return
|
|
4946
|
+
get: function () { return chunkT3YIKW2A_cjs.AuthManager; }
|
|
3099
4947
|
});
|
|
3100
4948
|
Object.defineProperty(exports, "PermissionManager", {
|
|
3101
4949
|
enumerable: true,
|
|
3102
|
-
get: function () { return
|
|
4950
|
+
get: function () { return chunkT3YIKW2A_cjs.PermissionManager; }
|
|
3103
4951
|
});
|
|
3104
4952
|
Object.defineProperty(exports, "bootstrapMiddleware", {
|
|
3105
4953
|
enumerable: true,
|
|
3106
|
-
get: function () { return
|
|
4954
|
+
get: function () { return chunkT3YIKW2A_cjs.bootstrapMiddleware; }
|
|
3107
4955
|
});
|
|
3108
4956
|
Object.defineProperty(exports, "cacheHeaders", {
|
|
3109
4957
|
enumerable: true,
|
|
3110
|
-
get: function () { return
|
|
4958
|
+
get: function () { return chunkT3YIKW2A_cjs.cacheHeaders; }
|
|
3111
4959
|
});
|
|
3112
4960
|
Object.defineProperty(exports, "compressionMiddleware", {
|
|
3113
4961
|
enumerable: true,
|
|
3114
|
-
get: function () { return
|
|
4962
|
+
get: function () { return chunkT3YIKW2A_cjs.compressionMiddleware; }
|
|
3115
4963
|
});
|
|
3116
4964
|
Object.defineProperty(exports, "detailedLoggingMiddleware", {
|
|
3117
4965
|
enumerable: true,
|
|
3118
|
-
get: function () { return
|
|
4966
|
+
get: function () { return chunkT3YIKW2A_cjs.detailedLoggingMiddleware; }
|
|
3119
4967
|
});
|
|
3120
4968
|
Object.defineProperty(exports, "getActivePlugins", {
|
|
3121
4969
|
enumerable: true,
|
|
3122
|
-
get: function () { return
|
|
4970
|
+
get: function () { return chunkT3YIKW2A_cjs.getActivePlugins; }
|
|
3123
4971
|
});
|
|
3124
4972
|
Object.defineProperty(exports, "isPluginActive", {
|
|
3125
4973
|
enumerable: true,
|
|
3126
|
-
get: function () { return
|
|
4974
|
+
get: function () { return chunkT3YIKW2A_cjs.isPluginActive; }
|
|
3127
4975
|
});
|
|
3128
4976
|
Object.defineProperty(exports, "logActivity", {
|
|
3129
4977
|
enumerable: true,
|
|
3130
|
-
get: function () { return
|
|
4978
|
+
get: function () { return chunkT3YIKW2A_cjs.logActivity; }
|
|
3131
4979
|
});
|
|
3132
4980
|
Object.defineProperty(exports, "loggingMiddleware", {
|
|
3133
4981
|
enumerable: true,
|
|
3134
|
-
get: function () { return
|
|
4982
|
+
get: function () { return chunkT3YIKW2A_cjs.loggingMiddleware; }
|
|
3135
4983
|
});
|
|
3136
4984
|
Object.defineProperty(exports, "optionalAuth", {
|
|
3137
4985
|
enumerable: true,
|
|
3138
|
-
get: function () { return
|
|
4986
|
+
get: function () { return chunkT3YIKW2A_cjs.optionalAuth; }
|
|
3139
4987
|
});
|
|
3140
4988
|
Object.defineProperty(exports, "performanceLoggingMiddleware", {
|
|
3141
4989
|
enumerable: true,
|
|
3142
|
-
get: function () { return
|
|
4990
|
+
get: function () { return chunkT3YIKW2A_cjs.performanceLoggingMiddleware; }
|
|
3143
4991
|
});
|
|
3144
4992
|
Object.defineProperty(exports, "requireActivePlugin", {
|
|
3145
4993
|
enumerable: true,
|
|
3146
|
-
get: function () { return
|
|
4994
|
+
get: function () { return chunkT3YIKW2A_cjs.requireActivePlugin; }
|
|
3147
4995
|
});
|
|
3148
4996
|
Object.defineProperty(exports, "requireActivePlugins", {
|
|
3149
4997
|
enumerable: true,
|
|
3150
|
-
get: function () { return
|
|
4998
|
+
get: function () { return chunkT3YIKW2A_cjs.requireActivePlugins; }
|
|
3151
4999
|
});
|
|
3152
5000
|
Object.defineProperty(exports, "requireAnyPermission", {
|
|
3153
5001
|
enumerable: true,
|
|
3154
|
-
get: function () { return
|
|
5002
|
+
get: function () { return chunkT3YIKW2A_cjs.requireAnyPermission; }
|
|
3155
5003
|
});
|
|
3156
5004
|
Object.defineProperty(exports, "requireAuth", {
|
|
3157
5005
|
enumerable: true,
|
|
3158
|
-
get: function () { return
|
|
5006
|
+
get: function () { return chunkT3YIKW2A_cjs.requireAuth; }
|
|
3159
5007
|
});
|
|
3160
5008
|
Object.defineProperty(exports, "requirePermission", {
|
|
3161
5009
|
enumerable: true,
|
|
3162
|
-
get: function () { return
|
|
5010
|
+
get: function () { return chunkT3YIKW2A_cjs.requirePermission; }
|
|
3163
5011
|
});
|
|
3164
5012
|
Object.defineProperty(exports, "requireRole", {
|
|
3165
5013
|
enumerable: true,
|
|
3166
|
-
get: function () { return
|
|
5014
|
+
get: function () { return chunkT3YIKW2A_cjs.requireRole; }
|
|
3167
5015
|
});
|
|
3168
5016
|
Object.defineProperty(exports, "securityHeaders", {
|
|
3169
5017
|
enumerable: true,
|
|
3170
|
-
get: function () { return
|
|
5018
|
+
get: function () { return chunkT3YIKW2A_cjs.securityHeaders; }
|
|
3171
5019
|
});
|
|
3172
5020
|
Object.defineProperty(exports, "securityLoggingMiddleware", {
|
|
3173
5021
|
enumerable: true,
|
|
3174
|
-
get: function () { return
|
|
5022
|
+
get: function () { return chunkT3YIKW2A_cjs.securityLoggingMiddleware; }
|
|
3175
5023
|
});
|
|
3176
5024
|
Object.defineProperty(exports, "PluginBootstrapService", {
|
|
3177
5025
|
enumerable: true,
|
|
3178
|
-
get: function () { return
|
|
5026
|
+
get: function () { return chunkMPT5PA6U_cjs.PluginBootstrapService; }
|
|
3179
5027
|
});
|
|
3180
5028
|
Object.defineProperty(exports, "PluginServiceClass", {
|
|
3181
5029
|
enumerable: true,
|
|
3182
|
-
get: function () { return
|
|
5030
|
+
get: function () { return chunkMPT5PA6U_cjs.PluginService; }
|
|
3183
5031
|
});
|
|
3184
5032
|
Object.defineProperty(exports, "cleanupRemovedCollections", {
|
|
3185
5033
|
enumerable: true,
|
|
3186
|
-
get: function () { return
|
|
5034
|
+
get: function () { return chunkMPT5PA6U_cjs.cleanupRemovedCollections; }
|
|
3187
5035
|
});
|
|
3188
5036
|
Object.defineProperty(exports, "fullCollectionSync", {
|
|
3189
5037
|
enumerable: true,
|
|
3190
|
-
get: function () { return
|
|
5038
|
+
get: function () { return chunkMPT5PA6U_cjs.fullCollectionSync; }
|
|
3191
5039
|
});
|
|
3192
5040
|
Object.defineProperty(exports, "getAvailableCollectionNames", {
|
|
3193
5041
|
enumerable: true,
|
|
3194
|
-
get: function () { return
|
|
5042
|
+
get: function () { return chunkMPT5PA6U_cjs.getAvailableCollectionNames; }
|
|
3195
5043
|
});
|
|
3196
5044
|
Object.defineProperty(exports, "getManagedCollections", {
|
|
3197
5045
|
enumerable: true,
|
|
3198
|
-
get: function () { return
|
|
5046
|
+
get: function () { return chunkMPT5PA6U_cjs.getManagedCollections; }
|
|
3199
5047
|
});
|
|
3200
5048
|
Object.defineProperty(exports, "isCollectionManaged", {
|
|
3201
5049
|
enumerable: true,
|
|
3202
|
-
get: function () { return
|
|
5050
|
+
get: function () { return chunkMPT5PA6U_cjs.isCollectionManaged; }
|
|
3203
5051
|
});
|
|
3204
5052
|
Object.defineProperty(exports, "loadCollectionConfig", {
|
|
3205
5053
|
enumerable: true,
|
|
3206
|
-
get: function () { return
|
|
5054
|
+
get: function () { return chunkMPT5PA6U_cjs.loadCollectionConfig; }
|
|
3207
5055
|
});
|
|
3208
5056
|
Object.defineProperty(exports, "loadCollectionConfigs", {
|
|
3209
5057
|
enumerable: true,
|
|
3210
|
-
get: function () { return
|
|
5058
|
+
get: function () { return chunkMPT5PA6U_cjs.loadCollectionConfigs; }
|
|
3211
5059
|
});
|
|
3212
5060
|
Object.defineProperty(exports, "registerCollections", {
|
|
3213
5061
|
enumerable: true,
|
|
3214
|
-
get: function () { return
|
|
5062
|
+
get: function () { return chunkMPT5PA6U_cjs.registerCollections; }
|
|
3215
5063
|
});
|
|
3216
5064
|
Object.defineProperty(exports, "syncCollection", {
|
|
3217
5065
|
enumerable: true,
|
|
3218
|
-
get: function () { return
|
|
5066
|
+
get: function () { return chunkMPT5PA6U_cjs.syncCollection; }
|
|
3219
5067
|
});
|
|
3220
5068
|
Object.defineProperty(exports, "syncCollections", {
|
|
3221
5069
|
enumerable: true,
|
|
3222
|
-
get: function () { return
|
|
5070
|
+
get: function () { return chunkMPT5PA6U_cjs.syncCollections; }
|
|
3223
5071
|
});
|
|
3224
5072
|
Object.defineProperty(exports, "validateCollectionConfig", {
|
|
3225
5073
|
enumerable: true,
|
|
3226
|
-
get: function () { return
|
|
5074
|
+
get: function () { return chunkMPT5PA6U_cjs.validateCollectionConfig; }
|
|
3227
5075
|
});
|
|
3228
5076
|
Object.defineProperty(exports, "MigrationService", {
|
|
3229
5077
|
enumerable: true,
|
|
3230
|
-
get: function () { return
|
|
5078
|
+
get: function () { return chunkIIRVZSP2_cjs.MigrationService; }
|
|
3231
5079
|
});
|
|
3232
5080
|
Object.defineProperty(exports, "renderFilterBar", {
|
|
3233
5081
|
enumerable: true,
|
|
3234
|
-
get: function () { return
|
|
5082
|
+
get: function () { return chunk63K7XXRX_cjs.renderFilterBar; }
|
|
3235
5083
|
});
|
|
3236
5084
|
Object.defineProperty(exports, "getConfirmationDialogScript", {
|
|
3237
5085
|
enumerable: true,
|
|
3238
|
-
get: function () { return
|
|
5086
|
+
get: function () { return chunkBZC4FYW7_cjs.getConfirmationDialogScript; }
|
|
3239
5087
|
});
|
|
3240
5088
|
Object.defineProperty(exports, "renderAlert", {
|
|
3241
5089
|
enumerable: true,
|
|
3242
|
-
get: function () { return
|
|
5090
|
+
get: function () { return chunkBZC4FYW7_cjs.renderAlert; }
|
|
3243
5091
|
});
|
|
3244
5092
|
Object.defineProperty(exports, "renderConfirmationDialog", {
|
|
3245
5093
|
enumerable: true,
|
|
3246
|
-
get: function () { return
|
|
5094
|
+
get: function () { return chunkBZC4FYW7_cjs.renderConfirmationDialog; }
|
|
3247
5095
|
});
|
|
3248
5096
|
Object.defineProperty(exports, "renderForm", {
|
|
3249
5097
|
enumerable: true,
|
|
3250
|
-
get: function () { return
|
|
5098
|
+
get: function () { return chunkBZC4FYW7_cjs.renderForm; }
|
|
3251
5099
|
});
|
|
3252
5100
|
Object.defineProperty(exports, "renderFormField", {
|
|
3253
5101
|
enumerable: true,
|
|
3254
|
-
get: function () { return
|
|
5102
|
+
get: function () { return chunkBZC4FYW7_cjs.renderFormField; }
|
|
3255
5103
|
});
|
|
3256
5104
|
Object.defineProperty(exports, "renderPagination", {
|
|
3257
5105
|
enumerable: true,
|
|
3258
|
-
get: function () { return
|
|
5106
|
+
get: function () { return chunkBZC4FYW7_cjs.renderPagination; }
|
|
3259
5107
|
});
|
|
3260
5108
|
Object.defineProperty(exports, "renderTable", {
|
|
3261
5109
|
enumerable: true,
|
|
3262
|
-
get: function () { return
|
|
5110
|
+
get: function () { return chunkBZC4FYW7_cjs.renderTable; }
|
|
3263
5111
|
});
|
|
3264
5112
|
Object.defineProperty(exports, "HookSystemImpl", {
|
|
3265
5113
|
enumerable: true,
|
|
3266
|
-
get: function () { return
|
|
5114
|
+
get: function () { return chunkY72M3MVX_cjs.HookSystemImpl; }
|
|
3267
5115
|
});
|
|
3268
5116
|
Object.defineProperty(exports, "HookUtils", {
|
|
3269
5117
|
enumerable: true,
|
|
3270
|
-
get: function () { return
|
|
5118
|
+
get: function () { return chunkY72M3MVX_cjs.HookUtils; }
|
|
3271
5119
|
});
|
|
3272
5120
|
Object.defineProperty(exports, "PluginManagerClass", {
|
|
3273
5121
|
enumerable: true,
|
|
3274
|
-
get: function () { return
|
|
5122
|
+
get: function () { return chunkY72M3MVX_cjs.PluginManager; }
|
|
3275
5123
|
});
|
|
3276
5124
|
Object.defineProperty(exports, "PluginRegistryImpl", {
|
|
3277
5125
|
enumerable: true,
|
|
3278
|
-
get: function () { return
|
|
5126
|
+
get: function () { return chunkY72M3MVX_cjs.PluginRegistryImpl; }
|
|
3279
5127
|
});
|
|
3280
5128
|
Object.defineProperty(exports, "PluginValidatorClass", {
|
|
3281
5129
|
enumerable: true,
|
|
3282
|
-
get: function () { return
|
|
5130
|
+
get: function () { return chunkY72M3MVX_cjs.PluginValidator; }
|
|
3283
5131
|
});
|
|
3284
5132
|
Object.defineProperty(exports, "ScopedHookSystemClass", {
|
|
3285
5133
|
enumerable: true,
|
|
3286
|
-
get: function () { return
|
|
5134
|
+
get: function () { return chunkY72M3MVX_cjs.ScopedHookSystem; }
|
|
5135
|
+
});
|
|
5136
|
+
Object.defineProperty(exports, "PluginBuilder", {
|
|
5137
|
+
enumerable: true,
|
|
5138
|
+
get: function () { return chunkYHW27CBV_cjs.PluginBuilder; }
|
|
5139
|
+
});
|
|
5140
|
+
Object.defineProperty(exports, "PluginHelpers", {
|
|
5141
|
+
enumerable: true,
|
|
5142
|
+
get: function () { return chunkYHW27CBV_cjs.PluginHelpers; }
|
|
3287
5143
|
});
|
|
3288
5144
|
Object.defineProperty(exports, "QueryFilterBuilder", {
|
|
3289
5145
|
enumerable: true,
|
|
3290
|
-
get: function () { return
|
|
5146
|
+
get: function () { return chunkYMTTGHEK_cjs.QueryFilterBuilder; }
|
|
3291
5147
|
});
|
|
3292
5148
|
Object.defineProperty(exports, "SONICJS_VERSION", {
|
|
3293
5149
|
enumerable: true,
|
|
3294
|
-
get: function () { return
|
|
5150
|
+
get: function () { return chunkYMTTGHEK_cjs.SONICJS_VERSION; }
|
|
3295
5151
|
});
|
|
3296
5152
|
Object.defineProperty(exports, "TemplateRenderer", {
|
|
3297
5153
|
enumerable: true,
|
|
3298
|
-
get: function () { return
|
|
5154
|
+
get: function () { return chunkYMTTGHEK_cjs.TemplateRenderer; }
|
|
3299
5155
|
});
|
|
3300
5156
|
Object.defineProperty(exports, "buildQuery", {
|
|
3301
5157
|
enumerable: true,
|
|
3302
|
-
get: function () { return
|
|
5158
|
+
get: function () { return chunkYMTTGHEK_cjs.buildQuery; }
|
|
3303
5159
|
});
|
|
3304
5160
|
Object.defineProperty(exports, "escapeHtml", {
|
|
3305
5161
|
enumerable: true,
|
|
3306
|
-
get: function () { return
|
|
5162
|
+
get: function () { return chunkYMTTGHEK_cjs.escapeHtml; }
|
|
3307
5163
|
});
|
|
3308
5164
|
Object.defineProperty(exports, "getCoreVersion", {
|
|
3309
5165
|
enumerable: true,
|
|
3310
|
-
get: function () { return
|
|
5166
|
+
get: function () { return chunkYMTTGHEK_cjs.getCoreVersion; }
|
|
3311
5167
|
});
|
|
3312
5168
|
Object.defineProperty(exports, "renderTemplate", {
|
|
3313
5169
|
enumerable: true,
|
|
3314
|
-
get: function () { return
|
|
5170
|
+
get: function () { return chunkYMTTGHEK_cjs.renderTemplate; }
|
|
3315
5171
|
});
|
|
3316
5172
|
Object.defineProperty(exports, "sanitizeInput", {
|
|
3317
5173
|
enumerable: true,
|
|
3318
|
-
get: function () { return
|
|
5174
|
+
get: function () { return chunkYMTTGHEK_cjs.sanitizeInput; }
|
|
3319
5175
|
});
|
|
3320
5176
|
Object.defineProperty(exports, "sanitizeObject", {
|
|
3321
5177
|
enumerable: true,
|
|
3322
|
-
get: function () { return
|
|
5178
|
+
get: function () { return chunkYMTTGHEK_cjs.sanitizeObject; }
|
|
3323
5179
|
});
|
|
3324
5180
|
Object.defineProperty(exports, "templateRenderer", {
|
|
3325
5181
|
enumerable: true,
|
|
3326
|
-
get: function () { return
|
|
5182
|
+
get: function () { return chunkYMTTGHEK_cjs.templateRenderer; }
|
|
3327
5183
|
});
|
|
3328
5184
|
Object.defineProperty(exports, "metricsTracker", {
|
|
3329
5185
|
enumerable: true,
|