Bot controller has keys endpoints

This commit is contained in:
2025-08-23 19:52:05 +08:00
parent d9620fd6a4
commit 953bf5d4de
5 changed files with 370 additions and 16 deletions

View File

@@ -1,8 +1,10 @@
using DysonNetwork.Shared.Data;
using DysonNetwork.Shared.Proto;
using Grpc.Core;
using NodaTime;
using Microsoft.EntityFrameworkCore;
using NodaTime.Serialization.Protobuf;
using ApiKey = DysonNetwork.Shared.Proto.ApiKey;
using AuthService = DysonNetwork.Pass.Auth.AuthService;
namespace DysonNetwork.Pass.Account;
@@ -10,7 +12,8 @@ public class BotAccountReceiverGrpc(
AppDatabase db,
AccountService accounts,
FileService.FileServiceClient files,
FileReferenceService.FileReferenceServiceClient fileRefs
FileReferenceService.FileReferenceServiceClient fileRefs,
AuthService authService
)
: BotAccountReceiverService.BotAccountReceiverServiceBase
{
@@ -107,10 +110,107 @@ public class BotAccountReceiverGrpc(
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"));
throw new RpcException(new Grpc.Core.Status(Grpc.Core.StatusCode.NotFound, "Account not found"));
await accounts.DeleteAccount(account);
return new DeleteBotAccountResponse();
}
public override async Task<ApiKey> GetApiKey(GetApiKeyRequest request, ServerCallContext context)
{
var keyId = Guid.Parse(request.Id);
var key = await db.ApiKeys
.Include(k => k.Account)
.FirstOrDefaultAsync(k => k.Id == keyId);
if (key == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "API key not found"));
return key.ToProtoValue();
}
public override async Task<GetApiKeyBatchResponse> ListApiKey(ListApiKeyRequest request, ServerCallContext context)
{
var automatedId = Guid.Parse(request.AutomatedId);
var account = await accounts.GetBotAccount(automatedId);
if (account == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
var keys = await db.ApiKeys
.Where(k => k.AccountId == account.Id)
.Select(k => k.ToProtoValue())
.ToListAsync();
var response = new GetApiKeyBatchResponse();
response.Data.AddRange(keys);
return response;
}
public override async Task<ApiKey> CreateApiKey(ApiKey request, ServerCallContext context)
{
var accountId = Guid.Parse(request.AccountId);
var account = await accounts.GetBotAccount(accountId);
if (account == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "Account not found"));
if (string.IsNullOrWhiteSpace(request.Label))
throw new RpcException(new Grpc.Core.Status(StatusCode.InvalidArgument, "Label is required"));
var key = await authService.CreateApiKey(account.Id, request.Label, null);
key.Key = await authService.IssueApiKeyToken(key);
return key.ToProtoValue();
}
public override async Task<ApiKey> UpdateApiKey(ApiKey request, ServerCallContext context)
{
var keyId = Guid.Parse(request.Id);
var accountId = Guid.Parse(request.AccountId);
var key = await db.ApiKeys
.FirstOrDefaultAsync(k => k.Id == keyId && k.AccountId == accountId);
if (key == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "API key not found"));
// Only update the label if provided
if (string.IsNullOrWhiteSpace(request.Label)) return key.ToProtoValue();
key.Label = request.Label;
db.ApiKeys.Update(key);
await db.SaveChangesAsync();
return key.ToProtoValue();
}
public override async Task<ApiKey> RotateApiKey(GetApiKeyRequest request, ServerCallContext context)
{
var keyId = Guid.Parse(request.Id);
var key = await db.ApiKeys
.Include(k => k.Account)
.FirstOrDefaultAsync(k => k.Id == keyId);
if (key == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "API key not found"));
key = await authService.RotateApiKeyToken(key);
key.Key = await authService.IssueApiKeyToken(key);
return key.ToProtoValue();
}
public override async Task<DeleteApiKeyResponse> DeleteApiKey(GetApiKeyRequest request, ServerCallContext context)
{
var keyId = Guid.Parse(request.Id);
var key = await db.ApiKeys
.Include(k => k.Account)
.FirstOrDefaultAsync(k => k.Id == keyId);
if (key == null)
throw new RpcException(new Grpc.Core.Status(StatusCode.NotFound, "API key not found"));
await authService.RevokeApiKeyToken(key);
return new DeleteApiKeyResponse { Success = true };
}
}

View File

@@ -9,11 +9,37 @@ public class ApiKey : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
[MaxLength(1024)] public string Label { get; set; } = null!;
public Guid AccountId { get; set; }
public Account.Account Account { get; set; } = null!;
public Guid SessionId { get; set; }
public AuthSession Session { get; set; } = null!;
[NotMapped] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Key { get; set; }
[NotMapped]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Key { get; set; }
public DysonNetwork.Shared.Proto.ApiKey ToProtoValue()
{
return new DysonNetwork.Shared.Proto.ApiKey
{
Id = Id.ToString(),
Label = Label,
AccountId = AccountId.ToString(),
SessionId = SessionId.ToString(),
Key = Key
};
}
public static ApiKey FromProtoValue(DysonNetwork.Shared.Proto.ApiKey proto)
{
return new ApiKey
{
Id = Guid.Parse(proto.Id),
AccountId = Guid.Parse(proto.AccountId),
SessionId = Guid.Parse(proto.SessionId),
Label = proto.Label,
Key = proto.Key
};
}
}