Add user registration and auth web app

FastAPI + Jinja2 + MariaDB web application with registration,
login, profile, password reset, and email confirmation flows.
All UI in Russian. Styled with Evotor brand colors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-03-04 22:01:58 +03:00
parent 053fa62395
commit 951c12a208
26 changed files with 929 additions and 0 deletions

43
web/templates/base.html Normal file
View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}EvoSync{% endblock %}</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<nav class="navbar">
<div class="container nav-content">
<a href="/" class="nav-logo">EvoSync</a>
<div class="nav-links">
{% if user %}
<a href="/profile">Личный кабинет</a>
<a href="/logout">Выход</a>
{% else %}
<a href="/login">Вход</a>
<a href="/register">Регистрация</a>
{% endif %}
</div>
</div>
</nav>
<main class="container">
{% if errors %}
<div class="alert alert-error">
{% for error in errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
{% if success %}
<div class="alert alert-success">
<p>{{ success }}</p>
</div>
{% endif %}
{% block content %}{% endblock %}
</main>
</body>
</html>

View File

@@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block title %}Подтверждение email — EvoSync{% endblock %}
{% block content %}
<div class="message-card">
<h1>Подтвердите ваш email</h1>
<p>Ссылка для подтверждения email выведена в консоль сервера.</p>
<p>Скопируйте её и откройте в браузере.</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block title %}Email подтвержден — EvoSync{% endblock %}
{% block content %}
<div class="message-card">
<h1>Email подтвержден!</h1>
<p>Ваш email успешно подтвержден. Теперь вы можете войти в систему.</p>
<p><a href="/login">Войти</a></p>
</div>
{% endblock %}

View File

@@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block title %}Забыли пароль — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Забыли пароль?</h1>
<p style="font-size: 14px; color: #555; margin-bottom: 20px;">
Введите email, указанный при регистрации.
</p>
<form method="post" action="/forgot-password">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
</div>
<button type="submit" class="btn btn-primary">Отправить ссылку для сброса</button>
</form>
<div class="form-links">
<a href="/login">Вернуться ко входу</a>
</div>
</div>
{% endblock %}

23
web/templates/login.html Normal file
View File

@@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block title %}Вход — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Вход</h1>
<form method="post" action="/login">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" value="{{ form.email if form else '' }}" required>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary">Войти</button>
</form>
<div class="form-links">
<a href="/forgot-password">Забыли пароль?</a><br>
<a href="/register">Зарегистрироваться</a>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% extends "base.html" %}
{% block title %}{{ title }} — EvoSync{% endblock %}
{% block content %}
<div class="message-card">
<h1>{{ title }}</h1>
<p>{{ message }}</p>
{% if link %}
<p><a href="{{ link }}">{{ link_text }}</a></p>
{% endif %}
</div>
{% endblock %}

View File

@@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block title %}Личный кабинет — EvoSync{% endblock %}
{% block content %}
<div class="profile-card">
<h1>Личный кабинет</h1>
<form method="post" action="/profile">
<div class="form-group">
<label for="first_name">Имя</label>
<input type="text" id="first_name" name="first_name"
value="{{ form.first_name if form else user.first_name }}">
</div>
<div class="form-group">
<label for="last_name">Фамилия</label>
<input type="text" id="last_name" name="last_name"
value="{{ form.last_name if form else user.last_name }}">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" value="{{ user.email }}" disabled>
</div>
<div class="form-group">
<label for="phone">Телефон</label>
<input type="tel" id="phone" name="phone"
value="{{ form.phone if form else user.phone }}">
</div>
<button type="submit" class="btn btn-primary">Сохранить</button>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,38 @@
{% extends "base.html" %}
{% block title %}Регистрация — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Регистрация</h1>
<form method="post" action="/register">
<div class="form-group">
<label for="first_name">Имя</label>
<input type="text" id="first_name" name="first_name" value="{{ form.first_name if form else '' }}">
</div>
<div class="form-group">
<label for="last_name">Фамилия</label>
<input type="text" id="last_name" name="last_name" value="{{ form.last_name if form else '' }}">
</div>
<div class="form-group">
<label for="email">Email *</label>
<input type="email" id="email" name="email" value="{{ form.email if form else '' }}" required>
</div>
<div class="form-group">
<label for="phone">Телефон *</label>
<input type="tel" id="phone" name="phone" value="{{ form.phone if form else '' }}" required>
</div>
<div class="form-group">
<label for="password">Пароль *</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="password_confirm">Подтверждение пароля *</label>
<input type="password" id="password_confirm" name="password_confirm" required>
</div>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
<div class="form-links">
<a href="/login">Уже есть аккаунт? Войти</a>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block title %}Новый пароль — EvoSync{% endblock %}
{% block content %}
<div class="form-card">
<h1>Новый пароль</h1>
<form method="post" action="/reset-password?token={{ token }}">
<div class="form-group">
<label for="password">Новый пароль</label>
<input type="password" id="password" name="password" required>
</div>
<div class="form-group">
<label for="password_confirm">Подтверждение пароля</label>
<input type="password" id="password_confirm" name="password_confirm" required>
</div>
<button type="submit" class="btn btn-primary">Сменить пароль</button>
</form>
</div>
{% endblock %}