কম্পিউটার টিউটোরিয়াল

QStash দিয়ে টাইমজোন-অवेয়ার শিডিউলিং: ইমেল ألার্ট সঠিক সময়ে ডেলিভার করুন

docsly-এ, আমরা একটি নতুন ফিচার লঞ্চ করেছি যা ব্যবহারকারীদের ইমেল নোটিফিকেশন পাঠায় তাদের পাওয়া সব ফিডব্যাকের সারাংশসহ গত সপ্তাহ বা মাসের। ইমেল পাঠানো একটি নতুন সমস্যা নয়, কিন্তু আমরা এই বিষয়ে সেরা ইউজার এক্সপেরিয়েন্স দিতে চেয়েছিলাম, তাই আমরা সিদ্ধান্ত নিলাম যে সকল ইমেল ব্যবহারকারীর টাইমজোন অনুযায়ী পাঠানো হবে যাতে বेतাল সময়ে ইমেল পাঠানো না হয়। আমরা ব্যবহারকারীদের ইমেল পাওয়ার ফ্রিকোয়েন্সি বেছে নেওয়ার ক্ষमतাও দিতে চেয়েছিলাম। আমরা ব্যবহারকারীদের যেকোনো সময় শিডিউল করা ইমেল নোটিফিকেশন ক্যানসেল করার ক্ষমতাও দিতে চেয়েছিলাম।

এই সমাধানটি ইমপ্লিমেন্ট করা ট্রিকি ছিল কারণ:

  1. আমরা ব্যবহারকারীর টাইমজোন আমাদের ডেটাবেসে সংরক্ষণ করতে চাইতাম না।
  2. আমরা প্রতি মিনিট বা ঘণ্টায় একটি ক্রন জব চালিয়ে চেক করতে চাইতাম না যে ইমেল পাঠানোর সময় হয়েছে কিনা।
  3. আমরা ব্যবহারকারীদের শিডিউল করা ইমেল নোটিফিকেশন ক্যানসেল করতে দিতে চেয়েছিলাম।

তাই, আমরা একটি অনন্য সমাধান বের করলাম: ব্যবহারকারীর টাইমজোন অনুযায়ী ক্রন জব শিডিউল করুন এবং ব্যবহারকারী ইমেল নোটিফিকেশন ক্যানসেল করলে ক্রন জব ক্যানসেল করুন। কিন্তু একটা প্রশ্ন বাকি ছিল: কীভাবে?

আমরা ইতিমধ্যে Upstash ব্যবহার করছিলাম একটি Redis স্টোর হিসেবে, এবং আমরা পেলাম যে QStash একটি Schedules ফিচারও সাপোর্ট করে। একটু গভীরে যেতে পেলাম QStash CRON এক্সপ্রেশনও সাপোর্ট করে। তাই, আমরা সিদ্ধান্ত নিলাম QStash ব্যবহার করতে ক্রন জব শিডিউল করার জন্য।

এই আর্টিকেলে, আমরা আপনাকে 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

আপনার প্রজেক্টের রুটে একটি নতুন .env.local ফাইল তৈরি করুন এবং আপনার Upstash অ্যাকাউন্ট থেকে নিচের এনভায়রনমেন্ট ভেরিয়েবলগুলো যোগ করুন:

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

সমাধান ওভারভিউ

কোড ইমপ্লিমেন্ট করার আগে, আসুন সমাধান ওভারভিউ দেখি। আমরা তিনটি Next.js API রুট তৈরি করব:

  • POST /api/schedule-cron - ব্যবহারকারীর টাইমজোন অনুযায়ী ইমেল ক্রন জব শিডিউল করার জন্য।
  • POST /api/cancel-schedule - শিডিউল করা ইমেল ক্রন জব ক্যানসেল করার জন্য।
  • POST /api/send-email - শিডিউল করা ক্রন জব দ্বারা ট্রিগার হয়ে ইমেল পাঠাতে।

API রুটগুলোর পাশাপাশি, আমরা একটি সিম্পল ফর্মও তৈরি করব ব্যবহারকারীর পছন্দের ইমেল পাওয়ার সময় সংগ্রহ করার জন্য।

ইউজার ইন্টারফেস তৈরি করা

ইউজার ইন্টারফেস তৈরি করতে, আমরা app/page.tsx-এ একটি নতুন পেজ তৈরি করব নিচের কোডের সাথে:

"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 এন্ডপয়েন্টগুলো তৈরি করব।

QStash দিয়ে টাইমজোন-অवेয়ার শিডিউলিং: ইমেল ألার্ট সঠিক সময়ে ডেলিভার করুন

ইমেল ক্রন জব শিডিউল করা

QStash দিয়ে টাইমজোন-অवेয়ার শিডিউলিং: ইমেল ألার্ট সঠিক সময়ে ডেলিভার করুন

আসুন POST /api/schedule-cron রুট তৈরি করা শুরু করি। এই রুটটি ব্যবহারকারীর টাইমজোন অনুযায়ী ইমেল ক্রন জব শিডিউল করতে ব্যবহৃত হবে। আমরা QStash লাইব্রেরি ব্যবহার করব ক্রন জব শিডিউল করার জন্য।

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!, });
// আপনার ডোমেনের সাথে মেলাতে এই এন্ডপয়েন্টটি এডিট করুন
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);
  // নতুন তৈরি করার আগে বিদ্যমান শিডিউল সরান
  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} * * *`;
  // নতুন শিডিউল তৈরি ও সংরক্ষণ
  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")}`; }

