Add unit tests for LoginButton component with comprehensive coverage
All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 46s
All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 46s
This commit is contained in:
@@ -3,9 +3,9 @@ import secrets, jwt
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from models.user import User
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from utils.email_instance import email_sender
|
||||
from tinydb import Query
|
||||
import os
|
||||
import utils.email_sender as email_sender
|
||||
|
||||
from api.utils import sanitize_email
|
||||
from config.paths import get_user_image_dir
|
||||
|
||||
@@ -101,7 +101,10 @@ def request_image(id):
|
||||
if image.user_id is not None and image.user_id != user_id:
|
||||
return jsonify({'error': 'Forbidden: image does not belong to user', 'code': 'FORBIDDEN'}), 403
|
||||
filename = f"{image.id}{image.extension}"
|
||||
filepath = os.path.abspath(os.path.join(get_user_image_dir(image.user_id or user_id), filename))
|
||||
if image.user_id is None:
|
||||
filepath = os.path.abspath(os.path.join(get_user_image_dir("default"), filename))
|
||||
else:
|
||||
filepath = os.path.abspath(os.path.join(get_user_image_dir(image.user_id), filename))
|
||||
if not os.path.exists(filepath):
|
||||
return jsonify({'error': 'File not found'}), 404
|
||||
return send_file(filepath)
|
||||
|
||||
@@ -5,8 +5,7 @@ from db.db import users_db
|
||||
import jwt
|
||||
import random
|
||||
import string
|
||||
import smtplib
|
||||
from backend.utils.email_instance import email_sender
|
||||
import utils.email_sender as email_sender
|
||||
from datetime import datetime, timedelta
|
||||
from api.utils import get_validated_user_id
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# File: db/debug.py
|
||||
|
||||
from tinydb import Query
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from api.image_api import IMAGE_TYPE_ICON, IMAGE_TYPE_PROFILE
|
||||
from db.db import task_db, reward_db, image_db
|
||||
@@ -119,7 +121,22 @@ def createDefaultRewards():
|
||||
reward_db.insert(reward.to_dict())
|
||||
|
||||
def initializeImages():
|
||||
"""Initialize the image database with default images if empty."""
|
||||
|
||||
"""Initialize the image database with default images if empty, and copy images to data/images/default."""
|
||||
# Step 1: Create data/images/default directory if it doesn't exist
|
||||
default_img_dir = os.path.join(os.path.dirname(__file__), '../data/images/default')
|
||||
os.makedirs(default_img_dir, exist_ok=True)
|
||||
|
||||
# Step 2: Copy all image files from resources/images/ to data/images/default
|
||||
src_img_dir = os.path.join(os.path.dirname(__file__), '../resources/images')
|
||||
if os.path.exists(src_img_dir):
|
||||
for fname in os.listdir(src_img_dir):
|
||||
src_path = os.path.join(src_img_dir, fname)
|
||||
dst_path = os.path.join(default_img_dir, fname)
|
||||
if os.path.isfile(src_path):
|
||||
shutil.copy2(src_path, dst_path)
|
||||
|
||||
# Original DB initialization logic
|
||||
if len(image_db.all()) == 0:
|
||||
image_defs = [
|
||||
('boy01', IMAGE_TYPE_PROFILE, '.png', True),
|
||||
|
||||
@@ -12,7 +12,6 @@ from api.task_api import task_api
|
||||
from api.user_api import user_api
|
||||
from config.version import get_full_version
|
||||
|
||||
from backend.utils.email_instance import email_sender
|
||||
from db.default import initializeImages, createDefaultTasks, createDefaultRewards
|
||||
from events.broadcaster import Broadcaster
|
||||
from events.sse import sse_response_for_user, send_to_user
|
||||
@@ -49,8 +48,6 @@ app.config.update(
|
||||
|
||||
CORS(app)
|
||||
|
||||
email_sender.init_app(app)
|
||||
|
||||
@app.route("/version")
|
||||
def api_version():
|
||||
return jsonify({"version": get_full_version()})
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
from utils.email_sender import EmailSender
|
||||
|
||||
email_sender = EmailSender()
|
||||
@@ -1,65 +1,56 @@
|
||||
import os
|
||||
from flask import current_app
|
||||
from flask_mail import Mail, Message
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
class EmailSender:
|
||||
def __init__(self, app=None):
|
||||
self.mail = None
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
def send_verification_email(to_email: str, token: str) -> None:
|
||||
verify_url = f"{current_app.config['FRONTEND_URL']}/auth/verify?token={token}"
|
||||
html_body = f'Click <a href="{verify_url}">here</a> to verify your account.'
|
||||
msg = Message(
|
||||
subject="Verify your account",
|
||||
recipients=[to_email],
|
||||
html=html_body,
|
||||
sender=current_app.config.get('MAIL_DEFAULT_SENDER', 'no-reply@reward-app.local')
|
||||
)
|
||||
try:
|
||||
Mail(current_app).send(msg)
|
||||
print(f"[EMAIL to {to_email}] Verification: {verify_url}")
|
||||
except Exception:
|
||||
print(f"Failed to send email to {to_email}. Verification link: {verify_url}")
|
||||
|
||||
def init_app(self, app):
|
||||
self.mail = Mail(app)
|
||||
def send_reset_password_email(to_email: str, token: str) -> None:
|
||||
reset_url = f"{current_app.config['FRONTEND_URL']}/auth/reset-password?token={token}"
|
||||
html_body = f'Click <a href="{reset_url}">here</a> to reset your password.'
|
||||
msg = Message(
|
||||
subject="Reset your password",
|
||||
recipients=[to_email],
|
||||
html=html_body,
|
||||
sender=current_app.config.get('MAIL_DEFAULT_SENDER', 'no-reply@reward-app.local')
|
||||
)
|
||||
try:
|
||||
Mail(current_app).send(msg)
|
||||
print(f"[EMAIL to {to_email}] Reset password: {reset_url}")
|
||||
except Exception:
|
||||
print(f"Failed to send email to {to_email}. Reset link: {reset_url}")
|
||||
|
||||
def send_verification_email(self, to_email, token):
|
||||
verify_url = f"{current_app.config['FRONTEND_URL']}/auth/verify?token={token}"
|
||||
html_body = f'Click <a href="{verify_url}">here</a> to verify your account.'
|
||||
msg = Message(
|
||||
subject="Verify your account",
|
||||
recipients=[to_email],
|
||||
html=html_body,
|
||||
sender=current_app.config.get('MAIL_DEFAULT_SENDER', 'no-reply@reward-app.local')
|
||||
)
|
||||
if self.mail:
|
||||
self.mail.send(msg)
|
||||
else:
|
||||
print(f"[EMAIL to {to_email}] Verification: {verify_url}")
|
||||
|
||||
def send_reset_password_email(self, to_email, token):
|
||||
reset_url = f"{current_app.config['FRONTEND_URL']}/auth/reset-password?token={token}"
|
||||
html_body = f'Click <a href="{reset_url}">here</a> to reset your password.'
|
||||
msg = Message(
|
||||
subject="Reset your password",
|
||||
recipients=[to_email],
|
||||
html=html_body,
|
||||
sender=current_app.config.get('MAIL_DEFAULT_SENDER', 'no-reply@reward-app.local')
|
||||
)
|
||||
if self.mail:
|
||||
self.mail.send(msg)
|
||||
else:
|
||||
print(f"[EMAIL to {to_email}] Reset password: {reset_url}")
|
||||
|
||||
def send_pin_setup_email(self, to_email, code):
|
||||
html_body = f"""
|
||||
<div style='font-family:sans-serif;'>
|
||||
<h2>Set up your Parent PIN</h2>
|
||||
<p>To set your Parent PIN, enter the following code in the app:</p>
|
||||
<div style='font-size:2rem; font-weight:bold; letter-spacing:0.2em; margin:1.5rem 0;'>{code}</div>
|
||||
<p>This code is valid for 10 minutes.</p>
|
||||
<p>If you did not request this, you can ignore this email.</p>
|
||||
<hr>
|
||||
<div style='color:#888;font-size:0.95rem;'>Reward App</div>
|
||||
</div>
|
||||
"""
|
||||
msg = Message(
|
||||
subject="Set up your Parent PIN",
|
||||
recipients=[to_email],
|
||||
html=html_body,
|
||||
sender=current_app.config.get('MAIL_DEFAULT_SENDER', 'no-reply@reward-app.local')
|
||||
)
|
||||
if self.mail:
|
||||
self.mail.send(msg)
|
||||
else:
|
||||
print(f"[EMAIL to {to_email}] Parent PIN setup code: {code}")
|
||||
def send_pin_setup_email(to_email: str, code: str) -> None:
|
||||
html_body = f"""
|
||||
<div style='font-family:sans-serif;'>
|
||||
<h2>Set up your Parent PIN</h2>
|
||||
<p>To set your Parent PIN, enter the following code in the app:</p>
|
||||
<div style='font-size:2rem; font-weight:bold; letter-spacing:0.2em; margin:1.5rem 0;'>{code}</div>
|
||||
<p>This code is valid for 10 minutes.</p>
|
||||
<p>If you did not request this, you can ignore this email.</p>
|
||||
<hr>
|
||||
<div style='color:#888;font-size:0.95rem;'>Reward App</div>
|
||||
</div>
|
||||
"""
|
||||
msg = Message(
|
||||
subject="Set up your Parent PIN",
|
||||
recipients=[to_email],
|
||||
html=html_body,
|
||||
sender=current_app.config.get('MAIL_DEFAULT_SENDER', 'no-reply@reward-app.local')
|
||||
)
|
||||
try:
|
||||
Mail(current_app).send(msg)
|
||||
print(f"[EMAIL to {to_email}] Parent PIN setup code: {code}")
|
||||
except Exception:
|
||||
print(f"Failed to send email to {to_email}. Parent PIN setup code: {code}")
|
||||
Reference in New Issue
Block a user