Skip to content

iwate/bookmark

Repository files navigation

bookmark

Simple single-user bookmark service running on Cloudflare Workers + D1.

Required configuration

This app requires both of the following at runtime:

  • D1 binding named DB
  • Worker secret named WRITE_SECRET

If either is missing, the app returns 500 server misconfigured.

Local setup

  1. Install dependencies.
npm install
  1. Ensure D1 binding is configured in wrangler.toml as [[d1_databases]] with binding = "DB".

  2. Set a local development secret in .dev.vars (do not commit this file):

WRITE_SECRET=replace-with-a-long-random-secret

With WRITE_SECRET declared as a required secret in wrangler.toml, both WRITE_SECRET=test npm run dev and .dev.vars work for local testing.

  1. Run local development server:
npm run dev

This applies the local D1 migrations automatically before starting Wrangler, so a fresh database no longer needs manual setup.

Production setup

Set the write secret in Cloudflare before deployment:

wrangler secret put WRITE_SECRET

Apply migrations as needed for the configured D1 database.

Tests

npm test
npm run test:e2e

npm test remains the default automated suite. npm run test:e2e verifies MVP critical paths (GET /, POST /bookmarks with valid WRITE_SECRET, GET /rss.xml) against a local Wrangler runtime.

Routes

  • GET /: render bookmark list and forms
  • GET /rss.xml: render RSS feed from current bookmarks
  • POST /bookmarks/metadata: fetch page metadata (url) and return { title, thumbnailUrl } without DB writes
  • POST /bookmarks: create a bookmark (url, title, thumbnailUrl, comment, secret)
  • POST /bookmarks/:id/update: update an existing bookmark (url, title, thumbnailUrl, comment, secret)
  • POST /bookmarks/:id/delete: delete an existing bookmark (secret)

All write routes require a valid WRITE_SECRET and return 403 when authentication fails.

POST /bookmarks/metadata is read-only and does not require WRITE_SECRET. The form calls this endpoint on URL blur, auto-filling only empty Title and Thumbnail URL fields. Metadata failures are non-fatal and manual form submission still works.

For SSRF hardening, metadata fetch performs host validation on both the requested URL and every redirect target. Before each upstream request, it resolves A/AAAA records via DNS-over-HTTPS and rejects requests if any resolved address is loopback/private/link-local/unspecified/multicast/documentation/reserved.

Cloudflare Workers cannot pin the outbound TCP connection to a specific resolved IP, so perfect DNS rebinding prevention is not possible in-process. As compensating controls, this app uses manual redirect handling with per-hop re-validation, strict scheme/host checks, and short upstream timeouts.

About

My bookmark tool

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors