diff --git a/apps/web/app/api/cron/cleanup-stale-domains/route.ts b/apps/web/app/api/cron/cleanup-stale-domains/route.ts index 82aea5c5..36fe4e22 100644 --- a/apps/web/app/api/cron/cleanup-stale-domains/route.ts +++ b/apps/web/app/api/cron/cleanup-stale-domains/route.ts @@ -1,4 +1,3 @@ -import { subDays } from "date-fns"; import { NextResponse } from "next/server"; import { deleteStaleUnverifiedDomainsByCutoff } from "@domainstack/db/queries/tracked-domains"; @@ -8,6 +7,7 @@ const logger = createLogger({ source: "cron/cleanup-stale-domains" }); // Domains that remain unverified after this many days will be deleted const STALE_DOMAIN_DAYS = 30; +const MS_PER_DAY = 24 * 60 * 60 * 1000; /** * Cron job to clean up stale unverified domains. @@ -25,7 +25,9 @@ export async function GET(request: Request) { try { logger.info("Starting cleanup stale domains cron job"); - const cutoffDate = subDays(new Date(), STALE_DOMAIN_DAYS); + // Absolute rather than calendar days: an hour either side of a clock + // change is immaterial to a month-long staleness cutoff. + const cutoffDate = new Date(Date.now() - STALE_DOMAIN_DAYS * MS_PER_DAY); // Optimized: Delete directly by cutoff date in a single query const deletedCount = await deleteStaleUnverifiedDomainsByCutoff(cutoffDate); diff --git a/apps/web/app/api/transport/[transport]/route.ts b/apps/web/app/api/transport/[transport]/route.ts index 8c176b26..dbfee42f 100644 --- a/apps/web/app/api/transport/[transport]/route.ts +++ b/apps/web/app/api/transport/[transport]/route.ts @@ -4,6 +4,7 @@ import { createMcpHandler } from "mcp-handler"; import { PostHog } from "posthog-node"; import { z } from "zod"; +import { type Section, SECTION_IDS } from "@/lib/constants/sections"; import { checkRateLimit } from "@/lib/ratelimit/api"; import { createCaller } from "@/server/routers/_app"; import type { Context } from "@/trpc/init"; @@ -27,22 +28,8 @@ const domainSchema = z.object({ domain: z.string().min(1, "Domain is required"), }); -/** - * Available sections for domain_report bundle tool. - */ -const REPORT_SECTIONS = [ - "dns", - "registration", - "hosting", - "certificates", - "headers", - "seo", -] as const; - -type ReportSection = (typeof REPORT_SECTIONS)[number]; - const sectionsSchema = z - .array(z.enum(REPORT_SECTIONS)) + .array(z.enum(SECTION_IDS)) .optional() .describe("Sections to include in the report. If omitted, all sections are included."); @@ -225,12 +212,12 @@ function createMcpHandlerWithContext(request: Request) { }, async ({ domain, sections }) => { // Default to all sections if not specified - const requestedSections: ReportSection[] = - sections && sections.length > 0 ? sections : [...REPORT_SECTIONS]; + const requestedSections: Section[] = + sections && sections.length > 0 ? sections : [...SECTION_IDS]; // Define section fetchers const sectionFetchers: Record< - ReportSection, + Section, () => Promise<{ success: boolean; data?: unknown; error?: string }> > = { registration: () => trpc.domain.getRegistration({ domain }), diff --git a/apps/web/components/calendar-instructions.tsx b/apps/web/components/calendar-instructions.tsx index af231127..5a5b8f98 100644 --- a/apps/web/components/calendar-instructions.tsx +++ b/apps/web/components/calendar-instructions.tsx @@ -8,7 +8,6 @@ import { IconRefresh, IconShieldLock, } from "@tabler/icons-react"; -import { formatDistanceStrict } from "date-fns"; import { useCallback, useState } from "react"; import { useCalendarFeed } from "@/hooks/use-calendar-feed"; @@ -35,7 +34,7 @@ import { } from "@domainstack/ui/dropdown-menu"; import { Skeleton } from "@domainstack/ui/skeleton"; import { Spinner } from "@domainstack/ui/spinner"; -import { toDateTimeAttr } from "@domainstack/utils/date"; +import { formatRelativeTime, toDateTimeAttr } from "@domainstack/utils/date"; /** * Outlook doesn't have an icon in @icons-pack/react-simple-icons, so we draw @@ -140,11 +139,7 @@ export function CalendarInstructions({ className }: { className?: string }) { Last accessed{" "} ) : ( diff --git a/apps/web/components/dashboard/archived-domains-list.tsx b/apps/web/components/dashboard/archived-domains-list.tsx index fd0720b7..662a668e 100644 --- a/apps/web/components/dashboard/archived-domains-list.tsx +++ b/apps/web/components/dashboard/archived-domains-list.tsx @@ -1,5 +1,4 @@ import { IconArchive, IconCircleArrowUp, IconRefresh, IconTrash } from "@tabler/icons-react"; -import { formatDistanceStrict } from "date-fns"; import { DashboardBannerDismissable } from "@/components/dashboard/dashboard-banner-dismissable"; import { Favicon } from "@/components/icons/favicon"; @@ -18,7 +17,7 @@ import { } from "@domainstack/ui/empty"; import { Tooltip, TooltipContent, TooltipTrigger } from "@domainstack/ui/tooltip"; import { cn } from "@domainstack/ui/utils"; -import { toDateTimeAttr } from "@domainstack/utils/date"; +import { formatRelativeTime, toDateTimeAttr } from "@domainstack/utils/date"; type ArchivedDomainsListProps = { domains: TrackedDomainWithDetails[]; @@ -122,7 +121,7 @@ function ArchivedRelativeTime({ archivedAt }: { archivedAt: Date | string | null return recently; } - const label = now ? formatDistanceStrict(new Date(dateTime), now, { addSuffix: true }) : null; + const label = now ? formatRelativeTime(new Date(dateTime), now) : null; return (