🔥 将 NextJS 提升到新的水平:创建 GitHub 星星监视器 🤯

2025-05-28

🔥 将 NextJS 提升到新的水平:创建 GitHub 星星监视器 🤯

在本文中,您将学习如何创建GitHub 星星监视器来检查您几个月来的星星数以及每天获得的星星数。

  • 使用 GitHub API 获取当前每天收到的星星数量。
  • 在屏幕上绘制每天星星的精美图表。
  • 创建一项工作来每天收集新星星。

吉米


您的后台工作平台🔌

Trigger.dev 是一个开源库,可让您使用 NextJS、Remix、Astro 等为您的应用程序创建和监控长时间运行的作业!

 

给我们星星

请帮我们点个星🥹。
这有助于我们创作更多类似的文章💖

为 Trigger.dev 仓库加星标⭐️


这是你需要知道的😻

获取 GitHub 上的星星数量的大部分工作将通过 GitHub API 完成。

GitHub API 有一些限制:

  • 每个请求最多 100 位观星者
  • 最大并发请求数:100
  • 每小时最多 60 个请求

TriggerDev存储库中有超过 5000 颗星,实际上不可能在合理的时间内(实时)统计出所有的星。

因此,我们将使用与GitHub Stars History相同的技巧

  • 获取星号总数(5,715)除以每页100 个结果 = 58 页
  • 设置我们想要的最大请求数量(最多 20 页)除以58 页= 跳过 3 页。
  • 从这些页面中提取星星(2000 颗星),然后提取剩余的星星,我们将按比例添加到其他日期(3715 颗星)。

它会为我们绘制一个漂亮的图表,并在需要的地方添加星星。

当我们每天获取新的星星数量时,事情会变得容易得多。
我们只需用当前星星总数减去 GitHub 上新增的星星数量即可。这样我们就不需要再迭代星星了。


让我们开始吧🔥

我们的应用程序将包含一个页面:

  • 添加您想要监控的存储库。
  • 查看存储库列表及其 GitHub 星图。
  • 删除那些你不再需要的。

星辰流转

💡 我们将使用 NextJS 新应用路由器,请在安装项目之前确保您拥有节点版本 18+。

使用 NextJS 设置新项目

npx create-next-app@latest
Enter fullscreen mode Exit fullscreen mode

我们必须将所有星星保存到我们的数据库中!

对于我们的演示,我们将使用 SQLite Prisma

安装非常简单,但也可以随意使用任何其他数据库。

npm install prisma @prisma/client --save
Enter fullscreen mode Exit fullscreen mode

在我们的项目中安装 Prisma

npx prisma init --datasource-provider sqlite
Enter fullscreen mode Exit fullscreen mode

转至prisma/schema.prisma并将其替换为以下模式:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Repository {
    id        String   @id @default(uuid())
    month     Int
    year      Int
    day       Int
    name      String
    stars     Int
    @@unique([name, day, month, year])
}
Enter fullscreen mode Exit fullscreen mode

然后运行

npx prisma db push
Enter fullscreen mode Exit fullscreen mode

我们基本上在 SQLite 数据库中创建了一个名为的新表Repository

  • month,,是日期yearday
  • name存储库的名称
  • stars以及该特定日期的星星数量。

您还可以看到我们@@unique在底部添加了一个,这意味着我们可以同时拥有namemonthyear和的重复记录day。这将引发错误。

让我们添加我们的 Prisma 客户端。

创建一个名为的新文件夹helper并添加一个名为的新文件prisma.ts,并在其中添加以下代码:

import {PrismaClient} from '@prisma/client';

export const prisma = new PrismaClient();
Enter fullscreen mode Exit fullscreen mode

我们稍后可以使用该prisma变量来查询我们的数据库。


应用程序 UI 骨架💀

我们需要一些库来完成本教程:

  • Axios - 向服务器发送请求(如果您觉得更方便,也可以使用 fetch)
  • Dayjs -处理日期的优秀库。它是 moment.js 的替代品,但已不再完全维护。
  • Lodash——用于玩数据结构的酷库。
  • react-hook-form -处理表单(验证/值/等)的最佳库
  • chart.js - 我选择绘制我们的 GitHub 星图的库。

让我们安装它们:

npm install axios dayjs lodash @types/lodash chart.js react-hook-form react-chartjs-2 --save
Enter fullscreen mode Exit fullscreen mode

