| 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. | |