package service import ( "context" "strings" "kra/internal/biz" "kra/internal/service/dto" ) type APIService struct { uc *biz.APIUsecase settings biz.RuntimeSettings } func NewAPIService(uc *biz.APIUsecase, settings biz.RuntimeSettings) *APIService { return &APIService{uc: uc, settings: settings} } func (s *APIService) NormalizeRoutePath(path string) string { if s.settings == nil { return path } return NormalizeRoutePath(path, s.settings.RouterPrefix()) } func (s *APIService) apiDomain(value *dto.APIRequest) *biz.API { return &biz.API{ID: value.ID, Path: s.NormalizeRoutePath(value.Path), Description: value.Description, APIGroup: value.APIGroup, Method: value.Method} } func apiResponse(value *biz.API) *dto.APIResponse { return &dto.APIResponse{ID: value.ID, CreatedAt: value.CreatedAt, UpdatedAt: value.UpdatedAt, DeletedAt: nil, Path: value.Path, Description: value.Description, APIGroup: value.APIGroup, Method: value.Method} } func apiResponses(items []*biz.API) []*dto.APIResponse { out := make([]*dto.APIResponse, 0, len(items)) for _, item := range items { out = append(out, apiResponse(item)) } return out } func (s *APIService) ListAPI(ctx context.Context, req *dto.APIListRequest) ([]*dto.APIResponse, int64, error) { items, total, err := s.uc.ListAPIs(ctx, req.Page, req.PageSize, &biz.API{Path: req.Path, Description: req.Description, APIGroup: req.APIGroup, Method: req.Method, OrderKey: req.OrderKey, Desc: req.Desc}) if err != nil { return nil, 0, err } return apiResponses(items), total, nil } func (s *APIService) AllAPI(ctx context.Context, strict bool) ([]*dto.APIResponse, error) { items, _, err := s.uc.ListAPIs(ctx, 0, 0, &biz.API{StrictAll: strict}) if err != nil { return nil, err } return apiResponses(items), nil } func (s *APIService) Groups(ctx context.Context) ([]string, map[string]string, error) { // The compatible group endpoint uses an unqualified Find, whose natural order is // the primary-key ascending order. GetAllApis intentionally uses id DESC, // so do not reuse it here or the group/dropdown order changes. items, _, err := s.uc.ListAPIs(ctx, 0, 0, &biz.API{OrderKey: "id"}) if err != nil { return nil, nil, err } seen := make(map[string]bool) groups := make([]string, 0) groupAPIMap := make(map[string]string) for _, item := range items { if !seen[item.APIGroup] { seen[item.APIGroup] = true groups = append(groups, item.APIGroup) } parts := strings.Split(item.Path, "/") if len(parts) > 1 { groupAPIMap[parts[1]] = item.APIGroup } } return groups, groupAPIMap, nil } func (s *APIService) CreateAPIRequest(ctx context.Context, req *dto.APIRequest) (*dto.APIResponse, error) { value := s.apiDomain(req) if err := s.uc.CreateAPI(ctx, value); err != nil { return nil, err } return apiResponse(value), nil } func (s *APIService) UpdateAPIRequest(ctx context.Context, req *dto.APIRequest) error { return s.uc.UpdateAPI(ctx, s.apiDomain(req)) } func (s *APIService) FindAPIResponse(ctx context.Context, id uint) (*dto.APIResponse, error) { value, err := s.uc.FindAPI(ctx, id) if err != nil { return nil, err } return apiResponse(value), nil } func (s *APIService) ApplyAPISyncRequest(ctx context.Context, req *dto.ApplyAPISyncRequest) error { added := make([]*biz.API, 0, len(req.NewAPIs)) deleted := make([]*biz.API, 0, len(req.DeleteAPIs)) for i := range req.NewAPIs { added = append(added, s.apiDomain(&req.NewAPIs[i])) } for i := range req.DeleteAPIs { deleted = append(deleted, s.apiDomain(&req.DeleteAPIs[i])) } return s.uc.ApplyAPISync(ctx, added, deleted) } func (s *APIService) SetPolicyPathsRequest(ctx context.Context, req *dto.SetPolicyPathsRequest) error { paths := make([]*biz.API, 0, len(req.Infos)) for _, value := range req.Infos { paths = append(paths, &biz.API{Path: value.Path, Method: value.Method}) } return s.uc.SetPolicyPaths(ctx, req.AuthorityID, paths) } func (s *APIService) PolicyPathResponses(ctx context.Context, authorityID uint) ([]dto.PolicyPathRequest, error) { items, err := s.uc.PolicyPaths(ctx, authorityID) if err != nil { return nil, err } var out []dto.PolicyPathRequest if len(items) > 0 { out = make([]dto.PolicyPathRequest, 0, len(items)) } for _, item := range items { out = append(out, dto.PolicyPathRequest{Path: item.Path, Method: item.Method}) } return out, nil } func (s *APIService) SyncAPIResponses(ctx context.Context, routes []dto.APIRequest) (*dto.APISyncResponse, error) { items := make([]*biz.API, 0, len(routes)) for i := range routes { routes[i].Path = s.NormalizeRoutePath(routes[i].Path) // The compatible sync endpoint compares Gin's route table directly, so newly // discovered routes carry only path and method. Group/description are // intentionally left empty for the operator to fill in the sync dialog. items = append(items, s.apiDomain(&routes[i])) } return s.SyncAPIs(ctx, items) } func (s *APIService) DeleteAPIs(ctx context.Context, ids []uint) error { return s.uc.DeleteAPIs(ctx, ids) } func (s *APIService) DeleteAPI(ctx context.Context, id uint) error { return s.uc.DeleteAPI(ctx, id) } func (s *APIService) APIRoleIDs(ctx context.Context, path, method string) ([]uint, error) { return s.uc.APIRoleIDs(ctx, s.NormalizeRoutePath(path), method) } func (s *APIService) SetAPIRoles(ctx context.Context, path, method string, ids []uint) error { return s.uc.SetAPIRoles(ctx, s.NormalizeRoutePath(path), method, ids) } func (s *APIService) FreshCasbin(ctx context.Context) error { return s.uc.FreshCasbin(ctx) } func (s *APIService) SyncAPIs(ctx context.Context, routes []*biz.API) (*dto.APISyncResponse, error) { diff, err := s.uc.SyncAPIs(ctx, routes) if err != nil { return nil, err } return &dto.APISyncResponse{NewAPIs: apiResponses(diff.Added), DeleteAPIs: apiResponses(diff.Deleted), IgnoreAPIs: apiResponses(diff.Ignored)}, nil } func (s *APIService) SetAPIIgnored(ctx context.Context, path, method string, ignored bool) error { return s.uc.SetAPIIgnored(ctx, s.NormalizeRoutePath(path), method, ignored) }