feat(gitea): surface mathias's own open Gitea issues on stage 03 (#4)
CD / Detect unsubstituted template (push) Successful in 1s
CD / Lint / Test / Vet (push) Successful in 5s
CD / var-go/oath (push) Has been skipped
CD / Build & Import (push) Successful in 12s
CD / Deploy via GitOps (push) Has been skipped

TDD: IssueNodes parses /repos/issues/search into stage nodes (title, repo
tag, clickable html_url). gitea.MyIssues() reads GITEA_TOKEN and skips
gracefully when unset, mirroring the existing liveOverlay fallback pattern.

Single-operator homelab, not per-visitor OAuth: a static read-only PAT
gates on "cleared Authentik forward-auth", not per-user token exchange —
see #4 discussion. GITEA_TOKEN provisioning in infra (ExternalSecret) is
a separate follow-up; without it the overlay is inert (no crash, just no
live nodes), so this ships safely ahead of that wiring.

Nodes with a url now render as clickable <a class="node"> instead of
<div class="node">.
This commit is contained in:
2026-07-20 23:04:44 +02:00
parent 68ae01cb75
commit b2761a4747
7 changed files with 126 additions and 8 deletions
+32
View File
@@ -0,0 +1,32 @@
package atlas
import (
"encoding/json"
"fmt"
)
// IssueNodes parses a Gitea `/repos/issues/search` response (the
// authenticated user's own open issues, newest first) into one node per
// issue, linking out to the issue.
func IssueNodes(searchJSON []byte) ([]Node, error) {
var issues []struct {
Number int `json:"number"`
Title string `json:"title"`
HTMLURL string `json:"html_url"`
Repository struct {
FullName string `json:"full_name"`
} `json:"repository"`
}
if err := json.Unmarshal(searchJSON, &issues); err != nil {
return nil, fmt.Errorf("parse issues: %w", err)
}
nodes := make([]Node, 0, len(issues))
for _, i := range issues {
nodes = append(nodes, Node{
Title: fmt.Sprintf("#%d %s", i.Number, i.Title),
Tags: []string{"live · Gitea", i.Repository.FullName},
URL: i.HTMLURL,
})
}
return nodes, nil
}