package storages import ( "context" "errors" "io" "log/slog" "github.com/google/uuid" azure_blob_storage "databasus-backend/internal/features/storages/models/azure_blob" ftp_storage "databasus-backend/internal/features/storages/models/ftp" google_drive_storage "databasus-backend/internal/features/storages/models/google_drive" local_storage "databasus-backend/internal/features/storages/models/local" nas_storage "databasus-backend/internal/features/storages/models/nas" rclone_storage "databasus-backend/internal/features/storages/models/rclone" s3_storage "databasus-backend/internal/features/storages/models/s3" sftp_storage "databasus-backend/internal/features/storages/models/sftp" "databasus-backend/internal/util/encryption" ) type Storage struct { ID uuid.UUID `json:"id" gorm:"column:id;primaryKey;type:uuid;default:gen_random_uuid()"` WorkspaceID uuid.UUID `json:"workspaceId" gorm:"column:workspace_id;not null;type:uuid;index"` Type StorageType `json:"type" gorm:"column:type;not null;type:text"` Name string `json:"name" gorm:"column:name;not null;type:text"` LastSaveError *string `json:"lastSaveError" gorm:"column:last_save_error;type:text"` // specific storage LocalStorage *local_storage.LocalStorage `json:"localStorage" gorm:"foreignKey:StorageID"` S3Storage *s3_storage.S3Storage `json:"s3Storage" gorm:"foreignKey:StorageID"` GoogleDriveStorage *google_drive_storage.GoogleDriveStorage `json:"googleDriveStorage" gorm:"foreignKey:StorageID"` NASStorage *nas_storage.NASStorage `json:"nasStorage" gorm:"foreignKey:StorageID"` AzureBlobStorage *azure_blob_storage.AzureBlobStorage `json:"azureBlobStorage" gorm:"foreignKey:StorageID"` FTPStorage *ftp_storage.FTPStorage `json:"ftpStorage" gorm:"foreignKey:StorageID"` SFTPStorage *sftp_storage.SFTPStorage `json:"sftpStorage" gorm:"foreignKey:StorageID"` RcloneStorage *rclone_storage.RcloneStorage `json:"rcloneStorage" gorm:"foreignKey:StorageID"` } func (s *Storage) SaveFile( ctx context.Context, encryptor encryption.FieldEncryptor, logger *slog.Logger, fileName string, file io.Reader, ) error { err := s.getSpecificStorage().SaveFile(ctx, encryptor, logger, fileName, file) if err != nil { lastSaveError := err.Error() s.LastSaveError = &lastSaveError return err } s.LastSaveError = nil return nil } func (s *Storage) GetFile( encryptor encryption.FieldEncryptor, fileName string, ) (io.ReadCloser, error) { return s.getSpecificStorage().GetFile(encryptor, fileName) } func (s *Storage) DeleteFile(encryptor encryption.FieldEncryptor, fileName string) error { return s.getSpecificStorage().DeleteFile(encryptor, fileName) } func (s *Storage) Validate(encryptor encryption.FieldEncryptor) error { if s.Type == "" { return errors.New("storage type is required") } if s.Name == "" { return errors.New("storage name is required") } return s.getSpecificStorage().Validate(encryptor) } func (s *Storage) TestConnection(encryptor encryption.FieldEncryptor) error { return s.getSpecificStorage().TestConnection(encryptor) } func (s *Storage) HideSensitiveData() { s.getSpecificStorage().HideSensitiveData() } func (s *Storage) EncryptSensitiveData(encryptor encryption.FieldEncryptor) error { return s.getSpecificStorage().EncryptSensitiveData(encryptor) } func (s *Storage) Update(incoming *Storage) { s.Name = incoming.Name s.Type = incoming.Type switch s.Type { case StorageTypeLocal: if s.LocalStorage != nil && incoming.LocalStorage != nil { s.LocalStorage.Update(incoming.LocalStorage) } else if incoming.LocalStorage != nil { s.LocalStorage = incoming.LocalStorage } case StorageTypeS3: if s.S3Storage != nil && incoming.S3Storage != nil { s.S3Storage.Update(incoming.S3Storage) } else if incoming.S3Storage != nil { s.S3Storage = incoming.S3Storage } case StorageTypeGoogleDrive: if s.GoogleDriveStorage != nil && incoming.GoogleDriveStorage != nil { s.GoogleDriveStorage.Update(incoming.GoogleDriveStorage) } else if incoming.GoogleDriveStorage != nil { s.GoogleDriveStorage = incoming.GoogleDriveStorage } case StorageTypeNAS: if s.NASStorage != nil && incoming.NASStorage != nil { s.NASStorage.Update(incoming.NASStorage) } else if incoming.NASStorage != nil { s.NASStorage = incoming.NASStorage } case StorageTypeAzureBlob: if s.AzureBlobStorage != nil && incoming.AzureBlobStorage != nil { s.AzureBlobStorage.Update(incoming.AzureBlobStorage) } else if incoming.AzureBlobStorage != nil { s.AzureBlobStorage = incoming.AzureBlobStorage } case StorageTypeFTP: if s.FTPStorage != nil && incoming.FTPStorage != nil { s.FTPStorage.Update(incoming.FTPStorage) } else if incoming.FTPStorage != nil { s.FTPStorage = incoming.FTPStorage } case StorageTypeSFTP: if s.SFTPStorage != nil && incoming.SFTPStorage != nil { s.SFTPStorage.Update(incoming.SFTPStorage) } else if incoming.SFTPStorage != nil { s.SFTPStorage = incoming.SFTPStorage } case StorageTypeRclone: if s.RcloneStorage != nil && incoming.RcloneStorage != nil { s.RcloneStorage.Update(incoming.RcloneStorage) } else if incoming.RcloneStorage != nil { s.RcloneStorage = incoming.RcloneStorage } } } func (s *Storage) getSpecificStorage() StorageFileSaver { switch s.Type { case StorageTypeLocal: return s.LocalStorage case StorageTypeS3: return s.S3Storage case StorageTypeGoogleDrive: return s.GoogleDriveStorage case StorageTypeNAS: return s.NASStorage case StorageTypeAzureBlob: return s.AzureBlobStorage case StorageTypeFTP: return s.FTPStorage case StorageTypeSFTP: return s.SFTPStorage case StorageTypeRclone: return s.RcloneStorage default: panic("invalid storage type: " + string(s.Type)) } }