VSCodeにフロント側Next.jsから登録済みのメールアドレスにワンタイムパスワード付きのメールをDjangoに送信してもらうコードサンプルです。
##フロントエンドの実装
NextJSDjangoProject/frontend/generalUsers/regeneratePassword/page.tsx
'use client'
import { useState } from "react"
import { useRouter } from "next/navigation"
import { useForm } from "react-hook-form"
import axios from "axios"
import {
Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter
} from "@material-tailwind/react"
export default function RegeneratePassword(){
const router = useRouter();
const defaultValues = {
Email:''
}
const clickBackToTopPage =()=>{
router.push("/");
}
const {register,handleSubmit,formState:{errors}} = useForm({
defaultValues
});
function isValidEmail(email:string):boolean{
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
return emailRegex.test(email);
}
const [email,SetEmail] = useState('');
const [modalVisible,setModalVisible] = useState(false);
const [modalMessage,setModalMessage] = useState('');
const onSubmit = async(data:any)=>{
if(!isValidEmail){
//Modal Daialogを表示
setModalMessage('Your email is required or invalid. Please input a valid email.');
setModalVisible(true);
return
}
try{
const formData = new FormData();
formData.append('generalUserEmail',email);
const response = await axios.post('http://127.0.0.1:8000/generalusers/sendGeneralUserEmailToChangePassword/',formData);
if(response.status !== 200){
setModalMessage('Email has not been registered.');
setModalVisible(true);
return
}
setModalMessage('A regenerated password has been sent to your registered email. Please check it.');
setModalVisible(true);
}catch(error){
console.log('Error:',error.message);
setModalMessage('An error occurred while sending your request. Please try again later.');
setModalVisible(true);
}
}
return (
<main>
<div className="flex items-center justify-center h-screen">
<form onSubmit = {handleSubmit(onSubmit)} className="border rounded px-4 py-4">
<label>Email</label>
<input
type="email"
className="w-full border border-gray-500 rounded shadow apperance-none focus:shadow-outline text-gray-700 leading-tight px-3 py-2"
defaultValue={defaultValues.Email}
{...register('Email',{
required:'Email is required.'
})}
placeholder="Sample@email.com"
onChange={(e)=>SetEmail(e.target.value)}
/>
<div className="text-rose-500">{errors.Email?.message}</div>
<div>
<button
type="submit"
className="bg bg-blue-500 rounded text-white font-bold hover:bg-blue-700 cursor-pointer px-4 py-4 mr-4"
>
Send
</button>
<button
type="button"
className="bg bg-green-500 rounded text-white font-bold hover:bg-green-700 cursor-pointer px-4 py-4"
onClick={()=>clickBackToTopPage()}
>
BackToPrePage
</button>
</div>
</form>
</div>
{/*--- Modal Dialog ---*/}
<Dialog open={modalVisible} size="sm" handler={()=>setModalVisible(false)}>
<DialogHeader className="w-full text-lg font-semibold">Notice</DialogHeader>
<DialogBody className="w-full text-gray-700">{modalMessage}</DialogBody>
<DialogFooter className="w-full">
<Button
onClick={() => setModalVisible(false)}
className="bg bg-blue-500 rounded text-white font-bold px-4 py-4 hover:bg-blue-700 cursor-pointer"
>
OK
</Button>
</DialogFooter>
</Dialog>
</main>
)
}
サーバサイドの実装
VSCodeのコンソールで試したいときは、settings.pyに下記を追加します。
NextJSDjangoProject/backend/todoproject/settings.py
# ローカル開発環境用:メールをターミナルに表示
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
つぎに、views.pyにメール送信の処理を追加します。
NextJSDjangoProject/backend/generateUsers/views.py
#from django.shortcuts import render
from .models import GeneralUsers
from .serializers import GeneralUsersSerializer
from rest_framework import viewsets
from rest_framework.decorators import api_view #追加
from rest_framework.response import Response # 追加
from rest_framework import status #追加
from django.db import transaction # 追加
from django.contrib.auth.hashers import make_password #追加
from django.core.exceptions import ObjectDoesNotExist #Add
from django.contrib.auth.hashers import check_password #Add
from .serializers import GeneralUsersSerializer
import secrets #追加
import string #追加
from django.core.mail import send_mail # 追加
import logging
# Create your views here.
class GeneralUsersViewSet(viewsets.ModelViewSet):
queryset = GeneralUsers.objects.all()
serializer_class = GeneralUsersSerializer
@api_view(['POST'])
def create_general_user(request):
if request.method == 'POST':
with transaction.atomic():
serializer = GeneralUsersSerializer(data=request.data)
if serializer.is_valid():
password = serializer.validated_data['password']
hashed_password = make_password(password)
serializer.validated_data['generalUserPassword'] = hashed_password
if serializer.is_valid():
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors,status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@api_view(['PUT'])
def update_general_user(request):
try:
general_user_id = request.data.get('generalUserId')
image_url = request.data.get('generalUserImage')
user = GeneralUsers.objects.get(generalUserId=general_user_id)
user.generalUserImage = image_url
user.save()
serializer = GeneralUsersSerializer(user)
return Response(serializer.data,status=status.HTTP_200_OK)
except GeneralUsers.DoesNotExist:
return Response({'error': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#ランダムなパスワードを生成する処理
def generate_random_password(length = 10):
characters = string.ascii_letters + string.digits
return ''.join(secrets.choice(characters) for _ in range(length))
#パスワード変更画面から登録済Emailあてにメール送信
@api_view(['POST'])
def send_general_user_email_to_change_password(request):
try:
general_user_email = request.data.get('generalUserEmail')
#Emailが空文字の場合処理を終了
if not general_user_email:
return Response({'error':'Your Password is required.'},status=status.HTTP_404_NOT_FOUND)
#ブラウザから取得したEmailから一意のデータを取得する
user = GeneralUsers.objects.get(generalUserEmail = general_user_email)
print('Userは',user)
#ランダムなパスワードを生成
new_plain_password = generate_random_password()
#ハッシュ化して保存
user.generalUserPassword = make_password(new_plain_password)
#コンソールデバッグ
print(f"Generated password: {new_plain_password}")
user.save()
#メールにはハッシュ化される前のパスワードを送信する
send_mail(
subject='【重要】新しいパスワードのお知らせ',
message = f'新しいパスワードは以下の通りです。\n\n{new_plain_password}\n\nログイン後、パスワードの変更をおすすめします。',
from_email='noreply@example.com',
recipient_list=[general_user_email],
fail_silently=False,
)
return Response({'message': 'Password has been reset and sent to your email.'},status=status.HTTP_200_OK)
except Exception as e:
return Response({'error':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
さいごに、urls.pyにviews.pyの関数を追加しておきます。
NextJSDjangoProject/backend/generateUsers/urls.py
from django.urls import path,include
from .views import GeneralUsersViewSet #Add
from rest_framework.routers import DefaultRouter
#from .views import GeneralUsersViewSet
from .views import update_general_user #Add
from .views import send_general_user_email_to_change_password #Add
router = DefaultRouter()
router.register('registergeneralusers',GeneralUsersViewSet,basename='create_general_user')
urlpatterns =[
path('updategeneralusers/', update_general_user),
path('sendGeneralUserEmailToChangePassword/',send_general_user_email_to_change_password),
path('',include(router.urls))
]
サイト
Material Tailwind
SMTODevメール
Pythonの複数行のコメントアウト
正規表現