@sonicjs-cms/core 3.0.0-beta.20 → 3.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-RDH6PUOP.js → chunk-4LVVMVQI.js} +2 -2
- package/dist/{chunk-RDH6PUOP.js.map → chunk-4LVVMVQI.js.map} +1 -1
- package/dist/{chunk-WCA4VENG.js → chunk-4UYJPHSV.js} +5 -5
- package/dist/{chunk-WCA4VENG.js.map → chunk-4UYJPHSV.js.map} +1 -1
- package/dist/{chunk-DSXEVMOM.js → chunk-5U75XP74.js} +2 -2
- package/dist/{chunk-DSXEVMOM.js.map → chunk-5U75XP74.js.map} +1 -1
- package/dist/{chunk-DTGCXWWM.cjs → chunk-5UXU3LP4.cjs} +10 -10
- package/dist/{chunk-DTGCXWWM.cjs.map → chunk-5UXU3LP4.cjs.map} +1 -1
- package/dist/{chunk-GMZ5TGYP.js → chunk-7OTOXWEB.js} +3 -3
- package/dist/{chunk-GMZ5TGYP.js.map → chunk-7OTOXWEB.js.map} +1 -1
- package/dist/{chunk-3E7IROND.cjs → chunk-7RUW244J.cjs} +217 -208
- package/dist/chunk-7RUW244J.cjs.map +1 -0
- package/dist/{chunk-5RSB5SEK.cjs → chunk-A4KUJ3LD.cjs} +4 -4
- package/dist/{chunk-5RSB5SEK.cjs.map → chunk-A4KUJ3LD.cjs.map} +1 -1
- package/dist/{chunk-AQJEMVAY.cjs → chunk-AOUVUF32.cjs} +3 -3
- package/dist/{chunk-AQJEMVAY.cjs.map → chunk-AOUVUF32.cjs.map} +1 -1
- package/dist/{chunk-4SHNHNRP.cjs → chunk-FCLKX2OS.cjs} +15 -15
- package/dist/{chunk-4SHNHNRP.cjs.map → chunk-FCLKX2OS.cjs.map} +1 -1
- package/dist/{chunk-MYQ7KZUQ.js → chunk-GICS5AIO.js} +4 -4
- package/dist/{chunk-MYQ7KZUQ.js.map → chunk-GICS5AIO.js.map} +1 -1
- package/dist/{chunk-W53J3BJ2.js → chunk-JEX3UPZL.js} +3 -3
- package/dist/{chunk-W53J3BJ2.js.map → chunk-JEX3UPZL.js.map} +1 -1
- package/dist/{chunk-5ARIHAGN.cjs → chunk-KVBHB7QP.cjs} +2 -2
- package/dist/{chunk-5ARIHAGN.cjs.map → chunk-KVBHB7QP.cjs.map} +1 -1
- package/dist/{chunk-TQG5FNNR.cjs → chunk-OJC4G5JE.cjs} +2 -2
- package/dist/{chunk-TQG5FNNR.cjs.map → chunk-OJC4G5JE.cjs.map} +1 -1
- package/dist/{chunk-5UCMLBRQ.js → chunk-ZP77LTN4.js} +24 -15
- package/dist/chunk-ZP77LTN4.js.map +1 -0
- package/dist/index.cjs +450 -321
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +204 -75
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +33 -33
- package/dist/middleware.js +4 -4
- package/dist/migrations-HMX5SPN2.js +4 -0
- package/dist/{migrations-HZJ6U6SX.js.map → migrations-HMX5SPN2.js.map} +1 -1
- package/dist/migrations-V73ARYV5.cjs +13 -0
- package/dist/{migrations-VQAFXMU7.cjs.map → migrations-V73ARYV5.cjs.map} +1 -1
- package/dist/plugins.cjs +40 -40
- package/dist/plugins.js +3 -3
- package/dist/routes.cjs +26 -26
- package/dist/routes.js +6 -6
- package/dist/services.cjs +18 -18
- package/dist/services.js +2 -2
- package/dist/utils.cjs +3 -3
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-3E7IROND.cjs.map +0 -1
- package/dist/chunk-5UCMLBRQ.js.map +0 -1
- package/dist/migrations-HZJ6U6SX.js +0 -4
- package/dist/migrations-VQAFXMU7.cjs +0 -13
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createAuth } from './chunk-SOZUG52O.js';
|
|
2
|
-
import { getEmailService, hasEmailService, setEmailService, invalidateVariablesCache, resolveVariablesInObject, resolveDocScope, denyIfNotAllowed, DocumentRepository, dispatchHookEvent, getCustomData, createDocumentSchema, renderTable, getVariablesCached, setVariablesCache, TenantService, isValidMemberRole, renderConfirmationDialog, getConfirmationDialogScript, VALID_MEMBER_ROLES, userProfilesPlugin, lexicalEditorPlugin, api_media_default, api_system_default, apiDocumentsRoutes, api_default, adminDocumentsRoutes, admin_api_default, adminCollectionsRoutes, adminSettingsRoutes, router2, admin_content_default, adminMediaRoutes, router, adminPluginRoutes, adminLogsRoutes, userRoutes, auth_default, test_cleanup_default } from './chunk-
|
|
3
|
-
export { DocumentRepository, ROUTES_INFO, admin_api_default as adminApiRoutes, adminCheckboxRoutes, adminCollectionsRoutes, admin_content_default as adminContentRoutes, router as adminDashboardRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, userRoutes as adminUsersRoutes, api_content_crud_default as apiContentCrudRoutes, api_media_default as apiMediaRoutes, api_default as apiRoutes, api_system_default as apiSystemRoutes, auth_default as authRoutes, createUserProfilesPlugin, defineUserProfile, getUserProfileConfig, userProfilesPlugin } from './chunk-
|
|
2
|
+
import { getEmailService, hasEmailService, setEmailService, invalidateVariablesCache, resolveVariablesInObject, resolveDocScope, denyIfNotAllowed, DocumentRepository, dispatchHookEvent, getCustomData, createDocumentSchema, renderTable, getVariablesCached, setVariablesCache, TenantService, isValidMemberRole, renderConfirmationDialog, getConfirmationDialogScript, VALID_MEMBER_ROLES, userProfilesPlugin, lexicalEditorPlugin, api_media_default, api_system_default, apiDocumentsRoutes, api_default, adminDocumentsRoutes, admin_api_default, adminCollectionsRoutes, adminSettingsRoutes, router2, admin_content_default, adminMediaRoutes, router, adminPluginRoutes, adminLogsRoutes, userRoutes, auth_default, test_cleanup_default } from './chunk-ZP77LTN4.js';
|
|
3
|
+
export { DocumentRepository, ROUTES_INFO, admin_api_default as adminApiRoutes, adminCheckboxRoutes, adminCollectionsRoutes, admin_content_default as adminContentRoutes, router as adminDashboardRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, userRoutes as adminUsersRoutes, api_content_crud_default as apiContentCrudRoutes, api_media_default as apiMediaRoutes, api_default as apiRoutes, api_system_default as apiSystemRoutes, auth_default as authRoutes, createUserProfilesPlugin, defineUserProfile, getUserProfileConfig, userProfilesPlugin } from './chunk-ZP77LTN4.js';
|
|
4
4
|
import './chunk-VVYVFCML.js';
|
|
5
5
|
import './chunk-RMRJGMDE.js';
|
|
6
6
|
import { SettingsService, setAppInstance } from './chunk-2XAQTPAE.js';
|
|
@@ -8,30 +8,30 @@ export { Logger, getLogger, initLogger } from './chunk-2XAQTPAE.js';
|
|
|
8
8
|
import { getAllCacheStats, CACHE_CONFIGS, getCacheService, clearAllCaches, parseCacheKey } from './chunk-K25XHMM3.js';
|
|
9
9
|
import { schema_exports } from './chunk-AI663NBO.js';
|
|
10
10
|
export { apiTokens, content, contentVersions, insertContentSchema, insertLogConfigSchema, insertMediaSchema, insertPluginActivityLogSchema, insertPluginAssetSchema, insertPluginHookSchema, insertPluginRouteSchema, insertPluginSchema, insertSystemLogSchema, insertUserSchema, insertWorkflowHistorySchema, logConfig, media, pluginActivityLog, pluginAssets, pluginHooks, pluginRoutes, plugins, selectContentSchema, selectLogConfigSchema, selectMediaSchema, selectPluginActivityLogSchema, selectPluginAssetSchema, selectPluginHookSchema, selectPluginRouteSchema, selectPluginSchema, selectSystemLogSchema, selectUserSchema, selectWorkflowHistorySchema, systemLogs, users, workflowHistory } from './chunk-AI663NBO.js';
|
|
11
|
-
import { requireAuth, DocumentTypeRegistry, requireRbac, requireRole, getJwtExpirySecondsFromDb, AuthManager, metricsMiddleware, bootstrapMiddleware, securityHeadersMiddleware, csrfProtection } from './chunk-
|
|
12
|
-
export { AuthManager, PermissionManager, bootstrapDocumentTypes, bootstrapMiddleware, cacheHeaders, compressionMiddleware, detailedLoggingMiddleware, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, optionalAuth, performanceLoggingMiddleware, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeadersMiddleware as securityHeaders, securityLoggingMiddleware } from './chunk-
|
|
11
|
+
import { requireAuth, DocumentTypeRegistry, requireRbac, requireRole, getJwtExpirySecondsFromDb, AuthManager, metricsMiddleware, bootstrapMiddleware, securityHeadersMiddleware, csrfProtection } from './chunk-4UYJPHSV.js';
|
|
12
|
+
export { AuthManager, PermissionManager, bootstrapDocumentTypes, bootstrapMiddleware, cacheHeaders, compressionMiddleware, detailedLoggingMiddleware, getActivePlugins, isPluginActive, logActivity, loggingMiddleware, optionalAuth, performanceLoggingMiddleware, requireActivePlugin, requireActivePlugins, requireAnyPermission, requireAuth, requirePermission, requireRole, securityHeadersMiddleware as securityHeaders, securityLoggingMiddleware } from './chunk-4UYJPHSV.js';
|
|
13
13
|
import { RbacService, DocumentsService } from './chunk-3TMTYJAJ.js';
|
|
14
14
|
export { DocumentsService, RbacService } from './chunk-3TMTYJAJ.js';
|
|
15
15
|
import { nanoid } from './chunk-UWUP2VMT.js';
|
|
16
|
-
import { PluginService, PLUGIN_REGISTRY, invalidateTenantCache, getCollectionRegistry, TENANT_COOKIE, MULTI_TENANT_PLUGIN_ID, collectionRecordToRow, tenantMiddleware } from './chunk-
|
|
17
|
-
export { PluginBootstrapService, PluginService as PluginServiceClass, getAvailableCollectionNames, loadCollectionConfig, loadCollectionConfigs, registerCollections, validateCollectionConfig } from './chunk-
|
|
18
|
-
export { MigrationService } from './chunk-
|
|
16
|
+
import { PluginService, PLUGIN_REGISTRY, invalidateTenantCache, getCollectionRegistry, TENANT_COOKIE, MULTI_TENANT_PLUGIN_ID, collectionRecordToRow, tenantMiddleware } from './chunk-4LVVMVQI.js';
|
|
17
|
+
export { PluginBootstrapService, PluginService as PluginServiceClass, getAvailableCollectionNames, loadCollectionConfig, loadCollectionConfigs, registerCollections, validateCollectionConfig } from './chunk-4LVVMVQI.js';
|
|
18
|
+
export { MigrationService } from './chunk-5U75XP74.js';
|
|
19
19
|
export { renderFilterBar } from './chunk-JQISFW6U.js';
|
|
20
20
|
import { renderAdminLayout } from './chunk-OBA2RYZN.js';
|
|
21
21
|
export { getConfirmationDialogScript, renderAlert, renderConfirmationDialog, renderForm, renderFormField, renderPagination, renderTable } from './chunk-OBA2RYZN.js';
|
|
22
22
|
import { init_admin_layout_catalyst_template, renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
|
|
23
23
|
export { renderAdminLayoutCatalyst } from './chunk-5V62WT6M.js';
|
|
24
|
-
import { TurnstileService, HookSystemImpl, createPluginWirer, registerPluginRoutes, setPluginMenu, resolvePluginMenuItems } from './chunk-
|
|
25
|
-
export { HookSystemImpl, HookUtils, PluginDependencyCycleError, PluginManager as PluginManagerClass, PluginRegistryImpl, PluginValidator as PluginValidatorClass, RegisterPluginsError, ScopedHookSystem as ScopedHookSystemClass, collectCronSchedules, collectCrons, createScheduledHandler, dispatchCronTick, getPluginMenu, registerPlugins, resetPluginMenu, resolvePluginMenuItems, setPluginMenu, topoSort } from './chunk-
|
|
26
|
-
import { definePlugin, setPluginDefinitions } from './chunk-
|
|
27
|
-
export { applySchemaDefaults, definePlugin, isDefinedPlugin, parseConfigSchema, parseFormDataToSettings, renderSchemaFields } from './chunk-
|
|
24
|
+
import { TurnstileService, HookSystemImpl, createPluginWirer, registerPluginRoutes, setPluginMenu, resolvePluginMenuItems } from './chunk-GICS5AIO.js';
|
|
25
|
+
export { HookSystemImpl, HookUtils, PluginDependencyCycleError, PluginManager as PluginManagerClass, PluginRegistryImpl, PluginValidator as PluginValidatorClass, RegisterPluginsError, ScopedHookSystem as ScopedHookSystemClass, collectCronSchedules, collectCrons, createScheduledHandler, dispatchCronTick, getPluginMenu, registerPlugins, resetPluginMenu, resolvePluginMenuItems, setPluginMenu, topoSort } from './chunk-GICS5AIO.js';
|
|
26
|
+
import { definePlugin, setPluginDefinitions } from './chunk-7OTOXWEB.js';
|
|
27
|
+
export { applySchemaDefaults, definePlugin, isDefinedPlugin, parseConfigSchema, parseFormDataToSettings, renderSchemaFields } from './chunk-7OTOXWEB.js';
|
|
28
28
|
import { setHookSystem } from './chunk-RNZFGN4R.js';
|
|
29
29
|
export { getHookSystem, getTypedHooks, hasHookSystem, resetHookSystem, setHookSystem } from './chunk-RNZFGN4R.js';
|
|
30
30
|
export { QueryFilterBuilder, TemplateRenderer, buildQuery, renderTemplate, templateRenderer } from './chunk-QZGABF2M.js';
|
|
31
31
|
export { metricsTracker } from './chunk-FICTAGD4.js';
|
|
32
32
|
import './chunk-X7ZAEI5S.js';
|
|
33
|
-
import { getCoreVersion, package_default } from './chunk-
|
|
34
|
-
export { SONICJS_VERSION, getCoreVersion } from './chunk-
|
|
33
|
+
import { getCoreVersion, package_default } from './chunk-JEX3UPZL.js';
|
|
34
|
+
export { SONICJS_VERSION, getCoreVersion } from './chunk-JEX3UPZL.js';
|
|
35
35
|
import { escapeHtml, sanitizeInput } from './chunk-TQABQWOP.js';
|
|
36
36
|
export { escapeHtml, sanitizeInput, sanitizeObject } from './chunk-TQABQWOP.js';
|
|
37
37
|
export { HOOKS } from './chunk-QXOZI5Q2.js';
|
|
@@ -193,9 +193,9 @@ var DatabaseToolsService = class {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
/**
|
|
196
|
-
* Get table data with optional pagination and
|
|
196
|
+
* Get table data with optional pagination, sorting, and search
|
|
197
197
|
*/
|
|
198
|
-
async getTableData(tableName, limit = 100, offset = 0, sortColumn, sortDirection = "asc") {
|
|
198
|
+
async getTableData(tableName, limit = 100, offset = 0, sortColumn, sortDirection = "asc", search) {
|
|
199
199
|
try {
|
|
200
200
|
const tables = await this.getTables();
|
|
201
201
|
if (!tables.includes(tableName)) {
|
|
@@ -206,14 +206,26 @@ var DatabaseToolsService = class {
|
|
|
206
206
|
if (sortColumn && !columns.includes(sortColumn)) {
|
|
207
207
|
sortColumn = void 0;
|
|
208
208
|
}
|
|
209
|
-
const
|
|
209
|
+
const searchTerm = search?.trim();
|
|
210
|
+
let whereClause = "";
|
|
211
|
+
let bindings = [];
|
|
212
|
+
if (searchTerm) {
|
|
213
|
+
const conditions = columns.map(() => `CAST(? AS TEXT) IS NOT NULL AND CAST(? AS TEXT) LIKE ?`).join(" OR ");
|
|
214
|
+
const colConditions = columns.map((col) => `CAST("${col}" AS TEXT) LIKE ?`).join(" OR ");
|
|
215
|
+
whereClause = ` WHERE (${colConditions})`;
|
|
216
|
+
bindings = columns.map(() => `%${searchTerm}%`);
|
|
217
|
+
}
|
|
218
|
+
const countQuery = `SELECT COUNT(*) as count FROM "${tableName}"${whereClause}`;
|
|
219
|
+
const countStmt = this.db.prepare(countQuery);
|
|
220
|
+
const countResult = bindings.length > 0 ? await countStmt.bind(...bindings).first() : await countStmt.first();
|
|
210
221
|
const totalRows = countResult?.count || 0;
|
|
211
|
-
let query = `SELECT * FROM ${tableName}`;
|
|
222
|
+
let query = `SELECT * FROM "${tableName}"${whereClause}`;
|
|
212
223
|
if (sortColumn) {
|
|
213
|
-
query += ` ORDER BY ${sortColumn} ${sortDirection.toUpperCase()}`;
|
|
224
|
+
query += ` ORDER BY "${sortColumn}" ${sortDirection.toUpperCase()}`;
|
|
214
225
|
}
|
|
215
226
|
query += ` LIMIT ${limit} OFFSET ${offset}`;
|
|
216
|
-
const
|
|
227
|
+
const dataStmt = this.db.prepare(query);
|
|
228
|
+
const dataResult = bindings.length > 0 ? await dataStmt.bind(...bindings).all() : await dataStmt.all();
|
|
217
229
|
return {
|
|
218
230
|
tableName,
|
|
219
231
|
columns,
|
|
@@ -317,6 +329,43 @@ function renderDatabaseTablePage(data) {
|
|
|
317
329
|
</div>
|
|
318
330
|
</div>
|
|
319
331
|
|
|
332
|
+
<!-- Search bar -->
|
|
333
|
+
<div class="flex items-center gap-3">
|
|
334
|
+
<div class="relative flex-1 max-w-xl">
|
|
335
|
+
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
|
336
|
+
<svg class="h-4 w-4 text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
337
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0"/>
|
|
338
|
+
</svg>
|
|
339
|
+
</div>
|
|
340
|
+
<input
|
|
341
|
+
id="searchInput"
|
|
342
|
+
type="text"
|
|
343
|
+
placeholder="Search all columns including JSON data..."
|
|
344
|
+
value="${escapeHtml2(data.search || "")}"
|
|
345
|
+
class="block w-full rounded-lg border-0 py-2.5 pl-10 pr-10 text-sm text-zinc-900 dark:text-white bg-white dark:bg-zinc-800 ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600"
|
|
346
|
+
onkeydown="if(event.key==='Enter') doSearch()"
|
|
347
|
+
/>
|
|
348
|
+
${data.search ? `
|
|
349
|
+
<button
|
|
350
|
+
onclick="clearSearch()"
|
|
351
|
+
class="absolute inset-y-0 right-0 flex items-center pr-3 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200"
|
|
352
|
+
title="Clear search"
|
|
353
|
+
>
|
|
354
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
355
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
356
|
+
</svg>
|
|
357
|
+
</button>
|
|
358
|
+
` : ""}
|
|
359
|
+
</div>
|
|
360
|
+
<button
|
|
361
|
+
onclick="doSearch()"
|
|
362
|
+
class="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-indigo-500 transition-colors shadow-sm"
|
|
363
|
+
>
|
|
364
|
+
Search
|
|
365
|
+
</button>
|
|
366
|
+
${data.search ? `<span class="text-sm text-zinc-500 dark:text-zinc-400">Filtered: <strong class="text-zinc-900 dark:text-white">${escapeHtml2(data.search)}</strong></span>` : ""}
|
|
367
|
+
</div>
|
|
368
|
+
|
|
320
369
|
<!-- Table Card -->
|
|
321
370
|
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 overflow-hidden">
|
|
322
371
|
<!-- Table -->
|
|
@@ -351,7 +400,7 @@ function renderDatabaseTablePage(data) {
|
|
|
351
400
|
<tr class="${idx % 2 === 0 ? "bg-white dark:bg-zinc-900" : "bg-zinc-50 dark:bg-zinc-900/50"}">
|
|
352
401
|
${data.columns.map((col) => `
|
|
353
402
|
<td class="px-4 py-3 text-sm text-zinc-700 dark:text-zinc-300 whitespace-nowrap max-w-xs overflow-hidden text-ellipsis" title="${escapeHtml2(String(row[col] ?? ""))}">
|
|
354
|
-
${formatCellValue(row[col])}
|
|
403
|
+
${formatCellValue(row[col], col)}
|
|
355
404
|
</td>
|
|
356
405
|
`).join("")}
|
|
357
406
|
</tr>
|
|
@@ -427,79 +476,143 @@ function renderDatabaseTablePage(data) {
|
|
|
427
476
|
</div>
|
|
428
477
|
</div>
|
|
429
478
|
|
|
479
|
+
<!-- JSON Viewer Modal -->
|
|
480
|
+
<div id="jsonModal" class="fixed inset-0 z-50 hidden" role="dialog" aria-modal="true">
|
|
481
|
+
<div class="fixed inset-0 bg-black/60 backdrop-blur-sm" onclick="closeJsonModal()"></div>
|
|
482
|
+
<div class="fixed inset-0 flex items-center justify-center p-4 pointer-events-none">
|
|
483
|
+
<div class="pointer-events-auto relative w-full max-w-4xl max-h-[85vh] flex flex-col rounded-xl bg-white dark:bg-zinc-900 shadow-2xl ring-1 ring-zinc-950/10 dark:ring-white/10">
|
|
484
|
+
<div class="flex items-center justify-between px-6 py-4 border-b border-zinc-200 dark:border-zinc-700">
|
|
485
|
+
<div>
|
|
486
|
+
<h3 id="jsonModalTitle" class="text-base font-semibold text-zinc-900 dark:text-white">Field Value</h3>
|
|
487
|
+
<p id="jsonModalSubtitle" class="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5"></p>
|
|
488
|
+
</div>
|
|
489
|
+
<div class="flex items-center gap-2">
|
|
490
|
+
<button onclick="copyJsonValue()" class="inline-flex items-center gap-1.5 rounded-lg bg-zinc-100 dark:bg-zinc-800 px-3 py-1.5 text-xs font-medium text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors">
|
|
491
|
+
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
492
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
|
|
493
|
+
</svg>
|
|
494
|
+
Copy
|
|
495
|
+
</button>
|
|
496
|
+
<button onclick="closeJsonModal()" class="rounded-lg p-1.5 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors">
|
|
497
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
498
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
|
499
|
+
</svg>
|
|
500
|
+
</button>
|
|
501
|
+
</div>
|
|
502
|
+
</div>
|
|
503
|
+
<div class="flex-1 overflow-auto p-6">
|
|
504
|
+
<pre id="jsonModalContent" class="text-sm font-mono text-zinc-800 dark:text-zinc-200 whitespace-pre-wrap break-words leading-relaxed"></pre>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
508
|
+
</div>
|
|
509
|
+
|
|
430
510
|
<script>
|
|
431
511
|
const currentTableName = '${data.tableName}';
|
|
432
512
|
let currentPage = ${data.currentPage};
|
|
433
513
|
let currentPageSize = ${data.pageSize};
|
|
434
514
|
let currentSort = '${data.sortColumn || ""}';
|
|
435
515
|
let currentSortDir = '${data.sortDirection || "asc"}';
|
|
516
|
+
let currentSearch = ${JSON.stringify(data.search || "")};
|
|
517
|
+
|
|
518
|
+
function buildParams(overrides = {}) {
|
|
519
|
+
const params = new URLSearchParams();
|
|
520
|
+
const p = { page: currentPage, pageSize: currentPageSize, sort: currentSort, dir: currentSortDir, search: currentSearch, ...overrides };
|
|
521
|
+
if (p.page) params.set('page', p.page);
|
|
522
|
+
if (p.pageSize) params.set('pageSize', p.pageSize);
|
|
523
|
+
if (p.sort) { params.set('sort', p.sort); params.set('dir', p.dir || 'asc'); }
|
|
524
|
+
if (p.search) params.set('search', p.search);
|
|
525
|
+
return params;
|
|
526
|
+
}
|
|
436
527
|
|
|
437
528
|
function goToPage(page) {
|
|
438
529
|
if (page < 1 || page > ${totalPages}) return;
|
|
439
|
-
|
|
440
|
-
params.set('page', page);
|
|
441
|
-
params.set('pageSize', currentPageSize);
|
|
442
|
-
if (currentSort) {
|
|
443
|
-
params.set('sort', currentSort);
|
|
444
|
-
params.set('dir', currentSortDir);
|
|
445
|
-
}
|
|
446
|
-
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${params}\`;
|
|
530
|
+
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page })}\`;
|
|
447
531
|
}
|
|
448
532
|
|
|
449
533
|
function sortTable(column) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
newDir = 'desc';
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const params = new URLSearchParams();
|
|
456
|
-
params.set('page', '1');
|
|
457
|
-
params.set('pageSize', currentPageSize);
|
|
458
|
-
params.set('sort', column);
|
|
459
|
-
params.set('dir', newDir);
|
|
460
|
-
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${params}\`;
|
|
534
|
+
const newDir = (currentSort === column && currentSortDir === 'asc') ? 'desc' : 'asc';
|
|
535
|
+
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, sort: column, dir: newDir })}\`;
|
|
461
536
|
}
|
|
462
537
|
|
|
463
538
|
function changePageSize(newSize) {
|
|
464
|
-
|
|
465
|
-
params.set('page', '1');
|
|
466
|
-
params.set('pageSize', newSize);
|
|
467
|
-
if (currentSort) {
|
|
468
|
-
params.set('sort', currentSort);
|
|
469
|
-
params.set('dir', currentSortDir);
|
|
470
|
-
}
|
|
471
|
-
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${params}\`;
|
|
539
|
+
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, pageSize: newSize })}\`;
|
|
472
540
|
}
|
|
473
541
|
|
|
474
542
|
function refreshTableData() {
|
|
475
543
|
window.location.reload();
|
|
476
544
|
}
|
|
477
545
|
|
|
478
|
-
function
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
546
|
+
function doSearch() {
|
|
547
|
+
const val = document.getElementById('searchInput').value.trim();
|
|
548
|
+
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, search: val })}\`;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function clearSearch() {
|
|
552
|
+
window.location.href = \`/admin/database-tools/tables/\${currentTableName}?\${buildParams({ page: 1, search: '' })}\`;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// JSON viewer modal
|
|
556
|
+
let _jsonModalValue = '';
|
|
557
|
+
|
|
558
|
+
function openJsonModal(rawValue, colName) {
|
|
559
|
+
// rawValue comes from dataset \u2014 browser already decoded HTML entities, so it's the original string
|
|
560
|
+
_jsonModalValue = rawValue;
|
|
561
|
+
document.getElementById('jsonModalTitle').textContent = colName;
|
|
562
|
+
const el = document.getElementById('jsonModalContent');
|
|
563
|
+
const subtitle = document.getElementById('jsonModalSubtitle');
|
|
564
|
+
try {
|
|
565
|
+
const parsed = JSON.parse(rawValue);
|
|
566
|
+
const pretty = JSON.stringify(parsed, null, 2);
|
|
567
|
+
subtitle.textContent = 'JSON \u2022 ' + rawValue.length + ' chars';
|
|
568
|
+
el.innerHTML = syntaxHighlight(pretty);
|
|
569
|
+
} catch {
|
|
570
|
+
el.textContent = rawValue;
|
|
571
|
+
subtitle.textContent = rawValue.length + ' chars';
|
|
491
572
|
}
|
|
492
|
-
|
|
573
|
+
document.getElementById('jsonModal').classList.remove('hidden');
|
|
574
|
+
document.body.style.overflow = 'hidden';
|
|
493
575
|
}
|
|
494
576
|
|
|
577
|
+
function closeJsonModal() {
|
|
578
|
+
document.getElementById('jsonModal').classList.add('hidden');
|
|
579
|
+
document.body.style.overflow = '';
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function copyJsonValue() {
|
|
583
|
+
navigator.clipboard.writeText(_jsonModalValue).then(() => {
|
|
584
|
+
const btn = event.currentTarget;
|
|
585
|
+
const orig = btn.innerHTML;
|
|
586
|
+
btn.textContent = 'Copied!';
|
|
587
|
+
setTimeout(() => { btn.innerHTML = orig; }, 1500);
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function syntaxHighlight(json) {
|
|
592
|
+
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
593
|
+
return json.replace(/("(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\"])*"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function(match) {
|
|
594
|
+
let cls = 'text-purple-600 dark:text-purple-400'; // number
|
|
595
|
+
if (/^"/.test(match)) {
|
|
596
|
+
if (/:$/.test(match)) {
|
|
597
|
+
cls = 'text-blue-600 dark:text-blue-400 font-medium'; // key
|
|
598
|
+
} else {
|
|
599
|
+
cls = 'text-green-700 dark:text-green-400'; // string value
|
|
600
|
+
}
|
|
601
|
+
} else if (/true|false/.test(match)) {
|
|
602
|
+
cls = 'text-orange-600 dark:text-orange-400';
|
|
603
|
+
} else if (/null/.test(match)) {
|
|
604
|
+
cls = 'text-zinc-400 italic';
|
|
605
|
+
}
|
|
606
|
+
return \`<span class="\${cls}">\${match}</span>\`;
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
document.addEventListener('keydown', (e) => {
|
|
611
|
+
if (e.key === 'Escape') closeJsonModal();
|
|
612
|
+
});
|
|
613
|
+
|
|
495
614
|
function escapeHtml(text) {
|
|
496
|
-
const map = {
|
|
497
|
-
'&': '&',
|
|
498
|
-
'<': '<',
|
|
499
|
-
'>': '>',
|
|
500
|
-
'"': '"',
|
|
501
|
-
"'": '''
|
|
502
|
-
};
|
|
615
|
+
const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
|
503
616
|
return String(text).replace(/[&<>"']/g, m => map[m]);
|
|
504
617
|
}
|
|
505
618
|
</script>
|
|
@@ -566,7 +679,16 @@ function escapeHtml2(text) {
|
|
|
566
679
|
};
|
|
567
680
|
return String(text).replace(/[&<>"']/g, (m) => map[m] || m);
|
|
568
681
|
}
|
|
569
|
-
function
|
|
682
|
+
function isJsonString(str2) {
|
|
683
|
+
const s = str2.trim();
|
|
684
|
+
return (s.startsWith("{") || s.startsWith("[")) && (s.endsWith("}") || s.endsWith("]"));
|
|
685
|
+
}
|
|
686
|
+
function jsonButton(raw, colName) {
|
|
687
|
+
const preview = raw.length > 60 ? escapeHtml2(raw.substring(0, 60)) + "\u2026" : escapeHtml2(raw);
|
|
688
|
+
return `<button data-json-val="${escapeHtml2(raw)}" data-col-name="${escapeHtml2(colName)}" onclick="openJsonModal(this.dataset.jsonVal, this.dataset.colName)" class="text-left text-xs font-mono text-indigo-600 dark:text-indigo-400 hover:underline">${preview}</button>`;
|
|
689
|
+
}
|
|
690
|
+
function formatCellValue(value, colName) {
|
|
691
|
+
const col = colName || "value";
|
|
570
692
|
if (value === null || value === void 0) {
|
|
571
693
|
return '<span class="text-zinc-400 dark:text-zinc-500 italic">null</span>';
|
|
572
694
|
}
|
|
@@ -574,11 +696,15 @@ function formatCellValue(value) {
|
|
|
574
696
|
return `<span class="px-2 py-0.5 rounded text-xs font-medium ${value ? "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400" : "bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-400"}">${value}</span>`;
|
|
575
697
|
}
|
|
576
698
|
if (typeof value === "object") {
|
|
577
|
-
return
|
|
699
|
+
return jsonButton(JSON.stringify(value), col);
|
|
578
700
|
}
|
|
579
701
|
const str2 = String(value);
|
|
580
|
-
if (str2
|
|
581
|
-
return
|
|
702
|
+
if (isJsonString(str2)) {
|
|
703
|
+
return jsonButton(str2, col);
|
|
704
|
+
}
|
|
705
|
+
if (str2.length > 120) {
|
|
706
|
+
const preview = escapeHtml2(str2.substring(0, 120)) + "\u2026";
|
|
707
|
+
return `<button data-json-val="${escapeHtml2(str2)}" data-col-name="${escapeHtml2(col)}" onclick="openJsonModal(this.dataset.jsonVal, this.dataset.colName)" class="text-left text-sm text-zinc-700 dark:text-zinc-300 hover:underline">${preview}</button>`;
|
|
582
708
|
}
|
|
583
709
|
return escapeHtml2(str2);
|
|
584
710
|
}
|
|
@@ -713,9 +839,10 @@ function createDatabaseToolsAdminRoutes() {
|
|
|
713
839
|
const offset = parseInt(c.req.query("offset") || "0");
|
|
714
840
|
const sortColumn = c.req.query("sort");
|
|
715
841
|
const sortDirection = c.req.query("dir") || "asc";
|
|
842
|
+
const search = c.req.query("search");
|
|
716
843
|
const db = c.env.DB;
|
|
717
844
|
const service = new DatabaseToolsService(db);
|
|
718
|
-
const tableData = await service.getTableData(tableName, limit, offset, sortColumn, sortDirection);
|
|
845
|
+
const tableData = await service.getTableData(tableName, limit, offset, sortColumn, sortDirection, search);
|
|
719
846
|
return c.json({
|
|
720
847
|
success: true,
|
|
721
848
|
data: tableData
|
|
@@ -739,10 +866,11 @@ function createDatabaseToolsAdminRoutes() {
|
|
|
739
866
|
const pageSize = parseInt(c.req.query("pageSize") || "20");
|
|
740
867
|
const sortColumn = c.req.query("sort");
|
|
741
868
|
const sortDirection = c.req.query("dir") || "asc";
|
|
869
|
+
const search = c.req.query("search");
|
|
742
870
|
const offset = (page - 1) * pageSize;
|
|
743
871
|
const db = c.env.DB;
|
|
744
872
|
const service = new DatabaseToolsService(db);
|
|
745
|
-
const tableData = await service.getTableData(tableName, pageSize, offset, sortColumn, sortDirection);
|
|
873
|
+
const tableData = await service.getTableData(tableName, pageSize, offset, sortColumn, sortDirection, search);
|
|
746
874
|
const pageData = {
|
|
747
875
|
user: {
|
|
748
876
|
name: user.email.split("@")[0] || "Unknown",
|
|
@@ -756,7 +884,8 @@ function createDatabaseToolsAdminRoutes() {
|
|
|
756
884
|
currentPage: page,
|
|
757
885
|
pageSize,
|
|
758
886
|
sortColumn,
|
|
759
|
-
sortDirection
|
|
887
|
+
sortDirection,
|
|
888
|
+
search
|
|
760
889
|
};
|
|
761
890
|
return c.html(renderDatabaseTablePage(pageData));
|
|
762
891
|
} catch (error) {
|