H1K0 c9b7f0701b feat(frontend): keyboard control for the add-to-pool dialog
The file viewer could only open the pool picker via its top-right button —
there was no `p` shortcut there (only the grid had one), so pressing `p`
on the view page did nothing. Add `p` to open the picker from the viewer,
and give the picker itself full keyboard control: `/` focuses the search
box, arrows move a highlight through the pool list, Enter adds to the
highlighted pool, and Escape clears the search first, then closes.

Both the viewer and the grid now yield the keyboard entirely to the open
picker (the picker owns Escape via its own window handler) so the
clear-then-close behaviour isn't pre-empted by the host's own Escape.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:15:12 +03:00

Tanabata File Manager

A multi-user, tag-based web file manager for images and video. Go + Gin backend (Clean Architecture, pgx, goose migrations), SvelteKit SPA frontend, PostgreSQL, JWT auth — shipped as a single Docker image that serves both the API and the built SPA on one port.

Documentation

Quick start

cp .env.example .env        # then edit the secrets (JWT_SECRET, ADMIN_PASSWORD, …)
docker compose up -d --build

By default this runs the app plus a bundled PostgreSQL container (COMPOSE_PROFILES=with-db). To point at a Postgres already on the host, set COMPOSE_PROFILES= empty and aim DATABASE_URL at host.docker.internal. See .env.example for the full matrix.

The app is published on 127.0.0.1 only and expects a reverse proxy in front (see below). The default port is 42776 — the sum of the Unicode code points of 七夕.

Reverse proxy (nginx)

The container publishes its port on loopback (127.0.0.1:${APP_PORT}:42776 in docker-compose.yml), so a reverse proxy on the host terminates TLS and forwards to it. Three settings matter for this app:

  1. client_max_body_size — uploads go up to MAX_UPLOAD_BYTES (500 MiB by default). nginx caps request bodies at 1 MiB out of the box, so without this every large upload fails with 413.
  2. Forwarded headers — the app trusts X-Forwarded-For only from the hops in TRUSTED_PROXIES (default: loopback + Docker bridge ranges) and keys its login/refresh rate limiter on the resulting client IP. If the proxy doesn't send the header, every request looks like it comes from the proxy and shares one rate-limit bucket.
  3. Streaming for big media — turning request/response buffering off lets large uploads stream straight to the app and lets video range-seeks work without nginx spooling whole files to disk first.
server {
    listen 443 ssl;
    server_name tanabata.example.com;

    # ssl_certificate / ssl_certificate_key ... (e.g. from certbot)

    # Match MAX_UPLOAD_BYTES (500 MiB default); nginx defaults to 1m → 413.
    client_max_body_size 512m;

    location / {
        proxy_pass http://127.0.0.1:42776;   # APP_PORT
        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Stream large uploads/downloads instead of buffering to disk; keeps
        # video range-seek responsive. Scope these to file/preview locations
        # instead if you'd rather keep buffering for small JSON responses.
        proxy_request_buffering off;
        proxy_buffering         off;
        proxy_read_timeout      300s;
        proxy_send_timeout      300s;
    }
}

If you run the app without a proxy and want it reachable on the LAN, drop the 127.0.0.1: prefix from the ports line in docker-compose.yml and adjust TRUSTED_PROXIES accordingly.

Development

# Backend
cd backend
go run ./cmd/server          # dev server
go test ./...                # all tests

# Frontend
cd frontend
npm run dev                  # Vite dev server
npm run build                # production build
npm run generate:types       # regenerate API types from openapi.yaml
S
Description
🎋Tanabata — web file manager with tags!
Readme 5.6 MiB
Languages
Go 49.6%
Svelte 39%
TypeScript 8.7%
PLpgSQL 1.4%
Dockerfile 0.5%
Other 0.8%