Go to file
Julio Ruiz 865f88b49c Initial commi 2026-08-08 16:10:46 -05:00
apps Initial commi 2026-08-08 16:10:46 -05:00
packages Initial commi 2026-08-08 16:00:50 -05:00
.env.example Initial commi 2026-08-08 16:00:50 -05:00
.gitignore Initial commi 2026-08-08 16:10:46 -05:00
.npmrc Initial commi 2026-08-08 16:00:50 -05:00
README.md Initial commi 2026-08-08 16:00:50 -05:00
_carpa_sync.zip Initial commi 2026-08-08 16:00:50 -05:00
docker-compose.yml Initial commi 2026-08-08 16:00:50 -05:00
package.json Initial commi 2026-08-08 16:00:50 -05:00
pnpm-lock.yaml Initial commi 2026-08-08 16:00:50 -05:00
pnpm-workspace.yaml Initial commi 2026-08-08 16:00:50 -05:00
portless.json Initial commi 2026-08-08 16:00:50 -05:00
turbo.json Initial commi 2026-08-08 16:00:50 -05:00

README.md

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

# 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. 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.

# 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:

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.
  • Bulkpnpm 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.