150 lines
5.6 KiB
Markdown
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`.
|