chore: initial commit of ai site platform
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
244
platform/internal/logic/applogic/agents.go
Normal file
244
platform/internal/logic/applogic/agents.go
Normal file
@@ -0,0 +1,244 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user