gitea: the app — repos, code, issues, pulls, notifications

Replicates what Gitea's own web UI offers, on top of the sidecar's /_api pass-through.

Repository browsing (tree, file view with the FileViewer's shiki renderer, README), commits,
branches, tags, releases, issues and pull requests both per-repo and cross-repo, notifications,
explore/search and organizations. Routes are /gitea/:section plus /gitea/repo/:owner/:name/:tab/:item,
all real Links with the URL as the source of truth — no selection channel.

Markdown is rendered client-side (react-markdown + remark-gfm + rehype-sanitize, rehype-raw
deliberately absent) rather than through the instance's /api/v1/markdown, because consuming that
means dangerouslySetInnerHTML and there is no DOMPurify in the tree with installs frozen. The cost
is Gitea's #123 and @mention cross-references; relative links and images are resolved instead.
The /markdown and /markup allow-list entries stay, so that door is open when a sanitiser lands.

retargetUrls rebases instance-minted URLs onto a browser-reachable origin, IN ONE DIRECTION ONLY.
This instance answers with two: /user and /repos build from its configured ROOT_URL
(http://localhost:9004), /contents from the public host. An unconditional rewrite onto the
connection URL therefore broke the second set to match the first, turning working https links into
dead loopback ones. Only a private/loopback URL is rewritten now, and only when the target is
itself public; when the connection URL is a dial address nothing is touched and the connection
screen says why avatars will not load.

Also carries the frontend half of the one-instance-many-tokens model: the connection form draws a
URL field only for the owner and sends no url key at all for anyone else, ServiceConnection.url is
string | null to match officerdb, and the rebase origin comes from the resolved instanceUrl rather
than connection.url, which is null for a member.

Not verified: no runtime pass since the last four changes, the issues and pull views have never
rendered a row (the instance has none), and the member path has never executed (one account).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 17:10:36 +00:00
co-authored by Claude Opus 5
parent 4892441ee2
commit 10ff23c5dd
33 changed files with 3113 additions and 3 deletions
+3 -2
View File
@@ -27,8 +27,9 @@ Long-running and privileged work lives in **sidecars**: separate processes that
(`ecosystem.config.cjs`): `officer` (the server), `officer-anthropic-proxy`, `officer-agent`,
`officer-opencode`, `officer-email`, `officer-pty`, `officer-vnc`, `officer-music`, `officer-vault`,
`officer-slskd`, `officer-headscale`, `officer-transmission`, `officer-invoiceshelf`, `officer-wallet`,
`officer-photos`, `officer-notify`, `officer-caldav`, `officer-memos`, `officer-jellyfin` — nineteen as of
2026-08-04, and a list that goes stale every time a sidecar lands. `pm2 jlist` is the source of truth.
`officer-photos`, `officer-notify`, `officer-caldav`, `officer-memos`, `officer-jellyfin`, `officer-gitea`
— twenty as of 2026-08-06, and a list that goes stale every time a sidecar lands. `pm2 jlist` is the
source of truth.
**`officer-anthropic-proxy` and `officer-agent` are not the same thing.** The proxy holds the Anthropic
credential and forwards API traffic; the agent is the process that spawns `claude`. They were one entry