From 9a25363f5573ed15a470f6d52dc11e51f4974ad2 Mon Sep 17 00:00:00 2001 From: Melvin Jones Repol Date: Thu, 19 Mar 2026 22:11:40 +0800 Subject: [PATCH 1/2] feat: add sitemap, robots, 404 and update-password route - feat(sitemap): add sitemap.ts and sitemaps/static.ts and sitemaps/leaderboards.ts (leaderboards fetched from Supabase) - feat(robots): add robots.ts with sitemap and disallow /api - feat(ui): add app/not-found.tsx 404 page component - fix(auth): include /update-password in protectedRoutes in auth.ts --- app/lib/proxy/auth.ts | 2 +- app/not-found.tsx | 120 +++++++++++++++++++++++++++++++++++ app/robots.ts | 12 ++++ app/sitemap.tsx | 14 ++++ app/sitemaps/leaderboards.ts | 17 +++++ app/sitemaps/static.ts | 12 ++++ 6 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 app/not-found.tsx create mode 100644 app/robots.ts create mode 100644 app/sitemap.tsx create mode 100644 app/sitemaps/leaderboards.ts create mode 100644 app/sitemaps/static.ts diff --git a/app/lib/proxy/auth.ts b/app/lib/proxy/auth.ts index 17cab24..7fe9cbf 100644 --- a/app/lib/proxy/auth.ts +++ b/app/lib/proxy/auth.ts @@ -29,7 +29,7 @@ export default async function Auth(req: NextRequest) { } = await supabase.auth.getUser(); const { pathname } = req.nextUrl; - const protectedRoutes = ["/dashboard", "/logout"]; + const protectedRoutes = ["/dashboard", "/update-password", "/logout"]; const isProtectedRoute = protectedRoutes.some((route) => { const regex = new RegExp(`^${route}(/.*)?$`); return regex.test(pathname); diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..0df0296 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,120 @@ +export default function NotFound() { + return ( +
+
+

404

+ +

Page not found

+ +

+ The page you’re looking for doesn’t exist or may have been moved. +

+ +

+ Tip: Check the URL or head back to the homepage. +

+
+ +
+ {/* Card 1 */} +
+
+
+ Total Coding +
+
+ +18% +
+
+
42h 15m
+
Last 7 days
+
+
+
+
+ + {/* Card 2 */} +
+

+ Top Languages +

+
+
+
+ TS +
+
+
+ TypeScript + + 28h 40m + +
+
+
+
+
+
+
+
+ Re +
+
+
+ React + + 12h 10m + +
+
+
+
+
+
+
+
+ + {/* Card 3 (Code terminal) */} +
+
+
+
+
+
+
+ import{" "} + {"{ Pulse }"}{" "} + from{" "} + 'devpulse';
+
+ Pulse. + syncWakaTime( + key). + then( + stats{" "} + => {"{"} +
+   console. + log( + "Leveling up!"); +
+ {"}"}); +
+
+
+
+ ); +} diff --git a/app/robots.ts b/app/robots.ts new file mode 100644 index 0000000..6f595e5 --- /dev/null +++ b/app/robots.ts @@ -0,0 +1,12 @@ +import type { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + disallow: ["/api"], + }, + sitemap: "https://devpulse-waka.vercel.app/sitemap.xml", + }; +} diff --git a/app/sitemap.tsx b/app/sitemap.tsx new file mode 100644 index 0000000..e398c1d --- /dev/null +++ b/app/sitemap.tsx @@ -0,0 +1,14 @@ +import type { MetadataRoute } from "next"; +import staticSitemap from "./sitemaps/static"; +import leaderboardsSitemap from "./sitemaps/leaderboards"; + +export const revalidate = 86400; + +export default async function sitemap(): Promise { + const [staticUrl, leaderboardsUrl] = await Promise.all([ + staticSitemap(), + leaderboardsSitemap(), + ]); + + return [...staticUrl, ...leaderboardsUrl]; +} diff --git a/app/sitemaps/leaderboards.ts b/app/sitemaps/leaderboards.ts new file mode 100644 index 0000000..1d70748 --- /dev/null +++ b/app/sitemaps/leaderboards.ts @@ -0,0 +1,17 @@ +import { createClient } from "../lib/supabase/server"; +import type { MetadataRoute } from "next"; + +export default async function sitemap(): Promise { + const supabase = await createClient(); + + const { data, error } = await supabase + .from("leaderboards") + .select("id, name, slug") + .order("created_at", { ascending: false }); + + if (error) return []; + + return data.map((leaderboard) => ({ + url: `https://devpulse-waka.vercel.app/leaderboard/${leaderboard.slug}`, + })); +} diff --git a/app/sitemaps/static.ts b/app/sitemaps/static.ts new file mode 100644 index 0000000..434b8dc --- /dev/null +++ b/app/sitemaps/static.ts @@ -0,0 +1,12 @@ +import type { MetadataRoute } from "next"; + +export default function sitemap(): MetadataRoute.Sitemap { + return [ + { url: "https://devpulse-waka.vercel.app" }, + { url: "https://devpulse-waka.vercel.app/login" }, + { url: "https://devpulse-waka.vercel.app/signup" }, + { url: "https://devpulse-waka.vercel.app/legal" }, + { url: "https://devpulse-waka.vercel.app/legal/terms" }, + { url: "https://devpulse-waka.vercel.app/legal/privacy" }, + ]; +} From 79b36165548df3f4789beb44a6841a7ca7cb9ee5 Mon Sep 17 00:00:00 2001 From: Melvin Jones Repol Date: Thu, 19 Mar 2026 22:33:24 +0800 Subject: [PATCH 2/2] feat(legal): add privacy and terms pages - Create app/legal/privacy/page.tsx with metadata and full privacy policy - Create app/legal/terms/page.tsx with metadata and terms of service - Refactor app/components/layout/Footer.tsx - responsive container and layout adjustments - add Privacy and Terms links and update GitHub link styling - add divider, maintainer note, and spacing tweaks --- app/components/layout/Footer.tsx | 64 ++++++++++----- app/legal/privacy/page.tsx | 135 +++++++++++++++++++++++++++++++ app/legal/terms/page.tsx | 114 ++++++++++++++++++++++++++ 3 files changed, 294 insertions(+), 19 deletions(-) create mode 100644 app/legal/privacy/page.tsx create mode 100644 app/legal/terms/page.tsx diff --git a/app/components/layout/Footer.tsx b/app/components/layout/Footer.tsx index 4cd3093..2c3e96d 100644 --- a/app/components/layout/Footer.tsx +++ b/app/components/layout/Footer.tsx @@ -1,24 +1,50 @@ +import Link from "next/link"; + export default function Footer() { return ( -