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

สร้างตัวจัดตารางโพสต์ X (Twitter) ด้วย Upstash: คู่มือทีละขั้นตอน

ในคู่มือทีละขั้นตอนนี้ ผมจะแชร์วิธีการสร้างตัวจัดตารางโพสต์ X (Twitter) โดยใช้ Upstash QStash, Upstash Redis, Next.js Server Actions และ Vercel การจัดตารางโพสต์ช่วยให้คุณรักษาความปรากฏตัวที่สม่ำเสมอ เข้าถึงผู้ติดตามในเวลาที่เหมาะสม และจัดการกลยุทธ์เนื้อหาได้อย่างมีประสิทธิภาพ

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

คุณจะต้องมีสิ่งต่อไปนี้:

  • Node.js 18 หรือใหม่กว่า
  • บัญชี Upstash
  • บัญชี X (Twitter)
  • บัญชี Vercel

เทคโนโลยีที่ใช้

เทคโนโลยีคำอธิบาย
Upstashแพลตฟอร์มฐานข้อมูล Serverless ใช้ Upstash Queue และ QStash สำหรับเก็บโพสต์ในคิวและเรียก API ตามเวลาที่กำหนด
Next.jsเฟรมเวิร์ก React สำหรับเว็บ ใช้ shadcn/ui สำหรับสร้าง UI รวดเร็ว
TailwindCSSเฟรมเวิร์ก CSS สำหรับออกแบบที่ปรับแต่งได้
Vercelแพลตฟอร์มคลาวด์สำหรับปรับใช้และปรับขนาดแอปพลิเคชันเว็บ
Prettierเครื่องมือจัดรูปแบบโค้ดแบบมีความคิดเห็น สำหรับสไตล์โค้ดที่สม่ำเสมอ

ขั้นตอนการทำงาน

เพื่อสร้างและปรับใช้ตัวจัดตารางโพสต์ของคุณเอง ให้ทำตามขั้นตอนเหล่านี้:

  • ตั้งค่า Upstash Redis
  • ตั้งค่า Upstash QStash
  • ตั้งค่า Twitter Developer Application
  • สร้างแอปพลิเคชัน Next.js ใหม่
  • นำระบบยืนยันตัวตนผู้ใช้ด้วย Twitter OAuth 2.0 ไปใช้
  • สร้าง UI สำหรับจัดตารางโพสต์
  • จัดตารางโพสต์ด้วย Upstash QStash
  • ปรับใช้งานบน Vercel

ตั้งค่า Upstash Redis

หลังจากสร้างบัญชี Upstash และเข้าสู่ระบบแล้ว ให้ไปที่แท็บ Redis และสร้างฐานข้อมูล

สร้างตัวจัดตารางโพสต์ X (Twitter) ด้วย Upstash: คู่มือทีละขั้นตอน

สร้างตัวจัดตารางโพสต์ X (Twitter) ด้วย Upstash: คู่มือทีละขั้นตอน

หลังจากสร้างฐานข้อมูลแล้ว ให้เลื่อนลงจนเจอส่วน REST API และเลือกปุ่ม .env คัดลอกเนื้อหาและเก็บไว้ในที่ปลอดภัย

สร้างตัวจัดตารางโพสต์ X (Twitter) ด้วย Upstash: คู่มือทีละขั้นตอน

ตั้งค่า Upstash QStash

เพื่อจัดตารางส่งคำขอ POST ไปยัง endpoint ตามช่วงเวลาที่กำหนด ให้ใช้ QStash ไปที่แท็บ QStash และเลื่อนลงไปที่แท็บ Request Builder

สร้างตัวจัดตารางโพสต์ X (Twitter) ด้วย Upstash: คู่มือทีละขั้นตอน

คัดลอก QStash URL, TOKEN, Current Signing Key และ Next Signing Key แล้วเก็บไว้ในที่ปลอดภัย

สร้างตัวจัดตารางโพสต์ X (Twitter) ด้วย Upstash: คู่มือทีละขั้นตอน

