Rò rỉ dữ liệu là một vấn đề lớn trên internet. Theo Statista, chỉ riêng năm 2022 đã có hơn 400 triệu người bị ảnh hưởng do rò rỉ dữ liệu. Không ai muốn dữ liệu của mình bị rò rỉ trên internet, vì vậy việc xây dựng các ứng dụng an toàn tôn trọng quyền riêng tư của người dùng là điều vô cùng quan trọng trong nhiều ngành.
Một cách để chống rò rỉ là lọc dữ liệu có vấn đề trước khi cung cấp cho các dịch vụ hoặc người dùng khác. Phương pháp này yêu cầu các bộ lọc cập nhật để đảm bảo dữ liệu không bị lọt qua và cơ sở hạ tầng có độ trễ thấp, nhằm duy trì tác động đến hiệu suất của việc lọc ở mức nhỏ nhất có thể.
Upstash dành cho Redis® và Vercel Edge Functions tạo thành một nhóm hùng mạnh có thể giải quyết vấn đề đồng thời hoàn thiện cả hai yêu cầu. Cả hai giải pháp serverless có độ trễ thấp này đều có thể được triển khai gần với người dùng của chúng tôi. Với tính năng cron mới của Vercel, chúng tôi có thể đảm bảo dữ liệu bộ lọc được cập nhật thường xuyên.
Để minh họa cách hoạt động của một bộ lọc như vậy, chúng tôi sẽ xây dựng một giao diện người dùng và phần phụ trợ tận dụng công nghệ biên không có máy chủ này để lọc những lời tục tĩu.
Tính năng
Ứng dụng sẽ sử dụng tính năng cron của Vercel để cập nhật cơ sở dữ liệu Upstash cho Redis với các từ hiện tại từ API từ xa.
Chúng tôi sẽ có ba phương tiện để lấy dữ liệu:
- Trả lại một trang web có văn bản được lọc.
- Trả về JSON với văn bản được lọc từ kho dữ liệu.
- Chấp nhận văn bản và trả về JSON với văn bản đã lọc.
Công nghệ
Chúng tôi sẽ xây dựng ứng dụng với Next.js và triển khai nó trên Vercel; bằng cách này, chúng ta sẽ có trải nghiệm phát triển serverless liền mạch khi sử dụng các hàm biên.
Chúng tôi sẽ sử dụng Upstash cho Redis® làm nơi lưu trữ dữ liệu vì nó có độ trễ thấp và dễ sử dụng.
Cả hai dịch vụ đều có cấp độ miễn phí và giá theo yêu cầu.
Điều kiện tiên quyết
Chúng tôi cần tài khoản cho các dịch vụ:
- GitHub tải mã của chúng tôi lên để Vercel có thể tải xuống và triển khai nó
- Vercel để lưu trữ ứng dụng của chúng tôi, với các chức năng trang chủ và biên
- Upstash để lưu trữ danh sách các từ chúng ta muốn lọc
Triển khai
Để bắt đầu, chúng tôi tạo một kho lưu trữ GitHub mới và đảm bảo. Chúng tôi chọn "Thêm tệp README" để nó không trống. Vì kho lưu trữ không trống nên GitHub cho phép chúng tôi khởi động Codespace cho nó, được cấu hình sẵn với Node.js và kết nối Git-to-GitHub.
Hình 1:Bắt đầu không gian mã
Trước tiên, chúng ta cần tạo một dự án Next.js mới và cài đặt ứng dụng khách Upstash Redis bằng các lệnh sau:
$ npx create-next-app@latest --typescript
$ npm i @upstash/redis Thực hiện chức năng làm mới
Tính năng đầu tiên chúng ta sẽ triển khai là chức năng làm mới danh sách các từ xấu của chúng ta. Để làm như vậy, hãy tạo một tệp mới tại pages/api/refresh-list.ts với nội dung sau:
Tệp pages/api/refresh-list.ts :
import { Redis } from "@upstash/redis";
export const config = { runtime: "edge" };
const redisClient = new Redis({
url: process.env.UPSTASH_REDIS_URL,
token: process.env.UPSTASH_REDIS_TOKEN,
});
export default async function handler() {
const wordResponse = await fetch(
"https://raw.githubusercontent.com/kay-is/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words/master/en",
);
const words = await wordResponse.text();
const redisCommands = redisClient.pipeline();
words
.trim()
.split("\n")
.forEach((word) => redisCommands.sadd("words", word));
await redisCommands.exec();
} Chúng tôi bắt đầu bằng cách định cấu hình chức năng để chạy ở biên. Điều này không cần thiết ở đây vì chức năng này sẽ chạy ở chế độ nền, nhưng Chức năng Vercel Edge được cung cấp bởi Cloudflare Workers, cho phép chúng tôi sử dụng phương thức tìm nạp mà Node.js vốn không hỗ trợ.
Trình xử lý sẽ tải một tệp văn bản chứa các từ xấu vào Upstash Redis. Tính năng đường dẫn đảm bảo chúng tôi chỉ gửi một yêu cầu bằng tất cả các lệnh Redis.
Chúng tôi sử dụng một bộ để lưu trữ các từ, vì vậy chúng tôi không bị trùng lặp. Nó cũng cho phép chúng ta tải tất cả các từ dưới dạng một chuỗi các chuỗi bằng một lệnh sau đó.
Nếu muốn lọc dữ liệu cá nhân, chúng tôi có thể sử dụng cơ sở dữ liệu tài khoản để lấy email, số điện thoại, tên và địa chỉ của người dùng làm cơ sở cho bộ lọc.
Để nói với Vercel rằng chúng ta muốn hàm này là hàm cron, chúng ta cần tạo một tệp vercel.json trong thư mục gốc của dự án với nội dung sau:
Tệp vercel.json :
{
"crons": [
{
"path": "/api/refresh-list",
"schedule": "0 10 * * *"
}
]
} Cấu hình này sẽ khiến Vercel thực thi chức năng làm mới danh sách hàng ngày vào lúc 10:00 UTC.
Tài khoản Vercel miễn phí chỉ hỗ trợ thực hiện tự động một lần mỗi ngày. Đối với ví dụ của chúng tôi, thế là đủ, nhưng nếu chúng tôi có dữ liệu thay đổi thường xuyên hơn, chúng tôi nên tăng tốc độ cập nhật.
Triển khai tiện ích bộ lọc
Tính năng tiếp theo là một chức năng tiện ích sẽ che dấu các từ trong văn bản khi chúng tương ứng với các từ trong cơ sở dữ liệu của chúng tôi. Tạo một tệp mới tại utils/word-filter.ts và thêm mã sau.
Tệp utils/word-filter.ts :
import { Redis } from "@upstash/redis";
const redisClient = new Redis({
url: process.env.UPSTASH_REDIS_URL,
token: process.env.UPSTASH_REDIS_TOKEN,
});
export async function filter(text: string) {
const filteredWords = await redisClient.smembers("words");
let maskedText = text;
for (let word of filteredWords)
maskedText = maskedText.replaceAll(new RegExp(word, "gi"), "[REDACTED]");
return maskedText;
} Một lần nữa, hàm này sử dụng ứng dụng khách Upstash Redis, nhưng lần này nó tải dữ liệu chúng ta đã lưu trước đó.
Vì chúng ta nhận được một chuỗi các chuỗi nên chúng ta có thể chỉ cần lặp lại nó và gọi một hàm thay thế để thay thế mọi từ xấu trong văn bản bằng "[REDACTED]".
Chức năng này không quan tâm đến loại từ mà nó lọc ra. Trong trường hợp này là dòng chữ "không an toàn cho công việc", nhưng quá trình lọc chỉ phụ thuộc vào dữ liệu chúng ta đã lưu trữ trước đó.
Triển khai trang chủ
Để xem bộ lọc hoạt động, hãy thay thế nội dung của pages/index.ts với những điều sau đây:
Tệp pages/index.ts :
import Head from "next/head";
import { filter } from "@/utils/word-filter";
export const config = { runtime: "experimental-edge" };
interface HomeProps {
maskedText: string;
}
export default function Home(props: HomeProps) {
return (
<>
<Head>
<title>Text with Filtered Words</title>
</Head>
<div>
<h1>Text with Filtered Words</h1>
<p>{props.maskedText}</p>
</div>
</>
);
}
export async function getServerSideProps(): Promise<{ props: HomeProps }> {
const maskedText = await filter(
"He slipped and fell on his butt. Well, that wasn't very sexy."
);
return { props: { maskedText } };
} Cấu hình đảm bảo mọi thứ được thực thi trên biên, thậm chí cả kết xuất phía máy chủ. Tính năng Vercel này vẫn đang thử nghiệm.
Phần thú vị là getServerSideProps sử dụng filter của chúng tôi chức năng từ trước trên một văn bản tĩnh. Nó chỉ được gọi trên máy chủ nên dữ liệu chưa được lọc sẽ không bao giờ đến được máy khách.
Trong ứng dụng thực tế, văn bản này có thể đến từ cơ sở dữ liệu chứa dữ liệu cá nhân cần được làm sạch trước khi hiển thị.
Triển khai lộ trình API đầu tiên
Tuyến API đầu tiên hoạt động giống như trang chủ; nó trả về JSON và không có HTML. Tạo một tập tin tại pages/api/filtered-data.ts với mã này:
Tệp pages/api/filtered-data.ts :
import { filter } from "@/utils/word-filter";
export const config = { runtime: "edge" };
export default async function handler() {
const maskedText = await filter(
"He slipped and fell on his butt. Well, that wasn't very sexy.",
);
return new Response(JSON.stringify({ text: maskedText }), {
status: 200,
headers: { "content-type": "application/json" },
});
} Một lần nữa, thời gian chạy rất phức tạp và cũng giống như hàm getServerSideProps, chúng tôi sử dụng văn bản tĩnh.
Triển khai lộ trình API thứ hai
Tuyến đường này chấp nhận văn bản qua yêu cầu và trả về phiên bản đã lọc. Tạo một tệp mới tại pages/api/filter.ts và thêm đoạn mã sau:
Tệp pages/api/filter.ts :
import type { NextApiRequest } from "next";
import { filter } from "@/utils/word-filter";
export const config = { runtime: "edge" };
export default async function handler(request: NextApiRequest) {
const { text } = await new Response(request.body).json();
const maskedText = await filter(text);
return new Response(JSON.stringify({ text: maskedText }), {
status: 200,
headers: { "content-type": "application/json" },
});
}
Lần này, chúng ta phải phân tích nội dung để có được văn bản mà chúng ta muốn lọc. Trong Hàm Edge của Vercel, body là ReadableStream; nếu chúng ta chuyển đổi nó thành Response , chúng ta có thể sử dụng trình phân tích cú pháp JSON gốc để trích xuất dữ liệu.
Sau khi chúng tôi nhận được dữ liệu từ yêu cầu, mọi thứ vẫn hoạt động như trước.
Thay đổi mã đẩy
Bây giờ mọi thứ đã được triển khai, chúng ta cần đẩy mã vào kho GitHub bằng các lệnh sau:
$ git add -A
$ git commit -m "Init"
$ git push Sau đó, mã sẽ có sẵn trực tuyến để Vercel tải xuống và triển khai.
Triển khai
Chúng ta cần tạo cơ sở dữ liệu Upstash Redis để lấy thông tin xác thực cho các biến môi trường và dự án Vercel.
Tạo cơ sở dữ liệu Redis
Chúng ta có thể tạo cơ sở dữ liệu Redis mới trong Bảng điều khiển Upstash bằng cách nhấp vào nút "Tạo cơ sở dữ liệu". Hình 2 cho thấy cấu hình. Đối với ví dụ này, cơ sở dữ liệu khu vực là đủ, nhưng nếu bạn có người dùng phân bổ toàn cầu và muốn duy trì độ trễ thấp, bạn cũng có thể chọn loại toàn cầu.

