🐛 Fix Pass service missing perks subscription
This commit is contained in:
@@ -4,6 +4,7 @@ using DysonNetwork.Shared.Auth;
|
|||||||
using DysonNetwork.Shared.Models;
|
using DysonNetwork.Shared.Models;
|
||||||
using DysonNetwork.Shared.Networking;
|
using DysonNetwork.Shared.Networking;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -22,7 +23,8 @@ public class AccountCurrentController(
|
|||||||
AccountEventService events,
|
AccountEventService events,
|
||||||
AuthService auth,
|
AuthService auth,
|
||||||
FileService.FileServiceClient files,
|
FileService.FileServiceClient files,
|
||||||
Credit.SocialCreditService creditService
|
Credit.SocialCreditService creditService,
|
||||||
|
RemoteSubscriptionService remoteSubscription
|
||||||
) : ControllerBase
|
) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -39,6 +41,24 @@ public class AccountCurrentController(
|
|||||||
.Where(e => e.Id == userId)
|
.Where(e => e.Id == userId)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (account != null)
|
||||||
|
{
|
||||||
|
// Populate PerkSubscription from Wallet service via gRPC
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
|
||||||
|
if (subscription != null)
|
||||||
|
{
|
||||||
|
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log error but don't fail the request - PerkSubscription is optional
|
||||||
|
Console.WriteLine($"Failed to populate PerkSubscription for account {account.Id}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(account);
|
return Ok(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +341,10 @@ public class AccountCurrentController(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (currentUser.PerkSubscription is null)
|
// Check PerkSubscription via RemoteSubscriptionService instead of relying on currentUser.PerkSubscription
|
||||||
|
// which is not populated when currentUser comes from HttpContext.Items
|
||||||
|
var perkSubscription = await remoteSubscription.GetPerkSubscription(currentUser.Id);
|
||||||
|
if (perkSubscription == null)
|
||||||
return StatusCode(403, ApiError.Unauthorized(
|
return StatusCode(403, ApiError.Unauthorized(
|
||||||
message: "You need to have a subscription to check-in backdated.",
|
message: "You need to have a subscription to check-in backdated.",
|
||||||
forbidden: true,
|
forbidden: true,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using DysonNetwork.Pass.Credit;
|
using DysonNetwork.Pass.Credit;
|
||||||
using DysonNetwork.Shared.Models;
|
using DysonNetwork.Shared.Models;
|
||||||
using DysonNetwork.Shared.Networking;
|
using DysonNetwork.Shared.Networking;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -10,7 +11,8 @@ namespace DysonNetwork.Pass.Account;
|
|||||||
[Route("/api/accounts")]
|
[Route("/api/accounts")]
|
||||||
public class AccountPublicController(
|
public class AccountPublicController(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
SocialCreditService socialCreditService
|
SocialCreditService socialCreditService,
|
||||||
|
RemoteSubscriptionService remoteSubscription
|
||||||
) : ControllerBase
|
) : ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet("{name}")]
|
[HttpGet("{name}")]
|
||||||
@@ -26,6 +28,21 @@ public class AccountPublicController(
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (account is null) return NotFound(ApiError.NotFound(name, traceId: HttpContext.TraceIdentifier));
|
if (account is null) return NotFound(ApiError.NotFound(name, traceId: HttpContext.TraceIdentifier));
|
||||||
|
|
||||||
|
// Populate PerkSubscription from Wallet service via gRPC
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
|
||||||
|
if (subscription != null)
|
||||||
|
{
|
||||||
|
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log error but don't fail the request - PerkSubscription is optional
|
||||||
|
Console.WriteLine($"Failed to populate PerkSubscription for account {account.Id}: {ex.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,11 +84,44 @@ public class AccountPublicController(
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
return [];
|
return [];
|
||||||
return await db.Accounts
|
|
||||||
|
var accounts = await db.Accounts
|
||||||
.Include(e => e.Profile)
|
.Include(e => e.Profile)
|
||||||
.Where(a => EF.Functions.ILike(a.Name, $"%{query}%") ||
|
.Where(a => EF.Functions.ILike(a.Name, $"%{query}%") ||
|
||||||
EF.Functions.ILike(a.Nick, $"%{query}%"))
|
EF.Functions.ILike(a.Nick, $"%{query}%"))
|
||||||
.Take(take)
|
.Take(take)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Populate PerkSubscriptions from Wallet service via gRPC
|
||||||
|
if (accounts.Count > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var accountIds = accounts.Select(a => a.Id).ToList();
|
||||||
|
var subscriptions = await remoteSubscription.GetPerkSubscriptions(accountIds);
|
||||||
|
|
||||||
|
var subscriptionDict = subscriptions
|
||||||
|
.Where(s => s != null)
|
||||||
|
.ToDictionary(
|
||||||
|
s => Guid.Parse(s.AccountId),
|
||||||
|
s => SnWalletSubscription.FromProtoValue(s).ToReference()
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
if (subscriptionDict.TryGetValue(account.Id, out var subscription))
|
||||||
|
{
|
||||||
|
account.PerkSubscription = subscription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log error but don't fail the request - PerkSubscription is optional
|
||||||
|
Console.WriteLine($"Failed to populate PerkSubscriptions for search results: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using NATS.Net;
|
|||||||
using NodaTime;
|
using NodaTime;
|
||||||
using OtpNet;
|
using OtpNet;
|
||||||
using AuthService = DysonNetwork.Pass.Auth.AuthService;
|
using AuthService = DysonNetwork.Pass.Auth.AuthService;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
|
|
||||||
namespace DysonNetwork.Pass.Account;
|
namespace DysonNetwork.Pass.Account;
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ public class AccountService(
|
|||||||
IStringLocalizer<EmailResource> emailLocalizer,
|
IStringLocalizer<EmailResource> emailLocalizer,
|
||||||
ICacheService cache,
|
ICacheService cache,
|
||||||
ILogger<AccountService> logger,
|
ILogger<AccountService> logger,
|
||||||
|
RemoteSubscriptionService remoteSubscription,
|
||||||
INatsConnection nats
|
INatsConnection nats
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -757,4 +759,56 @@ public class AccountService(
|
|||||||
}).ToByteArray()
|
}).ToByteArray()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the PerkSubscription property for a single account by calling the Wallet service via gRPC.
|
||||||
|
/// </summary>
|
||||||
|
public async Task PopulatePerkSubscriptionAsync(SnAccount account)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
|
||||||
|
if (subscription != null)
|
||||||
|
{
|
||||||
|
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Failed to populate PerkSubscription for account {AccountId}", account.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the PerkSubscription property for multiple accounts by calling the Wallet service via gRPC.
|
||||||
|
/// </summary>
|
||||||
|
public async Task PopulatePerkSubscriptionsAsync(List<SnAccount> accounts)
|
||||||
|
{
|
||||||
|
if (accounts.Count == 0) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var accountIds = accounts.Select(a => a.Id).ToList();
|
||||||
|
var subscriptions = await remoteSubscription.GetPerkSubscriptions(accountIds);
|
||||||
|
|
||||||
|
var subscriptionDict = subscriptions
|
||||||
|
.Where(s => s != null)
|
||||||
|
.ToDictionary(
|
||||||
|
s => Guid.Parse(s.AccountId),
|
||||||
|
s => SnWalletSubscription.FromProtoValue(s).ToReference()
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
if (subscriptionDict.TryGetValue(account.Id, out var subscription))
|
||||||
|
{
|
||||||
|
account.PerkSubscription = subscription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Failed to populate PerkSubscriptions for {Count} accounts", accounts.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using DysonNetwork.Shared.Models;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -10,6 +12,7 @@ public class AccountServiceGrpc(
|
|||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
AccountEventService accountEvents,
|
AccountEventService accountEvents,
|
||||||
RelationshipService relationships,
|
RelationshipService relationships,
|
||||||
|
RemoteSubscriptionService remoteSubscription,
|
||||||
ILogger<AccountServiceGrpc> logger
|
ILogger<AccountServiceGrpc> logger
|
||||||
)
|
)
|
||||||
: Shared.Proto.AccountService.AccountServiceBase
|
: Shared.Proto.AccountService.AccountServiceBase
|
||||||
@@ -33,6 +36,9 @@ public class AccountServiceGrpc(
|
|||||||
if (account == null)
|
if (account == null)
|
||||||
throw new RpcException(new Status(StatusCode.NotFound, $"Account {request.Id} not found"));
|
throw new RpcException(new Status(StatusCode.NotFound, $"Account {request.Id} not found"));
|
||||||
|
|
||||||
|
// Populate PerkSubscription from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionAsync(account);
|
||||||
|
|
||||||
return account.ToProtoValue();
|
return account.ToProtoValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +57,9 @@ public class AccountServiceGrpc(
|
|||||||
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound,
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound,
|
||||||
$"Account with automated ID {request.AutomatedId} not found"));
|
$"Account with automated ID {request.AutomatedId} not found"));
|
||||||
|
|
||||||
|
// Populate PerkSubscription from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionAsync(account);
|
||||||
|
|
||||||
return account.ToProtoValue();
|
return account.ToProtoValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +78,9 @@ public class AccountServiceGrpc(
|
|||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Populate PerkSubscriptions from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionsAsync(accounts);
|
||||||
|
|
||||||
var response = new GetAccountBatchResponse();
|
var response = new GetAccountBatchResponse();
|
||||||
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
||||||
return response;
|
return response;
|
||||||
@@ -90,6 +102,9 @@ public class AccountServiceGrpc(
|
|||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Populate PerkSubscriptions from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionsAsync(accounts);
|
||||||
|
|
||||||
var response = new GetAccountBatchResponse();
|
var response = new GetAccountBatchResponse();
|
||||||
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
||||||
return response;
|
return response;
|
||||||
@@ -126,6 +141,9 @@ public class AccountServiceGrpc(
|
|||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Populate PerkSubscriptions from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionsAsync(accounts);
|
||||||
|
|
||||||
var response = new GetAccountBatchResponse();
|
var response = new GetAccountBatchResponse();
|
||||||
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
||||||
return response;
|
return response;
|
||||||
@@ -140,6 +158,9 @@ public class AccountServiceGrpc(
|
|||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Populate PerkSubscriptions from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionsAsync(accounts);
|
||||||
|
|
||||||
var response = new GetAccountBatchResponse();
|
var response = new GetAccountBatchResponse();
|
||||||
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
||||||
return response;
|
return response;
|
||||||
@@ -176,6 +197,9 @@ public class AccountServiceGrpc(
|
|||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
// Populate PerkSubscriptions from Wallet service via gRPC
|
||||||
|
await PopulatePerkSubscriptionsAsync(accounts);
|
||||||
|
|
||||||
var response = new ListAccountsResponse
|
var response = new ListAccountsResponse
|
||||||
{
|
{
|
||||||
TotalSize = totalCount,
|
TotalSize = totalCount,
|
||||||
@@ -264,4 +288,56 @@ public class AccountServiceGrpc(
|
|||||||
);
|
);
|
||||||
return new BoolValue { Value = hasRelationship };
|
return new BoolValue { Value = hasRelationship };
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the PerkSubscription property for a single account by calling the Wallet service via gRPC.
|
||||||
|
/// </summary>
|
||||||
|
private async Task PopulatePerkSubscriptionAsync(SnAccount account)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var subscription = await remoteSubscription.GetPerkSubscription(account.Id);
|
||||||
|
if (subscription != null)
|
||||||
|
{
|
||||||
|
account.PerkSubscription = SnWalletSubscription.FromProtoValue(subscription).ToReference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to populate PerkSubscription for account {AccountId} in gRPC service", account.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the PerkSubscription property for multiple accounts by calling the Wallet service via gRPC.
|
||||||
|
/// </summary>
|
||||||
|
private async Task PopulatePerkSubscriptionsAsync(List<SnAccount> accounts)
|
||||||
|
{
|
||||||
|
if (accounts.Count == 0) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var accountIds = accounts.Select(a => a.Id).ToList();
|
||||||
|
var subscriptions = await remoteSubscription.GetPerkSubscriptions(accountIds);
|
||||||
|
|
||||||
|
var subscriptionDict = subscriptions
|
||||||
|
.Where(s => s != null)
|
||||||
|
.ToDictionary(
|
||||||
|
s => Guid.Parse(s.AccountId),
|
||||||
|
s => SnWalletSubscription.FromProtoValue(s).ToReference()
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
if (subscriptionDict.TryGetValue(account.Id, out var subscription))
|
||||||
|
{
|
||||||
|
account.PerkSubscription = subscription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to populate PerkSubscriptions for {Count} accounts in gRPC service", accounts.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user