refactoring(bw-limit): stylistic refactoring
This commit is contained in:
parent
cb72e6c073
commit
1622e34b0d
9 changed files with 1657 additions and 470 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -31,3 +31,5 @@ node_modules/
|
|||
|
||||
playwright/.auth
|
||||
playwright/temp
|
||||
|
||||
.idea/
|
||||
|
|
|
|||
|
|
@ -10,13 +10,7 @@ import {
|
|||
import { Input } from "../../../../components/ui/input";
|
||||
import { Textarea } from "../../../../components/ui/textarea";
|
||||
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 { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../../components/ui/collapsible";
|
||||
import type { RepositoryFormValues } from "../create-repository-form";
|
||||
|
|
@ -30,26 +24,15 @@ type Props = {
|
|||
export const AdvancedForm = ({ form }: Props) => {
|
||||
const insecureTls = form.watch("insecureTls");
|
||||
const cacert = form.watch("cacert");
|
||||
const uploadEnabled = form.watch("uploadLimit.enabled");
|
||||
const downloadEnabled = form.watch("downloadLimit.enabled");
|
||||
const uploadLimitEnabled = form.watch("uploadLimit.enabled");
|
||||
const downloadLimitEnabled = form.watch("downloadLimit.enabled");
|
||||
|
||||
return (
|
||||
<Collapsible>
|
||||
<CollapsibleTrigger className="">Advanced Settings</CollapsibleTrigger>
|
||||
<CollapsibleContent className="pb-4 space-y-4">
|
||||
{/* Bandwidth Limit Controls */}
|
||||
<div className="space-y-6 rounded-lg border bg-card p-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div>
|
||||
<h3 className="text-base font-semibold">Bandwidth Limits</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Control upload and download speeds to prevent saturating network bandwidth
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6 sm:grid-cols-2">
|
||||
{/* Upload Limit */}
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="grid gap-6">
|
||||
<div className="space-y-4 rounded-lg border bg-background/50 p-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
|
|
@ -57,155 +40,149 @@ export const AdvancedForm = ({ form }: Props) => {
|
|||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value ?? false}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
<Checkbox checked={field.value ?? false} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<div className="space-y-1 leading-none">
|
||||
<div className="space-y-1">
|
||||
<FormLabel>Enable upload speed limit</FormLabel>
|
||||
<FormDescription className="text-xs">
|
||||
Limit upload speed to the repository
|
||||
</FormDescription>
|
||||
<FormDescription className="text-xs">Limit upload speed to the repository</FormDescription>
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{uploadEnabled && (
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uploadLimit.value"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="10"
|
||||
min="0"
|
||||
step="0.1"
|
||||
className="pr-12"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<div className="h-4 w-px bg-border" />
|
||||
</div>
|
||||
</div>
|
||||
</FormControl>
|
||||
<div className="flex items-center gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uploadLimit.value"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
disabled={!uploadLimitEnabled}
|
||||
type="number"
|
||||
min={1}
|
||||
step={1}
|
||||
max={999999}
|
||||
placeholder="10"
|
||||
className="pr-12"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value, 10) || 1)}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<div className="h-4 w-px bg-border" />
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uploadLimit.unit"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-24">
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value || "Mbps"} value={field.value || "Mbps"}>
|
||||
<FormControl>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{Object.keys(BANDWIDTH_UNITS).map((unit) => (
|
||||
<SelectItem key={unit} value={unit} className="text-xs">
|
||||
{unit}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uploadLimit.unit"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-24">
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value || "Mbps"}
|
||||
value={field.value || "Mbps"}
|
||||
disabled={!uploadLimitEnabled}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{Object.keys(BANDWIDTH_UNITS).map((unit) => (
|
||||
<SelectItem key={unit} value={unit} className="text-xs">
|
||||
{unit}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Download Limit */}
|
||||
<div className="space-y-4 rounded-lg border bg-background/50 p-4">
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="downloadLimit.enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value ?? false}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="space-y-1 leading-none">
|
||||
<FormLabel>Enable download speed limit</FormLabel>
|
||||
<FormDescription className="text-xs">
|
||||
Limit download speed from the repository
|
||||
</FormDescription>
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{downloadEnabled && (
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="downloadLimit.value"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
type="number"
|
||||
placeholder="10"
|
||||
min="0"
|
||||
step="0.1"
|
||||
className="pr-12"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<div className="h-4 w-px bg-border" />
|
||||
</div>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="downloadLimit.unit"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-24">
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value || "Mbps"} value={field.value || "Mbps"}>
|
||||
<FormControl>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{Object.keys(BANDWIDTH_UNITS).map((unit) => (
|
||||
<SelectItem key={unit} value={unit} className="text-xs">
|
||||
{unit}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="rounded-lg border bg-background/50 p-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="downloadLimit.enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<Checkbox checked={field.value ?? false} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<div className="space-y-1">
|
||||
<FormLabel>Enable download speed limit</FormLabel>
|
||||
<FormDescription className="text-xs">Limit download speed from the repository</FormDescription>
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="space-y-3 pt-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="downloadLimit.value"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormControl>
|
||||
<div className="relative">
|
||||
<Input
|
||||
placeholder="10"
|
||||
type="number"
|
||||
min={1}
|
||||
step={1}
|
||||
max={999999}
|
||||
disabled={!downloadLimitEnabled}
|
||||
className="pr-12"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value, 10) || 1)}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
|
||||
<div className="h-4 w-px bg-border" />
|
||||
</div>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="downloadLimit.unit"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-24">
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value || "Mbps"}
|
||||
value={field.value || "Mbps"}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger className="text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{Object.keys(BANDWIDTH_UNITS).map((unit) => (
|
||||
<SelectItem key={unit} value={unit} className="text-xs">
|
||||
{unit}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
ALTER TABLE `repositories_table` ADD `upload_limit_enabled` integer DEFAULT false NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `upload_limit_value` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `upload_limit_unit` text DEFAULT 'MB/s' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `upload_limit_value` real DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `upload_limit_unit` text DEFAULT 'Mbps' NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `download_limit_enabled` integer DEFAULT false NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `download_limit_value` integer DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `download_limit_unit` text DEFAULT 'MB/s' NOT NULL;
|
||||
ALTER TABLE `repositories_table` ADD `download_limit_value` real DEFAULT 0 NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE `repositories_table` ADD `download_limit_unit` text DEFAULT 'Mbps' NOT NULL;
|
||||
1243
app/drizzle/meta/0033_snapshot.json
Normal file
1243
app/drizzle/meta/0033_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,237 +1,244 @@
|
|||
{
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1755765658194,
|
||||
"tag": "0000_known_madelyne_pryor",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1755775437391,
|
||||
"tag": "0001_far_frank_castle",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1756930554198,
|
||||
"tag": "0002_cheerful_randall",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "6",
|
||||
"when": 1758653407064,
|
||||
"tag": "0003_mature_hellcat",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "6",
|
||||
"when": 1758961535488,
|
||||
"tag": "0004_wealthy_tomas",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "6",
|
||||
"when": 1759416698274,
|
||||
"tag": "0005_simple_alice",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "6",
|
||||
"when": 1760734377440,
|
||||
"tag": "0006_secret_micromacro",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "6",
|
||||
"when": 1761224911352,
|
||||
"tag": "0007_watery_sersi",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "6",
|
||||
"when": 1761414054481,
|
||||
"tag": "0008_silent_lady_bullseye",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "6",
|
||||
"when": 1762095226041,
|
||||
"tag": "0009_little_adam_warlock",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "6",
|
||||
"when": 1762610065889,
|
||||
"tag": "0010_perfect_proemial_gods",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"version": "6",
|
||||
"when": 1763644043601,
|
||||
"tag": "0011_familiar_stone_men",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "6",
|
||||
"when": 1764100562084,
|
||||
"tag": "0012_add_short_ids",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"version": "6",
|
||||
"when": 1764182159797,
|
||||
"tag": "0013_elite_sprite",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "6",
|
||||
"when": 1764182405089,
|
||||
"tag": "0014_wild_echo",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "6",
|
||||
"when": 1764182465287,
|
||||
"tag": "0015_jazzy_sersi",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "6",
|
||||
"when": 1764194697035,
|
||||
"tag": "0016_fix-timestamps-to-ms",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"version": "6",
|
||||
"when": 1764357897219,
|
||||
"tag": "0017_fix-compression-modes",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"version": "6",
|
||||
"when": 1764794371040,
|
||||
"tag": "0018_breezy_invaders",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"version": "6",
|
||||
"when": 1764839917446,
|
||||
"tag": "0019_secret_nomad",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"version": "6",
|
||||
"when": 1764847918249,
|
||||
"tag": "0020_even_dexter_bennett",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"version": "6",
|
||||
"when": 1765307881092,
|
||||
"tag": "0021_steady_viper",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"version": "6",
|
||||
"when": 1765794552191,
|
||||
"tag": "0022_woozy_shen",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"version": "6",
|
||||
"when": 1766320570509,
|
||||
"tag": "0023_special_thor",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"version": "6",
|
||||
"when": 1766325504548,
|
||||
"tag": "0024_schedules-one-fs",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 25,
|
||||
"version": "6",
|
||||
"when": 1766431021321,
|
||||
"tag": "0025_remarkable_pete_wisdom",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 26,
|
||||
"version": "6",
|
||||
"when": 1766765013108,
|
||||
"tag": "0026_migrate-local-repo-paths",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 27,
|
||||
"version": "6",
|
||||
"when": 1766778073418,
|
||||
"tag": "0027_careful_cammi",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 28,
|
||||
"version": "6",
|
||||
"when": 1766778162985,
|
||||
"tag": "0028_third_amazoness",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 29,
|
||||
"version": "6",
|
||||
"when": 1767819883495,
|
||||
"tag": "0029_boring_luke_cage",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 30,
|
||||
"version": "6",
|
||||
"when": 1767821088612,
|
||||
"tag": "0030_lower-trim-username",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 31,
|
||||
"version": "6",
|
||||
"when": 1767863951955,
|
||||
"tag": "0031_graceful_squadron_supreme",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 32,
|
||||
"version": "6",
|
||||
"when": 1768136755563,
|
||||
"tag": "0032_lowercase-email",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
"version": "7",
|
||||
"dialect": "sqlite",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1755765658194,
|
||||
"tag": "0000_known_madelyne_pryor",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1755775437391,
|
||||
"tag": "0001_far_frank_castle",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1756930554198,
|
||||
"tag": "0002_cheerful_randall",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "6",
|
||||
"when": 1758653407064,
|
||||
"tag": "0003_mature_hellcat",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "6",
|
||||
"when": 1758961535488,
|
||||
"tag": "0004_wealthy_tomas",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 5,
|
||||
"version": "6",
|
||||
"when": 1759416698274,
|
||||
"tag": "0005_simple_alice",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 6,
|
||||
"version": "6",
|
||||
"when": 1760734377440,
|
||||
"tag": "0006_secret_micromacro",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 7,
|
||||
"version": "6",
|
||||
"when": 1761224911352,
|
||||
"tag": "0007_watery_sersi",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "6",
|
||||
"when": 1761414054481,
|
||||
"tag": "0008_silent_lady_bullseye",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 9,
|
||||
"version": "6",
|
||||
"when": 1762095226041,
|
||||
"tag": "0009_little_adam_warlock",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 10,
|
||||
"version": "6",
|
||||
"when": 1762610065889,
|
||||
"tag": "0010_perfect_proemial_gods",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 11,
|
||||
"version": "6",
|
||||
"when": 1763644043601,
|
||||
"tag": "0011_familiar_stone_men",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "6",
|
||||
"when": 1764100562084,
|
||||
"tag": "0012_add_short_ids",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"version": "6",
|
||||
"when": 1764182159797,
|
||||
"tag": "0013_elite_sprite",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "6",
|
||||
"when": 1764182405089,
|
||||
"tag": "0014_wild_echo",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "6",
|
||||
"when": 1764182465287,
|
||||
"tag": "0015_jazzy_sersi",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "6",
|
||||
"when": 1764194697035,
|
||||
"tag": "0016_fix-timestamps-to-ms",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"version": "6",
|
||||
"when": 1764357897219,
|
||||
"tag": "0017_fix-compression-modes",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"version": "6",
|
||||
"when": 1764794371040,
|
||||
"tag": "0018_breezy_invaders",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 19,
|
||||
"version": "6",
|
||||
"when": 1764839917446,
|
||||
"tag": "0019_secret_nomad",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 20,
|
||||
"version": "6",
|
||||
"when": 1764847918249,
|
||||
"tag": "0020_even_dexter_bennett",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 21,
|
||||
"version": "6",
|
||||
"when": 1765307881092,
|
||||
"tag": "0021_steady_viper",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 22,
|
||||
"version": "6",
|
||||
"when": 1765794552191,
|
||||
"tag": "0022_woozy_shen",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 23,
|
||||
"version": "6",
|
||||
"when": 1766320570509,
|
||||
"tag": "0023_special_thor",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 24,
|
||||
"version": "6",
|
||||
"when": 1766325504548,
|
||||
"tag": "0024_schedules-one-fs",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 25,
|
||||
"version": "6",
|
||||
"when": 1766431021321,
|
||||
"tag": "0025_remarkable_pete_wisdom",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 26,
|
||||
"version": "6",
|
||||
"when": 1766765013108,
|
||||
"tag": "0026_migrate-local-repo-paths",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 27,
|
||||
"version": "6",
|
||||
"when": 1766778073418,
|
||||
"tag": "0027_careful_cammi",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 28,
|
||||
"version": "6",
|
||||
"when": 1766778162985,
|
||||
"tag": "0028_third_amazoness",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 29,
|
||||
"version": "6",
|
||||
"when": 1767819883495,
|
||||
"tag": "0029_boring_luke_cage",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 30,
|
||||
"version": "6",
|
||||
"when": 1767821088612,
|
||||
"tag": "0030_lower-trim-username",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 31,
|
||||
"version": "6",
|
||||
"when": 1767863951955,
|
||||
"tag": "0031_graceful_squadron_supreme",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 32,
|
||||
"version": "6",
|
||||
"when": 1768136755563,
|
||||
"tag": "0032_lowercase-email",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 33,
|
||||
"version": "6",
|
||||
"when": 1768490085610,
|
||||
"tag": "0033_curved_warhawk",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -14,17 +14,16 @@ export const REPOSITORY_BACKENDS = {
|
|||
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
|
||||
|
||||
export const BANDWIDTH_UNITS = {
|
||||
"Kbps": "Kbps",
|
||||
"Mbps": "Mbps",
|
||||
"Gbps": "Gbps",
|
||||
Kbps: "Kbps",
|
||||
Mbps: "Mbps",
|
||||
Gbps: "Gbps",
|
||||
} as const;
|
||||
|
||||
export type BandwidthUnit = keyof typeof BANDWIDTH_UNITS;
|
||||
|
||||
// Bandwidth limit configuration
|
||||
export const bandwidthLimitSchema = type({
|
||||
enabled: "boolean = false",
|
||||
value: "number >= 0 = 0",
|
||||
value: "number > 0 = 1",
|
||||
unit: type.valueOf(BANDWIDTH_UNITS).default("Mbps"),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import { relations, sql } from "drizzle-orm";
|
||||
import { index, int, integer, sqliteTable, text, real, primaryKey, unique } from "drizzle-orm/sqlite-core";
|
||||
import type { CompressionMode, RepositoryBackend, repositoryConfigSchema, RepositoryStatus, BandwidthUnit } from "~/schemas/restic";
|
||||
import type {
|
||||
CompressionMode,
|
||||
RepositoryBackend,
|
||||
repositoryConfigSchema,
|
||||
RepositoryStatus,
|
||||
BandwidthUnit,
|
||||
} from "~/schemas/restic";
|
||||
import type { BackendStatus, BackendType, volumeConfigSchema } from "~/schemas/volumes";
|
||||
import type { NotificationType, notificationConfigSchema } from "~/schemas/notifications";
|
||||
|
||||
|
|
@ -159,7 +165,6 @@ export const repositoriesTable = sqliteTable("repositories_table", {
|
|||
status: text().$type<RepositoryStatus>().default("unknown"),
|
||||
lastChecked: int("last_checked", { mode: "number" }),
|
||||
lastError: text("last_error"),
|
||||
// Bandwidth limit fields
|
||||
uploadLimitEnabled: int("upload_limit_enabled", { mode: "boolean" }).notNull().default(false),
|
||||
uploadLimitValue: real("upload_limit_value").notNull().default(0),
|
||||
uploadLimitUnit: text("upload_limit_unit").$type<BandwidthUnit>().notNull().default("Mbps"),
|
||||
|
|
|
|||
|
|
@ -846,30 +846,13 @@ const copy = async (
|
|||
args.push("latest");
|
||||
}
|
||||
|
||||
// Apply common args without automatic bandwidth limit injection to prevent duplication
|
||||
addCommonArgs(args, env, destConfig, { skipBandwidth: true });
|
||||
|
||||
// Manually handle bandwidth limits with correct copy semantics:
|
||||
// --limit-download uses sourceConfig.downloadLimit (limiting downloads from source repo)
|
||||
// --limit-upload uses destConfig.uploadLimit (limiting uploads to destination repo)
|
||||
const sourceDownloadLimit = formatBandwidthLimit(sourceConfig.downloadLimit);
|
||||
const destUploadLimit = formatBandwidthLimit(destConfig.uploadLimit);
|
||||
|
||||
if (sourceConfig.backend === "rclone") {
|
||||
// For rclone source backends, use rclone.from.bwlimit with source download limit only
|
||||
let effectiveSourceLimit = "";
|
||||
if (sourceDownloadLimit) {
|
||||
effectiveSourceLimit = sourceDownloadLimit;
|
||||
}
|
||||
|
||||
if (effectiveSourceLimit) {
|
||||
args.push("-o", `rclone.from.bwlimit=${effectiveSourceLimit}`);
|
||||
}
|
||||
} else {
|
||||
// For restic source backends, apply download limit from source
|
||||
if (sourceDownloadLimit) {
|
||||
args.push("--limit-download", sourceDownloadLimit);
|
||||
}
|
||||
if (sourceDownloadLimit) {
|
||||
args.push("--limit-download", sourceDownloadLimit);
|
||||
}
|
||||
|
||||
// Apply upload limit to destination for all backends
|
||||
|
|
@ -899,13 +882,11 @@ const copy = async (
|
|||
};
|
||||
};
|
||||
|
||||
// Helper function to convert bandwidth limit to restic/rclone format
|
||||
const formatBandwidthLimit = (limit?: BandwidthLimit): string => {
|
||||
if (!limit || !limit.enabled || limit.value <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Convert to KiB/s for restic compatibility, or use suffixed format for rclone
|
||||
let kibibytesPerSecond: number;
|
||||
switch (limit.unit) {
|
||||
case "Kbps":
|
||||
|
|
@ -924,11 +905,15 @@ const formatBandwidthLimit = (limit?: BandwidthLimit): string => {
|
|||
return "";
|
||||
}
|
||||
|
||||
// Return as integer KiB/s for restic
|
||||
return `${Math.floor(kibibytesPerSecond)}`;
|
||||
};
|
||||
|
||||
export const addCommonArgs = (args: string[], env: Record<string, string>, config?: RepositoryConfig, options?: { skipBandwidth?: boolean }) => {
|
||||
export const addCommonArgs = (
|
||||
args: string[],
|
||||
env: Record<string, string>,
|
||||
config?: RepositoryConfig,
|
||||
options?: { skipBandwidth?: boolean },
|
||||
) => {
|
||||
args.push("--json");
|
||||
|
||||
if (env._SFTP_SSH_ARGS) {
|
||||
|
|
@ -943,37 +928,15 @@ export const addCommonArgs = (args: string[], env: Record<string, string>, confi
|
|||
args.push("--cacert", env.RESTIC_CACERT);
|
||||
}
|
||||
|
||||
// Add bandwidth limits if configuration is provided and not skipped
|
||||
if (config && !options?.skipBandwidth) {
|
||||
if (config.backend === "rclone") {
|
||||
// For rclone backends, consolidate both upload and download limits into one bwlimit
|
||||
const uploadLimit = formatBandwidthLimit(config.uploadLimit);
|
||||
const downloadLimit = formatBandwidthLimit(config.downloadLimit);
|
||||
const uploadLimit = formatBandwidthLimit(config.uploadLimit);
|
||||
if (uploadLimit) {
|
||||
args.push("--limit-upload", uploadLimit);
|
||||
}
|
||||
|
||||
// Determine effective limit (choose more restrictive when both exist)
|
||||
let effectiveLimit = "";
|
||||
if (uploadLimit && downloadLimit) {
|
||||
effectiveLimit = parseInt(uploadLimit, 10) < parseInt(downloadLimit, 10) ? uploadLimit : downloadLimit;
|
||||
} else if (uploadLimit) {
|
||||
effectiveLimit = uploadLimit;
|
||||
} else if (downloadLimit) {
|
||||
effectiveLimit = downloadLimit;
|
||||
}
|
||||
|
||||
if (effectiveLimit) {
|
||||
args.push("-o", `rclone.bwlimit=${effectiveLimit}`);
|
||||
}
|
||||
} else {
|
||||
// For restic backends, handle upload and download limits separately
|
||||
const uploadLimit = formatBandwidthLimit(config.uploadLimit);
|
||||
if (uploadLimit) {
|
||||
args.push("--limit-upload", uploadLimit);
|
||||
}
|
||||
|
||||
const downloadLimit = formatBandwidthLimit(config.downloadLimit);
|
||||
if (downloadLimit) {
|
||||
args.push("--limit-download", downloadLimit);
|
||||
}
|
||||
const downloadLimit = formatBandwidthLimit(config.downloadLimit);
|
||||
if (downloadLimit) {
|
||||
args.push("--limit-download", downloadLimit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -995,26 +958,17 @@ export const restic = {
|
|||
copy,
|
||||
};
|
||||
|
||||
// Helper function to clean up temporary files
|
||||
export const cleanupTemporaryKeys = async (env: Record<string, string>) => {
|
||||
const keysToClean = ["_SFTP_KEY_PATH", "_SFTP_KNOWN_HOSTS_PATH", "RESTIC_CACERT", "GOOGLE_APPLICATION_CREDENTIALS"];
|
||||
|
||||
for (const key of keysToClean) {
|
||||
if (env[key]) {
|
||||
try {
|
||||
await fs.unlink(env[key]);
|
||||
} catch (_error) {
|
||||
// Ignore errors when cleaning up temporary files
|
||||
}
|
||||
await fs.unlink(env[key]).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up custom password files
|
||||
if (env.RESTIC_PASSWORD_FILE && env.RESTIC_PASSWORD_FILE !== RESTIC_PASS_FILE) {
|
||||
try {
|
||||
await fs.unlink(env.RESTIC_PASSWORD_FILE);
|
||||
} catch (_error) {
|
||||
// Ignore errors when cleaning up temporary files
|
||||
}
|
||||
await fs.unlink(env.RESTIC_PASSWORD_FILE).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue