// giteaapi.go — 以 internal/gitea 的 HTTP 用戶端實作 workflow.Client。 // // 端點與查詢參數對照 gitea.py:清單一律帶 limit 分頁(ListAll 固定 50); // issues 端點帶 type=issues(排除 PR)與 state=open;pulls 帶 state=open。 package workflow import ( "context" "errors" "net/url" "strconv" "gitea.alterminal.com/alterminal/teai/internal/gitea" ) // errNoUsername 表示 /user 回應中找不到帳號名(伺服端回應異常)。 var errNoUsername = errors.New("gitea: GET /user: response has no username") // APIClient 是 Client 介面的 HTTP 實作,包裝 internal/gitea.Client。 type APIClient struct { Client *gitea.Client } // 確認 APIClient 實作 Client 介面(編譯期檢查)。 var _ Client = (*APIClient)(nil) func (a *APIClient) CurrentUser() (string, error) { var u User if err := a.Client.GetJSON(context.Background(), "/user", &u); err != nil { return "", err } name := u.Name() if name == "" { return "", errNoUsername } return name, nil } func (a *APIClient) ListOrgs() ([]string, error) { var orgs []Org if err := a.Client.ListAll(context.Background(), "/user/orgs", gitea.ListOptions{}, &orgs); err != nil { return nil, err } names := make([]string, 0, len(orgs)) for _, o := range orgs { if n := o.Account(); n != "" { names = append(names, n) } } return names, nil } func (a *APIClient) ListTeams(org string) ([]Team, error) { var teams []Team path := "/orgs/" + url.PathEscape(org) + "/teams" if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &teams); err != nil { return nil, err } return teams, nil } func (a *APIClient) ListTeamMembers(teamID int64) ([]string, error) { var users []User path := "/teams/" + strconv.FormatInt(teamID, 10) + "/members" if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &users); err != nil { return nil, err } names := make([]string, 0, len(users)) for i := range users { if n := users[i].Name(); n != "" { names = append(names, n) } } return names, nil } func (a *APIClient) ListOrgRepos(org string) ([]Repo, error) { var repos []Repo path := "/orgs/" + url.PathEscape(org) + "/repos" if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &repos); err != nil { return nil, err } return repos, nil } func (a *APIClient) ListIssues(owner, repo string) ([]Issue, error) { var issues []Issue path := "/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(repo) + "/issues" opts := gitea.ListOptions{Extra: url.Values{ "type": {"issues"}, "state": {"open"}, }} if err := a.Client.ListAll(context.Background(), path, opts, &issues); err != nil { return nil, err } // /issues 也會回傳 PR(帶 pull_request 欄位);一律排除。 out := make([]Issue, 0, len(issues)) for i := range issues { if issues[i].PullRequest == nil { out = append(out, issues[i]) } } return out, nil } func (a *APIClient) ListPulls(owner, repo string) ([]PullRequest, error) { var pulls []PullRequest path := "/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(repo) + "/pulls" opts := gitea.ListOptions{Extra: url.Values{"state": {"open"}}} if err := a.Client.ListAll(context.Background(), path, opts, &pulls); err != nil { return nil, err } return pulls, nil } func (a *APIClient) ListComments(owner, repo string, number int64) ([]Comment, error) { var comments []Comment path := "/repos/" + url.PathEscape(owner) + "/" + url.PathEscape(repo) + "/issues/" + strconv.FormatInt(number, 10) + "/comments" if err := a.Client.ListAll(context.Background(), path, gitea.ListOptions{}, &comments); err != nil { return nil, err } return comments, nil }