This commit is contained in:
Mateusz Gruszczyński
2025-11-04 09:56:37 +01:00
parent 32ef62e4ac
commit addb21bc3e
34 changed files with 3864 additions and 367 deletions

102
routes/auth_routes.py Normal file
View File

@@ -0,0 +1,102 @@
"""Authentication routes - Login, Logout, Register"""
from flask import Blueprint, render_template, request, redirect, url_for, session, jsonify, flash
from functools import wraps
from database import db
from database.models import User
from datetime import datetime
import logging
auth_bp = Blueprint('auth', __name__)
logger = logging.getLogger(__name__)
def login_required(f):
"""Decorator to require login"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
return redirect(url_for('auth.login', next=request.url))
return f(*args, **kwargs)
return decorated_function
def admin_required(f):
"""Decorator to require admin role"""
@wraps(f)
def decorated_function(*args, **kwargs):
if 'user_id' not in session:
return redirect(url_for('auth.login'))
user = User.query.get(session['user_id'])
if not user or not user.is_admin:
flash('Admin access required', 'danger')
return redirect(url_for('main.index'))
return f(*args, **kwargs)
return decorated_function
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
"""Login page"""
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if not username or not password:
flash('Username and password required', 'warning')
return redirect(url_for('auth.login'))
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
session['user_id'] = user.id
session['username'] = user.username
session['is_admin'] = user.is_admin
session.permanent = True
user.last_login = datetime.utcnow()
db.session.commit()
logger.info(f"[AUTH] User '{username}' logged in", flush=True)
next_page = request.args.get('next')
if next_page and next_page.startswith('/'):
return redirect(next_page)
return redirect(url_for('main.index'))
logger.warning(f"[AUTH] Failed login attempt for '{username}'", flush=True)
flash('Invalid username or password', 'danger')
return render_template('login.html')
@auth_bp.route('/logout')
def logout():
"""Logout"""
username = session.get('username', 'Unknown')
session.clear()
logger.info(f"[AUTH] User '{username}' logged out", flush=True)
flash('You have been logged out', 'info')
return redirect(url_for('auth.login'))
@auth_bp.route('/api/current-user')
def api_current_user():
"""Get current user info (API)"""
if 'user_id' not in session:
return jsonify({'error': 'Not authenticated'}), 401
user = User.query.get(session['user_id'])
if not user:
session.clear()
return jsonify({'error': 'User not found'}), 404
return jsonify({
'id': user.id,
'username': user.username,
'is_admin': user.is_admin,
'created_at': user.created_at.isoformat() if user.created_at else None,
'last_login': user.last_login.isoformat() if user.last_login else None
})