Emails.
This commit is contained in:
parent
ed4cd5c14f
commit
83ac1eb001
47
retoors/helpers/email_sender.py
Normal file
47
retoors/helpers/email_sender.py
Normal file
@ -0,0 +1,47 @@
|
||||
import aiosmtplib
|
||||
from email.message import EmailMessage
|
||||
from aiohttp import web
|
||||
import logging
|
||||
|
||||
from retoors.services.config_service import ConfigService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
async def send_email(app: web.Application, recipient_email: str, subject: str, body: str):
|
||||
"""
|
||||
Sends an email asynchronously using the configured SMTP settings.
|
||||
"""
|
||||
config_service: ConfigService = app["config_service"]
|
||||
|
||||
smtp_host = config_service.get_smtp_host()
|
||||
smtp_port = config_service.get_smtp_port()
|
||||
smtp_username = config_service.get_smtp_username()
|
||||
smtp_password = config_service.get_smtp_password()
|
||||
smtp_use_tls = config_service.get_smtp_use_tls()
|
||||
smtp_sender_email = config_service.get_smtp_sender_email()
|
||||
|
||||
if not smtp_host or not smtp_sender_email:
|
||||
logger.error("SMTP host or sender email not configured. Cannot send email.")
|
||||
return
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["From"] = smtp_sender_email
|
||||
msg["To"] = recipient_email
|
||||
msg["Subject"] = subject
|
||||
msg.set_content(body)
|
||||
|
||||
try:
|
||||
await aiosmtplib.send(
|
||||
msg,
|
||||
hostname=smtp_host,
|
||||
port=smtp_port,
|
||||
username=smtp_username,
|
||||
password=smtp_password,
|
||||
use_tls=False, # Always False when using start_tls
|
||||
start_tls=smtp_use_tls, # Use start_tls for explicit TLS negotiation
|
||||
)
|
||||
logger.info(f"Email sent successfully to {recipient_email} with subject '{subject}'")
|
||||
except aiosmtplib.SMTPException as e:
|
||||
logger.error(f"Failed to send email to {recipient_email}: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"An unexpected error occurred while sending email to {recipient_email}: {e}")
|
||||
52
retoors/helpers/env_manager.py
Normal file
52
retoors/helpers/env_manager.py
Normal file
@ -0,0 +1,52 @@
|
||||
from pathlib import Path
|
||||
|
||||
def ensure_env_file_exists(env_path: Path):
|
||||
"""
|
||||
Ensures that a .env file exists at the specified path.
|
||||
If it doesn't exist, it creates one with default values.
|
||||
"""
|
||||
if not env_path.exists():
|
||||
default_env_content = """# .env - Environment variables for Retoor's Cloud Solutions
|
||||
#
|
||||
# This file is used to configure various aspects of the application,
|
||||
# including pricing, email settings, and other sensitive information.
|
||||
#
|
||||
# For production, it is recommended to manage these variables through
|
||||
# your hosting provider's environment variable management system
|
||||
# rather than committing this file to version control.
|
||||
|
||||
# Pricing Configuration
|
||||
# PRICE_PER_GB: Cost per gigabyte of storage.
|
||||
PRICE_PER_GB=0.05
|
||||
|
||||
# SMTP (Email Sending) Configuration
|
||||
# Used for sending transactional emails (e.g., welcome, password reset).
|
||||
# SMTP_HOST: The hostname of your SMTP server.
|
||||
# SMTP_PORT: The port of your SMTP server (e.g., 587 for TLS, 465 for SSL, 25 for unencrypted).
|
||||
# SMTP_USERNAME: The username for authenticating with your SMTP server.
|
||||
# SMTP_PASSWORD: The password for authenticating with your SMTP server.
|
||||
# SMTP_USE_TLS: Set to True to enable STARTTLS encryption (recommended for most servers).
|
||||
# SMTP_SENDER_EMAIL: The email address from which system emails will be sent.
|
||||
SMTP_HOST=localhost
|
||||
SMTP_PORT=1025
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_USE_TLS=False
|
||||
SMTP_SENDER_EMAIL=no-reply@retoors.com
|
||||
|
||||
# IMAP (Email Receiving) Configuration (if applicable for future features)
|
||||
# IMAP_HOST=
|
||||
# IMAP_PORT=
|
||||
# IMAP_USERNAME=
|
||||
# IMAP_PASSWORD=
|
||||
# IMAP_USE_SSL=True
|
||||
"""
|
||||
with open(env_path, "w") as f:
|
||||
f.write(default_env_content)
|
||||
print(f"Created default .env file at {env_path}")
|
||||
else:
|
||||
print(f".env file already exists at {env_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage:
|
||||
ensure_env_file_exists(Path(__file__).parent.parent.parent / ".env")
|
||||
73
retoors/templates/emails/password_changed_confirmation.html
Normal file
73
retoors/templates/emails/password_changed_confirmation.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Password Changed - Retoor's Cloud Solutions</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f4f4f4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header {
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h2>Your Password Has Been Changed</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Dear {{ user_name }},</p>
|
||||
<p>This is a confirmation that the password for your Retoor's Cloud Solutions account has been successfully changed.</p>
|
||||
<p>If you made this change, you can now log in with your new password:</p>
|
||||
<p style="text-align: center;">
|
||||
<a href="{{ login_url }}" class="button">Log In Now</a>
|
||||
</p>
|
||||
<p>If you did not authorize this change, please contact our support team immediately.</p>
|
||||
<p>Best regards,</p>
|
||||
<p>The Retoor's Cloud Solutions Team</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© 2025 Retoor's Cloud Solutions. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
73
retoors/templates/emails/password_reset_request.html
Normal file
73
retoors/templates/emails/password_reset_request.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Password Reset Request - Retoor's Cloud Solutions</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f4f4f4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header {
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h2>Password Reset Request</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Dear {{ user_name }},</p>
|
||||
<p>We received a request to reset the password for your Retoor's Cloud Solutions account.</p>
|
||||
<p>To reset your password, please click on the link below:</p>
|
||||
<p style="text-align: center;">
|
||||
<a href="{{ reset_link }}" class="button">Reset My Password</a>
|
||||
</p>
|
||||
<p>This link will expire in 1 hour. If you did not request a password reset, please ignore this email.</p>
|
||||
<p>Best regards,</p>
|
||||
<p>The Retoor's Cloud Solutions Team</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© 2025 Retoor's Cloud Solutions. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
74
retoors/templates/emails/welcome.html
Normal file
74
retoors/templates/emails/welcome.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Welcome to Retoor's Cloud Solutions!</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f4f4f4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.header {
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #007bff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h2>Welcome to Retoor's Cloud Solutions!</h2>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Dear {{ user_name }},</p>
|
||||
<p>Thank you for registering with Retoor's Cloud Solutions. We are excited to have you on board!</p>
|
||||
<p>Our platform offers secure, reliable, and flexible cloud storage tailored to your needs. Whether you're a small business, a home office user, or an individual, we're here to help you manage your data with ease.</p>
|
||||
<p>To get started, please log in to your dashboard:</p>
|
||||
<p style="text-align: center;">
|
||||
<a href="{{ login_url }}" class="button">Go to My Dashboard</a>
|
||||
</p>
|
||||
<p>If you have any questions, feel free to visit our <a href="{{ support_url }}">Support Page</a> or contact our team.</p>
|
||||
<p>Best regards,</p>
|
||||
<p>The Retoor's Cloud Solutions Team</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>© 2025 Retoor's Cloud Solutions. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
35
retoors/templates/pages/forgot_password.html
Normal file
35
retoors/templates/pages/forgot_password.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
|
||||
{% block title %}Forgot Password - Retoor's Cloud Solutions{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="/static/css/components/form.css"> {# Reusing form.css for styling #}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="form-page-container">
|
||||
<h1>Forgot Your Password?</h1>
|
||||
<p class="subtitle">Enter your email address below and we'll send you a link to reset your password.</p>
|
||||
|
||||
<section class="form-container">
|
||||
<h2>Reset Password</h2>
|
||||
{% if error %}
|
||||
<p class="error">{{ error }}</p>
|
||||
{% endif %}
|
||||
{% if message %}
|
||||
<p class="message">{{ message }}</p>
|
||||
{% endif %}
|
||||
<form action="/forgot_password" method="post">
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
<input type="email" id="email" name="email" required value="{{ data.email or '' }}">
|
||||
{% if errors.email %}
|
||||
<p class="error">{{ errors.email }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Send Reset Link</button>
|
||||
</form>
|
||||
<p class="login-link">Remember your password? <a href="/login">Log In</a></p>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
38
retoors/templates/pages/reset_password.html
Normal file
38
retoors/templates/pages/reset_password.html
Normal file
@ -0,0 +1,38 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
|
||||
{% block title %}Reset Password - Retoor's Cloud Solutions{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="/static/css/components/form.css"> {# Reusing form.css for styling #}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="form-page-container">
|
||||
<h1>Set Your New Password</h1>
|
||||
<p class="subtitle">Please enter and confirm your new password below.</p>
|
||||
|
||||
<section class="form-container">
|
||||
<h2>Reset Password</h2>
|
||||
{% if error %}
|
||||
<p class="error">{{ error }}</p>
|
||||
{% endif %}
|
||||
<form action="/reset_password/{{ token }}" method="post">
|
||||
<div class="form-group">
|
||||
<label for="password">New Password</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
{% if errors.password %}
|
||||
<p class="error">{{ errors.password }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">Confirm New Password</label>
|
||||
<input type="password" id="confirm_password" name="confirm_password" required>
|
||||
{% if errors.confirm_password %}
|
||||
<p class="error">{{ errors.confirm_password }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Reset Password</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue
Block a user