package store import ( "context" "fmt" "strings" "time" ) type AdminTaskListFilter struct { Query string GatewayTenant string GatewayUser string UserGroup string Status string Platform string Model string ModelType string RunMode string BillingStatus string APIKey string CreatedFrom *time.Time CreatedTo *time.Time Page int PageSize int } type AdminTaskUserSummary struct { ID string `json:"id"` Username string `json:"username,omitempty"` DisplayName string `json:"displayName,omitempty"` Email string `json:"email,omitempty"` Source string `json:"source,omitempty"` } type AdminTaskTenantSummary struct { ID string `json:"id"` TenantKey string `json:"tenantKey,omitempty"` Name string `json:"name,omitempty"` } type AdminTaskPlatformSummary struct { ID string `json:"id"` Name string `json:"name,omitempty"` Provider string `json:"provider,omitempty"` } type AdminTaskContext struct { User *AdminTaskUserSummary `json:"user,omitempty"` Tenant *AdminTaskTenantSummary `json:"tenant,omitempty"` LatestPlatform *AdminTaskPlatformSummary `json:"latestPlatform,omitempty"` } type AdminGatewayTask struct { GatewayTask AdminContext AdminTaskContext `json:"adminContext"` } type AdminTaskListResult struct { Items []AdminGatewayTask Total int Page int PageSize int } func (s *Store) ListAdminTasks(ctx context.Context, filter AdminTaskListFilter) (AdminTaskListResult, error) { page := filter.Page if page <= 0 { page = 1 } pageSize := filter.PageSize if pageSize <= 0 { pageSize = 50 } if pageSize > 100 { pageSize = 100 } where, args := buildAdminTaskWhere(filter) var total int if err := s.pool.QueryRow(ctx, ` SELECT count(*) FROM gateway_tasks t WHERE `+strings.Join(where, "\n AND "), args...).Scan(&total); err != nil { return AdminTaskListResult{}, err } queryArgs := append([]any{}, args...) queryArgs = append(queryArgs, pageSize, (page-1)*pageSize) rows, err := s.pool.Query(ctx, ` SELECT t.id::text FROM gateway_tasks t WHERE `+strings.Join(where, "\n AND ")+` ORDER BY t.created_at DESC, t.id DESC LIMIT $`+fmt.Sprint(len(args)+1)+` OFFSET $`+fmt.Sprint(len(args)+2), queryArgs...) if err != nil { return AdminTaskListResult{}, err } defer rows.Close() taskIDs := make([]string, 0, pageSize) for rows.Next() { var taskID string if err := rows.Scan(&taskID); err != nil { return AdminTaskListResult{}, err } taskIDs = append(taskIDs, taskID) } if err := rows.Err(); err != nil { return AdminTaskListResult{}, err } items, err := s.loadAdminTasksByIDs(ctx, taskIDs) if err != nil { return AdminTaskListResult{}, err } return AdminTaskListResult{ Items: items, Total: total, Page: page, PageSize: pageSize, }, nil } func (s *Store) GetAdminTask(ctx context.Context, taskID string) (AdminGatewayTask, error) { task, err := s.GetTask(ctx, taskID) if err != nil { return AdminGatewayTask{}, err } contexts, err := s.loadAdminTaskContexts(ctx, []string{task.ID}) if err != nil { return AdminGatewayTask{}, err } return adminTaskWithContext(task, contexts[task.ID]), nil } func buildAdminTaskWhere(filter AdminTaskListFilter) ([]string, []any) { where := []string{"TRUE"} args := make([]any, 0, 14) add := func(clause string, value any) { args = append(args, value) placeholder := fmt.Sprintf("$%d", len(args)) where = append(where, strings.ReplaceAll(clause, "$%d", placeholder)) } if query := strings.TrimSpace(filter.Query); query != "" { add(`( t.id::text ILIKE $%d OR COALESCE(t.request_id, '') ILIKE $%d OR t.kind ILIKE $%d OR t.model ILIKE $%d OR COALESCE(t.requested_model, '') ILIKE $%d OR COALESCE(t.resolved_model, '') ILIKE $%d OR COALESCE(t.api_key_id, '') ILIKE $%d OR COALESCE(t.api_key_name, '') ILIKE $%d OR COALESCE(t.api_key_prefix, '') ILIKE $%d OR COALESCE(t.model_type, '') ILIKE $%d OR EXISTS ( SELECT 1 FROM gateway_users admin_user WHERE admin_user.id = t.gateway_user_id AND ( admin_user.username ILIKE $%d OR COALESCE(admin_user.display_name, '') ILIKE $%d OR COALESCE(admin_user.email, '') ILIKE $%d OR admin_user.user_key ILIKE $%d ) ) OR EXISTS ( SELECT 1 FROM gateway_tenants admin_tenant WHERE admin_tenant.id = t.gateway_tenant_id AND ( admin_tenant.name ILIKE $%d OR admin_tenant.tenant_key ILIKE $%d ) ) OR EXISTS ( SELECT 1 FROM gateway_task_attempts admin_attempt LEFT JOIN integration_platforms admin_platform ON admin_platform.id = admin_attempt.platform_id LEFT JOIN platform_models admin_model ON admin_model.id = admin_attempt.platform_model_id WHERE admin_attempt.task_id = t.id AND ( COALESCE(admin_platform.name, '') ILIKE $%d OR COALESCE(admin_platform.internal_name, '') ILIKE $%d OR COALESCE(admin_platform.provider, '') ILIKE $%d OR COALESCE(admin_model.model_name, '') ILIKE $%d OR COALESCE(admin_model.provider_model_name, '') ILIKE $%d OR COALESCE(admin_model.model_alias, '') ILIKE $%d ) ) )`, "%"+query+"%") } if value := strings.TrimSpace(filter.GatewayTenant); value != "" { add("t.gateway_tenant_id = $%d::uuid", value) } if value := strings.TrimSpace(filter.GatewayUser); value != "" { add("t.gateway_user_id = $%d::uuid", value) } if value := strings.TrimSpace(filter.UserGroup); value != "" { add("t.user_group_id = $%d::uuid", value) } if value := strings.TrimSpace(filter.Status); value != "" { add("t.status = $%d", value) } if value := strings.TrimSpace(filter.Platform); value != "" { add(`EXISTS ( SELECT 1 FROM gateway_task_attempts platform_attempt WHERE platform_attempt.task_id = t.id AND platform_attempt.platform_id = $%d::uuid )`, value) } if value := strings.TrimSpace(filter.Model); value != "" { add(`( t.model = $%d OR COALESCE(t.requested_model, '') = $%d OR COALESCE(t.resolved_model, '') = $%d OR EXISTS ( SELECT 1 FROM gateway_task_attempts model_attempt LEFT JOIN platform_models selected_model ON selected_model.id = model_attempt.platform_model_id WHERE model_attempt.task_id = t.id AND ( COALESCE(selected_model.model_name, '') = $%d OR COALESCE(selected_model.provider_model_name, '') = $%d OR COALESCE(selected_model.model_alias, '') = $%d ) ) )`, value) } if value := strings.TrimSpace(filter.ModelType); value != "" { add("COALESCE(t.model_type, '') = $%d", value) } if value := strings.TrimSpace(filter.RunMode); value != "" { add("t.run_mode = $%d", value) } if value := strings.TrimSpace(filter.BillingStatus); value != "" { add("t.billing_status = $%d", value) } if value := strings.TrimSpace(filter.APIKey); value != "" { add(`( COALESCE(t.api_key_id, '') ILIKE $%d OR COALESCE(t.api_key_name, '') ILIKE $%d OR COALESCE(t.api_key_prefix, '') ILIKE $%d )`, "%"+value+"%") } if filter.CreatedFrom != nil { add("t.created_at >= $%d::timestamptz", *filter.CreatedFrom) } if filter.CreatedTo != nil { add("t.created_at <= $%d::timestamptz", *filter.CreatedTo) } return where, args } func (s *Store) loadAdminTasksByIDs(ctx context.Context, taskIDs []string) ([]AdminGatewayTask, error) { if len(taskIDs) == 0 { return []AdminGatewayTask{}, nil } rows, err := s.pool.Query(ctx, ` SELECT `+gatewayTaskColumns+` FROM gateway_tasks WHERE id::text = ANY($1)`, taskIDs) if err != nil { return nil, err } defer rows.Close() tasksByID := make(map[string]GatewayTask, len(taskIDs)) tasks := make([]GatewayTask, 0, len(taskIDs)) for rows.Next() { task, err := scanGatewayTask(rows) if err != nil { return nil, err } tasks = append(tasks, task) } if err := rows.Err(); err != nil { return nil, err } tasks, err = s.attachTaskAttempts(ctx, tasks) if err != nil { return nil, err } for _, task := range tasks { tasksByID[task.ID] = task } contexts, err := s.loadAdminTaskContexts(ctx, taskIDs) if err != nil { return nil, err } items := make([]AdminGatewayTask, 0, len(taskIDs)) for _, taskID := range taskIDs { task, ok := tasksByID[taskID] if !ok { continue } items = append(items, adminTaskWithContext(task, contexts[taskID])) } return items, nil } func (s *Store) loadAdminTaskContexts(ctx context.Context, taskIDs []string) (map[string]AdminTaskContext, error) { contexts := make(map[string]AdminTaskContext, len(taskIDs)) if len(taskIDs) == 0 { return contexts, nil } rows, err := s.pool.Query(ctx, ` SELECT t.id::text, COALESCE(u.id::text, ''), COALESCE(u.username, ''), COALESCE(u.display_name, ''), COALESCE(u.email, ''), COALESCE(u.source, ''), COALESCE(tenant.id::text, ''), COALESCE(tenant.tenant_key, ''), COALESCE(tenant.name, '') FROM gateway_tasks t LEFT JOIN gateway_users u ON u.id = t.gateway_user_id LEFT JOIN gateway_tenants tenant ON tenant.id = t.gateway_tenant_id WHERE t.id::text = ANY($1)`, taskIDs) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var taskID string var user AdminTaskUserSummary var tenant AdminTaskTenantSummary if err := rows.Scan( &taskID, &user.ID, &user.Username, &user.DisplayName, &user.Email, &user.Source, &tenant.ID, &tenant.TenantKey, &tenant.Name, ); err != nil { return nil, err } context := AdminTaskContext{} if user.ID != "" { context.User = &user } if tenant.ID != "" { context.Tenant = &tenant } contexts[taskID] = context } return contexts, rows.Err() } func adminTaskWithContext(task GatewayTask, context AdminTaskContext) AdminGatewayTask { if context.User == nil { userID := strings.TrimSpace(task.GatewayUserID) if userID == "" { userID = strings.TrimSpace(task.UserID) } if userID != "" { context.User = &AdminTaskUserSummary{ID: userID, Source: task.UserSource} } } if context.Tenant == nil { tenantID := strings.TrimSpace(task.GatewayTenantID) if tenantID == "" { tenantID = strings.TrimSpace(task.TenantID) } if tenantID != "" || strings.TrimSpace(task.TenantKey) != "" { context.Tenant = &AdminTaskTenantSummary{ID: tenantID, TenantKey: task.TenantKey} } } for index := len(task.Attempts) - 1; index >= 0; index-- { attempt := task.Attempts[index] if strings.TrimSpace(attempt.PlatformID) == "" { continue } context.LatestPlatform = &AdminTaskPlatformSummary{ ID: attempt.PlatformID, Name: attempt.PlatformName, Provider: attempt.Provider, } break } return AdminGatewayTask{GatewayTask: task, AdminContext: context} }