yao/openapi/agent/robot/list.go
Max 64b5ffd154 Enhance Robot API with Permission Fields and Status Management
- Added `YaoCreatedBy` and `YaoTeamID` fields to `RobotState` and `RobotResponse` for improved access control.
- Updated `GetRobotStatus` to retrieve permission fields from the store and populate the robot state.
- Modified `robotFields` in the store to include new Yao permission fields for better management of access control.
- Enhanced OpenAPI integration by registering robot routes and ensuring proper permission checks in handlers.
- Updated documentation in TODO.md to reflect the completion of permission logic and API enhancements.
2026-01-22 12:21:44 +08:00

108 lines
2.9 KiB
Go

package robot
import (
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/yaoapp/kun/log"
robotapi "github.com/yaoapp/yao/agent/robot/api"
robottypes "github.com/yaoapp/yao/agent/robot/types"
"github.com/yaoapp/yao/openapi/oauth/authorized"
"github.com/yaoapp/yao/openapi/response"
)
// ListRobots lists robots with pagination and filtering
// GET /v1/agent/robots
func ListRobots(c *gin.Context) {
// Get authorized information
authInfo := authorized.GetInfo(c)
// Parse pagination parameters
page := 1
if pageStr := c.Query("page"); pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
pageSize := 20
if pageSizeStr := c.Query("pagesize"); pageSizeStr != "" {
if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 && ps <= 100 {
pageSize = ps
}
}
// Parse filter parameters
requestedTeamID := strings.TrimSpace(c.Query("team_id"))
status := strings.TrimSpace(c.Query("status"))
keywords := strings.TrimSpace(c.Query("keywords"))
// Apply permission-based filtering
// This ensures users only see robots they have access to:
// - No constraints: use requested team_id or no filter
// - TeamOnly: force filter to user's team
// - OwnerOnly: filter by user_id (personal resources)
effectiveTeamID := BuildListFilter(c, authInfo, requestedTeamID)
// Build query
query := &robotapi.ListQuery{
TeamID: effectiveTeamID,
Keywords: keywords,
Page: page,
PageSize: pageSize,
}
if status != "" {
query.Status = robottypes.RobotStatus(status)
}
// Create robot context
ctx := &robottypes.Context{}
// Call API layer
result, err := robotapi.ListRobots(ctx, query)
if err != nil {
log.Error("Failed to list robots: %v", err)
errorResp := &response.ErrorResponse{
Code: response.ErrServerError.Code,
ErrorDescription: "Failed to list robots: " + err.Error(),
}
response.RespondWithError(c, response.StatusInternalServerError, errorResp)
return
}
// Convert to HTTP response format
robots := make([]*Response, 0, len(result.Data))
for _, r := range result.Data {
robots = append(robots, newResponseFromRobot(r))
}
resp := &ListResponse{
Data: robots,
Total: result.Total,
Page: result.Page,
PageSize: result.PageSize,
}
response.RespondWithSuccess(c, response.StatusOK, resp)
}
// newResponseFromRobot converts types.Robot to Response
func newResponseFromRobot(r *robottypes.Robot) *Response {
if r == nil {
return nil
}
return &Response{
Name: r.MemberID, // Frontend mapping: name ← member_id
Description: r.Bio, // Frontend mapping: description ← bio
MemberID: r.MemberID,
TeamID: r.TeamID,
RobotStatus: string(r.Status),
AutonomousMode: r.AutonomousMode,
DisplayName: r.DisplayName,
Bio: r.Bio,
SystemPrompt: r.SystemPrompt,
RobotEmail: r.RobotEmail,
}
}