Compare commits
2 Commits
50e888b075
...
5d7429a416
Author | SHA1 | Date | |
---|---|---|---|
|
5d7429a416 | ||
|
fb7e52d6f3 |
@@ -1,7 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using DysonNetwork.Develop.Project;
|
using DysonNetwork.Develop.Project;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
using NodaTime;
|
|
||||||
using NodaTime.Serialization.Protobuf;
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
namespace DysonNetwork.Develop.Identity;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
@@ -15,6 +15,8 @@ public class BotAccount : ModelBase
|
|||||||
|
|
||||||
public Guid ProjectId { get; set; }
|
public Guid ProjectId { get; set; }
|
||||||
public DevProject Project { get; set; } = null!;
|
public DevProject Project { get; set; } = null!;
|
||||||
|
|
||||||
|
[NotMapped] public AccountReference? Account { get; set; }
|
||||||
|
|
||||||
public Shared.Proto.BotAccount ToProtoValue()
|
public Shared.Proto.BotAccount ToProtoValue()
|
||||||
{
|
{
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using DysonNetwork.Develop.Project;
|
using DysonNetwork.Develop.Project;
|
||||||
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 NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
namespace DysonNetwork.Develop.Identity;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
@@ -13,18 +16,61 @@ public class BotAccountController(
|
|||||||
BotAccountService botService,
|
BotAccountService botService,
|
||||||
DeveloperService developerService,
|
DeveloperService developerService,
|
||||||
DevProjectService projectService,
|
DevProjectService projectService,
|
||||||
ILogger<BotAccountController> logger
|
ILogger<BotAccountController> logger,
|
||||||
|
AccountClientHelper accounts
|
||||||
)
|
)
|
||||||
: ControllerBase
|
: ControllerBase
|
||||||
{
|
{
|
||||||
public record BotRequest(
|
public class CommonBotRequest
|
||||||
[Required] [MaxLength(1024)] string? Slug
|
{
|
||||||
);
|
[MaxLength(256)] public string? FirstName { get; set; }
|
||||||
|
[MaxLength(256)] public string? MiddleName { get; set; }
|
||||||
|
[MaxLength(256)] public string? LastName { get; set; }
|
||||||
|
[MaxLength(1024)] public string? Gender { get; set; }
|
||||||
|
[MaxLength(1024)] public string? Pronouns { get; set; }
|
||||||
|
[MaxLength(1024)] public string? TimeZone { get; set; }
|
||||||
|
[MaxLength(1024)] public string? Location { get; set; }
|
||||||
|
[MaxLength(4096)] public string? Bio { get; set; }
|
||||||
|
public Instant? Birthday { get; set; }
|
||||||
|
|
||||||
public record UpdateBotRequest(
|
[MaxLength(32)] public string? PictureId { get; set; }
|
||||||
[MaxLength(1024)] string? Slug,
|
[MaxLength(32)] public string? BackgroundId { get; set; }
|
||||||
bool? IsActive
|
}
|
||||||
) : BotRequest(Slug);
|
|
||||||
|
public class BotCreateRequest : CommonBotRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[MinLength(2)]
|
||||||
|
[MaxLength(256)]
|
||||||
|
[RegularExpression(@"^[A-Za-z0-9_-]+$",
|
||||||
|
ErrorMessage = "Name can only contain letters, numbers, underscores, and hyphens.")
|
||||||
|
]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required] [MaxLength(256)] public string Nick { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required] [MaxLength(1024)] public string Slug { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(128)] public string Language { get; set; } = "en-us";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateBotRequest : CommonBotRequest
|
||||||
|
{
|
||||||
|
[MinLength(2)]
|
||||||
|
[MaxLength(256)]
|
||||||
|
[RegularExpression(@"^[A-Za-z0-9_-]+$",
|
||||||
|
ErrorMessage = "Name can only contain letters, numbers, underscores, and hyphens.")
|
||||||
|
]
|
||||||
|
public string? Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(256)] public string? Nick { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[Required] [MaxLength(1024)] public string Slug { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(128)] public string? Language { get; set; }
|
||||||
|
|
||||||
|
public bool? IsActive { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> ListBots(
|
public async Task<IActionResult> ListBots(
|
||||||
@@ -47,7 +93,7 @@ public class BotAccountController(
|
|||||||
return NotFound("Project not found or you don't have access");
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
var bots = await botService.GetBotsByProjectAsync(projectId);
|
var bots = await botService.GetBotsByProjectAsync(projectId);
|
||||||
return Ok(bots);
|
return Ok(await botService.LoadBotsAccountAsync(bots));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{botId:guid}")]
|
[HttpGet("{botId:guid}")]
|
||||||
@@ -75,18 +121,16 @@ public class BotAccountController(
|
|||||||
if (bot is null || bot.ProjectId != projectId)
|
if (bot is null || bot.ProjectId != projectId)
|
||||||
return NotFound("Bot not found");
|
return NotFound("Bot not found");
|
||||||
|
|
||||||
return Ok(bot);
|
return Ok(await botService.LoadBotAccountAsync(bot));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> CreateBot(
|
public async Task<IActionResult> CreateBot(
|
||||||
[FromRoute] string pubName,
|
[FromRoute] string pubName,
|
||||||
[FromRoute] Guid projectId,
|
[FromRoute] Guid projectId,
|
||||||
[FromBody] BotRequest request
|
[FromBody] BotCreateRequest createRequest
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(request.Slug))
|
|
||||||
return BadRequest("Name is required");
|
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
@@ -102,9 +146,30 @@ public class BotAccountController(
|
|||||||
if (project is null)
|
if (project is null)
|
||||||
return NotFound("Project not found or you don't have access");
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
|
var account = new Account()
|
||||||
|
{
|
||||||
|
Name = createRequest.Name,
|
||||||
|
Nick = createRequest.Nick,
|
||||||
|
Language = createRequest.Language,
|
||||||
|
Profile = new AccountProfile()
|
||||||
|
{
|
||||||
|
Bio = createRequest.Bio,
|
||||||
|
Gender = createRequest.Gender,
|
||||||
|
FirstName = createRequest.FirstName,
|
||||||
|
MiddleName = createRequest.MiddleName,
|
||||||
|
LastName = createRequest.LastName,
|
||||||
|
TimeZone = createRequest.TimeZone,
|
||||||
|
Pronouns = createRequest.Pronouns,
|
||||||
|
Location = createRequest.Location,
|
||||||
|
Birthday = createRequest.Birthday?.ToTimestamp(),
|
||||||
|
Picture = new CloudFile() { Id = createRequest.PictureId },
|
||||||
|
Background = new CloudFile() { Id = createRequest.BackgroundId }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bot = await botService.CreateBotAsync(project, request.Slug);
|
var bot = await botService.CreateBotAsync(project, createRequest.Slug, account);
|
||||||
return Ok(bot);
|
return Ok(bot);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -141,10 +206,29 @@ public class BotAccountController(
|
|||||||
if (bot is null || bot.ProjectId != projectId)
|
if (bot is null || bot.ProjectId != projectId)
|
||||||
return NotFound("Bot not found");
|
return NotFound("Bot not found");
|
||||||
|
|
||||||
|
var botAccount = await accounts.GetBotAccount(bot.Id);
|
||||||
|
|
||||||
|
if (request.Name is not null) botAccount.Name = request.Name;
|
||||||
|
if (request.Nick is not null) botAccount.Nick = request.Nick;
|
||||||
|
if (request.Language is not null) botAccount.Language = request.Language;
|
||||||
|
if (request.Bio is not null) botAccount.Profile.Bio = request.Bio;
|
||||||
|
if (request.Gender is not null) botAccount.Profile.Gender = request.Gender;
|
||||||
|
if (request.FirstName is not null) botAccount.Profile.FirstName = request.FirstName;
|
||||||
|
if (request.MiddleName is not null) botAccount.Profile.MiddleName = request.MiddleName;
|
||||||
|
if (request.LastName is not null) botAccount.Profile.LastName = request.LastName;
|
||||||
|
if (request.TimeZone is not null) botAccount.Profile.TimeZone = request.TimeZone;
|
||||||
|
if (request.Pronouns is not null) botAccount.Profile.Pronouns = request.Pronouns;
|
||||||
|
if (request.Location is not null) botAccount.Profile.Location = request.Location;
|
||||||
|
if (request.Birthday is not null) botAccount.Profile.Birthday = request.Birthday?.ToTimestamp();
|
||||||
|
if (request.PictureId is not null) botAccount.Profile.Picture = new CloudFile() { Id = request.PictureId };
|
||||||
|
if (request.BackgroundId is not null)
|
||||||
|
botAccount.Profile.Background = new CloudFile() { Id = request.BackgroundId };
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var updatedBot = await botService.UpdateBotAsync(
|
var updatedBot = await botService.UpdateBotAsync(
|
||||||
bot,
|
bot,
|
||||||
|
botAccount,
|
||||||
request.Slug,
|
request.Slug,
|
||||||
request.IsActive
|
request.IsActive
|
||||||
);
|
);
|
||||||
|
@@ -1,13 +1,18 @@
|
|||||||
using DysonNetwork.Develop.Project;
|
using DysonNetwork.Develop.Project;
|
||||||
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using DysonNetwork.Shared.Registry;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
|
||||||
using NodaTime.Serialization.Protobuf;
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
namespace DysonNetwork.Develop.Identity;
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAccountReceiverServiceClient accountReceiver)
|
public class BotAccountService(
|
||||||
|
AppDatabase db,
|
||||||
|
BotAccountReceiverService.BotAccountReceiverServiceClient accountReceiver,
|
||||||
|
AccountClientHelper accounts
|
||||||
|
)
|
||||||
{
|
{
|
||||||
public async Task<BotAccount?> GetBotByIdAsync(Guid id)
|
public async Task<BotAccount?> GetBotByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
@@ -23,39 +28,23 @@ public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAcco
|
|||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BotAccount> CreateBotAsync(DevProject project, string slug)
|
public async Task<BotAccount> CreateBotAsync(DevProject project, string slug, Account account)
|
||||||
{
|
{
|
||||||
// First, check if a bot with this slug already exists in this project
|
// First, check if a bot with this slug already exists in this project
|
||||||
var existingBot = await db.BotAccounts
|
var existingBot = await db.BotAccounts
|
||||||
.FirstOrDefaultAsync(b => b.ProjectId == project.Id && b.Slug == slug);
|
.FirstOrDefaultAsync(b => b.ProjectId == project.Id && b.Slug == slug);
|
||||||
|
|
||||||
if (existingBot != null)
|
if (existingBot != null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("A bot with this slug already exists in this project.");
|
throw new InvalidOperationException("A bot with this slug already exists in this project.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = SystemClock.Instance.GetCurrentInstant();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// First create the bot account in the Pass service
|
|
||||||
var createRequest = new CreateBotAccountRequest
|
var createRequest = new CreateBotAccountRequest
|
||||||
{
|
{
|
||||||
AutomatedId = Guid.NewGuid().ToString(),
|
AutomatedId = Guid.NewGuid().ToString(),
|
||||||
Account = new Account
|
Account = account
|
||||||
{
|
|
||||||
Name = slug,
|
|
||||||
Nick = $"Bot {slug}",
|
|
||||||
Language = "en",
|
|
||||||
Profile = new AccountProfile
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
CreatedAt = now.ToTimestamp(),
|
|
||||||
UpdatedAt = now.ToTimestamp()
|
|
||||||
},
|
|
||||||
CreatedAt = now.ToTimestamp(),
|
|
||||||
UpdatedAt = now.ToTimestamp()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var createResponse = await accountReceiver.CreateBotAccountAsync(createRequest);
|
var createResponse = await accountReceiver.CreateBotAccountAsync(createRequest);
|
||||||
@@ -80,7 +69,8 @@ public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAcco
|
|||||||
}
|
}
|
||||||
catch (RpcException ex) when (ex.StatusCode == StatusCode.AlreadyExists)
|
catch (RpcException ex) when (ex.StatusCode == StatusCode.AlreadyExists)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("A bot account with this ID already exists in the authentication service.", ex);
|
throw new InvalidOperationException(
|
||||||
|
"A bot account with this ID already exists in the authentication service.", ex);
|
||||||
}
|
}
|
||||||
catch (RpcException ex) when (ex.StatusCode == StatusCode.InvalidArgument)
|
catch (RpcException ex) when (ex.StatusCode == StatusCode.InvalidArgument)
|
||||||
{
|
{
|
||||||
@@ -92,7 +82,8 @@ public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAcco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BotAccount> UpdateBotAsync(BotAccount bot, string? slug = null, bool? isActive = null)
|
public async Task<BotAccount> UpdateBotAsync(BotAccount bot, Account account, string? slug = null,
|
||||||
|
bool? isActive = null)
|
||||||
{
|
{
|
||||||
var updated = false;
|
var updated = false;
|
||||||
if (slug != null && bot.Slug != slug)
|
if (slug != null && bot.Slug != slug)
|
||||||
@@ -100,7 +91,7 @@ public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAcco
|
|||||||
bot.Slug = slug;
|
bot.Slug = slug;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isActive.HasValue && bot.IsActive != isActive.Value)
|
if (isActive.HasValue && bot.IsActive != isActive.Value)
|
||||||
{
|
{
|
||||||
bot.IsActive = isActive.Value;
|
bot.IsActive = isActive.Value;
|
||||||
@@ -108,19 +99,14 @@ public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAcco
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!updated) return bot;
|
if (!updated) return bot;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Update the bot account in the Pass service
|
// Update the bot account in the Pass service
|
||||||
var updateRequest = new UpdateBotAccountRequest
|
var updateRequest = new UpdateBotAccountRequest
|
||||||
{
|
{
|
||||||
AutomatedId = bot.Id.ToString(),
|
AutomatedId = bot.Id.ToString(),
|
||||||
Account = new Shared.Proto.Account
|
Account = account
|
||||||
{
|
|
||||||
Name = $"bot-{bot.Slug}",
|
|
||||||
Nick = $"Bot {bot.Slug}",
|
|
||||||
UpdatedAt = SystemClock.Instance.GetCurrentInstant().ToTimestamp()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var updateResponse = await accountReceiver.UpdateBotAccountAsync(updateRequest);
|
var updateResponse = await accountReceiver.UpdateBotAccountAsync(updateRequest);
|
||||||
@@ -160,9 +146,28 @@ public class BotAccountService(AppDatabase db, BotAccountReceiverService.BotAcco
|
|||||||
{
|
{
|
||||||
// Account not found in Pass service, continue with local deletion
|
// Account not found in Pass service, continue with local deletion
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the local bot account
|
// Delete the local bot account
|
||||||
db.BotAccounts.Remove(bot);
|
db.BotAccounts.Remove(bot);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public async Task<BotAccount?> LoadBotAccountAsync(BotAccount bot) =>
|
||||||
|
(await LoadBotsAccountAsync([bot])).FirstOrDefault();
|
||||||
|
|
||||||
|
public async Task<List<BotAccount>> LoadBotsAccountAsync(IEnumerable<BotAccount> bots)
|
||||||
|
{
|
||||||
|
bots = bots.ToList();
|
||||||
|
var automatedIds = bots.Select(b => b.Id).ToList();
|
||||||
|
var data = await accounts.GetBotAccountBatch(automatedIds);
|
||||||
|
|
||||||
|
foreach (var bot in bots)
|
||||||
|
{
|
||||||
|
bot.Account = data
|
||||||
|
.Select(AccountReference.FromProtoValue)
|
||||||
|
.FirstOrDefault(e => e.AutomatedId == bot.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bots as List<BotAccount> ?? [];
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using DysonNetwork.Develop.Project;
|
using DysonNetwork.Develop.Project;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using DysonNetwork.Shared.Data;
|
using DysonNetwork.Shared.Data;
|
||||||
@@ -11,7 +12,7 @@ public class Developer
|
|||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
public Guid PublisherId { get; set; }
|
public Guid PublisherId { get; set; }
|
||||||
|
|
||||||
public List<DevProject> Projects { get; set; } = [];
|
[JsonIgnore] public List<DevProject> Projects { get; set; } = [];
|
||||||
|
|
||||||
[NotMapped] public PublisherInfo? Publisher { get; set; }
|
[NotMapped] public PublisherInfo? Publisher { get; set; }
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
},
|
},
|
||||||
"Service": {
|
"Service": {
|
||||||
"Name": "DysonNetwork.Develop",
|
"Name": "DysonNetwork.Develop",
|
||||||
"Url": "https://localhost:7099",
|
"Url": "https://localhost:7192",
|
||||||
"ClientCert": "../Certificates/client.crt",
|
"ClientCert": "../Certificates/client.crt",
|
||||||
"ClientKey": "../Certificates/client.key"
|
"ClientKey": "../Certificates/client.key"
|
||||||
}
|
}
|
||||||
|
@@ -51,7 +51,8 @@ public class Account : ModelBase
|
|||||||
Profile = Profile.ToProtoValue(),
|
Profile = Profile.ToProtoValue(),
|
||||||
PerkSubscription = PerkSubscription?.ToProtoValue(),
|
PerkSubscription = PerkSubscription?.ToProtoValue(),
|
||||||
CreatedAt = CreatedAt.ToTimestamp(),
|
CreatedAt = CreatedAt.ToTimestamp(),
|
||||||
UpdatedAt = UpdatedAt.ToTimestamp()
|
UpdatedAt = UpdatedAt.ToTimestamp(),
|
||||||
|
AutomatedId = AutomatedId?.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add contacts
|
// Add contacts
|
||||||
@@ -81,6 +82,7 @@ public class Account : ModelBase
|
|||||||
: null,
|
: null,
|
||||||
CreatedAt = proto.CreatedAt.ToInstant(),
|
CreatedAt = proto.CreatedAt.ToInstant(),
|
||||||
UpdatedAt = proto.UpdatedAt.ToInstant(),
|
UpdatedAt = proto.UpdatedAt.ToInstant(),
|
||||||
|
AutomatedId = proto.AutomatedId is not null ? Guid.Parse(proto.AutomatedId) : null
|
||||||
};
|
};
|
||||||
|
|
||||||
account.Profile = AccountProfile.FromProtoValue(proto.Profile);
|
account.Profile = AccountProfile.FromProtoValue(proto.Profile);
|
||||||
@@ -119,7 +121,7 @@ public abstract class Leveling
|
|||||||
|
|
||||||
public class AccountProfile : ModelBase, IIdentifiedResource
|
public class AccountProfile : ModelBase, IIdentifiedResource
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
[MaxLength(256)] public string? FirstName { get; set; }
|
[MaxLength(256)] public string? FirstName { get; set; }
|
||||||
[MaxLength(256)] public string? MiddleName { get; set; }
|
[MaxLength(256)] public string? MiddleName { get; set; }
|
||||||
[MaxLength(256)] public string? LastName { get; set; }
|
[MaxLength(256)] public string? LastName { get; set; }
|
||||||
|
@@ -30,6 +30,7 @@ public class AccountCurrentController(
|
|||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[ProducesResponseType<Account>(StatusCodes.Status200OK)]
|
[ProducesResponseType<Account>(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType<ApiError>(StatusCodes.Status401Unauthorized)]
|
||||||
public async Task<ActionResult<Account>> GetCurrentIdentity()
|
public async Task<ActionResult<Account>> GetCurrentIdentity()
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
|
||||||
|
@@ -6,6 +6,7 @@ using DysonNetwork.Pass.Email;
|
|||||||
using DysonNetwork.Pass.Localization;
|
using DysonNetwork.Pass.Localization;
|
||||||
using DysonNetwork.Pass.Permission;
|
using DysonNetwork.Pass.Permission;
|
||||||
using DysonNetwork.Shared.Cache;
|
using DysonNetwork.Shared.Cache;
|
||||||
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using DysonNetwork.Shared.Stream;
|
using DysonNetwork.Shared.Stream;
|
||||||
using EFCore.BulkExtensions;
|
using EFCore.BulkExtensions;
|
||||||
@@ -21,6 +22,8 @@ namespace DysonNetwork.Pass.Account;
|
|||||||
public class AccountService(
|
public class AccountService(
|
||||||
AppDatabase db,
|
AppDatabase db,
|
||||||
MagicSpellService spells,
|
MagicSpellService spells,
|
||||||
|
FileService.FileServiceClient files,
|
||||||
|
FileReferenceService.FileReferenceServiceClient fileRefs,
|
||||||
AccountUsernameService uname,
|
AccountUsernameService uname,
|
||||||
EmailService mailer,
|
EmailService mailer,
|
||||||
PusherService.PusherServiceClient pusher,
|
PusherService.PusherServiceClient pusher,
|
||||||
@@ -182,7 +185,7 @@ public class AccountService(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Account> CreateBotAccount(Account account, Guid automatedId)
|
public async Task<Account> CreateBotAccount(Account account, Guid automatedId, string? pictureId, string? backgroundId)
|
||||||
{
|
{
|
||||||
var dupeAutomateCount = await db.Accounts.Where(a => a.AutomatedId == automatedId).CountAsync();
|
var dupeAutomateCount = await db.Accounts.Where(a => a.AutomatedId == automatedId).CountAsync();
|
||||||
if (dupeAutomateCount > 0)
|
if (dupeAutomateCount > 0)
|
||||||
@@ -195,8 +198,38 @@ public class AccountService(
|
|||||||
account.AutomatedId = automatedId;
|
account.AutomatedId = automatedId;
|
||||||
account.ActivatedAt = SystemClock.Instance.GetCurrentInstant();
|
account.ActivatedAt = SystemClock.Instance.GetCurrentInstant();
|
||||||
account.IsSuperuser = false;
|
account.IsSuperuser = false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(pictureId))
|
||||||
|
{
|
||||||
|
var file = await files.GetFileAsync(new GetFileRequest { Id = pictureId });
|
||||||
|
await fileRefs.CreateReferenceAsync(
|
||||||
|
new CreateReferenceRequest
|
||||||
|
{
|
||||||
|
ResourceId = account.Profile.ResourceIdentifier,
|
||||||
|
FileId = pictureId,
|
||||||
|
Usage = "profile.picture"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
account.Profile.Picture = CloudFileReferenceObject.FromProtoValue(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(backgroundId))
|
||||||
|
{
|
||||||
|
var file = await files.GetFileAsync(new GetFileRequest { Id = backgroundId });
|
||||||
|
await fileRefs.CreateReferenceAsync(
|
||||||
|
new CreateReferenceRequest
|
||||||
|
{
|
||||||
|
ResourceId = account.Profile.ResourceIdentifier,
|
||||||
|
FileId = backgroundId,
|
||||||
|
Usage = "profile.background"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
account.Profile.Background = CloudFileReferenceObject.FromProtoValue(file);
|
||||||
|
}
|
||||||
|
|
||||||
db.Accounts.Add(account);
|
db.Accounts.Add(account);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,6 +42,26 @@ public class AccountServiceGrpc(
|
|||||||
return account.ToProtoValue();
|
return account.ToProtoValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<Shared.Proto.Account> GetBotAccount(GetBotAccountRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
if (!Guid.TryParse(request.AutomatedId, out var automatedId))
|
||||||
|
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Invalid automated ID format"));
|
||||||
|
|
||||||
|
var account = await _db.Accounts
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(a => a.Profile)
|
||||||
|
.FirstOrDefaultAsync(a => a.AutomatedId == automatedId);
|
||||||
|
|
||||||
|
if (account == null)
|
||||||
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, $"Account with automated ID {request.AutomatedId} not found"));
|
||||||
|
|
||||||
|
var perk = await subscriptions.GetPerkSubscriptionAsync(account.Id);
|
||||||
|
account.PerkSubscription = perk?.ToReference();
|
||||||
|
|
||||||
|
return account.ToProtoValue();
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task<GetAccountBatchResponse> GetAccountBatch(GetAccountBatchRequest request,
|
public override async Task<GetAccountBatchResponse> GetAccountBatch(GetAccountBatchRequest request,
|
||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
{
|
{
|
||||||
@@ -56,7 +76,35 @@ public class AccountServiceGrpc(
|
|||||||
.Where(a => accountIds.Contains(a.Id))
|
.Where(a => accountIds.Contains(a.Id))
|
||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
var perks = await subscriptions.GetPerkSubscriptionsAsync(
|
||||||
|
accounts.Select(x => x.Id).ToList()
|
||||||
|
);
|
||||||
|
foreach (var account in accounts)
|
||||||
|
if (perks.TryGetValue(account.Id, out var perk))
|
||||||
|
account.PerkSubscription = perk?.ToReference();
|
||||||
|
|
||||||
|
var response = new GetAccountBatchResponse();
|
||||||
|
response.Accounts.AddRange(accounts.Select(a => a.ToProtoValue()));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override async Task<GetAccountBatchResponse> GetBotAccountBatch(GetBotAccountBatchRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var automatedIds = request.AutomatedId
|
||||||
|
.Select(id => Guid.TryParse(id, out var automatedId) ? automatedId : (Guid?)null)
|
||||||
|
.Where(id => id.HasValue)
|
||||||
|
.Select(id => id!.Value)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var accounts = await _db.Accounts
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(a => a.AutomatedId != null && automatedIds.Contains(a.AutomatedId.Value))
|
||||||
|
.Include(a => a.Profile)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
var perks = await subscriptions.GetPerkSubscriptionsAsync(
|
var perks = await subscriptions.GetPerkSubscriptionsAsync(
|
||||||
accounts.Select(x => x.Id).ToList()
|
accounts.Select(x => x.Id).ToList()
|
||||||
);
|
);
|
||||||
@@ -76,7 +124,8 @@ public class AccountServiceGrpc(
|
|||||||
return status.ToProtoValue();
|
return status.ToProtoValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<GetAccountStatusBatchResponse> GetAccountStatusBatch(GetAccountBatchRequest request, ServerCallContext context)
|
public override async Task<GetAccountStatusBatchResponse> GetAccountStatusBatch(GetAccountBatchRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
{
|
{
|
||||||
var accountIds = request.Id
|
var accountIds = request.Id
|
||||||
.Select(id => Guid.TryParse(id, out var accountId) ? accountId : (Guid?)null)
|
.Select(id => Guid.TryParse(id, out var accountId) ? accountId : (Guid?)null)
|
||||||
@@ -98,14 +147,14 @@ public class AccountServiceGrpc(
|
|||||||
.Where(a => accountNames.Contains(a.Name))
|
.Where(a => accountNames.Contains(a.Name))
|
||||||
.Include(a => a.Profile)
|
.Include(a => a.Profile)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var perks = await subscriptions.GetPerkSubscriptionsAsync(
|
var perks = await subscriptions.GetPerkSubscriptionsAsync(
|
||||||
accounts.Select(x => x.Id).ToList()
|
accounts.Select(x => x.Id).ToList()
|
||||||
);
|
);
|
||||||
foreach (var account in accounts)
|
foreach (var account in accounts)
|
||||||
if (perks.TryGetValue(account.Id, out var perk))
|
if (perks.TryGetValue(account.Id, out var perk))
|
||||||
account.PerkSubscription = perk?.ToReference();
|
account.PerkSubscription = perk?.ToReference();
|
||||||
|
|
||||||
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;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using DysonNetwork.Shared.Data;
|
||||||
using DysonNetwork.Shared.Proto;
|
using DysonNetwork.Shared.Proto;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
@@ -5,7 +6,12 @@ using NodaTime.Serialization.Protobuf;
|
|||||||
|
|
||||||
namespace DysonNetwork.Pass.Account;
|
namespace DysonNetwork.Pass.Account;
|
||||||
|
|
||||||
public class BotAccountReceiverGrpc(AppDatabase db, AccountService accounts)
|
public class BotAccountReceiverGrpc(
|
||||||
|
AppDatabase db,
|
||||||
|
AccountService accounts,
|
||||||
|
FileService.FileServiceClient files,
|
||||||
|
FileReferenceService.FileReferenceServiceClient fileRefs
|
||||||
|
)
|
||||||
: BotAccountReceiverService.BotAccountReceiverServiceBase
|
: BotAccountReceiverService.BotAccountReceiverServiceBase
|
||||||
{
|
{
|
||||||
public override async Task<CreateBotAccountResponse> CreateBotAccount(
|
public override async Task<CreateBotAccountResponse> CreateBotAccount(
|
||||||
@@ -14,7 +20,12 @@ public class BotAccountReceiverGrpc(AppDatabase db, AccountService accounts)
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
var account = Account.FromProtoValue(request.Account);
|
var account = Account.FromProtoValue(request.Account);
|
||||||
account = await accounts.CreateBotAccount(account, Guid.Parse(request.AutomatedId));
|
account = await accounts.CreateBotAccount(
|
||||||
|
account,
|
||||||
|
Guid.Parse(request.AutomatedId),
|
||||||
|
request.PictureId,
|
||||||
|
request.BackgroundId
|
||||||
|
);
|
||||||
|
|
||||||
return new CreateBotAccountResponse
|
return new CreateBotAccountResponse
|
||||||
{
|
{
|
||||||
@@ -34,16 +45,44 @@ public class BotAccountReceiverGrpc(AppDatabase db, AccountService accounts)
|
|||||||
ServerCallContext context
|
ServerCallContext context
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var automatedId = Guid.Parse(request.AutomatedId);
|
var account = Account.FromProtoValue(request.Account);
|
||||||
var account = await accounts.GetBotAccount(automatedId);
|
|
||||||
if (account is null)
|
if (request.PictureId is not null)
|
||||||
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
|
{
|
||||||
|
var file = await files.GetFileAsync(new GetFileRequest { Id = request.PictureId });
|
||||||
|
if (account.Profile.Picture is not null)
|
||||||
|
await fileRefs.DeleteResourceReferencesAsync(
|
||||||
|
new DeleteResourceReferencesRequest { ResourceId = account.Profile.ResourceIdentifier }
|
||||||
|
);
|
||||||
|
await fileRefs.CreateReferenceAsync(
|
||||||
|
new CreateReferenceRequest
|
||||||
|
{
|
||||||
|
ResourceId = account.Profile.ResourceIdentifier,
|
||||||
|
FileId = request.PictureId,
|
||||||
|
Usage = "profile.picture"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
account.Profile.Picture = CloudFileReferenceObject.FromProtoValue(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.BackgroundId is not null)
|
||||||
|
{
|
||||||
|
var file = await files.GetFileAsync(new GetFileRequest { Id = request.BackgroundId });
|
||||||
|
if (account.Profile.Background is not null)
|
||||||
|
await fileRefs.DeleteResourceReferencesAsync(
|
||||||
|
new DeleteResourceReferencesRequest { ResourceId = account.Profile.ResourceIdentifier }
|
||||||
|
);
|
||||||
|
await fileRefs.CreateReferenceAsync(
|
||||||
|
new CreateReferenceRequest
|
||||||
|
{
|
||||||
|
ResourceId = account.Profile.ResourceIdentifier,
|
||||||
|
FileId = request.BackgroundId,
|
||||||
|
Usage = "profile.background"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
account.Profile.Background = CloudFileReferenceObject.FromProtoValue(file);
|
||||||
|
}
|
||||||
|
|
||||||
account.Name = request.Account.Name;
|
|
||||||
account.Nick = request.Account.Nick;
|
|
||||||
account.Profile = AccountProfile.FromProtoValue(request.Account.Profile);
|
|
||||||
account.Language = request.Account.Language;
|
|
||||||
|
|
||||||
db.Accounts.Update(account);
|
db.Accounts.Update(account);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
@@ -56,7 +95,7 @@ public class BotAccountReceiverGrpc(AppDatabase db, AccountService accounts)
|
|||||||
CreatedAt = account.CreatedAt.ToTimestamp(),
|
CreatedAt = account.CreatedAt.ToTimestamp(),
|
||||||
UpdatedAt = account.UpdatedAt.ToTimestamp(),
|
UpdatedAt = account.UpdatedAt.ToTimestamp(),
|
||||||
IsActive = true
|
IsActive = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,9 +108,9 @@ public class BotAccountReceiverGrpc(AppDatabase db, AccountService accounts)
|
|||||||
var account = await accounts.GetBotAccount(automatedId);
|
var account = await accounts.GetBotAccount(automatedId);
|
||||||
if (account is null)
|
if (account is null)
|
||||||
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
|
||||||
|
|
||||||
await accounts.DeleteAccount(account);
|
await accounts.DeleteAccount(account);
|
||||||
|
|
||||||
return new DeleteBotAccountResponse();
|
return new DeleteBotAccountResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
309
DysonNetwork.Shared/Data/Account.cs
Normal file
309
DysonNetwork.Shared/Data/Account.cs
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Shared.Data;
|
||||||
|
|
||||||
|
public class AccountReference
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Nick { get; set; } = string.Empty;
|
||||||
|
public string Language { get; set; } = string.Empty;
|
||||||
|
public Instant? ActivatedAt { get; set; }
|
||||||
|
public bool IsSuperuser { get; set; }
|
||||||
|
public Guid? AutomatedId { get; set; }
|
||||||
|
public AccountProfileReference Profile { get; set; } = null!;
|
||||||
|
public List<AccountContactReference> Contacts { get; set; } = new();
|
||||||
|
public List<AccountBadgeReference> Badges { get; set; } = new();
|
||||||
|
public SubscriptionReference? PerkSubscription { get; set; }
|
||||||
|
public Instant CreatedAt { get; set; }
|
||||||
|
public Instant UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
public Proto.Account ToProtoValue()
|
||||||
|
{
|
||||||
|
var proto = new Proto.Account
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Name = Name,
|
||||||
|
Nick = Nick,
|
||||||
|
Language = Language,
|
||||||
|
ActivatedAt = ActivatedAt?.ToTimestamp(),
|
||||||
|
IsSuperuser = IsSuperuser,
|
||||||
|
Profile = Profile.ToProtoValue(),
|
||||||
|
PerkSubscription = PerkSubscription?.ToProtoValue(),
|
||||||
|
CreatedAt = CreatedAt.ToTimestamp(),
|
||||||
|
UpdatedAt = UpdatedAt.ToTimestamp()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var contact in Contacts)
|
||||||
|
proto.Contacts.Add(contact.ToProtoValue());
|
||||||
|
|
||||||
|
foreach (var badge in Badges)
|
||||||
|
proto.Badges.Add(badge.ToProtoValue());
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountReference FromProtoValue(Proto.Account proto)
|
||||||
|
{
|
||||||
|
var account = new AccountReference
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Name = proto.Name,
|
||||||
|
Nick = proto.Nick,
|
||||||
|
Language = proto.Language,
|
||||||
|
ActivatedAt = proto.ActivatedAt?.ToInstant(),
|
||||||
|
IsSuperuser = proto.IsSuperuser,
|
||||||
|
AutomatedId = string.IsNullOrEmpty(proto.AutomatedId) ? null : Guid.Parse(proto.AutomatedId),
|
||||||
|
PerkSubscription = proto.PerkSubscription != null
|
||||||
|
? SubscriptionReference.FromProtoValue(proto.PerkSubscription)
|
||||||
|
: null,
|
||||||
|
CreatedAt = proto.CreatedAt.ToInstant(),
|
||||||
|
UpdatedAt = proto.UpdatedAt.ToInstant()
|
||||||
|
};
|
||||||
|
|
||||||
|
account.Profile = AccountProfileReference.FromProtoValue(proto.Profile);
|
||||||
|
|
||||||
|
foreach (var contactProto in proto.Contacts)
|
||||||
|
account.Contacts.Add(AccountContactReference.FromProtoValue(contactProto));
|
||||||
|
|
||||||
|
foreach (var badgeProto in proto.Badges)
|
||||||
|
account.Badges.Add(AccountBadgeReference.FromProtoValue(badgeProto));
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountProfileReference
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string? FirstName { get; set; }
|
||||||
|
public string? MiddleName { get; set; }
|
||||||
|
public string? LastName { get; set; }
|
||||||
|
public string? Bio { get; set; }
|
||||||
|
public string? Gender { get; set; }
|
||||||
|
public string? Pronouns { get; set; }
|
||||||
|
public string? TimeZone { get; set; }
|
||||||
|
public string? Location { get; set; }
|
||||||
|
public List<ProfileLinkReference>? Links { get; set; }
|
||||||
|
public Instant? Birthday { get; set; }
|
||||||
|
public Instant? LastSeenAt { get; set; }
|
||||||
|
public VerificationMark? Verification { get; set; }
|
||||||
|
public int Experience { get; set; }
|
||||||
|
public int Level => Leveling.ExperiencePerLevel.Count(xp => Experience >= xp) - 1;
|
||||||
|
public double SocialCredits { get; set; } = 100;
|
||||||
|
|
||||||
|
public int SocialCreditsLevel => SocialCredits switch
|
||||||
|
{
|
||||||
|
< 100 => -1,
|
||||||
|
> 100 and < 200 => 0,
|
||||||
|
< 200 => 1,
|
||||||
|
_ => 2
|
||||||
|
};
|
||||||
|
|
||||||
|
public double LevelingProgress => Level >= Leveling.ExperiencePerLevel.Count - 1
|
||||||
|
? 100
|
||||||
|
: (Experience - Leveling.ExperiencePerLevel[Level]) * 100.0 /
|
||||||
|
(Leveling.ExperiencePerLevel[Level + 1] - Leveling.ExperiencePerLevel[Level]);
|
||||||
|
|
||||||
|
public CloudFileReferenceObject? Picture { get; set; }
|
||||||
|
public CloudFileReferenceObject? Background { get; set; }
|
||||||
|
public Guid AccountId { get; set; }
|
||||||
|
|
||||||
|
public Proto.AccountProfile ToProtoValue()
|
||||||
|
{
|
||||||
|
var proto = new Proto.AccountProfile
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
FirstName = FirstName ?? string.Empty,
|
||||||
|
MiddleName = MiddleName ?? string.Empty,
|
||||||
|
LastName = LastName ?? string.Empty,
|
||||||
|
Bio = Bio ?? string.Empty,
|
||||||
|
Gender = Gender ?? string.Empty,
|
||||||
|
Pronouns = Pronouns ?? string.Empty,
|
||||||
|
TimeZone = TimeZone ?? string.Empty,
|
||||||
|
Location = Location ?? string.Empty,
|
||||||
|
Birthday = Birthday?.ToTimestamp(),
|
||||||
|
LastSeenAt = LastSeenAt?.ToTimestamp(),
|
||||||
|
Experience = Experience,
|
||||||
|
Level = Level,
|
||||||
|
LevelingProgress = LevelingProgress,
|
||||||
|
SocialCredits = SocialCredits,
|
||||||
|
SocialCreditsLevel = SocialCreditsLevel,
|
||||||
|
Picture = Picture?.ToProtoValue(),
|
||||||
|
Background = Background?.ToProtoValue(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
Verification = Verification?.ToProtoValue(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountProfileReference FromProtoValue(Proto.AccountProfile proto)
|
||||||
|
{
|
||||||
|
return new AccountProfileReference
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
FirstName = string.IsNullOrEmpty(proto.FirstName) ? null : proto.FirstName,
|
||||||
|
MiddleName = string.IsNullOrEmpty(proto.MiddleName) ? null : proto.MiddleName,
|
||||||
|
LastName = string.IsNullOrEmpty(proto.LastName) ? null : proto.LastName,
|
||||||
|
Bio = string.IsNullOrEmpty(proto.Bio) ? null : proto.Bio,
|
||||||
|
Gender = string.IsNullOrEmpty(proto.Gender) ? null : proto.Gender,
|
||||||
|
Pronouns = string.IsNullOrEmpty(proto.Pronouns) ? null : proto.Pronouns,
|
||||||
|
TimeZone = string.IsNullOrEmpty(proto.TimeZone) ? null : proto.TimeZone,
|
||||||
|
Location = string.IsNullOrEmpty(proto.Location) ? null : proto.Location,
|
||||||
|
Birthday = proto.Birthday?.ToInstant(),
|
||||||
|
LastSeenAt = proto.LastSeenAt?.ToInstant(),
|
||||||
|
Experience = proto.Experience,
|
||||||
|
SocialCredits = proto.SocialCredits,
|
||||||
|
Picture = proto.Picture != null ? CloudFileReferenceObject.FromProtoValue(proto.Picture) : null,
|
||||||
|
Background = proto.Background != null ? CloudFileReferenceObject.FromProtoValue(proto.Background) : null,
|
||||||
|
AccountId = Guid.Parse(proto.AccountId),
|
||||||
|
Verification = proto.Verification != null ? VerificationMark.FromProtoValue(proto.Verification) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountContactReference : ModelBase
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public AccountContactReferenceType Type { get; set; }
|
||||||
|
public Instant? VerifiedAt { get; set; }
|
||||||
|
public bool IsPrimary { get; set; } = false;
|
||||||
|
public bool IsPublic { get; set; } = false;
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public Guid AccountId { get; set; }
|
||||||
|
|
||||||
|
public Shared.Proto.AccountContact ToProtoValue()
|
||||||
|
{
|
||||||
|
var proto = new Shared.Proto.AccountContact
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Type = Type switch
|
||||||
|
{
|
||||||
|
AccountContactReferenceType.Email => Shared.Proto.AccountContactType.Email,
|
||||||
|
AccountContactReferenceType.PhoneNumber => Shared.Proto.AccountContactType.PhoneNumber,
|
||||||
|
AccountContactReferenceType.Address => Shared.Proto.AccountContactType.Address,
|
||||||
|
_ => Shared.Proto.AccountContactType.Unspecified
|
||||||
|
},
|
||||||
|
Content = Content,
|
||||||
|
IsPrimary = IsPrimary,
|
||||||
|
VerifiedAt = VerifiedAt?.ToTimestamp(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
CreatedAt = CreatedAt.ToTimestamp(),
|
||||||
|
UpdatedAt = UpdatedAt.ToTimestamp()
|
||||||
|
};
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountContactReference FromProtoValue(Shared.Proto.AccountContact proto)
|
||||||
|
{
|
||||||
|
var contact = new AccountContactReference
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
AccountId = Guid.Parse(proto.AccountId),
|
||||||
|
Type = proto.Type switch
|
||||||
|
{
|
||||||
|
Shared.Proto.AccountContactType.Email => AccountContactReferenceType.Email,
|
||||||
|
Shared.Proto.AccountContactType.PhoneNumber => AccountContactReferenceType.PhoneNumber,
|
||||||
|
Shared.Proto.AccountContactType.Address => AccountContactReferenceType.Address,
|
||||||
|
_ => AccountContactReferenceType.Email
|
||||||
|
},
|
||||||
|
Content = proto.Content,
|
||||||
|
IsPrimary = proto.IsPrimary,
|
||||||
|
VerifiedAt = proto.VerifiedAt?.ToInstant(),
|
||||||
|
CreatedAt = proto.CreatedAt.ToInstant(),
|
||||||
|
UpdatedAt = proto.UpdatedAt.ToInstant()
|
||||||
|
};
|
||||||
|
|
||||||
|
return contact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountContactReferenceType
|
||||||
|
{
|
||||||
|
Email,
|
||||||
|
PhoneNumber,
|
||||||
|
Address
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AccountBadgeReference : ModelBase
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
public string Type { get; set; } = null!;
|
||||||
|
public string? Label { get; set; }
|
||||||
|
public string? Caption { get; set; }
|
||||||
|
public Dictionary<string, object?> Meta { get; set; } = new();
|
||||||
|
public Instant? ActivatedAt { get; set; }
|
||||||
|
public Instant? ExpiredAt { get; set; }
|
||||||
|
|
||||||
|
public Guid AccountId { get; set; }
|
||||||
|
|
||||||
|
public AccountBadge ToProtoValue()
|
||||||
|
{
|
||||||
|
var proto = new AccountBadge
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Type = Type,
|
||||||
|
Label = Label ?? string.Empty,
|
||||||
|
Caption = Caption ?? string.Empty,
|
||||||
|
ActivatedAt = ActivatedAt?.ToTimestamp(),
|
||||||
|
ExpiredAt = ExpiredAt?.ToTimestamp(),
|
||||||
|
AccountId = AccountId.ToString(),
|
||||||
|
CreatedAt = CreatedAt.ToTimestamp(),
|
||||||
|
UpdatedAt = UpdatedAt.ToTimestamp()
|
||||||
|
};
|
||||||
|
proto.Meta.Add(GrpcTypeHelper.ConvertToValueMap(Meta));
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AccountBadgeReference FromProtoValue(AccountBadge proto)
|
||||||
|
{
|
||||||
|
var badge = new AccountBadgeReference
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
AccountId = Guid.Parse(proto.AccountId),
|
||||||
|
Type = proto.Type,
|
||||||
|
Label = proto.Label,
|
||||||
|
Caption = proto.Caption,
|
||||||
|
ActivatedAt = proto.ActivatedAt?.ToInstant(),
|
||||||
|
ExpiredAt = proto.ExpiredAt?.ToInstant(),
|
||||||
|
CreatedAt = proto.CreatedAt.ToInstant(),
|
||||||
|
UpdatedAt = proto.UpdatedAt.ToInstant()
|
||||||
|
};
|
||||||
|
|
||||||
|
return badge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProfileLinkReference
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Leveling
|
||||||
|
{
|
||||||
|
public static readonly List<int> ExperiencePerLevel =
|
||||||
|
[
|
||||||
|
0, // Level 0
|
||||||
|
100, // Level 1
|
||||||
|
250, // Level 2
|
||||||
|
500, // Level 3
|
||||||
|
1000, // Level 4
|
||||||
|
2000, // Level 5
|
||||||
|
4000, // Level 6
|
||||||
|
8000, // Level 7
|
||||||
|
16000, // Level 8
|
||||||
|
32000, // Level 9
|
||||||
|
64000, // Level 10
|
||||||
|
128000, // Level 11
|
||||||
|
256000, // Level 12
|
||||||
|
512000, // Level 13
|
||||||
|
1024000
|
||||||
|
];
|
||||||
|
}
|
64
DysonNetwork.Shared/Data/Subscription.cs
Normal file
64
DysonNetwork.Shared/Data/Subscription.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Shared.Data;
|
||||||
|
|
||||||
|
public class SubscriptionReference
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Identifier { get; set; } = string.Empty;
|
||||||
|
public string DisplayName { get; set; } = string.Empty;
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
public bool IsAvailable { get; set; }
|
||||||
|
public Instant BegunAt { get; set; }
|
||||||
|
public Instant? EndedAt { get; set; }
|
||||||
|
public Instant? RenewalAt { get; set; }
|
||||||
|
public SubscriptionReferenceStatus Status { get; set; }
|
||||||
|
|
||||||
|
public static SubscriptionReference FromProtoValue(Proto.SubscriptionReferenceObject proto)
|
||||||
|
{
|
||||||
|
return new SubscriptionReference
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.Id),
|
||||||
|
Identifier = proto.Identifier,
|
||||||
|
DisplayName = proto.DisplayName,
|
||||||
|
IsActive = proto.IsActive,
|
||||||
|
IsAvailable = proto.IsAvailable,
|
||||||
|
BegunAt = proto.BegunAt.ToInstant(),
|
||||||
|
EndedAt = proto.EndedAt?.ToInstant(),
|
||||||
|
RenewalAt = proto.RenewalAt?.ToInstant(),
|
||||||
|
Status = (SubscriptionReferenceStatus)proto.Status
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Proto.SubscriptionReferenceObject ToProtoValue()
|
||||||
|
{
|
||||||
|
return new Proto.SubscriptionReferenceObject
|
||||||
|
{
|
||||||
|
Id = Id.ToString(),
|
||||||
|
Identifier = Identifier,
|
||||||
|
DisplayName = DisplayName,
|
||||||
|
IsActive = IsActive,
|
||||||
|
IsAvailable = IsAvailable,
|
||||||
|
BegunAt = BegunAt.ToTimestamp(),
|
||||||
|
EndedAt = EndedAt?.ToTimestamp(),
|
||||||
|
RenewalAt = RenewalAt?.ToTimestamp(),
|
||||||
|
Status = Status switch
|
||||||
|
{
|
||||||
|
SubscriptionReferenceStatus.Unpaid => Proto.SubscriptionStatus.Unpaid,
|
||||||
|
SubscriptionReferenceStatus.Active => Proto.SubscriptionStatus.Active,
|
||||||
|
SubscriptionReferenceStatus.Expired => Proto.SubscriptionStatus.Expired,
|
||||||
|
SubscriptionReferenceStatus.Cancelled => Proto.SubscriptionStatus.Cancelled,
|
||||||
|
_ => Proto.SubscriptionStatus.Unpaid
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SubscriptionReferenceStatus
|
||||||
|
{
|
||||||
|
Unpaid = 0,
|
||||||
|
Active = 1,
|
||||||
|
Expired = 2,
|
||||||
|
Cancelled = 3
|
||||||
|
}
|
@@ -32,6 +32,8 @@ message Account {
|
|||||||
|
|
||||||
google.protobuf.Timestamp created_at = 14;
|
google.protobuf.Timestamp created_at = 14;
|
||||||
google.protobuf.Timestamp updated_at = 15;
|
google.protobuf.Timestamp updated_at = 15;
|
||||||
|
|
||||||
|
optional string automated_id = 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enum for status attitude
|
// Enum for status attitude
|
||||||
@@ -246,7 +248,9 @@ message GetAccountStatusBatchResponse {
|
|||||||
service AccountService {
|
service AccountService {
|
||||||
// Account Operations
|
// Account Operations
|
||||||
rpc GetAccount(GetAccountRequest) returns (Account) {}
|
rpc GetAccount(GetAccountRequest) returns (Account) {}
|
||||||
|
rpc GetBotAccount(GetBotAccountRequest) returns (Account) {}
|
||||||
rpc GetAccountBatch(GetAccountBatchRequest) returns (GetAccountBatchResponse) {}
|
rpc GetAccountBatch(GetAccountBatchRequest) returns (GetAccountBatchResponse) {}
|
||||||
|
rpc GetBotAccountBatch(GetBotAccountBatchRequest) returns (GetAccountBatchResponse) {}
|
||||||
rpc LookupAccountBatch(LookupAccountBatchRequest) returns (GetAccountBatchResponse) {}
|
rpc LookupAccountBatch(LookupAccountBatchRequest) returns (GetAccountBatchResponse) {}
|
||||||
rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse) {}
|
rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse) {}
|
||||||
|
|
||||||
@@ -321,10 +325,18 @@ message GetAccountRequest {
|
|||||||
string id = 1; // Account ID to retrieve
|
string id = 1; // Account ID to retrieve
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetBotAccountRequest {
|
||||||
|
string automated_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message GetAccountBatchRequest {
|
message GetAccountBatchRequest {
|
||||||
repeated string id = 1; // Account ID to retrieve
|
repeated string id = 1; // Account ID to retrieve
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetBotAccountBatchRequest {
|
||||||
|
repeated string automated_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message LookupAccountBatchRequest {
|
message LookupAccountBatchRequest {
|
||||||
repeated string names = 1;
|
repeated string names = 1;
|
||||||
}
|
}
|
||||||
|
@@ -108,6 +108,8 @@ message BotAccount {
|
|||||||
message CreateBotAccountRequest {
|
message CreateBotAccountRequest {
|
||||||
Account account = 1;
|
Account account = 1;
|
||||||
string automated_id = 2;
|
string automated_id = 2;
|
||||||
|
optional string picture_id = 8;
|
||||||
|
optional string background_id = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CreateBotAccountResponse {
|
message CreateBotAccountResponse {
|
||||||
@@ -117,6 +119,8 @@ message CreateBotAccountResponse {
|
|||||||
message UpdateBotAccountRequest {
|
message UpdateBotAccountRequest {
|
||||||
string automated_id = 1; // ID of the bot account to update
|
string automated_id = 1; // ID of the bot account to update
|
||||||
Account account = 2; // Updated account information
|
Account account = 2; // Updated account information
|
||||||
|
optional string picture_id = 8;
|
||||||
|
optional string background_id = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateBotAccountResponse {
|
message UpdateBotAccountResponse {
|
||||||
|
@@ -11,6 +11,13 @@ public class AccountClientHelper(AccountService.AccountServiceClient accounts)
|
|||||||
var response = await accounts.GetAccountAsync(request);
|
var response = await accounts.GetAccountAsync(request);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Account> GetBotAccount(Guid automatedId)
|
||||||
|
{
|
||||||
|
var request = new GetBotAccountRequest { AutomatedId = automatedId.ToString() };
|
||||||
|
var response = await accounts.GetBotAccountAsync(request);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<Account>> GetAccountBatch(List<Guid> ids)
|
public async Task<List<Account>> GetAccountBatch(List<Guid> ids)
|
||||||
{
|
{
|
||||||
@@ -19,6 +26,14 @@ public class AccountClientHelper(AccountService.AccountServiceClient accounts)
|
|||||||
var response = await accounts.GetAccountBatchAsync(request);
|
var response = await accounts.GetAccountBatchAsync(request);
|
||||||
return response.Accounts.ToList();
|
return response.Accounts.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<Account>> GetBotAccountBatch(List<Guid> automatedIds)
|
||||||
|
{
|
||||||
|
var request = new GetBotAccountBatchRequest();
|
||||||
|
request.AutomatedId.AddRange(automatedIds.Select(id => id.ToString()));
|
||||||
|
var response = await accounts.GetBotAccountBatchAsync(request);
|
||||||
|
return response.Accounts.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Dictionary<Guid, AccountStatusReference>> GetAccountStatusBatch(List<Guid> ids)
|
public async Task<Dictionary<Guid, AccountStatusReference>> GetAccountStatusBatch(List<Guid> ids)
|
||||||
{
|
{
|
||||||
|
@@ -75,6 +75,7 @@ public class StickerController(
|
|||||||
var packs = await queryable
|
var packs = await queryable
|
||||||
.Skip(offset)
|
.Skip(offset)
|
||||||
.Take(take)
|
.Take(take)
|
||||||
|
.Include(e => e.Stickers.OrderByDescending(s => s.CreatedAt).Take(8))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
Response.Headers["X-Total"] = totalCount.ToString();
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
Reference in New Issue
Block a user