Refactor main.py to implement Flask app, SQLAlchemy models for Bot and BotLog, and health check functionality. Update pyproject.toml with new dependencies and add new HTML templates for the user interface. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e72fc1b7-94bd-4d6c-801f-cbac2fae245c Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 5f598d52-420e-4e2c-88ea-a4c3e41fdcb6 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/e72fc1b7-94bd-4d6c-801f-cbac2fae245c/jW8PJKQ Replit-Helium-Checkpoint-Created: true
254 lines
7.6 KiB
HTML
254 lines
7.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ bot.name }} - Bot Master{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.bot-header-section {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.bot-title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.bot-icon-large {
|
|
width: 64px;
|
|
height: 64px;
|
|
border-radius: 12px;
|
|
background: var(--accent);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.bot-info h1 {
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.bot-type-badge {
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.bot-actions-top {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.info-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.info-card {
|
|
background: var(--bg-card);
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
border: 1px solid var(--border);
|
|
}
|
|
|
|
.info-card h3 {
|
|
color: var(--text-secondary);
|
|
font-size: 0.875rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.75rem 0;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.info-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.info-label {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.info-value {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.stats-row {
|
|
display: flex;
|
|
gap: 2rem;
|
|
}
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-item .value {
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
color: var(--accent);
|
|
}
|
|
|
|
.stat-item .label {
|
|
color: var(--text-secondary);
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.endpoint-display {
|
|
background: var(--bg-secondary);
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 8px;
|
|
font-family: monospace;
|
|
font-size: 0.9rem;
|
|
word-break: break-all;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.danger-zone {
|
|
margin-top: 2rem;
|
|
padding: 1.5rem;
|
|
background: rgba(255, 68, 102, 0.1);
|
|
border: 1px solid var(--danger);
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.danger-zone h3 {
|
|
color: var(--danger);
|
|
margin-bottom: 1rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="bot-header-section">
|
|
<div class="bot-title">
|
|
<div class="bot-icon-large">{{ bot.name[0].upper() }}</div>
|
|
<div class="bot-info">
|
|
<h1>{{ bot.name }}</h1>
|
|
<span class="bot-type-badge">{{ bot.bot_type|capitalize }} Bot</span>
|
|
</div>
|
|
<span class="status-badge status-{{ bot.status }}" style="margin-left: 1rem;">
|
|
<span class="pulse"></span>
|
|
{{ bot.status|capitalize }}
|
|
</span>
|
|
</div>
|
|
<div class="bot-actions-top">
|
|
<button class="btn btn-secondary" id="checkHealthBtn">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M23 4v6h-6M1 20v-6h6"/>
|
|
<path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/>
|
|
</svg>
|
|
Check Status
|
|
</button>
|
|
<a href="/bots/{{ bot.id }}/edit" class="btn btn-secondary">Edit</a>
|
|
</div>
|
|
</div>
|
|
|
|
{% if bot.description %}
|
|
<div class="card" style="margin-bottom: 1.5rem;">
|
|
<p>{{ bot.description }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="info-grid">
|
|
<div class="info-card">
|
|
<h3>Statistics</h3>
|
|
<div class="stats-row">
|
|
<div class="stat-item">
|
|
<div class="value">{{ bot.guild_count or 0 }}</div>
|
|
<div class="label">Servers</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="value">{{ bot.command_count or 0 }}</div>
|
|
<div class="label">Commands</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="value">{{ (bot.uptime_seconds // 3600) if bot.uptime_seconds else 0 }}h</div>
|
|
<div class="label">Uptime</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-card">
|
|
<h3>Configuration</h3>
|
|
<div class="info-item">
|
|
<span class="info-label">Health Endpoint</span>
|
|
</div>
|
|
{% if bot.health_endpoint %}
|
|
<div class="endpoint-display">{{ bot.health_endpoint }}</div>
|
|
{% else %}
|
|
<div class="endpoint-display" style="color: var(--text-secondary);">Not configured</div>
|
|
{% endif %}
|
|
|
|
<div class="info-item" style="margin-top: 1rem;">
|
|
<span class="info-label">Admin Token</span>
|
|
<span class="info-value">{{ "Configured" if bot.admin_token else "Not set" }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-card">
|
|
<h3>Details</h3>
|
|
<div class="info-item">
|
|
<span class="info-label">Created</span>
|
|
<span class="info-value">{{ bot.created_at.strftime('%b %d, %Y %H:%M') if bot.created_at else 'Unknown' }}</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">Last Checked</span>
|
|
<span class="info-value">{{ bot.last_checked.strftime('%b %d, %Y %H:%M') if bot.last_checked else 'Never' }}</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">Last Updated</span>
|
|
<span class="info-value">{{ bot.updated_at.strftime('%b %d, %Y %H:%M') if bot.updated_at else 'Unknown' }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="danger-zone">
|
|
<h3>Danger Zone</h3>
|
|
<p style="color: var(--text-secondary); margin-bottom: 1rem;">Once you delete a bot, there is no going back. Please be certain.</p>
|
|
<form action="/bots/{{ bot.id }}/delete" method="POST" onsubmit="return confirm('Are you sure you want to delete this bot?');">
|
|
<button type="submit" class="btn btn-danger">Delete Bot</button>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.getElementById('checkHealthBtn').addEventListener('click', async function() {
|
|
const btn = this;
|
|
const badge = document.querySelector('.status-badge');
|
|
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<svg class="spinning" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 4v6h-6M1 20v-6h6"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/></svg> Checking...';
|
|
|
|
try {
|
|
const response = await fetch('/bots/{{ bot.id }}/check', { method: 'POST' });
|
|
const result = await response.json();
|
|
|
|
badge.className = `status-badge status-${result.status}`;
|
|
badge.innerHTML = `<span class="pulse"></span> ${result.status.charAt(0).toUpperCase() + result.status.slice(1)}`;
|
|
|
|
if (result.data) {
|
|
setTimeout(() => location.reload(), 500);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
}
|
|
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 4v6h-6M1 20v-6h6"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/></svg> Check Status';
|
|
});
|
|
</script>
|
|
<style>.spinning { animation: spin 1s linear infinite; }</style>
|
|
{% endblock %}
|