feat: Add support for Backblaze B2 and Wasabi cloud storage

- Introduced new source and destination forms for Backblaze B2 and Wasabi, allowing users to configure their cloud storage settings.
- Updated the configuration generation logic to handle B2 and Wasabi, including necessary parameters such as access keys, bucket names, and endpoints.
- Enhanced the user interface to provide clear instructions and security notes for both storage options, improving user experience and security awareness.
This commit is contained in:
StarFleetCPTN
2025-04-08 17:30:35 -07:00
parent d650247817
commit 11f8631929
8 changed files with 571 additions and 4 deletions
+41
View File
@@ -514,6 +514,14 @@ templ ConfigForm(ctx context.Context, data ConfigFormData) {
@source.S3SourceForm()
</template>
<template x-if="sourceType === 'b2'">
@source.B2SourceForm()
</template>
<template x-if="sourceType === 'wasabi'">
@source.WasabiSourceForm()
</template>
<template x-if="sourceType === 'minio'">
@source.MinIOSourceForm()
</template>
@@ -589,6 +597,14 @@ templ ConfigForm(ctx context.Context, data ConfigFormData) {
@destination.S3DestinationForm()
</template>
<template x-if="destinationType === 'b2'">
@destination.B2DestinationForm()
</template>
<template x-if="destinationType === 'wasabi'">
@destination.WasabiDestinationForm()
</template>
<template x-if="destinationType === 'minio'">
@destination.MinIODestinationForm()
</template>
@@ -626,6 +642,31 @@ templ ConfigForm(ctx context.Context, data ConfigFormData) {
@common.ArchiveOptions()
</div>
<div class="mb-6">
<label for="destination_path" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
<span>Destination Path</span>
<span x-show="destinationType === 's3' || destinationType === 'b2' || destinationType === 'wasabi' || destinationType === 'minio'" class="text-xs text-gray-500">(Optional)</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-folder text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="destination_path" name="destination_path" x-model="destinationPath"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
:placeholder="getDestinationPathPlaceholder()"
x-bind:required="requiresDestination && (destinationType !== 's3' && destinationType !== 'b2' && destinationType !== 'wasabi' && destinationType !== 'minio')" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
<span x-show="destinationType === 'local'">Directory where files will be saved</span>
<span x-show="destinationType === 'sftp' || destinationType === 'ftp'">Remote path on the server</span>
<span x-show="destinationType === 's3' || destinationType === 'b2' || destinationType === 'wasabi' || destinationType === 'minio'">Path within bucket. Leave empty for bucket root.</span>
<span x-show="destinationType === 'smb'">Share directory path</span>
<span x-show="destinationType === 'nextcloud' || destinationType === 'webdav'">Path on remote server</span>
<span x-show="destinationType === 'gdrive'">Folder in Google Drive</span>
<span x-show="destinationType === 'gphotos'">Album name in Google Photos</span>
</p>
</div>
</div>
+4
View File
@@ -195,6 +195,8 @@ templ SourceSelection() {
<option value="sftp">SFTP</option>
<option value="ftp">FTP</option>
<option value="s3">S3</option>
<option value="b2">Backblaze B2</option>
<option value="wasabi">Wasabi</option>
<option value="minio">MinIO</option>
<option value="smb">SMB</option>
<option value="nextcloud">NextCloud</option>
@@ -219,6 +221,8 @@ templ DestinationSelection() {
<option value="sftp">SFTP</option>
<option value="ftp">FTP</option>
<option value="s3">S3</option>
<option value="b2">Backblaze B2</option>
<option value="wasabi">Wasabi</option>
<option value="minio">MinIO</option>
<option value="smb">SMB</option>
<option value="nextcloud">NextCloud</option>
+97
View File
@@ -0,0 +1,97 @@
package destination
templ B2DestinationForm() {
<div class="space-y-6 mt-4">
<div class="p-4 mb-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-blue-900/30 dark:text-blue-300" role="alert">
<div class="flex items-center">
<i class="fas fa-info-circle mr-2"></i>
<span>Configure your Backblaze B2 details below. You'll need your account ID, application key, and bucket information.</span>
</div>
</div>
<div class="mb-6">
<label for="dest_bucket" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Bucket Name</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-archive text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_bucket" name="dest_bucket" x-model="destBucket" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="my-b2-bucket" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Name of your Backblaze B2 bucket (case-sensitive)
</p>
</div>
<div class="mb-6">
<label for="dest_access_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Account ID</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-id-card text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_access_key" name="dest_access_key" x-model="destAccessKey" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="0123456789abcdef0123456789" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Backblaze B2 Account ID
</p>
</div>
<div class="mb-6">
<label for="dest_secret_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Application Key</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-lock text-gray-400 dark:text-gray-500"></i>
</div>
<input type="password" id="dest_secret_key" name="dest_secret_key" x-model="destSecretKey" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="K0123456789abcdef0123456789abcdef0123456789" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Backblaze B2 Application Key
</p>
</div>
<div class="mb-6">
<label for="dest_endpoint" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Endpoint (Optional)</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-server text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_endpoint" name="dest_endpoint" x-model="destEndpoint"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="s3.us-west-000.backblazeb2.com" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Custom endpoint URL (only needed for non-standard regions or private endpoints)
</p>
</div>
<div class="mb-6">
<label for="destination_path" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Path in Bucket</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-folder-open text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="destination_path" name="destination_path" x-model="destinationPath"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="path/to/files/" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Path prefix within the bucket (e.g., "backups/"). Leave empty to access the entire bucket.
</p>
</div>
<div class="p-4 mb-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300" role="alert">
<div class="flex">
<i class="fas fa-shield-alt mr-2 flex-shrink-0"></i>
<div>
<h3 class="font-medium">Security Note</h3>
<p class="mt-1">It's recommended to create an application key with restricted permissions for this configuration. The application key should only have access to the specific B2 bucket and operations needed.</p>
</div>
</div>
</div>
</div>
}
@@ -0,0 +1,112 @@
package destination
templ WasabiDestinationForm() {
<div class="space-y-6 mt-4">
<div class="p-4 mb-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-blue-900/30 dark:text-blue-300" role="alert">
<div class="flex items-center">
<i class="fas fa-info-circle mr-2"></i>
<span>Configure your Wasabi Cloud Storage details below. You'll need your access key, secret key, region, and bucket information.</span>
</div>
</div>
<div class="mb-6">
<label for="dest_region" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Wasabi Region</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-globe text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_region" name="dest_region" x-model="destRegion" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="us-east-1" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Wasabi region where your bucket is located (e.g., us-east-1, us-west-1, eu-central-1)
</p>
</div>
<div class="mb-6">
<label for="dest_bucket" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Bucket Name</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-archive text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_bucket" name="dest_bucket" x-model="destBucket" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="my-wasabi-bucket" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Name of your Wasabi bucket (case-sensitive)
</p>
</div>
<div class="mb-6">
<label for="dest_access_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Access Key</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-key text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_access_key" name="dest_access_key" x-model="destAccessKey" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="ABCDEFGHIJKLMNOPQRST" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Wasabi Access Key
</p>
</div>
<div class="mb-6">
<label for="dest_secret_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Secret Key</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-lock text-gray-400 dark:text-gray-500"></i>
</div>
<input type="password" id="dest_secret_key" name="dest_secret_key" x-model="destSecretKey" x-bind:required="requiresDestination"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="abcdefghijklmnopqrstuvwxyz1234567890ABCD" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Wasabi Secret Key
</p>
</div>
<div class="mb-6">
<label for="dest_endpoint" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Endpoint (Optional)</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-server text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="dest_endpoint" name="dest_endpoint" x-model="destEndpoint"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="s3.wasabisys.com" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Wasabi endpoint URL (usually s3.wasabisys.com or region-specific endpoints)
</p>
</div>
<div class="mb-6">
<label for="destination_path" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Path in Bucket</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-folder-open text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="destination_path" name="destination_path" x-model="destinationPath"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="path/to/files/" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Path prefix within the bucket (e.g., "backups/"). Leave empty to access the entire bucket.
</p>
</div>
<div class="p-4 mb-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300" role="alert">
<div class="flex">
<i class="fas fa-shield-alt mr-2 flex-shrink-0"></i>
<div>
<h3 class="font-medium">Security Note</h3>
<p class="mt-1">It's recommended to use an IAM user with restricted permissions for this configuration. The IAM user should only have access to the specific Wasabi bucket and operations needed.</p>
</div>
</div>
</div>
</div>
}
+97
View File
@@ -0,0 +1,97 @@
package source
templ B2SourceForm() {
<div class="space-y-6 mt-4">
<div class="p-4 mb-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-blue-900/30 dark:text-blue-300" role="alert">
<div class="flex items-center">
<i class="fas fa-info-circle mr-2"></i>
<span>Configure your Backblaze B2 details below. You'll need your account ID, application key, and bucket information.</span>
</div>
</div>
<div class="mb-6">
<label for="source_bucket" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Bucket Name</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-archive text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_bucket" name="source_bucket" x-model="sourceBucket" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="my-b2-bucket" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Name of your Backblaze B2 bucket (case-sensitive)
</p>
</div>
<div class="mb-6">
<label for="source_access_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Account ID</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-id-card text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_access_key" name="source_access_key" x-model="sourceAccessKey" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="0123456789abcdef0123456789" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Backblaze B2 Account ID
</p>
</div>
<div class="mb-6">
<label for="source_secret_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Application Key</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-lock text-gray-400 dark:text-gray-500"></i>
</div>
<input type="password" id="source_secret_key" name="source_secret_key" x-model="sourceSecretKey" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="K0123456789abcdef0123456789abcdef0123456789" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Backblaze B2 Application Key
</p>
</div>
<div class="mb-6">
<label for="source_endpoint" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Endpoint (Optional)</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-server text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_endpoint" name="source_endpoint" x-model="sourceEndpoint"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="s3.us-west-000.backblazeb2.com" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Custom endpoint URL (only needed for non-standard regions or private endpoints)
</p>
</div>
<div class="mb-6">
<label for="source_path" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Path in Bucket</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-folder-open text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_path" name="source_path" x-model="sourcePath"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="path/to/files/" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Path prefix within the bucket (e.g., "backups/"). Leave empty to access the entire bucket.
</p>
</div>
<div class="p-4 mb-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300" role="alert">
<div class="flex">
<i class="fas fa-shield-alt mr-2 flex-shrink-0"></i>
<div>
<h3 class="font-medium">Security Note</h3>
<p class="mt-1">It's recommended to create an application key with restricted permissions for this configuration. The application key should only have access to the specific B2 bucket and operations needed.</p>
</div>
</div>
</div>
</div>
}
+112
View File
@@ -0,0 +1,112 @@
package source
templ WasabiSourceForm() {
<div class="space-y-6 mt-4">
<div class="p-4 mb-4 text-sm text-blue-800 rounded-lg bg-blue-50 dark:bg-blue-900/30 dark:text-blue-300" role="alert">
<div class="flex items-center">
<i class="fas fa-info-circle mr-2"></i>
<span>Configure your Wasabi Cloud Storage details below. You'll need your access key, secret key, region, and bucket information.</span>
</div>
</div>
<div class="mb-6">
<label for="source_region" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Wasabi Region</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-globe text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_region" name="source_region" x-model="sourceRegion" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="us-east-1" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Wasabi region where your bucket is located (e.g., us-east-1, us-west-1, eu-central-1)
</p>
</div>
<div class="mb-6">
<label for="source_bucket" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Bucket Name</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-archive text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_bucket" name="source_bucket" x-model="sourceBucket" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="my-wasabi-bucket" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Name of your Wasabi bucket (case-sensitive)
</p>
</div>
<div class="mb-6">
<label for="source_access_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Access Key</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-key text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_access_key" name="source_access_key" x-model="sourceAccessKey" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="ABCDEFGHIJKLMNOPQRST" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Wasabi Access Key
</p>
</div>
<div class="mb-6">
<label for="source_secret_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Secret Key</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-lock text-gray-400 dark:text-gray-500"></i>
</div>
<input type="password" id="source_secret_key" name="source_secret_key" x-model="sourceSecretKey" required
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="abcdefghijklmnopqrstuvwxyz1234567890ABCD" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Your Wasabi Secret Key
</p>
</div>
<div class="mb-6">
<label for="source_endpoint" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Endpoint (Optional)</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-server text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_endpoint" name="source_endpoint" x-model="sourceEndpoint"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="s3.wasabisys.com" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Wasabi endpoint URL (usually s3.wasabisys.com or region-specific endpoints)
</p>
</div>
<div class="mb-6">
<label for="source_path" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Path in Bucket</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3.5 pointer-events-none">
<i class="fas fa-folder-open text-gray-400 dark:text-gray-500"></i>
</div>
<input type="text" id="source_path" name="source_path" x-model="sourcePath"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="path/to/files/" />
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Path prefix within the bucket (e.g., "backups/"). Leave empty to access the entire bucket.
</p>
</div>
<div class="p-4 mb-4 text-sm text-yellow-800 rounded-lg bg-yellow-50 dark:bg-yellow-900/30 dark:text-yellow-300" role="alert">
<div class="flex">
<i class="fas fa-shield-alt mr-2 flex-shrink-0"></i>
<div>
<h3 class="font-medium">Security Note</h3>
<p class="mt-1">It's recommended to use an IAM user with restricted permissions for this configuration. The IAM user should only have access to the specific Wasabi bucket and operations needed.</p>
</div>
</div>
</div>
</div>
}
+74
View File
@@ -130,6 +130,43 @@ func (db *DB) GenerateRcloneConfig(config *TransferConfig) error {
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create source config (s3): %v\nOutput: %s", err, output)
}
case "wasabi":
args := []string{
"config", "create", sourceName, "s3",
"provider", "Wasabi",
"env_auth", "false",
"access_key_id", config.SourceAccessKey,
"secret_access_key", config.SourceSecretKey,
"region", config.SourceRegion,
"--non-interactive",
"--config", configPath,
"--log-level", "ERROR",
}
endpoint := config.SourceEndpoint
if endpoint == "" {
endpoint = "s3.wasabisys.com"
}
args = append(args, "endpoint", endpoint)
cmd := exec.Command(rclonePath, args...)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create source config (wasabi): %v\nOutput: %s", err, output)
}
case "b2":
args := []string{
"config", "create", sourceName, "b2",
"account", config.SourceAccessKey, // B2 Account ID
"key", config.SourceSecretKey, // B2 Application Key
"--non-interactive",
"--config", configPath,
"--log-level", "ERROR",
}
if config.SourceEndpoint != "" {
args = append(args, "endpoint", config.SourceEndpoint)
}
cmd := exec.Command(rclonePath, args...)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create source config (b2): %v\nOutput: %s", err, output)
}
case "minio":
args := []string{
"config", "create", sourceName, "s3",
@@ -247,6 +284,43 @@ func (db *DB) GenerateRcloneConfig(config *TransferConfig) error {
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create destination config (s3): %v\nOutput: %s", err, output)
}
case "wasabi":
args := []string{
"config", "create", destName, "s3",
"provider", "Wasabi",
"env_auth", "false",
"access_key_id", config.DestAccessKey,
"secret_access_key", config.DestSecretKey,
"region", config.DestRegion,
"--non-interactive",
"--config", configPath,
"--log-level", "ERROR",
}
endpoint := config.DestEndpoint
if endpoint == "" {
endpoint = "s3.wasabisys.com"
}
args = append(args, "endpoint", endpoint)
cmd := exec.Command(rclonePath, args...)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create destination config (wasabi): %v\nOutput: %s", err, output)
}
case "b2":
args := []string{
"config", "create", destName, "b2",
"account", config.DestAccessKey, // B2 Account ID
"key", config.DestSecretKey, // B2 Application Key
"--non-interactive",
"--config", configPath,
"--log-level", "ERROR",
}
if config.DestEndpoint != "" {
args = append(args, "endpoint", config.DestEndpoint)
}
cmd := exec.Command(rclonePath, args...)
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to create destination config (b2): %v\nOutput: %s", err, output)
}
case "minio":
args := []string{
"config", "create", destName, "s3",
+30
View File
@@ -130,6 +130,36 @@ func TestRcloneConnection(config db.TransferConfig, providerType string, dbInsta
if endpoint != "" {
createArgs = append(createArgs, "endpoint", endpoint)
}
case "wasabi":
createArgs = append(createArgs, "provider", "Wasabi", "env_auth", "false")
if accessKey != "" {
createArgs = append(createArgs, "access_key_id", accessKey)
}
if secretKey != "" {
createArgs = append(createArgs, "secret_access_key", secretKey)
}
if region != "" {
createArgs = append(createArgs, "region", region)
}
endpointValue := endpoint
if endpointValue == "" {
endpointValue = "s3.wasabisys.com"
}
createArgs = append(createArgs, "endpoint", endpointValue)
case "b2":
createArgs = append(createArgs, "provider", "B2", "env_auth", "false")
if accessKey != "" {
createArgs = append(createArgs, "account", accessKey)
}
if secretKey != "" {
createArgs = append(createArgs, "key", secretKey)
}
if endpoint != "" {
createArgs = append(createArgs, "endpoint", endpoint)
}
if region != "" {
createArgs = append(createArgs, "region", region)
}
case "minio":
createArgs = append(createArgs, "provider", "Minio", "env_auth", "false")
if accessKey != "" {