221 lines
5.9 KiB
Python
221 lines
5.9 KiB
Python
|
|
"""
|
||
|
|
Authentication routes - Register, Login, Logout, Profile
|
||
|
|
"""
|
||
|
|
from flask import Blueprint, request, jsonify
|
||
|
|
from app.services.auth_service import AuthService, token_required
|
||
|
|
from app.models.user import User, Customer
|
||
|
|
from app.models.domain import db
|
||
|
|
|
||
|
|
auth_bp = Blueprint('auth', __name__, url_prefix='/api/auth')
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.route('/register', methods=['POST'])
|
||
|
|
def register():
|
||
|
|
"""
|
||
|
|
Register new customer
|
||
|
|
|
||
|
|
Request body:
|
||
|
|
{
|
||
|
|
"email": "user@example.com",
|
||
|
|
"password": "password123",
|
||
|
|
"password_confirm": "password123",
|
||
|
|
"full_name": "John Doe",
|
||
|
|
"company_name": "Acme Inc" (optional)
|
||
|
|
}
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
data = request.json
|
||
|
|
|
||
|
|
# Validate required fields
|
||
|
|
required_fields = ['email', 'password', 'password_confirm', 'full_name']
|
||
|
|
for field in required_fields:
|
||
|
|
if not data.get(field):
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': f'{field} is required'
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
# Validate email format
|
||
|
|
email = data['email'].lower().strip()
|
||
|
|
if '@' not in email or '.' not in email:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': 'Invalid email format'
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
# Validate password match
|
||
|
|
if data['password'] != data['password_confirm']:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': 'Passwords do not match'
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
# Validate password strength
|
||
|
|
password = data['password']
|
||
|
|
if len(password) < 8:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': 'Password must be at least 8 characters'
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
# Register user
|
||
|
|
user, customer, error = AuthService.register_user(
|
||
|
|
email=email,
|
||
|
|
password=password,
|
||
|
|
full_name=data['full_name'].strip(),
|
||
|
|
company_name=data.get('company_name', '').strip() or None
|
||
|
|
)
|
||
|
|
|
||
|
|
if error:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': error
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
# Generate token
|
||
|
|
token = AuthService.generate_token(user.id, user.role)
|
||
|
|
|
||
|
|
return jsonify({
|
||
|
|
'status': 'success',
|
||
|
|
'message': 'Registration successful',
|
||
|
|
'token': token,
|
||
|
|
'user': user.to_dict(),
|
||
|
|
'customer': customer.to_dict()
|
||
|
|
}), 201
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
db.session.rollback()
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': f'Registration failed: {str(e)}'
|
||
|
|
}), 500
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.route('/login', methods=['POST'])
|
||
|
|
def login():
|
||
|
|
"""
|
||
|
|
Login user
|
||
|
|
|
||
|
|
Request body:
|
||
|
|
{
|
||
|
|
"email": "user@example.com",
|
||
|
|
"password": "password123"
|
||
|
|
}
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
data = request.json
|
||
|
|
|
||
|
|
# Validate required fields
|
||
|
|
if not data.get('email') or not data.get('password'):
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': 'Email and password are required'
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
# Login user
|
||
|
|
user, token, error = AuthService.login_user(
|
||
|
|
email=data['email'].lower().strip(),
|
||
|
|
password=data['password']
|
||
|
|
)
|
||
|
|
|
||
|
|
if error:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': error
|
||
|
|
}), 401
|
||
|
|
|
||
|
|
# Get customer profile if customer role
|
||
|
|
customer_data = None
|
||
|
|
if user.role == 'customer' and user.customer:
|
||
|
|
customer_data = user.customer.to_dict()
|
||
|
|
|
||
|
|
return jsonify({
|
||
|
|
'status': 'success',
|
||
|
|
'message': 'Login successful',
|
||
|
|
'token': token,
|
||
|
|
'user': user.to_dict(),
|
||
|
|
'customer': customer_data
|
||
|
|
}), 200
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': f'Login failed: {str(e)}'
|
||
|
|
}), 500
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.route('/me', methods=['GET'])
|
||
|
|
@token_required
|
||
|
|
def get_profile(current_user):
|
||
|
|
"""
|
||
|
|
Get current user profile
|
||
|
|
|
||
|
|
Headers:
|
||
|
|
Authorization: Bearer <token>
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
customer_data = None
|
||
|
|
if current_user.role == 'customer' and current_user.customer:
|
||
|
|
customer_data = current_user.customer.to_dict()
|
||
|
|
|
||
|
|
return jsonify({
|
||
|
|
'status': 'success',
|
||
|
|
'user': current_user.to_dict(),
|
||
|
|
'customer': customer_data
|
||
|
|
}), 200
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': f'Failed to get profile: {str(e)}'
|
||
|
|
}), 500
|
||
|
|
|
||
|
|
|
||
|
|
@auth_bp.route('/verify-token', methods=['POST'])
|
||
|
|
def verify_token():
|
||
|
|
"""
|
||
|
|
Verify if token is valid
|
||
|
|
|
||
|
|
Request body:
|
||
|
|
{
|
||
|
|
"token": "jwt_token_here"
|
||
|
|
}
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
data = request.json
|
||
|
|
token = data.get('token')
|
||
|
|
|
||
|
|
if not token:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': 'Token is required',
|
||
|
|
'valid': False
|
||
|
|
}), 400
|
||
|
|
|
||
|
|
payload = AuthService.verify_token(token)
|
||
|
|
|
||
|
|
if not payload:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': 'Invalid or expired token',
|
||
|
|
'valid': False
|
||
|
|
}), 401
|
||
|
|
|
||
|
|
return jsonify({
|
||
|
|
'status': 'success',
|
||
|
|
'message': 'Token is valid',
|
||
|
|
'valid': True,
|
||
|
|
'payload': {
|
||
|
|
'user_id': payload['user_id'],
|
||
|
|
'role': payload['role']
|
||
|
|
}
|
||
|
|
}), 200
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
return jsonify({
|
||
|
|
'status': 'error',
|
||
|
|
'message': str(e),
|
||
|
|
'valid': False
|
||
|
|
}), 500
|
||
|
|
|