diff --git a/DysonNetwork.Drive/DysonNetwork.Drive.csproj b/DysonNetwork.Drive/DysonNetwork.Drive.csproj index 19bfcab..0543986 100644 --- a/DysonNetwork.Drive/DysonNetwork.Drive.csproj +++ b/DysonNetwork.Drive/DysonNetwork.Drive.csproj @@ -66,9 +66,4 @@ - - - - - diff --git a/DysonNetwork.Drive/Storage/FileReferenceServiceGrpc.cs b/DysonNetwork.Drive/Storage/FileReferenceServiceGrpc.cs new file mode 100644 index 0000000..758efe2 --- /dev/null +++ b/DysonNetwork.Drive/Storage/FileReferenceServiceGrpc.cs @@ -0,0 +1,127 @@ +using System.Threading.Tasks; +using DysonNetwork.Shared.Proto; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; +using NodaTime; +using Duration = NodaTime.Duration; + +namespace DysonNetwork.Drive.Storage +{ + public class FileReferenceServiceGrpc(FileReferenceService fileReferenceService) : Shared.Proto.FileReferenceService.FileReferenceServiceBase + { + public override async Task CreateReference(CreateReferenceRequest request, ServerCallContext context) + { + Instant? expiredAt = null; + if (request.ExpiredAt != null) + { + expiredAt = Instant.FromUnixTimeSeconds(request.ExpiredAt.Seconds); + } + else if (request.Duration != null) + { + expiredAt = SystemClock.Instance.GetCurrentInstant() + Duration.FromTimeSpan(request.Duration.ToTimeSpan()); + } + + var reference = await fileReferenceService.CreateReferenceAsync( + request.FileId, + request.Usage, + request.ResourceId, + expiredAt + ); + return reference.ToProtoValue(); + } + + public override async Task GetReferences(GetReferencesRequest request, ServerCallContext context) + { + var references = await fileReferenceService.GetReferencesAsync(request.FileId); + var response = new GetReferencesResponse(); + response.References.AddRange(references.Select(r => r.ToProtoValue())); + return response; + } + + public override async Task GetReferenceCount(GetReferenceCountRequest request, ServerCallContext context) + { + var count = await fileReferenceService.GetReferenceCountAsync(request.FileId); + return new GetReferenceCountResponse { Count = count }; + } + + public override async Task GetResourceReferences(GetResourceReferencesRequest request, ServerCallContext context) + { + var references = await fileReferenceService.GetResourceReferencesAsync(request.ResourceId, request.Usage); + var response = new GetReferencesResponse(); + response.References.AddRange(references.Select(r => r.ToProtoValue())); + return response; + } + + public override async Task GetResourceFiles(GetResourceFilesRequest request, ServerCallContext context) + { + var files = await fileReferenceService.GetResourceFilesAsync(request.ResourceId, request.Usage); + var response = new GetResourceFilesResponse(); + response.Files.AddRange(files.Select(f => f.ToProtoValue())); + return response; + } + + public override async Task DeleteResourceReferences(DeleteResourceReferencesRequest request, ServerCallContext context) + { + var deletedCount = await fileReferenceService.DeleteResourceReferencesAsync(request.ResourceId, request.Usage); + return new DeleteResourceReferencesResponse { DeletedCount = deletedCount }; + } + + public override async Task DeleteReference(DeleteReferenceRequest request, ServerCallContext context) + { + var success = await fileReferenceService.DeleteReferenceAsync(Guid.Parse(request.ReferenceId)); + return new DeleteReferenceResponse { Success = success }; + } + + public override async Task UpdateResourceFiles(UpdateResourceFilesRequest request, ServerCallContext context) + { + Instant? expiredAt = null; + if (request.ExpiredAt != null) + { + expiredAt = Instant.FromUnixTimeSeconds(request.ExpiredAt.Seconds); + } + else if (request.Duration != null) + { + expiredAt = SystemClock.Instance.GetCurrentInstant() + Duration.FromTimeSpan(request.Duration.ToTimeSpan()); + } + + var references = await fileReferenceService.UpdateResourceFilesAsync( + request.ResourceId, + request.FileIds, + request.Usage, + expiredAt + ); + var response = new UpdateResourceFilesResponse(); + response.References.AddRange(references.Select(r => r.ToProtoValue())); + return response; + } + + public override async Task SetReferenceExpiration(SetReferenceExpirationRequest request, ServerCallContext context) + { + Instant? expiredAt = null; + if (request.ExpiredAt != null) + { + expiredAt = Instant.FromUnixTimeSeconds(request.ExpiredAt.Seconds); + } + else if (request.Duration != null) + { + expiredAt = SystemClock.Instance.GetCurrentInstant() + Duration.FromTimeSpan(request.Duration.ToTimeSpan()); + } + + var success = await fileReferenceService.SetReferenceExpirationAsync(Guid.Parse(request.ReferenceId), expiredAt); + return new SetReferenceExpirationResponse { Success = success }; + } + + public override async Task SetFileReferencesExpiration(SetFileReferencesExpirationRequest request, ServerCallContext context) + { + var expiredAt = Instant.FromUnixTimeSeconds(request.ExpiredAt.Seconds); + var updatedCount = await fileReferenceService.SetFileReferencesExpirationAsync(request.FileId, expiredAt); + return new SetFileReferencesExpirationResponse { UpdatedCount = updatedCount }; + } + + public override async Task HasFileReferences(HasFileReferencesRequest request, ServerCallContext context) + { + var hasReferences = await fileReferenceService.HasFileReferencesAsync(request.FileId); + return new HasFileReferencesResponse { HasReferences = hasReferences }; + } + } +} \ No newline at end of file diff --git a/DysonNetwork.Drive/Storage/FileServiceGrpc.cs b/DysonNetwork.Drive/Storage/FileServiceGrpc.cs new file mode 100644 index 0000000..d3118a5 --- /dev/null +++ b/DysonNetwork.Drive/Storage/FileServiceGrpc.cs @@ -0,0 +1,175 @@ +using System.Threading.Tasks; +using DysonNetwork.Shared.Proto; +using Google.Protobuf.WellKnownTypes; +using Grpc.Core; + +namespace DysonNetwork.Drive.Storage +{ + public class FileServiceGrpc(FileService fileService) : Shared.Proto.FileService.FileServiceBase + { + public override async Task GetFile(GetFileRequest request, ServerCallContext context) + { + var file = await fileService.GetFileAsync(request.Id); + return file?.ToProtoValue() ?? throw new RpcException(new Status(StatusCode.NotFound, "File not found")); + } + + public override async Task UpdateFile(UpdateFileRequest request, ServerCallContext context) + { + // Assuming UpdateFileAsync exists in FileService and handles the update_mask + // This is a placeholder, as the current FileService.cs doesn't have a direct UpdateFile method + // You might need to implement this logic in FileService based on your needs. + // For now, we'll just return the requested file. + var file = await fileService.GetFileAsync(request.File.Id); + if (file == null) + { + throw new RpcException(new Status(StatusCode.NotFound, "File not found")); + } + + // Apply updates from request.File to 'file' based on request.UpdateMask + // This part requires more detailed implementation based on how you want to handle partial updates. + return file.ToProtoValue(); + } + + public override async Task DeleteFile(DeleteFileRequest request, ServerCallContext context) + { + var file = await fileService.GetFileAsync(request.Id); + if (file == null) + { + throw new RpcException(new Status(StatusCode.NotFound, "File not found")); + } + + await fileService.DeleteFileAsync(file); + return new Empty(); + } + + public override async Task ProcessNewFile(IAsyncStreamReader requestStream, + ServerCallContext context) + { + ProcessNewFileRequest? metadataRequest = null; + var chunks = new List(); + + await foreach (var message in requestStream.ReadAllAsync()) + { + if (message.DataCase == ProcessNewFileRequest.DataOneofCase.Metadata) + { + metadataRequest = message; + } + else if (message.DataCase == ProcessNewFileRequest.DataOneofCase.Chunk) + { + chunks.Add(message.Chunk.ToByteArray()); + } + } + + if (metadataRequest == null || metadataRequest.Metadata == null) + { + throw new RpcException(new Status(StatusCode.InvalidArgument, "Missing file metadata")); + } + + var metadata = metadataRequest.Metadata; + using var memoryStream = new MemoryStream(); + foreach (var chunk in chunks) + { + await memoryStream.WriteAsync(chunk); + } + + memoryStream.Position = 0; + + // Assuming you have an Account object available or can create a dummy one for now + // You might need to adjust this based on how accounts are handled in your system + var dummyAccount = new Account { Id = metadata.AccountId }; + + var cloudFile = await fileService.ProcessNewFileAsync( + dummyAccount, + metadata.FileId, + memoryStream, + metadata.FileName, + metadata.ContentType + ); + return cloudFile.ToProtoValue(); + } + + public override async Task UploadFileToRemote( + IAsyncStreamReader requestStream, ServerCallContext context) + { + UploadFileToRemoteRequest? metadataRequest = null; + var chunks = new List(); + + await foreach (var message in requestStream.ReadAllAsync()) + { + if (message.DataCase == UploadFileToRemoteRequest.DataOneofCase.Metadata) + { + metadataRequest = message; + } + else if (message.DataCase == UploadFileToRemoteRequest.DataOneofCase.Chunk) + { + chunks.Add(message.Chunk.ToByteArray()); + } + } + + if (metadataRequest == null || metadataRequest.Metadata == null) + { + throw new RpcException(new Status(StatusCode.InvalidArgument, "Missing upload metadata")); + } + + var metadata = metadataRequest.Metadata; + using var memoryStream = new MemoryStream(); + foreach (var chunk in chunks) + { + await memoryStream.WriteAsync(chunk); + } + + memoryStream.Position = 0; + + var file = await fileService.GetFileAsync(metadata.FileId); + if (file == null) + { + throw new RpcException(new Status(StatusCode.NotFound, "File not found")); + } + + var uploadedFile = await fileService.UploadFileToRemoteAsync( + file, + memoryStream, + metadata.TargetRemote, + metadata.Suffix + ); + return uploadedFile.ToProtoValue(); + } + + public override async Task DeleteFileData(DeleteFileDataRequest request, ServerCallContext context) + { + var file = await fileService.GetFileAsync(request.FileId); + if (file == null) + { + throw new RpcException(new Status(StatusCode.NotFound, "File not found")); + } + + await fileService.DeleteFileDataAsync(file); + return new Empty(); + } + + public override async Task LoadFromReference(LoadFromReferenceRequest request, + ServerCallContext context) + { + // Assuming CloudFileReferenceObject is a simple class/struct that holds an ID + // You might need to define this or adjust the LoadFromReference method in FileService + var references = request.ReferenceIds.Select(id => new CloudFileReferenceObject { Id = id }).ToList(); + var files = await fileService.LoadFromReference(references); + var response = new LoadFromReferenceResponse(); + response.Files.AddRange(files.Where(f => f != null).Select(f => f!.ToProtoValue())); + return response; + } + + public override async Task IsReferenced(IsReferencedRequest request, + ServerCallContext context) + { + var isReferenced = await fileService.IsReferencedAsync(request.FileId); + return new IsReferencedResponse { IsReferenced = isReferenced }; + } + + public override async Task PurgeCache(PurgeCacheRequest request, ServerCallContext context) + { + await fileService._PurgeCacheAsync(request.FileId); + return new Empty(); + } + } +} \ No newline at end of file