90 lines
2.9 KiB
Markdown
90 lines
2.9 KiB
Markdown
# Personal Blog
|
|
|
|
A small static personal blog. No build step, no backend — plain HTML, CSS, and
|
|
vanilla JavaScript.
|
|
|
|
## Pages
|
|
|
|
- `index.html` — home page
|
|
- `reading.html` — reading list page (curated links grouped by category,
|
|
rendered from `data/reading-list.js`)
|
|
- `contact.html` — contact page (opens the visitor's mail client with the form
|
|
fields pre-filled via a `mailto:` link)
|
|
- `newsletter.html` — newsletter signup page (posts the visitor's email to a
|
|
configured serverless endpoint; no self-hosted backend or subscriber list
|
|
management)
|
|
|
|
## Newsletter signup
|
|
|
|
The signup form on `newsletter.html` POSTs the visitor's email to a serverless
|
|
endpoint. Everything is configured in one file — `js/newsletter-config.js`:
|
|
|
|
- `NEWSLETTER_ENDPOINT` — the serverless endpoint the form POSTs to.
|
|
- `NEWSLETTER_API_TOKEN` — the API token sent in the `Authorization` header.
|
|
|
|
**Security:** a real API token must never be committed to the repository. The
|
|
committed default is an empty placeholder; the deployer sets the real token in
|
|
`js/newsletter-config.js` at deploy time, and only then. The page logic reads
|
|
the token from this config module and never hardcodes one.
|
|
|
|
When the token is missing or the endpoint rejects it, the visitor sees a fixed,
|
|
user-safe error message — the token or endpoint internals are never surfaced.
|
|
After a successful signup a confirmation message is shown.
|
|
|
|
## Reading list
|
|
|
|
The reading list is a single data file: `data/reading-list.js`. The page code
|
|
renders whatever is in it, so it can grow to hundreds of entries without code
|
|
changes.
|
|
|
|
To add a link, edit `data/reading-list.js` only — add an entry with a `title`,
|
|
a `url`, a `category`, and an optional one-line `note`:
|
|
|
|
```js
|
|
{
|
|
category: "Engineering",
|
|
title: "A new article",
|
|
url: "https://example.com/article",
|
|
note: "Optional one-liner shown under the title.",
|
|
},
|
|
```
|
|
|
|
Categories appear on the page in the order they are first used; entries keep
|
|
the order they are listed in.
|
|
|
|
## Development
|
|
|
|
Serve the directory with any static file server, e.g.:
|
|
|
|
```sh
|
|
python3 -m http.server 8000
|
|
```
|
|
|
|
Then open <http://localhost:8000>.
|
|
|
|
## Tests
|
|
|
|
Tests use Node's built-in test runner (no dependencies to install):
|
|
|
|
```sh
|
|
npm test
|
|
```
|
|
|
|
## Project layout
|
|
|
|
```text
|
|
index.html Home page
|
|
reading.html Reading list page
|
|
contact.html Contact page
|
|
newsletter.html Newsletter signup page
|
|
css/style.css Global + responsive styles
|
|
data/reading-list.js Curated reading list data (edit to add links)
|
|
js/newsletter-config.js Newsletter endpoint + API token (edit at deploy time)
|
|
js/newsletter.js Newsletter signup wiring: POST + user-safe errors (tested)
|
|
js/mailto.js Pure mailto: URL builder (unit tested)
|
|
js/contact.js Contact form wiring (browser + tests)
|
|
js/reading-list.js Reading list renderer: data file -> grouped HTML (tested)
|
|
tests/ Node built-in test suite
|
|
.gitea/workflows/ ci.yml runs `npm test` on PRs and pushes to main
|
|
```
|