cdrdpyj-turborepo/README.md

185 lines
7.4 KiB
Markdown

# cdrdpyj
A TypeScript **Turborepo** wiring an **Astro** frontend to a **Payload CMS**
backend, both running against a **self-hosted Supabase** stack (Postgres),
orchestrated from a single **docker-compose** file — with **[portless]** for
stable `*.localhost` dev URLs.
```
┌──────────────┐ REST /api ┌──────────────┐ SQL ┌───────────────────────────┐
│ Astro (web) │ ──────────────────────▶ │ Payload (cms)│ ─────────────────▶ │ Supabase (Postgres 17) │
│ SSR, :4321 │ │ Next, :3000 │ schema: payload │ db · studio · kong · │
│ web.localhost│ │ │ │ auth · rest · storage │
└──────────────┘ └──────────────┘ └───────────────────────────┘
▲ portless Studio dashboard :8000
```
- **apps/web** — Astro 5 (SSR via `@astrojs/node`). Reads posts from Payload's REST API.
- **apps/cms** — Payload 3 (on Next.js 16). Stores content in a dedicated `payload` schema in Postgres.
- **Supabase** — trimmed self-hosted stack: `db`, `studio`, `meta`, `kong`, `auth`, `rest`, `storage`, `imgproxy`.
- **packages/typescript-config** — shared `tsconfig` bases.
[portless]: https://portless.sh
---
## Prerequisites
| Tool | Version | Notes |
| ------- | ------- | ----- |
| Node | ≥ 24 | portless and Payload both require Node 24+ |
| pnpm | ≥ 9 | `corepack enable` or `npm i -g pnpm` |
| Docker | + Compose v2 | for the Supabase backend |
| portless| latest | `pnpm add -g portless` (optional but recommended) |
---
## Quick start
```bash
# 1. Configure — one env file drives the whole stack
cp .env.example .env
# 2. Install workspace deps
pnpm install
# 3. Start the Supabase backend (Postgres + Studio + Auth + REST + Storage)
docker compose up -d # first run pulls images; give it a minute
# 4. Run the apps (Astro + Payload) with hot reload
pnpm dev
```
Then open:
| What | URL |
| --------------- | ------------------------------------- |
| Astro frontend | http://localhost:4321 (or the portless URL below) |
| Payload admin | http://localhost:3000/admin |
| Supabase Studio | http://localhost:8000 (login: `supabase` / `this_password_is_insecure_and_should_be_updated`) |
| Postgres | `postgresql://postgres:postgres@localhost:5432/postgres` |
First time in Payload admin you'll create the initial admin user. Then add a
**Post** (give it a `slug`) and it appears on the Astro homepage.
---
## portless — stable `*.localhost` URLs
[portless] gives the frontend a memorable HTTPS URL instead of a port number,
and injects `$PORT` automatically.
```bash
pnpm add -g portless # once
pnpm dev # Astro is started via `portless web astro dev`
```
The web app is served at **https://web.localhost**. On first run portless
generates and trusts a local CA and binds to `:443` (may prompt for sudo).
> **Why only the frontend?** Payload stays on plain `http://localhost:3000` so
> Astro's server-side `fetch` to it uses plain HTTP — Node doesn't trust
> portless's local CA for server-to-server HTTPS. To also route Payload through
> portless (https://cms.localhost), run `pnpm --filter cms dev:portless` and set
> `NODE_EXTRA_CA_CERTS` to portless's CA so Astro can reach it over HTTPS.
Prefer no portless? Use `pnpm --filter web dev:plain` (plain `astro dev`).
---
## Everything in Docker (optional)
The apps also ship with Dockerfiles behind a compose `apps` profile:
```bash
docker compose --profile apps up -d --build
```
This runs Astro (`:4321`) and Payload (`:3000`) as containers alongside
Supabase. `DATABASE_URI` and the Astro→Payload URL are rewired to the internal
Docker network automatically. The recommended day-to-day loop is still
`docker compose up -d` (backend) + `pnpm dev` (apps) for hot reload.
---
## Scripts
Run from the repo root:
| Command | Does |
| ------------------------ | ---- |
| `pnpm dev` | Turbo runs both apps (Astro via portless, Payload plain) |
| `pnpm build` | Build all apps |
| `pnpm typecheck` | Type-check all apps |
| `pnpm supabase:up` / `:down` | Start / stop the Supabase backend |
| `pnpm supabase:logs` | Tail backend logs |
| `pnpm supabase:reset` | ⚠️ Stop and **delete** all Postgres data + volumes |
| `pnpm stack:up` / `:down`| Backend **and** apps in Docker (`apps` profile) |
Per-app: `pnpm --filter cms generate:types`, `pnpm --filter cms generate:importmap`.
---
## How configuration works
There is **one** `.env` at the repo root, shared by everything:
- **docker-compose** reads it natively.
- **Payload** loads it from the root in `apps/cms/src/payload.config.ts`.
- **Astro** loads it via `vite.envDir` pointed at the root; `PUBLIC_*` vars are
exposed to the browser.
The Supabase `ANON_KEY` / `SERVICE_ROLE_KEY` in `.env.example` are the standard
Supabase demo keys, signed with the matching demo `JWT_SECRET`. They work out of
the box for local dev. **Changing `JWT_SECRET` invalidates them** — regenerate
with Supabase's `generate-keys` steps if you do.
---
## Project structure
```
.
├── docker-compose.yml # single-file Supabase stack (+ apps profile)
├── .env.example # one source of truth for all config
├── turbo.json # task pipeline
├── apps/
│ ├── web/ # Astro SSR frontend
│ └── cms/ # Payload 3 (Next.js) CMS
├── packages/
│ └── typescript-config/ # shared tsconfig bases
└── supabase/
└── volumes/ # kong config + Postgres init SQL (roles, jwt, payload schema)
```
---
## Production notes
This scaffold is tuned for local development. Before deploying anywhere real:
- Regenerate **every** secret in `.env` (`POSTGRES_PASSWORD`, `JWT_SECRET` +
matching keys, `PAYLOAD_SECRET`, `DASHBOARD_PASSWORD`, `PG_META_CRYPTO_KEY`,
the S3 protocol keys).
- Put Kong / the apps behind TLS and real domains; set `SITE_URL`,
`API_EXTERNAL_URL`, `SUPABASE_PUBLIC_URL`, `PAYLOAD_PUBLIC_SERVER_URL`,
`PUBLIC_PAYLOAD_API_URL` accordingly.
- Switch Payload from dev "push" to committed migrations
(`pnpm --filter cms payload migrate:create`).
- Consider adding back the Supabase services this scaffold trims (Realtime,
Edge Functions, Supavisor pooler) if you need them — see
[Supabase self-hosting docs](https://supabase.com/docs/guides/self-hosting/docker).
---
## Troubleshooting
- **Astro homepage shows "Couldn't reach the CMS"** — Payload isn't running or
the DB is down. Ensure `docker compose up -d` is healthy and `pnpm dev` shows
the `cms` server on `:3000`.
- **Payload can't connect to Postgres** — the DB exposes `:5432`; confirm
`docker compose ps` shows `cdrdpyj-db` healthy and `DATABASE_URI` matches.
- **Ports already in use** — `5432`, `8000`, `3000`, `4321` must be free (or
change them in `.env` / the app dev scripts).
```