mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
- Added fields for pre-rendering flags in the config form, allowing for better user experience during edits. - Updated Rclone command options to accept current command ID for pre-selection. - Introduced region input fields for MinIO source and destination forms, enhancing configuration flexibility. - Modified backend logic to handle region parameters when generating Rclone configurations. - Improved flag handling by parsing selected flags and values for better state management in the UI.
641 lines
22 KiB
Templ
641 lines
22 KiB
Templ
package components
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/starfleetcptn/gomft/internal/db"
|
|
"context"
|
|
"github.com/starfleetcptn/gomft/components/providers/source"
|
|
"github.com/starfleetcptn/gomft/components/providers/destination"
|
|
"github.com/starfleetcptn/gomft/components/providers/common"
|
|
)
|
|
|
|
type ConfigFormData struct {
|
|
Config *db.TransferConfig
|
|
IsNew bool
|
|
// Fields for pre-rendering flags on edit
|
|
InitialCommand *db.RcloneCommand
|
|
SelectedFlagsMap map[uint]bool
|
|
SelectedFlagValues map[uint]string
|
|
}
|
|
|
|
func getConfigFormTitle(isNew bool) string {
|
|
if isNew {
|
|
return "New Configuration"
|
|
}
|
|
return "Edit Configuration"
|
|
}
|
|
|
|
func getInitialData(config *db.TransferConfig) string {
|
|
// Default values for a new configuration
|
|
name := ""
|
|
sourceType := "local"
|
|
sourcePath := ""
|
|
sourceHost := ""
|
|
sourcePort := 0 // Initialize to 0 to trigger default setting
|
|
sourceUser := ""
|
|
sourcePassword := ""
|
|
sourceKeyFile := ""
|
|
sourceAuthType := "password"
|
|
sourceBucket := ""
|
|
sourceRegion := ""
|
|
sourceAccessKey := ""
|
|
sourceSecretKey := ""
|
|
sourceEndpoint := ""
|
|
sourceShare := ""
|
|
sourceDomain := ""
|
|
sourcePassiveMode := false
|
|
sourceClientId := ""
|
|
sourceClientSecret := ""
|
|
sourceDriveId := ""
|
|
sourceTeamDrive := ""
|
|
// Google Photos source fields
|
|
sourceReadOnly := false
|
|
sourceStartYear := 2025
|
|
sourceIncludeArchived := false
|
|
|
|
filePattern := ""
|
|
outputPattern := "${filename}"
|
|
|
|
destinationType := "local"
|
|
destinationPath := ""
|
|
destHost := ""
|
|
destPort := 0 // Initialize to 0 to trigger default setting
|
|
destUser := ""
|
|
destPassword := ""
|
|
destKeyFile := ""
|
|
destAuthType := "password"
|
|
destBucket := ""
|
|
destRegion := ""
|
|
destAccessKey := ""
|
|
destSecretKey := ""
|
|
destEndpoint := ""
|
|
destShare := ""
|
|
destDomain := ""
|
|
destPassiveMode := false
|
|
destClientId := ""
|
|
destClientSecret := ""
|
|
destDriveId := ""
|
|
destTeamDrive := ""
|
|
// Google Photos destination fields
|
|
destReadOnly := false
|
|
destStartYear := 2025
|
|
destIncludeArchived := false
|
|
|
|
archivePath := ""
|
|
archiveEnabled := false
|
|
deleteAfterTransfer := false
|
|
skipProcessedFiles := true
|
|
maxConcurrentTransfers := 4
|
|
rcloneFlags := ""
|
|
commandId := uint(1) // Default to 'copy' command
|
|
commandFlags := ""
|
|
useBuiltinAuthSource := true
|
|
useBuiltinAuthDest := true
|
|
|
|
// If editing an existing config, populate with those values
|
|
if config != nil {
|
|
name = config.Name
|
|
sourceType = config.SourceType
|
|
sourcePath = config.SourcePath
|
|
sourceHost = config.SourceHost
|
|
sourcePort = config.SourcePort
|
|
sourceUser = config.SourceUser
|
|
sourcePassword = config.SourcePassword
|
|
sourceKeyFile = config.SourceKeyFile
|
|
if sourceKeyFile != "" && sourcePassword == "" {
|
|
sourceAuthType = "key"
|
|
}
|
|
sourceBucket = config.SourceBucket
|
|
sourceRegion = config.SourceRegion
|
|
sourceAccessKey = config.SourceAccessKey
|
|
sourceSecretKey = config.SourceSecretKey
|
|
sourceEndpoint = config.SourceEndpoint
|
|
sourceShare = config.SourceShare
|
|
sourceDomain = config.SourceDomain
|
|
sourcePassiveMode = config.GetSourcePassiveMode()
|
|
sourceClientId = config.SourceClientID
|
|
sourceClientSecret = config.SourceClientSecret
|
|
sourceDriveId = config.SourceDriveID
|
|
sourceTeamDrive = config.SourceTeamDrive
|
|
|
|
// Google Photos source fields
|
|
if config.SourceReadOnly != nil {
|
|
sourceReadOnly = *config.SourceReadOnly
|
|
}
|
|
// sourceStartYear = config.SourceStartYear
|
|
if config.SourceIncludeArchived != nil {
|
|
sourceIncludeArchived = *config.SourceIncludeArchived
|
|
}
|
|
|
|
filePattern = config.FilePattern
|
|
outputPattern = config.OutputPattern
|
|
|
|
destinationType = config.DestinationType
|
|
destinationPath = config.DestinationPath
|
|
destHost = config.DestHost
|
|
destPort = config.DestPort
|
|
destUser = config.DestUser
|
|
destPassword = config.DestPassword
|
|
destKeyFile = config.DestKeyFile
|
|
if destKeyFile != "" && destPassword == "" {
|
|
destAuthType = "key"
|
|
}
|
|
destBucket = config.DestBucket
|
|
destRegion = config.DestRegion
|
|
destAccessKey = config.DestAccessKey
|
|
destSecretKey = config.DestSecretKey
|
|
destEndpoint = config.DestEndpoint
|
|
destShare = config.DestShare
|
|
destDomain = config.DestDomain
|
|
destPassiveMode = config.GetDestPassiveMode()
|
|
destClientId = config.DestClientID
|
|
destClientSecret = config.DestClientSecret
|
|
destDriveId = config.DestDriveID
|
|
destTeamDrive = config.DestTeamDrive
|
|
|
|
// Google Photos destination fields
|
|
if config.DestReadOnly != nil {
|
|
destReadOnly = *config.DestReadOnly
|
|
}
|
|
// destStartYear = config.DestStartYear
|
|
if config.DestIncludeArchived != nil {
|
|
destIncludeArchived = *config.DestIncludeArchived
|
|
}
|
|
|
|
archivePath = config.ArchivePath
|
|
archiveEnabled = config.GetArchiveEnabled()
|
|
deleteAfterTransfer = config.GetDeleteAfterTransfer()
|
|
skipProcessedFiles = config.GetSkipProcessedFiles()
|
|
maxConcurrentTransfers = config.MaxConcurrentTransfers
|
|
rcloneFlags = config.RcloneFlags
|
|
commandId = config.CommandID
|
|
commandFlags = config.CommandFlags
|
|
if config.UseBuiltinAuthSource != nil {
|
|
useBuiltinAuthSource = *config.UseBuiltinAuthSource
|
|
} else if sourceClientId != "" || sourceClientSecret != "" {
|
|
useBuiltinAuthSource = false
|
|
}
|
|
if config.UseBuiltinAuthDest != nil {
|
|
useBuiltinAuthDest = *config.UseBuiltinAuthDest
|
|
} else if destClientId != "" || destClientSecret != "" {
|
|
useBuiltinAuthDest = false
|
|
}
|
|
}
|
|
|
|
// Return the JSON-formatted string with all the data, add new path validation states
|
|
return fmt.Sprintf(`{
|
|
name: '%s',
|
|
sourceType: '%s',
|
|
sourcePath: '%s',
|
|
sourceHost: '%s',
|
|
sourcePort: %d,
|
|
sourceUser: '%s',
|
|
sourcePassword: '%s',
|
|
sourceKeyFile: '%s',
|
|
sourceAuthType: '%s',
|
|
sourceBucket: '%s',
|
|
sourceRegion: '%s',
|
|
sourceAccessKey: '%s',
|
|
sourceSecretKey: '%s',
|
|
sourceEndpoint: '%s',
|
|
sourceShare: '%s',
|
|
sourceDomain: '%s',
|
|
sourcePassiveMode: %v,
|
|
sourceClientId: '%s',
|
|
sourceClientSecret: '%s',
|
|
sourceDriveId: '%s',
|
|
sourceTeamDrive: '%s',
|
|
sourceReadOnly: %v,
|
|
sourceStartYear: %d,
|
|
sourceIncludeArchived: %v,
|
|
|
|
filePattern: '%s',
|
|
outputPattern: '%s',
|
|
|
|
destinationType: '%s',
|
|
destinationPath: '%s',
|
|
destHost: '%s',
|
|
destPort: %d,
|
|
destUser: '%s',
|
|
destPassword: '%s',
|
|
destKeyFile: '%s',
|
|
destAuthType: '%s',
|
|
destBucket: '%s',
|
|
destRegion: '%s',
|
|
destAccessKey: '%s',
|
|
destSecretKey: '%s',
|
|
destEndpoint: '%s',
|
|
destShare: '%s',
|
|
destDomain: '%s',
|
|
destPassiveMode: %v,
|
|
destClientId: '%s',
|
|
destClientSecret: '%s',
|
|
destDriveId: '%s',
|
|
destTeamDrive: '%s',
|
|
destReadOnly: %v,
|
|
destStartYear: %d,
|
|
destIncludeArchived: %v,
|
|
|
|
useBuiltinAuthSource: %v,
|
|
useBuiltinAuthDest: %v,
|
|
|
|
archivePath: '%s',
|
|
archiveEnabled: %v,
|
|
deleteAfterTransfer: %v,
|
|
skipProcessedFiles: %v,
|
|
maxConcurrentTransfers: %d,
|
|
rcloneFlags: '%s',
|
|
commandId: %d,
|
|
commandFlags: '%s',
|
|
|
|
// Path validation states
|
|
sourcePathValid: null,
|
|
sourcePathError: '',
|
|
destPathValid: null,
|
|
destPathError: '',
|
|
|
|
// Command configuration
|
|
requiresDestination: true,
|
|
|
|
// Methods for path validation
|
|
checkPath(path, type) {
|
|
if (!path) {
|
|
this[type + 'PathValid'] = false;
|
|
this[type + 'PathError'] = 'Path cannot be empty';
|
|
return;
|
|
}
|
|
|
|
fetch('/check-path?path=' + encodeURIComponent(path))
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
this[type + 'PathValid'] = data.valid;
|
|
this[type + 'PathError'] = data.error || '';
|
|
})
|
|
.catch(error => {
|
|
this[type + 'PathValid'] = false;
|
|
this[type + 'PathError'] = 'Error checking path: ' + error.message;
|
|
});
|
|
},
|
|
|
|
// Method to check if destination is required based on command type
|
|
updateCommandRequirements() {
|
|
// List of commands that don't require destination
|
|
const listingCommands = ['ls', 'lsd', 'lsl', 'lsf', 'lsjson', 'listremotes'];
|
|
const infoCommands = ['md5sum', 'sha1sum', 'size', 'version'];
|
|
const dirCommands = ['mkdir', 'rmdir', 'rmdirs'];
|
|
const destructiveCommands = ['delete', 'purge'];
|
|
const specialSinglePathCommands = ['obscure'];
|
|
|
|
// Get the command name from the command ID
|
|
// This will need coordination with your backend to ensure the IDs match the commands
|
|
let commandName = '';
|
|
switch(parseInt(this.commandId)) {
|
|
// Map command IDs to command names - adjust these based on your actual command IDs
|
|
case 1: commandName = 'copy'; break;
|
|
case 2: commandName = 'move'; break;
|
|
case 3: commandName = 'sync'; break;
|
|
case 4: commandName = 'ls'; break;
|
|
case 5: commandName = 'lsd'; break;
|
|
case 6: commandName = 'lsl'; break;
|
|
case 7: commandName = 'lsf'; break;
|
|
case 8: commandName = 'lsjson'; break;
|
|
case 9: commandName = 'md5sum'; break;
|
|
case 10: commandName = 'sha1sum'; break;
|
|
case 11: commandName = 'size'; break;
|
|
case 12: commandName = 'delete'; break;
|
|
case 13: commandName = 'purge'; break;
|
|
case 14: commandName = 'mkdir'; break;
|
|
case 15: commandName = 'rmdir'; break;
|
|
case 16: commandName = 'rmdirs'; break;
|
|
case 17: commandName = 'check'; break;
|
|
case 18: commandName = 'cleanup'; break;
|
|
case 19: commandName = 'dedupe'; break;
|
|
case 20: commandName = 'version'; break;
|
|
case 21: commandName = 'listremotes'; break;
|
|
case 22: commandName = 'cryptcheck'; break;
|
|
case 23: commandName = 'bisync'; break;
|
|
case 24: commandName = 'copyto'; break;
|
|
case 25: commandName = 'moveto'; break;
|
|
default: commandName = 'copy'; // Default to copy
|
|
}
|
|
|
|
console.log('Command ID:', this.commandId, 'Command Name:', commandName);
|
|
|
|
// Check if the command requires a destination
|
|
if (
|
|
listingCommands.includes(commandName) ||
|
|
infoCommands.includes(commandName) ||
|
|
(dirCommands.includes(commandName) && !this.rcloneFlags.includes('--dst')) ||
|
|
destructiveCommands.includes(commandName) ||
|
|
specialSinglePathCommands.includes(commandName) ||
|
|
commandName === 'version' ||
|
|
commandName === 'listremotes'
|
|
) {
|
|
this.requiresDestination = false;
|
|
console.log('Destination not required for command:', commandName);
|
|
} else {
|
|
this.requiresDestination = true;
|
|
console.log('Destination required for command:', commandName);
|
|
}
|
|
}
|
|
}`,
|
|
name, sourceType, sourcePath, sourceHost, sourcePort, sourceUser, sourcePassword, sourceKeyFile, sourceAuthType,
|
|
sourceBucket, sourceRegion, sourceAccessKey, sourceSecretKey, sourceEndpoint, sourceShare, sourceDomain, sourcePassiveMode,
|
|
sourceClientId, sourceClientSecret, sourceDriveId, sourceTeamDrive,
|
|
sourceReadOnly, sourceStartYear, sourceIncludeArchived,
|
|
filePattern, outputPattern,
|
|
destinationType, destinationPath, destHost, destPort, destUser, destPassword, destKeyFile, destAuthType,
|
|
destBucket, destRegion, destAccessKey, destSecretKey, destEndpoint, destShare, destDomain, destPassiveMode,
|
|
destClientId, destClientSecret, destDriveId, destTeamDrive,
|
|
destReadOnly, destStartYear, destIncludeArchived,
|
|
useBuiltinAuthSource, useBuiltinAuthDest,
|
|
archivePath, archiveEnabled, deleteAfterTransfer, skipProcessedFiles, maxConcurrentTransfers, rcloneFlags,
|
|
commandId, commandFlags)
|
|
}
|
|
|
|
templ ConfigForm(ctx context.Context, data ConfigFormData) {
|
|
@LayoutWithContext(getConfigFormTitle(data.IsNew), ctx) {
|
|
<!-- Main Content -->
|
|
<section class="py-8 px-4">
|
|
<div class="mx-auto max-w-3xl">
|
|
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm p-6 md:p-8">
|
|
<!-- Form Header -->
|
|
<div class="mb-8 text-center">
|
|
<div class="flex justify-center mb-4">
|
|
<span class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-blue-50 dark:bg-blue-900">
|
|
<i class="fas fa-cog text-blue-600 dark:text-blue-300 text-2xl"></i>
|
|
</span>
|
|
</div>
|
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
{ getConfigFormTitle(data.IsNew) }
|
|
</h1>
|
|
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
|
Configure your file transfer settings using the form below
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Main Form -->
|
|
<form
|
|
class="space-y-6"
|
|
if data.IsNew {
|
|
hx-post="/configs"
|
|
} else {
|
|
hx-post={ fmt.Sprintf("/configs/%d", data.Config.ID) }
|
|
}
|
|
hx-target="body"
|
|
hx-redirect="/configs"
|
|
hx-boost="true"
|
|
x-data={ getInitialData(data.Config) }
|
|
x-init="$nextTick(() => {
|
|
// Ensure initial form state displays correctly on load
|
|
sourceType = sourceType || 'local';
|
|
destinationType = destinationType || 'local';
|
|
|
|
// Set default ports based on connection type
|
|
if (sourcePort === 0 || !sourcePort) {
|
|
if (sourceType === 'sftp') {
|
|
sourcePort = 22;
|
|
} else if (sourceType === 'ftp') {
|
|
sourcePort = 21;
|
|
}
|
|
}
|
|
|
|
if (destPort === 0 || !destPort) {
|
|
if (destinationType === 'sftp') {
|
|
destPort = 22;
|
|
} else if (destinationType === 'ftp') {
|
|
destPort = 21;
|
|
}
|
|
}
|
|
|
|
// Initialize command requirements
|
|
updateCommandRequirements();
|
|
})"
|
|
x-effect="if (sourceType === 'sftp' && (sourcePort === 0 || sourcePort === 21)) {
|
|
sourcePort = 22;
|
|
console.log('Updating source port to 22 for SFTP');
|
|
} else if (sourceType === 'ftp' && (sourcePort === 0 || sourcePort === 22)) {
|
|
sourcePort = 21;
|
|
console.log('Updating source port to 21 for FTP');
|
|
}"
|
|
x-effect="if (destinationType === 'sftp' && (destPort === 0 || destPort === 21)) {
|
|
destPort = 22;
|
|
console.log('Updating destination port to 22 for SFTP');
|
|
} else if (destinationType === 'ftp' && (destPort === 0 || destPort === 22)) {
|
|
destPort = 21;
|
|
console.log('Updating destination port to 21 for FTP');
|
|
}"
|
|
>
|
|
|
|
<!-- Configuration Details Section -->
|
|
<div class="p-4 mb-4 bg-blue-50 border border-blue-100 rounded-lg dark:bg-blue-900/20 dark:border-blue-900">
|
|
<div class="flex items-center mb-2">
|
|
<i class="fas fa-info-circle text-blue-600 dark:text-blue-400 mr-2"></i>
|
|
<h3 class="text-lg font-medium text-blue-600 dark:text-blue-400">Configuration Details</h3>
|
|
</div>
|
|
<p class="text-sm text-blue-700 dark:text-blue-300">
|
|
Give your configuration a descriptive name and set up the source and destination locations.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Name field -->
|
|
<div class="mb-2">
|
|
<label for="name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Configuration Name</label>
|
|
<input
|
|
type="text"
|
|
id="name"
|
|
name="name"
|
|
x-model="name"
|
|
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 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 Transfer Configuration"
|
|
/>
|
|
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Choose a descriptive name to identify this configuration.</p>
|
|
</div>
|
|
|
|
<!-- Rclone Command Configuration Section -->
|
|
<div class="p-5 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
<h3 class="mb-4 text-xl font-bold text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-terminal mr-2 text-blue-500 dark:text-blue-400"></i>Command Configuration
|
|
</h3>
|
|
|
|
<!-- Container for flags, pre-rendered on edit, loaded via HTMX on new/change -->
|
|
<div id="command-flags-container" class="mt-4">
|
|
if !data.IsNew && data.InitialCommand != nil {
|
|
// Pre-render flags if editing and command data is available
|
|
@common.RcloneCommandFlagsContent(data.InitialCommand, data.SelectedFlagsMap, data.SelectedFlagValues)
|
|
}
|
|
</div>
|
|
|
|
<!-- Additonal Rclone Flags -->
|
|
@common.RcloneFlags(data.Config.CommandID) // Pass current command ID
|
|
|
|
</div>
|
|
|
|
<!-- Source Configuration Section -->
|
|
<div class="p-5 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
<h3 class="mb-4 text-xl font-bold text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-upload mr-2 text-blue-500 dark:text-blue-400"></i>Source Configuration
|
|
</h3>
|
|
|
|
<!-- Source selection -->
|
|
@common.SourceSelection()
|
|
|
|
<!-- Source type specific forms -->
|
|
<template x-if="sourceType === 'local'">
|
|
@source.LocalSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'sftp'">
|
|
@source.SFTPSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'ftp'">
|
|
@source.FTPSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 's3'">
|
|
@source.S3SourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'minio'">
|
|
@source.MinIOSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'smb'">
|
|
@source.SMBSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'webdav'">
|
|
@source.WebDAVSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'nextcloud'">
|
|
@source.NextCloudSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'gdrive'">
|
|
@source.GoogleDriveSourceForm()
|
|
</template>
|
|
|
|
<template x-if="sourceType === 'gphotos'">
|
|
@source.GooglePhotosSourceForm()
|
|
</template>
|
|
</div>
|
|
|
|
<!-- File Pattern Section -->
|
|
<div class="p-5 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
<h3 class="mb-4 text-xl font-bold text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-filter mr-2 text-blue-500 dark:text-blue-400"></i>File Patterns
|
|
</h3>
|
|
|
|
<!-- File pattern fields -->
|
|
@common.FilePatternFields()
|
|
</div>
|
|
|
|
<!-- Destination Configuration Section (only shown if required) -->
|
|
<div x-show="requiresDestination" x-transition class="p-5 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
<h3 class="mb-4 text-xl font-bold text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-download mr-2 text-blue-500 dark:text-blue-400"></i>Destination Configuration
|
|
</h3>
|
|
|
|
<!-- Destination selection -->
|
|
@common.DestinationSelection()
|
|
|
|
<!-- Destination type specific forms -->
|
|
<template x-if="destinationType === 'local'">
|
|
@destination.LocalDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'sftp'">
|
|
@destination.SFTPDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'ftp'">
|
|
@destination.FTPDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 's3'">
|
|
@destination.S3DestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'minio'">
|
|
@destination.MinIODestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'smb'">
|
|
@destination.SMBDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'nextcloud'">
|
|
@destination.NextCloudDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'webdav'">
|
|
@destination.WebDAVDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'gdrive'">
|
|
@destination.GoogleDriveDestinationForm()
|
|
</template>
|
|
|
|
<template x-if="destinationType === 'gphotos'">
|
|
@destination.GooglePhotosDestinationForm()
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Advanced Options Section -->
|
|
<div class="p-5 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
<h3 class="mb-4 text-xl font-bold text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-cogs mr-2 text-blue-500 dark:text-blue-400"></i>Advanced Options
|
|
</h3>
|
|
|
|
<!-- Archive options -->
|
|
<div class="mb-6">
|
|
<h4 class="text-lg font-medium text-gray-900 dark:text-white mb-4">Archive Settings</h4>
|
|
@common.ArchiveOptions()
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="flex items-center justify-between pt-6 border-t border-gray-200 dark:border-gray-700">
|
|
<a href="/configs"
|
|
class="text-white bg-gray-500 hover:bg-gray-600 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-gray-600 dark:hover:bg-gray-700 dark:focus:ring-gray-800">
|
|
<i class="fas fa-arrow-left mr-2"></i>Cancel
|
|
</a>
|
|
<button
|
|
type="submit"
|
|
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
|
|
<span class="flex items-center">
|
|
<i class="fas fa-save mr-2"></i>
|
|
if data.IsNew {
|
|
Create Configuration
|
|
} else {
|
|
Save Changes
|
|
}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Help Card -->
|
|
<div class="mt-4 p-4 bg-gray-50 border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
<div class="flex">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-lightbulb text-yellow-400 text-xl"></i>
|
|
</div>
|
|
<div class="ml-4">
|
|
<h5 class="text-sm font-medium text-gray-900 dark:text-white">Configuration Tips</h5>
|
|
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
Configure your file transfer settings carefully for optimal performance. Use file patterns to filter which files are transferred, and consider using archive options to keep track of processed files.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
}
|
|
} |