ในโพสต์นี้ ฉันพูดถึงวิธีที่ itsmy.fyi (ทางเลือกโอเพ่นซอร์สสำหรับ LinkTree) ถูกสร้างขึ้นด้วย Upstash, Astro, GitHub และ Edgio Upstash ช่วยให้ฉันจัดการข้อมูล (CRUD) ของผู้ใช้ทั้งหมด เสนอการจำกัดอัตราที่กว้างขวางเมื่อเปรียบเทียบกับ GitHub API สำหรับการดำเนินการ CRUD และใช้การจำกัดอัตราแบบละเอียด

สิ่งที่เราจะใช้
- Astro (ส่วนหน้าและส่วนหลัง)
- Upstash (การจำกัดอัตราและการดำเนินการ CRUD)
- ปัญหา GitHub และ Webhooks (CMS สาธารณะเพื่อจัดการโปรไฟล์ผู้ใช้)
- Tailwind CSS (สไตล์)
- Edgio (การปรับใช้)
สิ่งที่คุณต้องการ
- บัญชี GitHub
- บัญชี Upstash เพื่อสร้างฐานข้อมูล
การตั้งค่า Upstash Redis
เมื่อคุณสร้างบัญชี Upstash และเข้าสู่ระบบแล้ว คุณจะไปที่แท็บ Redis และสร้างฐานข้อมูล


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

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

การตั้งค่าโครงการ
หากต้องการตั้งค่า เพียงโคลน repo ของแอปแล้วทำตามบทช่วยสอนนี้เพื่อเรียนรู้ทุกอย่างที่อยู่ในนั้น หากต้องการแยกโปรเจ็กต์ ให้รัน:
git clone https://github.com/rishi-raj-jain/itsmy.fyi
cd itsmy.fyi
yarn install เมื่อคุณโคลน repo แล้ว คุณจะต้องสร้างไฟล์ .env คุณกำลังจะเพิ่มรายการที่เราบันทึกไว้จากส่วนด้านบน
มันควรมีลักษณะดังนี้:
.env# Obtained from your GitHub repo
GITHUB_API_TOKEN="to_create_and_update_github_comments"
GITHUB_WEBHOOK_SECRET="if_you_are_matching_github_webhooks_sha256"
# Obtained from the steps as above
UPSTASH_DB="your_upstash_redis_from_above"
UPSTASH_REDIS_REST_URL="your_upstash_redis_rest__url_from_above"
UPSTASH_REDIS_REST_TOKEN="your_upstash_redis_rest__token_from_above" หลังจากขั้นตอนเหล่านี้ คุณควรจะสามารถเริ่มต้นสภาพแวดล้อมภายในเครื่องได้โดยใช้คำสั่งต่อไปนี้:
yarn run edgio:dev โครงสร้างพื้นที่เก็บข้อมูล
นี่คือโครงสร้างโฟลเดอร์หลักสำหรับโปรเจ็กต์ ฉันได้วงกลมสีแดงไว้ในไฟล์ที่จะกล่าวถึงเพิ่มเติมในโพสต์นี้ที่เกี่ยวข้องกับการดำเนินการ CRUD การจำกัดอัตรา และไฟล์ที่มีการอ้างอิง

กระแสข้อมูลระดับสูง
นี่คือแผนภาพระดับสูงที่แสดงการไหลของข้อมูล
- หากผู้ใช้เยี่ยมชม itsmy.fyi/me/slug และการตอบสนองต่อหน้านี้ไม่ได้ถูกแคชไว้ (หรือกำลังได้รับการตรวจสอบความถูกต้องอีกครั้ง) ผู้ใช้จะเรียกใช้ฟังก์ชัน getUserInfo ซึ่งจะดึงข้อมูล json ของผู้ใช้จาก Upstash DB
- หากผู้ใช้สร้าง อัปเดต หรือลบปัญหา GitHub GitHub จะทริกเกอร์ Webhook ซึ่ง POST ไปยังจุดสิ้นสุด ในตำแหน่งข้อมูลนั้น อันดับแรกจะใช้การจำกัดอัตรา Upstash เพื่อประเมินว่าสามารถทำการเปลี่ยนแปลงที่ร้องขอได้หรือไม่ จากนั้นใช้ Upstash ผู้ใช้ json จะถูกสร้างขึ้น อัปเดต หรือลบ

