carpa_turborepo/README.md

150 lines
5.6 KiB
Markdown

# Carpa — Turborepo
A multilingual content platform built as a Turborepo monorepo:
- **Payload CMS** backend on SQLite, multilingual (**Spanish default** + English, French, Portuguese)
- **Typesense** for faceted search, filtering and full-text search
- **Astro** frontend (Tailwind v4 + DaisyUI) — the first of three parallel frontends
- Shared **design-system** package (DaisyUI + React) documented in **Storybook**
> Astro is live in this pass. Two more parallel frontends are planned on the same
> backend + Typesense foundation: **Nuxt** (NuxtUI) and **Next.js** (DaisyUI).
## Layout
```
carpa_turborepo/
├── apps/
│ ├── cms/ # Payload 3 + Next.js 16, SQLite, es/en/fr/pt localization
│ └── web-astro/ # Astro 7 SSR, Tailwind v4 + DaisyUI, Typesense search
├── packages/
│ ├── typesense/ # shared client, collection schema, mappers + indexer CLI
│ ├── ui/ # shared DaisyUI/React design system + Storybook
│ ├── tailwind-config/# shared Carpa DaisyUI theme (carpa / carpa-dark)
│ ├── tsconfig/ # shared TS base configs
│ └── eslint-config/ # shared flat ESLint config
├── docker-compose.yml # Typesense service
├── turbo.json
└── pnpm-workspace.yaml
```
## Prerequisites
- **Node ≥ 20.9**, **pnpm 10** (`corepack enable`)
- **Docker** (for Typesense) — or a Typesense server binary
## Quick start
```bash
# 1. Install
pnpm install
# 2. Environment — copy the examples and set secrets
cp .env.example .env
cp apps/cms/.env.example apps/cms/.env
cp apps/web-astro/.env.example apps/web-astro/.env
# In apps/cms/.env set a real PAYLOAD_SECRET: openssl rand -hex 32
# 3. Start Typesense
pnpm typesense:up
# 4. Start the CMS (creates the SQLite schema on first boot)
pnpm --filter @carpa/cms dev # http://localhost:3000/admin
# 5. (optional) Seed multilingual demo content
pnpm --filter @carpa/cms seed # admin@carpa.test / changeme123
# 6. Create the browser search-only key, then build the search index
pnpm typesense:keys
pnpm typesense:index
# 7. Start the Astro frontend
pnpm --filter @carpa/web-astro dev # http://localhost:4321 (redirects to /es/)
```
Or run everything at once with `pnpm dev` (Turborepo).
## Pretty local URLs (Portless)
Instead of juggling `localhost:3000` / `:4321`, you can serve the apps behind
stable, HTTPS `.localhost` names with [Portless](https://portless.sh). It's
wired in as an overlay — each app stays pinned to its normal port
(`portless.json`), so the plain `localhost` workflow and the Typesense CLIs
keep working unchanged; Portless just adds nice URLs on top.
```bash
# From the repo root — starts the apps behind their URLs and trusts a local CA
pnpm portless
```
| URL | App |
| --- | --- |
| https://cms.carpa.localhost | Payload admin + REST |
| https://web.carpa.localhost | Astro frontend |
For the **in-browser search** to work over HTTPS (a browser blocks a page on
`https://…` from calling `http://localhost:8108`), expose Typesense over HTTPS
too and point the frontend at it:
```bash
pnpm portless:typesense # → https://typesense.carpa.localhost
```
then add the values from `apps/web-astro/.env.portless.example` to
`apps/web-astro/.env` and restart the Astro dev server. Server-side calls
(Astro SSR → Payload) can stay on `http://localhost:3000`.
> First run trusts a local CA system-wide and may prompt for your password.
> Portless also injects that CA into the dev servers it starts, so their
> Node processes trust the HTTPS URLs automatically.
## How the pieces connect
**Localization.** Locales live in one place — `packages/typesense/src/config.ts`
(`LOCALES`, `DEFAULT_LOCALE`). Payload's `localization` config, the search index,
and every frontend read from it, so adding a language is a one-line change.
**Search sync.** Content stays searchable two ways:
- **Live** — Payload `afterChange` / `afterDelete` hooks (`apps/cms/src/hooks/typesense.ts`)
upsert/remove documents across all locales as you edit. If Typesense is down the
hooks log a warning and the CMS keeps working.
- **Bulk** — `pnpm typesense:index` rebuilds the whole `carpa_content` collection
from Payload's REST API.
The Typesense collection is flat: one row per `(document, locale)`, with `locale`,
`type`, `categories` and `tags` as facets. The Astro UI filters by the active locale
and refines on the rest.
**Search-only key.** The browser uses a scoped `documents:search` key, never the
admin key. `pnpm typesense:keys` provisions it deterministically.
## Frontends
| App | Framework | UI library | Status |
| --- | --- | --- | --- |
| `web-astro` | Astro 7 (SSR) | DaisyUI | ✅ implemented |
| `web-nuxt` | Nuxt | NuxtUI | ⏳ planned |
| `web-next` | Next.js | DaisyUI | ⏳ planned |
All three consume the same Payload REST API and the same Typesense index.
## Useful scripts (root)
| Script | What it does |
| --- | --- |
| `pnpm dev` | Run all apps via Turborepo |
| `pnpm build` | Build everything |
| `pnpm typecheck` | Typecheck every workspace |
| `pnpm typesense:up` / `:down` | Start/stop Typesense (Docker) |
| `pnpm typesense:keys` | Create the browser search-only key |
| `pnpm typesense:index` | Full reindex from Payload into Typesense |
| `pnpm ui storybook` | Run the design-system Storybook (port 6006) |
## Notes
- **SQLite → Turso/libSQL:** the SQLite adapter uses a libSQL client, so pointing
`DATABASE_URI` at a Turso URL (with an auth token) is a drop-in for production.
- **Migrations:** dev uses Drizzle `push` (auto-sync). For production run
`pnpm --filter @carpa/cms migrate:create` then `migrate`.