diff --git a/web/app/(dashboard)/dashboard/projects/[slug]/[env]/services/[serviceId]/builds/[buildId]/page.tsx b/web/app/(dashboard)/dashboard/projects/[slug]/[env]/services/[serviceId]/builds/[buildId]/page.tsx index 8a816bc3..bd9f9de5 100644 --- a/web/app/(dashboard)/dashboard/projects/[slug]/[env]/services/[serviceId]/builds/[buildId]/page.tsx +++ b/web/app/(dashboard)/dashboard/projects/[slug]/[env]/services/[serviceId]/builds/[buildId]/page.tsx @@ -3,7 +3,13 @@ import { notFound } from "next/navigation"; import { BuildDetails } from "@/components/builds/build-details"; import { SetBreadcrumbs } from "@/components/core/breadcrumb-data"; import { db } from "@/db"; -import { builds, projects, serviceRevisions, services } from "@/db/schema"; +import { + builds, + projects, + servers, + serviceRevisions, + services, +} from "@/db/schema"; import { parseServiceRevisionSpec } from "@/lib/service-revision-changes"; async function getBuild( @@ -27,13 +33,18 @@ async function getBuild( if (!service) return null; - const build = await db - .select() + const buildData = await db + .select({ + build: builds, + server: { id: servers.id, name: servers.name }, + }) .from(builds) + .leftJoin(servers, eq(builds.claimedBy, servers.id)) .where(and(eq(builds.id, buildId), eq(builds.serviceId, serviceId))) .then((r) => r[0]); - if (!build) return null; + if (!buildData) return null; + const { build, server } = buildData; const revision = await db .select({ specification: serviceRevisions.specification }) @@ -63,6 +74,7 @@ async function getBuild( project, service, build, + server, githubRepo, }; } @@ -108,6 +120,7 @@ export default async function BuildPage({ envName={env} service={data.service} build={data.build} + server={data.server} githubRepo={data.githubRepo} /> diff --git a/web/app/api/builds/[buildId]/route.ts b/web/app/api/builds/[buildId]/route.ts index bbe0b677..16237543 100644 --- a/web/app/api/builds/[buildId]/route.ts +++ b/web/app/api/builds/[buildId]/route.ts @@ -1,19 +1,30 @@ import { eq } from "drizzle-orm"; import { type NextRequest, NextResponse } from "next/server"; import { db } from "@/db"; -import { builds } from "@/db/schema"; +import { builds, servers } from "@/db/schema"; +import { requireRequestSession } from "@/lib/api-auth"; export async function GET( - _: NextRequest, + request: NextRequest, { params }: { params: Promise<{ buildId: string }> }, ) { + const sessionResult = await requireRequestSession(request); + if (!sessionResult.ok) return sessionResult.response; + const { buildId } = await params; - const [build] = await db.select().from(builds).where(eq(builds.id, buildId)); + const [buildData] = await db + .select({ + build: builds, + server: { id: servers.id, name: servers.name }, + }) + .from(builds) + .leftJoin(servers, eq(builds.claimedBy, servers.id)) + .where(eq(builds.id, buildId)); - if (!build) { + if (!buildData) { return NextResponse.json({ error: "Build not found" }, { status: 404 }); } - return NextResponse.json({ build }); + return NextResponse.json(buildData); } diff --git a/web/app/api/services/[id]/builds/route.ts b/web/app/api/services/[id]/builds/route.ts index aabecb7f..2e9c617e 100644 --- a/web/app/api/services/[id]/builds/route.ts +++ b/web/app/api/services/[id]/builds/route.ts @@ -1,17 +1,25 @@ -import { NextRequest, NextResponse } from "next/server"; -import { desc, eq } from "drizzle-orm"; +import { desc, eq, getTableColumns } from "drizzle-orm"; +import { type NextRequest, NextResponse } from "next/server"; import { db } from "@/db"; -import { builds } from "@/db/schema"; +import { builds, servers } from "@/db/schema"; +import { requireRequestSession } from "@/lib/api-auth"; export async function GET( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { + const sessionResult = await requireRequestSession(request); + if (!sessionResult.ok) return sessionResult.response; + const { id: serviceId } = await params; const buildsList = await db - .select() + .select({ + ...getTableColumns(builds), + server: { id: servers.id, name: servers.name }, + }) .from(builds) + .leftJoin(servers, eq(builds.claimedBy, servers.id)) .where(eq(builds.serviceId, serviceId)) .orderBy(desc(builds.createdAt)) .limit(50); diff --git a/web/components/builds/build-details.tsx b/web/components/builds/build-details.tsx index b80b335b..ca17f31d 100644 --- a/web/components/builds/build-details.tsx +++ b/web/components/builds/build-details.tsx @@ -42,6 +42,8 @@ type BuildWithDates = Omit< claimedAt: string | Date | null; }; +type BuildServer = { id: string; name: string }; + const STATUS_CONFIG: Record< BuildStatus, { @@ -97,28 +99,31 @@ export function BuildDetails({ envName, service, build: initialBuild, + server: initialServer, githubRepo, }: { projectSlug: string; envName: string; service: Pick; build: BuildWithDates; + server: BuildServer | null; githubRepo: { repoFullName: string } | null; }) { const router = useRouter(); const [isCancelling, setIsCancelling] = useState(false); const [isRetrying, setIsRetrying] = useState(false); - const { data } = useSWR<{ build: BuildWithDates }>( - `/api/builds/${initialBuild.id}`, - fetcher, - { - fallbackData: { build: initialBuild }, - refreshInterval: isActiveBuild(initialBuild.status) ? 1500 : 0, - }, - ); + const { data } = useSWR<{ + build: BuildWithDates; + server: BuildServer | null; + }>(`/api/builds/${initialBuild.id}`, fetcher, { + fallbackData: { build: initialBuild, server: initialServer }, + refreshInterval: (latestData) => + isActiveBuild(latestData?.build.status ?? initialBuild.status) ? 1500 : 0, + }); const build = data?.build || initialBuild; + const server = data ? data.server : initialServer; const config = STATUS_CONFIG[build.status]; const isAnimated = ["cloning", "building", "pushing"].includes(build.status); @@ -244,12 +249,23 @@ export function BuildDetails({ {build.commitMessage?.split("\n")[0] || "No message"} -
+
{build.branch} {build.author && by {build.author}} + {server && ( + + on{" "} + + {server.name} + + + )}
diff --git a/web/components/builds/builds-viewer.tsx b/web/components/builds/builds-viewer.tsx index 3c0aa718..dcf773d6 100644 --- a/web/components/builds/builds-viewer.tsx +++ b/web/components/builds/builds-viewer.tsx @@ -13,6 +13,7 @@ import { RotateCcw, XCircle, } from "lucide-react"; +import Link from "next/link"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "sonner"; @@ -61,6 +62,7 @@ type BuildListItem = Pick< createdAt: string; startedAt: string | null; completedAt: string | null; + server: { id: string; name: string } | null; }; type Commit = { @@ -411,10 +413,22 @@ export function BuildsViewer({ {build.branch} {build.author && ( - + by {build.author} )} + {build.server && ( + + on{" "} + event.stopPropagation()} + > + {build.server.name} + + + )} {formatRelativeTime(build.createdAt)}