diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index 300a262..81fc7db 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -7,7 +7,9 @@ on: workflow_dispatch: {} env: - IMAGE: git.ivanch.me/ivanch/transmission-manager + APP: transmission-manager + IMAGE: git.ivanch.me/ivanch/${{ APP }} + NAMESPACE: media jobs: build_transmission_manager: @@ -33,5 +35,5 @@ jobs: uses: https://git.ivanch.me/ivanch/pipeline-actions/deploy-restart@main with: kube_config: ${{ secrets.KUBE_CONFIG }} - deployment_name: transmission-manager - namespace: media + deployment_name: ${{ env.APP }} + namespace: ${{ env.NAMESPACE }} diff --git a/AGENTS.md b/AGENTS.md index 8e99fd0..1ff2b46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,29 +6,6 @@ Always read `project-context.md` before making code or documentation changes. If ## Quick Summary Transmission Manager is a lightweight single-binary Go web app for managing and viewing torrents in a Transmission RPC client. It serves a dark-mode vanilla HTML/CSS/JS SPA and proxies Transmission RPC calls through a minimal Go backend. -## Stack -- Backend: Go 1.24, standard library only. -- Frontend: Vanilla HTML, CSS, and JavaScript. No framework or build step. -- Static assets: embedded with Go `embed` from `web/`. -- Runtime port: `8080`. -- Container: multi-stage Docker build from `golang:1.24-alpine` to `alpine:latest`. - -## Current Files -- `project-context.md` - canonical project context and implementation rules for agents. -- `main.go` - HTTP server, embedded static file serving, environment-driven Transmission RPC client, connection warning page, API routes. -- `web/index.html` - SPA shell and toolbar controls. -- `web/app.js` - frontend state, polling, in-place rendering, sorting, pause/resume actions. -- `web/styles.css` - dark-only UI theme and responsive layout. -- `go.mod` - module `transmission-manager`, Go 1.24. -- `Dockerfile` - production container build. - -## Core API Surface -- `GET /` serves a Transmission connection warning page when setup fails; otherwise it serves the embedded frontend. -- `GET /api/torrents` returns Transmission torrent data. -- `GET /api/stats` returns global session stats. -- `POST /api/torrents/{id}/pause` maps to Transmission `torrent-stop`. -- `POST /api/torrents/{id}/resume` maps to queue-respecting Transmission `torrent-start`. - ## Implementation Guidelines - Keep the backend stdlib-only. Do not add Gin, Fiber, Chi, Gorilla, or other Go web frameworks. - Keep the frontend dependency-free. Do not add React, Vue, Svelte, bundlers, package managers, or transpilers. @@ -39,8 +16,7 @@ Transmission Manager is a lightweight single-binary Go web app for managing and - Keep UI dark-only, modern, compact, responsive, and based on CSS variables. ## Transmission RPC Notes -- Default RPC URL: `http://192.168.20.22:3210/transmission/rpc`. -- Override the RPC URL with `TRANSMISSION_URL`. +- RPC URL read from `TRANSMISSION_URL` (must be set). - Optional HTTP Basic Auth credentials come from `TRANSMISSION_RPC_USERNAME` and `TRANSMISSION_RPC_PASSWORD`; unset, empty, or literal `null` values mean no credential value. - Handle the Transmission session-ID handshake: 1. POST to the RPC URL. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1310341 --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +# Transmission Manager + +A lightweight, modern, single-binary web application for viewing and managing torrents from a [Transmission](https://transmissionbt.com/) RPC daemon. + +Transmission Manager serves a fast, clean, dark-mode Single Page Application (SPA) and proxies Transmission RPC calls through a minimal Go backend—all contained within a single executable. + +--- + +## Features + +- **Single Static Binary**: All frontend assets (`index.html`, `app.js`, `styles.css`) are embedded directly into the Go binary using `embed`. Zero runtime asset dependencies. +- **Modern Dark-Mode UI**: Built with vanilla HTML5, CSS3 (using CSS variables), and JavaScript. Responsive, compact layout designed for both desktop and mobile screens. +- **Live Polling & In-Place Updates**: Keyed DOM reconciliation updates existing torrent cards in place without screen flickering or scroll jumping. Selectable refresh interval (1s, 3s, 5s, 10s). +- **Rich Sorting & Filtering**: + - Filter torrents instantly by name. + - Sort by **Status**, **Name**, **Progress**, **Ratio**, **Size**, **Speed**, or **Availability**. +- **Interactive Controls**: Pause or resume individual torrents directly from the web interface. +- **Robust Transmission RPC Support**: + - Automatically handles Transmission `409 Conflict` session ID (`X-Transmission-Session-Id`) handshakes. + - Supports optional HTTP Basic Authentication. + +--- + +## Configuration + +Transmission Manager is configured entirely via environment variables: + +| Variable | Required | Description | +| :--- | :---: | :--- | +| `TRANSMISSION_URL` | **Yes** | Absolute HTTP or HTTPS RPC endpoint URL of your Transmission instance. | +| `TRANSMISSION_RPC_USERNAME` | No | Basic Authentication username for Transmission RPC. | +| `TRANSMISSION_RPC_PASSWORD` | No | Basic Authentication password for Transmission RPC. | + +--- + +## Quick Start + +### Using Docker + +Run the container directly: + +```bash +docker run -d \ + --name transmission-manager \ + -p 8080:8080 \ + -e TRANSMISSION_URL=http://192.168.1.100:9091/transmission/rpc \ + -e TRANSMISSION_RPC_USERNAME=admin \ + -e TRANSMISSION_RPC_PASSWORD=secret \ + transmission-manager:latest +``` + +### Using Docker Compose + +Create a `docker-compose.yml`: + +```yaml +services: + transmission-manager: + build: . + container_name: transmission-manager + ports: + - "8080:8080" + environment: + TRANSMISSION_URL: http://192.168.1.100:9091/transmission/rpc + TRANSMISSION_RPC_USERNAME: admin + TRANSMISSION_RPC_PASSWORD: secret + restart: unless-stopped +``` + +Then start the application: + +```bash +docker compose up -d +``` + +Open your browser and navigate to `http://localhost:8080`. + +--- + +## Building from Source + +### Prerequisites + +- [Go](https://go.dev/) 1.24 or later + +### Build + +Compile the single binary with embedded frontend assets: + +```bash +git clone https://github.com/your-repo/transmission-manager.git +cd transmission-manager +go build -ldflags="-s -w" -o transmission-manager . +``` + +### Run + +```bash +export TRANSMISSION_URL="http://localhost:9091/transmission/rpc" +./transmission-manager +``` + +By default, the server listens on port `8080`. diff --git a/main.go b/main.go index 6f7dc0e..c541b51 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,6 @@ import ( var webFS embed.FS const ( - defaultRPCURL = "http://192.168.20.22:3210/transmission/rpc" envTransmissionURL = "TRANSMISSION_URL" envTransmissionRPCUsername = "TRANSMISSION_RPC_USERNAME" envTransmissionRPCPassword = "TRANSMISSION_RPC_PASSWORD" @@ -62,11 +61,7 @@ func NewClientFromEnv() (*Client, error) { } func configuredRPCURL() string { - rpcURL := strings.TrimSpace(os.Getenv(envTransmissionURL)) - if rpcURL == "" { - return defaultRPCURL - } - return rpcURL + return strings.TrimSpace(os.Getenv(envTransmissionURL)) } func optionalEnv(name string) string { @@ -78,6 +73,9 @@ func optionalEnv(name string) string { } func validateRPCURL(raw string) error { + if raw == "" { + return fmt.Errorf("%s environment variable must be set", envTransmissionURL) + } parsed, err := url.Parse(raw) if err != nil { return fmt.Errorf("%s is invalid: %w", envTransmissionURL, err) diff --git a/project-context.md b/project-context.md index 3aa37f1..da8595c 100644 --- a/project-context.md +++ b/project-context.md @@ -17,7 +17,6 @@ The app is intentionally small: one Go binary serves both the API and embedded f - Static assets: embedded with Go `embed` - Runtime port: `8080` - Docker build: `golang:1.24-alpine` builder, `alpine:latest` runtime -- Default Transmission RPC target: `http://192.168.20.22:3210/transmission/rpc` - Runtime RPC configuration: `TRANSMISSION_URL`, `TRANSMISSION_RPC_USERNAME`, and `TRANSMISSION_RPC_PASSWORD` ## Repository Organization @@ -28,6 +27,7 @@ The app is intentionally small: one Go binary serves both the API and embedded f - `web/styles.css`: all styling. Owns dark theme variables, layout, cards, badges, progress bars, action buttons, empty/error states, and responsive rules. - `go.mod`: Go module declaration. - `Dockerfile`: multi-stage production build. +- `README.md`: public project documentation, configuration guide, and deployment instructions. - `AGENTS.md`: quick-start guide for coding agents. It must point agents back to this file. ## Backend Architecture @@ -46,7 +46,7 @@ API errors return JSON with an `error` string. Invalid torrent action methods sh ## Transmission RPC Model -The RPC endpoint defaults to `http://192.168.20.22:3210/transmission/rpc` and can be overridden with `TRANSMISSION_URL`. `TRANSMISSION_RPC_USERNAME` and `TRANSMISSION_RPC_PASSWORD` are optional; unset, empty, or literal `null` values mean no credential value. When either credential has a value, the backend sends HTTP Basic Auth on RPC requests. +The RPC endpoint is configured via `TRANSMISSION_URL` (which must be set). `TRANSMISSION_RPC_USERNAME` and `TRANSMISSION_RPC_PASSWORD` are optional; unset, empty, or literal `null` values mean no credential value. When either credential has a value, the backend sends HTTP Basic Auth on RPC requests. Transmission usually rejects the first request or an expired session with HTTP `409` and a fresh `X-Transmission-Session-Id` header. Correct behavior is to drain and close the response body, cache the new session ID, and retry the same JSON request once. @@ -122,5 +122,3 @@ node --check web\app.js git diff --check docker build . ``` - -Known local environment note from July 8, 2026: `go` and `gofmt` were not available in PATH, and Docker was configured to a remote builder with an SSH host-key mismatch. If that remains true, report the limitation instead of changing SSH trust settings automatically.