chore: initial commit of ai site platform

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-07-31 10:19:22 +08:00
commit 6366859bb3
222 changed files with 47313 additions and 0 deletions

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