🚚 Rename project Pass to Passport

This commit is contained in:
2026-03-07 13:25:31 +08:00
parent 639c4a4afb
commit f0789b238d
166 changed files with 394 additions and 340 deletions

View File

@@ -10,7 +10,7 @@ var queue = builder.AddNats("Queue").WithJetStream();
var ringService = builder.AddProject<Projects.DysonNetwork_Ring>("ring");
var padlockService = builder.AddProject<Projects.DysonNetwork_Padlock>("padlock")
.WithReference(ringService);
var passService = builder.AddProject<Projects.DysonNetwork_Pass>("pass")
var passService = builder.AddProject<Projects.DysonNetwork_Passport>("passport")
.WithReference(ringService)
.WithReference(padlockService);
var driveService = builder.AddProject<Projects.DysonNetwork_Drive>("drive")

View File

@@ -18,7 +18,7 @@
<ProjectReference Include="..\DysonNetwork.Develop\DysonNetwork.Develop.csproj" />
<ProjectReference Include="..\DysonNetwork.Drive\DysonNetwork.Drive.csproj" />
<ProjectReference Include="..\DysonNetwork.Padlock\DysonNetwork.Padlock.csproj" />
<ProjectReference Include="..\DysonNetwork.Pass\DysonNetwork.Pass.csproj" />
<ProjectReference Include="..\DysonNetwork.Passport\DysonNetwork.Passport.csproj" />
<ProjectReference Include="..\DysonNetwork.Ring\DysonNetwork.Ring.csproj" />
<ProjectReference Include="..\DysonNetwork.Sphere\DysonNetwork.Sphere.csproj" />
<ProjectReference Include="..\DysonNetwork.Insight\DysonNetwork.Insight.csproj" />

View File

