diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs index 1ddb460c..a911f62b 100644 --- a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs +++ b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs @@ -1,6 +1,7 @@ using AIStudio.Dialogs; using AIStudio.Tools.Rust; using AIStudio.Tools.Services; +using AIStudio.Tools.Validation; using Microsoft.AspNetCore.Components; @@ -91,7 +92,7 @@ public partial class AttachDocuments : MSGComponentBase foreach (var path in paths) { - if(!await this.IsFileExtensionValid(path)) + if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(path)) continue; this.DocumentPaths.Add(path); @@ -133,7 +134,7 @@ public partial class AttachDocuments : MSGComponentBase if (!File.Exists(selectedFile.SelectedFilePath)) return; - if (!await this.IsFileExtensionValid(selectedFile.SelectedFilePath)) + if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath)) return; this.DocumentPaths.Add(selectedFile.SelectedFilePath); @@ -141,36 +142,6 @@ public partial class AttachDocuments : MSGComponentBase await this.OnChange(this.DocumentPaths); } - private async Task IsFileExtensionValid(string selectedFile) - { - var ext = Path.GetExtension(selectedFile).TrimStart('.'); - if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, this.T("Executables are not allowed"))); - return false; - } - - if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, this.T("Images are not supported yet"))); - return false; - } - - if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet"))); - return false; - } - - if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.AudioFile, this.T("Audio files are not supported yet"))); - return false; - } - - return true; - } - private async Task ClearAllFiles() { this.DocumentPaths.Clear(); diff --git a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs index 422ad70e..cd970cd4 100644 --- a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs +++ b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs @@ -1,5 +1,5 @@ -using AIStudio.Tools.Rust; using AIStudio.Tools.Services; +using AIStudio.Tools.Validation; using Microsoft.AspNetCore.Components; @@ -55,35 +55,12 @@ public partial class ReadFileContent : MSGComponentBase return; } - var ext = Path.GetExtension(selectedFile.SelectedFilePath).TrimStart('.'); - if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath)) { - this.Logger.LogWarning("User attempted to load executable file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, T("Executables are not allowed"))); + this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath); return; } - if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - this.Logger.LogWarning("User attempted to load image file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, T("Images are not supported yet"))); - return; - } - - if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - this.Logger.LogWarning("User attempted to load video file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet"))); - return; - } - - if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - this.Logger.LogWarning("User attempted to load audio file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.AudioFile, this.T("Audio files are not supported yet"))); - return; - } - try { var fileContent = await UserFile.LoadFileData(selectedFile.SelectedFilePath, this.RustService, this.DialogService); diff --git a/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs new file mode 100644 index 00000000..cc40c959 --- /dev/null +++ b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs @@ -0,0 +1,55 @@ +using AIStudio.Tools.PluginSystem; +using AIStudio.Tools.Rust; + +namespace AIStudio.Tools.Validation; + +/// +/// Provides centralized validation for file extensions. +/// +public static class FileExtensionValidation +{ + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileExtensionValidation).Namespace, nameof(FileExtensionValidation)); + + /// + /// Validates the file extension and sends appropriate MessageBus notifications when invalid. + /// + /// The file path to validate. + /// True if valid, false if invalid (error/warning already sent via MessageBus). + public static async Task IsExtensionValidWithNotifyAsync(string filePath) + { + var ext = Path.GetExtension(filePath).TrimStart('.'); + if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendError(new( + Icons.Material.Filled.AppBlocking, + TB("Executables are not allowed"))); + return false; + } + + if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.ImageNotSupported, + TB("Images are not supported yet"))); + return false; + } + + if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.FeaturedVideo, + TB("Videos are not supported yet"))); + return false; + } + + if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.AudioFile, + TB("Audio files are not supported yet"))); + return false; + } + + return true; + } +}