AeThex-Bot-Master/aethex-bot/public/federation.html
sirpiglr 76cad00dd9 Enhance bot with federation protection and new command features
Introduces federation protection listener, expands federation commands, adds federation API endpoints to web server, and updates bot status to 'Protecting the Federation'.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Event-Id: d254ee4b-e69e-44a5-b1be-b89c088a485a
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/FUs0R2K
Replit-Helium-Checkpoint-Created: true
2025-12-10 01:41:25 +00:00

625 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Federation - AeThex | Warden</title>
<style>
:root {
--background: #030712;
--foreground: #f8fafc;
--card: rgba(15, 23, 42, 0.6);
--card-border: rgba(99, 102, 241, 0.15);
--card-border-hover: rgba(99, 102, 241, 0.4);
--primary: #6366f1;
--primary-light: #818cf8;
--secondary: rgba(30, 41, 59, 0.5);
--muted: #64748b;
--border: rgba(51, 65, 85, 0.5);
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Courier New', Courier, monospace;
background: var(--background);
color: var(--foreground);
min-height: 100vh;
line-height: 1.6;
}
.bg-grid {
position: fixed;
inset: 0;
background-image:
linear-gradient(rgba(99, 102, 241, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(99, 102, 241, 0.03) 1px, transparent 1px);
background-size: 64px 64px;
pointer-events: none;
z-index: -2;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
}
header {
background: rgba(3, 7, 18, 0.8);
backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
padding: 1rem 0;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
text-decoration: none;
color: var(--foreground);
}
.logo-icon { width: 40px; height: 40px; border-radius: 8px; }
.logo-text { font-size: 1.25rem; font-weight: 700; }
.nav-links {
display: flex;
gap: 2rem;
align-items: center;
}
.nav-links a {
color: var(--muted);
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
}
.nav-links a:hover, .nav-links a.active { color: var(--foreground); }
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1.25rem;
border-radius: 8px;
font-weight: 600;
text-decoration: none;
transition: all 0.2s;
border: none;
cursor: pointer;
font-size: 0.875rem;
font-family: inherit;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), #3b82f6);
color: white;
}
.btn-secondary {
background: var(--secondary);
border: 1px solid var(--border);
color: var(--foreground);
}
.btn-success { background: var(--success); color: white; }
.btn-danger { background: var(--danger); color: white; }
.btn-sm { padding: 0.375rem 0.75rem; font-size: 0.8rem; }
.hero {
padding: 3rem 0;
text-align: center;
}
.hero h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.hero p {
color: var(--muted);
font-size: 1.1rem;
}
.text-gradient {
background: linear-gradient(135deg, var(--primary), #3b82f6, #06b6d4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-bottom: 3rem;
}
.stat-card {
background: var(--card);
border: 1px solid var(--card-border);
border-radius: 16px;
padding: 1.5rem;
text-align: center;
}
.stat-value {
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(135deg, var(--primary), #3b82f6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-label {
color: var(--muted);
font-size: 0.9rem;
margin-top: 0.25rem;
}
.tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
border-bottom: 1px solid var(--border);
padding-bottom: 1rem;
}
.tab {
padding: 0.75rem 1.5rem;
border-radius: 8px;
cursor: pointer;
background: transparent;
border: 1px solid transparent;
color: var(--muted);
font-family: inherit;
font-weight: 500;
transition: all 0.2s;
}
.tab:hover { color: var(--foreground); }
.tab.active {
background: var(--card);
border-color: var(--primary);
color: var(--foreground);
}
.section { display: none; }
.section.active { display: block; }
.card {
background: var(--card);
border: 1px solid var(--card-border);
border-radius: 16px;
padding: 1.5rem;
margin-bottom: 1rem;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.card-title { font-size: 1.1rem; font-weight: 600; }
.table {
width: 100%;
border-collapse: collapse;
}
.table th, .table td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid var(--border);
}
.table th {
color: var(--muted);
font-size: 0.85rem;
font-weight: 500;
}
.badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
}
.badge-low { background: rgba(255, 255, 0, 0.2); color: #ffd700; }
.badge-medium { background: rgba(255, 153, 0, 0.2); color: #ff9900; }
.badge-high { background: rgba(255, 51, 0, 0.2); color: #ff3300; }
.badge-critical { background: rgba(255, 0, 0, 0.2); color: #ff0000; }
.badge-pending { background: rgba(255, 193, 7, 0.2); color: var(--warning); }
.badge-approved { background: rgba(16, 185, 129, 0.2); color: var(--success); }
.server-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.server-card {
background: var(--card);
border: 1px solid var(--card-border);
border-radius: 12px;
padding: 1.25rem;
transition: all 0.2s;
}
.server-card:hover {
border-color: var(--card-border-hover);
transform: translateY(-2px);
}
.server-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.server-icon {
width: 48px;
height: 48px;
border-radius: 12px;
background: var(--secondary);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
}
.server-name { font-weight: 600; }
.server-category { font-size: 0.8rem; color: var(--muted); }
.server-desc { font-size: 0.9rem; color: var(--muted); margin-bottom: 0.75rem; }
.server-members { font-size: 0.85rem; color: var(--primary-light); }
.leaderboard-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
border-bottom: 1px solid var(--border);
}
.leaderboard-rank {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
background: var(--secondary);
}
.leaderboard-rank.gold { background: linear-gradient(135deg, #ffd700, #ff8c00); color: #000; }
.leaderboard-rank.silver { background: linear-gradient(135deg, #c0c0c0, #a0a0a0); color: #000; }
.leaderboard-rank.bronze { background: linear-gradient(135deg, #cd7f32, #8b4513); color: #fff; }
.leaderboard-info { flex: 1; }
.leaderboard-name { font-weight: 600; }
.leaderboard-tier { font-size: 0.8rem; color: var(--muted); }
.leaderboard-score { font-weight: 700; color: var(--primary-light); }
.empty-state {
text-align: center;
padding: 3rem;
color: var(--muted);
}
@media (max-width: 768px) {
.stats-grid { grid-template-columns: 1fr; }
.nav-links { display: none; }
.tabs { overflow-x: auto; }
}
</style>
</head>
<body>
<div class="bg-grid"></div>
<header>
<div class="container header-content">
<a href="/" class="logo">
<img src="/logo.png" alt="AeThex" class="logo-icon">
<span class="logo-text">AeThex | Warden</span>
</a>
<nav class="nav-links">
<a href="/">Home</a>
<a href="/features">Features</a>
<a href="/commands">Commands</a>
<a href="/federation" class="active">Federation</a>
<a href="/dashboard">Dashboard</a>
</nav>
<a href="https://discord.com/api/oauth2/authorize?client_id=578971245454950421&permissions=8&scope=bot%20applications.commands" class="btn btn-primary" target="_blank">Add to Server</a>
</div>
</header>
<main>
<section class="hero">
<div class="container">
<h1>The <span class="text-gradient">Federation</span></h1>
<p>A network of protected servers. Ban one, ban all.</p>
</div>
</section>
<section class="container">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="totalServers">-</div>
<div class="stat-label">Member Servers</div>
</div>
<div class="stat-card">
<div class="stat-value" id="activeBans">-</div>
<div class="stat-label">Active Bans</div>
</div>
<div class="stat-card">
<div class="stat-value" id="pendingApps">-</div>
<div class="stat-label">Pending Applications</div>
</div>
</div>
<div class="tabs">
<button class="tab active" data-tab="servers">Servers</button>
<button class="tab" data-tab="bans">Global Bans</button>
<button class="tab" data-tab="applications">Applications</button>
<button class="tab" data-tab="leaderboard">Leaderboard</button>
</div>
<div id="servers" class="section active">
<div class="server-grid" id="serverGrid">
<div class="empty-state">Loading servers...</div>
</div>
</div>
<div id="bans" class="section">
<div class="card">
<div class="card-header">
<span class="card-title">Global Ban List</span>
</div>
<table class="table">
<thead>
<tr>
<th>User</th>
<th>Severity</th>
<th>Reason</th>
<th>Date</th>
</tr>
</thead>
<tbody id="banList">
<tr><td colspan="4" class="empty-state">Loading bans...</td></tr>
</tbody>
</table>
</div>
</div>
<div id="applications" class="section">
<div class="card">
<div class="card-header">
<span class="card-title">Pending Applications</span>
</div>
<table class="table">
<thead>
<tr>
<th>Server</th>
<th>Category</th>
<th>Members</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="appList">
<tr><td colspan="5" class="empty-state">Loading applications...</td></tr>
</tbody>
</table>
</div>
</div>
<div id="leaderboard" class="section">
<div class="card">
<div class="card-header">
<span class="card-title">Federation Reputation Leaders</span>
</div>
<div id="leaderboardList">
<div class="empty-state">Loading leaderboard...</div>
</div>
</div>
</div>
</section>
</main>
<script>
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
});
});
async function loadStats() {
try {
const res = await fetch('/api/federation/stats');
const data = await res.json();
document.getElementById('totalServers').textContent = data.totalServers || 0;
document.getElementById('activeBans').textContent = data.activeBans || 0;
document.getElementById('pendingApps').textContent = data.pendingApplications || 0;
} catch (e) {
console.error('Failed to load stats:', e);
}
}
async function loadServers() {
try {
const res = await fetch('/api/federation/servers');
const data = await res.json();
const grid = document.getElementById('serverGrid');
if (!data.servers || data.servers.length === 0) {
grid.innerHTML = '<div class="empty-state">No servers in the federation yet. Use /federation membership apply to join!</div>';
return;
}
const categoryEmojis = { gaming: '🎮', creative: '🎨', development: '💻', education: '📚', community: '👥', business: '🏢' };
grid.innerHTML = data.servers.map(s => `
<div class="server-card">
<div class="server-header">
<div class="server-icon">${categoryEmojis[s.category] || '🌐'}</div>
<div>
<div class="server-name">${s.guild_name}</div>
<div class="server-category">${s.category || 'General'}</div>
</div>
</div>
<div class="server-desc">${s.description || 'No description'}</div>
<div class="server-members">${(s.member_count || 0).toLocaleString()} members</div>
</div>
`).join('');
} catch (e) {
console.error('Failed to load servers:', e);
}
}
async function loadBans() {
try {
const res = await fetch('/api/federation/bans?limit=50');
const data = await res.json();
const tbody = document.getElementById('banList');
if (!data.bans || data.bans.length === 0) {
tbody.innerHTML = '<tr><td colspan="4" class="empty-state">No active bans</td></tr>';
return;
}
tbody.innerHTML = data.bans.map(b => `
<tr>
<td>${b.username || b.user_id}</td>
<td><span class="badge badge-${b.severity}">${b.severity.toUpperCase()}</span></td>
<td>${b.reason.substring(0, 50)}${b.reason.length > 50 ? '...' : ''}</td>
<td>${new Date(b.created_at).toLocaleDateString()}</td>
</tr>
`).join('');
} catch (e) {
console.error('Failed to load bans:', e);
}
}
async function loadApplications() {
try {
const res = await fetch('/api/federation/applications');
const data = await res.json();
const tbody = document.getElementById('appList');
if (!data.applications || data.applications.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" class="empty-state">No applications</td></tr>';
return;
}
tbody.innerHTML = data.applications.map(a => `
<tr>
<td>${a.guild_name}</td>
<td>${a.category || 'General'}</td>
<td>${(a.member_count || 0).toLocaleString()}</td>
<td><span class="badge badge-${a.status}">${a.status.toUpperCase()}</span></td>
<td>
${a.status === 'pending' ? `
<button class="btn btn-success btn-sm" onclick="approveApp(${a.id})">Approve</button>
<button class="btn btn-danger btn-sm" onclick="rejectApp(${a.id})">Reject</button>
` : '-'}
</td>
</tr>
`).join('');
} catch (e) {
console.error('Failed to load applications:', e);
}
}
async function loadLeaderboard() {
try {
const res = await fetch('/api/federation/leaderboard?limit=20');
const data = await res.json();
const container = document.getElementById('leaderboardList');
if (!data.leaderboard || data.leaderboard.length === 0) {
container.innerHTML = '<div class="empty-state">No reputation data yet. Be active across federation servers!</div>';
return;
}
const tierEmojis = { newcomer: '🌱', member: '⭐', veteran: '🏆', elite: '💎', legend: '👑' };
container.innerHTML = data.leaderboard.map((l, i) => {
const rankClass = i === 0 ? 'gold' : i === 1 ? 'silver' : i === 2 ? 'bronze' : '';
return `
<div class="leaderboard-item">
<div class="leaderboard-rank ${rankClass}">${i + 1}</div>
<div class="leaderboard-info">
<div class="leaderboard-name">${l.discord_id}</div>
<div class="leaderboard-tier">${tierEmojis[l.rank_tier] || '🌱'} ${(l.rank_tier || 'newcomer').toUpperCase()}</div>
</div>
<div class="leaderboard-score">${(l.reputation_score || 0).toLocaleString()} rep</div>
</div>
`;
}).join('');
} catch (e) {
console.error('Failed to load leaderboard:', e);
}
}
async function approveApp(id) {
if (!confirm('Approve this application?')) return;
try {
await fetch(`/api/federation/applications/${id}/approve`, { method: 'POST' });
loadApplications();
loadStats();
loadServers();
} catch (e) {
alert('Failed to approve');
}
}
async function rejectApp(id) {
const reason = prompt('Rejection reason:');
if (!reason) return;
try {
await fetch(`/api/federation/applications/${id}/reject`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reason })
});
loadApplications();
loadStats();
} catch (e) {
alert('Failed to reject');
}
}
loadStats();
loadServers();
loadBans();
loadApplications();
loadLeaderboard();
</script>
</body>
</html>