diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 45 |
1 files changed, 38 insertions, 7 deletions
@@ -19,7 +19,9 @@ Server-rendered HTML, no JavaScript required, works on old browsers and phones. - Changes are live immediately: save, then refresh the blog tab. - A **superadmin** creates bloggers, resets passwords, disables or deletes accounts. - The **root domain is itself a blog**, owned by the superadmin and managed like any other. -- Images are stored in Postgres; everything is in one database. +- **Each blog is its own Postgres database** (`blog_<name>`), images included, so one + `pg_dump` is a complete backup of a blog and one `psql` restores it. A small control + database holds the users and the list of blogs. ## Local development @@ -56,13 +58,13 @@ Go changes need a restart. | `BASE_DOMAIN` | `blogspace.localhost` | Root domain; blogs are `<name>.BASE_DOMAIN` | | `ADDR` | `:8080` | Listen address | | `PUBLIC_PORT` | — | Appended to generated blog links (dev only; unset behind a proxy on :80/:443) | -| `DATABASE_URL` | local dev DSN | Postgres connection string | +| `DATABASE_URL` | local dev DSN | Connection string of the **control** database; blog databases are created next to it by the same role | | `JWT_SECRET` | — | **Required** outside dev; long random string (`openssl rand -hex 32`) | | `SUPERADMIN_USERNAME` / `SUPERADMIN_PASSWORD` | `admin` / — | Created on first start if no superadmin exists | | `MAX_UPLOAD_MB` | `5` | Image upload limit | | `DEV` | `false` | Hot-reload templates, allow missing secrets | -Migrations run automatically at startup. +Migrations run automatically at startup, for the control database and for every blog database. ## Deployment (Docker) @@ -95,16 +97,45 @@ server { Caddy: `example.com, *.example.com { reverse_proxy 127.0.0.1:8080 }` (wildcard certificates need the DNS challenge). -Backups: dump the Postgres volume (`docker compose exec db pg_dump -U blogspace blogspace > backup.sql`). -Images live in the database, so that one dump is everything. +### Backups and restores + +Every blog lives in its own database, `blog_<name>` (dashes become underscores: +`my-blog` → `blog_my_blog`). The control database (`blogspace`) holds the users +and the blog registry. Images are in the blog database, so one dump is the whole blog. + +```sh +# one blog +docker compose exec db pg_dump -U blogspace blog_alice > alice.sql +# users and the blog list +docker compose exec db pg_dump -U blogspace blogspace > control.sql +# everything at once +docker compose exec db pg_dumpall -U blogspace > all.sql +``` + +Restoring a blog, with the app running: + +```sh +docker compose exec db dropdb -U blogspace --force blog_alice +docker compose exec db createdb -U blogspace blog_alice +docker compose exec -T db psql -U blogspace -q blog_alice < alice.sql +``` + +The registry row must exist: on a fresh install first create the user with that +subdomain in `/admin/` (which makes an empty `blog_alice`), then overwrite it as +above. A dump taken with an older version of Blogspace is upgraded at the next +start (or with `blogspace migrate`). + +Deleting a user in `/admin/` drops their blog database — take a dump first if +you may want it back. Blog pools are small (4 connections each, closed when +idle); with many blogs busy at once, raise `max_connections` on the `db` service. ## Layout ``` cmd/blogspace/ main (serve | seed | migrate) internal/config/ environment → Config -internal/db/ pgx pool + goose migrations (embedded SQL) -internal/store/ models and queries (users, blogs, pages, posts, images, sections) +internal/db/ control + per-blog pools, goose migrations (control/ and blog/) +internal/store/ Store (users, blog registry) and BlogStore (one blog's content) internal/auth/ bcrypt, JWT cookie sessions, CSRF tokens internal/markdown/ goldmark + bluemonday internal/slug/ title → slug |
