starting refactor styling
This commit is contained in:
109
api/auth_api.py
109
api/auth_api.py
@@ -1,7 +1,7 @@
|
||||
import logging
|
||||
import secrets, jwt
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from models.user import User
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from flask_mail import Mail, Message
|
||||
from tinydb import Query
|
||||
@@ -57,15 +57,17 @@ def signup():
|
||||
|
||||
token = secrets.token_urlsafe(32)
|
||||
now_iso = datetime.utcnow().isoformat()
|
||||
users_db.insert({
|
||||
'first_name': data['first_name'],
|
||||
'last_name': data['last_name'],
|
||||
'email': data['email'],
|
||||
'password': data['password'], # Hash in production!
|
||||
'verified': False,
|
||||
'verify_token': token,
|
||||
'verify_token_created': now_iso
|
||||
})
|
||||
user = User(
|
||||
first_name=data['first_name'],
|
||||
last_name=data['last_name'],
|
||||
email=data['email'],
|
||||
password=data['password'], # Hash in production!
|
||||
verified=False,
|
||||
verify_token=token,
|
||||
verify_token_created=now_iso,
|
||||
image_id="boy01"
|
||||
)
|
||||
users_db.insert(user.to_dict())
|
||||
send_verification_email(data['email'], token)
|
||||
return jsonify({'message': 'User created, verification email sent'}), 201
|
||||
|
||||
@@ -75,6 +77,7 @@ def verify():
|
||||
status = 'success'
|
||||
reason = ''
|
||||
code = ''
|
||||
user_dict = None
|
||||
user = None
|
||||
|
||||
if not token:
|
||||
@@ -82,13 +85,14 @@ def verify():
|
||||
reason = 'Missing token'
|
||||
code = MISSING_TOKEN
|
||||
else:
|
||||
user = users_db.get(Query().verify_token == token)
|
||||
user_dict = users_db.get(Query().verify_token == token)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if not user:
|
||||
status = 'error'
|
||||
reason = 'Invalid token'
|
||||
code = INVALID_TOKEN
|
||||
else:
|
||||
created_str = user.get('verify_token_created')
|
||||
created_str = user.verify_token_created
|
||||
if not created_str:
|
||||
status = 'error'
|
||||
reason = 'Token timestamp missing'
|
||||
@@ -100,14 +104,17 @@ def verify():
|
||||
reason = 'Token expired'
|
||||
code = TOKEN_EXPIRED
|
||||
else:
|
||||
users_db.update({'verified': True, 'verify_token': None, 'verify_token_created': None}, Query().verify_token == token)
|
||||
user.verified = True
|
||||
user.verify_token = None
|
||||
user.verify_token_created = None
|
||||
users_db.update(user.to_dict(), Query().email == user.email)
|
||||
|
||||
http_status = 200 if status == 'success' else 400
|
||||
if http_status == 200 and user is not None: ##user is verified, create the user's image directory
|
||||
if 'email' not in user:
|
||||
if http_status == 200 and user is not None:
|
||||
if not user.email:
|
||||
logger.error("Verified user has no email field.")
|
||||
else:
|
||||
user_image_dir = get_user_image_dir(sanitize_email(user['email']))
|
||||
user_image_dir = get_user_image_dir(sanitize_email(user.email))
|
||||
os.makedirs(user_image_dir, exist_ok=True)
|
||||
|
||||
return jsonify({'status': status, 'reason': reason, 'code': code}), http_status
|
||||
@@ -119,23 +126,22 @@ def resend_verify():
|
||||
if not email:
|
||||
return jsonify({'error': 'Missing email', 'code': MISSING_EMAIL}), 400
|
||||
|
||||
user = users_db.get(UserQuery.email == email)
|
||||
user_dict = users_db.get(UserQuery.email == email)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if not user:
|
||||
return jsonify({'error': 'User not found', 'code': USER_NOT_FOUND}), 404
|
||||
|
||||
if user.get('verified'):
|
||||
if user.verified:
|
||||
return jsonify({'error': 'Account already verified', 'code': ALREADY_VERIFIED}), 400
|
||||
|
||||
token = secrets.token_urlsafe(32)
|
||||
now_iso = datetime.utcnow().isoformat()
|
||||
users_db.update({
|
||||
'verify_token': token,
|
||||
'verify_token_created': now_iso
|
||||
}, UserQuery.email == email)
|
||||
user.verify_token = token
|
||||
user.verify_token_created = now_iso
|
||||
users_db.update(user.to_dict(), UserQuery.email == email)
|
||||
send_verification_email(email, token)
|
||||
return jsonify({'message': 'Verification email resent'}), 200
|
||||
|
||||
|
||||
@auth_api.route('/login', methods=['POST'])
|
||||
def login():
|
||||
data = request.get_json()
|
||||
@@ -144,11 +150,12 @@ def login():
|
||||
if not email or not password:
|
||||
return jsonify({'error': 'Missing email or password', 'code': MISSING_EMAIL_OR_PASSWORD}), 400
|
||||
|
||||
user = users_db.get(UserQuery.email == email)
|
||||
if not user or user.get('password') != password:
|
||||
user_dict = users_db.get(UserQuery.email == email)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if not user or user.password != password:
|
||||
return jsonify({'error': 'Invalid credentials', 'code': INVALID_CREDENTIALS}), 401
|
||||
|
||||
if not user.get('verified'):
|
||||
if not user.verified:
|
||||
return jsonify({'error': 'This account has not verified', 'code': NOT_VERIFIED}), 403
|
||||
|
||||
payload = {
|
||||
@@ -170,45 +177,39 @@ def me():
|
||||
try:
|
||||
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
email = payload.get('email')
|
||||
user = users_db.get(UserQuery.email == email)
|
||||
user_dict = users_db.get(UserQuery.email == email)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if not user:
|
||||
return jsonify({'error': 'User not found', 'code': USER_NOT_FOUND}), 404
|
||||
return jsonify({
|
||||
'email': user['email'],
|
||||
'id': sanitize_email(user['email']),
|
||||
'first_name': user['first_name'],
|
||||
'last_name': user['last_name'],
|
||||
'verified': user['verified']
|
||||
'email': user.email,
|
||||
'id': sanitize_email(user.email),
|
||||
'first_name': user.first_name,
|
||||
'last_name': user.last_name,
|
||||
'verified': user.verified
|
||||
}), 200
|
||||
except jwt.ExpiredSignatureError:
|
||||
return jsonify({'error': 'Token expired', 'code': TOKEN_EXPIRED}), 401
|
||||
except jwt.InvalidTokenError:
|
||||
return jsonify({'error': 'Invalid token', 'code': INVALID_TOKEN}), 401
|
||||
|
||||
@auth_api.route('/logout', methods=['POST'])
|
||||
def logout():
|
||||
resp = jsonify({'message': 'Logged out'})
|
||||
resp.set_cookie('token', '', expires=0, httponly=True, secure=True, samesite='Strict')
|
||||
return resp, 200
|
||||
|
||||
@auth_api.route('/request-password-reset', methods=['POST'])
|
||||
def request_password_reset():
|
||||
data = request.get_json()
|
||||
email = data.get('email')
|
||||
# Always return success for privacy
|
||||
success_msg = 'If this email is registered, you will receive a password reset link shortly.'
|
||||
|
||||
if not email:
|
||||
return jsonify({'error': 'Missing email', 'code': MISSING_EMAIL}), 400
|
||||
|
||||
user = users_db.get(UserQuery.email == email)
|
||||
user_dict = users_db.get(UserQuery.email == email)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if user:
|
||||
token = secrets.token_urlsafe(32)
|
||||
now_iso = datetime.utcnow().isoformat()
|
||||
users_db.update({
|
||||
'reset_token': token,
|
||||
'reset_token_created': now_iso
|
||||
}, UserQuery.email == email)
|
||||
user.reset_token = token
|
||||
user.reset_token_created = now_iso
|
||||
users_db.update(user.to_dict(), UserQuery.email == email)
|
||||
send_reset_password_email(email, token)
|
||||
|
||||
return jsonify({'message': success_msg}), 200
|
||||
@@ -219,11 +220,12 @@ def validate_reset_token():
|
||||
if not token:
|
||||
return jsonify({'error': 'Missing token', 'code': MISSING_TOKEN}), 400
|
||||
|
||||
user = users_db.get(UserQuery.reset_token == token)
|
||||
user_dict = users_db.get(UserQuery.reset_token == token)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if not user:
|
||||
return jsonify({'error': 'Invalid token', 'code': INVALID_TOKEN}), 400
|
||||
|
||||
created_str = user.get('reset_token_created')
|
||||
created_str = user.reset_token_created
|
||||
if not created_str:
|
||||
return jsonify({'error': 'Token timestamp missing', 'code': TOKEN_TIMESTAMP_MISSING}), 400
|
||||
|
||||
@@ -242,11 +244,12 @@ def reset_password():
|
||||
if not token or not new_password:
|
||||
return jsonify({'error': 'Missing token or password'}), 400
|
||||
|
||||
user = users_db.get(UserQuery.reset_token == token)
|
||||
user_dict = users_db.get(UserQuery.reset_token == token)
|
||||
user = User.from_dict(user_dict) if user_dict else None
|
||||
if not user:
|
||||
return jsonify({'error': 'Invalid token', 'code': INVALID_TOKEN}), 400
|
||||
|
||||
created_str = user.get('reset_token_created')
|
||||
created_str = user.reset_token_created
|
||||
if not created_str:
|
||||
return jsonify({'error': 'Token timestamp missing', 'code': TOKEN_TIMESTAMP_MISSING}), 400
|
||||
|
||||
@@ -254,11 +257,9 @@ def reset_password():
|
||||
if datetime.now(timezone.utc) - created_dt > timedelta(minutes=RESET_PASSWORD_TOKEN_EXPIRY_MINUTES):
|
||||
return jsonify({'error': 'Token expired', 'code': TOKEN_EXPIRED}), 400
|
||||
|
||||
users_db.update({
|
||||
'password': new_password, # Hash in production!
|
||||
'reset_token': None,
|
||||
'reset_token_created': None
|
||||
}, UserQuery.reset_token == token)
|
||||
user.password = new_password # Hash in production!
|
||||
user.reset_token = None
|
||||
user.reset_token_created = None
|
||||
users_db.update(user.to_dict(), UserQuery.email == user.email)
|
||||
|
||||
return jsonify({'message': 'Password has been reset'}), 200
|
||||
|
||||
|
||||
45
api/user_api.py
Normal file
45
api/user_api.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from models.user import User
|
||||
from tinydb import Query
|
||||
from db.db import users_db
|
||||
import jwt
|
||||
|
||||
user_api = Blueprint('user_api', __name__)
|
||||
UserQuery = Query()
|
||||
|
||||
def get_current_user():
|
||||
token = request.cookies.get('token')
|
||||
if not token:
|
||||
return None
|
||||
try:
|
||||
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
email = payload.get('email')
|
||||
user_dict = users_db.get(UserQuery.email == email)
|
||||
return User.from_dict(user_dict) if user_dict else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@user_api.route('/user/profile', methods=['GET'])
|
||||
def get_profile():
|
||||
user = get_current_user()
|
||||
if not user:
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
return jsonify({
|
||||
'first_name': user.first_name,
|
||||
'last_name': user.last_name,
|
||||
'email': user.email,
|
||||
'image_id': user.image_id
|
||||
}), 200
|
||||
|
||||
@user_api.route('/user/image', methods=['PUT'])
|
||||
def update_image():
|
||||
user = get_current_user()
|
||||
if not user:
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
data = request.get_json()
|
||||
image_id = data.get('image_id')
|
||||
if not image_id:
|
||||
return jsonify({'error': 'Missing image_id'}), 400
|
||||
user.image_id = image_id
|
||||
users_db.update(user.to_dict(), UserQuery.email == user.email)
|
||||
return jsonify({'message': 'Image updated', 'image_id': image_id}), 200
|
||||
Reference in New Issue
Block a user