- Updated migrations.html to detect production environment and use podman/docker accordingly - Added container runtime info to settings page - Updated VERSION to 2.1.1
190 lines
7.8 KiB
HTML
190 lines
7.8 KiB
HTML
{% extends "shared/frontend/base.html" %}
|
|
|
|
{% block title %}Database Migrationer - BMC Hub{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.migration-table td {
|
|
vertical-align: middle;
|
|
}
|
|
.command-box {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
font-size: 0.85rem;
|
|
background: var(--bg-secondary);
|
|
color: var(--text-primary);
|
|
border: 1px solid rgba(0,0,0,0.08);
|
|
border-radius: 8px;
|
|
padding: 0.75rem 1rem;
|
|
overflow-x: auto;
|
|
}
|
|
.command-actions .btn {
|
|
min-width: 120px;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h2 class="fw-bold mb-1">Database Migrationer</h2>
|
|
<p class="text-muted mb-0">Oversigt over SQL migrations og kommandoer til manuel kørsel</p>
|
|
</div>
|
|
<a href="/settings" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left me-2"></i>Tilbage til indstillinger
|
|
</a>
|
|
</div>
|
|
|
|
<div class="alert alert-warning d-flex align-items-start" role="alert">
|
|
<i class="bi bi-exclamation-triangle me-2 mt-1"></i>
|
|
<div>
|
|
<strong>Vigtigt:</strong> Migrationer køres manuelt. Kør kun migrations du er sikker på, og tag altid backup først.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-lg-8">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0 fw-bold"><i class="bi bi-database me-2"></i>Tilgængelige migrationer</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if migrations and migrations|length > 0 %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover migration-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Fil</th>
|
|
<th>Størrelse</th>
|
|
<th>Sidst ændret</th>
|
|
<th class="text-end">Handling</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for migration in migrations %}
|
|
<tr>
|
|
<td>
|
|
<strong>{{ migration.name }}</strong>
|
|
</td>
|
|
<td>{{ migration.size_kb }} KB</td>
|
|
<td>{{ migration.modified }}</td>
|
|
<td class="text-end d-flex gap-2 justify-content-end">
|
|
<button class="btn btn-sm btn-outline-primary" onclick="showCommand('{{ migration.name }}')">
|
|
<i class="bi bi-terminal me-1"></i>Vis kommando
|
|
</button>
|
|
<button class="btn btn-sm btn-success" onclick="runMigration('{{ migration.name }}', this)">
|
|
<i class="bi bi-play-circle me-1"></i>Kør migration
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="bi bi-info-circle text-muted fs-4"></i>
|
|
<p class="text-muted mb-0 mt-2">Ingen migrations fundet i mappen /migrations.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card shadow-sm border-0 mb-4">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0 fw-bold"><i class="bi bi-lightning-charge me-2"></i>Kommando</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted mb-2">Vælg en migration for at se en klar kommando til manuel kørsel.</p>
|
|
<div id="commandBox" class="command-box">Vælg en migration fra listen.</div>
|
|
<div class="command-actions d-flex gap-2 mt-3">
|
|
<button id="copyCommandBtn" class="btn btn-primary" disabled onclick="copyCommand()">
|
|
<i class="bi bi-clipboard me-2"></i>Kopiér
|
|
</button>
|
|
<button id="copyAltBtn" class="btn btn-outline-secondary" disabled onclick="copyAltCommand()">
|
|
<i class="bi bi-clipboard-plus me-2"></i>Kopiér alt
|
|
</button>
|
|
</div>
|
|
<div id="migrationFeedback" class="alert d-none mt-3" role="alert"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header bg-white">
|
|
<h6 class="mb-0 fw-bold"><i class="bi bi-info-circle me-2"></i>Standard opsætning</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled mb-0">
|
|
<li class="mb-2"><strong>DB bruger:</strong> {{ db_user }}</li>
|
|
<li class="mb-2"><strong>DB navn:</strong> {{ db_name }}</li>
|
|
<li class="mb-2"><strong>DB container:</strong> {{ db_container }}</li>
|
|
<li class="mb-0"><strong>Container runtime:</strong> {{ 'Podman' if is_production else 'Docker' }}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let currentCommand = "";
|
|
let currentAltCommand = "";
|
|
|
|
function buildCommand(migrationName) {
|
|
// Use podman for production, docker for local development
|
|
const runtime = {{ 'true' if is_production else 'false' }} ? 'podman' : 'docker';
|
|
|
|
const containerCmd = `${runtime} exec -i {{ db_container }} psql -U {{ db_user }} -d {{ db_name }} < migrations/${migrationName}`;
|
|
const localCmd = `psql \"$DATABASE_URL\" -f migrations/${migrationName}`;
|
|
currentCommand = containerCmd;
|
|
currentAltCommand = `${containerCmd}\n${localCmd}`;
|
|
document.getElementById('commandBox').textContent = containerCmd;
|
|
document.getElementById('copyCommandBtn').disabled = false;
|
|
document.getElementById('copyAltBtn').disabled = false;
|
|
}
|
|
|
|
function showCommand(migrationName) {
|
|
buildCommand(migrationName);
|
|
}
|
|
|
|
async function copyCommand() {
|
|
if (!currentCommand) return;
|
|
await navigator.clipboard.writeText(currentCommand);
|
|
}
|
|
|
|
async function copyAltCommand() {
|
|
if (!currentAltCommand) return;
|
|
await navigator.clipboard.writeText(currentAltCommand);
|
|
}
|
|
|
|
async function runMigration(migrationName, button) {
|
|
const feedback = document.getElementById('migrationFeedback');
|
|
const url = '/settings/migrations/execute';
|
|
|
|
button.disabled = true;
|
|
feedback.className = 'alert alert-info mt-3';
|
|
feedback.textContent = 'Kører migration...';
|
|
feedback.classList.remove('d-none');
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ file_name: migrationName })
|
|
});
|
|
|
|
const data = await response.json();
|
|
if (!response.ok) throw new Error(data.detail || data.message);
|
|
|
|
feedback.className = 'alert alert-success mt-3';
|
|
feedback.innerHTML = `<strong>Migration kørt</strong><br><pre class="mb-0">${data.output}</pre>`;
|
|
} catch (error) {
|
|
feedback.className = 'alert alert-danger mt-3';
|
|
feedback.innerHTML = `<strong>Fejl</strong><br>${error.message}`;
|
|
} finally {
|
|
button.disabled = false;
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|