Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

ส่งการแจ้งเตือนทางอีเมลทุกเมื่อที่คุณต้องการ:การกำหนดเขตเวลา–Aware ด้วย QStash

ที่ Docsly เราได้เปิดตัวฟีเจอร์ใหม่เพื่อส่งการแจ้งเตือนทางอีเมลไปยังผู้ใช้พร้อมสรุปความคิดเห็นทั้งหมดที่พวกเขาได้รับในสัปดาห์หรือเดือนที่ผ่านมา การส่งอีเมลไม่ใช่ปัญหาใหม่ แต่เราต้องการมอบประสบการณ์ผู้ใช้ที่ดีที่สุดในเรื่องนี้ ดังนั้นเราจึงตัดสินใจว่าควรส่งอีเมลทั้งหมดในเขตเวลาของผู้ใช้เพื่อหลีกเลี่ยงการส่งอีเมลในเวลาไม่ปกติ นอกจากนี้เรายังต้องการให้ผู้ใช้สามารถเลือกความถี่ที่ต้องการรับอีเมลได้ นอกจากนี้เรายังต้องการให้ผู้ใช้สามารถยกเลิกการแจ้งเตือนทางอีเมลที่กำหนดเวลาไว้ได้ตลอดเวลา

การใช้โซลูชันนี้เป็นเรื่องยากเนื่องจาก:

  1. เราไม่ต้องการจัดเก็บเขตเวลาของผู้ใช้ในฐานข้อมูลของเรา
  2. เรายังไม่ต้องการรันงาน cron ทุกนาทีหรือชั่วโมงเพื่อตรวจสอบว่าถึงเวลาส่งอีเมลหรือไม่
  3. เรายังต้องการให้ผู้ใช้ยกเลิกการแจ้งเตือนทางอีเมลที่กำหนดเวลาไว้

ดังนั้นเราจึงคิดวิธีแก้ปัญหาที่ไม่เหมือนใคร:กำหนดเวลางาน cron ในเขตเวลาของผู้ใช้ จากนั้นยกเลิกงาน cron เมื่อผู้ใช้ยกเลิกการแจ้งเตือนทางอีเมล แต่คำถามหนึ่งยังคงอยู่:อย่างไร?

เราใช้ Upstash เป็นร้านค้า Redis อยู่แล้ว และเราพบว่า QStash รองรับฟีเจอร์ Schedules เช่นกัน เมื่อเจาะลึกลงไปอีกเล็กน้อย เราพบว่า QStash รองรับนิพจน์ CRON เช่นกัน ดังนั้นเราจึงตัดสินใจใช้ QStash เพื่อกำหนดเวลางาน cron

ในบทความนี้ เราจะแนะนำคุณตลอดกระบวนการกำหนดเวลาอีเมลในเขตเวลาของผู้ใช้โดยใช้ QStash และ Upstash Redis ในแอปพลิเคชัน Next.js คุณยังค้นหาซอร์สโค้ดฉบับสมบูรณ์ได้ที่ GitHub

การสร้างแอปพลิเคชัน Next.js เพื่อกำหนดเวลาอีเมลในเขตเวลาของผู้ใช้

ข้อกำหนดเบื้องต้น

หากต้องการติดตามบทช่วยสอนนี้ คุณจะต้อง:

  • บัญชี Upstash
  • สภาพแวดล้อมการพัฒนา Node.js

ตั้งค่าโครงการ

ในการเริ่มต้น ให้สร้างโปรเจ็กต์ Next.js ใหม่โดยใช้คำสั่งต่อไปนี้:

npx create-next-app qstash-email-scheduling

จากนั้น ติดตั้งการขึ้นต่อกันต่อไปนี้เพื่อโต้ตอบกับ Upstash:

npm install --save @upstash/redis axios

สร้าง 06 ใหม่ ในรากของโครงการของคุณและเพิ่มตัวแปรสภาพแวดล้อมต่อไปนี้จากบัญชี Upstash ของคุณ:

UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
QSTASH_URL=
QSTASH_TOKEN=
QSATSH_CURRENT_SIGNING_KEY=
QSATSH_NEXT_SIGNING_KEY=

ภาพรวมโซลูชัน

