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({