Skip to content

Commit e3b4ec7

Browse files
authored
refactor(dashboard): centralize app URL usage and enhance robots.txt rules (#369)
- Replaced hardcoded URLs with APP_URL constant for consistency across layout, OG image generation, and status pages. - Updated robots.txt to allow access to the /public/ path in addition to /status/.
1 parent 0f3d904 commit e3b4ec7

8 files changed

Lines changed: 82 additions & 11 deletions

File tree

apps/dashboard/app/(dby)/dby/l/[slug]/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import {
77
} from "@databuddy/redis";
88
import type { Metadata } from "next";
99
import { notFound, redirect } from "next/navigation";
10+
import { APP_URL } from "@/lib/app-url";
1011

11-
const OG_IMAGE_BASE = "https://app.databuddy.cc/dby/og";
12+
const OG_IMAGE_BASE = `${APP_URL}/dby/og`;
1213

1314
function generateOgImageUrl(title: string, description?: string): string {
1415
const params = new URLSearchParams({ title });

apps/dashboard/app/layout.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Databuddy } from "@databuddy/sdk/react";
44
import type { Metadata, Viewport } from "next";
55
import localFont from "next/font/local";
66
import { Toaster } from "@/components/ui/sonner";
7+
import { APP_URL } from "@/lib/app-url";
78
import Providers from "./providers";
89

910
const ltSuperior = localFont({
@@ -31,9 +32,7 @@ const ltSuperiorMono = localFont({
3132
});
3233

3334
export const metadata: Metadata = {
34-
metadataBase: new URL(
35-
process.env.NEXT_PUBLIC_APP_URL || "https://app.databuddy.cc"
36-
),
35+
metadataBase: new URL(APP_URL),
3736
title: {
3837
template: "%s | Databuddy Dashboard",
3938
default: "Databuddy Dashboard",
@@ -58,7 +57,7 @@ export const metadata: Metadata = {
5857
openGraph: {
5958
type: "website",
6059
locale: "en_US",
61-
url: "https://app.databuddy.cc",
60+
url: APP_URL,
6261
title: "Databuddy Dashboard",
6362
description:
6463
"Powerful analytics dashboard for your websites. Track visitors, monitor performance, and gain insights into your audience.",
@@ -92,7 +91,7 @@ export const metadata: Metadata = {
9291
},
9392
},
9493
alternates: {
95-
canonical: "https://app.databuddy.cc",
94+
canonical: APP_URL,
9695
},
9796
appleWebApp: {
9897
title: "Databuddy",

apps/dashboard/app/robots.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ export default function robots(): MetadataRoute.Robots {
44
return {
55
rules: {
66
userAgent: "*",
7-
allow: "/status/",
7+
allow: ["/status/", "/public/"],
88
disallow: ["/", "/.well-known/", "/_next/"],
99
},
1010
};
11-
}
11+
}

apps/dashboard/app/sitemap.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { and, db, eq, organization, uptimeSchedules } from "@databuddy/db";
2+
import type { MetadataRoute } from "next";
3+
import { unstable_cache } from "next/cache";
4+
import { APP_URL } from "@/lib/app-url";
5+
import type { StatusSitemapRow } from "@/types/sitemap";
6+
7+
export const revalidate = 86_400;
8+
9+
const getPublicStatusPages = unstable_cache(
10+
async (): Promise<StatusSitemapRow[]> => {
11+
const rows = await db
12+
.select({
13+
slug: organization.slug,
14+
updatedAt: uptimeSchedules.updatedAt,
15+
})
16+
.from(organization)
17+
.innerJoin(
18+
uptimeSchedules,
19+
and(
20+
eq(uptimeSchedules.organizationId, organization.id),
21+
eq(uptimeSchedules.isPublic, true),
22+
eq(uptimeSchedules.isPaused, false)
23+
)
24+
);
25+
26+
const statusPagesBySlug = new Map<string, Date>();
27+
28+
for (const row of rows) {
29+
if (!row.slug) {
30+
continue;
31+
}
32+
33+
const existingUpdatedAt = statusPagesBySlug.get(row.slug);
34+
if (!existingUpdatedAt || row.updatedAt > existingUpdatedAt) {
35+
statusPagesBySlug.set(row.slug, row.updatedAt);
36+
}
37+
}
38+
39+
return [...statusPagesBySlug.entries()]
40+
.map(([slug, updatedAt]) => ({ slug, updatedAt }))
41+
.sort((a, b) => a.slug.localeCompare(b.slug));
42+
},
43+
["dashboard-status-sitemap"],
44+
{ revalidate: 86_400, tags: ["status-page"] }
45+
);
46+
47+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
48+
const statusPages = await getPublicStatusPages();
49+
50+
return statusPages.map((statusPage) => ({
51+
url: `${APP_URL}/status/${statusPage.slug}`,
52+
lastModified: statusPage.updatedAt,
53+
changeFrequency: "daily",
54+
priority: 0.7,
55+
}));
56+
}

apps/dashboard/app/status/[slug]/_components/status-navbar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Link from "next/link";
33
import { ThemeToggle } from "@/components/layout/theme-toggle";
44
import { Branding } from "@/components/logo/branding";
55
import { Button } from "@/components/ui/button";
6+
import { APP_URL } from "@/lib/app-url";
67

78
export function StatusNavbar() {
89
return (
@@ -33,7 +34,7 @@ export function StatusNavbar() {
3334

3435
<Button asChild className="ml-1" size="sm">
3536
<Link
36-
href="https://app.databuddy.cc/login"
37+
href={`${APP_URL}/login`}
3738
rel="noopener noreferrer"
3839
target="_blank"
3940
>

apps/dashboard/app/status/[slug]/page.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
22
import { unstable_cache } from "next/cache";
33
import { notFound } from "next/navigation";
44
import { Suspense } from "react";
5+
import { getStatusPageUrl } from "@/lib/app-url";
56
import { publicRPCClient } from "@/lib/orpc-public";
67
import { IncidentTimeline } from "./_components/incident-timeline";
78
import { LastUpdated } from "./_components/last-updated";
@@ -57,11 +58,14 @@ export async function generateMetadata({
5758

5859
const title = `${data.organization.name} Status`;
5960
const description = `Real-time system status for ${data.organization.name}`;
60-
const url = `https://app.databuddy.cc/status/${slug}`;
61+
const url = getStatusPageUrl(slug);
6162

6263
return {
6364
title,
6465
description,
66+
alternates: {
67+
canonical: `/status/${slug}`,
68+
},
6569
openGraph: {
6670
title,
6771
description,
@@ -108,7 +112,7 @@ export default async function StatusPage({
108112
"@type": "WebPage",
109113
name: `${data.organization.name} Status`,
110114
description: `Real-time system status for ${data.organization.name}`,
111-
url: `https://app.databuddy.cc/status/${slug}`,
115+
url: getStatusPageUrl(slug),
112116
publisher: {
113117
"@type": "Organization",
114118
name: data.organization.name,

apps/dashboard/lib/app-url.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const APP_URL =
2+
process.env.NEXT_PUBLIC_APP_URL || "https://app.databuddy.cc";
3+
4+
export function getStatusPageUrl(slug: string): string {
5+
return `${APP_URL}/status/${slug}`;
6+
}

apps/dashboard/types/sitemap.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface StatusSitemapRow {
2+
slug: string;
3+
updatedAt: Date;
4+
}

0 commit comments

Comments
 (0)