- Add Vertex AI provider (Gemini models via @google-cloud/vertexai SDK) - Add LiteLLM proxy support (OpenAI-compatible, routes to any provider) - Admin panel: model search/discover from provider API, enable/disable, custom models, set default - New endpoints: /config/models/discover, /config/models/add-discovered, /config/models/default - Updated models.js with VERTEX_MODELS and LITELLM_MODELS lists - Updated health endpoint with vertex + litellm status
276 lines
18 KiB
HTML
276 lines
18 KiB
HTML
<div class="module-header">
|
|
<h2><i class="fas fa-user-shield"></i> Admin Panel</h2>
|
|
<p>Manage users, app settings, and view usage statistics</p>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="admin-stats" id="admin-stats">
|
|
<div class="stat-card"><div class="stat-value" id="stat-users">—</div><div class="stat-label">Total Users</div></div>
|
|
<div class="stat-card"><div class="stat-value" id="stat-api-total">—</div><div class="stat-label">Total API Calls</div></div>
|
|
<div class="stat-card"><div class="stat-value" id="stat-api-today">—</div><div class="stat-label">API Calls Today</div></div>
|
|
</div>
|
|
|
|
<!-- Registration -->
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-door-open"></i> Registration</h3></div>
|
|
<div style="padding:16px;display:flex;align-items:center;gap:16px;flex-wrap:wrap;">
|
|
<span id="reg-status-text" style="font-size:13px;color:var(--g600);">Loading...</span>
|
|
<button id="btn-toggle-reg" class="btn-sm btn-primary">Toggle</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Users -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-users"></i> Users</h3>
|
|
<button id="btn-refresh-users" class="btn-sm btn-ghost"><i class="fas fa-rotate"></i> Refresh</button>
|
|
</div>
|
|
<div style="overflow-x:auto;">
|
|
<table class="admin-table" id="admin-users-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name / Email</th>
|
|
<th>Role</th>
|
|
<th>Status</th>
|
|
<th>Joined</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="admin-users-body">
|
|
<tr><td colspan="5" style="text-align:center;color:var(--g400);padding:20px;">Loading...</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: Announcement Banner ──────────────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-bullhorn"></i> Announcement Banner</h3></div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
<label style="font-size:13px;font-weight:600;">Show banner:</label>
|
|
<select id="cms-ann-enabled" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="false">Hidden</option>
|
|
<option value="true">Visible</option>
|
|
</select>
|
|
<select id="cms-ann-type" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="info">Info (blue)</option>
|
|
<option value="warning">Warning (amber)</option>
|
|
<option value="error">Error (red)</option>
|
|
<option value="success">Success (green)</option>
|
|
</select>
|
|
</div>
|
|
<textarea id="cms-ann-text" rows="2" style="font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;resize:vertical;" placeholder="Announcement message..."></textarea>
|
|
<div>
|
|
<button id="btn-save-announcement" class="btn-sm btn-primary">Save & Publish</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: Feature Flags ─────────────────────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-toggle-on"></i> Feature Flags</h3></div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:10px;" id="cms-feature-flags">
|
|
<div style="display:flex;align-items:center;gap:12px;">
|
|
<label style="font-size:13px;font-weight:600;min-width:160px;">Read Aloud (TTS):</label>
|
|
<select id="cms-flag-read-aloud" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="true">Enabled</option>
|
|
<option value="false">Disabled</option>
|
|
</select>
|
|
</div>
|
|
<div style="display:flex;align-items:center;gap:12px;">
|
|
<label style="font-size:13px;font-weight:600;min-width:160px;">Nextcloud Integration:</label>
|
|
<select id="cms-flag-nextcloud" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="true">Enabled</option>
|
|
<option value="false">Disabled</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<button id="btn-save-flags" class="btn-sm btn-primary">Save Flags</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: Email Templates ───────────────────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header"><h3><i class="fas fa-envelope"></i> Email Templates</h3></div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:16px;">
|
|
<!-- Email template selector -->
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
<label style="font-size:13px;font-weight:600;">Template:</label>
|
|
<select id="cms-email-template" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="verify">Email Verification</option>
|
|
<option value="reset">Password Reset</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);margin-bottom:4px;display:block;">Subject</label>
|
|
<input type="text" id="cms-email-subject" style="width:100%;font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="Email subject...">
|
|
</div>
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);margin-bottom:4px;display:block;">Body (plain text — a button link will be appended automatically)</label>
|
|
<textarea id="cms-email-body" rows="4" style="width:100%;font-size:13px;padding:8px;border:1px solid var(--g300);border-radius:6px;resize:vertical;box-sizing:border-box;" placeholder="Email body text..."></textarea>
|
|
</div>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
|
<button id="btn-save-email" class="btn-sm btn-primary">Save Template</button>
|
|
<button id="btn-test-email" class="btn-sm btn-ghost"><i class="fas fa-paper-plane"></i> Send Test</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: AI Prompts ────────────────────────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-robot"></i> AI Prompts</h3>
|
|
<span style="font-size:12px;color:var(--g500);">Changes take effect immediately, no restart needed</span>
|
|
</div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
<label style="font-size:13px;font-weight:600;">Prompt:</label>
|
|
<select id="cms-prompt-select" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;max-width:320px;"></select>
|
|
</div>
|
|
<textarea id="cms-prompt-text" rows="10" style="width:100%;font-size:12px;font-family:monospace;padding:8px;border:1px solid var(--g300);border-radius:6px;resize:vertical;box-sizing:border-box;" placeholder="Loading..."></textarea>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
|
<button id="btn-save-prompt" class="btn-sm btn-primary">Save Prompt</button>
|
|
<button id="btn-reset-prompt" class="btn-sm btn-ghost"><i class="fas fa-rotate-left"></i> Reset to Default</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: SMTP Email Server ─────────────────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-server"></i> SMTP Email Server</h3>
|
|
<span id="smtp-source-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
|
|
</div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">SMTP Host</label>
|
|
<input type="text" id="cms-smtp-host" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="smtp.gmail.com">
|
|
</div>
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Port</label>
|
|
<input type="number" id="cms-smtp-port" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="587" value="587">
|
|
</div>
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Username / Email</label>
|
|
<input type="text" id="cms-smtp-user" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="user@example.com">
|
|
</div>
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">Password / App Password</label>
|
|
<input type="password" id="cms-smtp-pass" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="Leave blank to keep current">
|
|
</div>
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:4px;">From Address</label>
|
|
<input type="text" id="cms-smtp-from" style="width:100%;font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="Pediatric Scribe <noreply@example.com>">
|
|
</div>
|
|
<div style="display:flex;align-items:flex-end;gap:8px;">
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;">TLS/SSL (port 465):</label>
|
|
<select id="cms-smtp-secure" style="font-size:13px;padding:7px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="false">STARTTLS (587)</option>
|
|
<option value="true">SSL/TLS (465)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
|
<button id="btn-save-smtp" class="btn-sm btn-primary">Save SMTP</button>
|
|
<button id="btn-clear-smtp" class="btn-sm btn-ghost"><i class="fas fa-trash"></i> Clear DB (use env)</button>
|
|
</div>
|
|
<p style="font-size:12px;color:var(--g500);margin:0;"><i class="fas fa-info-circle"></i> Environment variables take precedence over DB settings. Storing password in DB is convenient but consider using env vars for production security.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: Saved Encounters (Admin View) ─────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-database"></i> Saved Encounters (Site-wide)</h3>
|
|
<span style="font-size:12px;color:var(--g500);">Auto-deleted after <span id="admin-auto-delete-days">7</span> days</span>
|
|
</div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:10px;">
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
<label style="font-size:13px;font-weight:600;">Auto-delete after:</label>
|
|
<select id="cms-auto-delete-days" style="font-size:13px;padding:4px 8px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="1">1 day</option>
|
|
<option value="3">3 days</option>
|
|
<option value="7" selected>7 days</option>
|
|
<option value="14">14 days</option>
|
|
<option value="30">30 days</option>
|
|
</select>
|
|
<button id="btn-save-auto-delete" class="btn-sm btn-primary">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── AI Model Management ─────────────────────────────────── -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><i class="fas fa-microchip"></i> AI Model Management</h3>
|
|
<span id="admin-model-provider-badge" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--g100);color:var(--g600);">Loading...</span>
|
|
</div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:14px;">
|
|
|
|
<!-- Default Model -->
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
<label style="font-size:13px;font-weight:600;min-width:120px;">Default Model:</label>
|
|
<select id="admin-default-model" style="font-size:13px;padding:5px 8px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:400px;"></select>
|
|
<button id="btn-save-default-model" class="btn-sm btn-primary">Set Default</button>
|
|
</div>
|
|
|
|
<!-- Built-in Models Toggle -->
|
|
<div>
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Built-in Models (toggle to enable/disable for users)</label>
|
|
<div id="admin-builtin-models" style="display:flex;flex-direction:column;gap:4px;max-height:300px;overflow-y:auto;padding-right:4px;">
|
|
<p style="color:var(--g400);font-size:13px;">Loading...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Discover Models from API -->
|
|
<div style="border-top:1px solid var(--g100);padding-top:14px;">
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Discover Models from Provider API</label>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
|
|
<input type="text" id="admin-model-search" placeholder="Search models (e.g. gemini, vendor-model, gpt)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;min-width:200px;">
|
|
<button id="btn-discover-models" class="btn-sm btn-primary"><i class="fas fa-magnifying-glass"></i> Search API</button>
|
|
</div>
|
|
<div id="admin-discovered-models" style="margin-top:10px;display:flex;flex-direction:column;gap:4px;max-height:400px;overflow-y:auto;">
|
|
</div>
|
|
<p id="admin-discover-hint" style="font-size:12px;color:var(--g500);margin:8px 0 0;">Click "Search API" to query your configured provider for all available models. Use the search box to filter results.</p>
|
|
</div>
|
|
|
|
<!-- Custom Model (manual) -->
|
|
<div style="border-top:1px solid var(--g100);padding-top:14px;">
|
|
<label style="font-size:12px;font-weight:600;color:var(--g600);display:block;margin-bottom:8px;">Add Custom Model (manual)</label>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;">
|
|
<input type="text" id="admin-custom-model-id" placeholder="Model ID (e.g. openai/gpt-4.1)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
|
|
<input type="text" id="admin-custom-model-name" placeholder="Display name" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
|
|
<input type="text" id="admin-custom-model-cost" placeholder="Cost (e.g. ~$0.01)" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
|
|
<select id="admin-custom-model-cat" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;">
|
|
<option value="fast">Fast & Cheap</option>
|
|
<option value="smart" selected>Smart</option>
|
|
<option value="premium">Premium</option>
|
|
<option value="free">Free</option>
|
|
</select>
|
|
</div>
|
|
<div style="margin-top:8px;">
|
|
<button id="btn-add-custom-model" class="btn-sm btn-primary"><i class="fas fa-plus"></i> Add Model</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Custom Models List -->
|
|
<div id="admin-custom-models-list" style="display:flex;flex-direction:column;gap:4px;">
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── CMS: Reset to Defaults ─────────────────────────────────── -->
|
|
<div class="card" style="border:1px solid var(--red-light);">
|
|
<div class="card-header"><h3 style="color:var(--red);"><i class="fas fa-triangle-exclamation"></i> Reset Settings</h3></div>
|
|
<div style="padding:16px;display:flex;flex-direction:column;gap:10px;">
|
|
<p style="font-size:13px;color:var(--g600);margin:0;">Reset all announcement, feature flag, and email settings back to factory defaults. SMTP configuration and custom models are preserved.</p>
|
|
<div>
|
|
<button id="btn-reset-all-defaults" class="btn-sm" style="background:var(--red);color:white;border:none;border-radius:6px;padding:6px 14px;font-size:13px;cursor:pointer;"><i class="fas fa-rotate-left"></i> Reset All to Defaults</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|