first commit
This commit is contained in:
193
public_web/app/views/auth_views.py
Normal file
193
public_web/app/views/auth_views.py
Normal file
@@ -0,0 +1,193 @@
|
||||
"""
|
||||
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'):
|
||||
session['user'] = response['result']
|
||||
logger.info("User logged in successfully", user_id=response['result'].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'))
|
||||
Reference in New Issue
Block a user