কোডটির কোর হলো scheduleSummary ফাংশন, যা একটি API এন্ডপয়েন্ট। এটি ইনকামিং POST রিকোয়েস্ট হ্যান্ডেল করে এবং নিচের ধাপগুলো সম্পাদন করে:

  1. রিকোয়েস্ট মেথড ভ্যালিডেট করে, নিশ্চিত করে যে এটি একটি POST রিকোয়েস্ট।
  2. রিকোয়েস্ট বডি থেকে ডেটা এক্সট্র্যাক্ট করে, যার মধ্যে userId, selectedTime, এবং utcOffset।
  3. Upstash ডেটাবেসে ব্যবহারকারীর ইমেল শিডিউলের জন্য একটি কি কনস্ট্রাক্ট করে।
  4. ব্যবহারকারীর জন্য বিদ্যমান যেকোনো শিডিউল রিট্রিভ করে এবং QStash থেকে সরিয়ে দেয়।
  5. ব্যবহারকারীর সিলেক্টেড টাইমকে UTC ফরম্যাটে কনভার্ট করে convertToUTC ফাংশন ব্যবহার করে যা একটি টাইম স্ট্রিং এবং UTC অফসেট নেয়, অফসেট অ্যাকাউন্ট করে টাইমকে UTC ফরম্যাটে কনভার্ট করে।
  6. QStash-এর শিডিউলিং ফাংশনালিটি ব্যবহার করে ইমেল সারাংশ পাঠানোর জন্য একটি নতুন শিডিউল তৈরি করে এবং userId পেলোড হিসেবে যোগ করে যা /api/send-email এন্ডপয়েন্ট পাবে।
  7. নতুন তৈরি শিডিউল আইডি Upstash ডেটাবেসে সংরক্ষণ করে।

ইমেল সারাংশ পাঠানো

POST /api/send-email রুটটি শিডিউল করা ক্রন জব দ্বারা ট্রিগার হবে ইমেল পাঠাতে। আমরা @upstash/qstash/nextjs লাইব্রেরি ব্যবহার করব রিকোয়েস্টের সিগনেচার যাচাই করতে। এটি নিশ্চিত করবে যে রিকোয়েস্টটি QStash থেকে আসছে অন্য যেকোনো সোর্স থেকে না।

POST /api/send-email ফাংশনটি রিকোয়েস্ট বডিতে userId পায়। আপনি userId এর উপর ভিত্তি করে ইমেল প্রস্তুত ও পাঠানোর ফাংশন ইমপ্লিমেন্ট করতে পারেন।

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;
  // ইমেল প্রস্তুত ও পাঠান
  return res.status(200).json({ message: "success" }); }
export default verifySignature(handler);
export const config = { api: { bodyParser: false, }, };

শিডিউল করা ইমেল ক্রন জব ক্যানসেল করা

QStash দিয়ে টাইমজোন-অवेয়ার শিডিউলিং: ইমেল ألার্ট সঠিক সময়ে ডেলিভার করুন

এখন, আসুন POST /api/cancel-schedule রুট তৈরি করি। এই রুটটি শিডিউল করা ইমেল ক্রন জব ক্যানসেল করতে ব্যবহৃত হবে। আমরা QStash লাইব্রেরি ব্যবহার করব ক্রন জব ক্যানসেল করার জন্য।

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);
  // নতুন তৈরি করার আগে বিদ্যমান শিডিউল সরান
  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 ব্যবহার করে ব্যবহারকারীর টাইমজোন অনুযায়ী ইমেল শিডিউল করতে সক্ষম হয়েছি। আমরা এটি ব্যবহারকারীর টাইমজোন আমাদের ডেটাবেসে সংরক্ষণ না করেই অর্জন করেছি। আমরা ব্যবহারকারীদের যেকোনো সময় শিডিউল করা ইমেল নোটিফিকেশন ক্যানসেল করার ক্ষমতাও দিয়েছি।

আপনি যদি আপনার প্রোডাক্টের ডকুমেন্টেশন মেইন্টেইন করেন, তবে docsly চেক আউট করুন। এটি টেকনিক্যাল ডকুমেন্টেশনের জন্য একটি ফিডব্যাক টুল যা আপনাকে আপনার ব্যবহারকারীদের থেকে ফিডব্যাক সংগ্রহ করতে এবং সেগুলোকে অ্যাকশনযোগ্য ইন্সাইটে রূপান্তর করতে সাহায্য করে।

  1. জাভাতে JComboBox আইটেমগুলোর বর্ডার সেট করুন

  2. PHP-তে imagecopymerge() ফাংশন

  3. HTML-এ সাবমিট বাটনকে অন্য ওয়েবপেজের সাথে লিংক কীভাবে করবেন?

  4. জাভাস্ক্রিপ্টে অবজেক্ট সমতা ব্যাখ্যা