0a51001791
Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
7.8 KiB
HTML
154 lines
7.8 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Профиль — PhotoHost{% endblock %}
|
||
|
||
{% block content %}
|
||
<section class="page-header">
|
||
<div class="container">
|
||
<h1 class="page-header__title">Настройки профиля</h1>
|
||
<p class="page-header__subtitle">Безопасность, passkey, сессии и GDPR</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="auth-section profile-section">
|
||
<div class="container profile-grid">
|
||
<div class="auth-card auth-card--wide profile-card">
|
||
{% include "partials/alerts.html" %}
|
||
|
||
<div class="profile-info">
|
||
<div class="profile-info__row">
|
||
<span>Имя пользователя</span>
|
||
<strong>{{ current_user.username }}</strong>
|
||
</div>
|
||
<div class="profile-info__row">
|
||
<span>Роль</span>
|
||
<strong>{% if current_user.is_admin %}Администратор{% else %}Пользователь{% endif %}</strong>
|
||
</div>
|
||
<div class="profile-info__row">
|
||
<span>Дата регистрации</span>
|
||
<strong>{{ current_user.created_at.strftime('%d.%m.%Y') }}</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<h2 class="profile-card__title">Email и пароль</h2>
|
||
<form method="post" class="auth-form">
|
||
<input type="hidden" name="action" value="save">
|
||
<div class="form-group">
|
||
<label for="email">Email</label>
|
||
<input type="email" id="email" name="email" required value="{{ current_user.email }}">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="current_password">Текущий пароль</label>
|
||
<input type="password" id="current_password" name="current_password" required placeholder="для подтверждения изменений">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new_password">Новый пароль (необязательно)</label>
|
||
<input type="password" id="new_password" name="new_password" minlength="6">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="new_password2">Подтверждение нового пароля</label>
|
||
<input type="password" id="new_password2" name="new_password2" minlength="6">
|
||
</div>
|
||
<button type="submit" class="btn btn--primary">Сохранить</button>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="auth-card auth-card--wide profile-card">
|
||
<h2 class="profile-card__title">Passkey</h2>
|
||
<p class="profile-card__hint">Вход без пароля через Face ID, Touch ID, Windows Hello или ключ безопасности.</p>
|
||
|
||
{% if passkeys %}
|
||
<ul class="session-list">
|
||
{% for passkey in passkeys %}
|
||
<li class="session-item">
|
||
<div>
|
||
<strong>{{ passkey.name }}</strong>
|
||
<span class="session-item__meta">Добавлен {{ passkey.created_at.strftime('%d.%m.%Y') }}</span>
|
||
</div>
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="delete_passkey">
|
||
<input type="hidden" name="passkey_id" value="{{ passkey.id }}">
|
||
<button type="submit" class="btn btn--danger btn--sm">Удалить</button>
|
||
</form>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<p class="profile-card__empty">Passkey не настроен</p>
|
||
{% endif %}
|
||
|
||
<div class="form-group" style="margin-top:16px">
|
||
<label for="passkeyName">Название устройства</label>
|
||
<input type="text" id="passkeyName" value="Моё устройство" maxlength="120">
|
||
</div>
|
||
<button type="button" class="btn btn--ghost" id="addPasskeyBtn">Добавить passkey</button>
|
||
</div>
|
||
|
||
<div class="auth-card auth-card--wide profile-card">
|
||
<h2 class="profile-card__title">Активные сессии</h2>
|
||
<p class="profile-card__hint">Все устройства, где выполнен вход в ваш аккаунт.</p>
|
||
|
||
{% if sessions %}
|
||
<ul class="session-list">
|
||
{% for item in sessions %}
|
||
<li class="session-item {% if item.session_key == current_sid %}session-item--current{% endif %}">
|
||
<div>
|
||
<strong>{{ item.device_label }}</strong>
|
||
{% if item.session_key == current_sid %}<span class="badge badge--success">текущая</span>{% endif %}
|
||
<span class="session-item__meta">
|
||
{{ item.ip_address or 'IP неизвестен' }} ·
|
||
{{ item.last_seen_at.strftime('%d.%m.%Y %H:%M') }}
|
||
</span>
|
||
</div>
|
||
{% if item.session_key != current_sid %}
|
||
<form method="post">
|
||
<input type="hidden" name="action" value="revoke_session">
|
||
<input type="hidden" name="session_id" value="{{ item.id }}">
|
||
<button type="submit" class="btn btn--ghost btn--sm">Завершить</button>
|
||
</form>
|
||
{% endif %}
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
<form method="post" style="margin-top:16px">
|
||
<input type="hidden" name="action" value="revoke_all_sessions">
|
||
<button type="submit" class="btn btn--danger btn--sm" onclick="return confirm('Завершить все сессии кроме текущей?');">
|
||
Выйти на всех устройствах
|
||
</button>
|
||
</form>
|
||
{% else %}
|
||
<p class="profile-card__empty">Нет активных сессий</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="auth-card auth-card--wide profile-card">
|
||
<h2 class="profile-card__title">GDPR и данные</h2>
|
||
<p class="profile-card__hint">
|
||
Вы можете скачать копию своих данных или удалить аккаунт.
|
||
<a href="{{ url_for('legal.gdpr') }}">Подробнее о правах</a>
|
||
</p>
|
||
<div class="profile-actions">
|
||
<a href="{{ url_for('cabinet.export_profile') }}" class="btn btn--ghost">Скачать мои данные (JSON)</a>
|
||
</div>
|
||
|
||
<form method="post" class="profile-delete-form" onsubmit="return confirm('Удалить аккаунт без возможности восстановления?');">
|
||
<input type="hidden" name="action" value="delete_account">
|
||
<div class="form-group">
|
||
<label for="delete_password">Пароль для удаления аккаунта</label>
|
||
<input type="password" id="delete_password" name="delete_password" required>
|
||
</div>
|
||
<button type="submit" class="btn btn--danger">Удалить аккаунт</button>
|
||
</form>
|
||
</div>
|
||
|
||
<p class="auth-card__footer profile-footer">
|
||
<a href="{{ url_for('cabinet.index') }}">← Вернуться в кабинет</a>
|
||
</p>
|
||
</div>
|
||
</section>
|
||
{% endblock %}
|
||
|
||
{% block scripts %}
|
||
<script src="{{ url_for('static', filename='js/passkey.js') }}"></script>
|
||
{% endblock %}
|