Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/(dashboard)/details/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export default function DetailsPage() {
2. [Admin: Check Default Follow-up rule](/rules/default)
`}</Markdown>

<UseSWRComponent props={{ skip, limit }} Component={DetailsTable} refreshInterval={60e3} fallbackData={<DetailsTable {...{ skip, limit }} />}>
{<DetailsTable {...{ skip, limit }} />}
<UseSWRComponent props={{ skip, limit }} Component={DetailsTable} refreshInterval={60e3}>
<div>Loading...</div>
</UseSWRComponent>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion app/(dashboard)/followup/actions/send-gmail/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default async function GmailPage() {
console.log(error);

const emails = await GCloudOAuth2Credentials.find({ credentials: { $exists: true } })
.map(({ email, scopes }) => ({ email, scopes }))
.map(({ email, scopes }: { email: string; scopes: string[] }) => ({ email, scopes }))
.toArray();
return (
<div className="card-body">
Expand Down
4 changes: 4 additions & 0 deletions app/(dashboard)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import Link from "next/link";
import { Suspense } from "react";
import DetailsTable from "./DetailsTable";
import TotalsPage from "./totals/page";

// Force dynamic rendering to avoid build-time database access
export const dynamic = "force-dynamic";
export const revalidate = 60; // seconds

export default async function DashboardPage() {
return (
<main className="flex flex-wrap">
Expand Down
5 changes: 4 additions & 1 deletion app/(dashboard)/repos/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { CNRepos } from "@/src/CNRepos";
import { Suspense } from "react";
import yaml from "yaml";

// Force dynamic rendering to avoid build-time database access
export const dynamic = "force-dynamic";

/**
*
* @author: snomiao <[email protected]>
Expand Down Expand Up @@ -44,7 +47,7 @@ async function DataPage({ page = 0, size = 10000 }) {
if (!data.length) return null;
return (
<>
{data.map((item) => (
{data.map((item: any) => (
<div key={item.repository}>
<a href={item.repository} target="_blank" rel="noreferrer" title={yaml.stringify(item)}>
<noscript>{JSON.stringify(item)}</noscript>
Expand Down
4 changes: 2 additions & 2 deletions app/(dashboard)/rules/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ export default async function RulesList() {
<h2>Follow-up RuleSets</h2>
<div className="card">
<ul>
{followRuleSets.map((e) => {
{followRuleSets.map((e: any) => {
return (
<li key={e.name} className="card-body">
[{e.enabled ? "ENABLED" : "DISABLED"}]
{`${e.name}: (${
e.rules?.length ?? "FAIL to parse"
} rules, matched ${TaskDataOrNull(e.matched)?.length} prs, performed ${e.action_results?.map((r) => TaskDataOrNull(r.result).length).join("/") ?? "NO"} actions)`}
} rules, matched ${(TaskDataOrNull(e.matched) as any)?.length} prs, performed ${e.action_results?.map((r: any) => (TaskDataOrNull(r.result) as any).length).join("/") ?? "NO"} actions)`}
<Link className="btn" href={`/rules/${e.name}`}>
[Edit]
</Link>
Expand Down
6 changes: 5 additions & 1 deletion app/(dashboard)/totals/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import UseSWRComponent from "use-swr-component";
import { TotalsBlock } from "../TotalsBlock";

// Force dynamic rendering to avoid build-time database access
export const dynamic = "force-dynamic";

/**
* @author: snomiao <[email protected]>
*/
export default function TotalsPage() {
return (
<UseSWRComponent props={{}} Component={TotalsBlock} refreshInterval={1e3}>
{<TotalsBlock />}
<div>Loading...</div>
</UseSWRComponent>
);
}
40 changes: 24 additions & 16 deletions app/api/router.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import pkg from "@/package.json";
import { CNRepos } from "@/src/CNRepos";
import { getWorkerInstance } from "@/src/WorkerInstances";
import { analyzePullsStatus } from "@/src/analyzePullsStatus";
import DIE from "@snomiao/die";
import { initTRPC } from "@trpc/server";
import sflow from "sflow";
import type { OpenApiMeta } from "trpc-to-openapi";
import z from "zod/v3";
import { GithubDesignTaskMeta } from "../tasks/gh-design/gh-design";
import { GithubContributorAnalyzeTask } from "../tasks/github-contributor-analyze/GithubContributorAnalyzeTask";

