在这篇文章中,你将学习如何建立一个GitHub追踪器,当被追踪的仓库有新的问题/PR时,通过发送推送通知来通知用户。
如果你选择加入,GitHub 已经通过电子邮件发送通知了,但许多研究表明,推送通知比电子邮件更能到达用户。通过本教程建立 GitHub 跟踪器后,你将学会如何。
前提条件
你需要一些技能和服务来学习本文。
- 安装Node.js和npm
- 先前的Svelte知识
- 一个免费的GitHub账户,因为我们使用的是GitHub API
- 一个免费的MongoDB Atlas账户,以便在云端使用MongoDB
- 一个免费的Vercel账户,用于部署应用程序和云功能。
什么是推送通知?
让我们来看看这些所谓的 "推送通知 "是什么。
你一定对普通的通知很熟悉。这些是出现在你屏幕上的文字小气泡,用来通知你一些事情。推送通知与之相似,只是它们不是按需生成的,而是在收到推送事件时生成的。推送通知在应用程序关闭时也能发挥作用,而普通通知则需要你打开应用程序。
推送通知在现代网络浏览器中得到了支持,比如Chrome,它使用了一种叫做服务工作者的东西。服务工作者是独立于浏览器主线程运行的JavaScript小片段,因此,如果你的应用被安装为PWA(渐进式网络应用程序),则可以离线运行。
推送通知在聊天应用中用于通知用户有未读消息,在游戏中用于通知用户游戏事件,在新闻网站中用于通知用户有突发文章,以及其他许多用途。
在你的应用程序中显示推送通知有四个步骤。
- 申请许可
window.Notification.requestPermission() - 将你的应用程序转换为PWA并安装它
- 订阅推送事件
- 收到推送事件后,发送通知
第1步:创建跟踪器
让我们在本文中使用Svelte与Vite.js而不是Rollup。顾名思义,Vite比Rollup快,而且还提供了对环境变量的内置支持。要用Svelte和Vite创建一个新项目,请运行这个命令。
npm init vite
选择需要的框架svelte 。如果你愿意,你可以使用TypeScript。我将使用普通的JavaScript。
接下来,cd ,进入项目文件夹,你可以用这些命令将TailwindCSS添加到你的应用程序中,并安装所有的依赖项。
npx svelte-add tailwindcss
# Install packages
yarn install # or npm install
最后,在你最喜欢的代码编辑器中打开该项目,并运行npm run dev 或yarn dev ,在http://localhost:3000 上启动该应用程序。
追踪器将如何工作
我们将使用GitHub的API来获取用户所追踪的仓库的问题和拉取请求的列表。用户所追踪的仓库和他们的用户名将被存储在MongoDB数据库中。
第一步将是提示用户他们的用户名。创建src/lib/UsernamePrompt.svelte ,这将是做这件事的组件。这是我为表单设计的用户界面,但你可以随心所欲地设计它。
<script>
let username = "";
async function submit() {
// TODO
}
</script>
<form
on:submit|preventDefault="{submit}"
class="mx-auto min-w-[350px] max-w-[1100px] w-[50%] border border-gray-500 rounded my-4 px-6 py-4"
>
<h1 class="text-center text-3xl m-4">Enter a username</h1>
<p class="text-center text-xl m-4">Enter a username to use this tracker</p>
<input
type="text"
class="rounded px-4 py-2 border border-gray-300 w-full outline-none"
placeholder="Username"
aria-label="Username"
bind:value="{username}"
/>
<button
class="mt-4 border border-transparent bg-blue-500 text-white rounded px-4 py-2 w-full"
>
Submit
</button>
</form>
像这样在App.svelte 中添加这个组件。
<script>
import UsernamePrompt from "./lib/UsernamePrompt.svelte";
</script>
<UsernamePrompt />
接下来,让我们添加主跟踪器的用户界面。创建文件src/lib/Tracker.svelte ,并在其中添加以下代码。
<script>
let repo = "";
function track() {
// TODO
}
function untrack(repo) {
// TODO
}
</script>
<form
on:submit|preventDefault={track}
class="mx-auto min-w-[350px] max-w-[1100px] w-[50%] border border-gray-500 rounded my-4 px-6 py-4"
>
<h1 class="text-center text-3xl m-4">GitHub tracker</h1>
<input
type="text"
class="rounded px-4 py-2 border border-gray-300 w-full outline-none"
placeholder="Enter the repository's URL"
aria-label="Repository URL"
bind:value={repo}
/>
<button
class="mt-2 border border-transparent bg-blue-500 text-white rounded px-4 py-2 w-full"
>Track repository</button
>
<h2 class="mt-4 text-2xl">Tracked repositories</h2>
<ul class="m-2 list-decimal">
<!-- We'll use a loop to automatically add repositories here later on. -->
<li class="py-1 flex items-center justify-between">
<a class="text-gray-500 hover:underline" href="https://github.com/test/test"
>https://github.com/test/test</a
>
<button class="text-red-500 cursor-pointer" on:click={() => untrack("")}
>Untrack</button
>
</li>
</ul>
</form>
为了测试你的组件,暂时把UsernamePrompt 组件换成App.svelte 中新的Tracker 组件。
<script>
// import UsernamePrompt from "./lib/UsernamePrompt.svelte";
import Tracker from "./lib/Tracker.svelte";
</script>
<!-- <UsernamePrompt /> -->
<Tracker />
你的屏幕现在应该看起来像这样。
注意:记得将App.svelte 恢复到它之前的代码!
第二步:设置云功能
我们需要有一个后端服务器来向我们的应用程序发送推送事件。这意味着你需要创建一个新的(也许是)ExpressJS项目,然后将其单独部署。这对一个刚开始尝试推送通知的人来说都会很头疼。
Vercel云函数来拯救你!云函数就像Express路由。它们可以运行代码,并在你获取其URL时给你一个响应。Vercel支持云函数;你只需要在api 文件夹中创建文件。你将使用云函数与MongoDB进行交互,因为在客户端暴露秘密绝不是一件好事。
首先,确保你在MongoDB Atlas中有一个集群。MongoDB有一个免费计划*(M0*),所以如果你还没有的话,一定要创建一个。现在,进入Atlas仪表板侧边栏的数据库访问选项卡。点击右侧的绿色按钮,添加一个新的数据库用户。输入用户的详细信息(不要忘记密码),然后创建用户。
要连接到数据库,你将需要连接字符串。把新的用户和密码保存在某个地方,然后到你的群集的概览。点击右边的连接按钮,选择连接你的应用程序作为连接方法。你应该看到一个与下面类似的连接字符串。
现在你有了连接字符串,你可以连接到你的数据库,但首先,你需要把当前的应用程序部署到Vercel。最简单的方法是使用GitHub。
创建一个新的GitHub仓库,并将你的代码推送给它。接下来,到你的Vercel仪表板,点击新项目按钮。导入你的GitHub仓库,确保框架是Vite,并添加一个名为MONGODB_URL 的环境变量。将其值设置为MongoDB数据库的连接字符串。
一旦你的网站被部署,你需要将你的本地开发命令从yarn dev 改为vercel dev 。运行该命令后,如果你被要求链接到一个现有的项目,请点击是。
注意:如果你还没有安装Vercel CLI,请确保用npm i -g vercel 。
像我一样,如果你在使用vite 与vercel dev 时遇到问题,请确保在Vercel仪表板中把你项目的开发命令从vite 改为vite --port $PORT 。
这将使我们能够在本地使用具有正确环境变量的云函数。
让我们添加一个辅助文件,使我们能够访问MongoDB而不需要打开太多的连接。创建文件api/_mongo.js ,并将以下代码放入其中。api 目录中的文件,如果前缀为_ ,将不会被视为云函数。这允许我们在单独的文件中添加助手和其他逻辑。
const { MongoClient } = require("mongodb");
const mongo = new MongoClient(process.env.MONGODB_URL);
// Export the connection promise
export default mongo.connect();
导出连接承诺而不是主客户端本身将防止我们有多余的连接,因为我们是在一个无服务器平台上工作。
使用CommonJS而不是ESModules
注意到我是如何使用require ,而不是import ?这是因为,在撰写本文时,Vercel云功能不支持JavaScript文件中的ESMODULEimport 语句。相反,你需要使用CommonJSrequire 语句。
这里有一个问题。如果你看到我们应用程序的package.json ,你会发现它有一行"type": "module" 。这意味着该项目中的每个JavaScript文件都是一个EsModule。这不是我们想要的,所以为了将api 目录中的所有文件标记为CommonJS文件,所以我们可以使用require 语句,创建api/package.json 并在其中添加这一行。
{
"type": "commonjs"
}
现在这将允许我们在api 目录中使用require 语句。用这个命令安装MongoDB连接驱动。
# Don't forget to CD!
cd api
npm i mongodb # or use yarn
第3步:添加功能
追踪器,到现在为止,还没有真正发挥作用,所以让我们来解决这个问题。
认证
对于认证,我们需要将用户输入的用户名存储在MongoDB数据库中。
创建一个文件/api/storeusername.js 。这将是一个云函数,将被映射到http://localhost:3000/api/storeusername 。将下面的代码放入其中。
const mongoPromise = require("../src/lib/mongo");
// All cloud functions must export a function that takes a req and res object.
// These objects are similar to their express counterparts.
module.exports = async (req, res) => {
// TODO
};
接下来,像这样获得MongoDB客户端。
module.exports = async (req, res) =>
// Wait for the client to connect
const mongo = await mongoPromise;
}
从请求的正文中提取username 。
// ...
const { username } = req.body;
// Check if the username is valid
if (typeof username !== "string" || !username.trim()) {
res.status(400).json({ message: "Please send the username" });
return;
}
接下来,你需要在数据库中存储这个用户名。
// Get the collection
const usersCol = mongo.db().collection("users");
// Check if the username already exists in the database
if (await usersCol.findOne({ _id: username })) {
res.status(400).json({ message: "User already exists!" });
return;
}
// We want the username to be the identifier of the user
await usersCol.insertOne({ _id: username });
// Everything went well :)
res.status(200).json({ message: "Username recorded" });
最后,api/storeusername.js 文件应该是这个样子的。
const mongoPromise = require("./_mongo");
module.exports = async (req, res) => {
const mongo = await mongoPromise;
const { username } = req.body;
if (typeof username !== "string" || !username.trim()) {
res.status(400).json({ message: "Please send the username" });
return;
}
// Get the collection
const usersCol = mongo.db().collection("users");
// Check if the username already exists in the database
if (await usersCol.findOne({ _id: username })) {
res.status(400).json({ message: "User already exists!" });
return;
}
// We want the username to be the identifier of the user
await usersCol.insertOne({ _id: username });
// Everything went well :)
res.status(200).json({ message: "Username recorded" });
};
用vercel . ,或通过推送到GitHub,将你的应用程序部署到Vercel,你的无服务器功能就可以上线了你可以用这个命令用cURL来测试它。
curl -X POST -H "Content-Type: application/json" -d '{"username": "test"}' https://your-app.vercel.app/api/storeusername
这应该会在users 集合中创建一个新的文档,其中_id 字段是我们刚刚给出的用户名。
现在剩下的就是在前端获取这个函数了。在src/lib/UsernamePrompt.svelte ,在submit 函数中,首先你需要向云函数发送一个请求,然后把用户名放在localStorage ,这样我们就知道用户是经过认证的。你可以用fetch 函数来发送请求。
async function submit() {
const res = await fetch("/api/storeusername", {
body: JSON.stringify({ username }),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
const data = await res.json();
if (!res.ok) alert(data.message);
else {
// Store the username in localStorage
localStorage.setItem("username", username);
// Reload the page
window.location.reload();
}
}
我们重新加载页面,因为在App.svelte ,当页面被加载时,我们需要检查在localStorage 中是否有一个用户名。如果有,我们可以跳过UsernamePrompt 屏幕。要做到这一点,在App.svelte 的script 标签中添加这段代码。
<script>
import { onMount } from "svelte";
import UsernamePrompt from "./lib/UsernamePrompt.svelte";
import Tracker from "./lib/Tracker.svelte";
let isLoggedIn = false;
onMount(() => {
// If there is a username in the localStorage, set isLoggedIn to true
isLoggedIn = !!localStorage.getItem("username");
});
</script>
上述代码将检查localStorage 中是否有用户名,如果存在,则将isLoggedIn 设为true 。接下来,我们所要做的就是更新DOM。就在App.svelte 的script 标签下,添加这个。
{#if !isLoggedIn}
<UsernamePrompt />
{:else}
<Tracker />
{/if}
追踪和取消追踪存储库
现在,让我们为追踪器的实际追踪功能添加功能。如果你打开Tracker.svelte ,你会发现有两个函数 -track() 和untrack() 。这些函数应该分别跟踪和取消跟踪存储库,将它们添加到数据库中。
但在这之前,你还需要添加一些云函数。一个是跟踪版本库,另一个是取消跟踪,最后一个是获取用户的跟踪版本库。
让我们逐一来研究它们。