พัฒนา Backend ฟังก์ชัน ลืมรหัสผ่าน? (Forgot Password)
This commit is contained in:
parent
248a31b13f
commit
606008db88
@ -21,5 +21,5 @@ COPY . /app/
|
||||
EXPOSE 8000
|
||||
|
||||
# 7. ENTRYPOINT/CMD: กำหนด Entrypoint หลัก
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
||||
20
backend/Dockerfile.celery
Normal file
20
backend/Dockerfile.celery
Normal file
@ -0,0 +1,20 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
gcc \
|
||||
libpq-dev \
|
||||
libffi-dev \
|
||||
libjpeg62-turbo-dev \
|
||||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["celery", "-A", "core", "worker", "-l", "info"]
|
||||
81
backend/accounts/emails.py
Normal file
81
backend/accounts/emails.py
Normal file
@ -0,0 +1,81 @@
|
||||
from djoser import email
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
|
||||
class CustomPasswordResetEmail(email.PasswordResetEmail):
|
||||
|
||||
template_name = None
|
||||
|
||||
def render(self, context=None):
|
||||
|
||||
context = self.get_context_data()
|
||||
url = context['url']
|
||||
user = context['user']
|
||||
|
||||
self.html_content = self.create_email_html(url, user)
|
||||
self.plain_content = self.create_email_plain(url, user)
|
||||
|
||||
def get_context_data(self):
|
||||
return super().get_context_data()
|
||||
|
||||
def get_subject(self):
|
||||
"""
|
||||
subject ถูกสร้างโดย Djoser ใน context['subject']
|
||||
"""
|
||||
context = self.get_context_data()
|
||||
return context.get("subject", "Password Reset")
|
||||
|
||||
def send(self, to, *args, **kwargs):
|
||||
|
||||
self.render()
|
||||
|
||||
msg = EmailMultiAlternatives(
|
||||
subject=self.get_subject(),
|
||||
body=self.plain_content,
|
||||
from_email=self.from_email,
|
||||
to=to
|
||||
)
|
||||
if self.html_content:
|
||||
msg.attach_alternative(self.html_content, "text/html")
|
||||
|
||||
# djcelery_email จะ Intercep เมธอด send() ของ EmailMultiAlternatives โดยอัตโนมัติ
|
||||
return msg.send()
|
||||
|
||||
def create_email_html(self, url, user):
|
||||
# ดึง context data อีกครั้งเพื่อเข้าถึง protocol และ domain
|
||||
context = self.get_context_data()
|
||||
full_reset_url = f"{context['protocol']}://{context['domain']}{url}" # สร้าง Full URL
|
||||
|
||||
return f"""
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<p>สวัสดีคุณ <b>{user.username}</b>,</p>
|
||||
<p>คุณได้ร้องขอการรีเซ็ตรหัสผ่าน โปรดคลิกลิงก์ด้านล่างเพื่อตั้งรหัสผ่านใหม่:</p>
|
||||
|
||||
<p><a href="{full_reset_url}" style="background-color: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">รีเซ็ตรหัสผ่าน (คลิกที่นี่)</a></p>
|
||||
|
||||
<p>หากลิงก์ไม่สามารถคลิกได้ กรุณาคัดลอกลิงก์นี้ไปวางในเบราว์เซอร์: <b>{full_reset_url}</b></p>
|
||||
<p>หากคุณไม่ได้ร้องขอ โปรดเพิกเฉยต่ออีเมลนี้</p>
|
||||
<p>ขอบคุณครับ,<br/>ทีม DDO Console</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
def create_email_plain(self, url, user):
|
||||
# ดึง context data อีกครั้งเพื่อเข้าถึง protocol และ domain
|
||||
context = self.get_context_data()
|
||||
full_reset_url = f"{context['protocol']}://{context['domain']}{url}" # ⬅️ สร้าง Full URL
|
||||
|
||||
return f"""
|
||||
สวัสดี {user.username},
|
||||
|
||||
คุณได้ร้องขอการรีเซ็ตรหัสผ่าน กรุณาใช้ลิงก์ด้านล่างเพื่อตั้งรหัสผ่านใหม่:
|
||||
|
||||
{full_reset_url}
|
||||
|
||||
หากคุณไม่ได้ร้องขอ โปรดเพิกเฉยต่ออีเมลนี้
|
||||
|
||||
ขอบคุณครับ,
|
||||
ทีม DDO Console
|
||||
"""
|
||||
16
backend/core/celery.py
Normal file
16
backend/core/celery.py
Normal file
@ -0,0 +1,16 @@
|
||||
import os
|
||||
from celery import Celery
|
||||
|
||||
# กำหนดค่า Django settings module ให้ Celery
|
||||
# 'core.settings' คือ path ของ settings.py ของโปรเจกต์ Django
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
|
||||
|
||||
# สร้าง Celery application instance
|
||||
app = Celery('core') # ชื่อตรงนี้ต้องตรงกับ -A core และ CELERY_APP: core
|
||||
|
||||
# โหลด configuration จากไฟล์ settings.py ของ Django
|
||||
# โดย Celery จะใช้ prefix CELERY_ (เช่น CELERY_BROKER_URL)
|
||||
app.config_from_object('django.conf:settings', namespace='CELERY')
|
||||
|
||||
# ค้นหา tasks ทั้งหมดใน INSTALLED_APPS ของ Django โดยอัตโนมัติ
|
||||
app.autodiscover_tasks()
|
||||
@ -13,9 +13,11 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# ใน core/settings.py (ด้านบนสุด)
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv() # โหลดตัวแปรจาก .env
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv() # โหลดตัวแปรจาก .env ใน Local Dev
|
||||
except ImportError:
|
||||
pass # ไม่ทำอะไรถ้า Module ไม่พบ (หมายความว่ารันอยู่ใน Docker)
|
||||
|
||||
DB_HOST = os.getenv("DB_HOST", "cockroach-1")
|
||||
|
||||
@ -52,6 +54,7 @@ THIRD_PARTY_APPS = [
|
||||
'corsheaders',
|
||||
'drf_spectacular',
|
||||
'drf_spectacular_sidecar',
|
||||
'djcelery_email',
|
||||
]
|
||||
|
||||
LOCAL_APPS = [
|
||||
@ -206,12 +209,18 @@ REST_FRAMEWORK = {
|
||||
}
|
||||
|
||||
# 3. ตั้งค่า DJOSER (เพื่อจัดการ Auth Endpoints)
|
||||
DOMAIN = "localhost:5173"
|
||||
SITE_NAME = 'localhost:5173' # หรือชื่อ Domain จริง
|
||||
DJOSER = {
|
||||
# ใช้งาน JWT โดยตรง
|
||||
'USER_ID_FIELD': 'id', # ใช้ ID ของ User Model
|
||||
'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}', # URL สำหรับ Frontend
|
||||
'USERNAME_RESET_CONFIRM_URL': '#/username/reset/confirm/{uid}/{token}',
|
||||
'ACTIVATION_URL': '#/activate/{uid}/{token}', # หากต้องการยืนยันอีเมล
|
||||
'EMAIL': {
|
||||
# ชี้ไปยัง Custom Email Class ที่เราสร้าง
|
||||
'password_reset': 'accounts.emails.CustomPasswordResetEmail',
|
||||
},
|
||||
'PASSWORD_RESET_CONFIRM_URL': '/password/reset/confirm/{uid}/{token}', # URL ที่ Djoser จะใช้สร้างลิงก์ในอีเมล (ชี้ไปยัง Frontend Route)
|
||||
'USERNAME_RESET_CONFIRM_URL': '/username/reset/confirm/{uid}/{token}',
|
||||
'ACTIVATION_URL': '/activate/{uid}/{token}', # หากต้องการยืนยันอีเมล
|
||||
'SERIALIZERS': {
|
||||
'user_create': 'accounts.serializers.UserCreateSerializer', # จะสร้างในขั้นตอนถัดไป
|
||||
'user': 'accounts.serializers.UserSerializer', # ใช้ UserSerializer เดิม
|
||||
@ -220,7 +229,10 @@ DJOSER = {
|
||||
'TOKEN_MODEL': None, # ไม่ใช้ TokenAuth แบบเก่า
|
||||
'PERMISSIONS': {
|
||||
'user_create': ['rest_framework.permissions.AllowAny'],
|
||||
}
|
||||
},
|
||||
"DOMAIN": "localhost:5173",
|
||||
"SITE_NAME": "localhost:5173",
|
||||
"PROTOCOL": "http",
|
||||
}
|
||||
|
||||
REDIS_HOST = os.getenv("REDIS_HOST", "redis")
|
||||
@ -248,8 +260,8 @@ SESSION_CACHE_ALIAS = "default"
|
||||
# CACHE_MIDDLEWARE_KEY_PREFIX = 'auth_cache'
|
||||
|
||||
# CELERY CONFIGURATION
|
||||
CELERY_BROKER_URL = f'redis://{REDIS_HOST}:{REDIS_PORT}/0'
|
||||
CELERY_RESULT_BACKEND = f'redis://{REDIS_HOST}:{REDIS_PORT}/0'
|
||||
CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL', 'redis://redis:6379/0')
|
||||
CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', 'redis://redis:6379/0')
|
||||
CELERY_ACCEPT_CONTENT = ['json']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
@ -311,3 +323,25 @@ SPECTACULAR_SETTINGS = {
|
||||
],
|
||||
# ตั้งค่าอื่น ๆ (ถ้าจำเป็น)
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# CELERY EMAIL CONFIGURATION (Transactional Emails)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# 1. EMAIL_BACKEND หลัก ใช้ Celery Email Backend
|
||||
# Django จะส่งอีเมลทั้งหมดไปเข้าคิว Celery
|
||||
EMAIL_BACKEND = 'djcelery_email.backends.CeleryEmailBackend'
|
||||
|
||||
# 2. CELERY_EMAIL_BACKEND: กำหนด SMTP ที่ Celery Worker จะใช้
|
||||
# Celery Worker จะใช้ตัวนี้ในการส่งอีเมลออกจริง ๆ
|
||||
CELERY_EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
# Mailjet SMTP Configuration (ใช้ Env Vars)
|
||||
#EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = os.getenv('MAILJET_SMTP_HOST')
|
||||
EMAIL_PORT = os.getenv('MAILJET_SMTP_PORT')
|
||||
EMAIL_USE_TLS = os.getenv('MAILJET_SMTP_TLS', 'True') == 'True'
|
||||
EMAIL_HOST_USER = os.getenv('MAILJET_API_KEY') # API Key เป็น Username
|
||||
EMAIL_HOST_PASSWORD = os.getenv('MAILJET_SECRET_KEY') # Secret Key เป็น Password
|
||||
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL') # อีเมลผู้ส่ง
|
||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL # อีเมลสำหรับแจ้งเตือน Server
|
||||
@ -14,3 +14,6 @@ boto3
|
||||
python-dotenv
|
||||
drf-spectacular
|
||||
drf-spectacular-sidecar
|
||||
django-celery-email
|
||||
python-dotenv
|
||||
flower
|
||||
Loading…
x
Reference in New Issue
Block a user