mirror of
https://github.com/TheFunny/ArisuAutoSweeper
synced 2025-12-16 19:55:12 +00:00
372 lines
14 KiB
HTML
372 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ title }}</title>
|
|
|
|
<!-- Bootstrap CSS -->
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
|
|
<!-- Custom CSS - reuse existing styles -->
|
|
<link rel="stylesheet" href="/assets/css/alas.css">
|
|
<link rel="stylesheet" href="/assets/css/alas-pc.css">
|
|
{% if theme == 'dark' %}
|
|
<link rel="stylesheet" href="/assets/css/dark-alas.css">
|
|
{% else %}
|
|
<link rel="stylesheet" href="/assets/css/light-alas.css">
|
|
{% endif %}
|
|
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.app-container {
|
|
display: grid;
|
|
grid-template-areas:
|
|
"header header header"
|
|
"aside menu content";
|
|
grid-template-columns: 4rem 12rem 1fr;
|
|
grid-template-rows: 3.3125rem 1fr;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
grid-area: header;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 1rem;
|
|
border-bottom: 1px solid var(--border-color, #dee2e6);
|
|
}
|
|
|
|
.aside {
|
|
grid-area: aside;
|
|
overflow-y: auto;
|
|
border-right: 1px solid var(--border-color, #dee2e6);
|
|
}
|
|
|
|
.menu {
|
|
grid-area: menu;
|
|
overflow-y: auto;
|
|
padding: 1rem 0.5rem;
|
|
border-right: 1px solid var(--border-color, #dee2e6);
|
|
}
|
|
|
|
.content {
|
|
grid-area: content;
|
|
overflow-y: auto;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.instance-btn {
|
|
width: 100%;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.log-container {
|
|
background: #1e1e1e;
|
|
color: #d4d4d4;
|
|
padding: 1rem;
|
|
border-radius: 4px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 0.25rem 0.5rem;
|
|
border-radius: 4px;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.status-running { background: #28a745; color: white; }
|
|
.status-stopped { background: #6c757d; color: white; }
|
|
|
|
.card {
|
|
margin-bottom: 1rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app-container">
|
|
<!-- Header -->
|
|
<div class="header">
|
|
<h3 style="margin: 0;">{{ title }}</h3>
|
|
<div class="ms-auto">
|
|
<span id="status-indicator"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aside Navigation -->
|
|
<div class="aside">
|
|
<div class="text-center py-3">
|
|
<button class="btn btn-sm btn-primary instance-btn" onclick="showHome()">Home</button>
|
|
</div>
|
|
<div id="instance-list">
|
|
{% for instance in instances %}
|
|
<div class="text-center py-2">
|
|
<button class="btn btn-sm btn-outline-primary instance-btn"
|
|
onclick="selectInstance('{{ instance }}')">{{ instance }}</button>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Menu -->
|
|
<div class="menu">
|
|
<div id="menu-content">
|
|
<button class="btn btn-sm btn-menu w-100 mb-2" onclick="showOverview()">Overview</button>
|
|
<button class="btn btn-sm btn-menu w-100 mb-2" onclick="showConfig()">Config</button>
|
|
<button class="btn btn-sm btn-menu w-100 mb-2" onclick="showLogs()">Logs</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="content">
|
|
<div id="main-content">
|
|
<h2>Welcome to ArisuAutoSweeper</h2>
|
|
<p>Select an instance from the sidebar to get started.</p>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">System Information</h5>
|
|
<div id="system-info">Loading...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Language & Theme</h5>
|
|
<div class="mb-3">
|
|
<label class="form-label">Language:</label>
|
|
<select class="form-select" id="language-select" onchange="changeLanguage()">
|
|
<option value="zh-CN" {% if language == 'zh-CN' %}selected{% endif %}>简体中文</option>
|
|
<option value="en-US" {% if language == 'en-US' %}selected{% endif %}>English</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Theme:</label>
|
|
<select class="form-select" id="theme-select" onchange="changeTheme()">
|
|
<option value="default" {% if theme != 'dark' %}selected{% endif %}>Light</option>
|
|
<option value="dark" {% if theme == 'dark' %}selected{% endif %}>Dark</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bootstrap JS -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<!-- Custom JavaScript -->
|
|
<script>
|
|
let currentInstance = null;
|
|
let ws = null;
|
|
|
|
// Load system info on startup
|
|
async function loadSystemInfo() {
|
|
try {
|
|
const response = await fetch('/api/system/info');
|
|
const data = await response.json();
|
|
document.getElementById('system-info').innerHTML = `
|
|
<p><strong>Version:</strong> ${data.version}</p>
|
|
<p><strong>Language:</strong> ${data.language}</p>
|
|
<p><strong>Theme:</strong> ${data.theme}</p>
|
|
`;
|
|
} catch (error) {
|
|
console.error('Error loading system info:', error);
|
|
}
|
|
}
|
|
|
|
function showHome() {
|
|
document.getElementById('main-content').innerHTML = `
|
|
<h2>Welcome to ArisuAutoSweeper</h2>
|
|
<p>Select an instance from the sidebar to get started.</p>
|
|
`;
|
|
loadSystemInfo();
|
|
}
|
|
|
|
async function selectInstance(instance) {
|
|
currentInstance = instance;
|
|
document.getElementById('main-content').innerHTML = `
|
|
<h2>${instance}</h2>
|
|
<div id="instance-overview">Loading...</div>
|
|
`;
|
|
await loadInstanceOverview(instance);
|
|
}
|
|
|
|
async function loadInstanceOverview(instance) {
|
|
try {
|
|
const statusResponse = await fetch(`/api/process/${instance}/status`);
|
|
const status = await statusResponse.json();
|
|
|
|
const alive = status.alive;
|
|
const statusClass = alive ? 'status-running' : 'status-stopped';
|
|
const statusText = alive ? 'Running' : 'Stopped';
|
|
|
|
document.getElementById('instance-overview').innerHTML = `
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Status</h5>
|
|
<p>Status: <span class="status-badge ${statusClass}">${statusText}</span></p>
|
|
<div class="btn-group" role="group">
|
|
<button class="btn btn-success" onclick="startProcess('${instance}')">Start</button>
|
|
<button class="btn btn-danger" onclick="stopProcess('${instance}')">Stop</button>
|
|
<button class="btn btn-warning" onclick="restartProcess('${instance}')">Restart</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
} catch (error) {
|
|
console.error('Error loading instance:', error);
|
|
document.getElementById('instance-overview').innerHTML = `
|
|
<div class="alert alert-danger">Error loading instance information</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
async function startProcess(instance) {
|
|
try {
|
|
const response = await fetch(`/api/process/${instance}/start`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({})
|
|
});
|
|
const data = await response.json();
|
|
alert(data.message);
|
|
await loadInstanceOverview(instance);
|
|
} catch (error) {
|
|
alert('Error starting process: ' + error);
|
|
}
|
|
}
|
|
|
|
async function stopProcess(instance) {
|
|
try {
|
|
const response = await fetch(`/api/process/${instance}/stop`, {
|
|
method: 'POST'
|
|
});
|
|
const data = await response.json();
|
|
alert(data.message);
|
|
await loadInstanceOverview(instance);
|
|
} catch (error) {
|
|
alert('Error stopping process: ' + error);
|
|
}
|
|
}
|
|
|
|
async function restartProcess(instance) {
|
|
try {
|
|
const response = await fetch(`/api/process/${instance}/restart`, {
|
|
method: 'POST'
|
|
});
|
|
const data = await response.json();
|
|
alert(data.message);
|
|
await loadInstanceOverview(instance);
|
|
} catch (error) {
|
|
alert('Error restarting process: ' + error);
|
|
}
|
|
}
|
|
|
|
function showOverview() {
|
|
if (currentInstance) {
|
|
loadInstanceOverview(currentInstance);
|
|
} else {
|
|
alert('Please select an instance first');
|
|
}
|
|
}
|
|
|
|
function showConfig() {
|
|
document.getElementById('main-content').innerHTML = `
|
|
<h2>Configuration</h2>
|
|
<p>Configuration editor is under development.</p>
|
|
`;
|
|
}
|
|
|
|
function showLogs() {
|
|
if (!currentInstance) {
|
|
alert('Please select an instance first');
|
|
return;
|
|
}
|
|
|
|
document.getElementById('main-content').innerHTML = `
|
|
<h2>Logs - ${currentInstance}</h2>
|
|
<div class="log-container" id="log-output">
|
|
<div>Connecting to log stream...</div>
|
|
</div>
|
|
`;
|
|
|
|
// Connect WebSocket for logs
|
|
connectLogStream(currentInstance);
|
|
}
|
|
|
|
function connectLogStream(instance) {
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
const wsUrl = `${protocol}//${window.location.host}/ws/logs/${instance}`;
|
|
|
|
ws = new WebSocket(wsUrl);
|
|
|
|
ws.onopen = () => {
|
|
document.getElementById('log-output').innerHTML = '<div>Connected to log stream.</div>';
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const logOutput = document.getElementById('log-output');
|
|
if (logOutput) {
|
|
const data = JSON.parse(event.data);
|
|
logOutput.innerHTML += `<div>[${data.type}] ${JSON.stringify(data)}</div>`;
|
|
logOutput.scrollTop = logOutput.scrollHeight;
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('WebSocket error:', error);
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
console.log('WebSocket connection closed');
|
|
};
|
|
}
|
|
|
|
async function changeLanguage() {
|
|
const language = document.getElementById('language-select').value;
|
|
try {
|
|
await fetch('/api/system/language', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ language })
|
|
});
|
|
location.reload();
|
|
} catch (error) {
|
|
alert('Error changing language: ' + error);
|
|
}
|
|
}
|
|
|
|
async function changeTheme() {
|
|
const theme = document.getElementById('theme-select').value;
|
|
try {
|
|
await fetch('/api/system/theme', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ theme })
|
|
});
|
|
location.reload();
|
|
} catch (error) {
|
|
alert('Error changing theme: ' + error);
|
|
}
|
|
}
|
|
|
|
// Initialize
|
|
window.addEventListener('load', () => {
|
|
loadSystemInfo();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|