Files
ai_site/platform/internal/logic/applogic/agents.go
2026-07-31 10:31:17 +08:00

245 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package applogic
import (
"context"
"fmt"
"strings"
"aijianzhan/platform/internal/agentcap"
"aijianzhan/platform/internal/agentstore"
"aijianzhan/platform/internal/authx"
"aijianzhan/platform/internal/svc"
"aijianzhan/platform/internal/tenantperm"
"aijianzhan/platform/internal/types"
)
type AgentAdminLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAgentAdminLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentAdminLogic {
return &AgentAdminLogic{ctx: ctx, svcCtx: svcCtx}
}
func (l *AgentAdminLogic) store() (agentstore.Store, error) {
if l.svcCtx.Agents == nil {
return nil, fmt.Errorf("agent store unavailable")
}
return l.svcCtx.Agents, nil
}
func (l *AgentAdminLogic) List() ([]agentstore.Account, error) {
st, err := l.store()
if err != nil {
return nil, err
}
items, err := st.List(l.ctx, authx.TenantID(l.ctx))
if err != nil {
return nil, err
}
for i := range items {
l.attachRole(&items[i])
}
return items, nil
}
func (l *AgentAdminLogic) Get(agentID int64) (*agentstore.Account, error) {
st, err := l.store()
if err != nil {
return nil, err
}
acc, err := st.Get(l.ctx, authx.TenantID(l.ctx), agentID)
if err != nil {
return nil, err
}
l.attachRole(acc)
return acc, nil
}
func (l *AgentAdminLogic) attachRole(acc *agentstore.Account) {
if acc == nil || acc.RoleID <= 0 || l.svcCtx.Roles == nil {
return
}
role, err := l.svcCtx.Roles.Get(l.ctx, authx.TenantID(l.ctx), acc.RoleID)
if err != nil {
return
}
acc.RoleCode = role.Code
acc.RoleName = role.Name
}
func (l *AgentAdminLogic) resolvePerms(roleID int64, fallback []string) (int64, []string, error) {
if roleID <= 0 {
return 0, authx.NormalizePerms(fallback), nil
}
if l.svcCtx.Roles == nil {
return 0, nil, fmt.Errorf("role store unavailable")
}
role, err := l.svcCtx.Roles.Get(l.ctx, authx.TenantID(l.ctx), roleID)
if err != nil {
return 0, nil, err
}
return role.RoleID, authx.NormalizePerms(append([]string{}, role.Permissions...)), nil
}
func (l *AgentAdminLogic) Create(req *types.AgentCreateReq) (*types.AgentCreateResp, error) {
st, err := l.store()
if err != nil {
return nil, err
}
roleID, perms, err := l.resolvePerms(req.RoleID, req.Permissions)
if err != nil {
return nil, err
}
if roleID <= 0 && len(perms) == 0 {
return nil, fmt.Errorf("role_id or permissions required")
}
if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), perms); err != nil {
return nil, err
}
acc, secret, err := st.Create(l.ctx, authx.TenantID(l.ctx), authx.UserID(l.ctx), agentstore.CreateInput{
Name: req.Name,
Perms: perms,
AppSlugs: req.AppSlugs,
Status: agentstore.StatusActive,
RoleID: roleID,
})
if err != nil {
return nil, err
}
l.attachRole(acc)
return &types.AgentCreateResp{Account: *acc, ClientSecret: secret}, nil
}
func (l *AuthLogic) SelfRegisterAgent(req *types.AgentSelfRegisterReq) (*types.AgentSelfRegisterResp, error) {
if l.svcCtx.Agents == nil {
return nil, fmt.Errorf("agent store unavailable")
}
want := l.svcCtx.Config.Agent.RegisterSecret
if want == "" {
want = l.svcCtx.Config.Auth.IssueSecret
}
if want == "" {
want = l.svcCtx.JWT.AccessSecret
}
if req.RegisterSecret == "" || req.RegisterSecret != want {
return nil, fmt.Errorf("invalid register secret")
}
tenantID := req.TenantID
if tenantID <= 0 {
tenantID = 1
}
acc, secret, reused, err := l.svcCtx.Agents.Register(l.ctx, tenantID, req.Name, req.HostKey)
if err != nil {
return nil, err
}
msg := "已登记为 pending请管理员在控制台分配角色并启用后再换票"
if reused {
msg = "已存在 pending 登记,已轮换 client_secret仍须管理员分配角色并启用"
}
return &types.AgentSelfRegisterResp{
Account: *acc,
ClientSecret: secret,
Reused: reused,
Message: msg,
}, nil
}
func (l *AgentAdminLogic) Update(agentID int64, req *types.AgentUpdateReq) (*agentstore.Account, error) {
st, err := l.store()
if err != nil {
return nil, err
}
in := agentstore.UpdateInput{}
if req.Name != nil {
in.Name = req.Name
}
if req.Status != nil {
in.Status = req.Status
}
if req.AppSlugs != nil {
in.AppSlugs = req.AppSlugs
}
if req.RoleID != nil {
roleID, perms, err := l.resolvePerms(*req.RoleID, nil)
if err != nil {
return nil, err
}
if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), perms); err != nil {
return nil, err
}
in.RoleID = &roleID
in.Perms = &perms
} else if req.Permissions != nil {
n := authx.NormalizePerms(*req.Permissions)
if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), n); err != nil {
return nil, err
}
in.Perms = &n
}
acc, err := st.Update(l.ctx, authx.TenantID(l.ctx), agentID, in)
if err != nil {
return nil, err
}
l.attachRole(acc)
return acc, nil
}
func (l *AgentAdminLogic) Rotate(agentID int64) (*types.AgentSecretResp, error) {
st, err := l.store()
if err != nil {
return nil, err
}
secret, err := st.RotateSecret(l.ctx, authx.TenantID(l.ctx), agentID)
if err != nil {
return nil, err
}
acc, err := st.Get(l.ctx, authx.TenantID(l.ctx), agentID)
if err != nil {
return nil, err
}
return &types.AgentSecretResp{ClientID: acc.ClientID, ClientSecret: secret}, nil
}
func (l *AgentAdminLogic) Delete(agentID int64) error {
st, err := l.store()
if err != nil {
return err
}
return st.Delete(l.ctx, authx.TenantID(l.ctx), agentID)
}
func (l *AuthLogic) IssueClientCredentials(clientID, clientSecret string) (*types.TokenResp, error) {
if l.svcCtx.Agents == nil {
return nil, fmt.Errorf("agent store unavailable")
}
acc, err := l.svcCtx.Agents.Authenticate(l.ctx, strings.TrimSpace(clientID), clientSecret)
if err != nil {
return nil, err
}
token, exp, err := authx.IssueAgentToken(l.svcCtx.JWT, acc.TenantID, acc.AgentID, acc.Perms)
if err != nil {
return nil, err
}
_ = l.svcCtx.Agents.TouchToken(l.ctx, acc.AgentID)
secret := l.svcCtx.Config.Agent.CapsuleSecret
if secret == "" {
secret = l.svcCtx.JWT.AccessSecret
}
return &types.TokenResp{
AccessToken: token,
TokenType: "Bearer",
ExpiresAt: exp,
TenantID: acc.TenantID,
UserID: acc.AgentID,
Username: acc.ClientID,
DisplayName: acc.Name,
Role: authx.RoleAgent,
AgentKey: agentcap.PublicAgentKey(secret, acc.TenantID, acc.AgentID),
AgentID: acc.AgentID,
Permissions: append([]string{}, acc.Perms...),
AppSlugs: append([]string{}, acc.AppSlugs...),
}, nil
}