ตั้งค่า Twitter Developer Application

เพื่อตั้งค่าการยืนยันตัวตนด้วย Twitter OAuth 2.0 ให้สร้างแอปพลิเคชันใน Twitter Developer Portal:

  • เปิด Twitter Developer Portal > Projects
  • สร้าง Project
  • ไปที่แท็บ Settings ในการตั้งค่าแอปพลิเคชัน และทำดังนี้:
    • เลือก Read and write and Direct message ใน App permissions
    • เลือก Web App, Automated App or Bot ใน Type of App
    • ใส่ https://localhost:3000/api/auth/callback/twitter เป็น Callback URI / Redirect URL
  • ไปที่แท็บ Keys and tokens เลื่อนลงและทำดังนี้:
    • คัดลอก Client ID เก็บเป็น TWITTER_CLIENT_ID
    • คัดลอก Client Secret เก็บเป็น TWITTER_CLIENT_SECRET

เพียงเท่านี้ก็ตั้งค่า Twitter Developer Application สำหรับ OAuth 2.0 เรียบร้อยแล้ว

สร้างแอปพลิเคชัน Next.js ใหม่

เริ่มต้นด้วยการสร้างโปรเจกต์ Next.js ใหม่ เปิดเทอร์มินัลและรันคำสั่ง:

npx create-next-app@latest schedule-qstash-queue-upstash