@@ -76,7 +76,7 @@
}
}
},
"pass": {
"passport": {
"type": "project.v1",
"path": "../DysonNetwork.Pass/DysonNetwork.Pass.csproj",
"env": {
@@ -97,7 +97,7 @@
"GRPC_PORT": "7003",
"OTEL_EXPORTER_OTLP_ENDPOINT": "{docker-compose-dashboard.bindings.otlp-grpc.url}",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_SERVICE_NAME": "pass"
"OTEL_SERVICE_NAME": "passport"
},
"bindings": {
"http": {

View File

@@ -17,13 +17,13 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
// Try to parse as Guid first (ID), otherwise treat as username
if (Guid.TryParse(accountIdOrUsername, out _))
{
var account = await apiClient.GetAsync<SnAccount>("pass", $"/accounts/{accountIdOrUsername}");
var account = await apiClient.GetAsync<SnAccount>("passport", $"/accounts/{accountIdOrUsername}");
return account;
}
else
{
var accounts = await apiClient.GetAsync<List<SnAccount>>(
"pass",
"passport",
$"/accounts/search?q={Uri.EscapeDataString(accountIdOrUsername)}"
);
return accounts?.FirstOrDefault();
@@ -46,7 +46,7 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
try
{
var accounts = await apiClient.GetAsync<List<SnAccount>>(
"pass",
"passport",
$"/accounts/search?q={Uri.EscapeDataString(query)}&take={limit}"
);
@@ -67,7 +67,7 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
{
try
{
await apiClient.PostAsync("pass", $"/accounts/{accountId}/follow", new { });
await apiClient.PostAsync("passport", $"/accounts/{accountId}/follow", new { });
logger.LogInformation("Followed account {AccountId}", accountId);
return new { success = true, message = "Account followed successfully" };
@@ -87,7 +87,7 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
{
try
{
await apiClient.PostAsync("pass", $"/accounts/{accountId}/unfollow", new { });
await apiClient.PostAsync("passport", $"/accounts/{accountId}/unfollow", new { });
logger.LogInformation("Unfollowed account {AccountId}", accountId);
return new { success = true, message = "Account unfollowed successfully" };
@@ -109,7 +109,7 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
try
{
var followers = await apiClient.GetAsync<List<SnAccount>>(
"pass",
"passport",
$"/accounts/{accountId}/followers?take={limit}"
);
@@ -132,7 +132,7 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
try
{
var following = await apiClient.GetAsync<List<SnAccount>>(
"pass",
"passport",
$"/accounts/{accountId}/following?take={limit}"
);
@@ -151,7 +151,7 @@ public class AccountPlugin(SolarNetworkApiClient apiClient, ILogger<AccountPlugi
{
try
{
var account = await apiClient.GetAsync<SnAccount>("pass", "/accounts/me");
var account = await apiClient.GetAsync<SnAccount>("passport", "/accounts/me");
return account;
}
catch (Exception ex)

View File

@@ -0,0 +1,40 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using Grpc.Core;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Padlock.Account;
public class AccountServiceGrpc(AppDatabase db) : DyAccountService.DyAccountServiceBase
{
public override async Task<DyListContactsResponse> ListContacts(DyListContactsRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid account ID format"));
var query = db.AccountContacts
.AsNoTracking()
.Where(c => c.AccountId == accountId);
if (request.VerifiedOnly)
query = query.Where(c => c.VerifiedAt != null);
if (request.Type != DyAccountContactType.Unspecified)
{
var contactType = request.Type switch
{
DyAccountContactType.DyEmail => AccountContactType.Email,
DyAccountContactType.DyPhoneNumber => AccountContactType.PhoneNumber,
DyAccountContactType.DyAddress => AccountContactType.Address,
_ => AccountContactType.Email
};
query = query.Where(c => c.Type == contactType);
}
var contacts = await query.ToListAsync(context.CancellationToken);
var response = new DyListContactsResponse();
response.Contacts.AddRange(contacts.Select(c => c.ToProtoValue()));
return response;
}
}

View File

@@ -1,4 +1,5 @@
using DysonNetwork.Padlock.Auth;
using DysonNetwork.Padlock.Account;
using DysonNetwork.Shared.Networking;
namespace DysonNetwork.Padlock.Startup;
@@ -25,6 +26,7 @@ public static class ApplicationConfiguration
public static WebApplication ConfigureGrpcServices(this WebApplication app)
{
app.MapGrpcService<AuthServiceGrpc>();
app.MapGrpcService<AccountServiceGrpc>();
app.MapGrpcReflectionService();
return app;

View File

@@ -1,6 +0,0 @@
namespace DysonNetwork.Pass.Localization;
public class AccountEventResource
{
}

View File

@@ -1,5 +0,0 @@
namespace DysonNetwork.Pass.Localization;
public class EmailResource
{
}

View File

@@ -1,6 +0,0 @@
namespace DysonNetwork.Pass.Localization;
public class NotificationResource
{
}

View File

@@ -1,6 +0,0 @@
namespace DysonNetwork.Pass.Localization;
public class SharedResource
{
}

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Credit;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Passport.Permission;
using DysonNetwork.Passport.Credit;
using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Networking;
@@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/admin/accounts")]

View File

@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Affiliation;
using DysonNetwork.Pass.Auth;
using DysonNetwork.Passport.Affiliation;
using DysonNetwork.Passport.Auth;
using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Extensions;
using DysonNetwork.Shared.Geometry;
@@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/accounts")]

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Shared.Auth;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Networking;
@@ -9,10 +8,10 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NodaTime;
using AuthService = DysonNetwork.Pass.Auth.AuthService;
using AuthService = DysonNetwork.Passport.Auth.AuthService;
using SnAuthSession = DysonNetwork.Shared.Models.SnAuthSession;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[Authorize]
[ApiController]
@@ -20,6 +19,7 @@ namespace DysonNetwork.Pass.Account;
public class AccountCurrentController(
AppDatabase db,
AccountService accounts,
PadlockAccountContactService padlockContacts,
AccountEventService events,
AuthService auth,
DyFileService.DyFileServiceClient files,
@@ -62,6 +62,8 @@ public class AccountCurrentController(
// Log error but don't fail the request - PerkSubscription is optional
Console.WriteLine($"Failed to populate PerkSubscription for account {account.Id}: {ex.Message}");
}
account.Contacts = await padlockContacts.ListContactsAsync(account.Id);
}
return Ok(account);
@@ -718,11 +720,7 @@ public class AccountCurrentController(
public async Task<ActionResult<List<SnAccountContact>>> GetContacts()
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contacts = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id)
.ToListAsync();
var contacts = await padlockContacts.ListContactsAsync(currentUser.Id);
return Ok(contacts);
}
@@ -737,16 +735,7 @@ public class AccountCurrentController(
public async Task<ActionResult<SnAccountContact>> CreateContact([FromBody] AccountContactRequest request)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
try
{
var contact = await accounts.CreateContactMethod(currentUser, request.Type, request.Content);
return Ok(contact);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return ContactApiMovedToPadlock();
}
[HttpPost("contacts/{id:guid}/verify")]
@@ -754,21 +743,7 @@ public class AccountCurrentController(
public async Task<ActionResult<SnAccountContact>> VerifyContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
.FirstOrDefaultAsync();
if (contact is null) return NotFound();
try
{
await accounts.VerifyContactMethod(currentUser, contact);
return Ok(contact);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return ContactApiMovedToPadlock();
}
[HttpPost("contacts/{id:guid}/primary")]
@@ -776,21 +751,7 @@ public class AccountCurrentController(
public async Task<ActionResult<SnAccountContact>> SetPrimaryContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
.FirstOrDefaultAsync();
if (contact is null) return NotFound();
try
{
contact = await accounts.SetContactMethodPrimary(currentUser, contact);
return Ok(contact);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return ContactApiMovedToPadlock();
}
[HttpPost("contacts/{id:guid}/public")]
@@ -798,21 +759,7 @@ public class AccountCurrentController(
public async Task<ActionResult<SnAccountContact>> SetPublicContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
.FirstOrDefaultAsync();
if (contact is null) return NotFound();
try
{
contact = await accounts.SetContactMethodPublic(currentUser, contact, true);
return Ok(contact);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return ContactApiMovedToPadlock();
}
[HttpDelete("contacts/{id:guid}/public")]
@@ -820,21 +767,7 @@ public class AccountCurrentController(
public async Task<ActionResult<SnAccountContact>> UnsetPublicContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
.FirstOrDefaultAsync();
if (contact is null) return NotFound();
try
{
contact = await accounts.SetContactMethodPublic(currentUser, contact, false);
return Ok(contact);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return ContactApiMovedToPadlock();
}
[HttpDelete("contacts/{id:guid}")]
@@ -842,21 +775,18 @@ public class AccountCurrentController(
public async Task<ActionResult<SnAccountContact>> DeleteContact(Guid id)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
return ContactApiMovedToPadlock();
}
var contact = await db.AccountContacts
.Where(c => c.AccountId == currentUser.Id && c.Id == id)
.FirstOrDefaultAsync();
if (contact is null) return NotFound();
try
private ActionResult ContactApiMovedToPadlock()
{
return StatusCode(StatusCodes.Status410Gone, new ApiError
{
await accounts.DeleteContactMethod(currentUser, contact);
return NoContent();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
Code = "GONE",
Message = "Contacts are managed by Padlock. Use /padlock/accounts/me/contacts endpoints.",
Status = StatusCodes.Status410Gone,
TraceId = HttpContext.TraceIdentifier
});
}
[HttpGet("badges")]

View File

@@ -11,7 +11,7 @@ using NATS.Client.Core;
using NodaTime;
using NodaTime.Extensions;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class AccountEventService(
AppDatabase db,

View File

@@ -1,11 +1,11 @@
using DysonNetwork.Pass.Credit;
using DysonNetwork.Passport.Credit;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Networking;
using DysonNetwork.Shared.Registry;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/accounts")]
@@ -24,7 +24,6 @@ public class AccountPublicController(
var accountQuery = db.Accounts
.Include(e => e.Badges)
.Include(e => e.Profile)
.Include(e => e.Contacts.Where(c => c.IsPublic))
.AsQueryable();
if (Guid.TryParse(name, out var guid))
@@ -36,6 +35,7 @@ public class AccountPublicController(
var account = await accountQuery.FirstOrDefaultAsync();
if (account is null) return NotFound(ApiError.NotFound(name, traceId: HttpContext.TraceIdentifier));
await EnsureProfileAsync(account);
account.Contacts = [];
// Populate PerkSubscription from Wallet service via gRPC
try
@@ -103,6 +103,7 @@ public class AccountPublicController(
foreach (var account in accounts)
{
await EnsureProfileAsync(account);
account.Contacts = [];
}
// Populate PerkSubscriptions from Wallet service via gRPC

View File

@@ -1,8 +1,6 @@
using System.Globalization;
using DysonNetwork.Pass.Affiliation;
using DysonNetwork.Pass.Auth.OpenId;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Pass.Mailer;
using DysonNetwork.Passport.Affiliation;
using DysonNetwork.Passport.Auth.OpenId;
using DysonNetwork.Passport.Mailer;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Localization;
@@ -10,14 +8,11 @@ using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Queue;
using Microsoft.EntityFrameworkCore;
using NATS.Client.Core;
using NATS.Net;
using NodaTime;
using OtpNet;
using AuthService = DysonNetwork.Pass.Auth.AuthService;
using DysonNetwork.Shared.Registry;
using AuthService = DysonNetwork.Passport.Auth.AuthService;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class AccountService(
AppDatabase db,

View File

@@ -6,13 +6,14 @@ using Grpc.Core;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class AccountServiceGrpc(
AppDatabase db,
AccountEventService accountEvents,
RelationshipService relationships,
RemoteSubscriptionService remoteSubscription,
PadlockAccountContactService padlockContacts,
AccountService accountService,
ILogger<AccountServiceGrpc> logger
)
@@ -34,7 +35,6 @@ public class AccountServiceGrpc(
var account = await _db.Accounts
.AsNoTracking()
.Include(a => a.Profile)
.Include(a => a.Contacts.Where(c => c.IsPublic))
.FirstOrDefaultAsync(a => a.Id == accountId);
if (account == null)
@@ -42,6 +42,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscription from Wallet service via gRPC
await PopulatePerkSubscriptionAsync(account);
await padlockContacts.PopulateContactsAsync(account, cancellationToken: context.CancellationToken);
return account.ToProtoValue();
}
@@ -63,6 +64,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscription from Wallet service via gRPC
await PopulatePerkSubscriptionAsync(account);
await padlockContacts.PopulateContactsAsync(account, cancellationToken: context.CancellationToken);
return account.ToProtoValue();
}
@@ -84,6 +86,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
await PopulateContactsAsync(accounts, context.CancellationToken);
var response = new DyGetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
@@ -108,6 +111,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
await PopulateContactsAsync(accounts, context.CancellationToken);
var response = new DyGetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
@@ -147,6 +151,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
await PopulateContactsAsync(accounts, context.CancellationToken);
var response = new DyGetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
@@ -164,6 +169,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
await PopulateContactsAsync(accounts, context.CancellationToken);
var response = new DyGetAccountBatchResponse();
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
@@ -203,6 +209,7 @@ public class AccountServiceGrpc(
// Populate PerkSubscriptions from Wallet service via gRPC
await PopulatePerkSubscriptionsAsync(accounts);
await PopulateContactsAsync(accounts, context.CancellationToken);
var response = new DyListAccountsResponse
{
@@ -264,6 +271,31 @@ public class AccountServiceGrpc(
}
}
public override async Task<DyListContactsResponse> ListContacts(DyListContactsRequest request,
ServerCallContext context)
{
if (!Guid.TryParse(request.AccountId, out var accountId))
throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid account ID format"));
AccountContactType? type = request.Type switch
{
DyAccountContactType.DyEmail => AccountContactType.Email,
DyAccountContactType.DyPhoneNumber => AccountContactType.PhoneNumber,
DyAccountContactType.DyAddress => AccountContactType.Address,
_ => null
};
var contacts = await padlockContacts.ListContactsAsync(
accountId,
type,
request.VerifiedOnly,
context.CancellationToken);
var response = new DyListContactsResponse();
response.Contacts.AddRange(contacts.Select(c => c.ToProtoValue()));
return response;
}
public override async Task<DyGetRelationshipResponse> GetRelationship(
DyGetRelationshipRequest request,
ServerCallContext context
@@ -463,4 +495,12 @@ public class AccountServiceGrpc(
accounts.Count);
}
}
}
private async Task PopulateContactsAsync(List<SnAccount> accounts, CancellationToken cancellationToken)
{
foreach (var account in accounts)
{
await padlockContacts.PopulateContactsAsync(account, cancellationToken: cancellationToken);
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
/// <summary>
/// Service for handling username generation and validation

View File

@@ -3,7 +3,7 @@ using DysonNetwork.Shared.Extensions;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class ActionLogService(GeoService geo, FlushBufferService fbs)
{

View File

@@ -4,7 +4,7 @@ using Grpc.Core;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class ActionLogServiceGrpc(
ActionLogService actionLogService,

View File

@@ -3,9 +3,9 @@ using DysonNetwork.Shared.Proto;
using Grpc.Core;
using Microsoft.EntityFrameworkCore;
using NodaTime.Serialization.Protobuf;
using AuthService = DysonNetwork.Pass.Auth.AuthService;
using AuthService = DysonNetwork.Passport.Auth.AuthService;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class BotAccountReceiverGrpc(
AppDatabase db,

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public record FortuneSaying(
string Content,

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/friends")]

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/spells")]

View File

@@ -1,14 +1,14 @@
using System.Security.Cryptography;
using System.Text.Json;
using DysonNetwork.Pass.Mailer;
using DysonNetwork.Passport.Mailer;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using DysonNetwork.Shared.Localization;
using NodaTime;
using EmailResource = DysonNetwork.Pass.Localization.EmailResource;
using EmailResource = DysonNetwork.Passport.Localization.EmailResource;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class MagicSpellService(
AppDatabase db,

View File

@@ -1,7 +1,7 @@
using Nager.Holiday;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
/// <summary>
/// Reference from Nager.Holiday

View File

@@ -2,7 +2,7 @@ using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/notable")]

View File

@@ -2,7 +2,7 @@ using DysonNetwork.Shared.Cache;
using Nager.Holiday;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class NotableDaysService(ICacheService cache)
{

View File

@@ -0,0 +1,45 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
namespace DysonNetwork.Passport.Account;
public class PadlockAccountContactService(
DyAccountService.DyAccountServiceClient accountGrpc,
ILogger<PadlockAccountContactService> logger
)
{
public async Task<List<SnAccountContact>> ListContactsAsync(Guid accountId, AccountContactType? type = null,
bool verifiedOnly = false, CancellationToken cancellationToken = default)
{
try
{
var request = new DyListContactsRequest
{
AccountId = accountId.ToString(),
VerifiedOnly = verifiedOnly,
Type = type switch
{
AccountContactType.Email => DyAccountContactType.DyEmail,
AccountContactType.PhoneNumber => DyAccountContactType.DyPhoneNumber,
AccountContactType.Address => DyAccountContactType.DyAddress,
_ => DyAccountContactType.Unspecified
}
};
var response = await accountGrpc.ListContactsAsync(request, cancellationToken: cancellationToken);
return response.Contacts.Select(SnAccountContact.FromProtoValue).ToList();
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to fetch account contacts from Padlock for account {AccountId}", accountId);
return [];
}
}
public async Task PopulateContactsAsync(SnAccount account, bool verifiedOnly = false,
CancellationToken cancellationToken = default)
{
account.Contacts = await ListContactsAsync(account.Id, verifiedOnly: verifiedOnly,
cancellationToken: cancellationToken);
}
}

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
/// <summary>
/// Controller for managing user presence activities with lease-based expiration.

View File

@@ -1,6 +1,6 @@
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Account.Presences;
namespace DysonNetwork.Passport.Account.Presences;
/// <summary>
/// Interface for presence services that can update user presence activities

View File

@@ -1,6 +1,6 @@
using Quartz;
namespace DysonNetwork.Pass.Account.Presences;
namespace DysonNetwork.Passport.Account.Presences;
public class PresenceUpdateJob(
IEnumerable<IPresenceService> presenceServices,

View File

@@ -1,4 +1,4 @@
namespace DysonNetwork.Pass.Account.Presences;
namespace DysonNetwork.Passport.Account.Presences;
public enum PresenceUpdateStage
{

View File

@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
using NodaTime;
using SpotifyAPI.Web;
namespace DysonNetwork.Pass.Account.Presences;
namespace DysonNetwork.Passport.Account.Presences;
public class SpotifyPresenceService(
AppDatabase db,

View File

@@ -4,7 +4,7 @@ using NodaTime;
using SteamWebAPI2.Interfaces;
using SteamWebAPI2.Utilities;
namespace DysonNetwork.Pass.Account.Presences;
namespace DysonNetwork.Passport.Account.Presences;
public class SteamPresenceService(
AppDatabase db,

View File

@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
[ApiController]
[Route("/api/relationships")]

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Pass.Localization;
using DysonNetwork.Passport.Localization;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
@@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
using DysonNetwork.Shared.Localization;
using NodaTime;
namespace DysonNetwork.Pass.Account;
namespace DysonNetwork.Passport.Account;
public class RelationshipService(
AppDatabase db,

View File

@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Affiliation;
namespace DysonNetwork.Passport.Affiliation;
[ApiController]
[Route("/api/affiliations")]

View File

@@ -2,7 +2,7 @@ using System.Security.Cryptography;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Affiliation;
namespace DysonNetwork.Passport.Affiliation;
public class AffiliationSpellService(AppDatabase db)
{

View File

@@ -1,7 +1,7 @@
using System.Linq.Expressions;
using System.Text.Json;
using System.Text.Json.Serialization;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Passport.Permission;
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
@@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Query;
using NodaTime;
using Quartz;
namespace DysonNetwork.Pass;
namespace DysonNetwork.Passport;
public class AppDatabase(
DbContextOptions<AppDatabase> options,

View File

@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
[ApiController]
[Route("/api/auth/keys")]

View File

@@ -1,4 +1,4 @@
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
public static class AuthCacheConstants
{

View File

@@ -7,11 +7,11 @@ using DysonNetwork.Shared.Extensions;
using DysonNetwork.Shared.Geometry;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Localization;
using AccountService = DysonNetwork.Pass.Account.AccountService;
using ActionLogService = DysonNetwork.Pass.Account.ActionLogService;
using AccountService = DysonNetwork.Passport.Account.AccountService;
using ActionLogService = DysonNetwork.Passport.Account.ActionLogService;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
[ApiController]
[Route("/api/auth")]
@@ -329,7 +329,7 @@ public class AuthController(
}
[HttpPost("login/session")]
[Microsoft.AspNetCore.Authorization.Authorize] // Use full namespace to avoid ambiguity with DysonNetwork.Pass.Permission.Authorize
[Microsoft.AspNetCore.Authorization.Authorize] // Use full namespace to avoid ambiguity with DysonNetwork.Passport.Permission.Authorize
public async Task<ActionResult<TokenExchangeResponse>> LoginFromSession([FromBody] NewSessionRequest request)
{
if (HttpContext.Items["CurrentUser"] is not SnAccount ||

View File

@@ -9,7 +9,7 @@ using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
public class AuthService(
AppDatabase db,

View File

@@ -1,6 +1,6 @@
using System.Security.Cryptography;
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
public class AuthTokenKeyProvider(IConfiguration config)
{

View File

@@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using DysonNetwork.Shared.Extensions;
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
[ApiController]
[Route("/api/captcha")]

View File

@@ -1,4 +1,4 @@
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
public class CaptchaVerificationResponse
{

View File

@@ -1,23 +1,22 @@
using System.Security.Cryptography;
using DysonNetwork.Pass.Auth.OidcProvider.Responses;
using DysonNetwork.Pass.Auth.OidcProvider.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System.Text.Json.Serialization;
using System.Web;
using DysonNetwork.Pass.Auth.OidcProvider.Options;
using Microsoft.EntityFrameworkCore;
using DysonNetwork.Passport.Auth.OidcProvider.Options;
using DysonNetwork.Passport.Auth.OidcProvider.Responses;
using DysonNetwork.Passport.Auth.OidcProvider.Services;
using NodaTime;
using DysonNetwork.Shared.Models;
using Microsoft.IdentityModel.Tokens;
namespace DysonNetwork.Pass.Auth.OidcProvider.Controllers;
namespace DysonNetwork.Passport.Auth.OidcProvider.Controllers;
[Route("/api/auth/open")]
[ApiController]
public class OidcProviderController(
AppDatabase db,
Account.PadlockAccountContactService padlockContacts,
OidcProviderService oidcService,
IConfiguration configuration,
IOptions<OidcProviderOptions> options,
@@ -318,9 +317,8 @@ public class OidcProviderController(
userInfo["preferred_username"] = currentUser.Nick;
}
var userEmail = await db.AccountContacts
.Where(c => c.Type == AccountContactType.Email && c.AccountId == currentUser.Id)
.FirstOrDefaultAsync();
var userEmail = (await padlockContacts.ListContactsAsync(currentUser.Id, AccountContactType.Email))
.FirstOrDefault();
if (scopes.Contains("email") && userEmail is not null)
{
userInfo["email"] = userEmail.Content;

View File

@@ -1,6 +1,6 @@
using NodaTime;
namespace DysonNetwork.Pass.Auth.OidcProvider.Models;
namespace DysonNetwork.Passport.Auth.OidcProvider.Models;
public class AuthorizationCodeInfo
{

View File

@@ -1,4 +1,4 @@
namespace DysonNetwork.Pass.Auth.OidcProvider.Models;
namespace DysonNetwork.Passport.Auth.OidcProvider.Models;
public class ExternalUserInfo
{

View File

@@ -1,6 +1,6 @@
using System.Security.Cryptography;
namespace DysonNetwork.Pass.Auth.OidcProvider.Options;
namespace DysonNetwork.Passport.Auth.OidcProvider.Options;
public class OidcProviderOptions
{

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
namespace DysonNetwork.Passport.Auth.OidcProvider.Responses;
public class AuthorizationResponse
{

View File

@@ -1,6 +1,6 @@
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
namespace DysonNetwork.Passport.Auth.OidcProvider.Responses;
public class ClientInfoResponse
{

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
namespace DysonNetwork.Passport.Auth.OidcProvider.Responses;
public class ErrorResponse
{

View File

@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace DysonNetwork.Pass.Auth.OidcProvider.Responses;
namespace DysonNetwork.Passport.Auth.OidcProvider.Responses;
public class TokenResponse
{

View File

@@ -2,9 +2,9 @@ using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using DysonNetwork.Pass.Auth.OidcProvider.Models;
using DysonNetwork.Pass.Auth.OidcProvider.Options;
using DysonNetwork.Pass.Auth.OidcProvider.Responses;
using DysonNetwork.Passport.Auth.OidcProvider.Models;
using DysonNetwork.Passport.Auth.OidcProvider.Options;
using DysonNetwork.Passport.Auth.OidcProvider.Responses;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
@@ -15,12 +15,13 @@ using NodaTime;
using AccountContactType = DysonNetwork.Shared.Models.AccountContactType;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
namespace DysonNetwork.Pass.Auth.OidcProvider.Services;
namespace DysonNetwork.Passport.Auth.OidcProvider.Services;
public class OidcProviderService(
AppDatabase db,
AuthService auth,
DyCustomAppService.DyCustomAppServiceClient customApps,
Account.PadlockAccountContactService padlockContacts,
ICacheService cache,
IOptions<OidcProviderOptions> options,
ILogger<OidcProviderService> logger
@@ -74,7 +75,6 @@ public class OidcProviderService(
queryable = queryable
.Include(s => s.Account)
.ThenInclude(a => a.Profile)
.Include(a => a.Account.Contacts)
.AsQueryable();
return await queryable
@@ -260,7 +260,6 @@ public class OidcProviderService(
var account = await db.Accounts
.Where(a => a.Id == authCode.AccountId)
.Include(a => a.Profile)
.Include(a => a.Contacts)
.FirstOrDefaultAsync();
if (account == null) throw new InvalidOperationException("Account not found");
session = await auth.CreateSessionForOidcAsync(account, SystemClock.Instance.GetCurrentInstant(), clientId);
@@ -271,6 +270,8 @@ public class OidcProviderService(
session = existingSession;
}
session.Account.Contacts = await padlockContacts.ListContactsAsync(session.AccountId);
return (session, authCode.Nonce, authCode.Scopes);
}
@@ -663,4 +664,4 @@ public class OidcProviderService(
return string.Equals(base64, codeChallenge, StringComparison.Ordinal);
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class AfdianOidcService(
IConfiguration configuration,

View File

@@ -1,7 +1,7 @@
using System.ComponentModel.DataAnnotations;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class AppleMobileConnectRequest
{

View File

@@ -6,7 +6,7 @@ using System.Text.Json.Serialization;
using DysonNetwork.Shared.Cache;
using Microsoft.IdentityModel.Tokens;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
/// <summary>
/// Implementation of OpenID Connect service for Apple Sign In

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Pass.Account;
using DysonNetwork.Passport.Account;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.WebUtilities;
using NodaTime;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
[ApiController]
[Route("/api/accounts/me/connections")]

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class DiscordOidcService(
IConfiguration configuration,

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class GitHubOidcService(
IConfiguration configuration,
@@ -81,7 +81,7 @@ public class GitHubOidcService(
var client = HttpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/user");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("User-Agent", "DysonNetwork.Pass");
request.Headers.Add("User-Agent", "DysonNetwork.Passport");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
@@ -113,7 +113,7 @@ public class GitHubOidcService(
var client = HttpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/user/emails");
request.Headers.Add("Authorization", $"Bearer {accessToken}");
request.Headers.Add("User-Agent", "DysonNetwork.Pass");
request.Headers.Add("User-Agent", "DysonNetwork.Passport");
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) return null;

View File

@@ -2,7 +2,7 @@ using System.IdentityModel.Tokens.Jwt;
using DysonNetwork.Shared.Cache;
using Microsoft.IdentityModel.Tokens;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class GoogleOidcService(
IConfiguration configuration,

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class MicrosoftOidcService(
IConfiguration configuration,

View File

@@ -1,4 +1,4 @@
using DysonNetwork.Pass.Account;
using DysonNetwork.Passport.Account;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Mvc;
@@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using NodaTime;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
[ApiController]
[Route("/api/auth/login")]

View File

@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using NodaTime;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
/// <summary>
/// Base service for OpenID Connect authentication providers

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
/// <summary>
/// Represents the state parameter used in OpenID Connect flows.

View File

@@ -1,4 +1,4 @@
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
/// <summary>
/// Represents the user information from an OIDC provider

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class SpotifyOidcService(
IConfiguration configuration,

View File

@@ -1,6 +1,6 @@
using DysonNetwork.Shared.Cache;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
public class SteamOidcService(
IConfiguration configuration,

View File

@@ -2,7 +2,7 @@ using System.IdentityModel.Tokens.Jwt;
using System.Text.Json;
using Microsoft.IdentityModel.Tokens;
namespace DysonNetwork.Pass.Auth.OpenId;
namespace DysonNetwork.Passport.Auth.OpenId;
/// <summary>
/// Defines how to retrieve user information from an OIDC provider

View File

@@ -6,7 +6,7 @@ using DysonNetwork.Shared.Registry;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Auth;
namespace DysonNetwork.Passport.Auth;
public class TokenAuthService(
AppDatabase db,

View File

@@ -3,7 +3,7 @@ using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using NodaTime;
namespace DysonNetwork.Pass.Credit;
namespace DysonNetwork.Passport.Credit;
public class SocialCreditService(AppDatabase db, ICacheService cache)
{

View File

@@ -2,7 +2,7 @@ using DysonNetwork.Shared.Proto;
using Grpc.Core;
using NodaTime.Serialization.Protobuf;
namespace DysonNetwork.Pass.Credit;
namespace DysonNetwork.Passport.Credit;
public class SocialCreditServiceGrpc(SocialCreditService creditService)
: DySocialCreditService.DySocialCreditServiceBase

View File

@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
using NodaTime;
using Quartz;
namespace DysonNetwork.Pass.Credit;
namespace DysonNetwork.Passport.Credit;
public class SocialCreditValidationJob(AppDatabase db, ICacheService cache, ILogger<SocialCreditValidationJob> logger) : IJob
{

View File

@@ -3,7 +3,7 @@ using DysonNetwork.Shared.Models;
using NodaTime;
using Quartz;
namespace DysonNetwork.Pass.Handlers;
namespace DysonNetwork.Passport.Handlers;
public class ActionLogFlushHandler(IServiceProvider sp) : IFlushHandler<SnActionLog>
{

View File

@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore;
using NodaTime;
using Quartz;
namespace DysonNetwork.Pass.Handlers;
namespace DysonNetwork.Passport.Handlers;
public class LastActiveInfo
{

View File

@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
namespace DysonNetwork.Pass;
namespace DysonNetwork.Passport;
[ApiController]
[Route("/api/ip-check")]

View File

@@ -2,7 +2,7 @@ using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Registry;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Leveling;
namespace DysonNetwork.Passport.Leveling;
public class ExperienceService(AppDatabase db, RemoteSubscriptionService subscriptions)
{

View File

@@ -1,7 +1,7 @@
using DysonNetwork.Shared.Proto;
using Grpc.Core;
namespace DysonNetwork.Pass.Leveling;
namespace DysonNetwork.Passport.Leveling;
public class ExperienceServiceGrpc(ExperienceService experienceService) : DyExperienceService.DyExperienceServiceBase
{

View File

@@ -0,0 +1,6 @@
namespace DysonNetwork.Passport.Localization;
public class AccountEventResource
{
}

View File

@@ -0,0 +1,5 @@
namespace DysonNetwork.Passport.Localization;
public class EmailResource
{
}

View File

@@ -0,0 +1,6 @@
namespace DysonNetwork.Passport.Localization;
public class NotificationResource
{
}

View File

@@ -0,0 +1,6 @@
namespace DysonNetwork.Passport.Localization;
public class SharedResource
{
}

Some files were not shown because too many files have changed in this diff Show More