Release 2026.08.02-1
SQLite replaces Postgres, and two fixes from using the thing. - The database is a file under ./storage instead of a second container. Ten members never needed a database server, and the driver is pure Go, so the build stays CGO_ENABLED=0 and the dependency count is unchanged. One bind mount is now the whole backup: no pgdata, no healthcheck-gated depends_on, no startup retry loop. Timestamps are UTC text, idle_ttl is seconds, the divisive/unified boards carry their own stddev, and foreign keys are on by pragma. Tests get a database file each and run without any setup - Invites are copied, not clicked. An invite is something to send, and the anchor opened the join form in the admin's own browser - Feedback asks for more than faults: the footer reads "Ongelmia? Ideoita? Palautetta?" and the page behind it invites ideas rather than only bugs - Kuuntele YouTubessa opens in a new tab, so a half-typed review survives it
This commit is contained in:
@@ -45,10 +45,12 @@ Members have an address because it is their login and because mail is a planned
|
||||
|
||||
## Stack
|
||||
|
||||
Go, Postgres, `html/template`, HTMX + Alpine. Audio is converted with ffmpeg and downloaded with
|
||||
yt-dlp. One binary, one origin — there is no separate frontend to deploy.
|
||||
Go, SQLite, `html/template`, HTMX + Alpine. Audio is converted with ffmpeg and downloaded with
|
||||
yt-dlp. One binary, one origin, one container — there is no separate frontend and no database server
|
||||
to deploy.
|
||||
|
||||
Go dependencies: `pgx/v5` and `golang.org/x/crypto`. No Node, no npm, no bundler.
|
||||
Go dependencies: `modernc.org/sqlite` and `golang.org/x/crypto`. The SQLite driver is pure Go, so the
|
||||
build stays `CGO_ENABLED=0`. No Node, no npm, no bundler.
|
||||
|
||||
## Running it
|
||||
|
||||
@@ -64,8 +66,7 @@ creates no users: log into the admin panel and mint an invite.
|
||||
|
||||
| Variable | Default | Notes |
|
||||
|---|---|---|
|
||||
| `POSTGRES_PASSWORD` | — | **Required by Compose.** Used to build `DATABASE_URL` for the app |
|
||||
| `DATABASE_URL` | — | `postgres://user:pass@postgres:5432/levyraati` |
|
||||
| `DB_PATH` | `$STORAGE_DIR/levyraati.db` | The SQLite file. Created on first start |
|
||||
| `ADMIN_USER` | `admin` | Admin panel username |
|
||||
| `ADMIN_PASSWORD` | — | **Required.** No default; the app refuses to start without it |
|
||||
| `ADDR` | `:8080` | Member-facing listener |
|
||||
@@ -77,16 +78,14 @@ creates no users: log into the admin panel and mint an invite.
|
||||
### Local development
|
||||
|
||||
```sh
|
||||
docker compose up -d postgres
|
||||
export DATABASE_URL="postgres://levyraati:$POSTGRES_PASSWORD@localhost:5432/levyraati"
|
||||
export ADMIN_PASSWORD=dev SECURE_COOKIES=false
|
||||
go run .
|
||||
```
|
||||
|
||||
Requires Go 1.24+, plus `ffmpeg`, `ffprobe`, and `yt-dlp` on `PATH`.
|
||||
Requires Go 1.25+, plus `ffmpeg`, `ffprobe`, and `yt-dlp` on `PATH`. There is nothing to start first:
|
||||
the database is a file under `./storage`, created on the first run.
|
||||
|
||||
Tests that need a database are skipped unless `TEST_DATABASE_URL` points at a throwaway one — the
|
||||
migration test drops and recreates the `public` schema, so never point it at anything you care about.
|
||||
Tests get a fresh database file in a temp directory each, so they need no setup and touch nothing:
|
||||
|
||||
```sh
|
||||
go test ./...
|
||||
@@ -136,23 +135,22 @@ JSON to stdout, nothing else. There is no log table and no log viewer in the app
|
||||
|
||||
### Backups
|
||||
|
||||
Two paths hold everything:
|
||||
`./storage` holds everything: audio files, avatars, and `levyraati.db`. It is a bind mount, so a copy
|
||||
of that one directory is the whole backup. `storage/tmp/` is in-flight conversions and is safe to
|
||||
skip; it's cleared on startup anyway.
|
||||
|
||||
- `./pgdata` — the database. Postgres 18 stores it under a version subdirectory (`18/docker`), so
|
||||
the mount is `/var/lib/postgresql`, not `/var/lib/postgresql/data`
|
||||
- `./storage` — audio files and avatars
|
||||
|
||||
Both are bind mounts. `storage/tmp/` is in-flight conversions and is safe to skip; it's cleared on
|
||||
startup anyway.
|
||||
Copying the file while the app is running is not a backup — WAL means the latest writes live in a
|
||||
sidecar file. Ask SQLite for a consistent snapshot instead:
|
||||
|
||||
```sh
|
||||
docker compose exec postgres pg_dump -U levyraati levyraati | gzip > backup-$(date +%F).sql.gz
|
||||
docker compose exec app sqlite3 /storage/levyraati.db ".backup '/storage/tmp/backup.db'"
|
||||
gzip -c storage/tmp/backup.db > backup-$(date +%F).db.gz && rm storage/tmp/backup.db
|
||||
```
|
||||
|
||||
### Database shell
|
||||
|
||||
```sh
|
||||
docker compose exec postgres psql -U levyraati levyraati
|
||||
docker compose exec app sqlite3 /storage/levyraati.db
|
||||
```
|
||||
|
||||
## Layout
|
||||
|
||||
Reference in New Issue
Block a user