114 lines
3.4 KiB
Markdown
114 lines
3.4 KiB
Markdown
# Guestbook — Implementation Notes
|
|
|
|
## Architecture summary
|
|
|
|
The guestbook extends the Astro site with **hybrid SSR** mode using the `@astrojs/node` standalone adapter.
|
|
|
|
- Existing content pages remain static.
|
|
- Guestbook and admin pages are server-rendered (`export const prerender = false`).
|
|
- API routes handle submissions, moderation, and token-based admin login.
|
|
- SQLite (`better-sqlite3`) stores entries, sessions, rate-limit data, and audit logs.
|
|
|
|
## Relevant files
|
|
|
|
```
|
|
src/
|
|
lib/
|
|
db.ts — SQLite singleton + schema
|
|
guestbook.ts — Entry CRUD, pagination, moderation reads
|
|
auth.ts — Session management + cookie policy
|
|
spam.ts — Validation + heuristic spam scoring
|
|
pages/
|
|
guestbook.astro — Public guestbook page
|
|
admin/
|
|
index.astro — Moderation portal (session-gated)
|
|
login.astro — Token login form
|
|
pages/api/
|
|
guestbook/submit.ts — POST: public guestbook submission
|
|
admin/token-login.ts — POST: token authentication + session creation
|
|
admin/moderate.ts — POST: approve / reject / spam
|
|
admin/logout.ts — POST: end admin session
|
|
layouts/
|
|
AdminLayout.astro — Minimal admin UI layout
|
|
```
|
|
|
|
## Environment variables
|
|
|
|
Copy `.env.example` to `.env` and set:
|
|
|
|
| Variable | Required | Description |
|
|
|---|---|---|
|
|
| `SECRET_KEY` | **Yes** | Random secret for IP-hash salting and session-related values |
|
|
| `ADMIN_SECRET_TOKEN` | **Yes** | Shared secret token for `/admin/login` |
|
|
| `COOKIE_SECURE` | No | Force secure cookies (`true`/`false`). If unset, `NODE_ENV=production` => secure cookies |
|
|
| `DB_PATH` | No | SQLite path (default: `./data/guestbook.db`) |
|
|
| `PORT` | No | Server port (default: `3000`) |
|
|
| `HOST` | No | Bind host (default: `0.0.0.0`) |
|
|
|
|
Generate secrets:
|
|
|
|
```bash
|
|
openssl rand -hex 32 # SECRET_KEY
|
|
openssl rand -hex 32 # ADMIN_SECRET_TOKEN
|
|
```
|
|
|
|
## Admin setup
|
|
|
|
1. Set `ADMIN_SECRET_TOKEN` in your environment.
|
|
2. Open `/admin/login`.
|
|
3. Enter token.
|
|
4. After success, you are redirected to `/admin`.
|
|
|
|
If token is missing, `/admin/login` shows a configuration warning and login is disabled.
|
|
|
|
## Local development
|
|
|
|
```bash
|
|
npm install
|
|
cp .env.example .env
|
|
# set at minimum:
|
|
# SECRET_KEY=...
|
|
# ADMIN_SECRET_TOKEN=...
|
|
# DB_PATH=./data/guestbook.db
|
|
# COOKIE_SECURE=false # for local http
|
|
|
|
npm run dev
|
|
# guestbook: http://localhost:4321/guestbook
|
|
# admin: http://localhost:4321/admin/login
|
|
```
|
|
|
|
## Docker deployment
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
docker compose logs -f cozy-den
|
|
```
|
|
|
|
The `guestbook_data` Docker volume persists the SQLite database.
|
|
|
|
## Moderation flow
|
|
|
|
1. Visitor submits message at `/guestbook`.
|
|
2. Entry is saved as `pending`.
|
|
3. Admin logs in at `/admin/login` with token.
|
|
4. Admin approves/rejects/marks spam in `/admin`.
|
|
5. Approved entries are shown publicly.
|
|
|
|
## Privacy decisions
|
|
|
|
- IP addresses are never stored directly.
|
|
- A truncated salted hash is stored only for rate limiting.
|
|
- No tracking scripts or third-party analytics.
|
|
- Admin session cookie is `httpOnly` and `SameSite=Strict`.
|
|
- User content is stored as plain text (HTML stripped server-side).
|
|
|
|
## Database tables
|
|
|
|
| Table | Purpose |
|
|
|---|---|
|
|
| `guestbook_entries` | Submissions + moderation status |
|
|
| `admin_sessions` | Active admin sessions |
|
|
| `rate_limit` | Submission throttling by IP hash |
|
|
| `audit_log` | Moderation actions |
|
|
|