Web apps

Climate tech job board with paid listings and digests

Job board where companies pay via Stripe for 30-day listings, with weekly filter-matched email digests for seekers, built on Astro SSR and Cloudflare Workers.

astrocloudflare-workersd1stripetypescriptvitest

Built 1 time, downloaded 0 times.

Install from your terminal

Run this in your project folder. It signs you in, shows what changes and asks before it overwrites a file.

npx claude-lab add climate-tech-job-board-with-paid-listings-and-digests-e88570
10 filesDownload zip
CLAUDE.md
# Climate tech job board

## Purpose

- Climate tech job board. Companies pay via Stripe to post a listing.
- Listings expire 30 days after publish.
- Job seekers can save filters. A weekly email digest lists new roles that match.
- No scraping. All posts come from companies.

## Stack

- Astro in SSR mode with the `@astrojs/cloudflare` adapter.
- Cloudflare Workers, with D1 for data.
- Cron Triggers for listing expiry and the weekly digest.
- Queues or the email provider API for sending mail.
- Stripe Checkout plus a webhook.
- TypeScript strict.

## Commands

- `npm run dev`: local dev server.
- `npm run check`: astro check.
- `npm run lint`: eslint and prettier checks.
- `npm test`: vitest.
- `npm run build`: production build.
- `npx wrangler d1 migrations apply DB --local`: local D1 migrations.
- `npx wrangler dev`: Worker runtime locally.

## Layout

- `src/pages` and `src/pages/api`: routes and endpoints.
- `src/lib/db`: typed D1 query helpers.
- `src/lib/stripe`: checkout and webhook handling.
- `src/lib/digest`: digest building and sending.
- `src/lib/filters`: pure filter match function.
- `migrations/`: numbered SQL files.
- `wrangler.toml`: Worker, D1 and cron config.
- `tests/`: vitest suites and Stripe fixtures.

## Data rules

- Listing status is one of: draft, paid, live, expired.
- Only the Stripe webhook may move a listing from draft to live.
- `expires_at` = `published_at` + 30 days.
- Expired listings return 410 and are excluded from the sitemap and the digest.
- Every public query filters `status = 'live' AND expires_at > now`.

## Digest rules

- One email per subscriber per week.
- Include only listings published since `last_digest_at`.
- Skip the email if nothing matches.
- Every email has a one-click unsubscribe link with a signed token.
- Digest sends are idempotent: record sent rows before or with the send.

## Gotchas

- Workers has no Node `fs`. Use the Web Crypto API for signing and for verifying Stripe webhook signatures.
- D1 is async. Batch related writes with `db.batch`.
- The cron handler must be safe to re-run.
- Never trust client-side price or status.
- Sanitize company-submitted descriptions. Allow only a markdown subset.
- Use `.bind()` for every SQL parameter.

## Secrets

- Keep secrets only in `.dev.vars` (gitignored) and via `wrangler secret put`.
- Placeholders: `YOUR_STRIPE_SECRET_KEY`, `YOUR_STRIPE_WEBHOOK_SECRET`, `YOUR_EMAIL_API_KEY`.
- Never commit or print secret values.

## Conventions

- Test mode only for Stripe. No live-mode calls.
- Add tests for new logic, especially filter matching, expiry and digest.
- Keep imports relative inside `src/`.

## Workflow

- Explore, plan, code, then run the verify skill before saying a task is done.
- Use `/clear` between unrelated tasks.
- Ask before running `wrangler deploy`, `wrangler d1 execute --remote`, or any Stripe live-mode call.

## Reference

@REVIEW.md
@SPEC.md
Use for my project