Openstatus www.openstatus.dev

feat: first empty status page ๐Ÿ“„ (#60)

* feat: first empty status page ๐Ÿ“„

* feat: first empty status page ๐Ÿ“„

* chore: remove console

authored by

Thibault Le Ouay and committed by
GitHub
30364366 c826427a

+139 -2
+16
apps/web/src/app/not-found.tsx
··· 1 + import Link from "next/link"; 2 + 3 + export default function NotFound() { 4 + return ( 5 + <main className="flex min-h-screen w-full flex-col space-y-6 p-4 md:p-8"> 6 + <div className="flex flex-1 flex-col items-center justify-center gap-8"> 7 + <div className="mx-auto max-w-xl text-center"> 8 + <div className="border-border rounded-lg border p-8 backdrop-blur-[2px]"> 9 + <h2>Not Found ๐Ÿ˜ญ</h2> 10 + <p>This page could not be found</p> 11 + </div> 12 + </div> 13 + </div> 14 + </main> 15 + ); 16 + }
+32
apps/web/src/app/status-page/[domain]/page.tsx
··· 1 + import { notFound } from "next/navigation"; 2 + 3 + import { MonitorList } from "@/components/status-page/monitor-list"; 4 + import { api } from "@/trpc/server"; 5 + 6 + export default async function Page({ params }: { params: { domain: string } }) { 7 + // We should fetch the the monitors and incident here 8 + // also the page information 9 + if (!params.domain) return notFound(); 10 + const page = await api.page.getPageBySlug.query({ slug: params.domain }); 11 + if (!page) { 12 + return notFound(); 13 + } 14 + 15 + return ( 16 + <main className="flex min-h-screen w-full flex-col space-y-6 p-4 md:p-8"> 17 + <div className="flex flex-1 flex-col items-center justify-center gap-8"> 18 + <div className="mx-auto max-w-xl text-center"> 19 + <div className="border-border rounded-lg border p-8 backdrop-blur-[2px]"> 20 + <h1 className="text-foreground font-cal mb-6 mt-2 text-3xl"> 21 + {page.title} 22 + </h1> 23 + <div> 24 + <p className="text-muted-foreground">{page.description}</p> 25 + </div> 26 + </div> 27 + <MonitorList monitors={page.monitors} /> 28 + </div> 29 + </div> 30 + </main> 31 + ); 32 + }
+22
apps/web/src/components/status-page/monitor-list.tsx
··· 1 + import type { z } from "zod"; 2 + 3 + import type { selectMonitorSchema } from "@openstatus/db/src/schema"; 4 + 5 + import { Monitor } from "./monitor"; 6 + 7 + export const MonitorList = ({ 8 + monitors, 9 + }: { 10 + monitors: z.infer<typeof selectMonitorSchema>[]; 11 + }) => { 12 + return ( 13 + <div> 14 + {monitors.map((monitor, index) => ( 15 + <div key={index}> 16 + {/* Fetch tracker and data */} 17 + <Monitor monitor={monitor} /> 18 + </div> 19 + ))} 20 + </div> 21 + ); 22 + };
+28
apps/web/src/components/status-page/monitor.tsx
··· 1 + import type { z } from "zod"; 2 + 3 + import type { selectMonitorSchema } from "@openstatus/db/src/schema"; 4 + 5 + import { getMonitorListData } from "@/lib/tb"; 6 + import { Tracker } from "../monitor/tracker"; 7 + 8 + export const Monitor = async ({ 9 + monitor, 10 + }: { 11 + monitor: z.infer<typeof selectMonitorSchema>; 12 + }) => { 13 + // fix this we should update our tinybird to fetch with pageId and monitorId 14 + // const data = await getMonitorListData({ siteId: String(monitor.pageId) }); 15 + const data = await getMonitorListData({ siteId: "openstatus" }); 16 + 17 + return ( 18 + <div className="border-border rounded-lg border p-8"> 19 + <h1 className="font-cal mb-3 text-center text-2xl">Status</h1> 20 + <Tracker 21 + data={data} 22 + id="openstatus" 23 + name="Ping" 24 + url="https://openstatus.dev/api/ping" 25 + /> 26 + </div> 27 + ); 28 + };
+41 -2
apps/web/src/middleware.ts
··· 1 + import type { NextFetchEvent, NextRequest } from "next/server"; 1 2 import { NextResponse } from "next/server"; 2 3 import { authMiddleware, redirectToSignIn } from "@clerk/nextjs"; 3 4 4 - import { createTRPCContext } from "@openstatus/api"; 5 - import { edgeRouter } from "@openstatus/api/src/edge"; 6 5 import { db, eq } from "@openstatus/db"; 7 6 import { user, usersToWorkspaces } from "@openstatus/db/src/schema"; 8 7 8 + const before = (req: NextRequest, ev: NextFetchEvent) => { 9 + const url = req.nextUrl.clone(); 10 + 11 + if (url.pathname.includes("api/trpc")) { 12 + return NextResponse.next(); 13 + } 14 + 15 + const host = req.headers.get("host"); 16 + const subdomain = getValidSubdomain(host); 17 + if (subdomain) { 18 + // Subdomain available, rewriting 19 + console.log( 20 + `>>> Rewriting: ${url.pathname} to /status-page/${subdomain}${url.pathname}`, 21 + ); 22 + url.pathname = `/status-page/${subdomain}${url.pathname}`; 23 + return NextResponse.rewrite(url); 24 + } 25 + 26 + return NextResponse.next(); 27 + }; 28 + 29 + export const getValidSubdomain = (host?: string | null) => { 30 + let subdomain: string | null = null; 31 + if (!host && typeof window !== "undefined") { 32 + // On client side, get the host from window 33 + host = window.location.host; 34 + } 35 + // we should improve here for custom vercel deploy page 36 + if (host && host.includes(".") && !host.includes(".ngrok-free.app")) { 37 + const candidate = host.split(".")[0]; 38 + if (candidate && !candidate.includes("www")) { 39 + // Valid candidate 40 + subdomain = candidate; 41 + } 42 + } 43 + return subdomain; 44 + }; 45 + 9 46 export default authMiddleware({ 10 47 publicRoutes: [ 11 48 "/", ··· 21 58 "/api/checker/regions/(.*)", 22 59 "/api/checker/cron/10m", 23 60 ], 61 + 62 + beforeAuth: before, 24 63 async afterAuth(auth, req, evt) { 25 64 // handle users who aren't authenticated 26 65 if (!auth.userId && !auth.isPublicRoute) {