ก่อนที่เราจะติดตั้งโค้ด เรามาดูภาพรวมของโซลูชันกันก่อน เราจะสร้างเส้นทาง Next.js API สามเส้นทาง:

  • 16 - เพื่อกำหนดเวลางาน cron อีเมลในเขตเวลาของผู้ใช้
  • 23 - เพื่อยกเลิกงาน cron อีเมลที่กำหนดเวลาไว้
  • 31 - ทริกเกอร์โดยงาน cron ที่กำหนดเวลาไว้เพื่อส่งอีเมล

นอกจากเส้นทาง API แล้ว เรายังสร้างแบบฟอร์มง่ายๆ เพื่อรวบรวมเวลาที่ต้องการรับอีเมลของผู้ใช้

สร้างอินเทอร์เฟซผู้ใช้

ในการสร้างส่วนติดต่อผู้ใช้ เราจะสร้างหน้าใหม่ที่ 46 ด้วยรหัสต่อไปนี้:

"use client";
 
import { useState } from "react";
 
import axios from "axios";
 
export default function Home() {
 const userId = "tony-stark-11";
 const [selectedTime, setSelectedTime] = useState("10:00");
 
 async function createEmailNotificationSchedule() {
 try {
 await axios.post(
 "/api/schedule-cron",
 {
 userId,
 selectedTime,
 utcOffset: new Date().getTimezoneOffset(),
 },
 {
 headers: {
 "Content-Type": "application/json",
 },
 },
 );
 alert("Email notification scheduled");
 } catch (e) {
 console.log("Client side error", e);
 alert("Error scheduling email notification");
 }
 }
 
 async function cancelEmailNotificationSchedule() {
 try {
 await axios.post(
 "/api/cancel-schedule",
 {
 userId,
 },
 {
 headers: {
 "Content-Type": "application/json",
 },
 },
 );
 alert("Email notification schedule cancelled");
 } catch (e) {
 console.log("Client side error", e);
 alert("Error scheduling email notification");
 }
 }
 
 return (
 <main className="mx-auto flex min-h-screen max-w-md flex-col justify-center p-24">
 <h1 className="mb-4 text-xl font-bold text-neutral-600">
 Email Notification for {userId}
 </h1>
 Send daily email summary at:
 <select
 onChange={(e) => setSelectedTime(e.target.value)}
 className="mt-4 h-12 w-64 rounded-lg border-2 border-neutral-600 bg-neutral-800 p-2
 text-white"
 >
 {new Array(24).fill(0).map((_, i) => {
 const time = i < 10 ? `0${i}:00` : `${i}:00`;
 return (
 <option key={i} value={time}>
 {time}
 </option>
 );
 })}
 </select>
 <button
 className="mt-4 rounded bg-green-700 px-4 py-2 text-white"
 onClick={createEmailNotificationSchedule}
 >
 Schedule
 </button>
 <button
 className="mt-4 rounded bg-red-500 px-4 py-2 text-white"
 onClick={cancelEmailNotificationSchedule}
 >
 Cancel Schedule
 </button>
 </main>
 );
}

โค้ดด้านบนจะสร้างแบบฟอร์มพร้อมเมนูแบบเลื่อนลงเพื่อให้ผู้ใช้ตั้งเวลาที่ต้องการรับอีเมลสรุปรายวัน พวกเขาสามารถกำหนดเวลาหรือยกเลิกการแจ้งเตือนเหล่านี้ได้ และแอปพลิเคชันจะสื่อสารกับเซิร์ฟเวอร์โดยใช้คำขอ HTTP POST เราจะสร้างปลายทาง HTTP ในส่วนต่อไปนี้

ส่งการแจ้งเตือนทางอีเมลทุกเมื่อที่คุณต้องการ:การกำหนดเขตเวลา–Aware ด้วย QStash

กำหนดเวลางาน cron อีเมล

ส่งการแจ้งเตือนทางอีเมลทุกเมื่อที่คุณต้องการ:การกำหนดเขตเวลา–Aware ด้วย QStash

เริ่มต้นด้วยการสร้าง 50 เส้นทาง เส้นทางนี้จะใช้เพื่อกำหนดเวลางาน cron อีเมลในเขตเวลาของผู้ใช้ เราจะใช้ไลบรารี QStash เพื่อกำหนดเวลางาน cron

import { NextApiRequest, NextApiResponse } from "next";
 
import { Redis } from "@upstash/redis";
import axios from "axios";
 
