@jagilber-org/index-server 1.22.0 → 1.26.1
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/CHANGELOG.md +87 -2
- package/CODE_OF_CONDUCT.md +2 -0
- package/CONTRIBUTING.md +32 -2
- package/README.md +83 -20
- package/SECURITY.md +17 -5
- package/dist/config/dashboardConfig.d.ts +3 -0
- package/dist/config/dashboardConfig.js +3 -0
- package/dist/config/defaultValues.d.ts +1 -1
- package/dist/config/defaultValues.js +1 -1
- package/dist/config/featureConfig.d.ts +2 -0
- package/dist/config/featureConfig.js +6 -1
- package/dist/config/runtimeConfig.d.ts +1 -1
- package/dist/config/runtimeConfig.js +8 -9
- package/dist/dashboard/client/admin.html +173 -54
- package/dist/dashboard/client/css/admin.css +151 -0
- package/dist/dashboard/client/js/admin.auth.js +25 -11
- package/dist/dashboard/client/js/admin.config.js +1 -1
- package/dist/dashboard/client/js/admin.feedback.js +328 -0
- package/dist/dashboard/client/js/admin.graph.js +120 -18
- package/dist/dashboard/client/js/admin.instructions.js +27 -13
- package/dist/dashboard/client/js/admin.logs.js +1 -5
- package/dist/dashboard/client/js/admin.maintenance.js +53 -8
- package/dist/dashboard/client/js/admin.messaging.js +1 -4
- package/dist/dashboard/client/js/admin.overview.js +5 -1
- package/dist/dashboard/client/js/admin.sessions.js +1 -1
- package/dist/dashboard/client/js/admin.utils.js +43 -1
- package/dist/dashboard/client/js/mermaid.min.js +813 -537
- package/dist/dashboard/export/DataExporter.js +2 -1
- package/dist/dashboard/server/AdminPanel.d.ts +3 -0
- package/dist/dashboard/server/AdminPanel.js +132 -35
- package/dist/dashboard/server/ApiRoutes.js +40 -9
- package/dist/dashboard/server/DashboardServer.js +1 -1
- package/dist/dashboard/server/FileMetricsStorage.d.ts +19 -0
- package/dist/dashboard/server/FileMetricsStorage.js +52 -5
- package/dist/dashboard/server/HttpTransport.js +6 -0
- package/dist/dashboard/server/InstanceManager.js +7 -2
- package/dist/dashboard/server/KnowledgeStore.js +7 -2
- package/dist/dashboard/server/MetricsCollector.d.ts +16 -0
- package/dist/dashboard/server/MetricsCollector.js +113 -17
- package/dist/dashboard/server/legacyDashboardHtml.js +7 -2
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.d.ts +1 -1
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.js +8 -3
- package/dist/dashboard/server/routes/admin.feedback.routes.d.ts +15 -0
- package/dist/dashboard/server/routes/admin.feedback.routes.js +188 -0
- package/dist/dashboard/server/routes/admin.routes.js +35 -27
- package/dist/dashboard/server/routes/alerts.routes.js +4 -3
- package/dist/dashboard/server/routes/api.feedback.routes.js +2 -1
- package/dist/dashboard/server/routes/api.usage.routes.js +8 -7
- package/dist/dashboard/server/routes/embeddings.routes.d.ts +2 -1
- package/dist/dashboard/server/routes/embeddings.routes.js +18 -9
- package/dist/dashboard/server/routes/graph.routes.js +10 -13
- package/dist/dashboard/server/routes/index.d.ts +1 -0
- package/dist/dashboard/server/routes/index.js +74 -39
- package/dist/dashboard/server/routes/instances.routes.js +2 -1
- package/dist/dashboard/server/routes/instructions.routes.js +46 -27
- package/dist/dashboard/server/routes/knowledge.routes.js +4 -3
- package/dist/dashboard/server/routes/logs.routes.js +5 -4
- package/dist/dashboard/server/routes/messaging.routes.js +15 -14
- package/dist/dashboard/server/routes/metrics.routes.js +14 -13
- package/dist/dashboard/server/routes/scripts.routes.js +6 -3
- package/dist/dashboard/server/routes/status.routes.js +25 -6
- package/dist/dashboard/server/routes/synthetic.routes.js +3 -2
- package/dist/dashboard/server/routes/usage.routes.js +2 -1
- package/dist/dashboard/server/utils/escapeHtml.d.ts +1 -0
- package/dist/dashboard/server/utils/escapeHtml.js +11 -0
- package/dist/dashboard/server/utils/pathContainment.d.ts +1 -0
- package/dist/dashboard/server/utils/pathContainment.js +15 -0
- package/dist/dashboard/server/wsInit.js +2 -2
- package/dist/lib/mcpStdioLogging.d.ts +165 -0
- package/dist/lib/mcpStdioLogging.js +287 -0
- package/dist/schemas/index.d.ts +37 -2
- package/dist/schemas/index.js +27 -3
- package/dist/server/backgroundServicesStartup.d.ts +7 -1
- package/dist/server/backgroundServicesStartup.js +25 -8
- package/dist/server/certInit.d.ts +97 -0
- package/dist/server/certInit.js +359 -0
- package/dist/server/certInit.types.d.ts +92 -0
- package/dist/server/certInit.types.js +34 -0
- package/dist/server/handshake/fallbackFrames.d.ts +31 -0
- package/dist/server/handshake/fallbackFrames.js +38 -0
- package/dist/server/handshake/initializeDetector.d.ts +31 -0
- package/dist/server/handshake/initializeDetector.js +88 -0
- package/dist/server/handshake/protocol.d.ts +15 -0
- package/dist/server/handshake/protocol.js +37 -0
- package/dist/server/handshake/readyEmitter.d.ts +6 -0
- package/dist/server/handshake/readyEmitter.js +88 -0
- package/dist/server/handshake/safetyFallbacks.d.ts +1 -0
- package/dist/server/handshake/safetyFallbacks.js +134 -0
- package/dist/server/handshake/stdinSniffer.d.ts +1 -0
- package/dist/server/handshake/stdinSniffer.js +260 -0
- package/dist/server/handshake/tracing.d.ts +16 -0
- package/dist/server/handshake/tracing.js +95 -0
- package/dist/server/handshakeManager.d.ts +23 -23
- package/dist/server/handshakeManager.js +36 -466
- package/dist/server/index-server.d.ts +23 -0
- package/dist/server/index-server.js +194 -9
- package/dist/server/mcpReadOnlySurfaces.d.ts +44 -0
- package/dist/server/mcpReadOnlySurfaces.js +297 -0
- package/dist/server/sdkServer.js +69 -7
- package/dist/server/transport.d.ts +5 -6
- package/dist/server/transport.js +46 -64
- package/dist/server/transportFactory.d.ts +3 -9
- package/dist/server/transportFactory.js +18 -380
- package/dist/services/atomicFs.d.ts +3 -0
- package/dist/services/atomicFs.js +171 -13
- package/dist/services/auditLog.d.ts +17 -2
- package/dist/services/auditLog.js +75 -14
- package/dist/services/bootstrapGating.js +1 -1
- package/dist/services/categoryRules.d.ts +10 -0
- package/dist/services/categoryRules.js +17 -0
- package/dist/services/classificationService.js +7 -5
- package/dist/services/embeddingService.d.ts +27 -11
- package/dist/services/embeddingService.js +51 -14
- package/dist/services/feedbackStorage.d.ts +39 -0
- package/dist/services/feedbackStorage.js +88 -0
- package/dist/services/handlers/instructions.add.js +429 -317
- package/dist/services/handlers/instructions.groom.js +128 -31
- package/dist/services/handlers/instructions.import.js +56 -23
- package/dist/services/handlers/instructions.patch.js +43 -32
- package/dist/services/handlers/instructions.query.js +20 -29
- package/dist/services/handlers/instructions.shared.d.ts +54 -0
- package/dist/services/handlers/instructions.shared.js +126 -1
- package/dist/services/handlers.activation.js +83 -81
- package/dist/services/handlers.dashboardConfig.d.ts +2 -2
- package/dist/services/handlers.dashboardConfig.js +1 -2
- package/dist/services/handlers.diagnostics.js +75 -54
- package/dist/services/handlers.feedback.d.ts +4 -11
- package/dist/services/handlers.feedback.js +11 -333
- package/dist/services/handlers.gates.js +69 -37
- package/dist/services/handlers.graph.js +2 -2
- package/dist/services/handlers.help.js +2 -2
- package/dist/services/handlers.instructionSchema.js +4 -2
- package/dist/services/handlers.integrity.js +42 -22
- package/dist/services/handlers.messaging.js +1 -1
- package/dist/services/handlers.metrics.js +51 -6
- package/dist/services/handlers.prompt.js +10 -2
- package/dist/services/handlers.search.js +94 -44
- package/dist/services/handlers.trace.js +1 -1
- package/dist/services/handlers.usage.js +38 -7
- package/dist/services/indexContext.d.ts +21 -1
- package/dist/services/indexContext.js +267 -82
- package/dist/services/indexLoader.d.ts +1 -0
- package/dist/services/indexLoader.js +28 -8
- package/dist/services/instructionRecordValidation.d.ts +39 -0
- package/dist/services/instructionRecordValidation.js +388 -0
- package/dist/services/instructions.dispatcher.js +4 -4
- package/dist/services/loaderSchemaValidator.d.ts +15 -0
- package/dist/services/loaderSchemaValidator.js +69 -0
- package/dist/services/logger.js +11 -2
- package/dist/services/mcpLogBridge.d.ts +49 -0
- package/dist/services/mcpLogBridge.js +83 -0
- package/dist/services/ownershipService.js +18 -8
- package/dist/services/performanceBaseline.js +23 -22
- package/dist/services/promptReviewService.d.ts +3 -1
- package/dist/services/promptReviewService.js +41 -13
- package/dist/services/regexSafety.d.ts +6 -0
- package/dist/services/regexSafety.js +46 -0
- package/dist/services/seedBootstrap.js +4 -4
- package/dist/services/storage/factory.d.ts +14 -1
- package/dist/services/storage/factory.js +61 -1
- package/dist/services/storage/jsonEmbeddingStore.d.ts +15 -0
- package/dist/services/storage/jsonEmbeddingStore.js +83 -0
- package/dist/services/storage/jsonFileStore.d.ts +3 -1
- package/dist/services/storage/jsonFileStore.js +8 -6
- package/dist/services/storage/migrationEngine.d.ts +13 -0
- package/dist/services/storage/migrationEngine.js +31 -0
- package/dist/services/storage/sqliteEmbeddingStore.d.ts +30 -0
- package/dist/services/storage/sqliteEmbeddingStore.js +222 -0
- package/dist/services/storage/sqliteStore.d.ts +3 -1
- package/dist/services/storage/sqliteStore.js +2 -2
- package/dist/services/storage/types.d.ts +48 -1
- package/dist/services/toolRegistry.js +77 -67
- package/dist/services/toolRegistry.zod.js +89 -86
- package/dist/services/tracing.js +5 -4
- package/dist/utils/envUtils.d.ts +4 -0
- package/dist/utils/envUtils.js +7 -0
- package/dist/utils/memoryMonitor.js +11 -10
- package/package.json +11 -4
- package/schemas/instruction.schema.json +38 -1
- package/scripts/copy-dashboard-assets.mjs +1 -1
- package/scripts/dist/README.md +1 -1
- package/scripts/setup-wizard.mjs +781 -0
- package/server.json +1 -0
- package/dist/externalClientLib.d.ts +0 -1
- package/dist/externalClientLib.js +0 -2
- package/dist/portableClientWrapper.d.ts +0 -1
- package/dist/portableClientWrapper.js +0 -2
- package/dist/services/indexingService.d.ts +0 -1
- package/dist/services/indexingService.js +0 -2
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
-
<meta name="dashboard-build-version" content="1.
|
|
4
|
+
<meta name="dashboard-build-version" content="1.26.1-9badd8dd">
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>Index Server Admin</title>
|
|
8
|
-
<link rel="stylesheet" href="css/admin.css?v=1.
|
|
9
|
-
<script defer src="js/admin.utils.js?v=1.
|
|
10
|
-
<script defer src="js/admin.auth.js?v=1.
|
|
11
|
-
<script defer src="js/admin.overview.js?v=1.
|
|
12
|
-
<script defer src="js/admin.sessions.js?v=1.
|
|
13
|
-
<script defer src="js/admin.monitor.js?v=1.
|
|
14
|
-
<script defer src="js/admin.graph.js?v=1.
|
|
8
|
+
<link rel="stylesheet" href="css/admin.css?v=1.26.1-9badd8dd">
|
|
9
|
+
<script defer src="js/admin.utils.js?v=1.26.1-9badd8dd"></script>
|
|
10
|
+
<script defer src="js/admin.auth.js?v=1.26.1-9badd8dd"></script>
|
|
11
|
+
<script defer src="js/admin.overview.js?v=1.26.1-9badd8dd"></script>
|
|
12
|
+
<script defer src="js/admin.sessions.js?v=1.26.1-9badd8dd"></script>
|
|
13
|
+
<script defer src="js/admin.monitor.js?v=1.26.1-9badd8dd"></script>
|
|
14
|
+
<script defer src="js/admin.graph.js?v=1.26.1-9badd8dd"></script>
|
|
15
15
|
<script defer src="js/marked.umd.js"></script>
|
|
16
|
-
<script defer src="js/admin.instructions.js?v=1.
|
|
17
|
-
<script defer src="js/admin.logs.js?v=1.
|
|
18
|
-
<script defer src="js/admin.maintenance.js?v=1.
|
|
19
|
-
<script defer src="js/admin.config.js?v=1.
|
|
20
|
-
<script defer src="js/admin.performance.js?v=1.
|
|
21
|
-
<script defer src="js/admin.instances.js?v=1.
|
|
22
|
-
<script defer src="js/admin.embeddings.js?v=1.
|
|
23
|
-
<script defer src="js/admin.messaging.js?v=1.
|
|
24
|
-
<script defer src="js/admin.sqlite.js?v=1.
|
|
25
|
-
<script defer src="js/admin.boot.js?v=1.
|
|
16
|
+
<script defer src="js/admin.instructions.js?v=1.26.1-9badd8dd"></script>
|
|
17
|
+
<script defer src="js/admin.logs.js?v=1.26.1-9badd8dd"></script>
|
|
18
|
+
<script defer src="js/admin.maintenance.js?v=1.26.1-9badd8dd"></script>
|
|
19
|
+
<script defer src="js/admin.config.js?v=1.26.1-9badd8dd"></script>
|
|
20
|
+
<script defer src="js/admin.performance.js?v=1.26.1-9badd8dd"></script>
|
|
21
|
+
<script defer src="js/admin.instances.js?v=1.26.1-9badd8dd"></script>
|
|
22
|
+
<script defer src="js/admin.embeddings.js?v=1.26.1-9badd8dd"></script>
|
|
23
|
+
<script defer src="js/admin.messaging.js?v=1.26.1-9badd8dd"></script>
|
|
24
|
+
<script defer src="js/admin.sqlite.js?v=1.26.1-9badd8dd"></script>
|
|
25
|
+
<script defer src="js/admin.boot.js?v=1.26.1-9badd8dd"></script>
|
|
26
|
+
<script defer src="js/admin.feedback.js?v=1.26.1-9badd8dd"></script>
|
|
26
27
|
</head>
|
|
27
28
|
<body>
|
|
28
29
|
<div class="admin-container admin-root">
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
<button class="nav-btn" data-section="graph" onclick="window.showSection && window.showSection('graph')">Graph</button>
|
|
53
54
|
<button class="nav-btn" data-section="embeddings" onclick="window.showSection && window.showSection('embeddings')">Embeddings</button>
|
|
54
55
|
<button class="nav-btn" data-section="messaging" onclick="window.showSection && window.showSection('messaging')">Messaging</button>
|
|
56
|
+
<button class="nav-btn" data-section="feedback" onclick="window.showSection && window.showSection('feedback')">Feedback</button>
|
|
55
57
|
<button class="nav-btn" data-section="sqlite" id="nav-sqlite" onclick="window.showSection && window.showSection('sqlite')" style="display:none">SQLite</button>
|
|
56
58
|
</div>
|
|
57
59
|
</div>
|
|
@@ -596,6 +598,90 @@
|
|
|
596
598
|
<div id="messaging-detail"></div>
|
|
597
599
|
</div>
|
|
598
600
|
|
|
601
|
+
<!-- Feedback Section -->
|
|
602
|
+
<div id="feedback-section" class="admin-section hidden">
|
|
603
|
+
<div class="admin-card">
|
|
604
|
+
<div class="card-header">
|
|
605
|
+
<div class="card-icon">💬</div>
|
|
606
|
+
<div class="card-title">Feedback Entries</div>
|
|
607
|
+
</div>
|
|
608
|
+
<div class="feedback-toolbar">
|
|
609
|
+
<button id="feedback-create-btn" class="action-btn" data-fb-action="create">➕ New Entry</button>
|
|
610
|
+
<button class="action-btn" data-fb-action="refresh">🔄 Refresh</button>
|
|
611
|
+
<input id="feedback-filter" class="form-input" type="text" placeholder="Filter entries…" />
|
|
612
|
+
<select id="feedback-status-filter" class="form-input feedback-status-filter">
|
|
613
|
+
<option value="">All Status</option>
|
|
614
|
+
<option value="new">New</option>
|
|
615
|
+
<option value="acknowledged">Acknowledged</option>
|
|
616
|
+
<option value="in-progress">In Progress</option>
|
|
617
|
+
<option value="resolved">Resolved</option>
|
|
618
|
+
<option value="closed">Closed</option>
|
|
619
|
+
</select>
|
|
620
|
+
</div>
|
|
621
|
+
<div id="feedback-table" class="feedback-table-wrap" role="region" aria-label="Feedback entries">
|
|
622
|
+
<div class="feedback-empty">Click <strong>Refresh</strong> or <strong>New Entry</strong> to get started.</div>
|
|
623
|
+
</div>
|
|
624
|
+
</div>
|
|
625
|
+
|
|
626
|
+
<!-- Detail / edit area -->
|
|
627
|
+
<div id="feedback-detail" class="admin-card hidden feedback-detail-card">
|
|
628
|
+
<div class="card-header">
|
|
629
|
+
<div class="card-icon">📝</div>
|
|
630
|
+
<div class="card-title" id="feedback-detail-title">Entry Details</div>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="feedback-form">
|
|
633
|
+
<div class="form-group">
|
|
634
|
+
<label class="form-label" for="feedback-entry-title">Title <span class="feedback-required">*</span></label>
|
|
635
|
+
<input id="feedback-entry-title" class="form-input" type="text" placeholder="Short summary" aria-required="true" />
|
|
636
|
+
</div>
|
|
637
|
+
<div class="feedback-form-row">
|
|
638
|
+
<div class="form-group">
|
|
639
|
+
<label class="form-label" for="feedback-entry-type">Type</label>
|
|
640
|
+
<select id="feedback-entry-type" class="form-input">
|
|
641
|
+
<option value="bug-report">Bug Report</option>
|
|
642
|
+
<option value="feature-request">Feature Request</option>
|
|
643
|
+
<option value="issue">Issue</option>
|
|
644
|
+
<option value="performance">Performance</option>
|
|
645
|
+
<option value="usability">Usability</option>
|
|
646
|
+
<option value="security">Security</option>
|
|
647
|
+
<option value="status">Status</option>
|
|
648
|
+
<option value="other">Other</option>
|
|
649
|
+
</select>
|
|
650
|
+
</div>
|
|
651
|
+
<div class="form-group">
|
|
652
|
+
<label class="form-label" for="feedback-entry-severity">Severity</label>
|
|
653
|
+
<select id="feedback-entry-severity" class="form-input">
|
|
654
|
+
<option value="low">Low</option>
|
|
655
|
+
<option value="medium" selected>Medium</option>
|
|
656
|
+
<option value="high">High</option>
|
|
657
|
+
<option value="critical">Critical</option>
|
|
658
|
+
</select>
|
|
659
|
+
</div>
|
|
660
|
+
<div class="form-group">
|
|
661
|
+
<label class="form-label" for="feedback-entry-status">Status</label>
|
|
662
|
+
<select id="feedback-entry-status" class="form-input">
|
|
663
|
+
<option value="new">New</option>
|
|
664
|
+
<option value="acknowledged">Acknowledged</option>
|
|
665
|
+
<option value="in-progress">In Progress</option>
|
|
666
|
+
<option value="resolved">Resolved</option>
|
|
667
|
+
<option value="closed">Closed</option>
|
|
668
|
+
</select>
|
|
669
|
+
</div>
|
|
670
|
+
</div>
|
|
671
|
+
<div class="form-group">
|
|
672
|
+
<label class="form-label" for="feedback-entry-description">Description</label>
|
|
673
|
+
<textarea id="feedback-entry-description" class="form-input feedback-entry-description" rows="5" placeholder="Detailed description…"></textarea>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
<div class="feedback-detail-actions">
|
|
677
|
+
<button id="feedback-save-btn" class="action-btn" data-fb-action="save">💾 Save</button>
|
|
678
|
+
<button id="feedback-delete-btn" class="action-btn danger" data-fb-action="delete">🗑️ Delete</button>
|
|
679
|
+
<button id="feedback-github-btn" class="action-btn btn-info" data-fb-action="github" title="Open as GitHub issue in jagilber-org/index-server (client-side only)">🐙 Open GitHub Issue</button>
|
|
680
|
+
<button class="action-btn warning" data-fb-action="cancel">✖ Cancel</button>
|
|
681
|
+
</div>
|
|
682
|
+
</div>
|
|
683
|
+
</div>
|
|
684
|
+
|
|
599
685
|
<!-- SQLite Section -->
|
|
600
686
|
<div id="sqlite-section" class="admin-section hidden">
|
|
601
687
|
<div class="admin-grid">
|
|
@@ -787,13 +873,16 @@
|
|
|
787
873
|
case 'messaging':
|
|
788
874
|
if (window.initMessaging) window.initMessaging();
|
|
789
875
|
break;
|
|
876
|
+
case 'feedback':
|
|
877
|
+
if (window.initFeedback) window.initFeedback();
|
|
878
|
+
break;
|
|
790
879
|
}
|
|
791
880
|
}
|
|
792
881
|
|
|
793
|
-
// Graph logic was extracted to js/admin.graph.js?v=1.
|
|
882
|
+
// Graph logic was extracted to js/admin.graph.js?v=1.26.1-9badd8dd
|
|
794
883
|
// Functions available globally: reloadGraphMermaid, initGraphScopeDefaults, copyMermaidSource, toggleGraphEdit, applyGraphEdit, cancelGraphEdit, refreshDrillCategories, loadDrillInstructions, clearSelections
|
|
795
884
|
|
|
796
|
-
<!-- overview functions moved to js/admin.overview.js?v=1.
|
|
885
|
+
<!-- overview functions moved to js/admin.overview.js?v=1.26.1-9badd8dd -->
|
|
797
886
|
|
|
798
887
|
// Lightweight overview-level maintenance display (optional)
|
|
799
888
|
// Intentionally minimal to avoid blocking overview rendering.
|
|
@@ -873,11 +962,12 @@
|
|
|
873
962
|
if (statusOverride === 'healthy') statusOverride = 'unknown';
|
|
874
963
|
}
|
|
875
964
|
const statusClass = `status-${statusOverride}`;
|
|
965
|
+
const safeStatus = escapeHtml(statusOverride.toUpperCase());
|
|
876
966
|
let html = `
|
|
877
967
|
<div class="stat-row">
|
|
878
968
|
<span class="stat-label">Overall Status</span>
|
|
879
969
|
<span class="stat-value">
|
|
880
|
-
${
|
|
970
|
+
${safeStatus}
|
|
881
971
|
<span class="${statusClass} status-indicator"></span>
|
|
882
972
|
</span>
|
|
883
973
|
</div>
|
|
@@ -888,7 +978,7 @@
|
|
|
888
978
|
const checkKeys = Object.keys(normalized.checks);
|
|
889
979
|
if(checkKeys.length){
|
|
890
980
|
html += '<div class="health-mt"><strong>Checks:</strong><ul class="health-list">' +
|
|
891
|
-
checkKeys.map(k => `<li class="${normalized.checks[k]?'text-ok':'text-fail'}">${k}: ${normalized.checks[k]?'ok':'fail'}</li>`).join('') + '</ul></div>';
|
|
981
|
+
checkKeys.map(k => `<li class="${normalized.checks[k]?'text-ok':'text-fail'}">${escapeHtml(k)}: ${normalized.checks[k]?'ok':'fail'}</li>`).join('') + '</ul></div>';
|
|
892
982
|
}
|
|
893
983
|
} catch { /* ignore */ }
|
|
894
984
|
|
|
@@ -898,7 +988,7 @@
|
|
|
898
988
|
<div class="health-mt">
|
|
899
989
|
<strong>CPU Trend:</strong>
|
|
900
990
|
<span class="${health.cpuTrend === 'stable' ? 'text-ok' : health.cpuTrend === 'increasing' ? 'text-warn' : 'text-fail'}">
|
|
901
|
-
${health.cpuTrend}
|
|
991
|
+
${escapeHtml(health.cpuTrend)}
|
|
902
992
|
</span>
|
|
903
993
|
</div>
|
|
904
994
|
`;
|
|
@@ -911,14 +1001,17 @@
|
|
|
911
1001
|
if (Math.abs(rate) < 1024 * 1024) return `${(rate / 1024).toFixed(1)} KB/min`;
|
|
912
1002
|
return `${(rate / (1024 * 1024)).toFixed(1)} MB/min`;
|
|
913
1003
|
};
|
|
1004
|
+
const safeMemoryGrowthRate = health.memoryGrowthRate
|
|
1005
|
+
? escapeHtml(formatGrowthRate(health.memoryGrowthRate))
|
|
1006
|
+
: '';
|
|
914
1007
|
|
|
915
1008
|
html += `
|
|
916
1009
|
<div class="health-mt">
|
|
917
1010
|
<strong>Memory Trend:</strong>
|
|
918
1011
|
<span class="${health.memoryTrend === 'stable' ? 'text-ok' : health.memoryTrend === 'increasing' ? 'text-warn' : 'text-fail'}">
|
|
919
|
-
${health.memoryTrend}
|
|
1012
|
+
${escapeHtml(health.memoryTrend)}
|
|
920
1013
|
</span>
|
|
921
|
-
${
|
|
1014
|
+
${safeMemoryGrowthRate ? ` (${safeMemoryGrowthRate})` : ''}
|
|
922
1015
|
</div>
|
|
923
1016
|
`;
|
|
924
1017
|
}
|
|
@@ -928,7 +1021,7 @@
|
|
|
928
1021
|
<div class="health-mt-lg">
|
|
929
1022
|
<strong>Issues:</strong>
|
|
930
1023
|
<ul class="health-list">
|
|
931
|
-
${normalized.issues.map(issue => `<li class="text-fail">${issue}</li>`).join('')}
|
|
1024
|
+
${normalized.issues.map(issue => `<li class="text-fail">${escapeHtml(issue)}</li>`).join('')}
|
|
932
1025
|
</ul>
|
|
933
1026
|
</div>
|
|
934
1027
|
`;
|
|
@@ -939,7 +1032,7 @@
|
|
|
939
1032
|
<div class="health-mt-lg">
|
|
940
1033
|
<strong>Recommendations:</strong>
|
|
941
1034
|
<ul class="health-list">
|
|
942
|
-
${normalized.recommendations.map(rec => `<li class="text-warn">${rec}</li>`).join('')}
|
|
1035
|
+
${normalized.recommendations.map(rec => `<li class="text-warn">${escapeHtml(rec)}</li>`).join('')}
|
|
943
1036
|
</ul>
|
|
944
1037
|
</div>
|
|
945
1038
|
`;
|
|
@@ -955,12 +1048,12 @@
|
|
|
955
1048
|
<div class="resource-trend-col">
|
|
956
1049
|
<div class="spark-col">
|
|
957
1050
|
<span class="spark-label">CPU Spark (last ${Math.min(40, t.sampleCount || 0)} samples)</span>
|
|
958
|
-
<span class="spark-value">${t.spark || ''}</span>
|
|
1051
|
+
<span class="spark-value">${escapeHtml(t.spark || '')}</span>
|
|
959
1052
|
<span class="spark-stats">Latest ${t.latestCpu?.toFixed ? t.latestCpu.toFixed(1) : '0'}% • Min ${(t.minCpu??0).toFixed(1)}% • Max ${(t.maxCpu??0).toFixed(1)}%</span>
|
|
960
1053
|
</div>
|
|
961
1054
|
<div class="spark-col">
|
|
962
1055
|
<span class="spark-label">Mem Spark (heap)</span>
|
|
963
|
-
<span class="spark-value">${t.memSpark || ''}</span>
|
|
1056
|
+
<span class="spark-value">${escapeHtml(t.memSpark || '')}</span>
|
|
964
1057
|
<span class="spark-stats">Latest ${(t.latestHeap/1024/1024).toFixed(2)} MB • Min ${(t.minHeap/1024/1024).toFixed(2)} MB • Max ${(t.maxHeap/1024/1024).toFixed(2)} MB</span>
|
|
965
1058
|
</div>
|
|
966
1059
|
</div>
|
|
@@ -974,7 +1067,7 @@
|
|
|
974
1067
|
}
|
|
975
1068
|
|
|
976
1069
|
// --- Backup / Restore ---
|
|
977
|
-
// Extracted to js/admin.maintenance.js?v=1.
|
|
1070
|
+
// Extracted to js/admin.maintenance.js?v=1.26.1-9badd8dd
|
|
978
1071
|
|
|
979
1072
|
async function performBackup() {
|
|
980
1073
|
try {
|
|
@@ -1040,7 +1133,7 @@
|
|
|
1040
1133
|
}
|
|
1041
1134
|
|
|
1042
1135
|
async function loadConfiguration() {
|
|
1043
|
-
// Primary implementation in js/admin.config.js?v=1.
|
|
1136
|
+
// Primary implementation in js/admin.config.js?v=1.26.1-9badd8dd (loaded via defer).
|
|
1044
1137
|
// This inline fallback only fires if the external script failed to load.
|
|
1045
1138
|
if (window.__configExternalLoaded) return;
|
|
1046
1139
|
try {
|
|
@@ -1048,11 +1141,13 @@
|
|
|
1048
1141
|
var data = await res.json();
|
|
1049
1142
|
if (!data.success) throw new Error('Failed to load config');
|
|
1050
1143
|
var cfg = data.config;
|
|
1144
|
+
var safeMaxConnections = escapeHtml(String(cfg.serverSettings.maxConnections ?? ''));
|
|
1145
|
+
var safeRequestTimeout = escapeHtml(String(cfg.serverSettings.requestTimeout ?? ''));
|
|
1051
1146
|
var html = '<form onsubmit="return updateConfiguration(event)">'
|
|
1052
1147
|
+ '<div class="form-group"><label class="form-label">Max Connections</label>'
|
|
1053
|
-
+ '<input class="form-input" type="number" id="cfg-maxConnections" value="' +
|
|
1148
|
+
+ '<input class="form-input" type="number" id="cfg-maxConnections" value="' + safeMaxConnections + '" /></div>'
|
|
1054
1149
|
+ '<div class="form-group"><label class="form-label">Request Timeout (ms)</label>'
|
|
1055
|
-
+ '<input class="form-input" type="number" id="cfg-requestTimeout" value="' +
|
|
1150
|
+
+ '<input class="form-input" type="number" id="cfg-requestTimeout" value="' + safeRequestTimeout + '" /></div>'
|
|
1056
1151
|
+ '<div class="form-group"><label class="form-label">Verbose Logging</label>'
|
|
1057
1152
|
+ '<select class="form-input" id="cfg-verbose"><option value="1"' + (cfg.serverSettings.enableVerboseLogging ? ' selected' : '') + '>Enabled</option>'
|
|
1058
1153
|
+ '<option value="0"' + (!cfg.serverSettings.enableVerboseLogging ? ' selected' : '') + '>Disabled</option></select></div>'
|
|
@@ -1098,10 +1193,10 @@
|
|
|
1098
1193
|
return false;
|
|
1099
1194
|
}
|
|
1100
1195
|
|
|
1101
|
-
// Monitoring functions moved to js/admin.monitor.js?v=1.
|
|
1196
|
+
// Monitoring functions moved to js/admin.monitor.js?v=1.26.1-9badd8dd
|
|
1102
1197
|
|
|
1103
1198
|
// ===== Log Viewer =====
|
|
1104
|
-
// Extracted to js/admin.logs.js?v=1.
|
|
1199
|
+
// Extracted to js/admin.logs.js?v=1.26.1-9badd8dd
|
|
1105
1200
|
|
|
1106
1201
|
// ===== Instruction Management =====
|
|
1107
1202
|
let instructionEditing = null;
|
|
@@ -1240,18 +1335,23 @@
|
|
|
1240
1335
|
let short = rawSummary.slice(0, 160);
|
|
1241
1336
|
if (rawSummary.length > 160) short += '…';
|
|
1242
1337
|
const safeSummary = escapeHtml(short);
|
|
1338
|
+
const safeName = escapeHtml(instr.name || '');
|
|
1339
|
+
const safeCategory = escapeHtml(instr.category || '—');
|
|
1340
|
+
const safeSize = escapeHtml(String(instr.size ?? '0'));
|
|
1341
|
+
const safeSizeCategory = escapeHtml(instr.sizeCategory || 'unknown');
|
|
1342
|
+
const safeModified = escapeHtml(new Date(instr.mtime).toLocaleString());
|
|
1243
1343
|
return `
|
|
1244
1344
|
<div class="session-item instr-item-bg">
|
|
1245
1345
|
<div class="session-header">
|
|
1246
|
-
<span class="session-id instr-id-bg">${
|
|
1346
|
+
<span class="session-id instr-id-bg">${safeName}</span>
|
|
1247
1347
|
<div>
|
|
1248
|
-
<button class="action-btn"
|
|
1249
|
-
<button class="action-btn danger"
|
|
1348
|
+
<button class="action-btn" data-instruction-action="edit" data-instruction-name="${safeName}">✏ Edit</button>
|
|
1349
|
+
<button class="action-btn danger" data-instruction-action="delete" data-instruction-name="${safeName}">🗑 Delete</button>
|
|
1250
1350
|
</div>
|
|
1251
1351
|
</div>
|
|
1252
|
-
<div class="stat-row"><span class="stat-label">Category</span><span class="stat-value">${
|
|
1253
|
-
<div class="stat-row"><span class="stat-label">Size</span><span class="stat-value">${
|
|
1254
|
-
<div class="stat-row"><span class="stat-label">Modified</span><span class="stat-value">${
|
|
1352
|
+
<div class="stat-row"><span class="stat-label">Category</span><span class="stat-value">${safeCategory}</span></div>
|
|
1353
|
+
<div class="stat-row"><span class="stat-label">Size</span><span class="stat-value">${safeSize} bytes (${safeSizeCategory})</span></div>
|
|
1354
|
+
<div class="stat-row"><span class="stat-label">Modified</span><span class="stat-value">${safeModified}</span></div>
|
|
1255
1355
|
<div class="stat-row stat-row-top">
|
|
1256
1356
|
<span class="stat-label stat-label-pt">Summary</span>
|
|
1257
1357
|
<span class="stat-value stat-value-wrap">
|
|
@@ -1261,6 +1361,7 @@
|
|
|
1261
1361
|
</div>`;
|
|
1262
1362
|
}).join('');
|
|
1263
1363
|
document.getElementById('instructions-list').innerHTML = rows;
|
|
1364
|
+
wireLegacyInstructionActions(document.getElementById('instructions-list'));
|
|
1264
1365
|
buildInstructionPaginationControls(totalFiltered);
|
|
1265
1366
|
}
|
|
1266
1367
|
|
|
@@ -1592,7 +1693,7 @@
|
|
|
1592
1693
|
setInterval(fetchResourceTrends, 10000);
|
|
1593
1694
|
})();
|
|
1594
1695
|
|
|
1595
|
-
// Instruction management logic extracted to js/admin.instructions.js?v=1.
|
|
1696
|
+
// Instruction management logic extracted to js/admin.instructions.js?v=1.26.1-9badd8dd
|
|
1596
1697
|
// Functions exposed globally: loadInstructions, renderInstructionList, editInstruction, saveInstruction, deleteInstruction, etc.
|
|
1597
1698
|
|
|
1598
1699
|
function startAutoRefresh() {
|
|
@@ -1606,11 +1707,12 @@
|
|
|
1606
1707
|
}
|
|
1607
1708
|
}, 30000); // Refresh every 30 seconds
|
|
1608
1709
|
}
|
|
1609
|
-
// Build metadata loader
|
|
1710
|
+
// Build metadata loader — uses plain fetch (no auth needed for /api/status)
|
|
1610
1711
|
(async function fetchBuildMeta(){
|
|
1611
1712
|
try {
|
|
1612
1713
|
// Cache bust query param to avoid any intermediary caching of status response
|
|
1613
|
-
const r = await
|
|
1714
|
+
const r = await fetch('/api/status?t=' + Date.now());
|
|
1715
|
+
if (!r.ok) throw new Error(r.statusText);
|
|
1614
1716
|
const j = await r.json();
|
|
1615
1717
|
const el = document.getElementById('buildMeta');
|
|
1616
1718
|
const ver = j.version || '?.?.?';
|
|
@@ -1674,14 +1776,29 @@
|
|
|
1674
1776
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
1675
1777
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
|
|
1676
1778
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1779
|
+
// Defensive: window.adminUtils is provided by the deferred admin.utils.js
|
|
1780
|
+
// script which runs AFTER this inline script, so direct access throws and
|
|
1781
|
+
// permanently leaves this binding in TDZ — breaking every inline function
|
|
1782
|
+
// that references escapeHtml (notably displaySystemHealth, which would
|
|
1783
|
+
// silently exit and leave the System Health card stuck on its loading
|
|
1784
|
+
// placeholder). Wrap in a function that resolves the helper at call time.
|
|
1785
|
+
const escapeHtml = function(s) {
|
|
1786
|
+
const fn = (window.adminUtils && window.adminUtils.escapeHtml) || window.escapeHtml;
|
|
1787
|
+
if (fn) return fn(s);
|
|
1788
|
+
return String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]);
|
|
1789
|
+
};
|
|
1790
|
+
function wireLegacyInstructionActions(listEl) {
|
|
1791
|
+
if (!listEl) return;
|
|
1792
|
+
listEl.querySelectorAll('[data-instruction-action]').forEach((button) => {
|
|
1793
|
+
if (button.__legacyInstructionActionBound) return;
|
|
1794
|
+
button.addEventListener('click', () => {
|
|
1795
|
+
const action = button.getAttribute('data-instruction-action');
|
|
1796
|
+
const instructionName = button.getAttribute('data-instruction-name') || '';
|
|
1797
|
+
if (action === 'edit') editInstruction(instructionName);
|
|
1798
|
+
if (action === 'delete') deleteInstruction(instructionName);
|
|
1799
|
+
});
|
|
1800
|
+
button.__legacyInstructionActionBound = true;
|
|
1801
|
+
});
|
|
1685
1802
|
}
|
|
1686
1803
|
// ---------------- Drilldown Layered SVG (Experimental) -----------------
|
|
1687
1804
|
// ELK auto-layout integration (Option A): dynamically load elkjs for layered layout.
|
|
@@ -2026,13 +2143,15 @@
|
|
|
2026
2143
|
}
|
|
2027
2144
|
function updateDrillLegend(data){
|
|
2028
2145
|
const el = document.getElementById('drill-legend'); if(!el) return;
|
|
2146
|
+
const safeSelected = escapeHtml(data.selected);
|
|
2147
|
+
const safeLayout = escapeHtml(data.layout);
|
|
2029
2148
|
el.innerHTML = `<div class="drill-legend-title">Legend / Stats</div>` +
|
|
2030
2149
|
`<div class="drill-legend-body">`+
|
|
2031
|
-
`Selected: <b>${
|
|
2150
|
+
`Selected: <b>${safeSelected}</b>${data.expansionEnabled? ' (expand '+(data.expanded||0)+')':''}<br>`+
|
|
2032
2151
|
`Rendered Instructions: <b>${data.total}</b><br>`+
|
|
2033
2152
|
`Instruction Edges: <b>${data.edges}</b><br>`+
|
|
2034
2153
|
`Membership Lines: <b>${data.membership}</b> ${data.membership? '(primary category connectors)':''}<br>`+
|
|
2035
|
-
`Layout: <b>${
|
|
2154
|
+
`Layout: <b>${safeLayout}</b>${data.expansionEnabled? ' + expand':''}`+
|
|
2036
2155
|
`</div>`;
|
|
2037
2156
|
}
|
|
2038
2157
|
function exportDrillSvg(){
|
|
@@ -166,6 +166,25 @@ body {
|
|
|
166
166
|
padding-bottom: 8px;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
.feedback-btn {
|
|
170
|
+
position: absolute;
|
|
171
|
+
top: 12px;
|
|
172
|
+
right: 24px;
|
|
173
|
+
font-size: 12px;
|
|
174
|
+
color: var(--admin-text-dim);
|
|
175
|
+
background: transparent;
|
|
176
|
+
border: 1px solid var(--admin-border);
|
|
177
|
+
border-radius: 4px;
|
|
178
|
+
padding: 4px 10px;
|
|
179
|
+
text-decoration: none;
|
|
180
|
+
cursor: pointer;
|
|
181
|
+
transition: color 0.15s, border-color 0.15s;
|
|
182
|
+
}
|
|
183
|
+
.feedback-btn:hover {
|
|
184
|
+
color: var(--admin-accent);
|
|
185
|
+
border-color: var(--admin-accent);
|
|
186
|
+
}
|
|
187
|
+
|
|
169
188
|
/* --- Horizontal nav bar --- */
|
|
170
189
|
.admin-nav {
|
|
171
190
|
display: flex;
|
|
@@ -1585,3 +1604,135 @@ a.mermaid-link { color: var(--admin-accent); }
|
|
|
1585
1604
|
}
|
|
1586
1605
|
|
|
1587
1606
|
/* .admin-root marker (used by tests, no visual styling) */
|
|
1607
|
+
|
|
1608
|
+
/* ==========================================================================
|
|
1609
|
+
Feedback Tab
|
|
1610
|
+
========================================================================== */
|
|
1611
|
+
|
|
1612
|
+
.feedback-toolbar {
|
|
1613
|
+
display: flex;
|
|
1614
|
+
align-items: center;
|
|
1615
|
+
gap: 8px;
|
|
1616
|
+
flex-wrap: wrap;
|
|
1617
|
+
margin-bottom: 12px;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.feedback-status-filter {
|
|
1621
|
+
max-width: 160px;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.feedback-table-wrap {
|
|
1625
|
+
overflow-x: auto;
|
|
1626
|
+
min-height: 60px;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.feedback-empty {
|
|
1630
|
+
padding: 24px;
|
|
1631
|
+
color: var(--admin-text-dim);
|
|
1632
|
+
font-size: 13px;
|
|
1633
|
+
text-align: center;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
.fb-table {
|
|
1637
|
+
width: 100%;
|
|
1638
|
+
border-collapse: collapse;
|
|
1639
|
+
font-size: 13px;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
.fb-th {
|
|
1643
|
+
padding: 6px 10px;
|
|
1644
|
+
text-align: left;
|
|
1645
|
+
font-size: 11px;
|
|
1646
|
+
font-weight: 600;
|
|
1647
|
+
letter-spacing: 0.5px;
|
|
1648
|
+
text-transform: uppercase;
|
|
1649
|
+
color: var(--admin-text-dim);
|
|
1650
|
+
border-bottom: 1px solid var(--admin-border);
|
|
1651
|
+
white-space: nowrap;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
.fb-cell {
|
|
1655
|
+
padding: 8px 10px;
|
|
1656
|
+
border-bottom: 1px solid var(--admin-border-muted);
|
|
1657
|
+
color: var(--admin-text);
|
|
1658
|
+
vertical-align: middle;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
.feedback-row {
|
|
1662
|
+
cursor: pointer;
|
|
1663
|
+
transition: background 0.1s;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
.feedback-row:hover { background: var(--admin-surface-alt); }
|
|
1667
|
+
.feedback-row.selected { background: rgba(59,130,246,0.10); }
|
|
1668
|
+
|
|
1669
|
+
.fb-id { font-family: monospace; font-size: 11px; color: var(--admin-text-dim); }
|
|
1670
|
+
.fb-title { max-width: 280px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
|
|
1671
|
+
.fb-type { white-space: nowrap; }
|
|
1672
|
+
.fb-ts { font-size: 11px; white-space: nowrap; }
|
|
1673
|
+
.fb-actions { white-space: nowrap; }
|
|
1674
|
+
|
|
1675
|
+
.fb-badge {
|
|
1676
|
+
display: inline-block;
|
|
1677
|
+
padding: 2px 7px;
|
|
1678
|
+
border-radius: 10px;
|
|
1679
|
+
font-size: 11px;
|
|
1680
|
+
font-weight: 600;
|
|
1681
|
+
letter-spacing: 0.3px;
|
|
1682
|
+
white-space: nowrap;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
.feedback-form {
|
|
1686
|
+
padding: 8px 0 12px;
|
|
1687
|
+
display: flex;
|
|
1688
|
+
flex-direction: column;
|
|
1689
|
+
gap: 12px;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
.feedback-form-row {
|
|
1693
|
+
display: flex;
|
|
1694
|
+
gap: 12px;
|
|
1695
|
+
flex-wrap: wrap;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
.feedback-form-row .form-group {
|
|
1699
|
+
flex: 1;
|
|
1700
|
+
min-width: 130px;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.feedback-detail-card {
|
|
1704
|
+
margin-top: 16px;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.feedback-entry-description {
|
|
1708
|
+
width: 100%;
|
|
1709
|
+
resize: vertical;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
.feedback-required {
|
|
1713
|
+
color: var(--admin-danger);
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
.feedback-detail-actions {
|
|
1717
|
+
display: flex;
|
|
1718
|
+
gap: 8px;
|
|
1719
|
+
flex-wrap: wrap;
|
|
1720
|
+
padding-top: 8px;
|
|
1721
|
+
border-top: 1px solid var(--admin-border);
|
|
1722
|
+
margin-top: 4px;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/* Rate-limit banner (issue #63) */
|
|
1726
|
+
#rate-limit-banner {
|
|
1727
|
+
animation: rl-fade-in 0.3s ease-out;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
@keyframes rl-fade-in {
|
|
1731
|
+
from { opacity: 0; transform: translateY(-8px); }
|
|
1732
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/* Backup warning banner (issue #121) */
|
|
1736
|
+
#backup-warning-banner {
|
|
1737
|
+
animation: rl-fade-in 0.3s ease-out;
|
|
1738
|
+
}
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
// admin.auth.js
|
|
3
3
|
// Dashboard authentication — manages admin API key for non-loopback access.
|
|
4
|
-
//
|
|
5
|
-
//
|
|
4
|
+
// Token is held in module-scoped memory only (never written to sessionStorage
|
|
5
|
+
// or localStorage). It is cleared on tab close / reload by virtue of being a
|
|
6
|
+
// JavaScript variable, and cleared on 401/403 + explicit logout. On 401/403,
|
|
7
|
+
// shows a login modal and retries the request once after the user enters a
|
|
8
|
+
// key. This avoids js/clear-text-storage-of-sensitive-data exposure to XSS.
|
|
6
9
|
(function(window) {
|
|
7
10
|
'use strict';
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
// In-memory only. Re-prompt on page reload.
|
|
13
|
+
var _token = '';
|
|
10
14
|
var _loginPromise = null;
|
|
11
15
|
var _overlay = null;
|
|
12
16
|
|
|
13
17
|
function getToken() {
|
|
14
|
-
|
|
18
|
+
return _token || '';
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
function setToken(token) {
|
|
18
|
-
|
|
19
|
-
if (token) sessionStorage.setItem(STORAGE_KEY, token);
|
|
20
|
-
else sessionStorage.removeItem(STORAGE_KEY);
|
|
21
|
-
} catch (_) { /* private browsing or quota */ }
|
|
22
|
+
_token = token ? String(token) : '';
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
function clearToken() {
|
|
25
|
-
|
|
26
|
+
_token = '';
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
function isAuthenticated() {
|
|
29
|
-
return !!
|
|
30
|
+
return !!_token;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
function applyAuthHeader(headers, token) {
|
|
@@ -49,6 +50,19 @@
|
|
|
49
50
|
|
|
50
51
|
var response = await fetch(url, options);
|
|
51
52
|
|
|
53
|
+
// Rate-limit detection: surface 429 visibly (issue #63)
|
|
54
|
+
if (response.status === 429) {
|
|
55
|
+
try {
|
|
56
|
+
var rlBody = await response.clone().json();
|
|
57
|
+
var retryAfter = rlBody.retryAfterSeconds || Number(response.headers.get('Retry-After')) || 60;
|
|
58
|
+
var tier = rlBody.tier || 'global';
|
|
59
|
+
if (window.adminUtils && window.adminUtils.showRateLimitBanner) {
|
|
60
|
+
window.adminUtils.showRateLimitBanner(retryAfter, tier);
|
|
61
|
+
}
|
|
62
|
+
} catch (_e) { /* couldn't parse 429 body — still return the response */ }
|
|
63
|
+
return response;
|
|
64
|
+
}
|
|
65
|
+
|
|
52
66
|
if (response.status !== 401 && response.status !== 403) return response;
|
|
53
67
|
|
|
54
68
|
// Deduplicate: if login prompt is already showing, wait for the same promise
|
|
@@ -144,7 +158,7 @@
|
|
|
144
158
|
if (isAuthenticated()) {
|
|
145
159
|
badge.textContent = '🔓 Authenticated';
|
|
146
160
|
badge.className = 'auth-indicator auth-ok';
|
|
147
|
-
badge.title = 'Admin API key active (
|
|
161
|
+
badge.title = 'Admin API key active (in-memory; cleared on reload). Click to logout.';
|
|
148
162
|
badge.onclick = logout;
|
|
149
163
|
badge.style.cursor = 'pointer';
|
|
150
164
|
} else {
|