first commit

This commit is contained in:
Mateusz Gruszczyński
2026-03-13 15:17:32 +01:00
commit 986ffb200a
91 changed files with 4423 additions and 0 deletions

0
app/auth/__init__.py Normal file
View File

106
app/auth/routes.py Normal file
View File

@@ -0,0 +1,106 @@
from __future__ import annotations
from flask import Blueprint, current_app, flash, redirect, render_template, url_for
from flask_login import current_user, login_required, login_user, logout_user
from ..extensions import db, limiter
from ..forms import LoginForm, PasswordResetForm, RegistrationForm, ResetRequestForm
from ..models import PasswordResetToken, User
from ..services.i18n import translate as _
from ..services.mail import MailService
from ..services.settings import get_bool_setting
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login', methods=['GET', 'POST'])
@limiter.limit('5 per minute')
def login():
if current_user.is_authenticated:
return redirect(url_for('main.dashboard'))
form = LoginForm()
if form.validate_on_submit():
if form.website.data:
flash(_('flash.suspicious_request'), 'danger')
return redirect(url_for('auth.login'))
user = User.query.filter_by(email=form.email.data.lower()).first()
if user and user.check_password(form.password.data) and user.is_active_user:
login_user(user, remember=form.remember_me.data)
flash(_('flash.login_success'), 'success')
return redirect(url_for('main.dashboard'))
flash(_('flash.invalid_credentials'), 'danger')
return render_template('auth/login.html', form=form)
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
if not get_bool_setting('registration_enabled', current_app.config['REGISTRATION_ENABLED']):
flash(_('flash.registration_disabled'), 'warning')
return redirect(url_for('auth.login'))
form = RegistrationForm()
if form.validate_on_submit():
if form.website.data:
flash(_('flash.suspicious_request'), 'danger')
return redirect(url_for('auth.register'))
if User.query.filter_by(email=form.email.data.lower()).first():
flash(_('flash.email_exists'), 'danger')
else:
user = User(
email=form.email.data.lower(),
full_name=form.full_name.data,
language=current_app.config['DEFAULT_LANGUAGE'],
must_change_password=False,
)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash(_('flash.account_created'), 'success')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', form=form)
@auth_bp.route('/logout')
@login_required
def logout():
logout_user()
flash(_('flash.logged_out'), 'success')
return redirect(url_for('auth.login'))
@auth_bp.route('/forgot-password', methods=['GET', 'POST'])
@limiter.limit('3 per minute')
def forgot_password():
form = ResetRequestForm()
if form.validate_on_submit():
if form.website.data:
flash(_('flash.suspicious_request'), 'danger')
return redirect(url_for('auth.forgot_password'))
user = User.query.filter_by(email=form.email.data.lower()).first()
if user:
reset = PasswordResetToken.issue(user)
db.session.add(reset)
db.session.commit()
reset_link = url_for('auth.reset_password', token=reset.token, _external=True)
MailService().send_template(user.email, 'Password reset', 'password_reset', reset_link=reset_link, user=user)
current_app.logger.info('Reset link for %s: %s', user.email, reset_link)
flash(_('flash.reset_link_generated'), 'info')
return redirect(url_for('auth.login'))
return render_template('auth/forgot_password.html', form=form)
@auth_bp.route('/reset-password/<token>', methods=['GET', 'POST'])
def reset_password(token: str):
reset_entry = PasswordResetToken.query.filter_by(token=token).first_or_404()
if not reset_entry.is_valid():
flash(_('flash.reset_invalid'), 'danger')
return redirect(url_for('auth.login'))
form = PasswordResetForm()
if form.validate_on_submit():
reset_entry.user.set_password(form.password.data)
reset_entry.user.must_change_password = False
reset_entry.used_at = db.func.now()
db.session.commit()
flash(_('flash.password_updated'), 'success')
return redirect(url_for('auth.login'))
return render_template('auth/reset_password.html', form=form)