feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
111
app/templates/pages/auth/change-password.html
Normal file
111
app/templates/pages/auth/change-password.html
Normal 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 %}
|
||||
83
app/templates/pages/auth/login.html
Normal file
83
app/templates/pages/auth/login.html
Normal file
@@ -0,0 +1,83 @@
|
||||
{% 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-cogs fa-3x mb-3"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form
|
||||
hx-post="/api/v1/auth/login"
|
||||
hx-target="#login-result"
|
||||
hx-swap="innerHTML"
|
||||
class="login-form"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Имя пользователя</label>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
class="form-control"
|
||||
placeholder="admin"
|
||||
required
|
||||
autofocus
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Пароль</label>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
class="form-control"
|
||||
placeholder="••••••••"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<div id="login-result" class="mt-3"></div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="fas fa-sign-in-alt me-2"></i>
|
||||
Войти
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<p class="text-muted small">
|
||||
По умолчанию: <strong>admin</strong> / <strong>admin</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Проверяем, истекла ли сессия
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get('expired') === '1') {
|
||||
const resultDiv = document.getElementById('login-result');
|
||||
resultDiv.innerHTML = '<div class="alert alert-warning mt-3"><i class="fas fa-exclamation-triangle me-2"></i>Ваша сессия истекла. Пожалуйста, войдите снова.</div>';
|
||||
}
|
||||
|
||||
const form = document.querySelector('.login-form');
|
||||
|
||||
form.addEventListener('htmx:afterRequest', function(event) {
|
||||
if (event.detail.xhr.status === 200) {
|
||||
const response = JSON.parse(event.detail.xhr.responseText);
|
||||
// Токен сохраняется в cookie автоматически сервером
|
||||
// Перенаправление на главную
|
||||
window.location.href = '/';
|
||||
} else {
|
||||
// Показываем ошибку
|
||||
const resultDiv = document.getElementById('login-result');
|
||||
resultDiv.innerHTML = '<div class="alert alert-danger mt-3">Неверное имя пользователя или пароль</div>';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user