Skip to content

Commit a47cc48

Browse files
committed
feat: add /guestbook page with D1-backed entries and IP rate limiting
Visitors can leave a name + message (max 280 chars). Entries are stored in Cloudflare D1, hashed-IP rate-limited to one entry per 24 hours. Includes full theme support (scandinavian/hacker/space) and keyboard navigation integration. https://claude.ai/code/session_01VUYCfRzzGmbJvbzk65ESFb
1 parent 864d55c commit a47cc48

7 files changed

Lines changed: 528 additions & 2 deletions

File tree

β€ŽCLAUDE.mdβ€Ž

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pages/ β€” file-based routing
2626
feed.vue β€” /feed β€” Bluesky posts fetched from public AT Protocol API
2727
now.vue β€” /now β€” "what I'm doing now" page (live Bluesky + GitHub data)
2828
uses.vue β€” /uses β€” tech stack and tools overview (static content)
29+
guestbook.vue β€” /guestbook β€” visitor guestbook (D1-backed, rate-limited per IP per 24h)
2930
drafts/ β€” experimental pages
3031
components/ β€” Vue components
3132
composables/ β€” useTheme
@@ -34,6 +35,7 @@ server/api/ β€” H3 API routes
3435
feed.ts β€” fetches phareim's public Bluesky posts (AT Protocol API)
3536
rss.xml.ts β€” RSS 2.0 feed of Bluesky posts, served at /api/rss.xml
3637
projects.ts β€” fetches phareim's public GitHub repos
38+
guestbook.ts β€” GET/POST guestbook entries (D1)
3739
server/utils/ β€” db.ts, r2.ts, storage.ts, image-providers.ts, etc.
3840
types/ β€” shared TypeScript interfaces
3941
assets/themes/ β€” scandinavian.css, hacker.css, space.css
@@ -67,6 +69,6 @@ Three themes: **Scandinavian Glass** (default), **Cyberpunk**, **Space**.
6769
## Keyboard Shortcuts
6870

6971
- `M` key toggles the global menu (disabled on admin pages)
70-
- `[` / `]` navigate to previous / next page in order: `/`, `/about`, `/projects`, `/feed`, `/now`, `/uses`, `/meta`
72+
- `[` / `]` navigate to previous / next page in order: `/`, `/about`, `/projects`, `/feed`, `/now`, `/uses`, `/guestbook`, `/meta`
7173
- `1` / `2` / `3` switch themes (scandinavian / hacker / space)
7274
- `?` or `/` toggles the keyboard shortcuts overlay

β€Žapp.vueβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const showPalette = ref(false);
2727
const router = useRouter();
2828
const route = useRoute();
2929
30-
const NAV_PAGES = ['/', '/about', '/projects', '/feed', '/now', '/uses', '/meta'];
30+
const NAV_PAGES = ['/', '/about', '/projects', '/feed', '/now', '/uses', '/guestbook', '/meta'];
3131
3232
useHead({
3333
meta: [

β€Žcomponents/CommandPalette.vueβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const allCommands: Command[] = [
9090
{ id: 'nav-feed', label: 'thoughts', icon: 'πŸ’¬', hint: '/feed', type: 'page', action: () => router.push('/feed') },
9191
{ id: 'nav-now', label: 'now', icon: 'πŸ“', hint: '/now', type: 'page', action: () => router.push('/now') },
9292
{ id: 'nav-uses', label: 'uses', icon: 'πŸ”©', hint: '/uses', type: 'page', action: () => router.push('/uses') },
93+
{ id: 'nav-guestbook', label: 'guestbook', icon: '✍️', hint: '/guestbook', type: 'page', action: () => router.push('/guestbook') },
9394
{ id: 'nav-meta', label: 'meta', icon: 'πŸ“‹', hint: '/meta', type: 'page', action: () => router.push('/meta') },
9495
{ id: 'theme-scandi',label: 'scandinavian glass', icon: '❄️', hint: '1', type: 'theme', action: () => setTheme('scandi') },
9596
{ id: 'theme-hacker',label: 'cyberpunk', icon: 'πŸ“Ÿ', hint: '2', type: 'theme', action: () => setTheme('hacker') },

β€Ždatabase/schema.sqlβ€Ž

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ CREATE TABLE IF NOT EXISTS user_prompts (
4747
);
4848
CREATE INDEX IF NOT EXISTS idx_user_prompts_user_id ON user_prompts(user_id, created_at DESC);
4949

50+
-- Guestbook entries
51+
CREATE TABLE IF NOT EXISTS guestbook_entries (
52+
id TEXT PRIMARY KEY,
53+
name TEXT NOT NULL,
54+
message TEXT NOT NULL,
55+
ip_hash TEXT,
56+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
57+
);
58+
CREATE INDEX IF NOT EXISTS idx_guestbook_created_at ON guestbook_entries(created_at DESC);
59+
5060
-- AI model definitions for image generation
5161
CREATE TABLE IF NOT EXISTS model_definitions (
5262
id TEXT PRIMARY KEY,

0 commit comments

Comments
Β (0)