เมื่อถูกถาม ให้เลือก:

  • Yes สำหรับ TypeScript
  • No สำหรับ ESLint
  • Yes สำหรับ Tailwind CSS
  • No สำหรับโฟลเดอร์ src/
  • Yes สำหรับ App Router
  • No สำหรับปรับแต่ง import alias เริ่มต้น (@/*)

เมื่อเสร็จแล้ว ให้เข้าไปในไดเรกทอรีโปรเจกต์และรันในโหมดพัฒนา:

cd schedule-qstash-queue-upstash npm run dev

แอปจะรันบน localhost:3000

สร้างไฟล์ .env ที่รูทของโปรเจกต์ และเพิ่มค่าที่เก็บไว้จากขั้นตอนก่อนหน้า:

# .env
# Twitter Environment Variables
TWITTER_CLIENT_ID="..."
TWITTER_CLIENT_SECRET="..."
TWITTER_AUTH_CALLBACK_URL="https://localhost:3000/api/auth/callback/twitter"

# Upstash Environment Variables
UPSTASH_REDIS_REST_URL="https://...upstash.io"
UPSTASH_REDIS_REST_TOKEN="...="
QSTASH_URL="https://qstash.upstash.io/v2/publish/"
QSTASH_TOKEN="...="
QSTASH_CURRENT_SIGNING_KEY="sig_..."
QSTASH_NEXT_SIGNING_KEY="sig_..."

ผสานรวมคอมโพเนนต์ shadcn/ui

เพื่อสร้าง UI รวดเร็ว ให้ตั้งค่า shadcn/ui กับ Next.js รันคำสั่ง:

npx shadcn-ui@latest init

ตอบคำถามการตั้งค่า components.json ดังนี้:

  • Yes สำหรับ TypeScript
  • Default สำหรับสไตล์
  • Slate สำหรับสีพื้นฐาน
  • app/globals.css สำหรับไฟล์ CSS ทั่วไป
  • yes สำหรับ CSS variables
  • เว้นว่าง สำหรับ tailwind prefix
  • tailwind.config.ts สำหรับตำแหน่ง tailwind.config.js
  • @/components สำหรับ alias คอมโพเนนต์
  • @/lib/utils สำหรับ alias utils
  • Yes สำหรับ React Server Components
  • Yes สำหรับเขียน config ลง components.json

ติดตั้งคอมโพเนนต์ที่ต้องการ:

npx shadcn-ui@latest add button npx shadcn-ui@latest add input npx shadcn-ui@latest add textarea npx shadcn-ui@latest add toast npx shadcn-ui@latest add popover npx shadcn-ui@latest add calendar

จะได้โฟลเดอร์ ui ภายใน app/components พร้อมคอมโพเนนต์ต่างๆ

แก้ไข app/layout.tsx เพื่อเพิ่ม Toaster:

// File: app/layout.tsx
import './globals.css'
import { cn } from '@/lib/utils'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { Toaster } from '@/components/ui/toaster'

const fontSans = Inter({
  subsets: ['latin'],
  variable: '--font-sans',
})

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) {
  return (
    
      
        {children}
        
      
    
  )
}

สร้าง Upstash Queue สำหรับเก็บโพสต์ที่จัดตารางไว้

ในส่วนนี้ จะเรียนรู้การใช้ Upstash Queue เก็บข้อมูลโพสต์ที่จัดตารางไว้ และสร้างโค้ดฝั่งเซิร์ฟเวอร์ที่ทำงานเป็นฟังก์ชัน (Server Actions) แทน API route

ติดตั้ง SDK ของ Upstash:

npm install @upstash/qstash @upstash/queue @upstash/redis@1.28.0

แพ็กเกจที่ติดตั้ง:

  • @upstash/qstash: SDK สำหรับโต้ตอบกับ QStash ผ่าน HTTP
  • @upstash/queue: SDK จัดการคิวข้อความแบบสตรีม บน Upstash Redis
  • @upstash/redis: SDK สำหรับ Redis ผ่าน REST API

เริ่มต้น Upstash Redis และ Queue Clients

สร้างไฟล์ lib/upstash.ts:

// File: lib/upstash.ts
import { Redis } from '@upstash/redis'
import { Queue } from '@upstash/queue'

export const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL as string,
  token: process.env.UPSTASH_REDIS_REST_TOKEN as string,
})

export const queue = new Queue({
  redis,
  queueName: 'tweets',
  concurrencyLimit: 5,
})

โค้ดข้างต้น:

  • นำเข้าคลาส Redis และ Queue
  • ส่งออกอินสแตนซ์ redis ชี้ไปที่ URL Upstash Redis พร้อม token
  • ส่งออกอินสแตนซ์ queue ชื่อคิว tweets และ concurrencyLimit = 5

ใช้ queue instance สร้าง Server Action ที่รับข้อความและวันที่โพสต์ แล้วส่งเข้าคิว สร้างไฟล์ app/schedule.server.tsx:

// File: app/schedule.server.tsx
'use server'
import { queue } from '@/lib/upstash'
import type { FormProps } from './form'

export async function schedule(_: any, formData: FormData): Promise {
  try {
    const tweet_text = formData.get('tweet_text') as string
    const tweet_date = formData.get('tweet_date') as string
    const now = new Date().getTime()
    const delay = new Date(tweet_date).getTime() - now
    await queue.sendMessage({ tweet_text, tweet_date }, delay)
    return { ok: true, tweet_date }
  } catch (e) {
    console.log(e)
    return { ok: false }
  }
}

โค้ดทำงาน:

  • นำเข้า queue instance
  • ส่งออก server action ชื่อ schedule รับฟอร์มที่มีข้อความและวันที่
  • คำนวณช่วงเวลาจนถึงเวลาที่จัดตารางไว้
  • ใส่ข้อมูลโพสต์พร้อม delay ที่คำนวณได้

วิธีนี้ทำให้โพสต์ถูกเพิ่มในคิวเมื่อถึงเวลาเท่านั้น ช่วยจัดการสถานะคิวได้ง่ายขึ้น

นำระบบยืนยันตัวตนผู้ใช้ด้วย Twitter OAuth 2.0 ไปใช้

ส่วนนี้จะสอนการตั้งค่า Twitter OAuth 2.0 โดยใช้ Twitter SDK สร้าง endpoint ให้ผู้ใช้อนุญาตสิทธิ์ และเก็บ access token ใน Upstash Redis

ติดตั้งไลบรารี Twitter OAuth:

npm install twitter-api-sdk

สร้าง Twitter Authentication Client

สร้างไฟล์ lib/twitter.ts:

// File: lib/twitter.ts
import { auth } from 'twitter-api-sdk'

export const authClient = new auth.OAuth2User({
  client_id: process.env.TWITTER_CLIENT_ID as string,
  callback: process.env.TWITTER_AUTH_CALLBACK_URL as string,
  client_secret: process.env.TWITTER_CLIENT_SECRET as string,
  scopes: ['tweet.write', 'tweet.read', 'offline.access', 'users.read'],
})

เตรียม Twitter Authorization URL

ขั้นแรกของ OAuth 2.0 คือ redirect ผู้ใช้ไปยัง authorization URL สร้างไฟล์ app/api/auth/twitter/route.ts:

// File: app/api/auth/twitter/route.ts
export const dynamic = 'force-dynamic'
import { NextResponse } from 'next/server'
import { authClient } from '@/lib/twitter'

export async function GET() {
  const authUrl = authClient.generateAuthURL({
    state: 'state',
    code_challenge: 'challenge',
    code_challenge_method: 'plain',
  })
  return NextResponse.redirect(authUrl, 303)
}

เตรียม Authorization Callback URL

ขั้นที่สอง คือจัดการ callback จาก Twitter สร้างไฟล์ app/api/auth/callback/twitter/route.ts:

// File: app/api/auth/callback/twitter/route.ts
export const dynamic = 'force-dynamic'
import { redis } from '@/lib/upstash'
import { NextResponse } from 'next/server'
import { authClient } from '@/lib/twitter'

export async function GET(request: Request) {
  const code = new URL(request.url).searchParams.get('code')
  if (!code) return NextResponse.json({}, { status: 403 })

  authClient.generateAuthURL({
    state: 'state',
    code_challenge: 'challenge',
    code_challenge_method: 'plain',
  })

  const { token: { access_token }, } = await authClient.requestAccessToken(code)
  await redis.set('twitter_oauth_access_token', access_token)
  return NextResponse.redirect(new URL('/', request.url), 303)
}

สร้าง endpoint ตรวจสอบสถานะการยืนยันตัวตน app/api/auth/twitter/authenticated/route.ts:

// File: app/api/auth/twitter/authenticated/route.ts
export const dynamic = 'force-dynamic'
import { redis } from '@/lib/upstash'
import { NextResponse } from 'next/server'

export async function GET() {
  try {
    const access_token = await redis.get('twitter_oauth_access_token')
    if (!access_token) return NextResponse.json({ ok: false }, { status: 200 })
    return NextResponse.json({ ok: true }, { status: 200 })
  } catch(e) {}
  return NextResponse.json({ ok: false }, { status: 200 })
}

สร้าง UI สำหรับจัดตารางโพสต์

สร้างคอมโพเนนต์ฟอร์ม reactive ด้วย React hooks useFormState และ useFormStatus สร้างไฟล์ app/form.tsx:

// File: app/form.tsx
'use client'
import { cn } from '@/lib/utils'
import { format } from 'date-fns'
import { Button } from '@/components/ui/button'
import { Calendar } from '@/components/ui/calendar'
import { useToast } from '@/components/ui/use-toast'
import { Textarea } from '@/components/ui/textarea'
import { Calendar as CalendarIcon } from 'lucide-react'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { useFormStatus } from 'react-dom'
import { useEffect, useState } from 'react'

export interface FormProps {
  ok?: boolean
  tweet_date?: string
}

export default function ({ ok, tweet_date }: FormProps) {
  const { toast } = useToast()
  const { pending } = useFormStatus()
  
  useEffect(() => {
    if (!pending) {
      if (ok) {
        const scheduleForm = document.getElementById('schedule_form') as HTMLFormElement
        if (scheduleForm) scheduleForm.reset()
        toast({ title: 'Scheduled Tweet', description: tweet_date })
      } else {
        toast({ variant: 'destructive', title: 'Uh oh! Something went wrong.', description: 'There was a problem with your request.' })
      }
    }
  }, [pending])

  const [date, setDate] = useState()

  return (
    <>
      Tweet Scheduler