Add user auth, personal cabinet, admin panel and first admin bootstrap

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-06 22:20:09 +03:00
parent c6a7ecfc4c
commit 61e7290ce8
26 changed files with 1351 additions and 108 deletions
+71
View File
@@ -0,0 +1,71 @@
{% extends "base.html" %}
{% from "macros.html" import format_size %}
{% block title %}Личный кабинет — PhotoHost{% endblock %}
{% block content %}
<section class="page-header">
<div class="container">
<h1 class="page-header__title">Личный кабинет</h1>
<p class="page-header__subtitle">Привет, {{ current_user.username }}! Управляйте своими фотографиями.</p>
<div class="page-header__actions">
<a href="{{ url_for('cabinet.profile') }}" class="btn btn--ghost">Настройки профиля</a>
</div>
</div>
</section>
{% include "partials/alerts.html" %}
<section class="stats-bar">
<div class="container stats">
<div class="stat-card">
<span class="stat-card__value">{{ total_photos }}</span>
<span class="stat-card__label">ваших фото</span>
</div>
<div class="stat-card">
<span class="stat-card__value">{{ format_size(total_size) }}</span>
<span class="stat-card__label">занято места</span>
</div>
<div class="stat-card">
<span class="stat-card__value">до {{ max_upload_mb }} МБ</span>
<span class="stat-card__label">на файл</span>
</div>
</div>
</section>
<section id="upload" class="upload-section">
<div class="container">
<h2 class="section-title">Загрузить фото</h2>
<form action="{{ url_for('main.upload') }}" method="post" enctype="multipart/form-data" class="upload-form" id="uploadForm">
<div class="dropzone" id="dropzone">
<input type="file" name="photo" id="photoInput" accept="image/png,image/jpeg,image/gif,image/webp,image/bmp" hidden>
<div class="dropzone__icon">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M12 16V4m0 0L8 8m4-4l4 4"/>
<path d="M20 16.5v1a2.5 2.5 0 01-2.5 2.5h-11A2.5 2.5 0 014 17.5v-1"/>
</svg>
</div>
<p class="dropzone__title">Перетащите фото сюда</p>
<p class="dropzone__hint">или нажмите для выбора файла</p>
<div class="dropzone__preview" id="preview" hidden>
<img id="previewImg" alt="Предпросмотр">
<span id="previewName"></span>
</div>
</div>
<button type="submit" class="btn btn--primary" id="submitBtn" disabled>Загрузить</button>
</form>
</div>
</section>
<section class="gallery-section">
<div class="container">
<div class="gallery-header">
<h2 class="section-title">Мои фото</h2>
<span class="gallery-count">{{ total_photos }} фото</span>
</div>
{% with photos=photos, empty_title='У вас пока нет фото', empty_text='Загрузите первое изображение выше' %}
{% include "partials/photo_gallery.html" %}
{% endwith %}
</div>
</section>
{% endblock %}
+59
View File
@@ -0,0 +1,59 @@
{% 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">Измените email или пароль</p>
</div>
</section>
<section class="auth-section">
<div class="container auth-container">
<div class="auth-card auth-card--wide">
{% 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>
<form method="post" class="auth-form">
<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" placeholder="оставьте пустым, если не меняете">
</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 btn--full">Сохранить</button>
</form>
<p class="auth-card__footer">
<a href="{{ url_for('cabinet.index') }}">← Вернуться в кабинет</a>
</p>
</div>
</div>
</section>
{% endblock %}