fixing small things
This commit is contained in:
@@ -7,7 +7,9 @@ on:
|
|||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: git.ivanch.me/ivanch/transmission-manager
|
APP: transmission-manager
|
||||||
|
IMAGE: git.ivanch.me/ivanch/${{ APP }}
|
||||||
|
NAMESPACE: media
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_transmission_manager:
|
build_transmission_manager:
|
||||||
@@ -33,5 +35,5 @@ jobs:
|
|||||||
uses: https://git.ivanch.me/ivanch/pipeline-actions/deploy-restart@main
|
uses: https://git.ivanch.me/ivanch/pipeline-actions/deploy-restart@main
|
||||||
with:
|
with:
|
||||||
kube_config: ${{ secrets.KUBE_CONFIG }}
|
kube_config: ${{ secrets.KUBE_CONFIG }}
|
||||||
deployment_name: transmission-manager
|
deployment_name: ${{ env.APP }}
|
||||||
namespace: media
|
namespace: ${{ env.NAMESPACE }}
|
||||||
|
|||||||
26
AGENTS.md
26
AGENTS.md
@@ -6,29 +6,6 @@ Always read `project-context.md` before making code or documentation changes. If
|
|||||||
## Quick Summary
|
## 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.
|
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
|
## Implementation Guidelines
|
||||||
- Keep the backend stdlib-only. Do not add Gin, Fiber, Chi, Gorilla, or other Go web frameworks.
|
- 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.
|
- 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.
|
- Keep UI dark-only, modern, compact, responsive, and based on CSS variables.
|
||||||
|
|
||||||
## Transmission RPC Notes
|
## Transmission RPC Notes
|
||||||
- Default RPC URL: `http://192.168.20.22:3210/transmission/rpc`.
|
- RPC URL read from `TRANSMISSION_URL` (must be set).
|
||||||
- Override the RPC URL with `TRANSMISSION_URL`.
|
|
||||||
- Optional HTTP Basic Auth credentials come from `TRANSMISSION_RPC_USERNAME` and `TRANSMISSION_RPC_PASSWORD`; unset, empty, or literal `null` values mean no credential value.
|
- 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:
|
- Handle the Transmission session-ID handshake:
|
||||||
1. POST to the RPC URL.
|
1. POST to the RPC URL.
|
||||||
|
|||||||
103
README.md
Normal file
103
README.md
Normal file
@@ -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`.
|
||||||
10
main.go
10
main.go
@@ -23,7 +23,6 @@ import (
|
|||||||
var webFS embed.FS
|
var webFS embed.FS
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultRPCURL = "http://192.168.20.22:3210/transmission/rpc"
|
|
||||||
envTransmissionURL = "TRANSMISSION_URL"
|
envTransmissionURL = "TRANSMISSION_URL"
|
||||||
envTransmissionRPCUsername = "TRANSMISSION_RPC_USERNAME"
|
envTransmissionRPCUsername = "TRANSMISSION_RPC_USERNAME"
|
||||||
envTransmissionRPCPassword = "TRANSMISSION_RPC_PASSWORD"
|
envTransmissionRPCPassword = "TRANSMISSION_RPC_PASSWORD"
|
||||||
@@ -62,11 +61,7 @@ func NewClientFromEnv() (*Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configuredRPCURL() string {
|
func configuredRPCURL() string {
|
||||||
rpcURL := strings.TrimSpace(os.Getenv(envTransmissionURL))
|
return strings.TrimSpace(os.Getenv(envTransmissionURL))
|
||||||
if rpcURL == "" {
|
|
||||||
return defaultRPCURL
|
|
||||||
}
|
|
||||||
return rpcURL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func optionalEnv(name string) string {
|
func optionalEnv(name string) string {
|
||||||
@@ -78,6 +73,9 @@ func optionalEnv(name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateRPCURL(raw string) error {
|
func validateRPCURL(raw string) error {
|
||||||
|
if raw == "" {
|
||||||
|
return fmt.Errorf("%s environment variable must be set", envTransmissionURL)
|
||||||
|
}
|
||||||
parsed, err := url.Parse(raw)
|
parsed, err := url.Parse(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s is invalid: %w", envTransmissionURL, err)
|
return fmt.Errorf("%s is invalid: %w", envTransmissionURL, err)
|
||||||
|
|||||||
@@ -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`
|
- Static assets: embedded with Go `embed`
|
||||||
- Runtime port: `8080`
|
- Runtime port: `8080`
|
||||||
- Docker build: `golang:1.24-alpine` builder, `alpine:latest` runtime
|
- 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`
|
- Runtime RPC configuration: `TRANSMISSION_URL`, `TRANSMISSION_RPC_USERNAME`, and `TRANSMISSION_RPC_PASSWORD`
|
||||||
|
|
||||||
## Repository Organization
|
## 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.
|
- `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.
|
- `go.mod`: Go module declaration.
|
||||||
- `Dockerfile`: multi-stage production build.
|
- `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.
|
- `AGENTS.md`: quick-start guide for coding agents. It must point agents back to this file.
|
||||||
|
|
||||||
## Backend Architecture
|
## Backend Architecture
|
||||||
@@ -46,7 +46,7 @@ API errors return JSON with an `error` string. Invalid torrent action methods sh
|
|||||||
|
|
||||||
## Transmission RPC Model
|
## 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.
|
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
|
git diff --check
|
||||||
docker build .
|
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.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user