🐛 Fixes in CloudFile filemeta transfer via gRPC

This commit is contained in:
2025-07-19 12:03:18 +08:00
parent e0e1eb76cd
commit 4398984551
7 changed files with 73 additions and 52 deletions

View File

@@ -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;
@@ -103,7 +106,7 @@ public class CloudFile : ModelBase, ICloudFile, IIdentifiedResource
/// <returns>The protobuf message representation of this object</returns>
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;
}
}

View File

@@ -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<Dictionary<string, object?>>(
proto.FileMeta.ToStringUtf8(),
GrpcTypeHelper.SystemTextSerializerOptions
) ?? [],
UserMeta = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object?>>(
proto.UserMeta.ToStringUtf8(),
GrpcTypeHelper.SystemTextSerializerOptions
) ?? [],
MimeType = proto.MimeType,
Hash = proto.Hash,
Size = proto.Size,
@@ -55,9 +59,9 @@ public class CloudFileReferenceObject : ModelBase, ICloudFile
/// <summary>
/// Converts the current object to its protobuf representation
/// </summary>
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;
}

View File

@@ -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,

View File

@@ -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<string, google.protobuf.Value> file_meta = 3;
bytes file_meta = 3;
// User-defined metadata
map<string, google.protobuf.Value> user_meta = 4;
bytes user_meta = 4;
// MIME type of the file
string mime_type = 5;

View File

@@ -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; }

View File

@@ -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")]

View File

@@ -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<NotificationResource> localizer
IStringLocalizer<NotificationResource> 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<RealmMember> LoadMemberAccount(RealmMember member)
{
var account = await accountsHelper.GetAccount(member.AccountId);
member.Account = Pass.Account.Account.FromProtoValue(account);
return member;
}
public async Task<List<RealmMember>> LoadMemberAccounts(ICollection<RealmMember> 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();
}
}