feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile

- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
Сергей Антропов
2026-02-15 22:59:02 +03:00
parent 23e1a6037b
commit 1fbf9185a2
232 changed files with 38075 additions and 5 deletions

View File

@@ -0,0 +1,111 @@
{% extends "base.html" %}
{% block title %}Смена пароля - DevOpsLab{% endblock %}
{% block content %}
<div class="login-container">
<div class="login-card">
<div class="card-header">
<i class="fas fa-key fa-3x mb-3"></i>
<h1>Смена пароля</h1>
<p class="text-muted">Измените пароль для пользователя {{ current_user.username }}</p>
</div>
<div class="card-body">
<form
hx-post="/api/v1/auth/change-password"
hx-target="#change-password-result"
hx-swap="innerHTML"
class="login-form"
>
<div class="form-group">
<label class="form-label">Текущий пароль</label>
<input
type="password"
name="current_password"
class="form-control"
placeholder="••••••••"
required
autofocus
>
</div>
<div class="form-group">
<label class="form-label">Новый пароль</label>
<input
type="password"
name="new_password"
class="form-control"
placeholder="••••••••"
required
minlength="6"
>
</div>
<div class="form-group">
<label class="form-label">Подтвердите новый пароль</label>
<input
type="password"
name="new_password_confirm"
class="form-control"
placeholder="••••••••"
required
minlength="6"
>
</div>
<div id="change-password-result" class="mt-3"></div>
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-save me-2"></i>
Изменить пароль
</button>
</form>
<div class="mt-4 text-center">
<a href="/" class="btn btn-link">
<i class="fas fa-arrow-left me-2"></i>
Назад
</a>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('.login-form');
const newPassword = form.querySelector('input[name="new_password"]');
const newPasswordConfirm = form.querySelector('input[name="new_password_confirm"]');
// Проверка совпадения паролей
function validatePasswords() {
if (newPassword.value !== newPasswordConfirm.value) {
newPasswordConfirm.setCustomValidity('Пароли не совпадают');
} else {
newPasswordConfirm.setCustomValidity('');
}
}
newPassword.addEventListener('input', validatePasswords);
newPasswordConfirm.addEventListener('input', validatePasswords);
form.addEventListener('htmx:afterRequest', function(event) {
if (event.detail.xhr.status === 200) {
const resultDiv = document.getElementById('change-password-result');
resultDiv.innerHTML = '<div class="alert alert-success mt-3">Пароль успешно изменен</div>';
setTimeout(() => {
window.location.href = '/';
}, 2000);
} else {
const resultDiv = document.getElementById('change-password-result');
try {
const response = JSON.parse(event.detail.xhr.responseText);
resultDiv.innerHTML = `<div class="alert alert-danger mt-3">${response.detail || 'Ошибка при смене пароля'}</div>`;
} catch (e) {
resultDiv.innerHTML = '<div class="alert alert-danger mt-3">Ошибка при смене пароля</div>';
}
}
});
});
</script>
{% endblock %}