diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..b2ea3e2670 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,37 @@ +# Keep the Docker build context small and reproducible. +# pnpm-lock.yaml + sources are needed; everything below is not. + +# Dependencies (reinstalled inside the image) +**/node_modules +.pnpm-store +**/.pnpm-store + +# Build outputs / caches +**/.next +**/out +**/dist +**/build +**/.turbo +**/coverage +**/*.tsbuildinfo + +# VCS / CI +.git +.github + +# Local env files — secrets are injected at runtime via docker-compose, never baked +**/.env +**/.env.* +!**/.env.example + +# Vercel +**/.vercel + +# Editor / OS noise +**/.DS_Store +.claude + +# Docker files themselves +**/Dockerfile +**/.dockerignore +docker-compose*.yml diff --git a/.gitignore b/.gitignore index cc730bcfcc..b75786e620 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ node_modules .env.development.local .env.test.local .env.production.local +.env.docker .claude # Testing diff --git a/apps/cms/.env.docker.example b/apps/cms/.env.docker.example new file mode 100644 index 0000000000..26d6dd9584 --- /dev/null +++ b/apps/cms/.env.docker.example @@ -0,0 +1,41 @@ +# ============================================================================= +# Environment for docker-compose.prod.yml — copy to .env.docker at the repo +# root and fill in real values. NEVER commit the filled-in file. +# +# cp apps/cms/.env.docker.example .env.docker +# docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --build +# ============================================================================= + +# --- Self-hosted Postgres ---------------------------------------------------- +POSTGRES_USER=logos +POSTGRES_PASSWORD=change-me-strong-password +POSTGRES_DB=logos_cms + +# --- Payload core ------------------------------------------------------------ +# Generate with: openssl rand -hex 32 +PAYLOAD_SECRET=change-me-openssl-rand-hex-32 + +# --- Public URLs (BAKED INTO THE CLIENT BUNDLE AT BUILD TIME) ----------------- +# Must be the real public origins. Changing these requires a rebuild. +NEXT_PUBLIC_SERVER_URL=https://cms.logos.co +NEXT_PUBLIC_WEB_URL=https://logos.co + +# --- Host port mapping ------------------------------------------------------- +# Host port that maps to the container's :3000. Put a reverse proxy (TLS) in +# front of this in production. +CMS_PORT=3001 + +# --- Postgres tuning (optional — defaults shown) ----------------------------- +# PAYLOAD_DB_SCHEMA=payload +# PAYLOAD_DB_POOL_MAX=10 +# Schema auto-sync on boot. Keep "true" for the first deploy; flip to "false" +# once SQL migrations are wired up so prod no longer mutates schema on boot. +# PAYLOAD_DB_PUSH=true + +# --- GitHub "Create PR" workflow (optional admin feature) -------------------- +GITHUB_OWNER=logos-co +GITHUB_REPO=logos-web +GITHUB_APP_ID=123456 +GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +GITHUB_INSTALLATION_ID=12345678 +GITHUB_PR_BASE_BRANCH=develop diff --git a/apps/cms/Dockerfile b/apps/cms/Dockerfile new file mode 100644 index 0000000000..7eaa3e6f00 --- /dev/null +++ b/apps/cms/Dockerfile @@ -0,0 +1,95 @@ +# ============================================================================= +# Production image for apps/cms (Payload CMS 3.83 + Next.js 16) +# +# Build context MUST be the monorepo root so pnpm can resolve workspace +# packages (@repo/content). From the repo root: +# +# docker build -f apps/cms/Dockerfile \ +# --build-arg NEXT_PUBLIC_SERVER_URL=https://cms.example.com \ +# --build-arg NEXT_PUBLIC_WEB_URL=https://www.example.com \ +# -t logos-cms . +# +# NEXT_PUBLIC_* values are inlined into the client bundle at build time, so +# they must be the real public origins here — not at runtime. +# ============================================================================= + +FROM node:24-bookworm-slim AS base +ENV PNPM_HOME=/pnpm +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable +WORKDIR /app + +# ----------------------------------------------------------------------------- +# deps — install only what cms (and its workspace deps) need, with a frozen +# lockfile for reproducible builds. +# ----------------------------------------------------------------------------- +FROM base AS deps +# Copy the workspace manifest layer first for better layer caching. +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json .npmrc ./ +COPY apps/cms/package.json apps/cms/package.json +COPY packages/config/package.json packages/config/package.json +COPY packages/content/package.json packages/content/package.json +COPY packages/tokens/package.json packages/tokens/package.json +COPY packages/types/package.json packages/types/package.json +COPY packages/ui/package.json packages/ui/package.json +RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ + pnpm install --frozen-lockfile --filter cms... + +# ----------------------------------------------------------------------------- +# builder — copy sources and build the Next.js production output. +# ----------------------------------------------------------------------------- +FROM base AS builder +ARG NEXT_PUBLIC_SERVER_URL +ARG NEXT_PUBLIC_WEB_URL +# Build-time placeholders: payload.config.ts validates these env vars at module +# load (during `next build`). DATABASE_URL/PAYLOAD_SECRET are NOT inlined and +# are overridden with real values at runtime; the build never connects to the DB. +ENV NODE_ENV=production \ + NEXT_TELEMETRY_DISABLED=1 \ + NEXT_PUBLIC_SERVER_URL=${NEXT_PUBLIC_SERVER_URL} \ + NEXT_PUBLIC_WEB_URL=${NEXT_PUBLIC_WEB_URL} \ + DATABASE_URL=postgresql://build:build@localhost:5432/build \ + PAYLOAD_SECRET=build-time-placeholder-secret + +# Bring in the resolved node_modules from the deps stage, then layer the full +# monorepo sources on top. +COPY --from=deps /app/ ./ +COPY . . +RUN pnpm --filter cms build + +# ----------------------------------------------------------------------------- +# runner — production runtime. Runs `next start` via pnpm. +# ----------------------------------------------------------------------------- +FROM base AS runner +ENV NODE_ENV=production \ + NEXT_TELEMETRY_DISABLED=1 \ + PORT=3000 \ + HOSTNAME=0.0.0.0 + +# Run as an unprivileged user (with a home dir so any tool that needs a cache +# has a writable location). +RUN groupadd --system --gid 1001 nodejs \ + && useradd --system --create-home --uid 1001 --gid nodejs nextjs + +# Copy the fully built monorepo (sources + node_modules + .next output). +COPY --from=builder --chown=nextjs:nodejs /app ./ + +# Media uploads are written here at runtime; mount a volume on this path so +# uploaded files survive container restarts (see docker-compose). +RUN mkdir -p /app/apps/web/public/cms/uploads \ + && chown -R nextjs:nodejs /app/apps/web/public/cms/uploads \ + && chmod +x /app/apps/cms/docker-entrypoint.sh + +USER nextjs +WORKDIR /app/apps/cms +EXPOSE 3000 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD node -e "fetch('http://localhost:'+(process.env.PORT||3000)+'/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" + +# The entrypoint applies DB migrations, then execs the CMD below. `next start` +# runs via the workspace-local binary to avoid invoking pnpm/corepack at runtime +# (corepack would try to download a package manager into a non-writable cache +# and crash-loop the container). +ENTRYPOINT ["/app/apps/cms/docker-entrypoint.sh"] +CMD ["node_modules/.bin/next", "start"] diff --git a/apps/cms/README.md b/apps/cms/README.md new file mode 100644 index 0000000000..4fdedc8fce --- /dev/null +++ b/apps/cms/README.md @@ -0,0 +1,174 @@ +# Logos CMS + +Payload CMS 3.83 admin app on Next.js 16. Admin UI lives at `/admin`, the app runs on port **3001**. + +Postgres is **not** the production source of truth for content — it stores users, sessions, drafts, and PR cache. Published content changes flow through the GitHub "Create PR" workflow targeting `develop`. See [`AGENTS.md`](./AGENTS.md) for the content-workflow rules. + +--- + +## Prerequisites + +- Node.js `>=24 <25` +- pnpm `11.1.0` (via Corepack: `corepack enable`) +- A reachable Postgres instance (local, Supabase, or the bundled Docker Postgres) +- For Docker deploys: Docker + Docker Compose + +All commands run **from the monorepo root** so pnpm can resolve workspace packages (`@repo/content`, etc.). + +--- + +## Development + +Local development runs `next dev` directly against your own Postgres, with Payload schema auto-sync enabled. + +### 1. Configure environment + +```bash +cp apps/cms/.env.example apps/cms/.env +``` + +Fill in at minimum: + +| Variable | Purpose | +| --- | --- | +| `PAYLOAD_SECRET` | Required to boot. Generate with `openssl rand -hex 32`. | +| `DATABASE_URL` | Postgres connection string. | +| `NEXT_PUBLIC_SERVER_URL` | CMS origin — defaults to `http://localhost:3001`. | +| `NEXT_PUBLIC_WEB_URL` | Web frontend origin — defaults to `http://localhost:3000`. | + +The `GITHUB_*` variables are only required to exercise the Admin "Create PR" action. See [`.env.example`](./.env.example) for every option and its default. + +> Isolate dev data from other environments by setting `PAYLOAD_DB_SCHEMA=payload_dev` when sharing one Postgres database. + +### 2. Run + +```bash +pnpm --filter cms dev # from the repo root +# or +pnpm dev:cms # turbo wrapper (also builds workspace deps) +``` + +The dev wrapper (`scripts/dev.ts`) starts Next on port 3001 and auto-accepts Payload's interactive schema-push prompt. It **refuses to run in deployment environments** — production/staging must use the build/start path with reviewed migrations, never local dev schema sync. + +### Common tasks + +```bash +pnpm --filter cms lint # eslint, zero warnings +pnpm --filter cms check-types # payload typegen + tsc --noEmit +pnpm --filter cms test # node:test suite +pnpm --filter cms generate-types # regenerate Payload types after schema changes +pnpm --filter cms generate-import-map +``` + +Run `generate-types` after any Payload collection or schema change that affects generated types. + +--- + +## Production deployment (Docker) + +The production image bundles the built monorepo and runs `next start`. The build context **must be the monorepo root** so pnpm can resolve workspace packages. + +### Option A — Docker Compose (recommended) + +Brings up the CMS plus a self-hosted Postgres on an internal network. From the repo root: + +```bash +# 1. Create the env file and fill in real values +cp apps/cms/.env.docker.example .env.docker + +# 2. Build and start +docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --build +``` + +Required values in `.env.docker`: + +| Variable | Notes | +| --- | --- | +| `POSTGRES_USER` / `POSTGRES_PASSWORD` / `POSTGRES_DB` | Credentials for the bundled Postgres. | +| `PAYLOAD_SECRET` | `openssl rand -hex 32`. | +| `NEXT_PUBLIC_SERVER_URL` | **Baked into the client bundle at build time** — must be the real public CMS origin. Changing it requires a rebuild. | +| `NEXT_PUBLIC_WEB_URL` | **Build-time** public web origin (same caveat). | +| `CMS_PORT` | Host port mapped to the container's `:3000` (default `3001`). | + +The compose file constructs `DATABASE_URL` automatically from the Postgres vars and connects over the internal network — Postgres is **not** published to the host by default. + +What's persisted: + +- **`pgdata`** volume — Postgres data +- **`cms_uploads`** volume — uploaded media (`/app/apps/web/public/cms/uploads`) + +The container entrypoint applies database migrations before the server starts, so the schema is ready on first boot — no manual step needed. See [Database migrations](#database-migrations) below for how this works and how to add a migration. + +Put a reverse proxy (TLS termination) in front of `CMS_PORT` in production. + +### Option B — standalone image + +Build and run the image against an external Postgres (e.g. Supabase). Build from the repo root: + +```bash +docker build -f apps/cms/Dockerfile \ + --build-arg NEXT_PUBLIC_SERVER_URL=https://cms.logos.co \ + --build-arg NEXT_PUBLIC_WEB_URL=https://logos.co \ + -t logos-cms . +``` + +Run, injecting runtime secrets: + +```bash +docker run -d --name logos-cms -p 3001:3000 \ + -e DATABASE_URL='postgresql://user:password@host:5432/db' \ + -e PAYLOAD_SECRET='' \ + -e NEXT_PUBLIC_SERVER_URL='https://cms.logos.co' \ + -e NEXT_PUBLIC_WEB_URL='https://logos.co' \ + -v cms_uploads:/app/apps/web/public/cms/uploads \ + logos-cms +``` + +> `NEXT_PUBLIC_*` are inlined into the client bundle at **build time**, so they must be the real public origins passed as `--build-arg`. `DATABASE_URL` and `PAYLOAD_SECRET` are **not** baked in — they're supplied at runtime, and the build never connects to the database. + +### Build vs. runtime variables + +| Variable | When it's read | Notes | +| --- | --- | --- | +| `NEXT_PUBLIC_SERVER_URL`, `NEXT_PUBLIC_WEB_URL` | **Build time** (inlined into client bundle) | Must be real public origins; changing requires a rebuild. | +| `DATABASE_URL`, `PAYLOAD_SECRET` | **Runtime** | Never baked into the image. | +| `PAYLOAD_DB_SCHEMA`, `PAYLOAD_DB_POOL_MAX` | Runtime | Defaults: `payload`, `10`. | +| `PAYLOAD_DB_PUSH` | Runtime | Only affects **local dev** (`NODE_ENV !== production`). Ignored in production — see [Database migrations](#database-migrations). | +| `GITHUB_*` | Runtime | Optional — only for the Admin "Create PR" action. | + +### Database migrations + +Payload **never** auto-syncs the schema (`push`) when `NODE_ENV=production` — that includes both self-hosted Docker and Vercel. Production schema is owned entirely by the reviewed SQL migrations in [`src/migrations/`](./src/migrations/). + +**How it runs in Docker:** the container's [entrypoint](./docker-entrypoint.sh) runs `payload migrate` before `next start`. Migrations are idempotent — already-applied ones (tracked in the `payload-migrations` table) are skipped — so a fresh database is set up on first boot and restarts are no-ops. + +**Local dev** uses schema auto-push instead (`PAYLOAD_DB_PUSH` defaults on; set `PAYLOAD_DB_PUSH=false` to disable), so you normally don't run migrations during development. + +**When you change a collection or schema**, generate a migration and commit it alongside the code: + +```bash +pnpm --filter cms migrate:create # generate a new migration from current schema +pnpm --filter cms migrate:status # list applied / pending migrations +pnpm --filter cms migrate # apply pending migrations (run against the target DB) +``` + +The generated file under `src/migrations/` and the updated `src/migrations/index.ts` **must be committed** — production applies exactly what's in that directory. + +### Health check + +The image exposes a health endpoint used by Docker's `HEALTHCHECK`: + +``` +GET /api/health +``` + +--- + +## Environment file reference + +| File | Used by | Committed? | +| --- | --- | --- | +| [`.env.example`](./.env.example) | Local dev template → copy to `apps/cms/.env` | template only | +| [`.env.docker.example`](./.env.docker.example) | Docker Compose template → copy to repo-root `.env.docker` | template only | + +Never commit the filled-in `.env` / `.env.docker` files — secrets are injected at runtime, never baked into the image. diff --git a/apps/cms/docker-entrypoint.sh b/apps/cms/docker-entrypoint.sh new file mode 100644 index 0000000000..19faa8e5c0 --- /dev/null +++ b/apps/cms/docker-entrypoint.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Production container entrypoint. +# +# Payload does NOT push the schema in production (see @payloadcms/db-postgres +# connect.js: pushDevSchema only runs when NODE_ENV !== 'production'). The +# repo policy is migrations for any deployed environment, so we apply pending +# SQL migrations before the server starts accepting traffic. +# +# `payload migrate` is idempotent — already-applied migrations are skipped, so +# this is safe to run on every (re)start. +set -e + +echo "[entrypoint] Applying Payload migrations..." +PAYLOAD_CONFIG_PATH=payload.config.ts node_modules/.bin/payload migrate + +echo "[entrypoint] Migrations done. Starting server..." +exec "$@" diff --git a/apps/cms/package.json b/apps/cms/package.json index b8af081888..1d10f77fce 100644 --- a/apps/cms/package.json +++ b/apps/cms/package.json @@ -12,6 +12,9 @@ "check-types": "next typegen && tsc --noEmit", "generate-types": "PAYLOAD_CONFIG_PATH=payload.config.ts payload generate:types", "generate-import-map": "PAYLOAD_CONFIG_PATH=payload.config.ts payload generate:importmap", + "migrate": "PAYLOAD_CONFIG_PATH=payload.config.ts payload migrate", + "migrate:create": "PAYLOAD_CONFIG_PATH=payload.config.ts payload migrate:create", + "migrate:status": "PAYLOAD_CONFIG_PATH=payload.config.ts payload migrate:status", "save-as-pr-smoke": "tsx scripts/save-as-pr-smoke.ts", "sync-from-content": "tsx scripts/sync-from-content.ts" }, diff --git a/apps/cms/src/collections/Rfps.ts b/apps/cms/src/collections/Rfps.ts index 197c5d887f..85aff15afb 100644 --- a/apps/cms/src/collections/Rfps.ts +++ b/apps/cms/src/collections/Rfps.ts @@ -41,8 +41,8 @@ import { export const Rfps: CollectionConfig = { slug: 'rfps', labels: { - plural: 'RFSs', - singular: 'RFS', + plural: 'RFPs', + singular: 'RFP', }, admin: { components: recentPrAdminComponents, diff --git a/apps/cms/src/migrations/20260528_233905_initial.json b/apps/cms/src/migrations/20260528_233905_initial.json new file mode 100644 index 0000000000..7dca704210 --- /dev/null +++ b/apps/cms/src/migrations/20260528_233905_initial.json @@ -0,0 +1,3493 @@ +{ + "version": "7", + "dialect": "postgresql", + "tables": { + "payload.users_sessions": { + "name": "users_sessions", + "schema": "payload", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "users_sessions_order_idx": { + "name": "users_sessions_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_sessions_parent_id_idx": { + "name": "users_sessions_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "users_sessions_parent_id_fk": { + "name": "users_sessions_parent_id_fk", + "tableFrom": "users_sessions", + "tableTo": "users", + "schemaTo": "payload", + "columnsFrom": [ + "_parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.users": { + "name": "users", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "users_updated_at_idx": { + "name": "users_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_created_at_idx": { + "name": "users_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.media": { + "name": "media", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "public_path": { + "name": "public_path", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "thumbnail_u_r_l": { + "name": "thumbnail_u_r_l", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filesize": { + "name": "filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "focal_x": { + "name": "focal_x", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "focal_y": { + "name": "focal_y", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "media_updated_at_idx": { + "name": "media_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_created_at_idx": { + "name": "media_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "media_filename_idx": { + "name": "media_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.pages": { + "name": "pages", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "route": { + "name": "route", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "page": { + "name": "page", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "_status": { + "name": "_status", + "type": "enum_pages_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": false, + "default": "'draft'" + } + }, + "indexes": { + "pages_slug_idx": { + "name": "pages_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_route_idx": { + "name": "pages_route_idx", + "columns": [ + { + "expression": "route", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_updated_at_idx": { + "name": "pages_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages_created_at_idx": { + "name": "pages_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pages__status_idx": { + "name": "pages__status_idx", + "columns": [ + { + "expression": "_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload._pages_v": { + "name": "_pages_v", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "version_slug": { + "name": "version_slug", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "version_title": { + "name": "version_title", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "version_route": { + "name": "version_route", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "version_page": { + "name": "version_page", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "version_updated_at": { + "name": "version_updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "version_created_at": { + "name": "version_created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "version__status": { + "name": "version__status", + "type": "enum__pages_v_version_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": false, + "default": "'draft'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "latest": { + "name": "latest", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "_pages_v_parent_idx": { + "name": "_pages_v_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_version_version_slug_idx": { + "name": "_pages_v_version_version_slug_idx", + "columns": [ + { + "expression": "version_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_version_version_route_idx": { + "name": "_pages_v_version_version_route_idx", + "columns": [ + { + "expression": "version_route", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_version_version_updated_at_idx": { + "name": "_pages_v_version_version_updated_at_idx", + "columns": [ + { + "expression": "version_updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_version_version_created_at_idx": { + "name": "_pages_v_version_version_created_at_idx", + "columns": [ + { + "expression": "version_created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_version_version__status_idx": { + "name": "_pages_v_version_version__status_idx", + "columns": [ + { + "expression": "version__status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_created_at_idx": { + "name": "_pages_v_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_updated_at_idx": { + "name": "_pages_v_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "_pages_v_latest_idx": { + "name": "_pages_v_latest_idx", + "columns": [ + { + "expression": "latest", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "_pages_v_parent_id_pages_id_fk": { + "name": "_pages_v_parent_id_pages_id_fk", + "tableFrom": "_pages_v", + "tableTo": "pages", + "schemaTo": "payload", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.builder_hub_settings": { + "name": "builder_hub_settings", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_hub_settings_slug_idx": { + "name": "builder_hub_settings_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_hub_settings_updated_at_idx": { + "name": "builder_hub_settings_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_hub_settings_created_at_idx": { + "name": "builder_hub_settings_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.builder_listing_settings": { + "name": "builder_listing_settings", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "page": { + "name": "page", + "type": "enum_builder_listing_settings_page", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "breadcrumb_label": { + "name": "breadcrumb_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "submit_cta_label": { + "name": "submit_cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "submit_cta_href": { + "name": "submit_cta_href", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "submit_cta_external": { + "name": "submit_cta_external", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "default_view": { + "name": "default_view", + "type": "enum_builder_listing_settings_default_view", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'grid'" + }, + "page_size": { + "name": "page_size", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "previous_label": { + "name": "previous_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "next_label": { + "name": "next_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "bottom_cta_title": { + "name": "bottom_cta_title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "bottom_cta_label": { + "name": "bottom_cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "bottom_cta_href": { + "name": "bottom_cta_href", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "bottom_cta_external": { + "name": "bottom_cta_external", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_listing_settings_page_idx": { + "name": "builder_listing_settings_page_idx", + "columns": [ + { + "expression": "page", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_listing_settings_updated_at_idx": { + "name": "builder_listing_settings_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_listing_settings_created_at_idx": { + "name": "builder_listing_settings_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.site_settings_content": { + "name": "site_settings_content", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "settings": { + "name": "settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "site_settings_content_slug_idx": { + "name": "site_settings_content_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_settings_content_updated_at_idx": { + "name": "site_settings_content_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_settings_content_created_at_idx": { + "name": "site_settings_content_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.site_navigation_content": { + "name": "site_navigation_content", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "navigation": { + "name": "navigation", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "site_navigation_content_slug_idx": { + "name": "site_navigation_content_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_navigation_content_updated_at_idx": { + "name": "site_navigation_content_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_navigation_content_created_at_idx": { + "name": "site_navigation_content_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.site_footer_content": { + "name": "site_footer_content", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "footer": { + "name": "footer", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "site_footer_content_slug_idx": { + "name": "site_footer_content_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_footer_content_updated_at_idx": { + "name": "site_footer_content_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "site_footer_content_created_at_idx": { + "name": "site_footer_content_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.rfps": { + "name": "rfps", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_rfps_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "tagline": { + "name": "tagline", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reward_amount": { + "name": "reward_amount", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "reward_currency": { + "name": "reward_currency", + "type": "enum_rfps_reward_currency", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'USDC'" + }, + "reward_xp": { + "name": "reward_xp", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "apply_url": { + "name": "apply_url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "featured": { + "name": "featured", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "order": { + "name": "order", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "closes_at": { + "name": "closes_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "owner_name": { + "name": "owner_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "owner_handle": { + "name": "owner_handle", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rfps_slug_idx": { + "name": "rfps_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rfps_updated_at_idx": { + "name": "rfps_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "rfps_created_at_idx": { + "name": "rfps_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.rfps_texts": { + "name": "rfps_texts", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "rfps_texts_order_parent": { + "name": "rfps_texts_order_parent", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "rfps_texts_parent_fk": { + "name": "rfps_texts_parent_fk", + "tableFrom": "rfps_texts", + "tableTo": "rfps", + "schemaTo": "payload", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.ideas": { + "name": "ideas", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_ideas_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "tagline": { + "name": "tagline", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "summary": { + "name": "summary", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "submitter_name": { + "name": "submitter_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "submitter_handle": { + "name": "submitter_handle", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reward_amount": { + "name": "reward_amount", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "reward_currency": { + "name": "reward_currency", + "type": "enum_ideas_reward_currency", + "typeSchema": "payload", + "primaryKey": false, + "notNull": false, + "default": "'USDC'" + }, + "reward_xp": { + "name": "reward_xp", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "discussion_url": { + "name": "discussion_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "featured": { + "name": "featured", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "order": { + "name": "order", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "submitted_at": { + "name": "submitted_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ideas_slug_idx": { + "name": "ideas_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ideas_updated_at_idx": { + "name": "ideas_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "ideas_created_at_idx": { + "name": "ideas_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.ideas_texts": { + "name": "ideas_texts", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "ideas_texts_order_parent": { + "name": "ideas_texts_order_parent", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ideas_texts_parent_fk": { + "name": "ideas_texts_parent_fk", + "tableFrom": "ideas_texts", + "tableTo": "ideas", + "schemaTo": "payload", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.builder_resources": { + "name": "builder_resources", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_builder_resources_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "href": { + "name": "href", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "builder_resources_slug_idx": { + "name": "builder_resources_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_resources_updated_at_idx": { + "name": "builder_resources_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "builder_resources_created_at_idx": { + "name": "builder_resources_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.circles_organizers": { + "name": "circles_organizers", + "schema": "payload", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "handle": { + "name": "handle", + "type": "varchar", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "circles_organizers_order_idx": { + "name": "circles_organizers_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circles_organizers_parent_id_idx": { + "name": "circles_organizers_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "circles_organizers_parent_id_fk": { + "name": "circles_organizers_parent_id_fk", + "tableFrom": "circles_organizers", + "tableTo": "circles", + "schemaTo": "payload", + "columnsFrom": [ + "_parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.circles": { + "name": "circles", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_circles_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "city": { + "name": "city", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "country": { + "name": "country", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "lat": { + "name": "lat", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "lng": { + "name": "lng", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "member_count": { + "name": "member_count", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "discord_channel": { + "name": "discord_channel", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "discord_url": { + "name": "discord_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "forum_url": { + "name": "forum_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "join_url": { + "name": "join_url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "image_src": { + "name": "image_src", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "image_alt": { + "name": "image_alt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "image_width": { + "name": "image_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "image_height": { + "name": "image_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "order": { + "name": "order", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "circles_slug_idx": { + "name": "circles_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circles_updated_at_idx": { + "name": "circles_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circles_created_at_idx": { + "name": "circles_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.circle_events_hosted_by": { + "name": "circle_events_hosted_by", + "schema": "payload", + "columns": { + "_order": { + "name": "_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "_parent_id": { + "name": "_parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "id": { + "name": "id", + "type": "varchar", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "circle_events_hosted_by_order_idx": { + "name": "circle_events_hosted_by_order_idx", + "columns": [ + { + "expression": "_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_events_hosted_by_parent_id_idx": { + "name": "circle_events_hosted_by_parent_id_idx", + "columns": [ + { + "expression": "_parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "circle_events_hosted_by_parent_id_fk": { + "name": "circle_events_hosted_by_parent_id_fk", + "tableFrom": "circle_events_hosted_by", + "tableTo": "circle_events", + "schemaTo": "payload", + "columnsFrom": [ + "_parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.circle_events": { + "name": "circle_events", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_circle_events_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "circle_slug": { + "name": "circle_slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "location_label": { + "name": "location_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "timezone": { + "name": "timezone", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "venue_name": { + "name": "venue_name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "address": { + "name": "address", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "event_url": { + "name": "event_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "featured": { + "name": "featured", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "sequence_number": { + "name": "sequence_number", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "image_src": { + "name": "image_src", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "image_alt": { + "name": "image_alt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "image_width": { + "name": "image_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "image_height": { + "name": "image_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "circle_events_slug_idx": { + "name": "circle_events_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_events_circle_slug_idx": { + "name": "circle_events_circle_slug_idx", + "columns": [ + { + "expression": "circle_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_events_updated_at_idx": { + "name": "circle_events_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_events_created_at_idx": { + "name": "circle_events_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.circle_initiatives": { + "name": "circle_initiatives", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_circle_initiatives_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "circle_slug": { + "name": "circle_slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "href": { + "name": "href", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "location_label": { + "name": "location_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "image_src": { + "name": "image_src", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "image_alt": { + "name": "image_alt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "image_width": { + "name": "image_width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "image_height": { + "name": "image_height", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "featured": { + "name": "featured", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "order": { + "name": "order", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "circle_initiatives_slug_idx": { + "name": "circle_initiatives_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_initiatives_circle_slug_idx": { + "name": "circle_initiatives_circle_slug_idx", + "columns": [ + { + "expression": "circle_slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_initiatives_updated_at_idx": { + "name": "circle_initiatives_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_initiatives_created_at_idx": { + "name": "circle_initiatives_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.circle_resources": { + "name": "circle_resources", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_circle_resources_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "cta_label": { + "name": "cta_label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "href": { + "name": "href", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "circle_resources_slug_idx": { + "name": "circle_resources_slug_idx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_resources_updated_at_idx": { + "name": "circle_resources_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "circle_resources_created_at_idx": { + "name": "circle_resources_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.content_change_requests": { + "name": "content_change_requests", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "content_type": { + "name": "content_type", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "target_path": { + "name": "target_path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "branch_name": { + "name": "branch_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "pull_request_number": { + "name": "pull_request_number", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "pull_request_url": { + "name": "pull_request_url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "enum_content_change_requests_status", + "typeSchema": "payload", + "primaryKey": false, + "notNull": true, + "default": "'draft'" + }, + "commit_sha": { + "name": "commit_sha", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "content_change_requests_content_type_idx": { + "name": "content_change_requests_content_type_idx", + "columns": [ + { + "expression": "content_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_change_requests_target_path_idx": { + "name": "content_change_requests_target_path_idx", + "columns": [ + { + "expression": "target_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_change_requests_branch_name_idx": { + "name": "content_change_requests_branch_name_idx", + "columns": [ + { + "expression": "branch_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_change_requests_status_idx": { + "name": "content_change_requests_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_change_requests_created_by_idx": { + "name": "content_change_requests_created_by_idx", + "columns": [ + { + "expression": "created_by_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_change_requests_updated_at_idx": { + "name": "content_change_requests_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "content_change_requests_created_at_idx": { + "name": "content_change_requests_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "content_change_requests_created_by_id_users_id_fk": { + "name": "content_change_requests_created_by_id_users_id_fk", + "tableFrom": "content_change_requests", + "tableTo": "users", + "schemaTo": "payload", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.payload_kv": { + "name": "payload_kv", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "payload_kv_key_idx": { + "name": "payload_kv_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.payload_preferences": { + "name": "payload_preferences", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_preferences_key_idx": { + "name": "payload_preferences_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_updated_at_idx": { + "name": "payload_preferences_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_created_at_idx": { + "name": "payload_preferences_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.payload_preferences_rels": { + "name": "payload_preferences_rels", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "payload_preferences_rels_order_idx": { + "name": "payload_preferences_rels_order_idx", + "columns": [ + { + "expression": "order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_parent_idx": { + "name": "payload_preferences_rels_parent_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_path_idx": { + "name": "payload_preferences_rels_path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_preferences_rels_users_id_idx": { + "name": "payload_preferences_rels_users_id_idx", + "columns": [ + { + "expression": "users_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "payload_preferences_rels_parent_fk": { + "name": "payload_preferences_rels_parent_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "payload_preferences", + "schemaTo": "payload", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_users_fk": { + "name": "payload_preferences_rels_users_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "users", + "schemaTo": "payload", + "columnsFrom": [ + "users_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "payload.payload_migrations": { + "name": "payload_migrations", + "schema": "payload", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "batch": { + "name": "batch", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "payload_migrations_updated_at_idx": { + "name": "payload_migrations_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "payload_migrations_created_at_idx": { + "name": "payload_migrations_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "payload.enum_pages_status": { + "name": "enum_pages_status", + "schema": "payload", + "values": [ + "draft", + "published" + ] + }, + "payload.enum__pages_v_version_status": { + "name": "enum__pages_v_version_status", + "schema": "payload", + "values": [ + "draft", + "published" + ] + }, + "payload.enum_builder_listing_settings_page": { + "name": "enum_builder_listing_settings_page", + "schema": "payload", + "values": [ + "ideas", + "rfps" + ] + }, + "payload.enum_builder_listing_settings_default_view": { + "name": "enum_builder_listing_settings_default_view", + "schema": "payload", + "values": [ + "grid", + "list" + ] + }, + "payload.enum_rfps_status": { + "name": "enum_rfps_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_rfps_reward_currency": { + "name": "enum_rfps_reward_currency", + "schema": "payload", + "values": [ + "USDC" + ] + }, + "payload.enum_ideas_status": { + "name": "enum_ideas_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_ideas_reward_currency": { + "name": "enum_ideas_reward_currency", + "schema": "payload", + "values": [ + "USDC" + ] + }, + "payload.enum_builder_resources_status": { + "name": "enum_builder_resources_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_circles_status": { + "name": "enum_circles_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_circle_events_status": { + "name": "enum_circle_events_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_circle_initiatives_status": { + "name": "enum_circle_initiatives_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_circle_resources_status": { + "name": "enum_circle_resources_status", + "schema": "payload", + "values": [ + "draft", + "review", + "published", + "archived" + ] + }, + "payload.enum_content_change_requests_status": { + "name": "enum_content_change_requests_status", + "schema": "payload", + "values": [ + "draft", + "open", + "merged", + "closed" + ] + } + }, + "schemas": { + "payload": "payload" + }, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "id": "9a782bb5-f6f0-4ee6-a7ea-73929c769c57", + "prevId": "00000000-0000-0000-0000-000000000000" +} \ No newline at end of file diff --git a/apps/cms/src/migrations/20260528_233905_initial.ts b/apps/cms/src/migrations/20260528_233905_initial.ts new file mode 100644 index 0000000000..2dbaddc497 --- /dev/null +++ b/apps/cms/src/migrations/20260528_233905_initial.ts @@ -0,0 +1,492 @@ +import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres' + +export async function up({ db, payload, req }: MigrateUpArgs): Promise { + // Payload's `migrate` CLI does not auto-create a custom schema (the adapter + // uses schemaName: 'payload'). Without this, a fresh/self-hosted Postgres + // fails the first migration with: schema "payload" does not exist. + // Keep this line if the initial migration is ever regenerated. + await db.execute(sql`CREATE SCHEMA IF NOT EXISTS "payload";`) + + await db.execute(sql` + CREATE TYPE "payload"."enum_pages_status" AS ENUM('draft', 'published'); + CREATE TYPE "payload"."enum__pages_v_version_status" AS ENUM('draft', 'published'); + CREATE TYPE "payload"."enum_builder_listing_settings_page" AS ENUM('ideas', 'rfps'); + CREATE TYPE "payload"."enum_builder_listing_settings_default_view" AS ENUM('grid', 'list'); + CREATE TYPE "payload"."enum_rfps_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_rfps_reward_currency" AS ENUM('USDC'); + CREATE TYPE "payload"."enum_ideas_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_ideas_reward_currency" AS ENUM('USDC'); + CREATE TYPE "payload"."enum_builder_resources_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_circles_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_circle_events_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_circle_initiatives_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_circle_resources_status" AS ENUM('draft', 'review', 'published', 'archived'); + CREATE TYPE "payload"."enum_content_change_requests_status" AS ENUM('draft', 'open', 'merged', 'closed'); + CREATE TABLE "payload"."users_sessions" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "created_at" timestamp(3) with time zone, + "expires_at" timestamp(3) with time zone NOT NULL + ); + + CREATE TABLE "payload"."users" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "email" varchar NOT NULL, + "reset_password_token" varchar, + "reset_password_expiration" timestamp(3) with time zone, + "salt" varchar, + "hash" varchar, + "login_attempts" numeric DEFAULT 0, + "lock_until" timestamp(3) with time zone + ); + + CREATE TABLE "payload"."media" ( + "id" serial PRIMARY KEY NOT NULL, + "alt" varchar NOT NULL, + "public_path" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "url" varchar, + "thumbnail_u_r_l" varchar, + "filename" varchar, + "mime_type" varchar, + "filesize" numeric, + "width" numeric, + "height" numeric, + "focal_x" numeric, + "focal_y" numeric + ); + + CREATE TABLE "payload"."pages" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar, + "title" varchar, + "route" varchar, + "page" jsonb, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "_status" "payload"."enum_pages_status" DEFAULT 'draft' + ); + + CREATE TABLE "payload"."_pages_v" ( + "id" serial PRIMARY KEY NOT NULL, + "parent_id" integer, + "version_slug" varchar, + "version_title" varchar, + "version_route" varchar, + "version_page" jsonb, + "version_updated_at" timestamp(3) with time zone, + "version_created_at" timestamp(3) with time zone, + "version__status" "payload"."enum__pages_v_version_status" DEFAULT 'draft', + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "latest" boolean + ); + + CREATE TABLE "payload"."builder_hub_settings" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "settings" jsonb NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."builder_listing_settings" ( + "id" serial PRIMARY KEY NOT NULL, + "page" "payload"."enum_builder_listing_settings_page" NOT NULL, + "title" varchar NOT NULL, + "description" varchar NOT NULL, + "breadcrumb_label" varchar NOT NULL, + "submit_cta_label" varchar NOT NULL, + "submit_cta_href" varchar NOT NULL, + "submit_cta_external" boolean DEFAULT false, + "default_view" "payload"."enum_builder_listing_settings_default_view" DEFAULT 'grid' NOT NULL, + "page_size" numeric NOT NULL, + "previous_label" varchar NOT NULL, + "next_label" varchar NOT NULL, + "bottom_cta_title" varchar NOT NULL, + "bottom_cta_label" varchar NOT NULL, + "bottom_cta_href" varchar NOT NULL, + "bottom_cta_external" boolean DEFAULT false, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."site_settings_content" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "settings" jsonb NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."site_navigation_content" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "navigation" jsonb NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."site_footer_content" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "footer" jsonb NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."rfps" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_rfps_status" DEFAULT 'draft' NOT NULL, + "title" varchar NOT NULL, + "tagline" varchar, + "summary" varchar NOT NULL, + "description" varchar NOT NULL, + "cta_label" varchar, + "reward_amount" numeric NOT NULL, + "reward_currency" "payload"."enum_rfps_reward_currency" DEFAULT 'USDC' NOT NULL, + "reward_xp" numeric, + "apply_url" varchar NOT NULL, + "featured" boolean DEFAULT false, + "order" numeric, + "published_at" timestamp(3) with time zone, + "closes_at" timestamp(3) with time zone, + "owner_name" varchar, + "owner_handle" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."rfps_texts" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer NOT NULL, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "text" varchar + ); + + CREATE TABLE "payload"."ideas" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_ideas_status" DEFAULT 'draft' NOT NULL, + "title" varchar NOT NULL, + "tagline" varchar, + "summary" varchar NOT NULL, + "description" varchar NOT NULL, + "cta_label" varchar, + "submitter_name" varchar, + "submitter_handle" varchar NOT NULL, + "reward_amount" numeric, + "reward_currency" "payload"."enum_ideas_reward_currency" DEFAULT 'USDC', + "reward_xp" numeric, + "discussion_url" varchar, + "featured" boolean DEFAULT false, + "order" numeric, + "submitted_at" timestamp(3) with time zone, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."ideas_texts" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer NOT NULL, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "text" varchar + ); + + CREATE TABLE "payload"."builder_resources" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_builder_resources_status" DEFAULT 'draft' NOT NULL, + "title" varchar NOT NULL, + "description" varchar NOT NULL, + "cta_label" varchar NOT NULL, + "href" varchar NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."circles_organizers" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "name" varchar NOT NULL, + "handle" varchar + ); + + CREATE TABLE "payload"."circles" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_circles_status" DEFAULT 'draft' NOT NULL, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "city" varchar NOT NULL, + "country" varchar NOT NULL, + "region" varchar, + "lat" numeric NOT NULL, + "lng" numeric NOT NULL, + "timezone" varchar NOT NULL, + "member_count" numeric, + "discord_channel" varchar, + "discord_url" varchar, + "forum_url" varchar, + "join_url" varchar NOT NULL, + "image_src" varchar, + "image_alt" varchar, + "image_width" numeric, + "image_height" numeric, + "order" numeric, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."circle_events_hosted_by" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "name" varchar NOT NULL + ); + + CREATE TABLE "payload"."circle_events" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_circle_events_status" DEFAULT 'draft' NOT NULL, + "circle_slug" varchar NOT NULL, + "title" varchar NOT NULL, + "location_label" varchar NOT NULL, + "starts_at" timestamp(3) with time zone NOT NULL, + "ends_at" timestamp(3) with time zone, + "timezone" varchar NOT NULL, + "venue_name" varchar, + "address" varchar, + "event_url" varchar, + "featured" boolean DEFAULT false, + "sequence_number" numeric, + "image_src" varchar, + "image_alt" varchar, + "image_width" numeric, + "image_height" numeric, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."circle_initiatives" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_circle_initiatives_status" DEFAULT 'draft' NOT NULL, + "circle_slug" varchar NOT NULL, + "href" varchar NOT NULL, + "location_label" varchar NOT NULL, + "title" varchar NOT NULL, + "description" varchar NOT NULL, + "cta_label" varchar NOT NULL, + "image_src" varchar, + "image_alt" varchar, + "image_width" numeric, + "image_height" numeric, + "featured" boolean DEFAULT false, + "order" numeric, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."circle_resources" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "status" "payload"."enum_circle_resources_status" DEFAULT 'draft' NOT NULL, + "title" varchar NOT NULL, + "description" varchar NOT NULL, + "cta_label" varchar NOT NULL, + "href" varchar NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."content_change_requests" ( + "id" serial PRIMARY KEY NOT NULL, + "content_type" varchar NOT NULL, + "target_path" varchar NOT NULL, + "branch_name" varchar NOT NULL, + "pull_request_number" numeric, + "pull_request_url" varchar, + "status" "payload"."enum_content_change_requests_status" DEFAULT 'draft' NOT NULL, + "commit_sha" varchar, + "created_by_id" integer, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."payload_kv" ( + "id" serial PRIMARY KEY NOT NULL, + "key" varchar NOT NULL, + "data" jsonb NOT NULL + ); + + CREATE TABLE "payload"."payload_preferences" ( + "id" serial PRIMARY KEY NOT NULL, + "key" varchar, + "value" jsonb, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + CREATE TABLE "payload"."payload_preferences_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "users_id" integer + ); + + CREATE TABLE "payload"."payload_migrations" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "batch" numeric, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL + ); + + ALTER TABLE "payload"."users_sessions" ADD CONSTRAINT "users_sessions_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "payload"."users"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload"."_pages_v" ADD CONSTRAINT "_pages_v_parent_id_pages_id_fk" FOREIGN KEY ("parent_id") REFERENCES "payload"."pages"("id") ON DELETE set null ON UPDATE no action; + ALTER TABLE "payload"."rfps_texts" ADD CONSTRAINT "rfps_texts_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "payload"."rfps"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload"."ideas_texts" ADD CONSTRAINT "ideas_texts_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "payload"."ideas"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload"."circles_organizers" ADD CONSTRAINT "circles_organizers_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "payload"."circles"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload"."circle_events_hosted_by" ADD CONSTRAINT "circle_events_hosted_by_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "payload"."circle_events"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload"."content_change_requests" ADD CONSTRAINT "content_change_requests_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "payload"."users"("id") ON DELETE set null ON UPDATE no action; + ALTER TABLE "payload"."payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "payload"."payload_preferences"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "payload"."payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "payload"."users"("id") ON DELETE cascade ON UPDATE no action; + CREATE INDEX "users_sessions_order_idx" ON "payload"."users_sessions" USING btree ("_order"); + CREATE INDEX "users_sessions_parent_id_idx" ON "payload"."users_sessions" USING btree ("_parent_id"); + CREATE INDEX "users_updated_at_idx" ON "payload"."users" USING btree ("updated_at"); + CREATE INDEX "users_created_at_idx" ON "payload"."users" USING btree ("created_at"); + CREATE UNIQUE INDEX "users_email_idx" ON "payload"."users" USING btree ("email"); + CREATE INDEX "media_updated_at_idx" ON "payload"."media" USING btree ("updated_at"); + CREATE INDEX "media_created_at_idx" ON "payload"."media" USING btree ("created_at"); + CREATE UNIQUE INDEX "media_filename_idx" ON "payload"."media" USING btree ("filename"); + CREATE UNIQUE INDEX "pages_slug_idx" ON "payload"."pages" USING btree ("slug"); + CREATE UNIQUE INDEX "pages_route_idx" ON "payload"."pages" USING btree ("route"); + CREATE INDEX "pages_updated_at_idx" ON "payload"."pages" USING btree ("updated_at"); + CREATE INDEX "pages_created_at_idx" ON "payload"."pages" USING btree ("created_at"); + CREATE INDEX "pages__status_idx" ON "payload"."pages" USING btree ("_status"); + CREATE INDEX "_pages_v_parent_idx" ON "payload"."_pages_v" USING btree ("parent_id"); + CREATE INDEX "_pages_v_version_version_slug_idx" ON "payload"."_pages_v" USING btree ("version_slug"); + CREATE INDEX "_pages_v_version_version_route_idx" ON "payload"."_pages_v" USING btree ("version_route"); + CREATE INDEX "_pages_v_version_version_updated_at_idx" ON "payload"."_pages_v" USING btree ("version_updated_at"); + CREATE INDEX "_pages_v_version_version_created_at_idx" ON "payload"."_pages_v" USING btree ("version_created_at"); + CREATE INDEX "_pages_v_version_version__status_idx" ON "payload"."_pages_v" USING btree ("version__status"); + CREATE INDEX "_pages_v_created_at_idx" ON "payload"."_pages_v" USING btree ("created_at"); + CREATE INDEX "_pages_v_updated_at_idx" ON "payload"."_pages_v" USING btree ("updated_at"); + CREATE INDEX "_pages_v_latest_idx" ON "payload"."_pages_v" USING btree ("latest"); + CREATE UNIQUE INDEX "builder_hub_settings_slug_idx" ON "payload"."builder_hub_settings" USING btree ("slug"); + CREATE INDEX "builder_hub_settings_updated_at_idx" ON "payload"."builder_hub_settings" USING btree ("updated_at"); + CREATE INDEX "builder_hub_settings_created_at_idx" ON "payload"."builder_hub_settings" USING btree ("created_at"); + CREATE UNIQUE INDEX "builder_listing_settings_page_idx" ON "payload"."builder_listing_settings" USING btree ("page"); + CREATE INDEX "builder_listing_settings_updated_at_idx" ON "payload"."builder_listing_settings" USING btree ("updated_at"); + CREATE INDEX "builder_listing_settings_created_at_idx" ON "payload"."builder_listing_settings" USING btree ("created_at"); + CREATE UNIQUE INDEX "site_settings_content_slug_idx" ON "payload"."site_settings_content" USING btree ("slug"); + CREATE INDEX "site_settings_content_updated_at_idx" ON "payload"."site_settings_content" USING btree ("updated_at"); + CREATE INDEX "site_settings_content_created_at_idx" ON "payload"."site_settings_content" USING btree ("created_at"); + CREATE UNIQUE INDEX "site_navigation_content_slug_idx" ON "payload"."site_navigation_content" USING btree ("slug"); + CREATE INDEX "site_navigation_content_updated_at_idx" ON "payload"."site_navigation_content" USING btree ("updated_at"); + CREATE INDEX "site_navigation_content_created_at_idx" ON "payload"."site_navigation_content" USING btree ("created_at"); + CREATE UNIQUE INDEX "site_footer_content_slug_idx" ON "payload"."site_footer_content" USING btree ("slug"); + CREATE INDEX "site_footer_content_updated_at_idx" ON "payload"."site_footer_content" USING btree ("updated_at"); + CREATE INDEX "site_footer_content_created_at_idx" ON "payload"."site_footer_content" USING btree ("created_at"); + CREATE UNIQUE INDEX "rfps_slug_idx" ON "payload"."rfps" USING btree ("slug"); + CREATE INDEX "rfps_updated_at_idx" ON "payload"."rfps" USING btree ("updated_at"); + CREATE INDEX "rfps_created_at_idx" ON "payload"."rfps" USING btree ("created_at"); + CREATE INDEX "rfps_texts_order_parent" ON "payload"."rfps_texts" USING btree ("order","parent_id"); + CREATE UNIQUE INDEX "ideas_slug_idx" ON "payload"."ideas" USING btree ("slug"); + CREATE INDEX "ideas_updated_at_idx" ON "payload"."ideas" USING btree ("updated_at"); + CREATE INDEX "ideas_created_at_idx" ON "payload"."ideas" USING btree ("created_at"); + CREATE INDEX "ideas_texts_order_parent" ON "payload"."ideas_texts" USING btree ("order","parent_id"); + CREATE UNIQUE INDEX "builder_resources_slug_idx" ON "payload"."builder_resources" USING btree ("slug"); + CREATE INDEX "builder_resources_updated_at_idx" ON "payload"."builder_resources" USING btree ("updated_at"); + CREATE INDEX "builder_resources_created_at_idx" ON "payload"."builder_resources" USING btree ("created_at"); + CREATE INDEX "circles_organizers_order_idx" ON "payload"."circles_organizers" USING btree ("_order"); + CREATE INDEX "circles_organizers_parent_id_idx" ON "payload"."circles_organizers" USING btree ("_parent_id"); + CREATE UNIQUE INDEX "circles_slug_idx" ON "payload"."circles" USING btree ("slug"); + CREATE INDEX "circles_updated_at_idx" ON "payload"."circles" USING btree ("updated_at"); + CREATE INDEX "circles_created_at_idx" ON "payload"."circles" USING btree ("created_at"); + CREATE INDEX "circle_events_hosted_by_order_idx" ON "payload"."circle_events_hosted_by" USING btree ("_order"); + CREATE INDEX "circle_events_hosted_by_parent_id_idx" ON "payload"."circle_events_hosted_by" USING btree ("_parent_id"); + CREATE UNIQUE INDEX "circle_events_slug_idx" ON "payload"."circle_events" USING btree ("slug"); + CREATE INDEX "circle_events_circle_slug_idx" ON "payload"."circle_events" USING btree ("circle_slug"); + CREATE INDEX "circle_events_updated_at_idx" ON "payload"."circle_events" USING btree ("updated_at"); + CREATE INDEX "circle_events_created_at_idx" ON "payload"."circle_events" USING btree ("created_at"); + CREATE UNIQUE INDEX "circle_initiatives_slug_idx" ON "payload"."circle_initiatives" USING btree ("slug"); + CREATE INDEX "circle_initiatives_circle_slug_idx" ON "payload"."circle_initiatives" USING btree ("circle_slug"); + CREATE INDEX "circle_initiatives_updated_at_idx" ON "payload"."circle_initiatives" USING btree ("updated_at"); + CREATE INDEX "circle_initiatives_created_at_idx" ON "payload"."circle_initiatives" USING btree ("created_at"); + CREATE UNIQUE INDEX "circle_resources_slug_idx" ON "payload"."circle_resources" USING btree ("slug"); + CREATE INDEX "circle_resources_updated_at_idx" ON "payload"."circle_resources" USING btree ("updated_at"); + CREATE INDEX "circle_resources_created_at_idx" ON "payload"."circle_resources" USING btree ("created_at"); + CREATE INDEX "content_change_requests_content_type_idx" ON "payload"."content_change_requests" USING btree ("content_type"); + CREATE INDEX "content_change_requests_target_path_idx" ON "payload"."content_change_requests" USING btree ("target_path"); + CREATE UNIQUE INDEX "content_change_requests_branch_name_idx" ON "payload"."content_change_requests" USING btree ("branch_name"); + CREATE INDEX "content_change_requests_status_idx" ON "payload"."content_change_requests" USING btree ("status"); + CREATE INDEX "content_change_requests_created_by_idx" ON "payload"."content_change_requests" USING btree ("created_by_id"); + CREATE INDEX "content_change_requests_updated_at_idx" ON "payload"."content_change_requests" USING btree ("updated_at"); + CREATE INDEX "content_change_requests_created_at_idx" ON "payload"."content_change_requests" USING btree ("created_at"); + CREATE UNIQUE INDEX "payload_kv_key_idx" ON "payload"."payload_kv" USING btree ("key"); + CREATE INDEX "payload_preferences_key_idx" ON "payload"."payload_preferences" USING btree ("key"); + CREATE INDEX "payload_preferences_updated_at_idx" ON "payload"."payload_preferences" USING btree ("updated_at"); + CREATE INDEX "payload_preferences_created_at_idx" ON "payload"."payload_preferences" USING btree ("created_at"); + CREATE INDEX "payload_preferences_rels_order_idx" ON "payload"."payload_preferences_rels" USING btree ("order"); + CREATE INDEX "payload_preferences_rels_parent_idx" ON "payload"."payload_preferences_rels" USING btree ("parent_id"); + CREATE INDEX "payload_preferences_rels_path_idx" ON "payload"."payload_preferences_rels" USING btree ("path"); + CREATE INDEX "payload_preferences_rels_users_id_idx" ON "payload"."payload_preferences_rels" USING btree ("users_id"); + CREATE INDEX "payload_migrations_updated_at_idx" ON "payload"."payload_migrations" USING btree ("updated_at"); + CREATE INDEX "payload_migrations_created_at_idx" ON "payload"."payload_migrations" USING btree ("created_at");`) +} + +export async function down({ db, payload, req }: MigrateDownArgs): Promise { + await db.execute(sql` + DROP TABLE "payload"."users_sessions" CASCADE; + DROP TABLE "payload"."users" CASCADE; + DROP TABLE "payload"."media" CASCADE; + DROP TABLE "payload"."pages" CASCADE; + DROP TABLE "payload"."_pages_v" CASCADE; + DROP TABLE "payload"."builder_hub_settings" CASCADE; + DROP TABLE "payload"."builder_listing_settings" CASCADE; + DROP TABLE "payload"."site_settings_content" CASCADE; + DROP TABLE "payload"."site_navigation_content" CASCADE; + DROP TABLE "payload"."site_footer_content" CASCADE; + DROP TABLE "payload"."rfps" CASCADE; + DROP TABLE "payload"."rfps_texts" CASCADE; + DROP TABLE "payload"."ideas" CASCADE; + DROP TABLE "payload"."ideas_texts" CASCADE; + DROP TABLE "payload"."builder_resources" CASCADE; + DROP TABLE "payload"."circles_organizers" CASCADE; + DROP TABLE "payload"."circles" CASCADE; + DROP TABLE "payload"."circle_events_hosted_by" CASCADE; + DROP TABLE "payload"."circle_events" CASCADE; + DROP TABLE "payload"."circle_initiatives" CASCADE; + DROP TABLE "payload"."circle_resources" CASCADE; + DROP TABLE "payload"."content_change_requests" CASCADE; + DROP TABLE "payload"."payload_kv" CASCADE; + DROP TABLE "payload"."payload_preferences" CASCADE; + DROP TABLE "payload"."payload_preferences_rels" CASCADE; + DROP TABLE "payload"."payload_migrations" CASCADE; + DROP TYPE "payload"."enum_pages_status"; + DROP TYPE "payload"."enum__pages_v_version_status"; + DROP TYPE "payload"."enum_builder_listing_settings_page"; + DROP TYPE "payload"."enum_builder_listing_settings_default_view"; + DROP TYPE "payload"."enum_rfps_status"; + DROP TYPE "payload"."enum_rfps_reward_currency"; + DROP TYPE "payload"."enum_ideas_status"; + DROP TYPE "payload"."enum_ideas_reward_currency"; + DROP TYPE "payload"."enum_builder_resources_status"; + DROP TYPE "payload"."enum_circles_status"; + DROP TYPE "payload"."enum_circle_events_status"; + DROP TYPE "payload"."enum_circle_initiatives_status"; + DROP TYPE "payload"."enum_circle_resources_status"; + DROP TYPE "payload"."enum_content_change_requests_status";`) +} diff --git a/apps/cms/src/migrations/index.ts b/apps/cms/src/migrations/index.ts new file mode 100644 index 0000000000..9859dd9755 --- /dev/null +++ b/apps/cms/src/migrations/index.ts @@ -0,0 +1,9 @@ +import * as migration_20260528_233905_initial from './20260528_233905_initial'; + +export const migrations = [ + { + up: migration_20260528_233905_initial.up, + down: migration_20260528_233905_initial.down, + name: '20260528_233905_initial' + }, +]; diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000000..c441e22873 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,72 @@ +# ============================================================================= +# Production deployment for apps/cms with a self-hosted Postgres. +# +# Usage: +# 1. cp apps/cms/.env.docker.example .env.docker # then fill in real values +# 2. docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --build +# +# The container entrypoint applies Payload SQL migrations before starting, so +# the schema is set up on first boot — no manual migration step is needed. +# (In production Payload never schema-pushes; PAYLOAD_DB_PUSH only affects dev.) +# ============================================================================= + +services: + postgres: + image: postgres:17-bookworm + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + # Not published to the host — only the cms service reaches it over the + # internal network. Uncomment to expose for external admin access. + # ports: + # - "5432:5432" + + cms: + build: + context: . + dockerfile: apps/cms/Dockerfile + args: + # Baked into the client bundle at build time — must be the real origins. + NEXT_PUBLIC_SERVER_URL: ${NEXT_PUBLIC_SERVER_URL} + NEXT_PUBLIC_WEB_URL: ${NEXT_PUBLIC_WEB_URL} + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + environment: + NODE_ENV: production + PORT: 3000 + # Connects to the self-hosted Postgres over the compose network. + DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB} + PAYLOAD_SECRET: ${PAYLOAD_SECRET} + NEXT_PUBLIC_SERVER_URL: ${NEXT_PUBLIC_SERVER_URL} + NEXT_PUBLIC_WEB_URL: ${NEXT_PUBLIC_WEB_URL} + PAYLOAD_DB_SCHEMA: ${PAYLOAD_DB_SCHEMA:-payload} + PAYLOAD_DB_PUSH: ${PAYLOAD_DB_PUSH:-true} + PAYLOAD_DB_POOL_MAX: ${PAYLOAD_DB_POOL_MAX:-10} + # GitHub "Create PR" workflow (optional — admin feature). + GITHUB_OWNER: ${GITHUB_OWNER:-} + GITHUB_REPO: ${GITHUB_REPO:-} + GITHUB_APP_ID: ${GITHUB_APP_ID:-} + GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-} + GITHUB_INSTALLATION_ID: ${GITHUB_INSTALLATION_ID:-} + GITHUB_PR_BASE_BRANCH: ${GITHUB_PR_BASE_BRANCH:-develop} + ports: + - "${CMS_PORT:-3001}:3000" + volumes: + # Persist uploaded media (Payload writes to apps/web/public/cms/uploads). + - cms_uploads:/app/apps/web/public/cms/uploads + +volumes: + pgdata: + cms_uploads: