From 43989845510cb3cd92057be7d302867bde9f733a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 19 Jul 2025 12:03:18 +0800 Subject: [PATCH] :bug: Fixes in CloudFile filemeta transfer via gRPC --- DysonNetwork.Drive/Storage/CloudFile.cs | 37 ++++++++---------- .../Data/CloudFileReferenceObject.cs | 38 +++++++++---------- DysonNetwork.Shared/Proto/GrpcTypeHelper.cs | 10 ++++- DysonNetwork.Shared/Proto/file.proto | 6 +-- DysonNetwork.Sphere/Realm/Realm.cs | 2 + DysonNetwork.Sphere/Realm/RealmController.cs | 8 ++-- DysonNetwork.Sphere/Realm/RealmService.cs | 24 +++++++++++- 7 files changed, 73 insertions(+), 52 deletions(-) diff --git a/DysonNetwork.Drive/Storage/CloudFile.cs b/DysonNetwork.Drive/Storage/CloudFile.cs index 8ec116d..1fb7153 100644 --- a/DysonNetwork.Drive/Storage/CloudFile.cs +++ b/DysonNetwork.Drive/Storage/CloudFile.cs @@ -1,6 +1,9 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using DysonNetwork.Shared.Data; +using DysonNetwork.Shared.Proto; +using Google.Protobuf; +using Newtonsoft.Json; using NodaTime; using NodaTime.Serialization.Protobuf; @@ -54,7 +57,7 @@ public class CloudFile : ModelBase, ICloudFile, IIdentifiedResource public Instant? UploadedAt { get; set; } [MaxLength(128)] public string? UploadedTo { get; set; } public bool HasCompression { get; set; } = false; - + /// /// The field is set to true if the recycling job plans to delete the file. /// Due to the unstable of the recycling job, this doesn't really delete the file until a human verifies it. @@ -103,7 +106,7 @@ public class CloudFile : ModelBase, ICloudFile, IIdentifiedResource /// The protobuf message representation of this object public Shared.Proto.CloudFile ToProtoValue() { - var protoFile = new Shared.Proto.CloudFile + var proto = new Shared.Proto.CloudFile { Id = Id, Name = Name ?? string.Empty, @@ -113,28 +116,18 @@ public class CloudFile : ModelBase, ICloudFile, IIdentifiedResource HasCompression = HasCompression, Url = StorageUrl ?? string.Empty, ContentType = MimeType ?? string.Empty, - UploadedAt = UploadedAt?.ToTimestamp() + UploadedAt = UploadedAt?.ToTimestamp(), + // Convert file metadata + FileMeta = ByteString.CopyFromUtf8( + System.Text.Json.JsonSerializer.Serialize(FileMeta, GrpcTypeHelper.SystemTextSerializerOptions) + ), + // Convert user metadata + UserMeta = ByteString.CopyFromUtf8( + System.Text.Json.JsonSerializer.Serialize(UserMeta, GrpcTypeHelper.SystemTextSerializerOptions) + ) }; - // Convert FileMeta dictionary - if (FileMeta != null) - { - foreach (var (key, value) in FileMeta) - { - protoFile.FileMeta[key] = Google.Protobuf.WellKnownTypes.Value.ForString(value?.ToString() ?? string.Empty); - } - } - - // Convert UserMeta dictionary - if (UserMeta != null) - { - foreach (var (key, value) in UserMeta) - { - protoFile.UserMeta[key] = Google.Protobuf.WellKnownTypes.Value.ForString(value?.ToString() ?? string.Empty); - } - } - - return protoFile; + return proto; } } diff --git a/DysonNetwork.Shared/Data/CloudFileReferenceObject.cs b/DysonNetwork.Shared/Data/CloudFileReferenceObject.cs index ee24982..600886c 100644 --- a/DysonNetwork.Shared/Data/CloudFileReferenceObject.cs +++ b/DysonNetwork.Shared/Data/CloudFileReferenceObject.cs @@ -1,5 +1,5 @@ using DysonNetwork.Shared.Proto; -using Google.Protobuf.WellKnownTypes; +using Google.Protobuf; namespace DysonNetwork.Shared.Data; @@ -41,10 +41,14 @@ public class CloudFileReferenceObject : ModelBase, ICloudFile { Id = proto.Id, Name = proto.Name, - FileMeta = proto.FileMeta - .ToDictionary(kvp => kvp.Key, kvp => GrpcTypeHelper.ConvertValueToObject(kvp.Value)), - UserMeta = proto.UserMeta - .ToDictionary(kvp => kvp.Key, kvp => GrpcTypeHelper.ConvertValueToObject(kvp.Value)), + FileMeta = System.Text.Json.JsonSerializer.Deserialize>( + proto.FileMeta.ToStringUtf8(), + GrpcTypeHelper.SystemTextSerializerOptions + ) ?? [], + UserMeta = System.Text.Json.JsonSerializer.Deserialize>( + proto.UserMeta.ToStringUtf8(), + GrpcTypeHelper.SystemTextSerializerOptions + ) ?? [], MimeType = proto.MimeType, Hash = proto.Hash, Size = proto.Size, @@ -55,9 +59,9 @@ public class CloudFileReferenceObject : ModelBase, ICloudFile /// /// Converts the current object to its protobuf representation /// - public Proto.CloudFile ToProtoValue() + public CloudFile ToProtoValue() { - var proto = new Proto.CloudFile + var proto = new CloudFile { Id = Id, Name = Name, @@ -70,22 +74,14 @@ public class CloudFileReferenceObject : ModelBase, ICloudFile }; // Convert file metadata - if (FileMeta != null) - { - foreach (var (key, value) in FileMeta) - { - proto.FileMeta[key] = Value.ForString(value?.ToString() ?? string.Empty); - } - } + proto.FileMeta = ByteString.CopyFromUtf8( + System.Text.Json.JsonSerializer.Serialize(FileMeta, GrpcTypeHelper.SystemTextSerializerOptions) + ); // Convert user metadata - if (UserMeta != null) - { - foreach (var (key, value) in UserMeta) - { - proto.UserMeta[key] = Value.ForString(value?.ToString() ?? string.Empty); - } - } + proto.UserMeta = ByteString.CopyFromUtf8( + System.Text.Json.JsonSerializer.Serialize(UserMeta, GrpcTypeHelper.SystemTextSerializerOptions) + ); return proto; } diff --git a/DysonNetwork.Shared/Proto/GrpcTypeHelper.cs b/DysonNetwork.Shared/Proto/GrpcTypeHelper.cs index 7eb90b8..e12dfa4 100644 --- a/DysonNetwork.Shared/Proto/GrpcTypeHelper.cs +++ b/DysonNetwork.Shared/Proto/GrpcTypeHelper.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using System.Text.Json.Serialization; using Google.Protobuf.Collections; using Google.Protobuf.WellKnownTypes; using Newtonsoft.Json; @@ -8,7 +9,14 @@ namespace DysonNetwork.Shared.Proto; public abstract class GrpcTypeHelper { - private static readonly JsonSerializerSettings SerializerSettings = new() + public static readonly JsonSerializerOptions SystemTextSerializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { new JsonStringEnumConverter() } + }; + + public static readonly JsonSerializerSettings SerializerSettings = new() { ContractResolver = new DefaultContractResolver { NamingStrategy = new SnakeCaseNamingStrategy() }, PreserveReferencesHandling = PreserveReferencesHandling.Objects, diff --git a/DysonNetwork.Shared/Proto/file.proto b/DysonNetwork.Shared/Proto/file.proto index 1040410..7097069 100644 --- a/DysonNetwork.Shared/Proto/file.proto +++ b/DysonNetwork.Shared/Proto/file.proto @@ -19,11 +19,11 @@ message CloudFile { // Original name of the file string name = 2; + // The metadata uses JSON bytes to store to keep the data structure over gRPC // File metadata (e.g., dimensions, duration, etc.) - map file_meta = 3; - + bytes file_meta = 3; // User-defined metadata - map user_meta = 4; + bytes user_meta = 4; // MIME type of the file string mime_type = 5; diff --git a/DysonNetwork.Sphere/Realm/Realm.cs b/DysonNetwork.Sphere/Realm/Realm.cs index 6b41e8f..a7306e4 100644 --- a/DysonNetwork.Sphere/Realm/Realm.cs +++ b/DysonNetwork.Sphere/Realm/Realm.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; +using DysonNetwork.Pass.Account; using DysonNetwork.Shared.Data; using DysonNetwork.Sphere.Chat; using Microsoft.EntityFrameworkCore; @@ -48,6 +49,7 @@ public class RealmMember : ModelBase public Guid RealmId { get; set; } public Realm Realm { get; set; } = null!; public Guid AccountId { get; set; } + [NotMapped] public Account? Account { get; set; } public int Role { get; set; } = RealmMemberRole.Normal; public Instant? JoinedAt { get; set; } diff --git a/DysonNetwork.Sphere/Realm/RealmController.cs b/DysonNetwork.Sphere/Realm/RealmController.cs index 66debd6..3139182 100644 --- a/DysonNetwork.Sphere/Realm/RealmController.cs +++ b/DysonNetwork.Sphere/Realm/RealmController.cs @@ -62,7 +62,7 @@ public class RealmController( .Include(e => e.Realm) .ToListAsync(); - return members.ToList(); + return await rs.LoadMemberAccounts(members); } public class RealmMemberRequest @@ -256,7 +256,7 @@ public class RealmController( // // var result = members.Skip(offset).Take(take).ToList(); // - // return Ok(result); + // return Ok(await rs.LoadMemberAccounts(result)); // } // else // { @@ -269,7 +269,7 @@ public class RealmController( .Take(take) .ToListAsync(); - return Ok(members); + return Ok(await rs.LoadMemberAccounts(members)); // } } @@ -287,7 +287,7 @@ public class RealmController( .FirstOrDefaultAsync(); if (member is null) return NotFound(); - return Ok(member); + return Ok(await rs.LoadMemberAccount(member)); } [HttpDelete("{slug}/members/me")] diff --git a/DysonNetwork.Sphere/Realm/RealmService.cs b/DysonNetwork.Sphere/Realm/RealmService.cs index b5b1a5e..8ae22ca 100644 --- a/DysonNetwork.Sphere/Realm/RealmService.cs +++ b/DysonNetwork.Sphere/Realm/RealmService.cs @@ -1,5 +1,6 @@ using DysonNetwork.Shared; using DysonNetwork.Shared.Proto; +using DysonNetwork.Shared.Registry; using DysonNetwork.Sphere.Localization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Localization; @@ -10,7 +11,8 @@ public class RealmService( AppDatabase db, PusherService.PusherServiceClient pusher, AccountService.AccountServiceClient accounts, - IStringLocalizer localizer + IStringLocalizer localizer, + AccountClientHelper accountsHelper ) { public async Task SendInviteNotify(RealmMember member) @@ -44,4 +46,24 @@ public class RealmService( .FirstOrDefaultAsync(m => m.RealmId == realmId && m.AccountId == accountId); return member?.Role >= maxRequiredRole; } + + public async Task LoadMemberAccount(RealmMember member) + { + var account = await accountsHelper.GetAccount(member.AccountId); + member.Account = Pass.Account.Account.FromProtoValue(account); + return member; + } + + public async Task> LoadMemberAccounts(ICollection members) + { + var accountIds = members.Select(m => m.AccountId).ToList(); + var accounts = (await accountsHelper.GetAccountBatch(accountIds)).ToDictionary(a => Guid.Parse(a.Id), a => a); + + return members.Select(m => + { + if (accounts.TryGetValue(m.AccountId, out var account)) + m.Account = Pass.Account.Account.FromProtoValue(account); + return m; + }).ToList(); + } } \ No newline at end of file