{"id":49767533,"url":"https://github.com/michaelasper/headstash","last_synced_at":"2026-05-11T11:04:41.178Z","repository":{"id":335972315,"uuid":"1147666081","full_name":"michaelasper/headstash","owner":"michaelasper","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-14T09:07:18.000Z","size":341,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T14:01:54.914Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michaelasper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-02T04:03:15.000Z","updated_at":"2026-02-14T07:55:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/michaelasper/headstash","commit_stats":null,"previous_names":["michaelasper/headstash"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/michaelasper/headstash","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelasper%2Fheadstash","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelasper%2Fheadstash/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelasper%2Fheadstash/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelasper%2Fheadstash/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelasper","download_url":"https://codeload.github.com/michaelasper/headstash/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelasper%2Fheadstash/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32891967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-11T11:04:39.999Z","updated_at":"2026-05-11T11:04:41.171Z","avatar_url":"https://github.com/michaelasper.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Headstash\n\nMobile-friendly web app for logging cannabis strain reviews.\n\n## Prereqs\n\n## UI foundations\n\n- See `docs/design.md` for semantic tokens + focus ring + typography/spacing guidance.\n- See `docs/prisma-migration-hardening.md` for migration safety and deploy rollback checklist.\n- See `docs/deployment-runbook.md` for deploy prerequisites, secret metadata requirements, and CI gate flow.\n- See `docs/e2e-playbook.md` for E2E architecture, local debugging commands, CI behavior, flaky-test triage, and ownership/update process.\n- See `docs/frontend-qa-visual-regression-checklist.md` for pre-merge UI regression checks across breakpoints, states, and interaction patterns.\n\n- Node.js 20+\n- npm\n\n## Setup\n\n```bash\n# install deps\nnpm ci\n\n# set env vars\ncp .env.example .env\n\n# create/apply local sqlite migration\nmake db-migrate\n\n# seed deterministic local demo data\nmake db-seed\n\n# run the dev server\nmake dev\n```\n\n## Runtime commands\n\nUse these commands locally so dev/CI/prod workflows stay aligned:\n\n```bash\nmake help\nmake dev\nmake lint\nmake typecheck\nmake build\nmake sanity\nmake db-generate\nmake db-migrate\nmake db-deploy\nmake db-reset\nmake db-seed\nmake db-reset-seed\n```\n\n### E2E quickstart\n\n```bash\nnpm run e2e:install\nnpm run e2e:test -- --list\nnpm run e2e:test -- --grep \"home page renders key navigation\"\nnpm run e2e:test -- --project=chromium e2e/tests/critical/critical-path.spec.ts\nnpm run e2e:test:headed\n```\n\nFor full triage/debug guidance, see `docs/e2e-playbook.md`.\n\n### Deploy bootstrap helper scripts\n\nUse these scripts to wire AWS + GitHub deployment prerequisites from machine-readable artifacts:\n\n```bash\n# 1) Create/verify AWS backend + OIDC + deploy role metadata artifact\nnpm run aws:bootstrap -- --plan --json\nnpm run aws:bootstrap -- --apply --repo \u003cowner/repo\u003e --profile \u003caws-profile\u003e\n\n# 2) Configure GitHub staging/production environments from aws artifact\nnpm run github:bootstrap -- --plan --json\nnpm run github:bootstrap -- --apply --artifact artifacts/aws-bootstrap.json\n```\n\n- `aws:bootstrap` writes `artifacts/aws-bootstrap.json` (backend + OIDC + role metadata contract).\n- `github:bootstrap` consumes that artifact, upserts GitHub environments, sets env vars/secrets, and writes a redacted verification artifact.\n- Both support `--plan` (default) and `--apply`; `--json` prints machine-readable output for CI logs.\n\n### Staging DNS + health readiness automation\n\nUse this script to verify rollback-drill readiness (DNS + TLS + health endpoint), with optional Route53 UPSERT support and Namecheap handoff guidance:\n\n```bash\nnpm run staging:readiness -- --host=staging.headstash.app --health-path=/api/health\nnpm run staging:readiness -- --json\n\n# Route53 apply aliases (both forms supported)\nnpm run staging:readiness -- --apply --aws-zone-id=\u003czone-id\u003e --aws-target=\u003calb-or-dns-target\u003e\nnpm run staging:readiness -- --apply-route53 --route53-zone-id=\u003czone-id\u003e --route53-record-name=staging.headstash.app --route53-record-value=\u003calb-or-dns-target\u003e\n\n# Namecheap -\u003e Route53 nameserver handoff helper\nnpm run staging:readiness -- --namecheap-guide --route53-zone-id=\u003czone-id\u003e --namecheap-domain=headstash.app --json\n```\n\n- `staging:readiness` checks DNS resolution, TLS certificate validity, and health endpoint status.\n- `--apply` / `--apply-route53` performs Route53 UPSERT before validation (AWS CLI required).\n- `--namecheap-guide` fetches Route53 nameservers and prints the exact Namecheap handoff checklist.\n- Use `--json` for CI/log-friendly machine output.\n- Full operator steps: `docs/namecheap-to-route53-staging-runbook.md`.\n\n### Local observability toggles\n\nUse these optional env vars in `.env` for local diagnostics:\n- `PRISMA_SLOW_QUERY_THRESHOLD_MS` (default `100`)\n- `PRISMA_LOG_FORMAT` (`text` or `json`)\n- `PRISMA_LOG_INCLUDE_QUERY` (`1` to include SQL + params in logs)\n\n### GitHub OAuth setup (optional)\n\n1) Create a GitHub OAuth App\n- Callback URL: `http://localhost:3000/api/auth/callback/github`\n\n2) Set env vars:\n- `GITHUB_CLIENT_ID`\n- `GITHUB_CLIENT_SECRET`\n\n### Email + password setup (optional)\n\nNo extra env vars required.\n\n- Use the **Email + password** section on `/auth/signin`.\n- Passwords are hashed with bcrypt.\n- Rate limiting is in-memory (dev-only) and resets on server restart.\n\n### Local auth parity mode\n\nSet `AUTH_LOCAL_MODE=\"credentials-only\"` in `.env` to run a local auth mode that mirrors production-style protected-route behavior with credentials sessions only (disables GitHub + magic link on sign-in UI).\n\n### Profile MVP\n\n- Edit profile at `/profile` (requires sign-in)\n- Public profile: `/u/[handle]` (no auth required)\n- Fields: `displayName`, `handle` (unique, @format), `bio`, `avatarUrl`, `links` (URLs)\n\nHandle rules:\n- Stored in canonical form: lowercase, always prefixed with `@`\n- Allowed chars: `a-z`, `0-9`, `_`\n- Length: 3–20 characters (including `@`)\n- Reserved handles are blocked (e.g. `@admin`, `@auth`, `@api`, `@posts`, `@u`, ...)\n\n### Posts v0\n\n- Global feed: `/posts` (public)\n- Following feed: `/posts/following` (requires sign-in)\n- Creating a post requires sign-in\n- Posts can optionally attach a review (v0)\n\n### Search v0\n\n- Search users and posts at `/search`\n- Simple substring matching (SQLite, no full-text search)\n\n### Onboarding v0\n\n- Signed-in users without a handle are prompted from `/me` to complete onboarding at `/onboarding`.\n\n### Follow v0\n\n- Follow/unfollow from public profiles: `/u/[handle]`\n- Shows follower/following counts\n\n### Reactions v0\n\n- Like posts on `/posts` (auth required to like)\n- Shows like count + current user state\n\n### Favorites v0\n\n- Favorite posts on `/posts` and `/posts/[id]` (auth required)\n- View your favorites at `/me/favorites`\n\n### Notifications v0\n\n- In-app notifications at `/notifications` (auth required)\n- Events: follow, like your post, comment on your post\n\n### Comments v0\n\n- Post detail page: `/posts/[id]`\n- Commenting requires sign-in\n\n### Avatar upload (dev-local only)\n\nDEV ONLY: uploading an avatar writes into `public/uploads/avatars/` and sets `avatarUrl` to a local path.\n\n- This is not suitable for serverless/production.\n- Uploaded files are gitignored.\n\nOpen http://localhost:3000\n\n## Verify\n\n```bash\nmake sanity\n# optionally, for full production-bundle verification when env is configured:\nmake build\n```\n\n## Prisma Studio (optional)\n\n```bash\nnpx prisma studio\n```\n\n## Environment\n\nRuntime env is validated at startup. Invalid/incomplete configuration fails fast with a clear error.\n\nRequired in **all** environments:\n\n- `DATABASE_URL`\n\nRequired in **production**:\n\n- `AUTH_SECRET` (or `NEXTAUTH_SECRET`)\n- `AUTH_URL` (or `NEXTAUTH_URL`)\n\nDevelopment/test defaults:\n\n- `AUTH_SECRET` / `NEXTAUTH_SECRET` default to a local dev secret if omitted\n- `AUTH_URL` / `NEXTAUTH_URL` default to `http://localhost:3000` if omitted\n\nOptional integrations:\n\n- GitHub OAuth: `GITHUB_CLIENT_ID` + `GITHUB_CLIENT_SECRET` (must be set together)\n- SMTP magic links: `EMAIL_SERVER_HOST`, `EMAIL_SERVER_PORT`, `EMAIL_SERVER_USER`, `EMAIL_SERVER_PASSWORD`, `EMAIL_FROM` (all-or-none)\n- Local parity mode: `AUTH_LOCAL_MODE=full|credentials-only`\n- Prisma slow query logging threshold: `PRISMA_SLOW_QUERY_THRESHOLD_MS` (default `100`)\n\nSee `.env.example` for complete values and comments.\n\n## Validation / constraints\n\nRequest-boundary validation lives in `src/app/actions.ts` (Server Actions) and is intentionally kept small:\n\n- `Review.rating` must be one of Prisma enum `ReviewRating` (`ONE`–`FIVE`).\n- `Review.strainId` must be a `cuid()`.\n- `Review.consumedAt` is optional; when provided it must parse as a valid date.\n- `Review` can optionally be tagged with 1 Effect tag + 1 Terpene tag (v1).\n\n## Tags (effects / terpenes)\n\nHeadstash supports two kinds of tags for filtering reviews:\n\n- **Effects** (e.g., Relaxed, Uplifted)\n- **Terpenes** (e.g., Limonene, Myrcene)\n\nCreate tags at `/tags` (or `/tags/new`), then select them when creating a review.\n\nFiltering:\n\n- Use the `/reviews` page search box for strain name (and notes).\n- Use the Effect/Terpene dropdown filters to narrow results.\n\nTODO (when we add fields/endpoints):\n\n- If we start accepting `Strain.thcPct` / `Strain.cbdPct`, validate 0–100.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelasper%2Fheadstash","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelasper%2Fheadstash","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelasper%2Fheadstash/lists"}