Files
DevOpsLab/app/templates/pages/profile/index.html
Сергей Антропов d4b0d6f848 Исправление синтаксической ошибки в molecule_executor.py и обновление k8s preset'ов
- Исправлена незакрытая скобка в _build_test_command (строка 745)
- Добавлена поддержка k8s preset'ов: выполнение create_k8s_cluster.py перед create.yml
- Обновлены образы в k8s preset'ах: заменен недоступный ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy на inecs/ansible-lab:ubuntu22-latest
- Обновлены preset'ы в базе данных через SQL
- Обновлены файлы: k8s-single.yml, k8s-multi.yml, k8s-istio-full.yml
2026-02-16 00:31:09 +03:00

366 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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-swap="none"
>
<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 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.path === '/api/v1/profile' && event.detail.xhr.status === 200) {
try {
const response = JSON.parse(event.detail.xhr.responseText);
if (response.success) {
// Показываем модальное окно с успешным сообщением
if (window.showMessageModal) {
window.showMessageModal(
response.message || 'Профиль успешно обновлен',
'success',
'Успешно',
function() {
// После закрытия модального окна обновляем страницу
location.reload();
}
);
} else {
// Если функция недоступна, просто обновляем страницу
location.reload();
}
}
} catch (e) {
console.error('Ошибка парсинга ответа:', e);
if (window.showMessageModal) {
window.showMessageModal('Ошибка при обновлении профиля', 'error');
}
}
} else if (event.detail.path === '/api/v1/profile' && event.detail.xhr.status !== 200) {
// Ошибка - показываем в модальном окне
try {
const response = JSON.parse(event.detail.xhr.responseText);
const errorMessage = response.detail || response.message || 'Ошибка при обновлении профиля';
if (window.showMessageModal) {
window.showMessageModal(errorMessage, 'error');
} else {
alert(errorMessage);
}
} catch (e) {
if (window.showMessageModal) {
window.showMessageModal('Ошибка при обновлении профиля', 'error');
} else {
alert('Ошибка при обновлении профиля');
}
}
}
});
});
</script>
{% endblock %}