Commit 3f6647d2 authored by Your Name's avatar Your Name

Implement full web dashboard with authentication

- Added jinja2 and itsdangerous dependencies to requirements.txt
- Created templates directory with base layout and dashboard pages
- Implemented login/logout with session-based authentication
- Added dashboard overview page showing server stats
- Added configuration editors for:
  - Providers (providers.json)
  - Rotations (rotations.json)
  - Autoselect (autoselect.json)
  - Condensation prompts (markdown files)
  - Server settings (aisbf.json)
- Dashboard accessible at /dashboard with configurable username/password
- Session middleware with secure random secret key
- Authentication middleware skips dashboard routes
- All config changes saved to ~/.aisbf/ directory
- Dashboard config loaded from aisbf.json (username, password)
- Default credentials: admin/admin
parent 4ee2fc61
This diff is collapsed.
...@@ -12,4 +12,6 @@ anthropic ...@@ -12,4 +12,6 @@ anthropic
langchain-text-splitters langchain-text-splitters
tiktoken tiktoken
torch torch
transformers transformers
\ No newline at end of file jinja2
itsdangerous
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}AISBF Dashboard{% endblock %}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.header { background: #2c3e50; color: white; padding: 20px 0; margin-bottom: 30px; }
.header h1 { font-size: 24px; font-weight: 600; }
.nav { background: white; padding: 15px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.nav a { color: #2c3e50; text-decoration: none; margin-right: 20px; padding: 8px 12px; border-radius: 4px; }
.nav a:hover { background: #ecf0f1; }
.nav a.active { background: #3498db; color: white; }
.content { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.form-group { margin-bottom: 20px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; color: #2c3e50; }
.form-group input, .form-group textarea, .form-group select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; }
.form-group textarea { min-height: 200px; font-family: 'Courier New', monospace; }
.btn { padding: 10px 20px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; }
.btn:hover { background: #2980b9; }
.btn-secondary { background: #95a5a6; }
.btn-secondary:hover { background: #7f8c8d; }
.btn-danger { background: #e74c3c; }
.btn-danger:hover { background: #c0392b; }
.alert { padding: 15px; border-radius: 4px; margin-bottom: 20px; }
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.logout { float: right; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
th { background: #f8f9fa; font-weight: 600; }
.code { background: #f8f9fa; padding: 15px; border-radius: 4px; font-family: 'Courier New', monospace; font-size: 13px; overflow-x: auto; }
</style>
</head>
<body>
<div class="header">
<div class="container">
<h1>AISBF Dashboard</h1>
{% if session.logged_in %}
<a href="/dashboard/logout" class="btn btn-secondary logout">Logout</a>
{% endif %}
</div>
</div>
{% if session.logged_in %}
<div class="container">
<div class="nav">
<a href="/dashboard" {% if request.path == '/dashboard' %}class="active"{% endif %}>Overview</a>
<a href="/dashboard/providers" {% if '/providers' in request.path %}class="active"{% endif %}>Providers</a>
<a href="/dashboard/rotations" {% if '/rotations' in request.path %}class="active"{% endif %}>Rotations</a>
<a href="/dashboard/autoselect" {% if '/autoselect' in request.path %}class="active"{% endif %}>Autoselect</a>
<a href="/dashboard/condensation" {% if '/condensation' in request.path %}class="active"{% endif %}>Condensation</a>
<a href="/dashboard/settings" {% if '/settings' in request.path %}class="active"{% endif %}>Settings</a>
</div>
</div>
{% endif %}
<div class="container">
<div class="content">
{% block content %}{% endblock %}
</div>
</div>
</body>
</html>
{% extends "base.html" %}
{% block title %}{{ title }} - AISBF Dashboard{% endblock %}
{% block content %}
<h2 style="margin-bottom: 30px;">{{ title }}</h2>
{% if success %}
<div class="alert alert-success">{{ success }}</div>
{% endif %}
{% if error %}
<div class="alert alert-error">{{ error }}</div>
{% endif %}
<form method="POST">
<div class="form-group">
<label for="config">Configuration (JSON)</label>
<textarea id="config" name="config" required>{{ config_content }}</textarea>
</div>
<div style="display: flex; gap: 10px;">
<button type="submit" class="btn">Save Changes</button>
<a href="/dashboard" class="btn btn-secondary">Cancel</a>
</div>
</form>
<div style="margin-top: 30px; padding: 15px; background: #f8f9fa; border-radius: 4px;">
<h4 style="margin-bottom: 10px;">Tips:</h4>
<ul style="margin-left: 20px; line-height: 1.8;">
<li>Ensure valid JSON syntax before saving</li>
<li>Changes take effect after server restart</li>
<li>Backup your configuration before making changes</li>
</ul>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}Overview - AISBF Dashboard{% endblock %}
{% block content %}
<h2 style="margin-bottom: 30px;">Dashboard Overview</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="background: #3498db; color: white; padding: 20px; border-radius: 8px;">
<h3 style="font-size: 16px; margin-bottom: 10px;">Providers</h3>
<p style="font-size: 32px; font-weight: bold;">{{ providers_count }}</p>
</div>
<div style="background: #2ecc71; color: white; padding: 20px; border-radius: 8px;">
<h3 style="font-size: 16px; margin-bottom: 10px;">Rotations</h3>
<p style="font-size: 32px; font-weight: bold;">{{ rotations_count }}</p>
</div>
<div style="background: #e74c3c; color: white; padding: 20px; border-radius: 8px;">
<h3 style="font-size: 16px; margin-bottom: 10px;">Autoselect</h3>
<p style="font-size: 32px; font-weight: bold;">{{ autoselect_count }}</p>
</div>
</div>
<h3 style="margin-bottom: 15px;">Server Information</h3>
<table>
<tr>
<th>Setting</th>
<th>Value</th>
</tr>
<tr>
<td>Host</td>
<td>{{ server_config.host }}</td>
</tr>
<tr>
<td>Port</td>
<td>{{ server_config.port }}</td>
</tr>
<tr>
<td>Protocol</td>
<td>{{ server_config.protocol }}</td>
</tr>
<tr>
<td>Authentication</td>
<td>{{ 'Enabled' if server_config.auth_enabled else 'Disabled' }}</td>
</tr>
</table>
<h3 style="margin-top: 30px; margin-bottom: 15px;">Quick Actions</h3>
<div style="display: flex; gap: 10px;">
<a href="/dashboard/providers" class="btn">Manage Providers</a>
<a href="/dashboard/rotations" class="btn">Manage Rotations</a>
<a href="/dashboard/settings" class="btn btn-secondary">Server Settings</a>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}Login - AISBF Dashboard{% endblock %}
{% block content %}
<div style="max-width: 400px; margin: 50px auto;">
<h2 style="margin-bottom: 30px; text-align: center;">Dashboard Login</h2>
{% if error %}
<div class="alert alert-error">{{ error }}</div>
{% endif %}
<form method="POST" action="/dashboard/login">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" name="username" required autofocus>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn" style="width: 100%;">Login</button>
</form>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}Settings - AISBF Dashboard{% endblock %}
{% block content %}
<h2 style="margin-bottom: 30px;">Server Settings</h2>
{% if success %}
<div class="alert alert-success">{{ success }}</div>
{% endif %}
{% if error %}
<div class="alert alert-error">{{ error }}</div>
{% endif %}
<form method="POST">
<h3 style="margin-bottom: 20px;">Server Configuration</h3>
<div class="form-group">
<label for="host">Host</label>
<input type="text" id="host" name="host" value="{{ config.server.host }}" required>
</div>
<div class="form-group">
<label for="port">Port</label>
<input type="number" id="port" name="port" value="{{ config.server.port }}" required>
</div>
<div class="form-group">
<label for="protocol">Protocol</label>
<select id="protocol" name="protocol">
<option value="http" {% if config.server.protocol == 'http' %}selected{% endif %}>HTTP</option>
<option value="https" {% if config.server.protocol == 'https' %}selected{% endif %}>HTTPS</option>
</select>
</div>
<h3 style="margin: 30px 0 20px;">Authentication</h3>
<div class="form-group">
<label>
<input type="checkbox" name="auth_enabled" {% if config.auth.enabled %}checked{% endif %}>
Enable API Authentication
</label>
</div>
<div class="form-group">
<label for="auth_tokens">Auth Tokens (one per line)</label>
<textarea id="auth_tokens" name="auth_tokens" style="min-height: 100px;">{{ '\n'.join(config.auth.tokens) }}</textarea>
</div>
<h3 style="margin: 30px 0 20px;">Dashboard</h3>
<div class="form-group">
<label for="dashboard_username">Dashboard Username</label>
<input type="text" id="dashboard_username" name="dashboard_username" value="{{ config.dashboard.username }}" required>
</div>
<div class="form-group">
<label for="dashboard_password">Dashboard Password</label>
<input type="password" id="dashboard_password" name="dashboard_password" placeholder="Leave blank to keep current">
</div>
<h3 style="margin: 30px 0 20px;">Internal Model</h3>
<div class="form-group">
<label for="internal_model_id">Model ID</label>
<input type="text" id="internal_model_id" name="internal_model_id" value="{{ config.internal_model.model_id }}" required>
</div>
<div style="display: flex; gap: 10px; margin-top: 30px;">
<button type="submit" class="btn">Save Settings</button>
<a href="/dashboard" class="btn btn-secondary">Cancel</a>
</div>
</form>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment