diff --git a/DysonNetwork.Drive/Storage/CloudFile.cs b/DysonNetwork.Drive/Storage/CloudFile.cs index 7c8b358..d742547 100644 --- a/DysonNetwork.Drive/Storage/CloudFile.cs +++ b/DysonNetwork.Drive/Storage/CloudFile.cs @@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using DysonNetwork.Shared.Data; using DysonNetwork.Shared.Proto; -using Google.Protobuf; using NodaTime; using NodaTime.Serialization.Protobuf; @@ -13,7 +12,7 @@ public class CloudFile : ModelBase, ICloudFile, IIdentifiedResource { /// The id generated by TuS, basically just UUID remove the dash lines [MaxLength(32)] - public string Id { get; set; } = Guid.NewGuid().ToString(); + public string Id { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty); [MaxLength(1024)] public string Name { get; set; } = string.Empty; [MaxLength(4096)] public string? Description { get; set; } @@ -58,6 +57,10 @@ public class CloudFile : ModelBase, ICloudFile, IIdentifiedResource [MaxLength(4096)] public string? StorageUrl { get; set; } + [NotMapped] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? FastUploadLink { get; set; } + public Guid AccountId { get; set; } public CloudFileReferenceObject ToReferenceObject() @@ -138,4 +141,4 @@ public class CloudFileReference : ModelBase ExpiredAt = ExpiredAt?.ToTimestamp() }; } -} +} \ No newline at end of file diff --git a/DysonNetwork.Drive/Storage/FileController.cs b/DysonNetwork.Drive/Storage/FileController.cs index 9e72192..58799d3 100644 --- a/DysonNetwork.Drive/Storage/FileController.cs +++ b/DysonNetwork.Drive/Storage/FileController.cs @@ -258,4 +258,62 @@ public class FileController( var count = await fs.DeleteAllRecycledFilesAsync(); return Ok(new { Count = count }); } + + public class CreateFastFileRequest + { + public string Name { get; set; } = null!; + public long Size { get; set; } + public string Hash { get; set; } = null!; + public string? MimeType { get; set; } + public string? Description { get; set; } + public Dictionary? UserMeta { get; set; } + public Dictionary? FileMeta { get; set; } + public List? SensitiveMarks { get; set; } + public Guid PoolId { get; set; } + } + + [Authorize] + [HttpPost("fast")] + [RequiredPermission("global", "files.create")] + public async Task> CreateFastFile([FromBody] CreateFastFileRequest request) + { + if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); + var accountId = Guid.Parse(currentUser.Id); + + var pool = await db.Pools.FirstOrDefaultAsync(p => p.Id == request.PoolId); + if (pool is null) return BadRequest(); + if (!currentUser.IsSuperuser && pool.AccountId != accountId) + return StatusCode(403, "You don't have permission to create files in this pool."); + + await using var transaction = await db.Database.BeginTransactionAsync(); + try + { + var file = new CloudFile + { + Name = request.Name, + Size = request.Size, + Hash = request.Hash, + MimeType = request.MimeType, + Description = request.Description, + AccountId = accountId, + UserMeta = request.UserMeta, + FileMeta = request.FileMeta, + SensitiveMarks = request.SensitiveMarks, + PoolId = request.PoolId + }; + db.Files.Add(file); + await db.SaveChangesAsync(); + await fs._PurgeCacheAsync(file.Id); + await transaction.CommitAsync(); + + file.FastUploadLink = await fs.CreateFastUploadLinkAsync(file); + + return file; + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + throw; + } + } } \ No newline at end of file diff --git a/DysonNetwork.Drive/Storage/FileService.cs b/DysonNetwork.Drive/Storage/FileService.cs index bce726e..843cb3d 100644 --- a/DysonNetwork.Drive/Storage/FileService.cs +++ b/DysonNetwork.Drive/Storage/FileService.cs @@ -821,6 +821,27 @@ public class FileService( await db.SaveChangesAsync(); return count; } + + public async Task CreateFastUploadLinkAsync(CloudFile file) + { + if (file.PoolId is null) throw new InvalidOperationException("Pool ID is null"); + + var dest = await GetRemoteStorageConfig(file.PoolId.Value); + if (dest is null) throw new InvalidOperationException($"No remote storage configured for pool {file.PoolId}"); + var client = CreateMinioClient(dest); + if (client is null) + throw new InvalidOperationException( + $"Failed to configure client for remote destination '{file.PoolId}'" + ); + + var url = await client.PresignedPutObjectAsync( + new PresignedPutObjectArgs() + .WithBucket(dest.Bucket) + .WithObject(file.Id) + .WithExpiry(60 * 60 * 24) + ); + return url; + } } /// diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 567765d..c4becba 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -105,6 +105,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded