diff --git a/app/client/modules/backups/routes/backups.tsx b/app/client/modules/backups/routes/backups.tsx index 8883361c..5dd1f7e3 100644 --- a/app/client/modules/backups/routes/backups.tsx +++ b/app/client/modules/backups/routes/backups.tsx @@ -14,7 +14,6 @@ import { useState, useEffect } from "react"; import { EmptyState } from "~/client/components/empty-state"; import { Button } from "~/client/components/ui/button"; import { Card, CardContent } from "~/client/components/ui/card"; -import { listBackupSchedules } from "~/client/api-client"; import { listBackupSchedulesOptions, reorderBackupSchedulesMutation, @@ -23,12 +22,6 @@ import { SortableCard } from "~/client/components/sortable-card"; import { BackupCard } from "../components/backup-card"; import { Link } from "@tanstack/react-router"; -export const clientLoader = async () => { - const jobs = await listBackupSchedules(); - if (jobs.data) return jobs.data; - return []; -}; - export function BackupsPage() { const { data: schedules } = useSuspenseQuery({ ...listBackupSchedulesOptions(), diff --git a/app/client/modules/repositories/routes/snapshot-details.tsx b/app/client/modules/repositories/routes/snapshot-details.tsx index 3582a39e..912772c9 100644 --- a/app/client/modules/repositories/routes/snapshot-details.tsx +++ b/app/client/modules/repositories/routes/snapshot-details.tsx @@ -110,10 +110,6 @@ export function SnapshotDetailsPage({ repositoryId, snapshotId }: { repositoryId Snapshot ID:

{data.short_id}

-
- Short ID: -

{data.short_id}

-
Hostname:

{data.hostname}

diff --git a/app/lib/request-client.ts b/app/lib/request-client.ts index 7f30904b..305c488b 100644 --- a/app/lib/request-client.ts +++ b/app/lib/request-client.ts @@ -14,6 +14,7 @@ type AsyncLocalStorageConstructor = new () => { }; let requestClientStore: RequestClientStore | undefined; +let requestClientStorePromise: Promise | undefined; const ASYNC_HOOKS_MODULE = "node:async_hooks"; @@ -22,14 +23,17 @@ const loadRequestClientStore = async (): Promise return undefined; } - if (!requestClientStore) { - const asyncHooksModule = (await import(/* @vite-ignore */ ASYNC_HOOKS_MODULE)) as { - AsyncLocalStorage: AsyncLocalStorageConstructor; - }; - requestClientStore = new asyncHooksModule.AsyncLocalStorage(); + if (!requestClientStorePromise) { + requestClientStorePromise = (async () => { + const asyncHooksModule = (await import(/* @vite-ignore */ ASYNC_HOOKS_MODULE)) as { + AsyncLocalStorage: AsyncLocalStorageConstructor; + }; + requestClientStore = new asyncHooksModule.AsyncLocalStorage(); + return requestClientStore; + })(); } - return requestClientStore; + return requestClientStorePromise; }; export function getRequestClient(): RequestClient { diff --git a/app/middleware/auth.ts b/app/middleware/auth.ts index 3dcbb8c3..6d987d9a 100644 --- a/app/middleware/auth.ts +++ b/app/middleware/auth.ts @@ -1,8 +1,8 @@ -import { getStatus } from "~/client/api-client"; import { createMiddleware } from "@tanstack/react-start"; import { redirect } from "@tanstack/react-router"; import { auth } from "~/server/lib/auth"; import { getRequestHeaders } from "@tanstack/react-start/server"; +import { authService } from "~/server/modules/auth/auth.service"; export const authMiddleware = createMiddleware().server(async ({ next, request }) => { const headers = getRequestHeaders(); @@ -11,8 +11,8 @@ export const authMiddleware = createMiddleware().server(async ({ next, request } const isAuthRoute = ["/login", "/onboarding"].includes(new URL(request.url).pathname); if (!session?.user?.id && !isAuthRoute) { - const status = await getStatus().catch(() => ({ data: { hasUsers: false } })); - if (!status.data?.hasUsers) { + const hasUsers = await authService.hasUsers(); + if (!hasUsers) { throw redirect({ to: "/onboarding" }); } diff --git a/app/routes/(dashboard)/backups/$backupId.$snapshotId.restore.tsx b/app/routes/(dashboard)/backups/$backupId.$snapshotId.restore.tsx index 126c5791..fb15bb24 100644 --- a/app/routes/(dashboard)/backups/$backupId.$snapshotId.restore.tsx +++ b/app/routes/(dashboard)/backups/$backupId.$snapshotId.restore.tsx @@ -21,6 +21,15 @@ export const Route = createFileRoute("/(dashboard)/backups/$backupId/$snapshotId return { snapshot, repository, schedule: schedule.data }; }, + head: ({ params }) => ({ + meta: [ + { title: `Zerobyte - Restore Snapshot ${params.snapshotId}` }, + { + name: "description", + content: "Restore files from a backup snapshot.", + }, + ], + }), staticData: { breadcrumb: (match) => [ { label: "Backup Jobs", href: "/backups" }, diff --git a/app/routes/__root.tsx b/app/routes/__root.tsx index c3c798ec..0b282375 100644 --- a/app/routes/__root.tsx +++ b/app/routes/__root.tsx @@ -12,11 +12,7 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( middleware: [apiClientMiddleware], }, head: () => ({ - meta: [ - { charSet: "utf-8" }, - { name: "viewport", content: "width=device-width, initial-scale=1" }, - { title: "Zerobyte - Open Source Backup Solution" }, - ], + meta: [{ title: "Zerobyte - Open Source Backup Solution" }], links: [ { rel: "stylesheet", diff --git a/app/server.ts b/app/server.ts index 37c20d69..0c291b41 100644 --- a/app/server.ts +++ b/app/server.ts @@ -32,12 +32,22 @@ export default createServerEntry({ process.on("SIGTERM", async () => { logger.info("SIGTERM received, starting graceful shutdown..."); - await shutdown(); - process.exit(0); + try { + await shutdown(); + } catch (err) { + logger.error("Error during shutdown", err); + } finally { + process.exit(0); + } }); process.on("SIGINT", async () => { logger.info("SIGINT received, starting graceful shutdown..."); - await shutdown(); - process.exit(0); + try { + await shutdown(); + } catch (err) { + logger.error("Error during shutdown", err); + } finally { + process.exit(0); + } }); diff --git a/app/server/utils/logger.ts b/app/server/utils/logger.ts index d8c3e439..b4174a87 100644 --- a/app/server/utils/logger.ts +++ b/app/server/utils/logger.ts @@ -1,3 +1,4 @@ +import { format } from "date-fns"; import { createConsola, type ConsolaReporter } from "consola"; import { formatWithOptions } from "node:util"; import { sanitizeSensitiveData } from "./sanitize"; @@ -50,17 +51,18 @@ const reporter: ConsolaReporter = { log(logObj, ctx) { const level = resolveLevel(logObj.type); + const timestamp = colorize("\x1b[90m", format(new Date(), "HH:mm:ss")); const style = levelStyles[level]; const prefix = colorize(style.color, style.label); const tag = logObj.tag ? `[${logObj.tag}]` : ""; const message = formatWithOptions( { - colors: useColor, ...ctx.options.formatOptions, + colors: useColor, }, ...logObj.args, ); - const line = [prefix, tag, message].filter(Boolean).join(" "); + const line = [timestamp, prefix, tag, message].filter(Boolean).join(" "); const stream = logObj.level < 2 ? (ctx.options.stderr ?? process.stderr) : (ctx.options.stdout ?? process.stdout); stream.write(line + "\n"); }, diff --git a/vite.config.ts b/vite.config.ts index 6abc6502..8e618d54 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -6,6 +6,9 @@ import tsconfigPaths from "vite-tsconfig-paths"; import viteReact from "@vitejs/plugin-react"; export default defineConfig({ + define: { + "process.env.NODE_ENV": "production", + }, plugins: [ tsconfigPaths(), tanstackStart({