- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
341 lines
16 KiB
HTML
341 lines
16 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Профиль - DevOpsLab{% endblock %}
|
||
{% block page_title %}Профиль пользователя{% endblock %}
|
||
|
||
{% block header_actions %}
|
||
<a href="/" class="btn btn-secondary btn-sm">
|
||
<i class="fas fa-arrow-left me-2"></i>
|
||
Назад
|
||
</a>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="row">
|
||
<!-- Боковая панель с информацией -->
|
||
<div class="col-12 col-lg-4 mb-3">
|
||
<div class="card">
|
||
<div class="card-body text-center">
|
||
<div class="mb-3">
|
||
<div class="avatar-circle mx-auto mb-3" style="width: 100px; height: 100px; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; align-items: center; justify-content: center; color: white; font-size: 2.5rem; font-weight: bold;">
|
||
{{ user.username[0].upper() }}
|
||
</div>
|
||
<h4 class="mb-1">{{ profile.full_name if profile and profile.full_name else user.username }}</h4>
|
||
<p class="text-muted mb-0">@{{ user.username }}</p>
|
||
</div>
|
||
|
||
<div class="d-grid gap-2 mb-3">
|
||
<span class="badge {% if user.is_superuser %}bg-danger{% else %}bg-secondary{% endif %}">
|
||
{% if user.is_superuser %}
|
||
<i class="fas fa-crown me-1"></i>Администратор
|
||
{% else %}
|
||
<i class="fas fa-user me-1"></i>Пользователь
|
||
{% endif %}
|
||
</span>
|
||
<span class="badge {% if user.is_active %}bg-success{% else %}bg-danger{% endif %}">
|
||
{% if user.is_active %}
|
||
<i class="fas fa-check-circle me-1"></i>Активен
|
||
{% else %}
|
||
<i class="fas fa-times-circle me-1"></i>Неактивен
|
||
{% endif %}
|
||
</span>
|
||
</div>
|
||
|
||
<hr>
|
||
|
||
<div class="text-start">
|
||
<div class="mb-2">
|
||
<small class="text-muted d-block">Дата регистрации</small>
|
||
<strong>{{ user.created_at.strftime('%d.%m.%Y') if user.created_at else 'N/A' }}</strong>
|
||
</div>
|
||
{% if user.updated_at %}
|
||
<div class="mb-2">
|
||
<small class="text-muted d-block">Последнее обновление</small>
|
||
<strong>{{ user.updated_at.strftime('%d.%m.%Y %H:%M') }}</strong>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Статистика -->
|
||
<div class="card mt-3">
|
||
<div class="card-header">
|
||
<h6 class="mb-0">Статистика</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row g-2 text-center">
|
||
<div class="col-6">
|
||
<div class="p-2 bg-light rounded">
|
||
<div class="h4 mb-0 text-primary">{{ stats.presets }}</div>
|
||
<small class="text-muted">Preset'ов</small>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="p-2 bg-light rounded">
|
||
<div class="h4 mb-0 text-info">{{ stats.dockerfiles }}</div>
|
||
<small class="text-muted">Dockerfile'ов</small>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="p-2 bg-light rounded">
|
||
<div class="h4 mb-0 text-success">{{ stats.playbooks }}</div>
|
||
<small class="text-muted">Playbook'ов</small>
|
||
</div>
|
||
</div>
|
||
<div class="col-6">
|
||
<div class="p-2 bg-light rounded">
|
||
<div class="h4 mb-0 text-warning">{{ stats.commands }}</div>
|
||
<small class="text-muted">Команд</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Быстрые ссылки -->
|
||
<div class="card mt-3">
|
||
<div class="card-header">
|
||
<h6 class="mb-0">Быстрые действия</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="d-grid gap-2">
|
||
<a href="/change-password" class="btn btn-outline-primary btn-sm">
|
||
<i class="fas fa-key me-2"></i>
|
||
Сменить пароль
|
||
</a>
|
||
<a href="/profile/docker-settings" class="btn btn-outline-info btn-sm">
|
||
<i class="fab fa-docker me-2"></i>
|
||
Настройки Docker
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Основной контент с вкладками -->
|
||
<div class="col-12 col-lg-8">
|
||
<div class="card">
|
||
<!-- Навигация по вкладкам -->
|
||
<ul class="nav nav-tabs card-header-tabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button
|
||
class="nav-link active"
|
||
id="view-tab"
|
||
data-bs-toggle="tab"
|
||
data-bs-target="#view"
|
||
type="button"
|
||
role="tab"
|
||
>
|
||
<i class="fas fa-eye me-2"></i>
|
||
Просмотр
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button
|
||
class="nav-link"
|
||
id="edit-tab"
|
||
data-bs-toggle="tab"
|
||
data-bs-target="#edit"
|
||
type="button"
|
||
role="tab"
|
||
>
|
||
<i class="fas fa-edit me-2"></i>
|
||
Редактирование
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<div class="tab-content">
|
||
<!-- Вкладка просмотра -->
|
||
<div class="tab-pane fade show active" id="view" role="tabpanel">
|
||
<div class="card-body">
|
||
<h5 class="mb-4">Информация о профиле</h5>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label class="form-label text-muted small">Имя пользователя</label>
|
||
<div class="fw-semibold">{{ user.username }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label class="form-label text-muted small">Полное имя</label>
|
||
<div class="fw-semibold">
|
||
{{ profile.full_name if profile and profile.full_name else 'Не указано' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label class="form-label text-muted small">Email</label>
|
||
<div class="fw-semibold">
|
||
{% if profile and profile.email %}
|
||
<a href="mailto:{{ profile.email }}">{{ profile.email }}</a>
|
||
{% else %}
|
||
Не указан
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label class="form-label text-muted small">Роль</label>
|
||
<div>
|
||
{% if user.is_superuser %}
|
||
<span class="badge bg-danger">Администратор</span>
|
||
{% else %}
|
||
<span class="badge bg-secondary">Пользователь</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr>
|
||
|
||
<h6 class="mb-3">Настройки Docker</h6>
|
||
|
||
<div class="row mb-3">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label class="form-label text-muted small">
|
||
<i class="fab fa-docker me-1"></i>
|
||
Docker Hub
|
||
</label>
|
||
<div>
|
||
{% if profile and profile.dockerhub_username %}
|
||
<span class="badge bg-info">{{ profile.dockerhub_username }}</span>
|
||
{% if profile.dockerhub_repository %}
|
||
<span class="text-muted">/ {{ profile.dockerhub_repository }}</span>
|
||
{% endif %}
|
||
{% else %}
|
||
<span class="text-muted">Не настроено</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label class="form-label text-muted small">
|
||
<i class="fas fa-server me-1"></i>
|
||
Harbor
|
||
</label>
|
||
<div>
|
||
{% if profile and profile.harbor_url %}
|
||
<span class="badge bg-success">{{ profile.harbor_url }}</span>
|
||
{% if profile.harbor_project %}
|
||
<span class="text-muted">/ {{ profile.harbor_project }}</span>
|
||
{% endif %}
|
||
{% else %}
|
||
<span class="text-muted">Не настроено</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="d-flex gap-2">
|
||
<a href="/profile/docker-settings" class="btn btn-outline-primary btn-sm">
|
||
<i class="fab fa-docker me-2"></i>
|
||
Настроить Docker
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Вкладка редактирования -->
|
||
<div class="tab-pane fade" id="edit" role="tabpanel">
|
||
<div class="card-body">
|
||
<h5 class="mb-4">Редактирование профиля</h5>
|
||
|
||
<form
|
||
hx-post="/api/v1/profile"
|
||
hx-target="#result"
|
||
hx-swap="innerHTML"
|
||
>
|
||
<div class="mb-3">
|
||
<label class="form-label">Имя пользователя</label>
|
||
<input
|
||
type="text"
|
||
value="{{ user.username }}"
|
||
class="form-control"
|
||
readonly
|
||
disabled
|
||
>
|
||
<div class="form-text">Имя пользователя нельзя изменить</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Полное имя</label>
|
||
<input
|
||
type="text"
|
||
name="full_name"
|
||
value="{{ profile.full_name if profile and profile.full_name else '' }}"
|
||
class="form-control"
|
||
placeholder="Иван Иванов"
|
||
>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Email</label>
|
||
<input
|
||
type="email"
|
||
name="email"
|
||
value="{{ profile.email if profile and profile.email else '' }}"
|
||
class="form-control"
|
||
placeholder="user@example.com"
|
||
>
|
||
</div>
|
||
|
||
<div id="result" class="mb-3"></div>
|
||
|
||
<div class="d-flex gap-2">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-save me-2"></i>
|
||
Сохранить изменения
|
||
</button>
|
||
<button type="button" class="btn btn-secondary" onclick="location.reload()">
|
||
<i class="fas fa-times me-2"></i>
|
||
Отмена
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// Обработка успешного сохранения профиля
|
||
document.body.addEventListener('htmx:afterRequest', function(event) {
|
||
if (event.detail.target.id === 'result' && event.detail.xhr.status === 200) {
|
||
const response = JSON.parse(event.detail.xhr.responseText);
|
||
if (response.success) {
|
||
// Показываем сообщение об успехе
|
||
const resultDiv = document.getElementById('result');
|
||
resultDiv.innerHTML = '<div class="alert alert-success alert-dismissible fade show" role="alert">' +
|
||
'<i class="fas fa-check-circle me-2"></i>' + response.message +
|
||
'<button type="button" class="btn-close" data-bs-dismiss="alert"></button>' +
|
||
'</div>';
|
||
|
||
// Обновляем страницу через 1.5 секунды
|
||
setTimeout(() => {
|
||
location.reload();
|
||
}, 1500);
|
||
}
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
{% endblock %}
|