Add isTokenError() helper that detects HTTP 401/403 responses from the Gitea API, and redirectOnTokenError() that redirects to /settings with an error=token_expired query parameter. Update Dashboard, ListIssues, and ListPulls handlers to check for token errors. The settings page now displays an error banner explaining the token needs to be refreshed. Closes leeworks-agents/gitea-mobile#192 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gitea Mobile
A mobile-first Progressive Web App (PWA) for managing Gitea issues and pull requests across multiple repositories and organizations from an iPhone. Built with Go, HTMX, and hand-rolled CSS -- no JavaScript frameworks, no build step, no node_modules.
Tech Stack
| Layer | Choice |
|---|---|
| Backend | Go + Gitea SDK (code.gitea.io/sdk/gitea) |
| Frontend | HTMX + Go html/template + hand-rolled CSS |
| Container | Multi-stage Dockerfile -> distroless (~15MB) |
| Deployment | Kustomize manifests + FluxCD GitOps |
Project Structure
/
├── cmd/server/main.go # entrypoint
├── internal/
│ ├── config/config.go # env-based configuration
│ ├── gitea/client.go # Gitea SDK wrapper / aggregation layer
│ ├── handlers/ # HTTP handlers (issues, PRs, triage, settings)
│ ├── auth/ # cookie-based token auth
│ ├── middleware/ # auth middleware, logging
│ └── templates/ # Go html/template files (for HTMX)
├── static/ # CSS, JS (htmx.min.js), icons, manifest
├── .gitea/workflows/build.yaml # CI pipeline (Gitea Actions)
├── Dockerfile
├── flake.nix # Nix dev shell with Go + air
└── go.mod
Local Development
Prerequisites
- Nix with flakes enabled, or Go 1.22+
- A Gitea instance with an API token
Quick Start
# Enter the Nix dev shell (provides Go, gopls, air)
nix develop
# Set required environment variables
export GITEA_URL=https://gitea.leeworks.dev
export SESSION_SECRET=$(openssl rand -hex 32)
# Optional: set a default API token
export GITEA_TOKEN=your-gitea-api-token
# Start the server with live reload
air
If you are not using Nix, install Go 1.22+ and air manually, then run the same commands above starting from the export lines.
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
GITEA_URL |
Yes | -- | Base URL of the Gitea instance |
SESSION_SECRET |
Yes | -- | HMAC key for signing session cookies (min 32 chars) |
GITEA_TOKEN |
No | -- | Default API token (users can set their own via the settings page) |
LISTEN_ADDR |
No | :8080 |
Server listen address |
Live Reload with Air
The dev shell includes air for automatic recompilation on file changes. Configuration is in .air.toml. Air watches .go and .html files under cmd/, internal/, and static/ and rebuilds/restarts the server automatically.
Running Tests
# Run all tests
go test ./...
# Run tests with race detection
go test -race ./...
Building the Container
# Build the Docker image
docker build -t gitea-mobile .
# Run locally
docker run -p 8080:8080 \
-e GITEA_URL=https://gitea.leeworks.dev \
-e SESSION_SECRET=$(openssl rand -hex 32) \
gitea-mobile
The Dockerfile uses a multi-stage build: Go binary compiled in an Alpine builder stage, then copied into a distroless image (~15MB final size).
Deployment
Kubernetes manifests for this app live in the Talos cluster repo under testing1/first-cluster/apps/gitea-mobile/. FluxCD syncs from that repo and handles automated image updates via ImagePolicy annotations.
Key deployment resources:
deployment.yaml-- Pod spec with health checksservice.yaml-- ClusterIP service on port 8080ingressroute.yaml-- Traefik IngressRoute forgitea-mobile.testing.leeworks.devkustomization.yaml-- Kustomize overlay
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes and add tests
- Run
go test -race ./...to verify - Commit with a clear message referencing the issue number
- Push to your fork and open a pull request
All PRs target the fork (leeworks-agents/gitea-mobile), not the upstream repo.