export const QSTASH_CONFIG = {
 QSTASH_URL: process.env.QSTASH_URL,
 QSTASH_TOKEN: process.env.QSTASH_TOKEN,
 QSTASH_CURRENT_SIGNING_KEY: process.env.QSTASH_CURRENT_SIGNING_KEY,
};
 
export const upstash = new Redis({
 url: process.env.UPSTASH_REDIS_REST_URL!,
 token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
 
// Edit this endpoint to match your domain
const SUMMARY_ENDPOINT = "https://<your-domain>/api/send-email";
 
export default async function scheduleSummary(
 req: NextApiRequest,
 res: NextApiResponse,
) {
 console.log("========SCHEDULE SUMMARY========");
 if (req.method !== "POST") {
 return res.status(400).json({ message: "bad request" });
 }
 const { body } = req;
 
 const { userId, selectedTime, utcOffset } = body;
 
 const emailScheduleKey = `email-schedule-${userId}`;
 
 const scheduleId = await upstash.get(emailScheduleKey);
 
 // remove existing schedule before creating a new one
 if (scheduleId) {
 try {
 await axios.delete(
 `https://qstash.upstash.io/v1/schedules/${scheduleId}`,
 {
 headers: {
 Authorization: `Bearer ${QSTASH_CONFIG.QSTASH_TOKEN}`,
 },
 },
 );
 } catch (e) {
 console.log("Schedule not found in QStash ");
 }
 await upstash.del(emailScheduleKey);
 }
 
 const [hour, min] = convertToUTC(selectedTime, utcOffset).split(":");
 const selectedCron = `${min} ${hour} * * *`;
 
 // create and store new schedule
 try {
 const { data, status } = await axios.post(
 `${QSTASH_CONFIG.QSTASH_URL}${SUMMARY_ENDPOINT}`,
 { userId },
 {
 headers: {
 "Content-Type": "application/json",
 Authorization: `Bearer ${QSTASH_CONFIG.QSTASH_TOKEN}`,
 "Upstash-Cron": selectedCron,
 },
 },
 );
 console.log({ data, status });
 if (data.scheduleId) {
 await upstash.set(emailScheduleKey, data.scheduleId);
 }
 } catch (e) {
 console.log({ e });
 }
 
 return res.status(200).json({ message: "success" });
}
 
function convertToUTC(timeString: string, utcOffset: number) {
 const [hours, minutes] = timeString.split(":").map(Number);
 const timeInMinutes = hours * 60 + minutes;
 const utcTimeInMinutes = (timeInMinutes + utcOffset + 1440) % 1440;
 const utcHours = Math.floor(utcTimeInMinutes / 60);
 const utcMinutes = utcTimeInMinutes % 60;
 return `${utcHours.toString().padStart(2, "0")}:${utcMinutes
 .toString()
 .padStart(2, "0")}`;
}

แกนหลักของโค้ดคือ 62 ฟังก์ชันซึ่งเป็นจุดสิ้นสุด API โดยจะจัดการกับคำขอ POST ที่เข้ามาและดำเนินการตามขั้นตอนต่อไปนี้:

  1. ตรวจสอบวิธีการร้องขอ เพื่อให้แน่ใจว่าเป็นคำขอ POST
  2. ดึงข้อมูลจากเนื้อหาคำขอ รวมถึง userId, SelectedTime และ utcOffset
  3. สร้างคีย์สำหรับกำหนดเวลาอีเมลของผู้ใช้ในฐานข้อมูล Upstash
  4. ดึงข้อมูลกำหนดการที่มีอยู่สำหรับผู้ใช้และลบออกจาก QStash
  5. แปลงเวลาที่ผู้ใช้เลือกเป็นรูปแบบ UTC โดยใช้ 75 ฟังก์ชันที่รับสตริงเวลาและออฟเซ็ต UTC โดยแปลงเวลาเป็นรูปแบบ UTC ในขณะที่คำนึงถึงออฟเซ็ต
  6. สร้างกำหนดการใหม่สำหรับการส่งอีเมลสรุปโดยใช้ฟังก์ชันการกำหนดเวลาของ QStash และเพิ่ม 82 เป็นเพย์โหลดที่ 95 ปลายทางได้รับ
  7. จัดเก็บ ID กำหนดการที่สร้างขึ้นใหม่ในฐานข้อมูล Upstash

ส่งอีเมลสรุป

102 เส้นทางจะถูกทริกเกอร์โดยงาน cron ที่กำหนดเวลาไว้เพื่อส่งอีเมล เราจะใช้ 113 ห้องสมุดเพื่อตรวจสอบลายเซ็นของคำขอ สิ่งนี้จะช่วยให้แน่ใจว่าคำขอนั้นมาจาก QStash และไม่ได้มาจากแหล่งอื่นใด

128 ฟังก์ชั่นรับ 136 ในเนื้อหาคำขอ คุณสามารถใช้ฟังก์ชันเพื่อจัดเตรียมและส่งอีเมลตาม 145 .

import { NextApiRequest, NextApiResponse } from "next";
 
import { verifySignature } from "@upstash/qstash/nextjs";
 
async function handler(request: NextApiRequest, res: NextApiResponse) {
 console.log("==========Project summary handler==========");
 if (request.method !== "POST") {
 return res.status(400).json({ message: "bad request" });
 }
 const { body } = request;
 const { userId } = body;
 
 // prepare and send email
 
 return res.status(200).json({ message: "success" });
}
 
export default verifySignature(handler);
 
export const config = {
 api: {
 bodyParser: false,
 },
};

ยกเลิกงาน cron อีเมลที่กำหนดเวลาไว้

ส่งการแจ้งเตือนทางอีเมลทุกเมื่อที่คุณต้องการ:การกำหนดเขตเวลา–Aware ด้วย QStash

ต่อไปเรามาสร้าง 154 เส้นทาง เส้นทางนี้จะใช้เพื่อยกเลิกงาน cron อีเมลที่กำหนดเวลาไว้ เราจะใช้ไลบรารี QStash เพื่อยกเลิกงาน cron

import { NextApiRequest, NextApiResponse } from "next";
import axios from "axios";
import { Redis } from "@upstash/redis";
 
export const QSTASH_CONFIG = {
 QSTASH_URL: process.env.QSTASH_URL,
 QSTASH_TOKEN: process.env.QSTASH_TOKEN,
 QSTASH_CURRENT_SIGNING_KEY: process.env.QSTASH_CURRENT_SIGNING_KEY,
};
 
export const upstash = new Redis({
 url: process.env.UPSTASH_REDIS_REST_URL!,
 token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
 
export default async function scheduleSummary(
 req: NextApiRequest,
 res: NextApiResponse
) {
 console.log("========REMOVE SCHEDULE SUMMARY========");
 if (req.method !== "POST") {
 return res.status(400).json({ message: "bad request" });
 }
 const { body } = req;
 
 const { userId } = body;
 
 const emailScheduleKey = `email-schedule-${userId}`;
 
 const scheduleId = await upstash.get(emailScheduleKey);
 
 // remove existing schedule before creating a new one
 if (scheduleId) {
 try {
 await axios.delete(
 `https://qstash.upstash.io/v1/schedules/${scheduleId}`,
 {
 headers: {
 Authorization: `Bearer ${QSTASH_CONFIG.QSTASH_TOKEN}`,
 },
 }
 );
 } catch (e) {
 console.log("Schedule not found in QStash ");
 }
 await upstash.del(emailScheduleKey);
 }
 
 
 return res.status(200).json({ message: "success" });
}

บทสรุป

โดยสรุป เราสามารถกำหนดเวลาอีเมลในเขตเวลาของผู้ใช้ได้โดยใช้ Upstash Redis และ QStash เราบรรลุเป้าหมายนี้โดยไม่ต้องจัดเก็บเขตเวลาของผู้ใช้ไว้ในฐานข้อมูลของเรา นอกจากนี้เรายังสามารถให้ผู้ใช้สามารถยกเลิกการแจ้งเตือนทางอีเมลที่กำหนดเวลาไว้ได้ตลอดเวลา

หากคุณกำลังเก็บเอกสารสำหรับผลิตภัณฑ์ของคุณ คุณควรตรวจสอบเอกสาร เป็นเครื่องมือคำติชมที่สร้างขึ้นสำหรับเอกสารทางเทคนิคที่ช่วยให้คุณรวบรวมคำติชมจากผู้ใช้และเปลี่ยนให้เป็นข้อมูลเชิงลึกที่นำไปใช้ได้จริง