@jagilber-org/index-server 1.22.1 → 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 +82 -19
- 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 +170 -53
- package/dist/dashboard/client/css/admin.css +132 -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 +5 -4
- 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 +263 -78
- 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 +1 -1
- 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">
|
|
@@ -40,7 +41,6 @@
|
|
|
40
41
|
</div>
|
|
41
42
|
</div>
|
|
42
43
|
<div id="buildMeta" class="build-meta">Loading build metadata…</div>
|
|
43
|
-
<a id="feedback-btn" class="feedback-btn" href="https://github.com/jagilber-org/index-server/issues/new/choose" target="_blank" rel="noopener noreferrer" title="Send Feedback (opens GitHub Issues)">💬 Feedback</a>
|
|
44
44
|
<div class="admin-nav">
|
|
45
45
|
<!-- Added explicit data-section attributes so JS can reliably map buttons to sections after HTML refactor -->
|
|
46
46
|
<!-- Redundant inline onclick fallback keeps basic navigation working even if JS wiring changes -->
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
<button class="nav-btn" data-section="graph" onclick="window.showSection && window.showSection('graph')">Graph</button>
|
|
54
54
|
<button class="nav-btn" data-section="embeddings" onclick="window.showSection && window.showSection('embeddings')">Embeddings</button>
|
|
55
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>
|
|
56
57
|
<button class="nav-btn" data-section="sqlite" id="nav-sqlite" onclick="window.showSection && window.showSection('sqlite')" style="display:none">SQLite</button>
|
|
57
58
|
</div>
|
|
58
59
|
</div>
|
|
@@ -597,6 +598,90 @@
|
|
|
597
598
|
<div id="messaging-detail"></div>
|
|
598
599
|
</div>
|
|
599
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
|
+
|
|
600
685
|
<!-- SQLite Section -->
|
|
601
686
|
<div id="sqlite-section" class="admin-section hidden">
|
|
602
687
|
<div class="admin-grid">
|
|
@@ -788,13 +873,16 @@
|
|
|
788
873
|
case 'messaging':
|
|
789
874
|
if (window.initMessaging) window.initMessaging();
|
|
790
875
|
break;
|
|
876
|
+
case 'feedback':
|
|
877
|
+
if (window.initFeedback) window.initFeedback();
|
|
878
|
+
break;
|
|
791
879
|
}
|
|
792
880
|
}
|
|
793
881
|
|
|
794
|
-
// 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
|
|
795
883
|
// Functions available globally: reloadGraphMermaid, initGraphScopeDefaults, copyMermaidSource, toggleGraphEdit, applyGraphEdit, cancelGraphEdit, refreshDrillCategories, loadDrillInstructions, clearSelections
|
|
796
884
|
|
|
797
|
-
<!-- overview functions moved to js/admin.overview.js?v=1.
|
|
885
|
+
<!-- overview functions moved to js/admin.overview.js?v=1.26.1-9badd8dd -->
|
|
798
886
|
|
|
799
887
|
// Lightweight overview-level maintenance display (optional)
|
|
800
888
|
// Intentionally minimal to avoid blocking overview rendering.
|
|
@@ -874,11 +962,12 @@
|
|
|
874
962
|
if (statusOverride === 'healthy') statusOverride = 'unknown';
|
|
875
963
|
}
|
|
876
964
|
const statusClass = `status-${statusOverride}`;
|
|
965
|
+
const safeStatus = escapeHtml(statusOverride.toUpperCase());
|
|
877
966
|
let html = `
|
|
878
967
|
<div class="stat-row">
|
|
879
968
|
<span class="stat-label">Overall Status</span>
|
|
880
969
|
<span class="stat-value">
|
|
881
|
-
${
|
|
970
|
+
${safeStatus}
|
|
882
971
|
<span class="${statusClass} status-indicator"></span>
|
|
883
972
|
</span>
|
|
884
973
|
</div>
|
|
@@ -889,7 +978,7 @@
|
|
|
889
978
|
const checkKeys = Object.keys(normalized.checks);
|
|
890
979
|
if(checkKeys.length){
|
|
891
980
|
html += '<div class="health-mt"><strong>Checks:</strong><ul class="health-list">' +
|
|
892
|
-
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>';
|
|
893
982
|
}
|
|
894
983
|
} catch { /* ignore */ }
|
|
895
984
|
|
|
@@ -899,7 +988,7 @@
|
|
|
899
988
|
<div class="health-mt">
|
|
900
989
|
<strong>CPU Trend:</strong>
|
|
901
990
|
<span class="${health.cpuTrend === 'stable' ? 'text-ok' : health.cpuTrend === 'increasing' ? 'text-warn' : 'text-fail'}">
|
|
902
|
-
${health.cpuTrend}
|
|
991
|
+
${escapeHtml(health.cpuTrend)}
|
|
903
992
|
</span>
|
|
904
993
|
</div>
|
|
905
994
|
`;
|
|
@@ -912,14 +1001,17 @@
|
|
|
912
1001
|
if (Math.abs(rate) < 1024 * 1024) return `${(rate / 1024).toFixed(1)} KB/min`;
|
|
913
1002
|
return `${(rate / (1024 * 1024)).toFixed(1)} MB/min`;
|
|
914
1003
|
};
|
|
1004
|
+
const safeMemoryGrowthRate = health.memoryGrowthRate
|
|
1005
|
+
? escapeHtml(formatGrowthRate(health.memoryGrowthRate))
|
|
1006
|
+
: '';
|
|
915
1007
|
|
|
916
1008
|
html += `
|
|
917
1009
|
<div class="health-mt">
|
|
918
1010
|
<strong>Memory Trend:</strong>
|
|
919
1011
|
<span class="${health.memoryTrend === 'stable' ? 'text-ok' : health.memoryTrend === 'increasing' ? 'text-warn' : 'text-fail'}">
|
|
920
|
-
${health.memoryTrend}
|
|
1012
|
+
${escapeHtml(health.memoryTrend)}
|
|
921
1013
|
</span>
|
|
922
|
-
${
|
|
1014
|
+
${safeMemoryGrowthRate ? ` (${safeMemoryGrowthRate})` : ''}
|
|
923
1015
|
</div>
|
|
924
1016
|
`;
|
|
925
1017
|
}
|
|
@@ -929,7 +1021,7 @@
|
|
|
929
1021
|
<div class="health-mt-lg">
|
|
930
1022
|
<strong>Issues:</strong>
|
|
931
1023
|
<ul class="health-list">
|
|
932
|
-
${normalized.issues.map(issue => `<li class="text-fail">${issue}</li>`).join('')}
|
|
1024
|
+
${normalized.issues.map(issue => `<li class="text-fail">${escapeHtml(issue)}</li>`).join('')}
|
|
933
1025
|
</ul>
|
|
934
1026
|
</div>
|
|
935
1027
|
`;
|
|
@@ -940,7 +1032,7 @@
|
|
|
940
1032
|
<div class="health-mt-lg">
|
|
941
1033
|
<strong>Recommendations:</strong>
|
|
942
1034
|
<ul class="health-list">
|
|
943
|
-
${normalized.recommendations.map(rec => `<li class="text-warn">${rec}</li>`).join('')}
|
|
1035
|
+
${normalized.recommendations.map(rec => `<li class="text-warn">${escapeHtml(rec)}</li>`).join('')}
|
|
944
1036
|
</ul>
|
|
945
1037
|
</div>
|
|
946
1038
|
`;
|
|
@@ -956,12 +1048,12 @@
|
|
|
956
1048
|
<div class="resource-trend-col">
|
|
957
1049
|
<div class="spark-col">
|
|
958
1050
|
<span class="spark-label">CPU Spark (last ${Math.min(40, t.sampleCount || 0)} samples)</span>
|
|
959
|
-
<span class="spark-value">${t.spark || ''}</span>
|
|
1051
|
+
<span class="spark-value">${escapeHtml(t.spark || '')}</span>
|
|
960
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>
|
|
961
1053
|
</div>
|
|
962
1054
|
<div class="spark-col">
|
|
963
1055
|
<span class="spark-label">Mem Spark (heap)</span>
|
|
964
|
-
<span class="spark-value">${t.memSpark || ''}</span>
|
|
1056
|
+
<span class="spark-value">${escapeHtml(t.memSpark || '')}</span>
|
|
965
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>
|
|
966
1058
|
</div>
|
|
967
1059
|
</div>
|
|
@@ -975,7 +1067,7 @@
|
|
|
975
1067
|
}
|
|
976
1068
|
|
|
977
1069
|
// --- Backup / Restore ---
|
|
978
|
-
// Extracted to js/admin.maintenance.js?v=1.
|
|
1070
|
+
// Extracted to js/admin.maintenance.js?v=1.26.1-9badd8dd
|
|
979
1071
|
|
|
980
1072
|
async function performBackup() {
|
|
981
1073
|
try {
|
|
@@ -1041,7 +1133,7 @@
|
|
|
1041
1133
|
}
|
|
1042
1134
|
|
|
1043
1135
|
async function loadConfiguration() {
|
|
1044
|
-
// 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).
|
|
1045
1137
|
// This inline fallback only fires if the external script failed to load.
|
|
1046
1138
|
if (window.__configExternalLoaded) return;
|
|
1047
1139
|
try {
|
|
@@ -1049,11 +1141,13 @@
|
|
|
1049
1141
|
var data = await res.json();
|
|
1050
1142
|
if (!data.success) throw new Error('Failed to load config');
|
|
1051
1143
|
var cfg = data.config;
|
|
1144
|
+
var safeMaxConnections = escapeHtml(String(cfg.serverSettings.maxConnections ?? ''));
|
|
1145
|
+
var safeRequestTimeout = escapeHtml(String(cfg.serverSettings.requestTimeout ?? ''));
|
|
1052
1146
|
var html = '<form onsubmit="return updateConfiguration(event)">'
|
|
1053
1147
|
+ '<div class="form-group"><label class="form-label">Max Connections</label>'
|
|
1054
|
-
+ '<input class="form-input" type="number" id="cfg-maxConnections" value="' +
|
|
1148
|
+
+ '<input class="form-input" type="number" id="cfg-maxConnections" value="' + safeMaxConnections + '" /></div>'
|
|
1055
1149
|
+ '<div class="form-group"><label class="form-label">Request Timeout (ms)</label>'
|
|
1056
|
-
+ '<input class="form-input" type="number" id="cfg-requestTimeout" value="' +
|
|
1150
|
+
+ '<input class="form-input" type="number" id="cfg-requestTimeout" value="' + safeRequestTimeout + '" /></div>'
|
|
1057
1151
|
+ '<div class="form-group"><label class="form-label">Verbose Logging</label>'
|
|
1058
1152
|
+ '<select class="form-input" id="cfg-verbose"><option value="1"' + (cfg.serverSettings.enableVerboseLogging ? ' selected' : '') + '>Enabled</option>'
|
|
1059
1153
|
+ '<option value="0"' + (!cfg.serverSettings.enableVerboseLogging ? ' selected' : '') + '>Disabled</option></select></div>'
|
|
@@ -1099,10 +1193,10 @@
|
|
|
1099
1193
|
return false;
|
|
1100
1194
|
}
|
|
1101
1195
|
|
|
1102
|
-
// Monitoring functions moved to js/admin.monitor.js?v=1.
|
|
1196
|
+
// Monitoring functions moved to js/admin.monitor.js?v=1.26.1-9badd8dd
|
|
1103
1197
|
|
|
1104
1198
|
// ===== Log Viewer =====
|
|
1105
|
-
// Extracted to js/admin.logs.js?v=1.
|
|
1199
|
+
// Extracted to js/admin.logs.js?v=1.26.1-9badd8dd
|
|
1106
1200
|
|
|
1107
1201
|
// ===== Instruction Management =====
|
|
1108
1202
|
let instructionEditing = null;
|
|
@@ -1241,18 +1335,23 @@
|
|
|
1241
1335
|
let short = rawSummary.slice(0, 160);
|
|
1242
1336
|
if (rawSummary.length > 160) short += '…';
|
|
1243
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());
|
|
1244
1343
|
return `
|
|
1245
1344
|
<div class="session-item instr-item-bg">
|
|
1246
1345
|
<div class="session-header">
|
|
1247
|
-
<span class="session-id instr-id-bg">${
|
|
1346
|
+
<span class="session-id instr-id-bg">${safeName}</span>
|
|
1248
1347
|
<div>
|
|
1249
|
-
<button class="action-btn"
|
|
1250
|
-
<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>
|
|
1251
1350
|
</div>
|
|
1252
1351
|
</div>
|
|
1253
|
-
<div class="stat-row"><span class="stat-label">Category</span><span class="stat-value">${
|
|
1254
|
-
<div class="stat-row"><span class="stat-label">Size</span><span class="stat-value">${
|
|
1255
|
-
<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>
|
|
1256
1355
|
<div class="stat-row stat-row-top">
|
|
1257
1356
|
<span class="stat-label stat-label-pt">Summary</span>
|
|
1258
1357
|
<span class="stat-value stat-value-wrap">
|
|
@@ -1262,6 +1361,7 @@
|
|
|
1262
1361
|
</div>`;
|
|
1263
1362
|
}).join('');
|
|
1264
1363
|
document.getElementById('instructions-list').innerHTML = rows;
|
|
1364
|
+
wireLegacyInstructionActions(document.getElementById('instructions-list'));
|
|
1265
1365
|
buildInstructionPaginationControls(totalFiltered);
|
|
1266
1366
|
}
|
|
1267
1367
|
|
|
@@ -1593,7 +1693,7 @@
|
|
|
1593
1693
|
setInterval(fetchResourceTrends, 10000);
|
|
1594
1694
|
})();
|
|
1595
1695
|
|
|
1596
|
-
// 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
|
|
1597
1697
|
// Functions exposed globally: loadInstructions, renderInstructionList, editInstruction, saveInstruction, deleteInstruction, etc.
|
|
1598
1698
|
|
|
1599
1699
|
function startAutoRefresh() {
|
|
@@ -1676,14 +1776,29 @@
|
|
|
1676
1776
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
1677
1777
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
|
|
1678
1778
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
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
|
+
});
|
|
1687
1802
|
}
|
|
1688
1803
|
// ---------------- Drilldown Layered SVG (Experimental) -----------------
|
|
1689
1804
|
// ELK auto-layout integration (Option A): dynamically load elkjs for layered layout.
|
|
@@ -2028,13 +2143,15 @@
|
|
|
2028
2143
|
}
|
|
2029
2144
|
function updateDrillLegend(data){
|
|
2030
2145
|
const el = document.getElementById('drill-legend'); if(!el) return;
|
|
2146
|
+
const safeSelected = escapeHtml(data.selected);
|
|
2147
|
+
const safeLayout = escapeHtml(data.layout);
|
|
2031
2148
|
el.innerHTML = `<div class="drill-legend-title">Legend / Stats</div>` +
|
|
2032
2149
|
`<div class="drill-legend-body">`+
|
|
2033
|
-
`Selected: <b>${
|
|
2150
|
+
`Selected: <b>${safeSelected}</b>${data.expansionEnabled? ' (expand '+(data.expanded||0)+')':''}<br>`+
|
|
2034
2151
|
`Rendered Instructions: <b>${data.total}</b><br>`+
|
|
2035
2152
|
`Instruction Edges: <b>${data.edges}</b><br>`+
|
|
2036
2153
|
`Membership Lines: <b>${data.membership}</b> ${data.membership? '(primary category connectors)':''}<br>`+
|
|
2037
|
-
`Layout: <b>${
|
|
2154
|
+
`Layout: <b>${safeLayout}</b>${data.expansionEnabled? ' + expand':''}`+
|
|
2038
2155
|
`</div>`;
|
|
2039
2156
|
}
|
|
2040
2157
|
function exportDrillSvg(){
|
|
@@ -1604,3 +1604,135 @@ a.mermaid-link { color: var(--admin-accent); }
|
|
|
1604
1604
|
}
|
|
1605
1605
|
|
|
1606
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 {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
var _configRefreshTimer = null;
|
|
5
5
|
var _collapsedCategories = {};
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
var escapeHtml = window.adminUtils.escapeHtml;
|
|
8
8
|
|
|
9
9
|
function buildFlagRow(f, featureFlags) {
|
|
10
10
|
var isBool = f.type === 'boolean';
|