export const t = initTRPC.meta<OpenApiMeta>().create(); /* 👈 */
export const router = t.router({
Expand Down Expand Up @@ -36,7 +30,10 @@ export const router = t.router({
.meta({ openapi: { method: "GET", path: "/worker", description: "Get current worker" } })
.input(z.object({}))
.output(z.any())
.query(async () => await getWorkerInstance()),
.query(async () => {
const { getWorkerInstance } = await import("@/src/WorkerInstances");
return await getWorkerInstance();
}),
analyzePullsStatus: t.procedure
.meta({ openapi: { method: "GET", path: "/analyze-pulls-status", description: "Get current worker" } })
.input(z.object({ skip: z.number(), limit: z.number() }).partial())
Expand All @@ -56,18 +53,22 @@ export const router = t.router({
author_email: z.string().optional(),
}),
)
.query(async ({ input: { limit = 0, skip = 0 } }) => (await analyzePullsStatus({ limit, skip })) as any),
.query(async ({ input: { limit = 0, skip = 0 } }) => {
const { analyzePullsStatus } = await import("@/src/analyzePullsStatus");
return (await analyzePullsStatus({ limit, skip })) as any;
}),
getRepoUrls: t.procedure
.meta({ openapi: { method: "GET", path: "/repo-urls", description: "Get repo urls" } })
.input(z.object({}))
.output(z.array(z.string()))
.query(
async () =>
await sflow(CNRepos.find({}, { projection: { repository: 1 } }))
.map((e) => (e as unknown as { repository: string }).repository)
.filter((repo) => typeof repo === "string" && repo.length > 0)
.toArray(),
),
.query(async () => {
const sflow = (await import("sflow")).default;
const { CNRepos } = await import("@/src/CNRepos");
return await sflow(CNRepos.find({}, { projection: { repository: 1 } }))
.map((e) => (e as unknown as { repository: string }).repository)
.filter((repo) => typeof repo === "string" && repo.length > 0)
.toArray();
}),
GithubContributorAnalyzeTask: t.procedure
.meta({
openapi: {
Expand Down Expand Up @@ -96,7 +97,12 @@ export const router = t.router({
}),
),
)
.query(async () => await GithubContributorAnalyzeTask.find({}).toArray()),
.query(async () => {
const { GithubContributorAnalyzeTask } = await import(
"../tasks/github-contributor-analyze/GithubContributorAnalyzeTask"
);
return await GithubContributorAnalyzeTask.find({}).toArray();
}),

githubContributorAnalyze: t.procedure
.meta({
Expand Down Expand Up @@ -149,6 +155,7 @@ export const router = t.router({
)
.query(async () => {
try {
const { GithubDesignTaskMeta } = await import("../tasks/gh-design/gh-design");
const meta = await GithubDesignTaskMeta.findOne({ coll: "GithubDesignTask" });
return { meta };
} catch (error) {
Expand Down Expand Up @@ -214,6 +221,7 @@ export const router = t.router({
throw new Error("Meta editing functionality is temporarily disabled. This feature is under maintenance.");
// TODO: add back later
try {
const { GithubDesignTaskMeta } = await import("../tasks/gh-design/gh-design");
const updateData: any = {};
if (input.slackMessageTemplate !== undefined) updateData.slackMessageTemplate = input.slackMessageTemplate;
if (input.requestReviewers !== undefined) updateData.requestReviewers = input.requestReviewers;
Expand Down
11 changes: 10 additions & 1 deletion app/tasks/gh-design/gh-design.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,19 @@ type GithubDesignTask = {
const COLLECTION_NAME = "GithubDesignTask";
export const GithubDesignTaskMeta = TaskMetaCollection(COLLECTION_NAME, githubDesignTaskMetaSchema);
export const GithubDesignTask = db.collection<GithubDesignTask>(COLLECTION_NAME);
await GithubDesignTask.createIndex({ url: 1 }, { unique: true }); // ensure url is unique

// Lazy index creation to avoid build-time execution
let _indexCreated = false;
async function ensureIndexes() {
if (!_indexCreated) {
await GithubDesignTask.createIndex({ url: 1 }, { unique: true }); // ensure url is unique
_indexCreated = true;
}
}

// Helper function to save/update GithubDesignTask
async function saveGithubDesignTask(url: string, $set: Partial<GithubDesignTask>) {
await ensureIndexes();
return (
(await GithubDesignTask.findOneAndUpdate({ url }, { $set }, { upsert: true, returnDocument: "after" })) ||
DIE("NEVER")
Expand Down
74 changes: 38 additions & 36 deletions app/tasks/gh-design/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { Badge } from "@/components/ui/badge";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import Link from "next/link";
import { GithubDesignTask } from "./gh-design";
import { GithubDesignTaskMetaEditor } from "./GithubDesignTaskMetaEditor";

// Force dynamic rendering to avoid build-time database access
export const dynamic = "force-dynamic";

/**
* GitHub Design Task Dashboard
* Displays all design-labeled issues and PRs being tracked
*/
export default async function GithubDesignTaskPage() {
// Dynamically import to ensure indexes are created at runtime
const { GithubDesignTask } = await import("./gh-design");

// Fetch all tasks from the database
const tasks = await GithubDesignTask.find({}).sort({ lastCheckedAt: -1 }).toArray();


const formatDate = (date: Date | string | undefined) => {
if (!date) return "N/A";
try {
Expand Down Expand Up @@ -64,8 +60,7 @@ export default async function GithubDesignTaskPage() {
<TableCaption>
{tasks.length === 0
? "No design tasks found"
: `A list of ${tasks.length} design task${tasks.length !== 1 ? 's' : ''} being tracked`
}
: `A list of ${tasks.length} design task${tasks.length !== 1 ? "s" : ""} being tracked`}
</TableCaption>
<TableHeader>
<TableRow>
Expand All @@ -91,8 +86,12 @@ export default async function GithubDesignTaskPage() {
tasks.map((task) => (
<TableRow key={task.url}>
<TableCell>
<Link className="text-sm text-muted-foreground outline outline-[1px] rounded-full px-2 py-1 whitespace-pre" href={`https://github.com/${task.user}`}
target="_blank" rel="noopener noreferrer">
<Link
className="text-sm text-muted-foreground outline outline-[1px] rounded-full px-2 py-1 whitespace-pre"
href={`https://github.com/${task.user}`}
target="_blank"
rel="noopener noreferrer"
>
@{task.user}
</Link>
</TableCell>
Expand All @@ -105,19 +104,15 @@ export default async function GithubDesignTaskPage() {
>
<div className="flex items-center gap-2">
<Badge className="w-16 text-center justify-center">
{({ "pull_request": "PR", 'issue': "Issue" })[task.type] || "Task"}
{{ pull_request: "PR", issue: "Issue" }[task.type] || "Task"}
</Badge>
<span className="text-sm text-muted-foreground">
{getIssueNumber(task.url)}
</span>
<h3>
{task.title}
</h3>
<span className="text-sm text-muted-foreground">{getIssueNumber(task.url)}</span>
<h3>{task.title}</h3>
</div>
</Link>
</TableCell>
<TableCell className="text-center">
<Badge variant={'outline'}>{task.state?.toUpperCase() || '?'}</Badge>
<Badge variant={"outline"}>{task.state?.toUpperCase() || "?"}</Badge>
</TableCell>
<TableCell className="text-center text-sm text-muted-foreground">
{formatDate(task.stateAt)}
Expand All @@ -126,28 +121,34 @@ export default async function GithubDesignTaskPage() {
{task.labels && task.labels.length > 0 ? (
<div className="flex flex-wrap gap-1">
{task.labels.map((label, index) => (
<Badge key={index} variant="secondary" className="text-xs" style={{ backgroundColor: `#${label.color}` }}>
<Badge
key={index}
variant="secondary"
className="text-xs"
style={{ backgroundColor: `#${label.color}` }}
>
{label.name}
</Badge>
))}
</div>
) : (
null
)}</TableCell>
) : null}
</TableCell>
<TableCell className="text-center">

{task.reviewers && task.reviewers.length > 0 ? (
<div className=" flex flex-wrap gap-1">
{task.reviewers.map((reviewer, index) => (
<Link key={index} className="text-sm text-muted-foreground outline outline-[1px] rounded-full px-2 py-1" href={`https://github.com/${reviewer}`}
target="_blank" rel="noopener noreferrer">
<Link
key={index}
className="text-sm text-muted-foreground outline outline-[1px] rounded-full px-2 py-1"
href={`https://github.com/${reviewer}`}
target="_blank"
rel="noopener noreferrer"
>
@{reviewer}
</Link>
))}
</div>
) : (
null
)}
) : null}
</TableCell>
<TableCell className="text-center">
{task.slackUrl ? (
Expand All @@ -161,7 +162,6 @@ export default async function GithubDesignTaskPage() {
</Link>
) : null}
</TableCell>

</TableRow>
))
)}
Expand All @@ -171,9 +171,11 @@ export default async function GithubDesignTaskPage() {

<div className="mt-6 text-sm text-muted-foreground">
<p>
{('This table shows all GitHub issues and pull requests with the "Design" label that have been processed by the automated tracking system. The system monitors repositories, sends Slack notifications, and requests reviews for design-related items.')}
{
'This table shows all GitHub issues and pull requests with the "Design" label that have been processed by the automated tracking system. The system monitors repositories, sends Slack notifications, and requests reviews for design-related items.'
}
</p>
</div>
</div>
);
}
}
41 changes: 23 additions & 18 deletions app/tasks/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import { GithubActionUpdateTask } from "@/src/GithubActionUpdateTask/GithubActionUpdateTask";
import Link from "next/link";
import { Suspense } from "react";
import { GithubBugcopTask } from "../../run/gh-bugcop/gh-bugcop";
import { GithubBountyTask } from "./gh-bounty/gh-bounty";
import { GithubDesignTask } from "./gh-design/gh-design";
import {
GithubContributorAnalyzeTask,
GithubContributorAnalyzeTaskFilter,
} from "./github-contributor-analyze/GithubContributorAnalyzeTask";
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

const Counts = {
GithubActionUpdateTask: () => <Suspense>{GithubActionUpdateTask.estimatedDocumentCount()}</Suspense>,
GithubContributorAnalyzeTask: () => <Suspense>{GithubContributorAnalyzeTask.estimatedDocumentCount()}</Suspense>,
GithubContributorAnalyzeTaskRemain: () => (
<Suspense>{GithubContributorAnalyzeTask.countDocuments(GithubContributorAnalyzeTaskFilter)}</Suspense>
),
GithubBountyTask: () => <Suspense>{GithubBountyTask.estimatedDocumentCount()}</Suspense>,
GithubDesignTask: () => <Suspense>{GithubDesignTask.estimatedDocumentCount()}</Suspense>,
};
// Force dynamic rendering to avoid build-time database access
export const dynamic = "force-dynamic";

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

/**
*
* @author: snomiao <[email protected]>
*/
export default async function TasksIndexPage() {
// Dynamic imports to avoid build-time execution
const { GithubActionUpdateTask } = await import("@/src/GithubActionUpdateTask/GithubActionUpdateTask");
const { GithubBugcopTask } = await import("../../run/gh-bugcop/gh-bugcop");
const { GithubBountyTask } = await import("./gh-bounty/gh-bounty");
const { GithubDesignTask } = await import("./gh-design/gh-design");
const { GithubContributorAnalyzeTask, GithubContributorAnalyzeTaskFilter } = await import(
"./github-contributor-analyze/GithubContributorAnalyzeTask"
);

const Counts = {
GithubActionUpdateTask: () => <Suspense>{GithubActionUpdateTask.estimatedDocumentCount()}</Suspense>,
GithubContributorAnalyzeTask: () => <Suspense>{GithubContributorAnalyzeTask.estimatedDocumentCount()}</Suspense>,
GithubContributorAnalyzeTaskRemain: () => (
<Suspense>{GithubContributorAnalyzeTask.countDocuments(GithubContributorAnalyzeTaskFilter)}</Suspense>
),
GithubBountyTask: () => <Suspense>{GithubBountyTask.estimatedDocumentCount()}</Suspense>,
GithubDesignTask: () => <Suspense>{GithubDesignTask.estimatedDocumentCount()}</Suspense>,
};

return (
<ol className="px-8">
<li>
Expand Down
Loading