import React, { useState, useEffect } from 'react'; import { Box, VStack, HStack, Text, Heading, FormControl, FormLabel, Input, Button, useToast, Alert, AlertIcon, AlertTitle, AlertDescription, Card, CardBody, CardHeader, Spinner, Badge, Icon, Switch, FormHelperText, Select, Divider, } from '@chakra-ui/react'; import { FiSettings, FiZap, FiSave, FiCloud, FiServer } from 'react-icons/fi'; interface S3Config { provider: 's3'; endpoint: string; region: string; accessKeyId: string; secretAccessKey: string; bucketName: string; useSSL: boolean; } interface WebDAVConfig { provider: 'webdav'; url: string; username: string; password: string; basePath?: string; } type StorageConfig = S3Config | WebDAVConfig; interface TestResult { success: boolean; message: string; details?: any; provider?: string; } export const StorageConfiguration: React.FC = () => { const [config, setConfig] = useState({ provider: 's3', endpoint: '', region: 'us-east-1', accessKeyId: '', secretAccessKey: '', bucketName: '', useSSL: true, }); const [isLoading, setIsLoading] = useState(false); const [isTesting, setIsTesting] = useState(false); const [isSaving, setIsSaving] = useState(false); const [testResult, setTestResult] = useState(null); const [currentConfig, setCurrentConfig] = useState(null); const toast = useToast(); // Load current configuration on component mount useEffect(() => { loadCurrentConfig(); }, []); const loadCurrentConfig = async () => { setIsLoading(true); try { const response = await fetch('/api/config/storage'); if (response.ok) { const data = await response.json(); setCurrentConfig(data.config); // Handle masked passwords - don't set masked values as initial state const configWithEmptyPasswords = { ...data.config }; if (configWithEmptyPasswords.provider === 'webdav' && configWithEmptyPasswords.password === '***') { configWithEmptyPasswords.password = ''; } if (configWithEmptyPasswords.provider === 's3') { if (configWithEmptyPasswords.accessKeyId === '***') { configWithEmptyPasswords.accessKeyId = ''; } if (configWithEmptyPasswords.secretAccessKey === '***') { configWithEmptyPasswords.secretAccessKey = ''; } } setConfig(configWithEmptyPasswords); } } catch (error) { console.error('Error loading storage config:', error); } finally { setIsLoading(false); } }; const handleProviderChange = (provider: 's3' | 'webdav') => { if (provider === 's3') { setConfig({ provider: 's3', endpoint: '', region: 'us-east-1', accessKeyId: '', secretAccessKey: '', bucketName: '', useSSL: true, }); } else { setConfig({ provider: 'webdav', url: '', username: '', password: '', basePath: '/music-files', }); } }; const handleInputChange = (field: string, value: string | boolean) => { setConfig(prev => ({ ...prev, [field]: value, })); }; const testConnection = async () => { setIsTesting(true); setTestResult(null); try { // For testing, merge current form state with existing config to preserve passwords const testConfig = { ...currentConfig, ...config }; const response = await fetch('/api/config/storage/test', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(testConfig), }); const result = await response.json(); if (response.ok) { setTestResult({ success: true, message: 'Connection successful!', details: result, provider: testConfig.provider, }); toast({ title: 'Connection Test Successful', description: `${testConfig.provider.toUpperCase()} connection is working properly`, status: 'success', duration: 5000, isClosable: true, }); } else { setTestResult({ success: false, message: result.error || 'Connection failed', details: result, provider: testConfig.provider, }); toast({ title: 'Connection Test Failed', description: result.error || `Failed to connect to ${testConfig.provider.toUpperCase()}`, status: 'error', duration: 5000, isClosable: true, }); } } catch (error) { console.error('Error testing storage connection:', error); setTestResult({ success: false, message: 'Network error or server unavailable', provider: testConfig.provider, }); toast({ title: 'Connection Test Failed', description: 'Network error or server unavailable', status: 'error', duration: 5000, isClosable: true, }); } finally { setIsTesting(false); } }; const saveConfiguration = async () => { setIsSaving(true); try { // Always send the complete configuration const configToSave = { ...config }; const response = await fetch('/api/config/storage', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(configToSave), }); if (response.ok) { setCurrentConfig(config); toast({ title: 'Configuration Saved', description: `${config.provider.toUpperCase()} configuration has been saved successfully`, status: 'success', duration: 3000, isClosable: true, }); } else { const error = await response.json(); throw new Error(error.error || 'Failed to save configuration'); } } catch (error) { console.error('Error saving storage config:', error); toast({ title: 'Save Failed', description: error instanceof Error ? error.message : 'Failed to save configuration', status: 'error', duration: 5000, isClosable: true, }); } finally { setIsSaving(false); } }; const hasChanges = () => { if (!currentConfig) return true; // Create a copy of currentConfig with masked values replaced with empty strings for comparison const normalizedCurrentConfig = { ...currentConfig }; if (normalizedCurrentConfig.provider === 'webdav' && normalizedCurrentConfig.password === '***') { normalizedCurrentConfig.password = ''; } if (normalizedCurrentConfig.provider === 's3') { if (normalizedCurrentConfig.accessKeyId === '***') { normalizedCurrentConfig.accessKeyId = ''; } if (normalizedCurrentConfig.secretAccessKey === '***') { normalizedCurrentConfig.secretAccessKey = ''; } } return JSON.stringify(config) !== JSON.stringify(normalizedCurrentConfig); }; const renderS3Config = () => ( {/* Endpoint */} S3 Endpoint handleInputChange('endpoint', e.target.value)} placeholder="https://s3.amazonaws.com or http://localhost:9000 for MinIO" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> For AWS S3, use: https://s3.amazonaws.com. For MinIO: http://localhost:9000 {/* Region */} Region handleInputChange('region', e.target.value)} placeholder="us-east-1" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> AWS region (e.g., us-east-1, eu-west-1) or 'us-east-1' for MinIO {/* Access Key */} Access Key ID handleInputChange('accessKeyId', e.target.value)} placeholder="Your S3 access key" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> {/* Secret Key */} Secret Access Key handleInputChange('secretAccessKey', e.target.value)} placeholder="Your S3 secret key" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> {/* Bucket Name */} Bucket Name handleInputChange('bucketName', e.target.value)} placeholder="music-files" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> The S3 bucket where music files will be stored {/* Use SSL */} Use SSL/TLS handleInputChange('useSSL', e.target.checked)} colorScheme="blue" /> Enable for HTTPS connections (recommended for production) ); const renderWebDAVConfig = () => ( {/* URL */} WebDAV URL handleInputChange('url', e.target.value)} placeholder="https://your-nextcloud.com/remote.php/dav/files/username/" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> Your Nextcloud WebDAV URL (usually ends with /remote.php/dav/files/username/) {/* Username */} Username handleInputChange('username', e.target.value)} placeholder="Your Nextcloud username" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> {/* Password */} Password handleInputChange('password', e.target.value)} placeholder="Your Nextcloud password or app password" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> Use your Nextcloud password or create an app password for better security {/* Base Path */} Base Path (Optional) handleInputChange('basePath', e.target.value)} placeholder="/music-files" bg="gray.700" borderColor="gray.600" color="white" _placeholder={{ color: 'gray.400' }} _focus={{ borderColor: 'blue.400', boxShadow: 'none' }} /> Subfolder within your WebDAV storage where music files will be stored ); if (isLoading) { return ( Loading storage configuration... ); } return ( {/* Header */} Storage Configuration Configure your storage provider for music file storage and playback. Choose between S3-compatible storage or WebDAV (Nextcloud). {/* Provider Selection */} Storage Provider Select Storage Provider {config.provider === 's3' ? 'S3-compatible storage for scalable cloud storage' : 'WebDAV for self-hosted solutions like Nextcloud' } {/* Configuration Form */} {config.provider === 's3' ? 'S3 Configuration' : 'WebDAV Configuration'} {config.provider === 's3' ? renderS3Config() : renderWebDAVConfig()} {/* Test Connection */} Test Connection Test your {config.provider.toUpperCase()} configuration to ensure it's working properly before saving. {testResult && ( {testResult.success ? 'Connection Successful' : 'Connection Failed'} {testResult.message} {testResult.details && ( Details: {JSON.stringify(testResult.details, null, 2)} )} )} {/* Save Configuration */} Save Configuration Save your {config.provider.toUpperCase()} configuration to use it for music file storage and playback. {currentConfig && ( Configuration Loaded )} {!hasChanges() && currentConfig && ( No changes to save )} {/* Help Section */} Configuration Help {/* S3 Help */} S3-Compatible Storage For AWS S3: • Endpoint: https://s3.amazonaws.com
• Region: Your AWS region (e.g., us-east-1)
• Access Key: Your AWS access key
• Secret Key: Your AWS secret key
• Bucket: Your S3 bucket name
For MinIO (Local Development): • Endpoint: http://localhost:9000
• Region: us-east-1
• Access Key: minioadmin
• Secret Key: minioadmin
• Bucket: Create a bucket named 'music-files'
{/* WebDAV Help */} WebDAV (Nextcloud/ownCloud) For Nextcloud: • URL: https://your-nextcloud.com/remote.php/dav/files/username/
• Username: Your Nextcloud username
• Password: Your Nextcloud password or app password
• Base Path: /music-files (optional subfolder)
For ownCloud: • URL: https://your-owncloud.com/remote.php/dav/files/username/
• Username: Your ownCloud username
• Password: Your ownCloud password or app password
• Base Path: /music-files (optional subfolder)
Note: For better security, create an app password in your Nextcloud/ownCloud settings instead of using your main password.
); };