Initial commit
This commit is contained in:
116
CLAUDE.md
Normal file
116
CLAUDE.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Full-stack starter template: **Nuxt 4 SPA** frontend + **PocketBase** backend + **Capacitor** mobile (Android/iOS) + **Deno sidecar** for FCM push notifications. Designed as a copy-and-customize base for shipping apps fast.
|
||||
|
||||
**SSR is disabled** (`ssr: false`) — this is a pure SPA. There are no Nuxt server routes.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Frontend
|
||||
pnpm dev # Start Nuxt dev server (localhost:3000)
|
||||
pnpm build # Production build
|
||||
pnpm lint # ESLint
|
||||
pnpm typecheck # nuxt typecheck (vue-tsc)
|
||||
pnpm test # Vitest (unit/e2e/nuxt projects)
|
||||
|
||||
# Backend
|
||||
pnpm pocketbase:start # docker compose up (PocketBase + sidecar + mailpit)
|
||||
pnpm pocketbase:stop # docker compose down
|
||||
pnpm pocketbase:types # Regenerate TypeScript types from PocketBase schema
|
||||
|
||||
# Mobile
|
||||
pnpm assets # Generate app icons and splash screens
|
||||
```
|
||||
|
||||
**Dev workflow:** Run `pnpm pocketbase:start` first, then `pnpm dev` in a second terminal.
|
||||
|
||||
**Mailpit** (dev email viewer) runs at `http://localhost:8025` when PocketBase is up.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
app/ # Nuxt frontend (SPA)
|
||||
├── pages/ # File-based routing (index, login, profile, confirm)
|
||||
├── stores/ # Pinia stores (user, counter, notifications, avatar)
|
||||
├── plugins/pocketbase.ts # PB client init with ReactiveAuthStore
|
||||
├── composables/ # usePocketBase() → { pb, authStore }
|
||||
├── middleware/ # auth guard + global user fetch
|
||||
├── components/ # App/, Counter/, Profile/ subdirs
|
||||
├── i18n/locales/ # en.json, de.json (frontend translations)
|
||||
└── types/ # pocketbase.types.ts (auto-generated), i18n.types.ts
|
||||
|
||||
pocketbase/
|
||||
├── pb_migrations/ # Numbered JS migrations (0-4)
|
||||
├── pb_hooks/ # Server-side JS hooks (OTP user creation, mail templates, FCM, custom endpoints)
|
||||
│ ├── templates/ # Localized HTML email templates (en/, de/)
|
||||
│ └── locales/ # Backend translation JSON (mail subjects, notification text)
|
||||
├── sidecar/ # Deno microservice for FCM push (:8091)
|
||||
└── docker-compose.yml # pocketbase + sidecar + mailpit
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### Authentication
|
||||
Three methods: **Email OTP** (primary), **Google OAuth**, **Apple OAuth**. Flow lives in `app/stores/user.ts`. PocketBase hooks auto-create users on first OTP request (`pb_hooks/createOtpUser.pb.js`). OAuth redirect lands on `/confirm` page.
|
||||
|
||||
### Reactive Auth
|
||||
`app/plugins/pocketbase.ts` wraps PocketBase's `LocalAuthStore` in a Vue-reactive `ReactiveAuthStore` using `shallowRef`. Access via `usePocketBase()` composable — never instantiate PocketBase directly.
|
||||
|
||||
### Realtime Subscriptions
|
||||
Stores (`counter.ts`, `notifications.ts`) use `pb.collection('x').subscribe('*', callback)` for live updates. Always call `unsubscribe()` on component unmount.
|
||||
|
||||
### PocketBase Hooks
|
||||
Hooks in `pocketbase/pb_hooks/` run inside PocketBase's embedded JS engine (not Node/Deno). They use PocketBase's JSVM API — `onMailerRecordOTPSend`, `routerAdd`, `$app.findFirstRecordByFilter`, etc. See [PocketBase JSVM docs](https://pocketbase.io/docs/js-overview/).
|
||||
|
||||
### Custom Endpoints
|
||||
Custom backend routes are defined in PocketBase hooks via `routerAdd()`. Example: `POST /counter/reset` in `resetCounter.pb.js`.
|
||||
|
||||
### Adding a New Collection
|
||||
1. Create a migration in `pocketbase/pb_migrations/` (next number in sequence)
|
||||
2. Run `pnpm pocketbase:types` to regenerate `app/types/pocketbase.types.ts`
|
||||
3. Create a Pinia store in `app/stores/` for the new collection
|
||||
4. Use typed collection names from `Collections` enum in `pocketbase.types.ts`
|
||||
|
||||
### Localization
|
||||
Frontend: `@nuxtjs/i18n` with `no_prefix` strategy (en, de). Backend: per-user `language` field drives email template and notification text selection. When adding a new language, update both `app/i18n/locales/` and `pocketbase/pb_hooks/locales/` + `templates/`.
|
||||
|
||||
## Tech Stack Quick Reference
|
||||
|
||||
| Layer | Tech |
|
||||
|-------|------|
|
||||
| Frontend | Nuxt 4, Vue 3, Pinia, Nuxt UI, Tailwind CSS v4 |
|
||||
| Backend | PocketBase (SQLite, REST, Realtime WS) |
|
||||
| Mobile | Capacitor (Android/iOS), Push Notifications |
|
||||
| Sidecar | Deno (FCM auth + push delivery, port 8091) |
|
||||
| Email (dev) | Mailpit (SMTP + web UI) |
|
||||
| Validation | Zod |
|
||||
| Package mgr | pnpm (monorepo via pnpm-workspace.yaml) |
|
||||
| Testing | Vitest + @nuxt/test-utils + Playwright |
|
||||
|
||||
## Environment
|
||||
|
||||
Copy `.env.example` to `.env`. Key vars: `NUXT_PUBLIC_POCKETBASE_URL`, `SUPERUSER_EMAIL`/`SUPERUSER_PW`, OAuth client IDs/secrets, `SIDECAR_SECRET`, `GOOGLE_CREDENTIALS_JSON` (for FCM).
|
||||
|
||||
## Validating Changes
|
||||
|
||||
After making changes to the frontend, **always validate them using the Playwright MCP server** by interacting with the app as a user would. The dev server runs at `http://localhost:3000`.
|
||||
|
||||
Typical validation flow:
|
||||
1. Navigate to the relevant page with `mcp__playwright__browser_navigate`
|
||||
2. Use `mcp__playwright__browser_snapshot` to inspect the page structure
|
||||
3. Interact with UI elements via `mcp__playwright__browser_click`, `mcp__playwright__browser_fill_form`, etc.
|
||||
4. Confirm the expected behaviour is visible in the snapshot or page state
|
||||
|
||||
This applies to any visible UI change — new components, routing changes, form flows, auth states, etc.
|
||||
|
||||
## File Conventions
|
||||
|
||||
- `app/types/pocketbase.types.ts` is **auto-generated** — do not edit manually, run `pnpm pocketbase:types`
|
||||
- PocketBase migrations are numbered sequentially (`0_setup.js`, `1_users_language.js`, ...)
|
||||
- PocketBase hooks use `.pb.js` extension
|
||||
- Email templates are plain HTML with `{{.Variable}}` Go template syntax
|
||||
Reference in New Issue
Block a user