feat: config file editor with API endpoints, frontend UI, and tests
- Added GET /api/config/list - list whitelisted config files with metadata - Added GET /api/config/read - read file content with line numbers - Added POST /api/config/save - save with auto-backup and YAML validation - Added POST /api/config/backup - manual backup endpoint - Security: whitelist-based path validation, rejects paths outside whitelist - Auto-backup before save with .bak.<timestamp> naming - YAML validation for .yaml/.yml files - Frontend: file list sidebar, code editor with line numbers, status bar - Mobile-responsive layout (file list top, editor below) - Config panel in nav, lazy-loaded when navigated to - 13/13 tests passing
This commit is contained in:
+134
-8
@@ -19,12 +19,63 @@
|
||||
</nav>
|
||||
<main class="content">
|
||||
<section id="agents" class="panel">
|
||||
<h2>Agent Chat</h2>
|
||||
<p>Coming soon...</p>
|
||||
<div id="chat-app">
|
||||
<div class="chat-header">
|
||||
<div class="chat-profile-strip">
|
||||
<span class="chat-profiles-label">Agent:</span>
|
||||
<div id="chat-profile-tabs" class="chat-profile-tabs"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chat-messages" class="chat-messages">
|
||||
<div class="chat-welcome">
|
||||
<div class="chat-welcome-icon">💬</div>
|
||||
<h3>Agent Chat</h3>
|
||||
<p>Select an agent profile above and type a message to start a conversation.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chat-typing" class="chat-typing" hidden>
|
||||
<span class="typing-dot"></span>
|
||||
<span class="typing-dot"></span>
|
||||
<span class="typing-dot"></span>
|
||||
<span class="typing-label">Agent is typing...</span>
|
||||
</div>
|
||||
<div class="chat-input-area">
|
||||
<textarea id="chat-input" rows="1" placeholder="Type a message..." maxlength="2000"></textarea>
|
||||
<button id="chat-send-btn" class="btn btn-primary" disabled>Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="config" class="panel" hidden>
|
||||
<h2>Config Editor</h2>
|
||||
<p>Coming soon...</p>
|
||||
<section id="config" class="panel config-panel" hidden>
|
||||
<div class="config-file-list" id="config-file-list">
|
||||
<div class="config-header">
|
||||
<h2>Config Editor</h2>
|
||||
<span class="config-count" id="config-count">0 files</span>
|
||||
</div>
|
||||
<div class="config-files-container" id="config-files-container">
|
||||
<p class="loading-text">Select a panel to open...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="config-editor" id="config-editor" hidden>
|
||||
<div class="editor-header">
|
||||
<button id="config-back-btn" class="btn btn-sm" title="Back to file list">← Back</button>
|
||||
<span class="editor-filename" id="editor-filename"></span>
|
||||
<div class="editor-actions">
|
||||
<button id="config-save-btn" class="btn btn-primary btn-sm" disabled>Saved ✓</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-body">
|
||||
<div class="editor-line-numbers" id="editor-line-numbers"></div>
|
||||
<textarea class="editor-textarea" id="config-editor-textarea"
|
||||
spellcheck="false" wrap="off" disabled
|
||||
placeholder="Select a file to edit..."></textarea>
|
||||
</div>
|
||||
<div class="editor-status-bar">
|
||||
<span id="status-path"></span>
|
||||
<span id="status-meta"></span>
|
||||
<span id="status-saved" class="status-saved"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="config-toast" class="config-toast" hidden></div>
|
||||
</section>
|
||||
<section id="vault" class="panel" hidden>
|
||||
<div id="vault-app">
|
||||
@@ -46,9 +97,84 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="network" class="panel" hidden>
|
||||
<h2>Network Status</h2>
|
||||
<p>Coming soon...</p>
|
||||
<section id="network" class="panel network-panel" hidden>
|
||||
<div class="network-header">
|
||||
<h2>Network Status</h2>
|
||||
<div class="network-controls">
|
||||
<div class="countdown-bar" id="countdown-bar">
|
||||
<div class="countdown-fill" id="countdown-fill"></div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-refresh" id="network-refresh-btn" title="Refresh now">⟳</button>
|
||||
<label class="auto-refresh-toggle" title="Toggle auto-refresh">
|
||||
<input type="checkbox" id="auto-refresh-toggle" checked>
|
||||
<span>Auto</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Summary Bar -->
|
||||
<div class="network-summary" id="network-summary">
|
||||
<div class="summary-item online"><span class="summary-count" id="hosts-online">0</span> Online</div>
|
||||
<div class="summary-item degraded"><span class="summary-count" id="hosts-degraded">0</span> Degraded</div>
|
||||
<div class="summary-item offline"><span class="summary-count" id="hosts-offline">0</span> Offline</div>
|
||||
<div class="summary-item total"><span class="summary-count" id="hosts-total">0</span> Total</div>
|
||||
</div>
|
||||
|
||||
<div class="network-tabs">
|
||||
<button class="tab-btn active" data-tab="hosts">Hosts</button>
|
||||
<button class="tab-btn" data-tab="services">Services</button>
|
||||
<button class="tab-btn" data-tab="local">Local Services</button>
|
||||
</div>
|
||||
|
||||
<!-- Hosts Tab -->
|
||||
<div class="tab-content active" id="tab-hosts">
|
||||
<div class="hosts-grid" id="hosts-grid">
|
||||
<div class="loading-text">Loading hosts...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Services Tab -->
|
||||
<div class="tab-content" id="tab-services">
|
||||
<div class="services-table-wrap">
|
||||
<table class="services-table" id="services-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th>Category</th>
|
||||
<th>URL / Port</th>
|
||||
<th>Status</th>
|
||||
<th>Latency</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="services-tbody">
|
||||
<tr><td colspan="5" class="loading-text">Loading services...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Local Services Tab -->
|
||||
<div class="tab-content" id="tab-local">
|
||||
<div class="services-table-wrap">
|
||||
<table class="services-table" id="local-services-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Address</th>
|
||||
<th>Port</th>
|
||||
<th>Process</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="local-services-tbody">
|
||||
<tr><td colspan="3" class="loading-text">Loading local services...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="network-footer">
|
||||
<span class="last-updated" id="last-updated">Last updated: —</span>
|
||||
<span class="cache-info" id="cache-info"></span>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user