46602f1933
Remove dead code identified in docs/SANITIZATION-REVIEW.md:
- app/blueprints/content_old.py and app/blueprints/playlist.py
- app/models/group.py, app/utils/nginx_config_reader.py
- orphaned templates (content_list, edit_content, upload_content,
player_page) and the related group/Template references
Result: 6 blueprints, 82 routes, no dead modules or orphan templates.
Add files that deploy.sh and docker-entrypoint.sh already require but
which were never tracked:
- https_manager.py (referenced by deploy.sh, migrate_network.sh,
docker-entrypoint.sh)
- Caddyfile.example (seeded by deploy.sh; its absence aborts deploy)
Relocate generated Graphify artifacts from graphify-out/ to
docs/graphify-out/ (110 files, no content change) and archive the
superseded docs under docs/.
Ignore hygiene:
- ignore ad-hoc .env backups (.env.bak*) — they contain live secrets
- keep the pre-sanitization snapshots (docs/legacy code/,
docs/old_code_documentation/) on disk but out of the repo
Fix .env.example: drop a duplicated config block, genericize the
hardcoded host IP, and document HOSTNAME_INTERNAL.
211 lines
9.5 KiB
HTML
211 lines
9.5 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Build Player Files - DigiServer v2{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container">
|
||
<div class="page-header">
|
||
<a href="{{ url_for('admin.admin_panel') }}" class="back-link">← Back to Admin Panel</a>
|
||
<h1>🧰 Build Player Files for Deployment</h1>
|
||
<p style="color: #6c757d; margin-top: 6px;">
|
||
Pull the latest player source from a repository onto this server and bake the
|
||
configuration it needs to talk to this server. SSH deployments ship exactly
|
||
this staged copy, so the version you build here is what players run.
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Current staged code status -->
|
||
<div class="card status-card">
|
||
<h2>Current Staged Player Code</h2>
|
||
{% if code_status.available %}
|
||
<p><span class="badge badge-success">✅ Ready</span></p>
|
||
<ul style="line-height: 1.8;">
|
||
<li><strong>Version (git):</strong> <code>{{ code_status.version }}</code></li>
|
||
<li><strong>Size:</strong> {{ code_status.size }}</li>
|
||
{% if code_status.updated_str %}
|
||
<li><strong>Last updated:</strong> {{ code_status.updated_str }}</li>
|
||
{% endif %}
|
||
<li><strong>Path:</strong> <code>{{ code_status.path }}</code></li>
|
||
</ul>
|
||
{% else %}
|
||
<p>
|
||
<span class="badge badge-warning">⚠️ Not staged</span>
|
||
{{ code_status.reason }}
|
||
</p>
|
||
<p style="color: #6c757d;">Path: <code>{{ code_status.path }}</code></p>
|
||
{% endif %}
|
||
{% if settings.built_at %}
|
||
<p style="color: #6c757d; margin-top: 8px;">
|
||
Last build saved: {{ settings.built_at }}
|
||
{% if settings.built_by %}by {{ settings.built_by }}{% endif %}
|
||
</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Live build progress (updated by polling below) -->
|
||
<div class="card" id="build-progress" style="display: none;">
|
||
<h2>Build progress</h2>
|
||
<p id="build-progress-text" style="margin: 0;">
|
||
<span class="badge badge-warning" id="build-progress-badge">⏳ Running</span>
|
||
<span id="build-progress-step"></span>
|
||
</p>
|
||
<p id="build-progress-message" style="color: #6c757d; margin-top: 8px;"></p>
|
||
<div style="margin-top: 10px;">
|
||
<button type="button" class="btn btn-secondary" onclick="window.location.reload()">
|
||
🔄 Refresh page
|
||
</button>
|
||
</div>
|
||
<p style="color: #6c757d; font-size: 13px; margin-top: 10px;">
|
||
The clone takes a couple of minutes. You can leave this page and come back.
|
||
</p>
|
||
</div>
|
||
|
||
<form method="POST" action="{{ url_for('admin.build_player_action') }}">
|
||
<!-- Repository -->
|
||
<div class="card">
|
||
<h2>1. Player Files Repository</h2>
|
||
<div class="form-group">
|
||
<label for="repo_url">Git Repository URL</label>
|
||
<input type="text" id="repo_url" name="repo_url" class="form-control"
|
||
value="{{ settings.repo_url }}"
|
||
placeholder="https://gitea.example.com/org/Kiwy-Signage.git">
|
||
<small style="color: #6c757d;">Cloned/refreshed into the staged directory on this server.</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="branch">Branch</label>
|
||
<input type="text" id="branch" name="branch" class="form-control"
|
||
value="{{ settings.branch }}" placeholder="main">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Server configuration -->
|
||
<div class="card">
|
||
<h2>2. Player → Server Configuration</h2>
|
||
<p style="color: #6c757d;">
|
||
The player contacts the DigiServer API directly at
|
||
<code>{scheme}://{server}:{port}/api/...</code> (no <code>/digiserver</code> path).
|
||
Enter the address players can reach this server on.
|
||
</p>
|
||
<div class="form-group">
|
||
<label for="server_ip">Server IP / Domain</label>
|
||
<input type="text" id="server_ip" name="server_ip" class="form-control"
|
||
value="{{ settings.server_ip }}" placeholder="signage.example.com or 192.168.0.50">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="port">Port</label>
|
||
<input type="number" id="port" name="port" class="form-control"
|
||
value="{{ settings.port }}" min="1" max="65535">
|
||
<small style="color: #6c757d;">Use 443 for HTTPS, 80 for plain HTTP, or your custom port (e.g. 8080).</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
||
<input type="checkbox" name="use_https" {% if settings.use_https %}checked{% endif %}>
|
||
Use HTTPS
|
||
</label>
|
||
</div>
|
||
<div class="form-group">
|
||
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
||
<input type="checkbox" name="verify_ssl" {% if settings.verify_ssl %}checked{% endif %}>
|
||
Verify SSL certificate
|
||
</label>
|
||
<small style="color: #6c757d;">Leave off for self-signed certificates.</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="orientation">Orientation</label>
|
||
<select id="orientation" name="orientation" class="form-control">
|
||
<option value="Landscape" {% if settings.orientation == 'Landscape' %}selected{% endif %}>Landscape</option>
|
||
<option value="Portrait" {% if settings.orientation == 'Portrait' %}selected{% endif %}>Portrait</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="max_resolution">Max Resolution</label>
|
||
<input type="text" id="max_resolution" name="max_resolution" class="form-control"
|
||
value="{{ settings.max_resolution }}" placeholder="1920x1080">
|
||
</div>
|
||
<p style="color: #6c757d; font-size: 13px;">
|
||
ℹ️ The per-player <strong>screen name</strong> and <strong>quick-connect code</strong>
|
||
are filled in automatically for each device during SSH deployment.
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Actions -->
|
||
<div class="card">
|
||
<h2>3. Build</h2>
|
||
<div class="card-actions" style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||
<button type="submit" name="action" value="build_and_config"
|
||
class="btn btn-primary" id="btn-build-all">
|
||
⬇️ Build files & write config
|
||
</button>
|
||
<button type="submit" name="action" value="build_files"
|
||
class="btn btn-secondary" id="btn-build-files">
|
||
Build files only
|
||
</button>
|
||
<button type="submit" name="action" value="save_config" class="btn btn-secondary">
|
||
Write config only
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<script>
|
||
// Poll the build status so the admin sees live progress instead of a request
|
||
// that appears to hang (the clone takes ~1-2 minutes).
|
||
(function () {
|
||
const statusUrl = "{{ url_for('admin.build_player_status') }}";
|
||
const panel = document.getElementById('build-progress');
|
||
const badge = document.getElementById('build-progress-badge');
|
||
const step = document.getElementById('build-progress-step');
|
||
const message = document.getElementById('build-progress-message');
|
||
const bAll = document.getElementById('btn-build-all');
|
||
const bFiles = document.getElementById('btn-build-files');
|
||
let sawRunning = false;
|
||
|
||
function render(s) {
|
||
const state = s.state || 'idle';
|
||
|
||
if (state === 'running') {
|
||
sawRunning = true;
|
||
panel.style.display = 'block';
|
||
badge.className = 'badge badge-warning';
|
||
badge.textContent = '⏳ Running';
|
||
step.textContent = s.step || '';
|
||
message.textContent = '';
|
||
if (bAll) { bAll.disabled = true; bAll.textContent = '⏳ Building…'; }
|
||
if (bFiles) { bFiles.disabled = true; }
|
||
return;
|
||
}
|
||
|
||
// Reached a terminal state.
|
||
if (state === 'success' || state === 'error') {
|
||
panel.style.display = 'block';
|
||
if (state === 'success') {
|
||
badge.className = 'badge badge-success';
|
||
badge.textContent = '✅ Build complete';
|
||
} else {
|
||
badge.className = 'badge badge-danger';
|
||
badge.textContent = '❌ Build failed';
|
||
}
|
||
step.textContent = '';
|
||
message.textContent = s.message || '';
|
||
if (sawRunning) {
|
||
// Reload once so the staged-version panel reflects the new build.
|
||
setTimeout(function () { window.location.reload(); }, 1500);
|
||
}
|
||
}
|
||
}
|
||
|
||
function poll() {
|
||
fetch(statusUrl, { headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
||
.then(function (r) { return r.ok ? r.json() : null; })
|
||
.then(function (s) { if (s) render(s); })
|
||
.catch(function () { /* transient — keep polling */ });
|
||
}
|
||
|
||
poll();
|
||
setInterval(poll, 3000);
|
||
})();
|
||
</script>
|
||
{% endblock %}
|
||
|