dbf1fa17d7
Capture hard-won dev knowledge that was only in commit messages: - wsl-dev-networking: WSL2 NAT blocks UDP broadcast (device discovery can't reach the LAN); fix is mirrored networking (.wslconfig, Win11 22H2+), plus the gotchas that remained after — multiple interfaces, subnet-directed broadcast, localhost->IPv6 stall. Alternatives for non-mirrored setups. - local-dev-workflow: first-time setup, pnpm dev, and the gotchas (the strip-types dev-server hang -> tsx, the 127.0.0.1 proxy fix, .env loading, seeding into the right DB). - device-discovery: corrected the old "broadcast permission (EACCES)" note — the real cause was the lib not enabling SO_BROADCAST for global 255.255.255.255; documented the three verified broadcast gotchas + I/O serialization. - schema: add a `reference` page type; new "Dev environment" index section; log. Links lint clean; both new pages well-connected.
57 lines
2.7 KiB
Markdown
57 lines
2.7 KiB
Markdown
---
|
|
type: reference
|
|
tags: [parking, dev-environment, workflow]
|
|
sources: []
|
|
updated: 2026-06-15
|
|
---
|
|
|
|
# Local Dev Workflow
|
|
|
|
> Dev-environment reference, not product architecture. How to run the stack locally and the
|
|
> gotchas that have bitten us. For device testing under WSL also read [[wsl-dev-networking]].
|
|
|
|
## First-time setup
|
|
|
|
```bash
|
|
pnpm install
|
|
cp apps/server/.env.example apps/server/.env # then fill in JWT_SECRET
|
|
# JWT_SECRET=$(openssl rand -hex 32) # server refuses to start without a strong one
|
|
pnpm --filter @parking/db exec drizzle-kit migrate # create the SQLite schema
|
|
pnpm seed:admin # create the first admin (see [[local-jwt-auth]])
|
|
```
|
|
|
|
`apps/server/.env` and the `*.sqlite` files are **gitignored** (local-only). Leave `NODE_ENV`
|
|
**unset** in dev so the auth cookies aren't `Secure`-only (Vite dev is plain http).
|
|
|
|
## Running
|
|
|
|
```bash
|
|
pnpm dev # turbo runs both: Vite (web, :5173) + Fastify (server, :3000)
|
|
```
|
|
|
|
Open `http://localhost:5173`. The Vite dev proxy forwards `/api` + `/health` to the backend, so
|
|
the SPA and API are **same-origin** and the [[local-jwt-auth|cookie auth]] works without CORS.
|
|
Production uses an **nginx** reverse proxy (`deploy/nginx.conf`) for the same same-origin setup.
|
|
|
|
## Gotchas (all fixed, recorded so they don't recur)
|
|
|
|
- **Server dev must not be `node --experimental-strip-types src/index.ts`.** Type-stripping does
|
|
**not** rewrite `.js` import specifiers to `.ts`, so it crashed with `ERR_MODULE_NOT_FOUND` and
|
|
silently never started — the symptom was the SPA hanging for *minutes* (the Vite proxy waiting
|
|
on a dead backend), then finally erroring. The `dev` script uses **`tsx watch`** instead.
|
|
- **Vite proxy → `127.0.0.1`, not `localhost`.** `localhost` resolves to IPv6 `::1` first while
|
|
the backend binds IPv4; Node's proxy can stall on the v6 attempt. Same class of "slow then
|
|
works" hang, worse under WSL2 mirrored mode ([[wsl-dev-networking]]).
|
|
- **`.env` must actually be loaded.** The server reads `process.env` only; the dev/start scripts
|
|
load the file via Node's `--env-file-if-exists=.env`. An empty `JWT_SECRET=` makes the server
|
|
fail-fast at boot.
|
|
- **Seed into the DB the server reads.** `seed:admin` and the server must use the same
|
|
`DATABASE_URL`; running via `pnpm seed:admin` (which loads `apps/server/.env`) keeps them aligned.
|
|
|
|
## Useful one-offs
|
|
|
|
- First admin: `pnpm seed:admin` (prompts; blank username → `admin`). Non-interactive:
|
|
`ADMIN_USER=.. ADMIN_PASS=.. pnpm seed:admin`. Reset a password: add `FORCE=1`.
|
|
- Hardware test scripts (UHPPOTE): `apps/server/scripts/uhppote-listen.mjs` (live events),
|
|
`uhppote-relay.mjs` (guarded door-open). See [[uhppote-controller]].
|