194 lines
5.7 KiB
Python
194 lines
5.7 KiB
Python
"""
|
|
Auth Views Blueprint
|
|
|
|
This module provides web UI routes for authentication:
|
|
- Login page
|
|
- Registration page
|
|
- Password reset pages
|
|
- Email verification
|
|
|
|
All forms use HTMX to submit to the API endpoints.
|
|
"""
|
|
|
|
from flask import Blueprint, render_template, redirect, url_for, request, session
|
|
from app.utils.auth import get_current_user, clear_user_session
|
|
from app.utils.logging import get_logger
|
|
from app.utils.api_client import get_api_client, APIError
|
|
|
|
|
|
# Initialize logger
|
|
logger = get_logger(__file__)
|
|
|
|
# Create blueprint
|
|
auth_bp = Blueprint('auth_views', __name__)
|
|
|
|
|
|
@auth_bp.route('/')
|
|
def index():
|
|
"""
|
|
Landing page / home page.
|
|
|
|
If user is authenticated, redirect to character list.
|
|
Otherwise, redirect to login page.
|
|
"""
|
|
user = get_current_user()
|
|
|
|
if user:
|
|
logger.info("Authenticated user accessing home, redirecting to characters", user_id=user.get('id'))
|
|
return redirect(url_for('character_views.list_characters'))
|
|
|
|
logger.info("Unauthenticated user accessing home, redirecting to login")
|
|
return redirect(url_for('auth_views.login'))
|
|
|
|
|
|
@auth_bp.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
"""
|
|
Display login page and handle login.
|
|
|
|
GET: If user is already authenticated, redirect to character list.
|
|
POST: Authenticate via API and set session.
|
|
"""
|
|
user = get_current_user()
|
|
|
|
if user:
|
|
logger.info("User already authenticated, redirecting to characters", user_id=user.get('id'))
|
|
return redirect(url_for('character_views.list_characters'))
|
|
|
|
if request.method == 'POST':
|
|
# Get form data
|
|
email = request.form.get('email', '').strip()
|
|
password = request.form.get('password', '')
|
|
|
|
if not email or not password:
|
|
return render_template('auth/login.html', error="Email and password are required")
|
|
|
|
# Call API to authenticate
|
|
try:
|
|
api_client = get_api_client()
|
|
response = api_client.post("/api/v1/auth/login", data={
|
|
'email': email,
|
|
'password': password
|
|
})
|
|
|
|
# Store user in session
|
|
if response.get('result') and response['result'].get('user'):
|
|
session['user'] = response['result']['user']
|
|
logger.info("User logged in successfully", user_id=response['result']['user'].get('id'))
|
|
|
|
# Redirect to next page or character list
|
|
next_url = session.pop('next', None)
|
|
if next_url:
|
|
return redirect(next_url)
|
|
return redirect(url_for('character_views.list_characters'))
|
|
|
|
except APIError as e:
|
|
logger.warning("Login failed", error=str(e))
|
|
return render_template('auth/login.html', error=e.message)
|
|
|
|
logger.info("Rendering login page")
|
|
return render_template('auth/login.html')
|
|
|
|
|
|
@auth_bp.route('/register')
|
|
def register():
|
|
"""
|
|
Display registration page.
|
|
|
|
If user is already authenticated, redirect to character list.
|
|
"""
|
|
user = get_current_user()
|
|
|
|
if user:
|
|
logger.info("User already authenticated, redirecting to characters", user_id=user.get('id'))
|
|
return redirect(url_for('character_views.list_characters'))
|
|
|
|
logger.info("Rendering registration page")
|
|
return render_template('auth/register.html')
|
|
|
|
|
|
@auth_bp.route('/forgot-password')
|
|
def forgot_password():
|
|
"""
|
|
Display forgot password page.
|
|
|
|
Allows users to request a password reset email.
|
|
"""
|
|
logger.info("Rendering forgot password page")
|
|
return render_template('auth/forgot_password.html')
|
|
|
|
|
|
@auth_bp.route('/reset-password')
|
|
def reset_password():
|
|
"""
|
|
Display password reset page.
|
|
|
|
This page is accessed via a link in the password reset email.
|
|
The reset token should be in the query parameters.
|
|
"""
|
|
# Get reset token from query parameters
|
|
token = request.args.get('token')
|
|
user_id = request.args.get('userId')
|
|
secret = request.args.get('secret')
|
|
|
|
if not all([token, user_id, secret]):
|
|
logger.warning("Reset password accessed without required parameters")
|
|
# Could redirect to forgot-password with an error message
|
|
return redirect(url_for('auth_views.forgot_password'))
|
|
|
|
logger.info("Rendering password reset page", user_id=user_id)
|
|
return render_template(
|
|
'auth/reset_password.html',
|
|
token=token,
|
|
user_id=user_id,
|
|
secret=secret
|
|
)
|
|
|
|
|
|
@auth_bp.route('/verify-email')
|
|
def verify_email():
|
|
"""
|
|
Display email verification page.
|
|
|
|
This page is accessed via a link in the verification email.
|
|
The verification token should be in the query parameters.
|
|
"""
|
|
# Get verification token from query parameters
|
|
token = request.args.get('token')
|
|
user_id = request.args.get('userId')
|
|
secret = request.args.get('secret')
|
|
|
|
if not all([token, user_id, secret]):
|
|
logger.warning("Email verification accessed without required parameters")
|
|
return redirect(url_for('auth_views.login'))
|
|
|
|
logger.info("Rendering email verification page", user_id=user_id)
|
|
return render_template(
|
|
'auth/verify_email.html',
|
|
token=token,
|
|
user_id=user_id,
|
|
secret=secret
|
|
)
|
|
|
|
|
|
@auth_bp.route('/logout', methods=['POST'])
|
|
def logout():
|
|
"""
|
|
Handle logout by calling API and clearing session.
|
|
|
|
This is a convenience route for non-HTMX logout forms.
|
|
"""
|
|
logger.info("Logout initiated via web form")
|
|
|
|
# Call API to logout (this will invalidate session cookie)
|
|
try:
|
|
api_client = get_api_client()
|
|
api_client.post("/api/v1/auth/logout")
|
|
except APIError as e:
|
|
logger.error("Failed to call logout API", error=str(e))
|
|
|
|
# Clear local session
|
|
clear_user_session()
|
|
|
|
return redirect(url_for('auth_views.login'))
|