✨ Account bot basis
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Text.Json;
|
||||||
using DysonNetwork.Develop.Identity;
|
using DysonNetwork.Develop.Identity;
|
||||||
using DysonNetwork.Develop.Project;
|
using DysonNetwork.Develop.Project;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -16,6 +17,7 @@ public class AppDatabase(
|
|||||||
|
|
||||||
public DbSet<CustomApp> CustomApps { get; set; } = null!;
|
public DbSet<CustomApp> CustomApps { get; set; } = null!;
|
||||||
public DbSet<CustomAppSecret> CustomAppSecrets { get; set; } = null!;
|
public DbSet<CustomAppSecret> CustomAppSecrets { get; set; } = null!;
|
||||||
|
public DbSet<BotAccount> BotAccounts { get; set; } = null!;
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
|
46
DysonNetwork.Develop/Identity/BotAccount.cs
Normal file
46
DysonNetwork.Develop/Identity/BotAccount.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using DysonNetwork.Develop.Project;
|
||||||
|
using DysonNetwork.Shared.Data;
|
||||||
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
|
public class BotAccount : ModelBase
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
[MaxLength(1024)] public string Slug { get; set; } = null!;
|
||||||
|
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
public Guid ProjectId { get; set; }
|
||||||
|
public DevProject Project { get; set; } = null!;
|
||||||
|
|
||||||
|
public Shared.Proto.BotAccount ToProtoValue()
|
||||||
|
{
|
||||||
|
var proto = new Shared.Proto.BotAccount
|
||||||
|
{
|
||||||
|
Slug = Slug,
|
||||||
|
IsActive = IsActive,
|
||||||
|
AutomatedId = Id.ToString(),
|
||||||
|
CreatedAt = CreatedAt.ToTimestamp(),
|
||||||
|
UpdatedAt = UpdatedAt.ToTimestamp()
|
||||||
|
};
|
||||||
|
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BotAccount FromProto(Shared.Proto.BotAccount proto)
|
||||||
|
{
|
||||||
|
var botAccount = new BotAccount
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(proto.AutomatedId),
|
||||||
|
Slug = proto.Slug,
|
||||||
|
IsActive = proto.IsActive,
|
||||||
|
CreatedAt = proto.CreatedAt.ToInstant(),
|
||||||
|
UpdatedAt = proto.UpdatedAt.ToInstant()
|
||||||
|
};
|
||||||
|
|
||||||
|
return botAccount;
|
||||||
|
}
|
||||||
|
}
|
197
DysonNetwork.Develop/Identity/BotAccountController.cs
Normal file
197
DysonNetwork.Develop/Identity/BotAccountController.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using DysonNetwork.Develop.Project;
|
||||||
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/developers/{pubName}/projects/{projectId:guid}/bots")]
|
||||||
|
[Authorize]
|
||||||
|
public class BotAccountController(
|
||||||
|
BotAccountService botService,
|
||||||
|
DeveloperService developerService,
|
||||||
|
DevProjectService projectService,
|
||||||
|
ILogger<BotAccountController> logger
|
||||||
|
)
|
||||||
|
: ControllerBase
|
||||||
|
{
|
||||||
|
public record BotRequest(
|
||||||
|
[Required] [MaxLength(1024)] string? Slug
|
||||||
|
);
|
||||||
|
|
||||||
|
public record UpdateBotRequest(
|
||||||
|
[MaxLength(1024)] string? Slug,
|
||||||
|
bool? IsActive
|
||||||
|
) : BotRequest(Slug);
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> ListBots(
|
||||||
|
[FromRoute] string pubName,
|
||||||
|
[FromRoute] Guid projectId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var developer = await developerService.GetDeveloperByName(pubName);
|
||||||
|
if (developer is null)
|
||||||
|
return NotFound("Developer not found");
|
||||||
|
|
||||||
|
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
|
||||||
|
PublisherMemberRole.Editor))
|
||||||
|
return StatusCode(403, "You must be an editor of the developer to list bots");
|
||||||
|
|
||||||
|
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||||
|
if (project is null)
|
||||||
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
|
var bots = await botService.GetBotsByProjectAsync(projectId);
|
||||||
|
return Ok(bots);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{botId:guid}")]
|
||||||
|
public async Task<IActionResult> GetBot(
|
||||||
|
[FromRoute] string pubName,
|
||||||
|
[FromRoute] Guid projectId,
|
||||||
|
[FromRoute] Guid botId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var developer = await developerService.GetDeveloperByName(pubName);
|
||||||
|
if (developer is null)
|
||||||
|
return NotFound("Developer not found");
|
||||||
|
|
||||||
|
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
|
||||||
|
PublisherMemberRole.Editor))
|
||||||
|
return StatusCode(403, "You must be an editor of the developer to view bot details");
|
||||||
|
|
||||||
|
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||||
|
if (project is null)
|
||||||
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
|
var bot = await botService.GetBotByIdAsync(botId);
|
||||||
|
if (bot is null || bot.ProjectId != projectId)
|
||||||
|
return NotFound("Bot not found");
|
||||||
|
|
||||||
|
return Ok(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> CreateBot(
|
||||||
|
[FromRoute] string pubName,
|
||||||
|
[FromRoute] Guid projectId,
|
||||||
|
[FromBody] BotRequest request
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(request.Slug))
|
||||||
|
return BadRequest("Name is required");
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var developer = await developerService.GetDeveloperByName(pubName);
|
||||||
|
if (developer is null)
|
||||||
|
return NotFound("Developer not found");
|
||||||
|
|
||||||
|
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
|
||||||
|
PublisherMemberRole.Editor))
|
||||||
|
return StatusCode(403, "You must be an editor of the developer to create a bot");
|
||||||
|
|
||||||
|
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||||
|
if (project is null)
|
||||||
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var bot = await botService.CreateBotAsync(project, request.Slug);
|
||||||
|
return Ok(bot);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error creating bot account");
|
||||||
|
return StatusCode(500, "An error occurred while creating the bot account");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{botId:guid}")]
|
||||||
|
public async Task<IActionResult> UpdateBot(
|
||||||
|
[FromRoute] string pubName,
|
||||||
|
[FromRoute] Guid projectId,
|
||||||
|
[FromRoute] Guid botId,
|
||||||
|
[FromBody] UpdateBotRequest request
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var developer = await developerService.GetDeveloperByName(pubName);
|
||||||
|
if (developer is null)
|
||||||
|
return NotFound("Developer not found");
|
||||||
|
|
||||||
|
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
|
||||||
|
PublisherMemberRole.Editor))
|
||||||
|
return StatusCode(403, "You must be an editor of the developer to update a bot");
|
||||||
|
|
||||||
|
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||||
|
if (project is null)
|
||||||
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
|
var bot = await botService.GetBotByIdAsync(botId);
|
||||||
|
if (bot is null || bot.ProjectId != projectId)
|
||||||
|
return NotFound("Bot not found");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var updatedBot = await botService.UpdateBotAsync(
|
||||||
|
bot,
|
||||||
|
request.Slug,
|
||||||
|
request.IsActive
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok(updatedBot);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error updating bot account {BotId}", botId);
|
||||||
|
return StatusCode(500, "An error occurred while updating the bot account");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{botId:guid}")]
|
||||||
|
public async Task<IActionResult> DeleteBot(
|
||||||
|
[FromRoute] string pubName,
|
||||||
|
[FromRoute] Guid projectId,
|
||||||
|
[FromRoute] Guid botId)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account currentUser)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
var developer = await developerService.GetDeveloperByName(pubName);
|
||||||
|
if (developer is null)
|
||||||
|
return NotFound("Developer not found");
|
||||||
|
|
||||||
|
if (!await developerService.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id),
|
||||||
|
PublisherMemberRole.Editor))
|
||||||
|
return StatusCode(403, "You must be an editor of the developer to delete a bot");
|
||||||
|
|
||||||
|
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||||
|
if (project is null)
|
||||||
|
return NotFound("Project not found or you don't have access");
|
||||||
|
|
||||||
|
var bot = await botService.GetBotByIdAsync(botId);
|
||||||
|
if (bot is null || bot.ProjectId != projectId)
|
||||||
|
return NotFound("Bot not found");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await botService.DeleteBotAsync(bot);
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, "Error deleting bot {BotId}", botId);
|
||||||
|
return StatusCode(500, "An error occurred while deleting the bot account");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
DysonNetwork.Develop/Identity/BotAccountService.cs
Normal file
57
DysonNetwork.Develop/Identity/BotAccountService.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using DysonNetwork.Develop.Project;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Develop.Identity;
|
||||||
|
|
||||||
|
public class BotAccountService(AppDatabase db, ILogger<BotAccountService> logger)
|
||||||
|
{
|
||||||
|
public async Task<BotAccount?> GetBotByIdAsync(Guid id)
|
||||||
|
{
|
||||||
|
return await db.BotAccounts
|
||||||
|
.Include(b => b.Project)
|
||||||
|
.FirstOrDefaultAsync(b => b.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<BotAccount>> GetBotsByProjectAsync(Guid projectId)
|
||||||
|
{
|
||||||
|
return await db.BotAccounts
|
||||||
|
.Where(b => b.ProjectId == projectId)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BotAccount> CreateBotAsync(DevProject project, string slug)
|
||||||
|
{
|
||||||
|
var bot = new BotAccount
|
||||||
|
{
|
||||||
|
Slug = slug,
|
||||||
|
ProjectId = project.Id,
|
||||||
|
Project = project,
|
||||||
|
IsActive = true
|
||||||
|
};
|
||||||
|
|
||||||
|
db.BotAccounts.Add(bot);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BotAccount> UpdateBotAsync(BotAccount bot, string? slug = null, bool? isActive = null)
|
||||||
|
{
|
||||||
|
if (slug != null)
|
||||||
|
bot.Slug = slug;
|
||||||
|
if (isActive.HasValue)
|
||||||
|
bot.IsActive = isActive.Value;
|
||||||
|
|
||||||
|
bot.UpdatedAt = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteBotAsync(BotAccount bot)
|
||||||
|
{
|
||||||
|
db.BotAccounts.Remove(bot);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
@@ -52,6 +52,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddScoped<DeveloperService>();
|
services.AddScoped<DeveloperService>();
|
||||||
services.AddScoped<CustomAppService>();
|
services.AddScoped<CustomAppService>();
|
||||||
services.AddScoped<DevProjectService>();
|
services.AddScoped<DevProjectService>();
|
||||||
|
services.AddScoped<BotAccountService>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,9 @@ public class Account : ModelBase
|
|||||||
public Instant? ActivatedAt { get; set; }
|
public Instant? ActivatedAt { get; set; }
|
||||||
public bool IsSuperuser { get; set; } = false;
|
public bool IsSuperuser { get; set; } = false;
|
||||||
|
|
||||||
|
// The ID is the BotAccount ID in the DysonNetwork.Develop
|
||||||
|
public Guid? AutomatedId { get; set; }
|
||||||
|
|
||||||
public AccountProfile Profile { get; set; } = null!;
|
public AccountProfile Profile { get; set; } = null!;
|
||||||
public ICollection<AccountContact> Contacts { get; set; } = new List<AccountContact>();
|
public ICollection<AccountContact> Contacts { get; set; } = new List<AccountContact>();
|
||||||
public ICollection<AccountBadge> Badges { get; set; } = new List<AccountBadge>();
|
public ICollection<AccountBadge> Badges { get; set; } = new List<AccountBadge>();
|
||||||
|
@@ -178,6 +178,29 @@ public class AccountService(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Account> CreateBotAccount(Account account, Guid automatedId)
|
||||||
|
{
|
||||||
|
var dupeAutomateCount = await db.Accounts.Where(a => a.AutomatedId == automatedId).CountAsync();
|
||||||
|
if (dupeAutomateCount > 0)
|
||||||
|
throw new InvalidOperationException("Automated ID has already been used.");
|
||||||
|
|
||||||
|
var dupeNameCount = await db.Accounts.Where(a => a.Name == account.Name).CountAsync();
|
||||||
|
if (dupeNameCount > 0)
|
||||||
|
throw new InvalidOperationException("Account name has already been taken.");
|
||||||
|
|
||||||
|
account.AutomatedId = automatedId;
|
||||||
|
account.ActivatedAt = SystemClock.Instance.GetCurrentInstant();
|
||||||
|
account.IsSuperuser = false;
|
||||||
|
db.Accounts.Add(account);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Account?> GetBotAccount(Guid automatedId)
|
||||||
|
{
|
||||||
|
return await db.Accounts.FirstOrDefaultAsync(a => a.AutomatedId == automatedId);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task RequestAccountDeletion(Account account)
|
public async Task RequestAccountDeletion(Account account)
|
||||||
{
|
{
|
||||||
var spell = await spells.CreateMagicSpell(
|
var spell = await spells.CreateMagicSpell(
|
||||||
@@ -665,21 +688,13 @@ public class AccountService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public async Task DeleteAccount(Account account)
|
||||||
/// The maintenance method for server administrator.
|
|
||||||
/// To check every user has an account profile and to create them if it isn't having one.
|
|
||||||
/// </summary>
|
|
||||||
public async Task EnsureAccountProfileCreated()
|
|
||||||
{
|
{
|
||||||
var accountsId = await db.Accounts.Select(a => a.Id).ToListAsync();
|
await db.AuthSessions
|
||||||
var existingId = await db.AccountProfiles.Select(p => p.AccountId).ToListAsync();
|
.Where(s => s.AccountId == account.Id)
|
||||||
var missingId = accountsId.Except(existingId).ToList();
|
.ExecuteDeleteAsync();
|
||||||
|
|
||||||
if (missingId.Count != 0)
|
db.Accounts.Remove(account);
|
||||||
{
|
await db.SaveChangesAsync();
|
||||||
var newProfiles = missingId.Select(id => new AccountProfile { Id = Guid.NewGuid(), AccountId = id })
|
|
||||||
.ToList();
|
|
||||||
await db.BulkInsertAsync(newProfiles);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
77
DysonNetwork.Pass/Account/BotAccountReceiverGrpc.cs
Normal file
77
DysonNetwork.Pass/Account/BotAccountReceiverGrpc.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using DysonNetwork.Shared.Proto;
|
||||||
|
using Grpc.Core;
|
||||||
|
using NodaTime;
|
||||||
|
using NodaTime.Serialization.Protobuf;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Account;
|
||||||
|
|
||||||
|
public class BotAccountReceiverGrpc(AppDatabase db, AccountService accounts)
|
||||||
|
: BotAccountReceiverService.BotAccountReceiverServiceBase
|
||||||
|
{
|
||||||
|
public override async Task<CreateBotAccountResponse> CreateBotAccount(
|
||||||
|
CreateBotAccountRequest request,
|
||||||
|
ServerCallContext context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var account = Account.FromProtoValue(request.Account);
|
||||||
|
account = await accounts.CreateBotAccount(account, Guid.Parse(request.AutomatedId));
|
||||||
|
|
||||||
|
return new CreateBotAccountResponse
|
||||||
|
{
|
||||||
|
Bot = new BotAccount
|
||||||
|
{
|
||||||
|
Account = account.ToProtoValue(),
|
||||||
|
AutomatedId = account.Id.ToString(),
|
||||||
|
CreatedAt = account.CreatedAt.ToTimestamp(),
|
||||||
|
UpdatedAt = account.UpdatedAt.ToTimestamp(),
|
||||||
|
IsActive = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<UpdateBotAccountResponse> UpdateBotAccount(
|
||||||
|
UpdateBotAccountRequest request,
|
||||||
|
ServerCallContext context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var automatedId = Guid.Parse(request.AutomatedId);
|
||||||
|
var account = await accounts.GetBotAccount(automatedId);
|
||||||
|
if (account is null)
|
||||||
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
|
||||||
|
|
||||||
|
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);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return new UpdateBotAccountResponse
|
||||||
|
{
|
||||||
|
Bot = new BotAccount
|
||||||
|
{
|
||||||
|
Account = account.ToProtoValue(),
|
||||||
|
AutomatedId = account.Id.ToString(),
|
||||||
|
CreatedAt = account.CreatedAt.ToTimestamp(),
|
||||||
|
UpdatedAt = account.UpdatedAt.ToTimestamp(),
|
||||||
|
IsActive = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<DeleteBotAccountResponse> DeleteBotAccount(
|
||||||
|
DeleteBotAccountRequest request,
|
||||||
|
ServerCallContext context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var automatedId = Guid.Parse(request.AutomatedId);
|
||||||
|
var account = await accounts.GetBotAccount(automatedId);
|
||||||
|
if (account is null)
|
||||||
|
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
|
||||||
|
|
||||||
|
await accounts.DeleteAccount(account);
|
||||||
|
|
||||||
|
return new DeleteBotAccountResponse();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,4 @@
|
|||||||
using DysonNetwork.Pass;
|
using DysonNetwork.Pass;
|
||||||
using DysonNetwork.Pass.Auth;
|
|
||||||
using DysonNetwork.Pass.Pages.Data;
|
using DysonNetwork.Pass.Pages.Data;
|
||||||
using DysonNetwork.Pass.Startup;
|
using DysonNetwork.Pass.Startup;
|
||||||
using DysonNetwork.Shared.Http;
|
using DysonNetwork.Shared.Http;
|
||||||
|
@@ -76,6 +76,7 @@ public static class ApplicationConfiguration
|
|||||||
app.MapGrpcService<AuthServiceGrpc>();
|
app.MapGrpcService<AuthServiceGrpc>();
|
||||||
app.MapGrpcService<ActionLogServiceGrpc>();
|
app.MapGrpcService<ActionLogServiceGrpc>();
|
||||||
app.MapGrpcService<PermissionServiceGrpc>();
|
app.MapGrpcService<PermissionServiceGrpc>();
|
||||||
|
app.MapGrpcService<BotAccountReceiverGrpc>();
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
@@ -32,12 +32,9 @@ public static class GrpcClientHelper
|
|||||||
private static async Task<string> GetServiceUrlFromEtcd(IEtcdClient etcdClient, string serviceName)
|
private static async Task<string> GetServiceUrlFromEtcd(IEtcdClient etcdClient, string serviceName)
|
||||||
{
|
{
|
||||||
var response = await etcdClient.GetAsync($"/services/{serviceName}");
|
var response = await etcdClient.GetAsync($"/services/{serviceName}");
|
||||||
if (response.Kvs.Count == 0)
|
return response.Kvs.Count == 0
|
||||||
{
|
? throw new InvalidOperationException($"Service '{serviceName}' not found in Etcd.")
|
||||||
throw new InvalidOperationException($"Service '{serviceName}' not found in Etcd.");
|
: response.Kvs[0].Value.ToStringUtf8();
|
||||||
}
|
|
||||||
|
|
||||||
return response.Kvs[0].Value.ToStringUtf8();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<AccountService.AccountServiceClient> CreateAccountServiceClient(
|
public static async Task<AccountService.AccountServiceClient> CreateAccountServiceClient(
|
||||||
@@ -52,6 +49,20 @@ public static class GrpcClientHelper
|
|||||||
clientCertPassword));
|
clientCertPassword));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<BotAccountReceiverService.BotAccountReceiverServiceClient>
|
||||||
|
CreateBotAccountReceiverServiceClient(
|
||||||
|
IEtcdClient etcdClient,
|
||||||
|
string clientCertPath,
|
||||||
|
string clientKeyPath,
|
||||||
|
string? clientCertPassword = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var url = await GetServiceUrlFromEtcd(etcdClient, "DysonNetwork.Pass");
|
||||||
|
return new BotAccountReceiverService.BotAccountReceiverServiceClient(CreateCallInvoker(url, clientCertPath,
|
||||||
|
clientKeyPath,
|
||||||
|
clientCertPassword));
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<ActionLogService.ActionLogServiceClient> CreateActionLogServiceClient(
|
public static async Task<ActionLogService.ActionLogServiceClient> CreateActionLogServiceClient(
|
||||||
IEtcdClient etcdClient,
|
IEtcdClient etcdClient,
|
||||||
string clientCertPath,
|
string clientCertPath,
|
||||||
@@ -147,4 +158,4 @@ public static class GrpcClientHelper
|
|||||||
return new CustomAppService.CustomAppServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
return new CustomAppService.CustomAppServiceClient(CreateCallInvoker(url, clientCertPath, clientKeyPath,
|
||||||
clientCertPassword));
|
clientCertPassword));
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,6 +5,7 @@ package proto;
|
|||||||
option csharp_namespace = "DysonNetwork.Shared.Proto";
|
option csharp_namespace = "DysonNetwork.Shared.Proto";
|
||||||
|
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
|
import "account.proto";
|
||||||
import "file.proto";
|
import "file.proto";
|
||||||
|
|
||||||
message CustomAppOauthConfig {
|
message CustomAppOauthConfig {
|
||||||
@@ -25,7 +26,7 @@ enum CustomAppStatus {
|
|||||||
SUSPENDED = 4;
|
SUSPENDED = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CustomApp {
|
message CustomApp {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string slug = 2;
|
string slug = 2;
|
||||||
string name = 3;
|
string name = 3;
|
||||||
@@ -43,7 +44,7 @@ enum CustomAppStatus {
|
|||||||
|
|
||||||
google.protobuf.Timestamp created_at = 11;
|
google.protobuf.Timestamp created_at = 11;
|
||||||
google.protobuf.Timestamp updated_at = 12;
|
google.protobuf.Timestamp updated_at = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CustomAppSecret {
|
message CustomAppSecret {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
@@ -86,3 +87,60 @@ service CustomAppService {
|
|||||||
rpc GetCustomApp(GetCustomAppRequest) returns (GetCustomAppResponse);
|
rpc GetCustomApp(GetCustomAppRequest) returns (GetCustomAppResponse);
|
||||||
rpc CheckCustomAppSecret(CheckCustomAppSecretRequest) returns (CheckCustomAppSecretResponse);
|
rpc CheckCustomAppSecret(CheckCustomAppSecretRequest) returns (CheckCustomAppSecretResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BotAccount represents a bot account in the system
|
||||||
|
// It extends the base Account with bot-specific fields
|
||||||
|
message BotAccount {
|
||||||
|
// Base account information
|
||||||
|
Account account = 1;
|
||||||
|
|
||||||
|
// Bot-specific information
|
||||||
|
string slug = 2; // Unique identifier for the bot
|
||||||
|
bool is_active = 3; // Whether the bot is currently active
|
||||||
|
string automated_id = 5; // The bot ID
|
||||||
|
|
||||||
|
// Timestamps
|
||||||
|
google.protobuf.Timestamp created_at = 6;
|
||||||
|
google.protobuf.Timestamp updated_at = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request/Response messages for BotAccount operations
|
||||||
|
message CreateBotAccountRequest {
|
||||||
|
Account account = 1;
|
||||||
|
string automated_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreateBotAccountResponse {
|
||||||
|
BotAccount bot = 1; // The created bot account
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateBotAccountRequest {
|
||||||
|
string automated_id = 1; // ID of the bot account to update
|
||||||
|
Account account = 2; // Updated account information
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateBotAccountResponse {
|
||||||
|
BotAccount bot = 1; // The updated bot account
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteBotAccountRequest {
|
||||||
|
string automated_id = 1; // ID of the bot account to delete
|
||||||
|
bool force = 2; // Whether to force delete (bypass soft delete)
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteBotAccountResponse {
|
||||||
|
bool success = 1; // Whether the deletion was successful
|
||||||
|
}
|
||||||
|
|
||||||
|
// This service should be implemented by the Pass service to handle the creation, update, and deletion of bot accounts
|
||||||
|
service BotAccountReceiverService {
|
||||||
|
// Create a new bot account
|
||||||
|
rpc CreateBotAccount(CreateBotAccountRequest) returns (CreateBotAccountResponse);
|
||||||
|
|
||||||
|
// Update an existing bot account
|
||||||
|
rpc UpdateBotAccount(UpdateBotAccountRequest) returns (UpdateBotAccountResponse);
|
||||||
|
|
||||||
|
// Delete a bot account
|
||||||
|
rpc DeleteBotAccount(DeleteBotAccountRequest) returns (DeleteBotAccountResponse);
|
||||||
|
}
|
||||||
|
|
@@ -43,6 +43,20 @@ public static class ServiceInjectionHelper
|
|||||||
});
|
});
|
||||||
services.AddSingleton<AccountClientHelper>();
|
services.AddSingleton<AccountClientHelper>();
|
||||||
|
|
||||||
|
services.AddSingleton<BotAccountReceiverService.BotAccountReceiverServiceClient>(sp =>
|
||||||
|
{
|
||||||
|
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||||
|
var config = sp.GetRequiredService<IConfiguration>();
|
||||||
|
var clientCertPath = config["Service:ClientCert"]!;
|
||||||
|
var clientKeyPath = config["Service:ClientKey"]!;
|
||||||
|
var clientCertPassword = config["Service:CertPassword"];
|
||||||
|
|
||||||
|
return GrpcClientHelper
|
||||||
|
.CreateBotAccountReceiverServiceClient(etcdClient, clientCertPath, clientKeyPath, clientCertPassword)
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
});
|
||||||
|
|
||||||
services.AddSingleton<ActionLogService.ActionLogServiceClient>(sp =>
|
services.AddSingleton<ActionLogService.ActionLogServiceClient>(sp =>
|
||||||
{
|
{
|
||||||
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
var etcdClient = sp.GetRequiredService<IEtcdClient>();
|
||||||
|
Reference in New Issue
Block a user