fix a lot of the coderabbit warnings

This commit is contained in:
Raj Dave 2026-01-10 14:23:39 +03:00
parent db127afe5a
commit a7a4ccef6b
4 changed files with 66 additions and 25 deletions

View file

@ -10,7 +10,13 @@ import {
import { Input } from "../../../../components/ui/input"; import { Input } from "../../../../components/ui/input";
import { Textarea } from "../../../../components/ui/textarea"; import { Textarea } from "../../../../components/ui/textarea";
import { Checkbox } from "../../../../components/ui/checkbox"; import { Checkbox } from "../../../../components/ui/checkbox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../../../../components/ui/select"; import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from "../../../../components/ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "../../../../components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "../../../../components/ui/tooltip";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../../components/ui/collapsible"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../../components/ui/collapsible";
import type { RepositoryFormValues } from "../create-repository-form"; import type { RepositoryFormValues } from "../create-repository-form";
@ -103,7 +109,7 @@ export const AdvancedForm = ({ form }: Props) => {
name="uploadLimit.unit" name="uploadLimit.unit"
render={({ field }) => ( render={({ field }) => (
<FormItem className="w-24"> <FormItem className="w-24">
<Select onValueChange={field.onChange} defaultValue={field.value || "MB/s"} value={field.value || "MB/s"}> <Select onValueChange={field.onChange} defaultValue={field.value || "Mbps"} value={field.value || "Mbps"}>
<FormControl> <FormControl>
<SelectTrigger className="text-xs"> <SelectTrigger className="text-xs">
<SelectValue /> <SelectValue />
@ -186,7 +192,7 @@ export const AdvancedForm = ({ form }: Props) => {
name="downloadLimit.unit" name="downloadLimit.unit"
render={({ field }) => ( render={({ field }) => (
<FormItem className="w-24"> <FormItem className="w-24">
<Select onValueChange={field.onChange} defaultValue={field.value || "MB/s"} value={field.value || "MB/s"}> <Select onValueChange={field.onChange} defaultValue={field.value || "Mbps"} value={field.value || "Mbps"}>
<FormControl> <FormControl>
<SelectTrigger className="text-xs"> <SelectTrigger className="text-xs">
<SelectValue /> <SelectValue />

View file

@ -14,9 +14,9 @@ export const REPOSITORY_BACKENDS = {
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS; export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
export const BANDWIDTH_UNITS = { export const BANDWIDTH_UNITS = {
"KB/s": "KB/s", "Kbps": "Kbps",
"MB/s": "MB/s", "Mbps": "Mbps",
"GB/s": "GB/s", "Gbps": "Gbps",
} as const; } as const;
export type BandwidthUnit = keyof typeof BANDWIDTH_UNITS; export type BandwidthUnit = keyof typeof BANDWIDTH_UNITS;
@ -24,8 +24,8 @@ export type BandwidthUnit = keyof typeof BANDWIDTH_UNITS;
// Bandwidth limit configuration // Bandwidth limit configuration
export const bandwidthLimitSchema = type({ export const bandwidthLimitSchema = type({
enabled: "boolean = false", enabled: "boolean = false",
value: "number >= 0", value: "number >= 0 = 0",
unit: type.valueOf(BANDWIDTH_UNITS).default("MB/s"), unit: type.valueOf(BANDWIDTH_UNITS).default("Mbps"),
}); });
export type BandwidthLimit = typeof bandwidthLimitSchema.infer; export type BandwidthLimit = typeof bandwidthLimitSchema.infer;

View file

@ -1,6 +1,6 @@
import { relations, sql } from "drizzle-orm"; import { relations, sql } from "drizzle-orm";
import { index, int, integer, sqliteTable, text, primaryKey, unique } from "drizzle-orm/sqlite-core"; import { index, int, integer, sqliteTable, text, real, primaryKey, unique } from "drizzle-orm/sqlite-core";
import type { CompressionMode, RepositoryBackend, repositoryConfigSchema, RepositoryStatus } from "~/schemas/restic"; import type { CompressionMode, RepositoryBackend, repositoryConfigSchema, RepositoryStatus, BandwidthUnit } from "~/schemas/restic";
import type { BackendStatus, BackendType, volumeConfigSchema } from "~/schemas/volumes"; import type { BackendStatus, BackendType, volumeConfigSchema } from "~/schemas/volumes";
import type { NotificationType, notificationConfigSchema } from "~/schemas/notifications"; import type { NotificationType, notificationConfigSchema } from "~/schemas/notifications";
@ -161,11 +161,11 @@ export const repositoriesTable = sqliteTable("repositories_table", {
lastError: text("last_error"), lastError: text("last_error"),
// Bandwidth limit fields // Bandwidth limit fields
uploadLimitEnabled: int("upload_limit_enabled", { mode: "boolean" }).notNull().default(false), uploadLimitEnabled: int("upload_limit_enabled", { mode: "boolean" }).notNull().default(false),
uploadLimitValue: int("upload_limit_value").notNull().default(0), uploadLimitValue: real("upload_limit_value").notNull().default(0),
uploadLimitUnit: text("upload_limit_unit").notNull().default("MB/s"), uploadLimitUnit: text("upload_limit_unit").$type<BandwidthUnit>().notNull().default("Mbps"),
downloadLimitEnabled: int("download_limit_enabled", { mode: "boolean" }).notNull().default(false), downloadLimitEnabled: int("download_limit_enabled", { mode: "boolean" }).notNull().default(false),
downloadLimitValue: int("download_limit_value").notNull().default(0), downloadLimitValue: real("download_limit_value").notNull().default(0),
downloadLimitUnit: text("download_limit_unit").notNull().default("MB/s"), downloadLimitUnit: text("download_limit_unit").$type<BandwidthUnit>().notNull().default("Mbps"),
createdAt: int("created_at", { mode: "number" }) createdAt: int("created_at", { mode: "number" })
.notNull() .notNull()
.default(sql`(unixepoch() * 1000)`), .default(sql`(unixepoch() * 1000)`),

View file

@ -846,8 +846,39 @@ const copy = async (
args.push("latest"); args.push("latest");
} }
// Apply bandwidth limits from both source and destination configs
// First apply destination config limits (for the main operation)
addCommonArgs(args, env, destConfig); addCommonArgs(args, env, destConfig);
// Then explicitly apply source-side bandwidth limits for the from-repo operation
if (sourceConfig.uploadLimit?.enabled) {
const sourceUploadLimit = formatBandwidthLimit(sourceConfig.uploadLimit);
if (sourceUploadLimit) {
if (sourceConfig.backend === "rclone") {
// For rclone source backends, use rclone.bwlimit for the from-repo
args.push("-o", `rclone.from.bwlimit=${sourceUploadLimit}`);
} else {
// For restic source backends, the download from source becomes an upload limit
args.push("--limit-upload", sourceUploadLimit);
}
}
}
if (sourceConfig.downloadLimit?.enabled) {
const sourceDownloadLimit = formatBandwidthLimit(sourceConfig.downloadLimit);
if (sourceDownloadLimit) {
if (sourceConfig.backend === "rclone") {
// For rclone source backends
const sourceUploadLimit = sourceConfig.uploadLimit?.enabled ? formatBandwidthLimit(sourceConfig.uploadLimit) : "";
const effectiveLimit = sourceUploadLimit && parseInt(sourceUploadLimit) < parseInt(sourceDownloadLimit) ? sourceUploadLimit : sourceDownloadLimit;
args.push("-o", `rclone.from.bwlimit=${effectiveLimit}`);
} else {
// For restic source backends, this affects reading from the source repo
args.push("--limit-download", sourceDownloadLimit);
}
}
}
if (sourceConfig.backend === "sftp" && sourceEnv._SFTP_SSH_ARGS) { if (sourceConfig.backend === "sftp" && sourceEnv._SFTP_SSH_ARGS) {
args.push("-o", `sftp.args=${sourceEnv._SFTP_SSH_ARGS}`); args.push("-o", `sftp.args=${sourceEnv._SFTP_SSH_ARGS}`);
} }
@ -874,29 +905,33 @@ const copy = async (
}; };
}; };
// Helper function to convert bandwidth limit to restic format // Helper function to convert bandwidth limit to restic/rclone format
const formatBandwidthLimit = (limit: BandwidthLimit): string => { const formatBandwidthLimit = (limit: BandwidthLimit): string => {
if (!limit.enabled || limit.value <= 0) { if (!limit.enabled || limit.value <= 0) {
return ""; return "";
} }
// Convert to bytes per second for restic // Convert to KiB/s for restic compatibility, or use suffixed format for rclone
let bytesPerSecond: number; let kibibytesPerSecond: number;
switch (limit.unit) { switch (limit.unit) {
case "KB/s": case "Kbps":
bytesPerSecond = limit.value * 1024; // Kilobits per second to KiB/s: divide by 8 (bits to bytes), then by 1024 (bytes to KiB)
kibibytesPerSecond = limit.value / (8 * 1024);
break; break;
case "MB/s": case "Mbps":
bytesPerSecond = limit.value * 1024 * 1024; // Megabits per second to KiB/s: multiply by 1000000 (Mb to bits), divide by 8 (bits to bytes), then by 1024 (bytes to KiB)
kibibytesPerSecond = (limit.value * 1000000) / (8 * 1024);
break; break;
case "GB/s": case "Gbps":
bytesPerSecond = limit.value * 1024 * 1024 * 1024; // Gigabits per second to KiB/s: multiply by 1000000000 (Gb to bits), divide by 8 (bits to bytes), then by 1024 (bytes to KiB)
kibibytesPerSecond = (limit.value * 1000000000) / (8 * 1024);
break; break;
default: default:
return ""; return "";
} }
return `${Math.floor(bytesPerSecond)}`; // Return as integer KiB/s for restic
return `${Math.floor(kibibytesPerSecond)}`;
}; };
export const addCommonArgs = (args: string[], env: Record<string, string>, config?: RepositoryConfig) => { export const addCommonArgs = (args: string[], env: Record<string, string>, config?: RepositoryConfig) => {
@ -968,7 +1003,7 @@ export const restic = {
// Helper function to clean up temporary files // Helper function to clean up temporary files
const cleanupTemporaryKeys = async (env: Record<string, string>) => { const cleanupTemporaryKeys = async (env: Record<string, string>) => {
const keysToClean = ['_SFTP_KEY_PATH', '_SFTP_KNOWN_HOSTS_PATH', 'RESTIC_CACERT', 'GOOGLE_APPLICATION_CREDENTIALS']; const keysToClean = ["_SFTP_KEY_PATH", "_SFTP_KNOWN_HOSTS_PATH", "RESTIC_CACERT", "GOOGLE_APPLICATION_CREDENTIALS"];
for (const key of keysToClean) { for (const key of keysToClean) {
if (env[key]) { if (env[key]) {