โปรไฟล์ผู้ใช้ การดำเนินการ CRUD ผ่าน Upstash Redis
ในส่วนนี้ เราจะเจาะลึกเกี่ยวกับวิธีการดึงข้อมูล การอัปเดต และการลบโปรไฟล์ผู้ใช้ เราใช้ Upstash อย่างต่อเนื่อง (ผ่าน 14 ) เพื่อดึงและแสดงข้อมูล
เหตุใดฉันจึงย้ายจาก GitHub ไปยัง Upstash สำหรับการดำเนินการ CRUD
ในขณะที่ฉันเริ่มต้นด้วย GitHub เป็นแหล่งที่มาของการจัดการข้อมูล:จากแบบฟอร์มข้อมูล Github Issuesas ไปจนถึง GitHub Webhooks ไปจนถึง jsons ผู้ใช้ CRUD ภายในพื้นที่เก็บข้อมูล ข้อจำกัดของ GitHub REST API:27 ดูเหมือนจะจำกัดและค่อนข้างถดถอยการใช้งานตามวัตถุประสงค์ของแพลตฟอร์ม
Upstash โดดเด่นกว่ามากเพราะมันเสนอคำสั่งให้ฉัน 10,000 คำสั่งทุกวันในแผนฟรีของพวกเขาเพื่อเริ่มต้น และหลังจากนั้นก็มีอัตราที่น้อยมากเมื่อการใช้งานของฉันปรับขนาด แนวทางประเภทนี้ทำให้ฉันได้ผู้ใช้มามากขึ้นโดยแทบไม่ต้องเสียค่าใช้จ่ายเลย และทำซ้ำได้เร็วขึ้นโดยไม่ต้องกังวลกับการปรับขนาดและการจัดการต้นทุนของฐานข้อมูล
getUserInfo:การดึงฟังก์ชันโปรไฟล์ผู้ใช้
33รหัส> ฟังก์ชั่นใช้ 47 ของ ioredis ผ่าน slug เป็นกุญแจสำคัญในการสร้างคำขอ API เพื่อ Upstash สำหรับหน้าโปรไฟล์ผู้ใช้ที่เกี่ยวข้อง ซึ่งระบุด้วย 54 ที่ไม่ซ้ำกัน หากไม่มีโปรไฟล์ผู้ใช้นั้น (หรือมีข้อผิดพลาด) ฟังก์ชันจะถูกตั้งค่าให้ส่งคืนออบเจ็กต์ที่มี 65 เพื่อให้ผู้ใช้สามารถเปลี่ยนเส้นทางไปที่ 404 โดยอัตโนมัติในเส้นทางไดนามิกของ Astro
// File: lib/Upstash/users/get.js
// Read User Profile Code
import redis from "../setup";
export async function getUserInfo(slug) {
try {
const userData = await redis.hget("profiles", slug);
const parsedData = JSON.parse(userData);
if (parsedData.slug === slug) {
return { ...parsedData, code: 1 };
}
return {
code: 0,
error: `slug doesn't match for the user.`,
};
} catch (e) {
const error = e.message || e.toString();
console.log(error);
return {
code: 0,
error,
};
}
} ในทำนองเดียวกัน การดำเนินการ CRUD ที่เหลือจะเป็นดังนี้:
import redis from "../setup";
// File: @/lib/Upstash/users/delete.js
// Delete User Profile Code
export async function deleteUserInfo(slug) {
try {
await redis.hdel("profiles", slug);
return { code: 1 };
} catch (e) {
console.log(e.message || e.toString());
return {
code: 0,
};
}
}
// File: @/lib/Upstash/users/post.js
// Create/Update User Profile Code
export async function postUserInfo(data) {
try {
await redis.hset("profiles", data.slug, JSON.stringify(data));
return { code: 1 };
} catch (e) {
const error = e.message || e.toString();
console.log(error);
return {
code: 0,
error,
};
}
} การจำกัดอัตรา
หากต้องการใช้การจำกัดอัตราในสภาพแวดล้อมแบบไร้เซิร์ฟเวอร์ด้วย Edgio เราใช้ไคลเอนต์ฐานข้อมูล Upstash Redis และไลบรารีตัวจำกัดอัตราที่เรียกว่า 70 .
// Reference Function to ratelimiting
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { getENV } from "@/lib/env";
const url = getENV("UPSTASH_REDIS_REST_URL");
const token = getENV("UPSTASH_REDIS_REST_TOKEN");
export const ratelimit = (number, time) => {
if (url && token) {
return new Ratelimit({
redis: new Redis({
url,
token,
}),
limiter: Ratelimit.fixedWindow(number, time),
});
}
return;
}; การใช้การจำกัดอัตราทำให้ฉันสามารถบรรลุสิ่งต่อไปนี้:
ก. ใช้บริการ - ฟรีและไม่จำกัด
ด้วยการใช้การจำกัดอัตรา ฉันสามารถเปิดเผย API การสร้างโปรไฟล์แบบสาธารณะได้! สิ่งนี้ทำให้ฉันสามารถแสดงประโยชน์ของระบบ เช่น ความง่ายในการตั้งค่าโปรไฟล์ผ่าน GUI แท้จริงแล้วใครก็ตามสามารถสร้าง 3 โปรไฟล์ในหนึ่งสัปดาห์ผ่านทางเว็บไซต์ (itsmy.fyi) เอง และสำหรับการเข้าถึงคุณสมบัติอย่างไม่จำกัด เช่น แก้ไขโปรไฟล์ สร้างโปรไฟล์ไม่จำกัด ผู้ใช้จะต้องเปลี่ยนไปใช้วิธีสร้างโปรไฟล์ของ GitHub เราสามารถบังคับใช้ขีดจำกัดอัตรา 3 โปรไฟล์ในหนึ่งสัปดาห์ตาม IP ที่อยู่เป็นคีย์
// Rate limit 3 profiles in a week via the web for a user
const ratelimitUser = ratelimit(3, 7 * 24 * 60 * 60 + " s");
if (rateLimiter) {
// Look at the x-0-client-ip set by Edgio in serverless
const result = await rateLimiter.limit("x-0-client-ip");
limit = result.limit;
remaining = result.remaining;
if (!result.success) {
// Return a message
}
} บี ใช้การกลั่นกรองแบบละเอียดกับจำนวนการแก้ไขโดยผู้ใช้
นอกจากนี้ ด้วยการจำกัดอัตรา เราจึงสามารถกลั่นกรองจำนวนการแก้ไขที่ทำโดยผู้ใช้ในหนึ่งนาทีตามชื่อผู้ใช้ GitHub ของพวกเขา ณ ขณะนี้ เราอนุญาตให้พวกเขาทำการเปลี่ยนแปลงได้สูงสุด 3 รายการภายในหนึ่งนาที ซึ่งจะช่วยลดสแปมที่มองไม่เห็น
@/pages/github/hook/issue.jsconst rateLimiter = ratelimit(3, "60 s");
if (rateLimiter) {
const result = await rateLimiter.limit(context.sender.login);
limit = result.limit;
remaining = result.remaining;
if (!result.success) {
return {
headers: {
"X-RateLimit-Limit": limit,
"X-RateLimit-Remaining": remaining,
},
body: JSON.stringify({
message:
"Too many updates in 1 minute. Please try again in a few minutes.",
}),
};
}
} การใช้งานเก่าขณะตรวจสอบใหม่บน Edge สำหรับโปรไฟล์ผู้ใช้ทั้งหมด
รหัสต่อไปนี้อธิบายวิธีการใช้แนวคิด Stale While Revalidate เพื่อปรับปรุงอัตราการเข้าชมแคช ในรหัส (ใน 88 ) 97 ใช้เพื่อจับคู่โปรไฟล์ผู้ใช้ทั้งหมด (ที่ขึ้นต้นด้วย 106 ).ภายในวิธีการแคช เราป้องกันการแคชเพจในเบราว์เซอร์ และเปิดใช้งานเฉพาะการแคช Edge เท่านั้นเพื่อให้ให้บริการผู้ใช้อย่างรวดเร็วและด้วยเนื้อหาล่าสุดเสมอ ตัวเลือก Edge ถูกตั้งค่าเป็น 119 เพื่อให้แน่ใจว่าข้อมูลถูกแคชไว้เพียงวินาทีเดียว 129 ตัวเลือกถูกตั้งค่าเป็นปีเพื่อให้ข้อมูลสามารถให้บริการได้โดยตรงจากแคชในขณะที่แคชกำลังรีเฟรช
// User path(s)
router.match("/me/:path", ({ cache, removeUpstreamResponseHeader }) => {
// Remove the cache-control header from Astro's standalone server
removeUpstreamResponseHeader("cache-control");
// Disable in browser caching, and use Edgio's edge to use SWR
cache({
edge: {
maxAgeSeconds: 1,
staleWhileRevalidateSeconds: 60 * 60 * 24 * 365,
},
browser: false,
});
}); การใช้ Stale While Revalidate สามารถช่วยปรับปรุงประสิทธิภาพของแอปได้โดยการลดภาระบนเซิร์ฟเวอร์และให้การตอบสนองแก่ผู้ใช้เร็วขึ้น
การสร้างโปรไฟล์ผู้ใช้แบบไดนามิกได้ทันที
Astro ทำให้การตั้งค่าเส้นทางแบบไดนามิกเป็นเรื่องง่ายมาก ในแอป คุณจะพบ 133 ซึ่งจับคู่หน้าที่ขึ้นต้นด้วย 147 .ตัวอย่างได้แก่ 152 และ 163รหัส> .
กำลังดึงข้อมูลโปรไฟล์ผู้ใช้
เราดึงข้อมูลสำหรับผู้ใช้ปัจจุบันโดยใช้ 173 พารามิเตอร์การสืบค้นที่แยกออกจากพารามิเตอร์ Astro และเรียกใช้ฟังก์ชัน getUserInfo (ตามที่อธิบายไว้ข้างต้น) เพื่อรับข้อมูลผู้ใช้ที่เกี่ยวข้องทั้งหมด ในกรณีที่ไม่พบหรือมีข้อผิดพลาด เราจะเปลี่ยนเส้นทางผู้เยี่ยมชมไปที่ 404
import { getUserInfo } from "@/lib/Upstash/users";
// Extract slug query
const { slug } = Astro.params;
// Get data from Upstash using the getUserInfo function
const {
name: userName,
image: userImage,
links = [],
socials = [],
about = "",
og = {},
background = {},
code = 1,
} = await getUserInfo(userSlug);
// In case the code: 0 is recevied, redirect to a 404
if (code === 0) {
return Astro.redirect("/404");
} ปรับใช้จาก CLI
คุณสามารถสร้างเวอร์ชันที่ใช้งานจริงของแอปและทดสอบในเครื่องได้โดยใช้:
yarn run edgio:build && yarn run edgio:production การปรับใช้ต้องมีบัญชีใน Edgio ลงทะเบียนที่นี่ฟรี เมื่อคุณมีบัญชีแล้ว คุณสามารถปรับใช้กับ Edgio ได้โดยการรันคำสั่งต่อไปนี้ในโฟลเดอร์รูทของโปรเจ็กต์ของคุณ:
yarn run edgio:deploy ตอนนี้เราเสร็จสิ้นการปรับใช้แล้ว! ใช่ นั่นคือทั้งหมด
บทสรุป
โดยสรุป โปรเจ็กต์นี้ได้มอบประสบการณ์อันมีค่าในการใช้งานการจำกัดอัตราแบบละเอียด การดำเนินการข้อมูล CRUD ในแบบไร้เซิร์ฟเวอร์ ใช้ปัญหา GitHub เป็น CMS และตัดสินใจได้ดีขึ้นในการจัดส่ง MVP โดยใช้บริการที่ปรับขนาดตามความต้องการของคุณ เช่น Upstash