Files
DevOpsLab/app/templates/pages/dockerfiles/build-logs.html
Сергей Антропов 1fbf9185a2 feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
2026-02-15 22:59:02 +03:00

244 lines
9.9 KiB
HTML

{% extends "base.html" %}
{% block title %}История сборок - {{ dockerfile.name }} - DevOpsLab{% endblock %}
{% block page_title %}История сборок: {{ dockerfile.name }}{% endblock %}
{% block content %}
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-history me-2"></i>
История сборок Dockerfile
</h5>
<div>
<button type="button" class="btn btn-danger btn-sm" onclick="clearLogs()">
<i class="fas fa-trash me-2"></i>
Очистить логи
</button>
<a href="/dockerfiles/{{ dockerfile.id }}/edit" class="btn btn-secondary btn-sm">
<i class="fas fa-arrow-left me-2"></i>
Назад
</a>
</div>
</div>
<div class="card-body">
{% if logs %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Образ</th>
<th>Тип</th>
<th>Платформы</th>
<th>Статус</th>
<th>Начало</th>
<th>Длительность</th>
<th>Пользователь</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{ log.id }}</td>
<td>
<code>{{ log.image_name }}{% if log.tag %}:{{ log.tag }}{% endif %}</code>
</td>
<td>
{% if log.extra_data and log.extra_data.get('type') == 'push' %}
<span class="badge bg-info">
<i class="fas fa-upload me-1"></i>Push
</span>
{% else %}
<span class="badge bg-secondary">
<i class="fas fa-hammer me-1"></i>Build
</span>
{% endif %}
</td>
<td>
{% if log.extra_data and log.extra_data.get('type') == 'push' %}
{# Для push показываем registry вместо платформ #}
{% if log.extra_data.get('registry') %}
<span class="badge bg-primary">
<i class="fas fa-server me-1"></i>{{ log.extra_data.get('registry') }}
</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
{% elif log.platforms %}
{% for platform in log.platforms %}
<span class="badge bg-info me-1">{{ platform }}</span>
{% endfor %}
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if log.status == "success" %}
<span class="badge bg-success">Успешно</span>
{% elif log.status == "failed" %}
<span class="badge bg-danger">Ошибка</span>
{% else %}
<span class="badge bg-warning">Выполняется</span>
{% endif %}
</td>
<td>
{% if log.started_at %}
{{ log.started_at.strftime('%Y-%m-%d %H:%M:%S') }}
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>
{% if log.duration %}
{{ log.duration }} сек
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>{{ log.user or '-' }}</td>
<td>
<button type="button" class="btn btn-sm btn-primary" onclick="showLogDetail({{ log.id }})">
<i class="fas fa-eye me-1"></i>
Просмотр
</button>
<button type="button" class="btn btn-sm btn-danger" onclick="deleteLog({{ log.id }})">
<i class="fas fa-trash me-1"></i>
Удалить
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Пагинация -->
{% if total_pages > 1 %}
<nav aria-label="Навигация по страницам">
<ul class="pagination justify-content-center">
{% if page > 1 %}
<li class="page-item">
<a class="page-link" href="?page={{ page - 1 }}">Предыдущая</a>
</li>
{% endif %}
{% for p in range(1, total_pages + 1) %}
{% if p == page %}
<li class="page-item active">
<span class="page-link">{{ p }}</span>
</li>
{% elif p <= 3 or p >= total_pages - 2 or (p >= page - 1 and p <= page + 1) %}
<li class="page-item">
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
</li>
{% elif p == 4 or p == total_pages - 3 %}
<li class="page-item disabled">
<span class="page-link">...</span>
</li>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<li class="page-item">
<a class="page-link" href="?page={{ page + 1 }}">Следующая</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
Логи сборки отсутствуют
</div>
{% endif %}
</div>
</div>
<!-- Модальное окно для просмотра лога -->
<div class="modal fade" id="logDetailModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Детали лога сборки</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="log-detail-content" class="log-container" style="height: 500px; overflow-y: auto; background: #1e1e1e; color: #d4d4d4; padding: 1rem; font-family: 'Courier New', monospace; font-size: 0.875rem; white-space: pre-wrap;">
Загрузка...
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
</div>
</div>
</div>
</div>
<script>
async function showLogDetail(logId) {
const modal = new bootstrap.Modal(document.getElementById('logDetailModal'));
const content = document.getElementById('log-detail-content');
content.textContent = 'Загрузка...';
modal.show();
try {
const response = await fetch(`/api/v1/dockerfiles/build-logs/${logId}`);
const data = await response.json();
if (data.logs) {
content.textContent = data.logs;
} else {
content.textContent = 'Логи не найдены';
}
} catch (error) {
content.textContent = `Ошибка загрузки: ${error.message}`;
}
}
async function deleteLog(logId) {
const confirmed = await showConfirmModal('Вы уверены, что хотите удалить этот лог?');
if (!confirmed) {
return;
}
try {
const response = await fetch(`/api/v1/dockerfiles/build-logs/${logId}`, {
method: 'DELETE'
});
if (response.ok) {
location.reload();
} else {
alert('Ошибка при удалении лога');
}
} catch (error) {
alert(`Ошибка: ${error.message}`);
}
}
async function clearLogs() {
const confirmed = await showConfirmModal('Вы уверены, что хотите очистить все логи сборки для этого Dockerfile?');
if (!confirmed) {
return;
}
try {
const response = await fetch(`/api/v1/dockerfiles/{{ dockerfile.id }}/build-logs`, {
method: 'DELETE'
});
if (response.ok) {
location.reload();
} else {
alert('Ошибка при очистке логов');
}
} catch (error) {
alert(`Ошибка: ${error.message}`);
}
}
</script>
{% endblock %}