Compare commits
2 Commits
387246a95c
...
92ab7a1a2a
| Author | SHA1 | Date | |
|---|---|---|---|
| 92ab7a1a2a | |||
| 28067d18f6 |
@@ -66,9 +66,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\DysonNetwork.Shared\DysonNetwork.Shared.csproj" />
|
<ProjectReference Include="..\DysonNetwork.Shared\DysonNetwork.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Storage\FileReferenceServiceGrpc.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
127
DysonNetwork.Drive/Storage/FileReferenceServiceGrpc.cs
Normal file
127
DysonNetwork.Drive/Storage/FileReferenceServiceGrpc.cs
Normal file
@@ -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<Shared.Proto.CloudFileReference> 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<GetReferencesResponse> 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<GetReferenceCountResponse> GetReferenceCount(GetReferenceCountRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var count = await fileReferenceService.GetReferenceCountAsync(request.FileId);
|
||||||
|
return new GetReferenceCountResponse { Count = count };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetReferencesResponse> 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<GetResourceFilesResponse> 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<DeleteResourceReferencesResponse> DeleteResourceReferences(DeleteResourceReferencesRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var deletedCount = await fileReferenceService.DeleteResourceReferencesAsync(request.ResourceId, request.Usage);
|
||||||
|
return new DeleteResourceReferencesResponse { DeletedCount = deletedCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<DeleteReferenceResponse> DeleteReference(DeleteReferenceRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var success = await fileReferenceService.DeleteReferenceAsync(Guid.Parse(request.ReferenceId));
|
||||||
|
return new DeleteReferenceResponse { Success = success };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<UpdateResourceFilesResponse> 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<SetReferenceExpirationResponse> 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<SetFileReferencesExpirationResponse> 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<HasFileReferencesResponse> HasFileReferences(HasFileReferencesRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var hasReferences = await fileReferenceService.HasFileReferencesAsync(request.FileId);
|
||||||
|
return new HasFileReferencesResponse { HasReferences = hasReferences };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using FFMpegCore;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using DysonNetwork.Shared.Cache;
|
using DysonNetwork.Shared.Cache;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Minio;
|
using Minio;
|
||||||
using Minio.DataModel.Args;
|
using Minio.DataModel.Args;
|
||||||
@@ -344,6 +345,68 @@ public class FileService(
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<CloudFile> UpdateFileAsync(CloudFile file, FieldMask updateMask)
|
||||||
|
{
|
||||||
|
var existingFile = await db.Files.FirstOrDefaultAsync(f => f.Id == file.Id);
|
||||||
|
if (existingFile == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"File with ID {file.Id} not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var path in updateMask.Paths)
|
||||||
|
{
|
||||||
|
switch (path)
|
||||||
|
{
|
||||||
|
case "name":
|
||||||
|
existingFile.Name = file.Name;
|
||||||
|
break;
|
||||||
|
case "description":
|
||||||
|
existingFile.Description = file.Description;
|
||||||
|
break;
|
||||||
|
case "file_meta":
|
||||||
|
existingFile.FileMeta = file.FileMeta;
|
||||||
|
break;
|
||||||
|
case "user_meta":
|
||||||
|
existingFile.UserMeta = file.UserMeta;
|
||||||
|
break;
|
||||||
|
case "mime_type":
|
||||||
|
existingFile.MimeType = file.MimeType;
|
||||||
|
break;
|
||||||
|
case "hash":
|
||||||
|
existingFile.Hash = file.Hash;
|
||||||
|
break;
|
||||||
|
case "size":
|
||||||
|
existingFile.Size = file.Size;
|
||||||
|
break;
|
||||||
|
case "uploaded_at":
|
||||||
|
existingFile.UploadedAt = file.UploadedAt;
|
||||||
|
break;
|
||||||
|
case "uploaded_to":
|
||||||
|
existingFile.UploadedTo = file.UploadedTo;
|
||||||
|
break;
|
||||||
|
case "has_compression":
|
||||||
|
existingFile.HasCompression = file.HasCompression;
|
||||||
|
break;
|
||||||
|
case "is_marked_recycle":
|
||||||
|
existingFile.IsMarkedRecycle = file.IsMarkedRecycle;
|
||||||
|
break;
|
||||||
|
case "storage_id":
|
||||||
|
existingFile.StorageId = file.StorageId;
|
||||||
|
break;
|
||||||
|
case "storage_url":
|
||||||
|
existingFile.StorageUrl = file.StorageUrl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.LogWarning("Attempted to update unknown field: {Field}", path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
await _PurgeCacheAsync(file.Id);
|
||||||
|
return existingFile;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteFileAsync(CloudFile file)
|
public async Task DeleteFileAsync(CloudFile file)
|
||||||
{
|
{
|
||||||
await DeleteFileDataAsync(file);
|
await DeleteFileDataAsync(file);
|
||||||
|
|||||||
167
DysonNetwork.Drive/Storage/FileServiceGrpc.cs
Normal file
167
DysonNetwork.Drive/Storage/FileServiceGrpc.cs
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
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<Shared.Proto.CloudFile> 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<Shared.Proto.CloudFile> UpdateFile(UpdateFileRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
var file = await fileService.GetFileAsync(request.File.Id);
|
||||||
|
if (file == null)
|
||||||
|
throw new RpcException(new Status(StatusCode.NotFound, "File not found"));
|
||||||
|
var updatedFile = await fileService.UpdateFileAsync(file, request.UpdateMask);
|
||||||
|
return updatedFile.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Empty> 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<Shared.Proto.CloudFile> ProcessNewFile(IAsyncStreamReader<ProcessNewFileRequest> requestStream,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
ProcessNewFileRequest? metadataRequest = null;
|
||||||
|
var chunks = new List<byte[]>();
|
||||||
|
|
||||||
|
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<Shared.Proto.CloudFile> UploadFileToRemote(
|
||||||
|
IAsyncStreamReader<UploadFileToRemoteRequest> requestStream, ServerCallContext context)
|
||||||
|
{
|
||||||
|
UploadFileToRemoteRequest? metadataRequest = null;
|
||||||
|
var chunks = new List<byte[]>();
|
||||||
|
|
||||||
|
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<Empty> 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<LoadFromReferenceResponse> 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<IsReferencedResponse> IsReferenced(IsReferencedRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var isReferenced = await fileService.IsReferencedAsync(request.FileId);
|
||||||
|
return new IsReferencedResponse { IsReferenced = isReferenced };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Empty> PurgeCache(PurgeCacheRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
await fileService._PurgeCacheAsync(request.FileId);
|
||||||
|
return new Empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user