diff --git a/SqlDatabaseVectorSearch/Components/Pages/Documents.razor b/SqlDatabaseVectorSearch/Components/Pages/Documents.razor index 1308ad1..475c24a 100644 --- a/SqlDatabaseVectorSearch/Components/Pages/Documents.razor +++ b/SqlDatabaseVectorSearch/Components/Pages/Documents.razor @@ -22,12 +22,13 @@
- + - +
+
@@ -188,14 +189,13 @@ else try { - await using var inputStream = Model.File.OpenReadStream(20 * 1024 * 1024); // 20 MB + await using var inputStream = Model.File.OpenReadStream(UploadDocument.MaxFileSize); await using var stream = await inputStream.GetMemoryStreamAsync(); await using var scope = ServiceScopeFactory.CreateAsyncScope(); var vectorSearchService = scope.ServiceProvider.GetRequiredService(); - var documentId = string.IsNullOrWhiteSpace(Model.DocumentId) ? null : (Guid?)Guid.Parse(Model.DocumentId); - await vectorSearchService.ImportAsync(EmbeddingRequest.Create(stream, fileName, documentId)); + await vectorSearchService.ImportAsync(EmbeddingRequest.Create(stream, fileName, Model.GetDocumentId())); ToastService.Notify(await CreateToastMessageAsync(ToastType.Success, "Upload document", $"The document '{fileName}' has been successfully uploaded and indexed.")); @@ -219,7 +219,7 @@ else private async Task DeleteSelectedDocuments() { - var selectedDocumentIds = documents?.Where(d => d.IsSelected).Select(d => d.Id) ?? []; + var selectedDocumentIds = documents.Where(d => d.IsSelected).Select(d => d.Id); var options = new ConfirmDialogOptions { @@ -289,11 +289,46 @@ else public string LocalCreationDateString { get; set; } = string.Empty; } - public class UploadDocument + public class UploadDocument : IValidatableObject { + public const int MaxFileSizeInMegaBytes = 20; + + public const long MaxFileSize = MaxFileSizeInMegaBytes * 1024 * 1024; + + public static readonly string[] SupportedExtensions = [".pdf", ".docx", ".txt", ".md"]; + + public static string AcceptedFileTypes { get; } = string.Join(',', SupportedExtensions); + + public static string SupportedExtensionsDescription { get; } = string.Join(", ", SupportedExtensions.Select(e => e.TrimStart('.').ToUpperInvariant())); + public IBrowserFile? File { get; set; } - [RegularExpression(@"^(\{|\()?[0-9a-fA-F]{8}(-?)[0-9a-fA-F]{4}(-?)[0-9a-fA-F]{4}(-?)[0-9a-fA-F]{4}(-?)[0-9a-fA-F]{12}(\}|\))?$", ErrorMessage = "Invalid GUID format.")] public string? DocumentId { get; set; } + + public Guid? GetDocumentId() + => Guid.TryParse(DocumentId, out var documentId) ? documentId : null; + + public IEnumerable Validate(ValidationContext validationContext) + { + if (!string.IsNullOrWhiteSpace(DocumentId) && !Guid.TryParse(DocumentId, out _)) + { + yield return new ValidationResult("Invalid GUID format.", [nameof(DocumentId)]); + } + + if (File is null) + { + yield break; + } + + if (!SupportedExtensions.Contains(Path.GetExtension(File.Name), StringComparer.OrdinalIgnoreCase)) + { + yield return new ValidationResult($"Only {SupportedExtensionsDescription} files are supported.", [nameof(File)]); + } + + if (File.Size > MaxFileSize) + { + yield return new ValidationResult($"The file exceeds the maximum allowed size of {MaxFileSizeInMegaBytes} MB.", [nameof(File)]); + } + } } }