// Package gitea reads the repo's own Gitea Actions run history from the // in-cluster Gitea service (public read — no token), so the atlas can show the // pipeline's live executions. package gitea import ( "context" "fmt" "io" "net/http" "os" "time" ) // base is the in-cluster Gitea service by default; override with GITEA_BASE. func base() string { if b := os.Getenv("GITEA_BASE"); b != "" { return b } return "http://gitea-http.gitea.svc.cluster.local:3000" } // Runs returns the raw /actions/tasks JSON for mathias/cad-atlas (newest first). func Runs() ([]byte, error) { return get(base()+"/api/v1/repos/mathias/cad-atlas/actions/tasks?limit=50", "") } // MyIssues returns the raw /repos/issues/search JSON for the token owner's // own open issues across every repo they can see. Requires GITEA_TOKEN — a // read-only PAT for the mathias account (this is a single-operator homelab, // not per-visitor OAuth: anyone who clears Authentik forward-auth sees // Mathias's own data). Returns an error if GITEA_TOKEN is unset, so callers // can skip the overlay gracefully. func MyIssues() ([]byte, error) { token := os.Getenv("GITEA_TOKEN") if token == "" { return nil, fmt.Errorf("GITEA_TOKEN not set") } url := base() + "/api/v1/repos/issues/search?state=open&created=true&type=issues&limit=8" return get(url, token) } // get performs a short-lived GET, optionally with a bearer token, and returns // the response body. func get(url, token string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } if token != "" { req.Header.Set("Authorization", "token "+token) } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20)) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("gitea: %s", resp.Status) } return body, nil }