Hình 2:Tạo cơ sở dữ liệu mới
Sau khi tạo, chúng tôi có thể tìm thấy URL và mã thông báo cần thiết cho các biến môi trường của mình trong danh mục API REST; nó trông giống như trong hình 3.

Hình 3:Thông tin xác thực cơ sở dữ liệu
Tạo dự án Vercel
Để tạo dự án Vercel mới, hãy mở Bảng điều khiển Vercel trong trình duyệt và nhấp vào "Tạo dự án mới" ở giữa. Sau khi kết nối Vercel với tài khoản GitHub, bạn có thể chọn kho lưu trữ để nhập.
Chúng tôi có thể giữ cấu hình mặc định và thêm các biến môi trường của mình bằng thông tin xác thực Upstash Redis ở trên. Hình 4 hiển thị giao diện người dùng tạo của Vercel để tham khảo.

Hình 4:Tạo dự án Vercel
Tên của các biến môi trường là UPSTASH_REDIS_URL và UPSTASH_REDIS_TOKEN . Chúng tôi sử dụng các giá trị từ bước trước để tạo chúng.
Sau khi nhấp vào "Triển khai", Vercel sẽ tải xuống và triển khai mã từ kho lưu trữ GitHub của chúng tôi.
Thử nghiệm ứng dụng
Sau khi triển khai, ứng dụng sẽ vẫn tiếp tục hiển thị các từ chưa được lọc do công việc định kỳ chưa chạy. Nhưng chúng ta có thể thực hiện lần đầu tiên một cách thủ công. Nhấp vào nút "Tiếp tục tới Trang tổng quan" và chọn tab "Cron Jobs".
Ở đây chúng ta thấy /api/refresh-list của chúng ta hoạt động với nút "Chạy" để nhấp.
Khi chức năng kết thúc, chúng tôi điều hướng đến tab "Dự án" và nhấp vào một trong các URL trong "Miền". Thao tác này sẽ mở trang web có văn bản được lọc trong trình duyệt; nó sẽ giống như trong hình 5.
Hình 5:Trang web được lọc
Nếu chúng ta thêm /api/filtered-data vào URL của chúng tôi, chúng tôi có thể thấy rằng điều này cũng hoạt động đối với các phản hồi API. Chúng sẽ trông giống như ví dụ sau:
{
"text": "He slipped and fell on his [REDACTED]. Well, that wasn't very [REDACTED]y."
}
Cuối cùng, nếu chúng ta gửi yêu cầu qua cURL tới /api/filter điểm cuối, chúng tôi sẽ lọc văn bản tùy chỉnh của mình. Đảm bảo thay thế <PROJECT> với dự án Vercel của bạn.
$ curl -X POST https://<PROJECT>.vercel.app/api/filter \
-H "Content-Type: application/json" \
-d '{"text":"He fell on his butt."}'
Câu trả lời:
{
"text": "He fell on his [REDACTED]."
} Tiếp theo là gì
Sau phần hướng dẫn này, bạn có thể tự hỏi điều gì sẽ xảy ra nếu dữ liệu được cập nhật nhưng công việc định kỳ chưa cập nhật cơ sở dữ liệu. Quan sát tốt!
Một công việc định kỳ sẽ kích hoạt chức năng làm mới của chúng tôi, nhưng đây vẫn là một chức năng API thông thường nên chúng tôi có thể gọi nó theo cách chúng tôi muốn.
Trong trường hợp bộ lọc dữ liệu thực, chúng tôi có thể muốn kích hoạt chức năng này để phản hồi thay đổi dữ liệu, nhưng chi tiết triển khai phụ thuộc nhiều vào bộ lưu trữ dữ liệu mà chúng tôi sử dụng làm cơ sở cho bộ lọc của mình. Vì vậy, hãy ghi nhớ điều đó khi xây dựng!
Tài nguyên bổ sung
Bạn có thể tìm thấy dự án hoàn chỉnh trên GitHub.