refactor: pr feedbacks

This commit is contained in:
Nicolas Meienberger 2026-02-11 21:07:35 +01:00
parent 0da4b45d67
commit b7e98deca1
9 changed files with 44 additions and 31 deletions

View file

@ -14,7 +14,6 @@ import { useState, useEffect } from "react";
import { EmptyState } from "~/client/components/empty-state"; import { EmptyState } from "~/client/components/empty-state";
import { Button } from "~/client/components/ui/button"; import { Button } from "~/client/components/ui/button";
import { Card, CardContent } from "~/client/components/ui/card"; import { Card, CardContent } from "~/client/components/ui/card";
import { listBackupSchedules } from "~/client/api-client";
import { import {
listBackupSchedulesOptions, listBackupSchedulesOptions,
reorderBackupSchedulesMutation, reorderBackupSchedulesMutation,
@ -23,12 +22,6 @@ import { SortableCard } from "~/client/components/sortable-card";
import { BackupCard } from "../components/backup-card"; import { BackupCard } from "../components/backup-card";
import { Link } from "@tanstack/react-router"; import { Link } from "@tanstack/react-router";
export const clientLoader = async () => {
const jobs = await listBackupSchedules();
if (jobs.data) return jobs.data;
return [];
};
export function BackupsPage() { export function BackupsPage() {
const { data: schedules } = useSuspenseQuery({ const { data: schedules } = useSuspenseQuery({
...listBackupSchedulesOptions(), ...listBackupSchedulesOptions(),

View file

@ -110,10 +110,6 @@ export function SnapshotDetailsPage({ repositoryId, snapshotId }: { repositoryId
<span className="text-muted-foreground">Snapshot ID:</span> <span className="text-muted-foreground">Snapshot ID:</span>
<p className="font-mono break-all">{data.short_id}</p> <p className="font-mono break-all">{data.short_id}</p>
</div> </div>
<div>
<span className="text-muted-foreground">Short ID:</span>
<p className="font-mono break-all">{data.short_id}</p>
</div>
<div> <div>
<span className="text-muted-foreground">Hostname:</span> <span className="text-muted-foreground">Hostname:</span>
<p>{data.hostname}</p> <p>{data.hostname}</p>

View file

@ -14,6 +14,7 @@ type AsyncLocalStorageConstructor = new <T>() => {
}; };
let requestClientStore: RequestClientStore | undefined; let requestClientStore: RequestClientStore | undefined;
let requestClientStorePromise: Promise<RequestClientStore | undefined> | undefined;
const ASYNC_HOOKS_MODULE = "node:async_hooks"; const ASYNC_HOOKS_MODULE = "node:async_hooks";
@ -22,14 +23,17 @@ const loadRequestClientStore = async (): Promise<RequestClientStore | undefined>
return undefined; return undefined;
} }
if (!requestClientStore) { if (!requestClientStorePromise) {
const asyncHooksModule = (await import(/* @vite-ignore */ ASYNC_HOOKS_MODULE)) as { requestClientStorePromise = (async () => {
AsyncLocalStorage: AsyncLocalStorageConstructor; const asyncHooksModule = (await import(/* @vite-ignore */ ASYNC_HOOKS_MODULE)) as {
}; AsyncLocalStorage: AsyncLocalStorageConstructor;
requestClientStore = new asyncHooksModule.AsyncLocalStorage<RequestClient>(); };
requestClientStore = new asyncHooksModule.AsyncLocalStorage<RequestClient>();
return requestClientStore;
})();
} }
return requestClientStore; return requestClientStorePromise;
}; };
export function getRequestClient(): RequestClient { export function getRequestClient(): RequestClient {

View file

@ -1,8 +1,8 @@
import { getStatus } from "~/client/api-client";
import { createMiddleware } from "@tanstack/react-start"; import { createMiddleware } from "@tanstack/react-start";
import { redirect } from "@tanstack/react-router"; import { redirect } from "@tanstack/react-router";
import { auth } from "~/server/lib/auth"; import { auth } from "~/server/lib/auth";
import { getRequestHeaders } from "@tanstack/react-start/server"; import { getRequestHeaders } from "@tanstack/react-start/server";
import { authService } from "~/server/modules/auth/auth.service";
export const authMiddleware = createMiddleware().server(async ({ next, request }) => { export const authMiddleware = createMiddleware().server(async ({ next, request }) => {
const headers = getRequestHeaders(); 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); const isAuthRoute = ["/login", "/onboarding"].includes(new URL(request.url).pathname);
if (!session?.user?.id && !isAuthRoute) { if (!session?.user?.id && !isAuthRoute) {
const status = await getStatus().catch(() => ({ data: { hasUsers: false } })); const hasUsers = await authService.hasUsers();
if (!status.data?.hasUsers) { if (!hasUsers) {
throw redirect({ to: "/onboarding" }); throw redirect({ to: "/onboarding" });
} }

View file

@ -21,6 +21,15 @@ export const Route = createFileRoute("/(dashboard)/backups/$backupId/$snapshotId
return { snapshot, repository, schedule: schedule.data }; 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: { staticData: {
breadcrumb: (match) => [ breadcrumb: (match) => [
{ label: "Backup Jobs", href: "/backups" }, { label: "Backup Jobs", href: "/backups" },

View file

@ -12,11 +12,7 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()(
middleware: [apiClientMiddleware], middleware: [apiClientMiddleware],
}, },
head: () => ({ head: () => ({
meta: [ meta: [{ title: "Zerobyte - Open Source Backup Solution" }],
{ charSet: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" },
{ title: "Zerobyte - Open Source Backup Solution" },
],
links: [ links: [
{ {
rel: "stylesheet", rel: "stylesheet",

View file

@ -32,12 +32,22 @@ export default createServerEntry({
process.on("SIGTERM", async () => { process.on("SIGTERM", async () => {
logger.info("SIGTERM received, starting graceful shutdown..."); logger.info("SIGTERM received, starting graceful shutdown...");
await shutdown(); try {
process.exit(0); await shutdown();
} catch (err) {
logger.error("Error during shutdown", err);
} finally {
process.exit(0);
}
}); });
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
logger.info("SIGINT received, starting graceful shutdown..."); logger.info("SIGINT received, starting graceful shutdown...");
await shutdown(); try {
process.exit(0); await shutdown();
} catch (err) {
logger.error("Error during shutdown", err);
} finally {
process.exit(0);
}
}); });

View file

@ -1,3 +1,4 @@
import { format } from "date-fns";
import { createConsola, type ConsolaReporter } from "consola"; import { createConsola, type ConsolaReporter } from "consola";
import { formatWithOptions } from "node:util"; import { formatWithOptions } from "node:util";
import { sanitizeSensitiveData } from "./sanitize"; import { sanitizeSensitiveData } from "./sanitize";
@ -50,17 +51,18 @@ const reporter: ConsolaReporter = {
log(logObj, ctx) { log(logObj, ctx) {
const level = resolveLevel(logObj.type); const level = resolveLevel(logObj.type);
const timestamp = colorize("\x1b[90m", format(new Date(), "HH:mm:ss"));
const style = levelStyles[level]; const style = levelStyles[level];
const prefix = colorize(style.color, style.label); const prefix = colorize(style.color, style.label);
const tag = logObj.tag ? `[${logObj.tag}]` : ""; const tag = logObj.tag ? `[${logObj.tag}]` : "";
const message = formatWithOptions( const message = formatWithOptions(
{ {
colors: useColor,
...ctx.options.formatOptions, ...ctx.options.formatOptions,
colors: useColor,
}, },
...logObj.args, ...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); const stream = logObj.level < 2 ? (ctx.options.stderr ?? process.stderr) : (ctx.options.stdout ?? process.stdout);
stream.write(line + "\n"); stream.write(line + "\n");
}, },

View file

@ -6,6 +6,9 @@ import tsconfigPaths from "vite-tsconfig-paths";
import viteReact from "@vitejs/plugin-react"; import viteReact from "@vitejs/plugin-react";
export default defineConfig({ export default defineConfig({
define: {
"process.env.NODE_ENV": "production",
},
plugins: [ plugins: [
tsconfigPaths(), tsconfigPaths(),
tanstackStart({ tanstackStart({