fix a lot of the coderabbit warnings
This commit is contained in:
parent
db127afe5a
commit
a7a4ccef6b
4 changed files with 66 additions and 25 deletions
|
|
@ -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 />
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)`),
|
||||||
|
|
|
||||||
|
|
@ -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]) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue