Account bot basis

This commit is contained in:
2025-08-19 15:16:25 +08:00
parent c056938b6e
commit bf181b88ec
13 changed files with 512 additions and 31 deletions

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

View 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");
}
}
}

View 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();
}
}