创建一个名为的新文件夹components并添加一个名为main.tsx

添加以下代码:

"use client";
import {useForm} from "react-hook-form";
import axios from "axios";
import {Repository} from "@prisma/client";
import {useCallback, useState} from "react";

export default function Main() {
    const [repositoryState, setRepositoryState] = useState([]);
    const {register, handleSubmit} = useForm();

    const submit = useCallback(async (data: any) => {
        const {data: repositoryResponse} = await axios.post('/api/repository', {todo: 'add', repository: data.name});
        setRepositoryState([...repositoryState, ...repositoryResponse]);
    }, [repositoryState])

    const deleteFromList = useCallback((val: List) => () => {
        axios.post('/api/repository', {todo: 'delete', repository: `https://github.com/${val.name}`});
        setRepositoryState(repositoryState.filter(v => v.name !== val.name));
    }, [repositoryState])

    return (
        <div className="w-full max-w-2xl mx-auto p-6 space-y-12">
            <form className="flex items-center space-x-4" onSubmit={handleSubmit(submit)}>
                <input className="flex-grow p-3 border border-black/20 rounded-xl" placeholder="Add Git repository" type="text" {...register('name', {required: 'true'})} />
                <button className="flex-shrink p-3 border border-black/20 rounded-xl" type="submit">
                    Add
                </button>
            </form>
            <div className="divide-y-2 divide-gray-300">
                {repositoryState.map(val => (
                    <div key={val.name} className="space-y-4">
                        <div className="flex justify-between items-center py-10">
                            <h2 className="text-xl font-bold">{val.name}</h2>
                            <button className="p-3 border border-black/20 rounded-xl bg-red-400" onClick={deleteFromList(val)}>Delete</button>
                        </div>
                        <div className="bg-white rounded-lg border p-10">
                            <div className="h-[300px]]">
                                {/* Charts Component */}
                            </div>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

超级简单的 React 组件

  • 允许我们添加新的 GitHub 库并将其发送到服务器 POST 的表单 -/api/repository {todo: 'add'}
  • 删除我们不想要的存储库 POST -/api/repository {todo: 'delete'}
  • 所有已添加库及其图表的列表。

让我们转到文章的复杂部分,添加新的存储库。


数星星

数星星

在里面helper创建一个名为的新文件all.stars.ts并添加以下代码:

import axios from "axios";
import dayjs from "dayjs";
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);

const requestAmount = 20;

export const getAllGithubStars = async (owner: string, name: string) => {
    // Get the amount of stars from GitHub
    const totalStars = (await axios.get(`https://api.github.com/repos/${owner}/${name}`)).data.stargazers_count;

    // get total pages
    const totalPages = Math.ceil(totalStars / 100);

    // How many pages to skip? We don't want to spam requests
    const pageSkips = totalPages < requestAmount ? requestAmount : Math.ceil(totalPages / requestAmount);

    // Send all the requests at the same time
    const starsDates = (await Promise.all([...new Array(requestAmount)].map(async (_, index) => {
        const getPage = (index * pageSkips) || 1;
        return (await axios.get(`https://api.github.com/repos/${owner}/${name}/stargazers?per_page=100&page=${getPage}`, {
            headers: {
                Accept: "application/vnd.github.v3.star+json",
            },
        })).data;
    }))).flatMap(p => p).reduce((acc: any, stars: any) => {
        const yearMonth = stars.starred_at.split('T')[0];
        acc[yearMonth] = (acc[yearMonth] || 0) + 1;
        return acc;
    }, {});

    // how many stars did we find from a total of `requestAmount` requests?
    const foundStars = Object.keys(starsDates).reduce((all, current) => all + starsDates[current], 0);

    // Find the earliest date
    const lowestMonthYear = Object.keys(starsDates).reduce((lowest, current) => {
        if (lowest.isAfter(dayjs.utc(current.split('T')[0]))) {
            return dayjs.utc(current.split('T')[0]);
        }
        return lowest;
    }, dayjs.utc());

    // Count dates until today
    const splitDate = dayjs.utc().diff(lowestMonthYear, 'day') + 1;

    // Create an array with the amount of stars we didn't find
    const array = [...new Array(totalStars - foundStars)];

    // Set the amount of value to add proportionally for each day
    let splitStars: any[][] = [];
    for (let i = splitDate; i > 0; i--) {
        splitStars.push(array.splice(0, Math.ceil(array.length / i)));
    }

    // Calculate the amount of stars for each day
    return [...new Array(splitDate)].map((_, index, arr) => {
        const yearMonthDay = lowestMonthYear.add(index, 'day').format('YYYY-MM-DD');
        const value = starsDates[yearMonthDay] || 0;
        return {
            stars: value + splitStars[index].length,
            date: {
                month: +dayjs.utc(yearMonthDay).format('M'),
                year: +dayjs.utc(yearMonthDay).format('YYYY'),
                day: +dayjs.utc(yearMonthDay).format('D'),
            }
        };
    });
}
Enter fullscreen mode Exit fullscreen mode

那么这里发生了什么:

  • totalStars- 我们计算图书馆拥有的星星总数。
  • totalPages- 我们计算页数(每页100条记录)
  • pageSkips- 由于我们最多需要 20 个请求,因此我们每次都会检查必须跳过多少页面。
  • starsDates- 我们为每个日期填充星星的数量。
  • foundStars- 由于我们跳过了日期,我们需要计算实际发现的星星总数。
  • lowestMonthYear- 寻找我们拥有的恒星的最早日期。
  • splitDate- 最早的日期和今天之间有多少个日期?
  • arraysplitDate- 具有一定数量项目的空数组。
  • splitStars- 我们缺少星星的数量,需要按比例添加每个日期。
  • 最终回报——自开始以来每天的星星数量的新数组。

因此,我们成功创建了一个可以每天给我们星星的功能。

我试过像这样显示,结果很乱。
你可能想显示每个月的星星数量。
此外,你可能想累计星星,而不是:

  • 二月 - 300 颗星
  • 三月 - 200 颗星
  • 四月 - 400 颗星

如果像这样就更好了:

  • 二月 - 300 颗星
  • 三月 - 500颗星
  • 四月 - 900 颗星

两种选择都有效。取决于你想展示什么!

因此,让我们转到我们的帮助文件夹并创建一个名为的新文件get.list.ts

以下是该文件的内容:

import {prisma} from "./prisma";
import {groupBy, sortBy} from "lodash";
import {Repository} from "@prisma/client";

function fixStars (arr: any[]): Array<{name: string, stars: number, month: number, year: number}> {
    return arr.map((current, index) => {
        return {
            ...current,
            stars: current.stars + arr.slice(index + 1, arr.length).reduce((acc, current) => acc + current.stars, 0),
        }
    }).reverse();
}

export const getList = async (data?: Repository[]) => {
    const repo = data || await prisma.repository.findMany();
    const uniqMonth = Object.values(
        groupBy(
            sortBy(
                Object.values(
                    groupBy(repo, (p) => p.name + '-' + p.year + '-' + p.month))
                    .map(current => {
                        const stars = current.reduce((acc, current) => acc + current.stars, 0);
                        return {
                            name: current[0].name,
                            stars,
                            month: current[0].month,
                            year: current[0].year
                        }
                    }),
            [(p: any) => -p.year, (p: any) => -p.month]
        ),p => p.name)
    );

    const fixMonthDesc = uniqMonth.map(p => fixStars(p));

    return fixMonthDesc.map(p => ({
        name: p[0].name,
        list: p
    }));
}
Enter fullscreen mode Exit fullscreen mode

首先,它将所有按天列出的星星转换为按月列出的星星。

稍后我们将累计每个月的星星数量。

这里要注意的一点是它data?: Repository[]是可选的。

我们制定了一个简单的逻辑:如果我们不传递数据,它将对我们数据库中的所有存储库执行此操作。

如果我们传递数据,它将只对其起作用。

你问为什么?

  • 当我们创建一个新的存储库时,我们需要将其添加到数据库后对特定的存储库数据进行处理。
  • 当我们重新加载页面时,我们需要获取所有数据的数据。

现在,让我们开始创建/删除星星的路线。

前往src/app/api并创建一个名为 的新文件夹repository。在该文件夹中,创建一个名为 的新文件route.tsx

在那里添加以下代码:

import {getAllGithubStars} from "../../../../helper/all.stars";
import {prisma} from "../../../../helper/prisma";
import {Repository} from "@prisma/client";
import {getList} from "../../../../helper/get.list";

export async function POST(request: Request) {
    const body = await request.json();
    if (!body.repository) {
        return new Response(JSON.stringify({error: 'Repository is required'}), {status: 400});
    }

    const {owner, name} = body.repository.match(/github.com\/(?<owner>.*)\/(?<name>.*)/).groups;
    if (!owner || !name) {
        return new Response(JSON.stringify({error: 'Repository is invalid'}), {status: 400});
    }

    if (body.todo === 'delete') {
        await prisma.repository.deleteMany({
            where: {
                name: `${owner}/${name}`
            }
        });

        return new Response(JSON.stringify({deleted: true}), {status: 200});
    }

    const starsMonth = await getAllGithubStars(owner, name);
    const repo: Repository[] = [];
    for (const stars of starsMonth) {
        repo.push(
            await prisma.repository.upsert({
                where: {
                    name_day_month_year: {
                        name: `${owner}/${name}`,
                        month: stars.date.month,
                        year: stars.date.year,
                        day: stars.date.day,
                    },
                },
                update: {
                    stars: stars.stars,
                },
                create: {
                    name: `${owner}/${name}`,
                    month: stars.date.month,
                    year: stars.date.year,
                    day: stars.date.day,
                    stars: stars.stars,
                }
            })
        );
    }
    return new Response(JSON.stringify(await getList(repo)), {status: 200});
}
Enter fullscreen mode Exit fullscreen mode

我们共享了 DELETE 和 CREATE 路线,这些路线通常不应在生产用途中使用,但我们已在本文中这样做以使您更容易使用。

我们从请求中获取 JSON,检查“repository”字段是否存在,以及它是否是 GitHub 存储库的有效路径。

如果是删除请求,我们用prisma存储库的名称从数据库中删除存储库并返回请求。

如果是创建,我们用它getAllGithubStars来获取数据并保存到我们的数据库中。

💡 由于我们已经在 上放置了唯一索引namemonth因此如果记录已经存在,我们可以使用它来year更新day数据prisma upsert

最后,我们将新积累的数据返回给客户端。

最困难的部分完成了🍾


主页人口💽

我们尚未创建主页面组件。

我们开始做吧。

转到app文件夹创建或编辑page.tsx并添加以下代码:

"use server";

import Main from "@/components/main";
import {getList} from "../../helper/get.list";

export default async function Home() {
  const list: any[] = await getList();
  return (
      <Main list={list} />
  )
}
Enter fullscreen mode Exit fullscreen mode

我们使用相同的函数getList来获取所有存储库累积的所有数据。

我们还修改主要组件以支持它。

编辑components/main.tsx并替换为:

"use client";
import {useForm} from "react-hook-form";
import axios from "axios";
import {Repository} from "@prisma/client";
import {useCallback, useState} from "react";

interface List {
    name: string,
    list: Repository[]
}

export default function Main({list}: {list: List[]}) {
    const [repositoryState, setRepositoryState] = useState(list);
    const {register, handleSubmit} = useForm();

    const submit = useCallback(async (data: any) => {
        const {data: repositoryResponse} = await axios.post('/api/repository', {todo: 'add', repository: data.name});
        setRepositoryState([...repositoryState, ...repositoryResponse]);
    }, [repositoryState])

    const deleteFromList = useCallback((val: List) => () => {
        axios.post('/api/repository', {todo: 'delete', repository: `https://github.com/${val.name}`});
        setRepositoryState(repositoryState.filter(v => v.name !== val.name));
    }, [repositoryState])

    return (
        <div className="w-full max-w-2xl mx-auto p-6 space-y-12">
            <form className="flex items-center space-x-4" onSubmit={handleSubmit(submit)}>
                <input className="flex-grow p-3 border border-black/20 rounded-xl" placeholder="Add Git repository" type="text" {...register('name', {required: 'true'})} />
                <button className="flex-shrink p-3 border border-black/20 rounded-xl" type="submit">
                    Add
                </button>
            </form>
            <div className="divide-y-2 divide-gray-300">
                {repositoryState.map(val => (
                    <div key={val.name} className="space-y-4">
                        <div className="flex justify-between items-center py-10">
                            <h2 className="text-xl font-bold">{val.name}</h2>
                            <button className="p-3 border border-black/20 rounded-xl bg-red-400" onClick={deleteFromList(val)}>Delete</button>
                        </div>
                        <div className="bg-white rounded-lg border p-10">
                            <div className="h-[300px]]">
                                {/* Charts Components */}
                            </div>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

显示图表!📈

转到components文件夹并添加一个名为 的新文件chart.tsx

添加以下代码:

"use client";
import {Repository} from "@prisma/client";
import {useMemo} from "react";
import React from 'react';
import {
    Chart as ChartJS,
    CategoryScale,
    LinearScale,
    PointElement,
    LineElement,
    Title,
    Tooltip,
    Legend,
} from 'chart.js';
import { Line } from 'react-chartjs-2';

ChartJS.register(
    CategoryScale,
    LinearScale,
    PointElement,
    LineElement,
    Title,
    Tooltip,
    Legend
);

export default function ChartComponent({repository}: {repository: Repository[]}) {
    const labels = useMemo(() => {
        return repository.map(r => `${r.year}/${r.month}`);
    }, [repository]);

    const data = useMemo(() => ({
        labels,
        datasets: [
            {
                label: repository[0].name,
                data: repository.map(p => p.stars),
                borderColor: 'rgb(255, 99, 132)',
                backgroundColor: 'rgba(255, 99, 132, 0.5)',
                tension: 0.2,
            },
        ],
    }), [repository]);

    return (
        <Line options={{
            responsive: true,
        }} data={data} />
    );
}
Enter fullscreen mode Exit fullscreen mode

我们使用该chart.js库来绘制一种Line图形。

这非常简单,因为我们在服务器端完成了所有数据结构。

这里需要注意的一点是,我们export default的 ChartComponent 组件。这是因为它使用了Canvas。服务器端不可用,我们需要延迟加载此组件。

让我们修改一下main.tsx

"use client";
import {useForm} from "react-hook-form";
import axios from "axios";
import {Repository} from "@prisma/client";
import dynamic from "next/dynamic";
import {useCallback, useState} from "react";
const ChartComponent = dynamic(() => import('@/components/chart'), { ssr: false, })

interface List {
    name: string,
    list: Repository[]
}

export default function Main({list}: {list: List[]}) {
    const [repositoryState, setRepositoryState] = useState(list);
    const {register, handleSubmit} = useForm();

    const submit = useCallback(async (data: any) => {
        const {data: repositoryResponse} = await axios.post('/api/repository', {todo: 'add', repository: data.name});
        setRepositoryState([...repositoryState, ...repositoryResponse]);
    }, [repositoryState])

    const deleteFromList = useCallback((val: List) => () => {
        axios.post('/api/repository', {todo: 'delete', repository: `https://github.com/${val.name}`});
        setRepositoryState(repositoryState.filter(v => v.name !== val.name));
    }, [repositoryState])

    return (
        <div className="w-full max-w-2xl mx-auto p-6 space-y-12">
            <form className="flex items-center space-x-4" onSubmit={handleSubmit(submit)}>
                <input className="flex-grow p-3 border border-black/20 rounded-xl" placeholder="Add Git repository" type="text" {...register('name', {required: 'true'})} />
                <button className="flex-shrink p-3 border border-black/20 rounded-xl" type="submit">
                    Add
                </button>
            </form>
            <div className="divide-y-2 divide-gray-300">
                {repositoryState.map(val => (
                    <div key={val.name} className="space-y-4">
                        <div className="flex justify-between items-center py-10">
                            <h2 className="text-xl font-bold">{val.name}</h2>
                            <button className="p-3 border border-black/20 rounded-xl bg-red-400" onClick={deleteFromList(val)}>Delete</button>
                        </div>
                        <div className="bg-white rounded-lg border p-10">
                            <div className="h-[300px]]">
                                <ChartComponent repository={val.list} />
                            </div>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

您可以看到我们使用了nextjs/dynamic延迟加载组件。

我希望将来 NextJS 能"use lazy-load"为客户端组件添加类似的功能😺


那么新星呢?来认识一下 Trigger.Dev!

每天添加新星星的最佳方式是运行 cron 请求来检查新添加的星星并将其添加到我们的数据库中。

而不是使用 Vercel cron / GitHub 操作,或者,上帝保佑,为此创建一个新的服务器。

我们可以使用Trigger.DEV直接与我们的 NextJS 应用程序协同工作。

那么让我们开始设置吧!

注册一个 Trigger.dev 帐户

注册后,创建一个组织并为您的工作选择一个项目名称。

新组织

选择 Next.js 作为您的框架,并按照将 Trigger.dev 添加到现有 Next.js 项目的过程进行操作。

NextJS

否则,请单击 Environments & API Keys 项目仪表板的侧边栏菜单。

开发密钥

复制您的 DEV 服务器 API 密钥并运行下面的代码片段来安装 Trigger.dev。

请仔细遵循说明。

npx @trigger.dev/cli@latest init
Enter fullscreen mode Exit fullscreen mode

在另一个终端中运行以下代码片段,以在 Trigger.dev 和 Next.js 项目之间建立隧道。

npx @trigger.dev/cli@latest dev
Enter fullscreen mode Exit fullscreen mode

让我们创建我们的 TriggerDev 作业!

您将看到一个名为的新文件夹jobs
在那里创建一个名为sync.stars.ts

添加以下代码:

import { cronTrigger, invokeTrigger } from "@trigger.dev/sdk";
import { client } from "@/trigger";
import { prisma } from "../../helper/prisma";
import axios from "axios";
import { z } from "zod";

// Your first job
// This Job will be triggered by an event, log a joke to the console, and then wait 5 seconds before logging the punchline.
client.defineJob({
  id: "sync-stars",
  name: "Sync Stars Daily",
  version: "0.0.1",
  // Run a cron every day at 23:00 AM
  trigger: cronTrigger({
    cron: "0 23 * * *",
  }),
  run: async (payload, io, ctx) => {
    const repos = await io.runTask("get-stars", async () => {
      // get all libraries and current amount of stars
      return await prisma.repository.groupBy({
        by: ["name"],
        _sum: {
          stars: true,
        },
      });
    });

    //loop through all repos and invoke the Job that gets the latest stars
    for (const repo of repos) {
      getStars.invoke(repo.name, {
        name: repo.name,
        previousStarCount: repo?._sum?.stars || 0,
      });
    }
  },
});

const getStars = client.defineJob({
  id: "get-latest-stars",
  name: "Get latest stars",
  version: "0.0.1",
  // Run a cron every day at 23:00 AM
  trigger: invokeTrigger({
    schema: z.object({
      name: z.string(),
      previousStarCount: z.number(),
    }),
  }),
  run: async (payload, io, ctx) => {
    const stargazers_count = await io.runTask("get-stars", async () => {
      const { data } = await axios.get(
          `https://api.github.com/repos/${payload.name}`,
          {
            headers: {
              authorization: `token ${process.env.TOKEN}`,
            },
          }
      );
      return data.stargazers_count as number;
    });

    await prisma.repository.upsert({
      where: {
        name_day_month_year: {
          name: payload.name,
          month: new Date().getMonth() + 1,
          year: new Date().getFullYear(),
          day: new Date().getDate(),
        },
      },
      update: {
          stars: stargazers_count - payload.previousStarCount,
      },
      create: {
        name: payload.name,
        stars: stargazers_count - payload.previousStarCount,
        month: new Date().getMonth() + 1,
        year: new Date().getFullYear(),
        day: new Date().getDate(),
      },
    });
  },
});
Enter fullscreen mode Exit fullscreen mode

我们创建了一项名为“Sync Stars Daily”的新任务,该任务将在每天 23:00 运行 - 它在 cron 文本中的表示为:0 23 * * *

我们将所有当前的存储库放入数据库中,按名称对它们进行分组,然后对星级进行求和。

由于一切都在 Vercel 无服务器上运行,我们可能会在检查所有存储库时超时。

为此,我们将每个存储库发送到不同的作业。

我们使用它invoke来创建新的工作,然后在内部处理它们Get latest stars

我们遍历所有新的存储库并获取当前的星号。

我们用新的星星数量减去旧的星星数量,就得到了今天的星星数量。

我们使用 将其添加到数据库中prisma。没有比这更简单的了!

最后一件事是编辑jobs/index.ts并替换以下内容:

export * from "./sync.stars";
Enter fullscreen mode Exit fullscreen mode

你就完成了🥳


让我们联系吧!🔌

作为开源开发者,我们诚邀您加入我们的 社区 ,贡献力量并与维护人员互动。欢迎访问我们的 GitHub 代码库 ,贡献代码并创建与 Trigger.dev 相关的问题。

本教程的源代码可以在这里找到:

https://github.com/triggerdotdev/blog/tree/main/stars-monitor

感谢您的阅读!

文章来源:https://dev.to/triggerdotdev/take-nextjs-to-the-next-level-create-a-github-stars-monitor-130a
PREV
🚀 发送新星通知的 4 大方法⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️ TL;DR
NEXT
🤯 在 X (Twitter) 上获得知名度:使用 NextJS 安排你的帖子 🚀🚀 TL;DR