1
Fork 0
random-quote/README.md
Leonardo Devai 6f9fc1d79f Review and modernize all 42 projects to the updated standard
Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24,
Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root
runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone
line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login,
self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the
blog series, safe markdown links in the frontends, and many smaller bugs — every
project was built, run and exercised end to end.

Adds scripts/publish.sh + a CI publish job that splits every folder into its own
repo at git.devai.io/templates/<folder>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G
2026-09-27 21:10:38 +02:00

68 lines
2.1 KiB
Markdown

# random-quote
Show a random quote, click for another — and learn the `fetch()` pattern that
powers real APIs, using a data file you can open and read yourself. It never
shows the same quote twice in a row.
## Run
Get it: `git clone https://git.devai.io/templates/random-quote.git`
This one needs a tiny local web server: browsers won't let a page opened
straight from disk (`file://`) `fetch()` another file, even one in the same
folder. From this folder:
```sh
python3 -m http.server 8000
```
Then open http://localhost:8000. (That one needs Python 3; any static file
server works just as well.)
Or serve it like production:
```sh
docker compose up --build
```
Then open http://localhost:8080.
## How it works
`quotes.json` is an array of `{ "text": …, "author": … }` objects sitting next
to the page. `loadQuotes()` reads it exactly the way you'd read a live API:
```js
const res = await fetch("quotes.json");
quotes = await res.json();
```
Swap that URL for a real endpoint and the rest barely changes. `showRandom()`
picks `Math.floor(Math.random() * quotes.length)` and re-rolls if it matches
the last pick. "Loading…" shows while the file loads, and if it can't be read
(most often because the page was opened from disk) the page tells you to serve
the folder instead.
Try it: add your own quotes to `quotes.json` and refresh, add a "Copy" button
with `navigator.clipboard.writeText`, or point `fetch` at a live quotes API.
## Layout
```
index.html the quote, its author and the button
app.js loadQuotes() and showRandom()
quotes.json the data — edit it freely
styles.css the look; follows your system's light or dark mode
```
## Deploy
Push to your own GitHub repo and the shipped workflow
(`.github/workflows/ci.yml`) tests the compose stack, publishes the image to
GHCR, and — once you set the `DEPLOY_HOST` / `DEPLOY_USER` variables and
`DEPLOY_KEY` secret — deploys it to your server over ssh.
---
Part of [devai.io](https://devai.io) — the APIs & Data track: fetch real data
from the internet. Next up:
[currency-converter](https://git.devai.io/templates/currency-converter).