apps/quotes/README.md 3.0 K raw
1
# quotes
2
3
A minimal quote-a-day site. The landing page shows a single **quote of the day**
4
from classic literature — white, centered, on a dark background. `/admin` is a
5
session-protected page to add and manage quotes, matching the other andromeda
6
apps.
7
8
## Run
9
10
```sh
11
cp .env.example .env   # set QUOTES_PASSWORD
12
go run .
13
```
14
15
Landing: `http://localhost:3000/` — the quote of the day (deterministic, rotates
16
at UTC midnight).
17
Admin: `http://localhost:3000/admin` — log in with `QUOTES_PASSWORD`.
18
19
## Seeding from a CSV
20
21
`quotes.csv` is a Goodreads-style export (`quote,author,category`). The seed
22
command imports only classic-literature quotes, matched case-sensitively against
23
the author/title list in `classic_authors.txt`:
24
25
```sh
26
go run . seed quotes.csv
27
```
28
29
- Edit `classic_authors.txt` (one author or book title per line, `#` comments
30
  allowed) to widen or narrow the selection, then re-run the command.
31
- Attribution is split at the first comma: `author` and `source` (book title).
32
- Re-seeding is idempotent — quotes already present (matched on text + author)
33
  are skipped.
34
- The author list is also **embedded in the binary**, so `seed` works even when
35
  `classic_authors.txt` is not on disk (e.g. inside a container). An on-disk
36
  file always takes precedence, so local edits apply without a rebuild.
37
38
### Seeding a Docker volume
39
40
The image only ships the binary — the DB lives on the `quotes_data` volume
41
(`QUOTES_DB_PATH=/data/quotes.sqlite`), and the 138 MB `quotes.csv` is
42
deliberately excluded from the build (see `.dockerignore`). So seeding is a
43
one-off `run` that bind-mounts the CSV and writes into the same named volume the
44
service uses:
45
46
```sh
47
# From the repo root (uses the `quotes` service's volume + env)
48
docker compose run --rm \
49
  -v "$PWD/apps/quotes/quotes.csv:/seed/quotes.csv:ro" \
50
  quotes quotes seed /seed/quotes.csv
51
52
# Then start the service normally
53
docker compose up -d quotes
54
```
55
56
The author list comes from the embedded copy. To seed with a different list
57
without rebuilding the image, mount your edited file over the working directory
58
(`/data`) too:
59
60
```sh
61
docker compose run --rm \
62
  -v "$PWD/apps/quotes/quotes.csv:/seed/quotes.csv:ro" \
63
  -v "$PWD/apps/quotes/classic_authors.txt:/data/classic_authors.txt:ro" \
64
  quotes quotes seed /seed/quotes.csv
65
```
66
67
Because seeding is idempotent, you can re-run either command after widening the
68
list to pull in the newly matched quotes.
69
70
## API (public, read-only)
71
72
- `GET /api/quotes?limit=100` — most recent quotes
73
- `GET /api/quotes/today` — the quote of the day
74
- `GET /api/quotes/{short_id}` — a single quote
75
76
## Environment
77
78
| Var | Default | Notes |
79
| --- | --- | --- |
80
| `QUOTES_PASSWORD` | _(empty)_ | Admin login password (plaintext or bcrypt hash). Empty disables login. |
81
| `QUOTES_API_KEY` | _(empty)_ | Reserved; the read API is currently public. |
82
| `QUOTES_DB_PATH` | `quotes.sqlite` | SQLite file path. |
83
| `HOST` / `PORT` | `0.0.0.0` / `3000` | Listen address. |
84
| `BASE_URL` | `http://localhost:3000` | Used in social meta tags. |
85
| `COOKIE_SECURE` | `false` | Set `true` behind HTTPS. |