├── .npmrc
├── apps
├── cms
│ ├── src
│ │ ├── app
│ │ │ └── (payload)
│ │ │ │ ├── custom.scss
│ │ │ │ ├── api
│ │ │ │ ├── graphql
│ │ │ │ │ └── route.ts
│ │ │ │ ├── graphql-playground
│ │ │ │ │ └── route.ts
│ │ │ │ └── [...slug]
│ │ │ │ │ └── route.ts
│ │ │ │ ├── layout.tsx
│ │ │ │ └── admin
│ │ │ │ └── [[...segments]]
│ │ │ │ ├── page.tsx
│ │ │ │ └── not-found.tsx
│ │ ├── config
│ │ │ ├── collections
│ │ │ │ ├── Media.ts
│ │ │ │ └── Users.ts
│ │ │ ├── payload.config.ts
│ │ │ └── payload-types.ts
│ │ └── migrations
│ │ │ ├── 20240706_165421.ts
│ │ │ ├── 20240706_173841.ts
│ │ │ ├── 20240705_163535_init.ts
│ │ │ ├── 20240705_163535_init.json
│ │ │ ├── 20240706_165421.json
│ │ │ └── 20240706_173841.json
│ ├── .prettierrc.json
│ ├── .env.example
│ ├── .eslintrc.cjs
│ ├── next.config.mjs
│ ├── .gitignore
│ ├── .dockerignore
│ ├── tsconfig.json
│ ├── Dockerfile
│ ├── package.json
│ └── README.md
├── web
│ ├── .npmrc
│ ├── src
│ │ ├── routes
│ │ │ ├── +layout.ts
│ │ │ ├── +layout.svelte
│ │ │ ├── +page.server.ts
│ │ │ └── +page.svelte
│ │ ├── lib
│ │ │ ├── components
│ │ │ │ ├── ui
│ │ │ │ │ ├── sonner
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── sonner.svelte
│ │ │ │ │ ├── alert-dialog
│ │ │ │ │ │ ├── alert-dialog-portal.svelte
│ │ │ │ │ │ ├── alert-dialog-header.svelte
│ │ │ │ │ │ ├── alert-dialog-footer.svelte
│ │ │ │ │ │ ├── alert-dialog-description.svelte
│ │ │ │ │ │ ├── alert-dialog-title.svelte
│ │ │ │ │ │ ├── alert-dialog-action.svelte
│ │ │ │ │ │ ├── alert-dialog-cancel.svelte
│ │ │ │ │ │ ├── alert-dialog-overlay.svelte
│ │ │ │ │ │ ├── alert-dialog-content.svelte
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── card
│ │ │ │ │ │ ├── card-content.svelte
│ │ │ │ │ │ ├── card-footer.svelte
│ │ │ │ │ │ ├── card-header.svelte
│ │ │ │ │ │ ├── card-description.svelte
│ │ │ │ │ │ ├── card.svelte
│ │ │ │ │ │ ├── card-title.svelte
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ ├── button
│ │ │ │ │ │ ├── button.svelte
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── input
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ └── input.svelte
│ │ │ │ └── utils.ts
│ │ │ └── server
│ │ │ │ └── payload.ts
│ │ ├── app.d.ts
│ │ ├── app.html
│ │ └── app.pcss
│ ├── .prettierignore
│ ├── postcss.config.js
│ ├── static
│ │ └── favicon.png
│ ├── vite.config.ts
│ ├── .env.example
│ ├── tests
│ │ └── test.ts
│ ├── .prettierrc
│ ├── .gitignore
│ ├── .dockerignore
│ ├── playwright.config.ts
│ ├── payload
│ │ ├── collections
│ │ │ ├── Media.ts
│ │ │ └── Users.ts
│ │ ├── payload.config.ts
│ │ └── payload-types.ts
│ ├── components.json
│ ├── tsconfig.json
│ ├── eslint.config.js
│ ├── svelte.config.js
│ ├── README.md
│ ├── Dockerfile
│ ├── package.json
│ └── tailwind.config.ts
└── migrator
│ ├── package.json
│ └── tsconfig.json
├── pnpm-workspace.yaml
├── .vscode
└── settings.json
├── .env.example
├── .dockerignore
├── Dockerfile.migrator
├── turbo.json
├── .gitignore
├── docker-compose.db.yml
├── docker-compose.yml
├── package.json
├── README.md
├── scripts
└── watcher.js
└── docker-compose.prod.yml
/.npmrc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/custom.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/web/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/*"
3 |
--------------------------------------------------------------------------------
/apps/web/src/routes/+layout.ts:
--------------------------------------------------------------------------------
1 | export const prerender = false;
2 |
--------------------------------------------------------------------------------
/apps/web/.prettierignore:
--------------------------------------------------------------------------------
1 | # Package Managers
2 | package-lock.json
3 | pnpm-lock.yaml
4 | yarn.lock
5 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/sonner/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Toaster } from "./sonner.svelte";
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [
3 | {
4 | "mode": "auto"
5 | }
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/apps/web/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fcastrovilli/sveltekit-payload-3-starterkit/HEAD/apps/web/static/favicon.png
--------------------------------------------------------------------------------
/apps/cms/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 100,
5 | "semi": false
6 | }
7 |
--------------------------------------------------------------------------------
/apps/cms/.env.example:
--------------------------------------------------------------------------------
1 | # PayloadCMS
2 | DATABASE_URI=postgressql://postgres_user:postgres_S3cret@127.0.0.1:5432/backend
3 | PAYLOAD_SECRET=YOUR_SECRET_HERE
--------------------------------------------------------------------------------
/apps/web/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/apps/web/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [sveltekit()]
6 | });
7 |
--------------------------------------------------------------------------------
/apps/web/.env.example:
--------------------------------------------------------------------------------
1 | # Sveltekit
2 | ORIGIN=http://localhost:5001
3 |
4 | # PayloadCMS
5 | DATABASE_URI=postgressql://postgres_user:postgres_S3cret@127.0.0.1:5432/backend
6 | PAYLOAD_SECRET=secret
--------------------------------------------------------------------------------
/apps/web/tests/test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '@playwright/test';
2 |
3 | test('home page has expected h1', async ({ page }) => {
4 | await page.goto('/');
5 | await expect(page.locator('h1')).toBeVisible();
6 | });
7 |
--------------------------------------------------------------------------------
/apps/cms/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('eslint').Linter.Config} */
2 | module.exports = {
3 | extends: ['next/core-web-vitals'],
4 | parserOptions: {
5 | project: ['./tsconfig.json'],
6 | tsconfigRootDir: __dirname,
7 | },
8 | }
9 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # PayloadCMS
2 | DATABASE_URI=postgressql://postgres_user:postgres_S3cret@127.0.0.1:5432/backend
3 | PAYLOAD_SECRET=YOUR_SECRET_HERE
4 |
5 | # Postgres
6 | POSTGRES_USER=postgres_user
7 | POSTGRES_PASSWORD=postgres_S3cret
8 | POSTGRES_DB=backend
--------------------------------------------------------------------------------
/apps/web/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
8 | }
9 |
--------------------------------------------------------------------------------
/apps/web/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | # Output
4 | .output
5 | .vercel
6 | /.svelte-kit
7 | /build
8 |
9 | # OS
10 | .DS_Store
11 | Thumbs.db
12 |
13 | # Env
14 | .env
15 | .env.*
16 | !.env.example
17 | !.env.test
18 |
19 | # Vite
20 | vite.config.js.timestamp-*
21 | vite.config.ts.timestamp-*
22 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/api/graphql/route.ts:
--------------------------------------------------------------------------------
1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2 | /* DO NOT MODIFY it because it could be re-written at any time. */
3 | import config from '@/config/payload.config'
4 | import { GRAPHQL_POST } from '@payloadcms/next/routes'
5 |
6 | export const POST = GRAPHQL_POST(config)
7 |
--------------------------------------------------------------------------------
/apps/web/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | db_data
3 |
4 | Dockerfile
5 | Dockerfile.*
6 |
7 | # Output
8 | .output
9 | .vercel
10 | /.svelte-kit
11 | /build
12 |
13 | # OS
14 | .DS_Store
15 | Thumbs.db
16 |
17 | # Env
18 | .env
19 | .env.*
20 |
21 | # Vite
22 | vite.config.js.timestamp-*
23 | vite.config.ts.timestamp-*
24 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-portal.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/apps/web/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import type { PlaywrightTestConfig } from '@playwright/test';
2 |
3 | const config: PlaywrightTestConfig = {
4 | webServer: {
5 | command: 'npm run build && npm run preview',
6 | port: 4173
7 | },
8 | testDir: 'tests',
9 | testMatch: /(.+\.)?(test|spec)\.[jt]s/
10 | };
11 |
12 | export default config;
13 |
--------------------------------------------------------------------------------
/apps/web/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://kit.svelte.dev/docs/types#app
2 | // for information about these interfaces
3 | declare global {
4 | namespace App {
5 | // interface Error {}
6 | // interface Locals {}
7 | // interface PageData {}
8 | // interface PageState {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/api/graphql-playground/route.ts:
--------------------------------------------------------------------------------
1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2 | /* DO NOT MODIFY it because it could be re-written at any time. */
3 | import config from '@/config/payload.config'
4 | import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
5 |
6 | export const GET = GRAPHQL_PLAYGROUND_GET(config)
7 |
--------------------------------------------------------------------------------
/apps/web/payload/collections/Media.ts:
--------------------------------------------------------------------------------
1 | import type { CollectionConfig } from 'payload'
2 |
3 | export const Media: CollectionConfig = {
4 | slug: 'media',
5 | access: {
6 | read: () => true,
7 | },
8 | fields: [
9 | {
10 | name: 'alt',
11 | type: 'text',
12 | required: true,
13 | },
14 | ],
15 | upload: true,
16 | }
17 |
--------------------------------------------------------------------------------
/apps/cms/src/config/collections/Media.ts:
--------------------------------------------------------------------------------
1 | import type { CollectionConfig } from 'payload'
2 |
3 | export const Media: CollectionConfig = {
4 | slug: 'media',
5 | access: {
6 | read: () => true,
7 | },
8 | fields: [
9 | {
10 | name: 'alt',
11 | type: 'text',
12 | required: true,
13 | },
14 | ],
15 | upload: true,
16 | }
17 |
--------------------------------------------------------------------------------
/apps/web/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://shadcn-svelte.com/schema.json",
3 | "style": "new-york",
4 | "tailwind": {
5 | "config": "tailwind.config.ts",
6 | "css": "src\\app.pcss",
7 | "baseColor": "zinc"
8 | },
9 | "aliases": {
10 | "components": "$lib/components",
11 | "utils": "$lib/components/utils"
12 | },
13 | "typescript": true
14 | }
--------------------------------------------------------------------------------
/apps/migrator/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "migrator",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "payload": "cross-env NODE_OPTIONS=--no-deprecation payload"
6 | },
7 | "keywords": [],
8 | "author": "",
9 | "license": "ISC",
10 | "dependencies": {
11 | "cross-env": "^7.0.3",
12 | "payload": "beta",
13 | "@payloadcms/db-postgres": "beta",
14 | "typescript": "^5.5.3"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/apps/web/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | db_data
3 | scripts
4 |
5 | Dockerfile
6 | Dockerfile.*
7 | docker-compose.yml
8 | docker-compose.*.yml
9 |
10 | .vscode
11 |
12 | # Output
13 | out
14 | dist
15 | .next
16 | .turbo
17 | .output
18 | .vercel
19 | .svelte-kit
20 | build
21 |
22 | # OS
23 | .DS_Store
24 | Thumbs.db
25 |
26 | # Env
27 | .env
28 | .env.*
29 |
30 | # Vite
31 | vite.config.js.timestamp-*
32 | vite.config.ts.timestamp-*
33 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/card-content.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Dockerfile.migrator:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine3.19
2 |
3 | WORKDIR /app
4 |
5 | ENV PNPM_HOME="/pnpm"
6 | ENV PATH="$PNPM_HOME:$PATH"
7 | RUN corepack enable
8 |
9 | COPY ./apps/migrator/package.json ./package.json
10 | COPY ./apps/migrator/tsconfig.json ./tsconfig.json
11 |
12 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
13 |
14 | COPY ./apps/cms/src/config ./src/config
15 | COPY ./apps/cms/src/migrations ./src/migrations
--------------------------------------------------------------------------------
/apps/cms/next.config.mjs:
--------------------------------------------------------------------------------
1 | import { withPayload } from '@payloadcms/next/withPayload'
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | output: 'standalone',
6 | reactStrictMode: true,
7 | redirects: async () => {
8 | return [
9 | {
10 | source: '/',
11 | destination: '/admin',
12 | permanent: false,
13 | },
14 | ]
15 | },
16 | }
17 |
18 | export default withPayload(nextConfig)
19 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/card-footer.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/card-header.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/card-description.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/cms/src/config/collections/Users.ts:
--------------------------------------------------------------------------------
1 | import type { CollectionConfig } from 'payload'
2 |
3 | export const Users: CollectionConfig = {
4 | slug: 'users',
5 | admin: {
6 | useAsTitle: 'username',
7 | },
8 | auth: true,
9 | fields: [
10 | {
11 | name: 'username',
12 | type: 'text',
13 | label: 'Username',
14 | required: true,
15 | unique: true,
16 | },
17 | // Email added by default
18 | // Add more fields as needed
19 | ],
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/payload/collections/Users.ts:
--------------------------------------------------------------------------------
1 | import type { CollectionConfig } from 'payload'
2 |
3 | export const Users: CollectionConfig = {
4 | slug: 'users',
5 | admin: {
6 | useAsTitle: 'username',
7 | },
8 | auth: true,
9 | fields: [
10 | {
11 | name: 'username',
12 | type: 'text',
13 | label: 'Username',
14 | required: true,
15 | unique: true,
16 | },
17 | // Email added by default
18 | // Add more fields as needed
19 | ],
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-header.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/apps/cms/src/migrations/20240706_165421.ts:
--------------------------------------------------------------------------------
1 | import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
2 |
3 | export async function up({ payload, req }: MigrateUpArgs): Promise {
4 | await payload.db.drizzle.execute(sql`
5 | ALTER TABLE "users" ADD COLUMN "username" varchar;`)
6 | };
7 |
8 | export async function down({ payload, req }: MigrateDownArgs): Promise {
9 | await payload.db.drizzle.execute(sql`
10 | ALTER TABLE "users" DROP COLUMN IF EXISTS "username";`)
11 | };
12 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-footer.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "tasks": {
4 | "build": {
5 | "dependsOn": ["^build"],
6 | "inputs": ["$TURBO_DEFAULT$", ".env*"],
7 | "outputs": [
8 | "dist/**",
9 | "build/**",
10 | "public/**",
11 | ".next/**",
12 | "!.next/cache/**"
13 | ]
14 | },
15 | "lint": {
16 | "dependsOn": ["^lint"]
17 | },
18 | "dev": {
19 | "cache": false,
20 | "persistent": true
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/api/[...slug]/route.ts:
--------------------------------------------------------------------------------
1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2 | /* DO NOT MODIFY it because it could be re-written at any time. */
3 | import config from '@/config/payload.config'
4 | import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
5 |
6 | export const GET = REST_GET(config)
7 | export const POST = REST_POST(config)
8 | export const DELETE = REST_DELETE(config)
9 | export const PATCH = REST_PATCH(config)
10 | export const OPTIONS = REST_OPTIONS(config)
11 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/layout.tsx:
--------------------------------------------------------------------------------
1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2 | import configPromise from '@/config/payload.config'
3 | import '@payloadcms/next/css'
4 | import { RootLayout } from '@payloadcms/next/layouts'
5 | /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
6 | import React from 'react'
7 |
8 | import './custom.scss'
9 |
10 | type Args = {
11 | children: React.ReactNode
12 | }
13 |
14 | const Layout = ({ children }: Args) => {children}
15 |
16 | export default Layout
17 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-description.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-title.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # Dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # Local env files
9 | .env
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | # Testing
16 | coverage
17 |
18 | # Turbo
19 | .turbo
20 |
21 | # Vercel
22 | .vercel
23 |
24 | # Build Outputs
25 | .next/
26 | out/
27 | build
28 | dist
29 |
30 | # Database
31 | db_data
32 |
33 | # Debug
34 | npm-debug.log*
35 | yarn-debug.log*
36 | yarn-error.log*
37 |
38 | # Misc
39 | .DS_Store
40 | *.pem
41 |
--------------------------------------------------------------------------------
/docker-compose.db.yml:
--------------------------------------------------------------------------------
1 | services:
2 | db:
3 | image: postgres:16.3-alpine3.20
4 | container_name: db
5 | ports:
6 | - 5432:5432
7 | volumes:
8 | - db_data:/var/lib/postgresql/data
9 | environment:
10 | - POSTGRES_USER=${POSTGRES_USER}
11 | - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
12 | - POSTGRES_DB=${POSTGRES_DB}
13 | healthcheck:
14 | test:
15 | [
16 | "CMD-SHELL",
17 | "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'",
18 | ]
19 | interval: 10s
20 | timeout: 5s
21 | retries: 5
22 |
23 | volumes:
24 | db_data:
25 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/card.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/apps/cms/src/migrations/20240706_173841.ts:
--------------------------------------------------------------------------------
1 | import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
2 |
3 | export async function up({ payload, req }: MigrateUpArgs): Promise {
4 | await payload.db.drizzle.execute(sql`
5 | ALTER TABLE "users" ALTER COLUMN "username" SET NOT NULL;
6 | CREATE UNIQUE INDEX IF NOT EXISTS "users_username_idx" ON "users" ("username");`)
7 | };
8 |
9 | export async function down({ payload, req }: MigrateDownArgs): Promise {
10 | await payload.db.drizzle.execute(sql`
11 | DROP INDEX IF EXISTS "users_username_idx";
12 | ALTER TABLE "users" ALTER COLUMN "username" DROP NOT NULL;`)
13 | };
14 |
--------------------------------------------------------------------------------
/apps/cms/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | /.idea/*
10 | !/.idea/runConfigurations
11 |
12 | # testing
13 | /coverage
14 |
15 | # next.js
16 | /.next/
17 | /out/
18 |
19 | # production
20 | /build
21 |
22 | # misc
23 | .DS_Store
24 | *.pem
25 |
26 | # debug
27 | npm-debug.log*
28 | yarn-debug.log*
29 | yarn-error.log*
30 |
31 | # local env files
32 | .env*.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 | next-env.d.ts
40 |
41 | .env
42 |
43 | /media
44 |
--------------------------------------------------------------------------------
/apps/migrator/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "paths": {
16 | "@/*": ["./src/*"],
17 | "@payload-config": ["src/config/payload.config.ts"]
18 | },
19 | "target": "ES2017"
20 | },
21 | "include": ["**/*.ts", "**/*.tsx"],
22 | "exclude": ["node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/card-title.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/card/index.ts:
--------------------------------------------------------------------------------
1 | import Root from "./card.svelte";
2 | import Content from "./card-content.svelte";
3 | import Description from "./card-description.svelte";
4 | import Footer from "./card-footer.svelte";
5 | import Header from "./card-header.svelte";
6 | import Title from "./card-title.svelte";
7 |
8 | export {
9 | Root,
10 | Content,
11 | Description,
12 | Footer,
13 | Header,
14 | Title,
15 | //
16 | Root as Card,
17 | Content as CardContent,
18 | Description as CardDescription,
19 | Footer as CardFooter,
20 | Header as CardHeader,
21 | Title as CardTitle,
22 | };
23 |
24 | export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
25 |
--------------------------------------------------------------------------------
/apps/cms/.dockerignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | Dockerfile
4 | Dockerfile.*
5 |
6 | # dependencies
7 | /node_modules
8 | /.pnp
9 | .pnp.js
10 | .yarn/install-state.gz
11 |
12 | /.idea/*
13 | !/.idea/runConfigurations
14 |
15 | # testing
16 | /coverage
17 |
18 | # next.js
19 | /.next/
20 | /out/
21 |
22 | # production
23 | /build
24 |
25 | # misc
26 | .DS_Store
27 | *.pem
28 |
29 | # debug
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 |
34 | # local env files
35 | .env*.local
36 |
37 | # vercel
38 | .vercel
39 |
40 | # typescript
41 | *.tsbuildinfo
42 | next-env.d.ts
43 |
44 | .env
45 |
46 | /media
47 |
48 | db_data
49 |
50 | web
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-action.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/apps/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true,
12 | "moduleResolution": "bundler"
13 | }
14 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
15 | // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
16 | //
17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
18 | // from the referenced tsconfig.json - TypeScript does not merge them in
19 | }
20 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-cancel.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-overlay.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/sonner/sonner.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
21 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/admin/[[...segments]]/page.tsx:
--------------------------------------------------------------------------------
1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2 | import type { Metadata } from 'next'
3 |
4 | import config from '@/config/payload.config'
5 | /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
6 | import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
7 |
8 | type Args = {
9 | params: {
10 | segments: string[]
11 | }
12 | searchParams: {
13 | [key: string]: string | string[]
14 | }
15 | }
16 |
17 | export const generateMetadata = ({ params, searchParams }: Args): Promise =>
18 | generatePageMetadata({ config, params, searchParams })
19 |
20 | const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
21 |
22 | export default Page
23 |
--------------------------------------------------------------------------------
/apps/cms/src/app/(payload)/admin/[[...segments]]/not-found.tsx:
--------------------------------------------------------------------------------
1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2 | import type { Metadata } from 'next'
3 |
4 | import config from '@/config/payload.config'
5 | /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
6 | import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
7 |
8 | type Args = {
9 | params: {
10 | segments: string[]
11 | }
12 | searchParams: {
13 | [key: string]: string | string[]
14 | }
15 | }
16 |
17 | export const generateMetadata = ({ params, searchParams }: Args): Promise =>
18 | generatePageMetadata({ config, params, searchParams })
19 |
20 | const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
21 |
22 | export default NotFound
23 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/button/button.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/apps/web/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import ts from 'typescript-eslint';
3 | import svelte from 'eslint-plugin-svelte';
4 | import prettier from 'eslint-config-prettier';
5 | import globals from 'globals';
6 |
7 | /** @type {import('eslint').Linter.FlatConfig[]} */
8 | export default [
9 | js.configs.recommended,
10 | ...ts.configs.recommended,
11 | ...svelte.configs['flat/recommended'],
12 | prettier,
13 | ...svelte.configs['flat/prettier'],
14 | {
15 | languageOptions: {
16 | globals: {
17 | ...globals.browser,
18 | ...globals.node
19 | }
20 | }
21 | },
22 | {
23 | files: ['**/*.svelte'],
24 | languageOptions: {
25 | parserOptions: {
26 | parser: ts.parser
27 | }
28 | }
29 | },
30 | {
31 | ignores: ['build/', '.svelte-kit/', 'dist/']
32 | }
33 | ];
34 |
--------------------------------------------------------------------------------
/apps/web/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-node';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13 | // See https://kit.svelte.dev/docs/adapters for more information about adapters.
14 | adapter: adapter(),
15 | alias: {
16 | $payload: './payload'
17 | }
18 | }
19 | };
20 |
21 | export default config;
22 |
--------------------------------------------------------------------------------
/apps/cms/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"],
23 | "@payload-config": ["src/config/payload.config.ts"]
24 | },
25 | "target": "ES2017"
26 | },
27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
28 | "exclude": ["node_modules"]
29 | }
30 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | include:
2 | - ./docker-compose.db.yml
3 |
4 | services:
5 | cms:
6 | build: ./apps/cms
7 | container_name: cms
8 | environment:
9 | - PAYLOAD_SECRET=${PAYLOAD_SECRET}
10 | - DATABASE_URI=postgressql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
11 | ports:
12 | - ${CMS_PORT:-5000}:3000
13 | depends_on:
14 | db:
15 | condition: service_healthy
16 | restart: true
17 |
18 | web:
19 | build: ./apps/web
20 | container_name: web
21 | environment:
22 | - NODE_ENV=production
23 | - ORIGIN=${ORIGIN}
24 | - PAYLOAD_SECRET=${PAYLOAD_SECRET}
25 | - DATABASE_URI=postgressql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
26 | ports:
27 | - ${WEB_PORT:-5001}:3000
28 | depends_on:
29 | db:
30 | condition: service_healthy
31 | restart: true
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "turbo-starterkit",
3 | "private": true,
4 | "scripts": {
5 | "db": "docker compose -f docker-compose.db.yml up -d",
6 | "db:down": "docker compose -f docker-compose.db.yml down",
7 | "prod": "pnpm run sync-config && docker compose -f docker-compose.prod.yml up --build",
8 | "down": "docker compose -f docker-compose.prod.yml down",
9 | "build": "pnpm run sync-config && turbo build --filter=cms --filter=web",
10 | "payload": "cd apps/cms && pnpm payload",
11 | "dev": "pnpm run sync-config && turbo dev --filter=cms --filter=web",
12 | "lint": "turbo lint",
13 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
14 | "sync-config": "node scripts/watcher.js"
15 | },
16 | "devDependencies": {
17 | "fs-extra": "^11.2.0",
18 | "turbo": "latest"
19 | },
20 | "packageManager": "pnpm@9.1.1",
21 | "engines": {
22 | "node": ">=20"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/input/index.ts:
--------------------------------------------------------------------------------
1 | import Root from "./input.svelte";
2 |
3 | export type FormInputEvent = T & {
4 | currentTarget: EventTarget & HTMLInputElement;
5 | };
6 | export type InputEvents = {
7 | blur: FormInputEvent;
8 | change: FormInputEvent;
9 | click: FormInputEvent;
10 | focus: FormInputEvent;
11 | focusin: FormInputEvent;
12 | focusout: FormInputEvent;
13 | keydown: FormInputEvent;
14 | keypress: FormInputEvent;
15 | keyup: FormInputEvent;
16 | mouseover: FormInputEvent;
17 | mouseenter: FormInputEvent;
18 | mouseleave: FormInputEvent;
19 | mousemove: FormInputEvent;
20 | paste: FormInputEvent;
21 | input: FormInputEvent;
22 | wheel: FormInputEvent;
23 | };
24 |
25 | export {
26 | Root,
27 | //
28 | Root as Input,
29 | };
30 |
--------------------------------------------------------------------------------
/apps/cms/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine3.19 AS image
2 |
3 | FROM image AS preparer
4 | WORKDIR /app
5 |
6 | ENV PNPM_HOME="/pnpm"
7 | ENV PATH="$PNPM_HOME:$PATH"
8 | RUN corepack enable
9 | # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
10 | RUN apk update
11 | RUN apk add --no-cache libc6-compat
12 |
13 | COPY . .
14 |
15 | FROM preparer AS build
16 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install
17 | RUN pnpm run build
18 |
19 | FROM image AS prod
20 | WORKDIR /app
21 |
22 | ENV NODE_ENV=production
23 | ENV PAYLOAD_CONFIG_PATH=dist/payload.config.js
24 |
25 | # Don't run production as root
26 | RUN addgroup --system --gid 1001 nodejs
27 | RUN adduser --system --uid 1001 nextjs
28 | USER nextjs
29 |
30 | COPY --from=build --chown=nextjs:nodejs /app/.next/standalone /app
31 | COPY --from=build --chown=nextjs:nodejs /app/.next/static /app/.next/static
32 |
33 | EXPOSE 3000
34 |
35 | CMD node server.js
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/alert-dialog-content.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/apps/web/src/lib/server/payload.ts:
--------------------------------------------------------------------------------
1 | import { BasePayload, getPayload, type SanitizedConfig } from 'payload';
2 | import { importConfig } from 'payload/node';
3 | import path from 'node:path';
4 | import { fileURLToPath } from 'node:url';
5 | import { building } from '$app/environment';
6 |
7 | let payloadInstance: BasePayload | null = null;
8 |
9 | async function initPayload() {
10 | if (!payloadInstance && !building) {
11 | try {
12 | const __dirname = path.dirname(fileURLToPath(import.meta.url));
13 | const payloadConfigPath: string = path.join(__dirname, '../../../payload/payload.config.ts');
14 |
15 | console.log(payloadConfigPath);
16 |
17 | const awaitedConfig: SanitizedConfig = await importConfig(payloadConfigPath);
18 | payloadInstance = await getPayload({ config: awaitedConfig });
19 | } catch (error) {
20 | console.log('error', error);
21 | payloadInstance = null;
22 | }
23 | return payloadInstance;
24 | }
25 | }
26 |
27 | export const payload = payloadInstance ? payloadInstance : await initPayload();
28 |
--------------------------------------------------------------------------------
/apps/cms/src/config/payload.config.ts:
--------------------------------------------------------------------------------
1 | // storage-adapter-import-placeholder
2 | import { postgresAdapter } from '@payloadcms/db-postgres'
3 | // import { lexicalEditor } from '@payloadcms/richtext-lexical'
4 | import path from 'path'
5 | import { buildConfig } from 'payload'
6 | import { fileURLToPath } from 'url'
7 | // import sharp from 'sharp'
8 |
9 | import { Users } from './collections/Users'
10 | import { Media } from './collections/Media'
11 |
12 | const filename = fileURLToPath(import.meta.url)
13 | const dirname = path.dirname(filename)
14 |
15 | export default buildConfig({
16 | admin: {
17 | user: Users.slug,
18 | },
19 | collections: [Users, Media],
20 | // editor: lexicalEditor(),
21 | secret: process.env.PAYLOAD_SECRET || '',
22 | typescript: {
23 | outputFile: path.resolve(dirname, 'payload-types.ts'),
24 | },
25 | db: postgresAdapter({
26 | pool: {
27 | connectionString: process.env.DATABASE_URI || '',
28 | },
29 | }),
30 | // sharp,
31 | plugins: [
32 | // storage-adapter-placeholder
33 | ],
34 | })
35 |
--------------------------------------------------------------------------------
/apps/web/payload/payload.config.ts:
--------------------------------------------------------------------------------
1 | // storage-adapter-import-placeholder
2 | import { postgresAdapter } from '@payloadcms/db-postgres'
3 | // import { lexicalEditor } from '@payloadcms/richtext-lexical'
4 | import path from 'path'
5 | import { buildConfig } from 'payload'
6 | import { fileURLToPath } from 'url'
7 | // import sharp from 'sharp'
8 |
9 | import { Users } from './collections/Users'
10 | import { Media } from './collections/Media'
11 |
12 | const filename = fileURLToPath(import.meta.url)
13 | const dirname = path.dirname(filename)
14 |
15 | export default buildConfig({
16 | admin: {
17 | user: Users.slug,
18 | },
19 | collections: [Users, Media],
20 | // editor: lexicalEditor(),
21 | secret: process.env.PAYLOAD_SECRET || '',
22 | typescript: {
23 | outputFile: path.resolve(dirname, 'payload-types.ts'),
24 | },
25 | db: postgresAdapter({
26 | pool: {
27 | connectionString: process.env.DATABASE_URI || '',
28 | },
29 | }),
30 | // sharp,
31 | plugins: [
32 | // storage-adapter-placeholder
33 | ],
34 | })
35 |
--------------------------------------------------------------------------------
/apps/web/README.md:
--------------------------------------------------------------------------------
1 | # create-svelte
2 |
3 | Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
4 |
5 | ## Creating a project
6 |
7 | If you're seeing this, you've probably already done this step. Congrats!
8 |
9 | ```bash
10 | # create a new project in the current directory
11 | npm create svelte@latest
12 |
13 | # create a new project in my-app
14 | npm create svelte@latest my-app
15 | ```
16 |
17 | ## Developing
18 |
19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20 |
21 | ```bash
22 | npm run dev
23 |
24 | # or start the server and open the app in a new browser tab
25 | npm run dev -- --open
26 | ```
27 |
28 | ## Building
29 |
30 | To create a production version of your app:
31 |
32 | ```bash
33 | npm run build
34 | ```
35 |
36 | You can preview the production build with `npm run preview`.
37 |
38 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
39 |
--------------------------------------------------------------------------------
/apps/web/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine3.19 AS image
2 |
3 | FROM image AS preparer
4 | WORKDIR /app
5 |
6 | ENV PNPM_HOME="/pnpm"
7 | ENV PATH="$PNPM_HOME:$PATH"
8 | RUN corepack enable
9 |
10 | COPY . .
11 |
12 | FROM preparer AS prod-deps
13 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
14 |
15 | FROM preparer AS builder
16 | RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install
17 | RUN pnpm run check
18 | RUN pnpm run build
19 |
20 | FROM image AS prod
21 | WORKDIR /app
22 |
23 | # Don't run production as root
24 | RUN addgroup --system --gid 1001 nodejs
25 | RUN adduser --system --uid 1001 svelte
26 | USER svelte
27 |
28 | # Copy from repo
29 | COPY --chown=svelte:nodejs ./package.json ./package.json
30 | COPY --chown=svelte:nodejs ./tsconfig.json ./tsconfig.json
31 | COPY --chown=svelte:nodejs ./payload ./payload
32 |
33 | # Copy from build
34 | COPY --chown=svelte:nodejs --from=builder /app/build ./build
35 | COPY --chown=svelte:nodejs --from=builder /app/.svelte-kit ./.svelte-kit
36 | COPY --chown=svelte:nodejs --from=prod-deps /app/node_modules ./node_modules
37 |
38 | EXPOSE 3000
39 |
40 | CMD node build
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sveltekit + Payload CMS Starter Kit
2 |
3 | This is a simple boilerplate to get started with Sveltekit & Payload CMS 3.0.
4 |
5 | ## Features
6 |
7 | - [Turborepo](https://turbo.build/repo) for local development
8 | - [Sveltekit](https://kit.svelte.dev/) for frontend (`apps/web`)
9 | - TypeScript
10 | - Svelte 5
11 | - Tailwind CSS & [shadcn-svelte](https://shadcn-svelte.com/)
12 | - [Payload CMS](https://payloadcms.com/) for backend (`apps/cms`)
13 | - TypeScript
14 | - Next.js
15 | - Payload CMS 3.0
16 | - Postgres DB for local development
17 | - Docker Compose for production
18 |
19 | ## Getting Started
20 |
21 | ### Prerequisites
22 |
23 | - [Docker](https://docs.docker.com/get-docker/)
24 | - [Docker Compose](https://docs.docker.com/compose/install/)
25 | - [pnpm](https://pnpm.io/installation)
26 | - [Node.js](https://nodejs.org/en/download/) (>= 20)
27 |
28 | ### Installation
29 |
30 | Clone or fork this repository to your local machine.
31 | Then, run the following commands to install dependencies and start the local development server.
32 |
33 | ```bash
34 | pnpm install
35 | ```
36 |
37 | ### Development
38 |
39 | ```bash
40 | pnpm db && pnpm dev
41 | ```
42 |
43 | ### Production Testing
44 |
45 | ```bash
46 | pnpm prod
47 | ```
48 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/alert-dialog/index.ts:
--------------------------------------------------------------------------------
1 | import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
2 |
3 | import Title from "./alert-dialog-title.svelte";
4 | import Action from "./alert-dialog-action.svelte";
5 | import Cancel from "./alert-dialog-cancel.svelte";
6 | import Portal from "./alert-dialog-portal.svelte";
7 | import Footer from "./alert-dialog-footer.svelte";
8 | import Header from "./alert-dialog-header.svelte";
9 | import Overlay from "./alert-dialog-overlay.svelte";
10 | import Content from "./alert-dialog-content.svelte";
11 | import Description from "./alert-dialog-description.svelte";
12 |
13 | const Root = AlertDialogPrimitive.Root;
14 | const Trigger = AlertDialogPrimitive.Trigger;
15 |
16 | export {
17 | Root,
18 | Title,
19 | Action,
20 | Cancel,
21 | Portal,
22 | Footer,
23 | Header,
24 | Trigger,
25 | Overlay,
26 | Content,
27 | Description,
28 | //
29 | Root as AlertDialog,
30 | Title as AlertDialogTitle,
31 | Action as AlertDialogAction,
32 | Cancel as AlertDialogCancel,
33 | Portal as AlertDialogPortal,
34 | Footer as AlertDialogFooter,
35 | Header as AlertDialogHeader,
36 | Trigger as AlertDialogTrigger,
37 | Overlay as AlertDialogOverlay,
38 | Content as AlertDialogContent,
39 | Description as AlertDialogDescription,
40 | };
41 |
--------------------------------------------------------------------------------
/scripts/watcher.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs-extra");
2 | const path = require("path");
3 |
4 | const sourceDir = path.join(__dirname, "../apps/cms/src/config");
5 | const destDir = path.join(__dirname, "../apps/web/payload");
6 |
7 | async function removeFile(filePath) {
8 | try {
9 | await fs.remove(filePath);
10 | console.log(`File removed at ${filePath}`);
11 | } catch (err) {
12 | console.error("Error during file removal:", err);
13 | }
14 | }
15 |
16 | async function syncDirectories(srcDir, destDir) {
17 | try {
18 | // Copy files from source to destination
19 | await fs.copy(srcDir, destDir, {
20 | overwrite: true,
21 | errorOnExist: false,
22 | });
23 |
24 | // Remove files from destination that do not exist in source
25 | const srcFiles = await fs.readdir(srcDir);
26 | const destFiles = await fs.readdir(destDir);
27 |
28 | for (const file of destFiles) {
29 | if (!srcFiles.includes(file)) {
30 | await removeFile(path.join(destDir, file));
31 | }
32 | }
33 |
34 | console.log(`Directories ${srcDir} and ${destDir} are now in sync`);
35 | } catch (err) {
36 | console.error("Error during directory sync:", err);
37 | }
38 | }
39 |
40 | // Sync directories at startup
41 | syncDirectories(sourceDir, destDir);
42 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/input/input.svelte:
--------------------------------------------------------------------------------
1 |
17 |
18 |
43 |
--------------------------------------------------------------------------------
/apps/web/src/routes/+page.server.ts:
--------------------------------------------------------------------------------
1 | import type { PageServerLoad } from './$types';
2 | import { payload } from '$lib/server/payload';
3 | import type { Actions } from '@sveltejs/kit';
4 | import type { PaginatedDocs } from 'payload';
5 | import type { User } from '$payload/payload-types.ts';
6 |
7 | export const load: PageServerLoad = async () => {
8 | if (!payload) return;
9 | const users: PaginatedDocs = await payload.find({
10 | collection: 'users',
11 | limit: 10
12 | });
13 | return {
14 | users: users
15 | };
16 | };
17 |
18 | export const actions: Actions = {
19 | create: async ({ request }) => {
20 | if (!payload) return;
21 | const form = await request.formData();
22 | const email: string = form.get('email') as string;
23 | const username: string = form.get('username') as string;
24 | const password: string = form.get('password') as string;
25 | try {
26 | const user: User = await payload.create({
27 | collection: 'users',
28 | data: {
29 | email: email,
30 | username: username,
31 | password: password
32 | }
33 | });
34 | return {
35 | user
36 | };
37 | } catch (error) {
38 | console.log(error);
39 | }
40 | },
41 | delete: async ({ request }) => {
42 | if (!payload) return;
43 | const form = await request.formData();
44 | const id: string = form.get('id') as string;
45 | try {
46 | const user: User = await payload.delete({
47 | collection: 'users',
48 | id: id
49 | });
50 | return {
51 | user
52 | };
53 | } catch (error) {
54 | console.log(error);
55 | }
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/apps/cms/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cms",
3 | "version": "1.0.0",
4 | "description": "A blank template to get started with Payload 3.0",
5 | "license": "MIT",
6 | "type": "module",
7 | "scripts": {
8 | "build": "cross-env NODE_OPTIONS=--no-deprecation next build",
9 | "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
10 | "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
11 | "generate:types": "payload generate:types",
12 | "lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
13 | "payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
14 | "start": "cross-env NODE_OPTIONS=--no-deprecation next start"
15 | },
16 | "dependencies": {
17 | "payload": "beta",
18 | "@payloadcms/next": "beta",
19 | "@payloadcms/plugin-cloud": "beta",
20 | "@payloadcms/richtext-lexical": "beta",
21 | "@payloadcms/db-postgres": "beta",
22 | "cross-env": "^7.0.3",
23 | "graphql": "^16.9.0",
24 | "next": "15.0.0-canary.53",
25 | "react": "rc",
26 | "react-dom": "rc",
27 | "sharp": "0.33.4"
28 | },
29 | "devDependencies": {
30 | "@types/node": "^20.14.9",
31 | "@types/react": "npm:types-react@rc",
32 | "@types/react-dom": "npm:types-react-dom@rc",
33 | "dotenv": "^16.4.5",
34 | "eslint": "^9",
35 | "eslint-config-next": "15.0.0-canary.53",
36 | "typescript": "5.5.3"
37 | },
38 | "engines": {
39 | "node": "^18.20.2 || >=20.9.0"
40 | },
41 | "overrides": {
42 | "@types/react": "npm:types-react@rc",
43 | "@types/react-dom": "npm:types-react-dom@rc"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/apps/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "db": "docker compose -f docker-compose.db.yml up -d",
7 | "dev": "vite dev",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "test": "playwright test",
11 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
12 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
13 | "lint": "prettier --check . && eslint .",
14 | "format": "prettier --write ."
15 | },
16 | "devDependencies": {
17 | "@playwright/test": "^1.28.1",
18 | "@sveltejs/adapter-node": "^5.2.0",
19 | "@sveltejs/kit": "^2.0.0",
20 | "@sveltejs/vite-plugin-svelte": "^3.0.0",
21 | "@tailwindcss/typography": "^0.5.13",
22 | "@types/eslint": "^8.56.7",
23 | "autoprefixer": "^10.4.19",
24 | "eslint": "^9.0.0",
25 | "eslint-config-prettier": "^9.1.0",
26 | "eslint-plugin-svelte": "^2.36.0",
27 | "globals": "^15.0.0",
28 | "postcss": "^8.4.38",
29 | "prettier": "^3.1.1",
30 | "prettier-plugin-svelte": "^3.1.2",
31 | "prettier-plugin-tailwindcss": "^0.6.4",
32 | "svelte": "^5.0.0-next.1",
33 | "svelte-check": "^3.6.0",
34 | "tailwindcss": "^3.4.4",
35 | "tslib": "^2.4.1",
36 | "typescript-eslint": "^8.0.0-alpha.20",
37 | "vite": "^5.0.3"
38 | },
39 | "dependencies": {
40 | "@payloadcms/db-postgres": "beta",
41 | "bits-ui": "^0.21.11",
42 | "clsx": "^2.1.1",
43 | "mode-watcher": "^0.3.1",
44 | "payload": "beta",
45 | "svelte-sonner": "^0.3.25",
46 | "tailwind-merge": "^2.3.0",
47 | "tailwind-variants": "^0.2.1",
48 | "typescript": "^5.5.3"
49 | },
50 | "type": "module"
51 | }
52 |
--------------------------------------------------------------------------------
/apps/web/src/lib/components/ui/button/index.ts:
--------------------------------------------------------------------------------
1 | import type { Button as ButtonPrimitive } from "bits-ui";
2 | import { type VariantProps, tv } from "tailwind-variants";
3 | import Root from "./button.svelte";
4 |
5 | const buttonVariants = tv({
6 | base: "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
7 | variants: {
8 | variant: {
9 | default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
10 | destructive:
11 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
12 | outline:
13 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
14 | secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
15 | ghost: "hover:bg-accent hover:text-accent-foreground",
16 | link: "text-primary underline-offset-4 hover:underline",
17 | },
18 | size: {
19 | default: "h-9 px-4 py-2",
20 | sm: "h-8 rounded-md px-3 text-xs",
21 | lg: "h-10 rounded-md px-8",
22 | icon: "h-9 w-9",
23 | },
24 | },
25 | defaultVariants: {
26 | variant: "default",
27 | size: "default",
28 | },
29 | });
30 |
31 | type Variant = VariantProps["variant"];
32 | type Size = VariantProps["size"];
33 |
34 | type Props = ButtonPrimitive.Props & {
35 | variant?: Variant;
36 | size?: Size;
37 | };
38 |
39 | type Events = ButtonPrimitive.Events;
40 |
41 | export {
42 | Root,
43 | type Props,
44 | type Events,
45 | //
46 | Root as Button,
47 | type Props as ButtonProps,
48 | type Events as ButtonEvents,
49 | buttonVariants,
50 | };
51 |
--------------------------------------------------------------------------------
/apps/web/src/app.pcss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 240 10% 3.9%;
9 |
10 | --muted: 240 4.8% 95.9%;
11 | --muted-foreground: 240 3.8% 46.1%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 240 10% 3.9%;
15 |
16 | --card: 0 0% 100%;
17 | --card-foreground: 240 10% 3.9%;
18 |
19 | --border: 240 5.9% 90%;
20 | --input: 240 5.9% 90%;
21 |
22 | --primary: 240 5.9% 10%;
23 | --primary-foreground: 0 0% 98%;
24 |
25 | --secondary: 240 4.8% 95.9%;
26 | --secondary-foreground: 240 5.9% 10%;
27 |
28 | --accent: 240 4.8% 95.9%;
29 | --accent-foreground: 240 5.9% 10%;
30 |
31 | --destructive: 0 72.2% 50.6%;
32 | --destructive-foreground: 0 0% 98%;
33 |
34 | --ring: 240 10% 3.9%;
35 |
36 | --radius: 0.5rem;
37 | }
38 |
39 | .dark {
40 | --background: 240 10% 3.9%;
41 | --foreground: 0 0% 98%;
42 |
43 | --muted: 240 3.7% 15.9%;
44 | --muted-foreground: 240 5% 64.9%;
45 |
46 | --popover: 240 10% 3.9%;
47 | --popover-foreground: 0 0% 98%;
48 |
49 | --card: 240 10% 3.9%;
50 | --card-foreground: 0 0% 98%;
51 |
52 | --border: 240 3.7% 15.9%;
53 | --input: 240 3.7% 15.9%;
54 |
55 | --primary: 0 0% 98%;
56 | --primary-foreground: 240 5.9% 10%;
57 |
58 | --secondary: 240 3.7% 15.9%;
59 | --secondary-foreground: 0 0% 98%;
60 |
61 | --accent: 240 3.7% 15.9%;
62 | --accent-foreground: 0 0% 98%;
63 |
64 | --destructive: 0 62.8% 30.6%;
65 | --destructive-foreground: 0 0% 98%;
66 |
67 | --ring: 240 4.9% 83.9%;
68 | }
69 | }
70 |
71 | @layer base {
72 | * {
73 | @apply border-border;
74 | }
75 | body {
76 | @apply bg-background text-foreground;
77 | }
78 | }
--------------------------------------------------------------------------------
/apps/web/src/lib/components/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 | import { cubicOut } from "svelte/easing";
4 | import type { TransitionConfig } from "svelte/transition";
5 |
6 | export function cn(...inputs: ClassValue[]) {
7 | return twMerge(clsx(inputs));
8 | }
9 |
10 | type FlyAndScaleParams = {
11 | y?: number;
12 | x?: number;
13 | start?: number;
14 | duration?: number;
15 | };
16 |
17 | export const flyAndScale = (
18 | node: Element,
19 | params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
20 | ): TransitionConfig => {
21 | const style = getComputedStyle(node);
22 | const transform = style.transform === "none" ? "" : style.transform;
23 |
24 | const scaleConversion = (
25 | valueA: number,
26 | scaleA: [number, number],
27 | scaleB: [number, number]
28 | ) => {
29 | const [minA, maxA] = scaleA;
30 | const [minB, maxB] = scaleB;
31 |
32 | const percentage = (valueA - minA) / (maxA - minA);
33 | const valueB = percentage * (maxB - minB) + minB;
34 |
35 | return valueB;
36 | };
37 |
38 | const styleToString = (
39 | style: Record
40 | ): string => {
41 | return Object.keys(style).reduce((str, key) => {
42 | if (style[key] === undefined) return str;
43 | return str + `${key}:${style[key]};`;
44 | }, "");
45 | };
46 |
47 | return {
48 | duration: params.duration ?? 200,
49 | delay: 0,
50 | css: (t) => {
51 | const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
52 | const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
53 | const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
54 |
55 | return styleToString({
56 | transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
57 | opacity: t
58 | });
59 | },
60 | easing: cubicOut
61 | };
62 | };
--------------------------------------------------------------------------------
/docker-compose.prod.yml:
--------------------------------------------------------------------------------
1 | services:
2 | db:
3 | image: postgres:16.3-alpine3.20
4 | container_name: db
5 | volumes:
6 | - ./db_data:/var/lib/postgresql/data
7 | environment:
8 | - POSTGRES_USER=${POSTGRES_USER}
9 | - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
10 | - POSTGRES_DB=${POSTGRES_DB}
11 | healthcheck:
12 | test:
13 | [
14 | "CMD-SHELL",
15 | "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'",
16 | ]
17 | interval: 10s
18 | timeout: 5s
19 | retries: 5
20 | migrator:
21 | build:
22 | context: ./
23 | dockerfile: Dockerfile.migrator
24 | container_name: migrator
25 | command: ["pnpm", "payload", "migrate"]
26 | restart: no
27 | environment:
28 | - PAYLOAD_SECRET=${PAYLOAD_SECRET}
29 | - DATABASE_URI=postgressql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${DB_PORT:-5432}/${POSTGRES_DB}
30 | depends_on:
31 | db:
32 | condition: service_healthy
33 | restart: true
34 | cms:
35 | build: ./apps/cms
36 | container_name: cms
37 | environment:
38 | - PAYLOAD_SECRET=${PAYLOAD_SECRET}
39 | - DATABASE_URI=postgressql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${DB_PORT:-5432}/${POSTGRES_DB}
40 | depends_on:
41 | migrator:
42 | condition: service_completed_successfully
43 | db:
44 | condition: service_healthy
45 | restart: true
46 | web:
47 | build: ./apps/web
48 | container_name: web
49 | environment:
50 | - NODE_ENV=production
51 | - ORIGIN=${ORIGIN}
52 | - PAYLOAD_SECRET=${PAYLOAD_SECRET}
53 | - DATABASE_URI=postgressql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${DB_PORT:-5432}/${POSTGRES_DB}
54 | depends_on:
55 | migrator:
56 | condition: service_completed_successfully
57 | db:
58 | condition: service_healthy
59 | restart: true
60 |
--------------------------------------------------------------------------------
/apps/web/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { fontFamily } from "tailwindcss/defaultTheme";
2 | import type { Config } from "tailwindcss";
3 |
4 | const config: Config = {
5 | darkMode: ["class"],
6 | content: ["./src/**/*.{html,js,svelte,ts}"],
7 | safelist: ["dark"],
8 | theme: {
9 | container: {
10 | center: true,
11 | padding: "2rem",
12 | screens: {
13 | "2xl": "1400px"
14 | }
15 | },
16 | extend: {
17 | colors: {
18 | border: "hsl(var(--border) / )",
19 | input: "hsl(var(--input) / )",
20 | ring: "hsl(var(--ring) / )",
21 | background: "hsl(var(--background) / )",
22 | foreground: "hsl(var(--foreground) / )",
23 | primary: {
24 | DEFAULT: "hsl(var(--primary) / )",
25 | foreground: "hsl(var(--primary-foreground) / )"
26 | },
27 | secondary: {
28 | DEFAULT: "hsl(var(--secondary) / )",
29 | foreground: "hsl(var(--secondary-foreground) / )"
30 | },
31 | destructive: {
32 | DEFAULT: "hsl(var(--destructive) / )",
33 | foreground: "hsl(var(--destructive-foreground) / )"
34 | },
35 | muted: {
36 | DEFAULT: "hsl(var(--muted) / )",
37 | foreground: "hsl(var(--muted-foreground) / )"
38 | },
39 | accent: {
40 | DEFAULT: "hsl(var(--accent) / )",
41 | foreground: "hsl(var(--accent-foreground) / )"
42 | },
43 | popover: {
44 | DEFAULT: "hsl(var(--popover) / )",
45 | foreground: "hsl(var(--popover-foreground) / )"
46 | },
47 | card: {
48 | DEFAULT: "hsl(var(--card) / )",
49 | foreground: "hsl(var(--card-foreground) / )"
50 | }
51 | },
52 | borderRadius: {
53 | lg: "var(--radius)",
54 | md: "calc(var(--radius) - 2px)",
55 | sm: "calc(var(--radius) - 4px)"
56 | },
57 | fontFamily: {
58 | sans: [...fontFamily.sans]
59 | }
60 | }
61 | },
62 | };
63 |
64 | export default config;
65 |
--------------------------------------------------------------------------------
/apps/cms/README.md:
--------------------------------------------------------------------------------
1 | # Payload Blank Template
2 |
3 | A blank template for [Payload](https://github.com/payloadcms/payload) to help you get up and running quickly. This repo may have been created by running `npx create-payload-app@latest` and selecting the "blank" template or by cloning this template on [Payload Cloud](https://payloadcms.com/new/clone/blank).
4 |
5 | See the official [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) for details on how to use Payload in a variety of different ways.
6 |
7 | ## Development
8 |
9 | To spin up the project locally, follow these steps:
10 |
11 | 1. First clone the repo
12 | 1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env`
13 | 1. Next `yarn && yarn dev` (or `docker-compose up`, see [Docker](#docker))
14 | 1. Now `open http://localhost:3000/admin` to access the admin panel
15 | 1. Create your first admin user using the form on the page
16 |
17 | That's it! Changes made in `./src` will be reflected in your app.
18 |
19 | ### Docker
20 |
21 | Alternatively, you can use [Docker](https://www.docker.com) to spin up this project locally. To do so, follow these steps:
22 |
23 | 1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root
24 | 1. Next run `docker-compose up`
25 | 1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user
26 |
27 | That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams.
28 |
29 | ## Production
30 |
31 | To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps:
32 |
33 | 1. First invoke the `payload build` script by running `yarn build` or `npm run build` in your project root. This creates a `./build` directory with a production-ready admin bundle.
34 | 1. Then run `yarn serve` or `npm run serve` to run Node in production and serve Payload from the `./build` directory.
35 |
36 | ### Deployment
37 |
38 | The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details.
39 |
40 | ## Questions
41 |
42 | If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
43 |
--------------------------------------------------------------------------------
/apps/web/payload/payload-types.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | /**
4 | * This file was automatically generated by Payload.
5 | * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
6 | * and re-run `payload generate:types` to regenerate this file.
7 | */
8 |
9 | export interface Config {
10 | auth: {
11 | users: UserAuthOperations;
12 | };
13 | collections: {
14 | users: User;
15 | media: Media;
16 | 'payload-preferences': PayloadPreference;
17 | 'payload-migrations': PayloadMigration;
18 | };
19 | globals: {};
20 | locale: null;
21 | user: User & {
22 | collection: 'users';
23 | };
24 | }
25 | export interface UserAuthOperations {
26 | forgotPassword: {
27 | email: string;
28 | };
29 | login: {
30 | password: string;
31 | email: string;
32 | };
33 | registerFirstUser: {
34 | email: string;
35 | password: string;
36 | };
37 | }
38 | /**
39 | * This interface was referenced by `Config`'s JSON-Schema
40 | * via the `definition` "users".
41 | */
42 | export interface User {
43 | id: number;
44 | username: string;
45 | updatedAt: string;
46 | createdAt: string;
47 | email: string;
48 | resetPasswordToken?: string | null;
49 | resetPasswordExpiration?: string | null;
50 | salt?: string | null;
51 | hash?: string | null;
52 | loginAttempts?: number | null;
53 | lockUntil?: string | null;
54 | password?: string | null;
55 | }
56 | /**
57 | * This interface was referenced by `Config`'s JSON-Schema
58 | * via the `definition` "media".
59 | */
60 | export interface Media {
61 | id: number;
62 | alt: string;
63 | updatedAt: string;
64 | createdAt: string;
65 | url?: string | null;
66 | thumbnailURL?: string | null;
67 | filename?: string | null;
68 | mimeType?: string | null;
69 | filesize?: number | null;
70 | width?: number | null;
71 | height?: number | null;
72 | focalX?: number | null;
73 | focalY?: number | null;
74 | }
75 | /**
76 | * This interface was referenced by `Config`'s JSON-Schema
77 | * via the `definition` "payload-preferences".
78 | */
79 | export interface PayloadPreference {
80 | id: number;
81 | user: {
82 | relationTo: 'users';
83 | value: number | User;
84 | };
85 | key?: string | null;
86 | value?:
87 | | {
88 | [k: string]: unknown;
89 | }
90 | | unknown[]
91 | | string
92 | | number
93 | | boolean
94 | | null;
95 | updatedAt: string;
96 | createdAt: string;
97 | }
98 | /**
99 | * This interface was referenced by `Config`'s JSON-Schema
100 | * via the `definition` "payload-migrations".
101 | */
102 | export interface PayloadMigration {
103 | id: number;
104 | name?: string | null;
105 | batch?: number | null;
106 | updatedAt: string;
107 | createdAt: string;
108 | }
109 | /**
110 | * This interface was referenced by `Config`'s JSON-Schema
111 | * via the `definition` "auth".
112 | */
113 | export interface Auth {
114 | [k: string]: unknown;
115 | }
116 |
117 |
118 | declare module 'payload' {
119 | export interface GeneratedTypes extends Config {}
120 | }
--------------------------------------------------------------------------------
/apps/cms/src/config/payload-types.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | /**
4 | * This file was automatically generated by Payload.
5 | * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
6 | * and re-run `payload generate:types` to regenerate this file.
7 | */
8 |
9 | export interface Config {
10 | auth: {
11 | users: UserAuthOperations;
12 | };
13 | collections: {
14 | users: User;
15 | media: Media;
16 | 'payload-preferences': PayloadPreference;
17 | 'payload-migrations': PayloadMigration;
18 | };
19 | globals: {};
20 | locale: null;
21 | user: User & {
22 | collection: 'users';
23 | };
24 | }
25 | export interface UserAuthOperations {
26 | forgotPassword: {
27 | email: string;
28 | };
29 | login: {
30 | password: string;
31 | email: string;
32 | };
33 | registerFirstUser: {
34 | email: string;
35 | password: string;
36 | };
37 | }
38 | /**
39 | * This interface was referenced by `Config`'s JSON-Schema
40 | * via the `definition` "users".
41 | */
42 | export interface User {
43 | id: number;
44 | username: string;
45 | updatedAt: string;
46 | createdAt: string;
47 | email: string;
48 | resetPasswordToken?: string | null;
49 | resetPasswordExpiration?: string | null;
50 | salt?: string | null;
51 | hash?: string | null;
52 | loginAttempts?: number | null;
53 | lockUntil?: string | null;
54 | password?: string | null;
55 | }
56 | /**
57 | * This interface was referenced by `Config`'s JSON-Schema
58 | * via the `definition` "media".
59 | */
60 | export interface Media {
61 | id: number;
62 | alt: string;
63 | updatedAt: string;
64 | createdAt: string;
65 | url?: string | null;
66 | thumbnailURL?: string | null;
67 | filename?: string | null;
68 | mimeType?: string | null;
69 | filesize?: number | null;
70 | width?: number | null;
71 | height?: number | null;
72 | focalX?: number | null;
73 | focalY?: number | null;
74 | }
75 | /**
76 | * This interface was referenced by `Config`'s JSON-Schema
77 | * via the `definition` "payload-preferences".
78 | */
79 | export interface PayloadPreference {
80 | id: number;
81 | user: {
82 | relationTo: 'users';
83 | value: number | User;
84 | };
85 | key?: string | null;
86 | value?:
87 | | {
88 | [k: string]: unknown;
89 | }
90 | | unknown[]
91 | | string
92 | | number
93 | | boolean
94 | | null;
95 | updatedAt: string;
96 | createdAt: string;
97 | }
98 | /**
99 | * This interface was referenced by `Config`'s JSON-Schema
100 | * via the `definition` "payload-migrations".
101 | */
102 | export interface PayloadMigration {
103 | id: number;
104 | name?: string | null;
105 | batch?: number | null;
106 | updatedAt: string;
107 | createdAt: string;
108 | }
109 | /**
110 | * This interface was referenced by `Config`'s JSON-Schema
111 | * via the `definition` "auth".
112 | */
113 | export interface Auth {
114 | [k: string]: unknown;
115 | }
116 |
117 |
118 | declare module 'payload' {
119 | export interface GeneratedTypes extends Config {}
120 | }
--------------------------------------------------------------------------------
/apps/cms/src/migrations/20240705_163535_init.ts:
--------------------------------------------------------------------------------
1 | import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
2 |
3 | export async function up({ payload, req }: MigrateUpArgs): Promise {
4 | await payload.db.drizzle.execute(sql`
5 | CREATE TABLE IF NOT EXISTS "users" (
6 | "id" serial PRIMARY KEY NOT NULL,
7 | "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
8 | "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
9 | "email" varchar NOT NULL,
10 | "reset_password_token" varchar,
11 | "reset_password_expiration" timestamp(3) with time zone,
12 | "salt" varchar,
13 | "hash" varchar,
14 | "login_attempts" numeric,
15 | "lock_until" timestamp(3) with time zone
16 | );
17 |
18 | CREATE TABLE IF NOT EXISTS "media" (
19 | "id" serial PRIMARY KEY NOT NULL,
20 | "alt" varchar NOT NULL,
21 | "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
22 | "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
23 | "url" varchar,
24 | "thumbnail_u_r_l" varchar,
25 | "filename" varchar,
26 | "mime_type" varchar,
27 | "filesize" numeric,
28 | "width" numeric,
29 | "height" numeric,
30 | "focal_x" numeric,
31 | "focal_y" numeric
32 | );
33 |
34 | CREATE TABLE IF NOT EXISTS "payload_preferences" (
35 | "id" serial PRIMARY KEY NOT NULL,
36 | "key" varchar,
37 | "value" jsonb,
38 | "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
39 | "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
40 | );
41 |
42 | CREATE TABLE IF NOT EXISTS "payload_preferences_rels" (
43 | "id" serial PRIMARY KEY NOT NULL,
44 | "order" integer,
45 | "parent_id" integer NOT NULL,
46 | "path" varchar NOT NULL,
47 | "users_id" integer
48 | );
49 |
50 | CREATE TABLE IF NOT EXISTS "payload_migrations" (
51 | "id" serial PRIMARY KEY NOT NULL,
52 | "name" varchar,
53 | "batch" numeric,
54 | "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
55 | "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
56 | );
57 |
58 | CREATE INDEX IF NOT EXISTS "users_created_at_idx" ON "users" ("created_at");
59 | CREATE UNIQUE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
60 | CREATE INDEX IF NOT EXISTS "media_created_at_idx" ON "media" ("created_at");
61 | CREATE UNIQUE INDEX IF NOT EXISTS "media_filename_idx" ON "media" ("filename");
62 | CREATE INDEX IF NOT EXISTS "payload_preferences_key_idx" ON "payload_preferences" ("key");
63 | CREATE INDEX IF NOT EXISTS "payload_preferences_created_at_idx" ON "payload_preferences" ("created_at");
64 | CREATE INDEX IF NOT EXISTS "payload_preferences_rels_order_idx" ON "payload_preferences_rels" ("order");
65 | CREATE INDEX IF NOT EXISTS "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" ("parent_id");
66 | CREATE INDEX IF NOT EXISTS "payload_preferences_rels_path_idx" ON "payload_preferences_rels" ("path");
67 | CREATE INDEX IF NOT EXISTS "payload_migrations_created_at_idx" ON "payload_migrations" ("created_at");
68 | DO $$ BEGIN
69 | ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "payload_preferences"("id") ON DELETE cascade ON UPDATE no action;
70 | EXCEPTION
71 | WHEN duplicate_object THEN null;
72 | END $$;
73 |
74 | DO $$ BEGIN
75 | ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;
76 | EXCEPTION
77 | WHEN duplicate_object THEN null;
78 | END $$;
79 | `)
80 | };
81 |
82 | export async function down({ payload, req }: MigrateDownArgs): Promise {
83 | await payload.db.drizzle.execute(sql`
84 | DROP TABLE "users";
85 | DROP TABLE "media";
86 | DROP TABLE "payload_preferences";
87 | DROP TABLE "payload_preferences_rels";
88 | DROP TABLE "payload_migrations";`)
89 | };
90 |
--------------------------------------------------------------------------------
/apps/web/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
Sveltekit + Payload CMS
22 |
23 |
24 | User Table {users ? '🎉' : '⛔'}
25 | Doing a simple query to check if we can connect to Payload CMS.
28 |
29 |
30 | {#if users}
31 |
32 |
Create User
33 |
55 |
56 |
57 |
All Users
58 |
61 | ID
62 | Email
63 | Delete
64 |
65 | {#each users.docs as user}
66 |
67 |
{user.id}
68 |
69 | {user.email}
70 | {user.username}
71 |
72 |
73 | ❌
74 |
75 |
76 | Are you absolutely sure?
77 |
78 | This action cannot be undone. This will permanently delete `{user.email}` account from our servers.
81 |
82 |
83 |
84 | Cancel
85 |
104 |
105 |
106 |
107 |
108 | {/each}
109 |
110 | {:else}
111 | Can't connect to Payload CMS 😢
112 | {/if}
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/apps/cms/src/migrations/20240705_163535_init.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "0c223872-b2e7-44f2-888f-45268302ba4b",
3 | "prevId": "00000000-0000-0000-0000-000000000000",
4 | "version": "5",
5 | "dialect": "pg",
6 | "tables": {
7 | "users": {
8 | "name": "users",
9 | "schema": "",
10 | "columns": {
11 | "id": {
12 | "name": "id",
13 | "type": "serial",
14 | "primaryKey": true,
15 | "notNull": true
16 | },
17 | "updated_at": {
18 | "name": "updated_at",
19 | "type": "timestamp(3) with time zone",
20 | "primaryKey": false,
21 | "notNull": true,
22 | "default": "now()"
23 | },
24 | "created_at": {
25 | "name": "created_at",
26 | "type": "timestamp(3) with time zone",
27 | "primaryKey": false,
28 | "notNull": true,
29 | "default": "now()"
30 | },
31 | "email": {
32 | "name": "email",
33 | "type": "varchar",
34 | "primaryKey": false,
35 | "notNull": true
36 | },
37 | "reset_password_token": {
38 | "name": "reset_password_token",
39 | "type": "varchar",
40 | "primaryKey": false,
41 | "notNull": false
42 | },
43 | "reset_password_expiration": {
44 | "name": "reset_password_expiration",
45 | "type": "timestamp(3) with time zone",
46 | "primaryKey": false,
47 | "notNull": false
48 | },
49 | "salt": {
50 | "name": "salt",
51 | "type": "varchar",
52 | "primaryKey": false,
53 | "notNull": false
54 | },
55 | "hash": {
56 | "name": "hash",
57 | "type": "varchar",
58 | "primaryKey": false,
59 | "notNull": false
60 | },
61 | "login_attempts": {
62 | "name": "login_attempts",
63 | "type": "numeric",
64 | "primaryKey": false,
65 | "notNull": false
66 | },
67 | "lock_until": {
68 | "name": "lock_until",
69 | "type": "timestamp(3) with time zone",
70 | "primaryKey": false,
71 | "notNull": false
72 | }
73 | },
74 | "indexes": {
75 | "users_created_at_idx": {
76 | "name": "users_created_at_idx",
77 | "columns": [
78 | "created_at"
79 | ],
80 | "isUnique": false
81 | },
82 | "users_email_idx": {
83 | "name": "users_email_idx",
84 | "columns": [
85 | "email"
86 | ],
87 | "isUnique": true
88 | }
89 | },
90 | "foreignKeys": {},
91 | "compositePrimaryKeys": {},
92 | "uniqueConstraints": {}
93 | },
94 | "media": {
95 | "name": "media",
96 | "schema": "",
97 | "columns": {
98 | "id": {
99 | "name": "id",
100 | "type": "serial",
101 | "primaryKey": true,
102 | "notNull": true
103 | },
104 | "alt": {
105 | "name": "alt",
106 | "type": "varchar",
107 | "primaryKey": false,
108 | "notNull": true
109 | },
110 | "updated_at": {
111 | "name": "updated_at",
112 | "type": "timestamp(3) with time zone",
113 | "primaryKey": false,
114 | "notNull": true,
115 | "default": "now()"
116 | },
117 | "created_at": {
118 | "name": "created_at",
119 | "type": "timestamp(3) with time zone",
120 | "primaryKey": false,
121 | "notNull": true,
122 | "default": "now()"
123 | },
124 | "url": {
125 | "name": "url",
126 | "type": "varchar",
127 | "primaryKey": false,
128 | "notNull": false
129 | },
130 | "thumbnail_u_r_l": {
131 | "name": "thumbnail_u_r_l",
132 | "type": "varchar",
133 | "primaryKey": false,
134 | "notNull": false
135 | },
136 | "filename": {
137 | "name": "filename",
138 | "type": "varchar",
139 | "primaryKey": false,
140 | "notNull": false
141 | },
142 | "mime_type": {
143 | "name": "mime_type",
144 | "type": "varchar",
145 | "primaryKey": false,
146 | "notNull": false
147 | },
148 | "filesize": {
149 | "name": "filesize",
150 | "type": "numeric",
151 | "primaryKey": false,
152 | "notNull": false
153 | },
154 | "width": {
155 | "name": "width",
156 | "type": "numeric",
157 | "primaryKey": false,
158 | "notNull": false
159 | },
160 | "height": {
161 | "name": "height",
162 | "type": "numeric",
163 | "primaryKey": false,
164 | "notNull": false
165 | },
166 | "focal_x": {
167 | "name": "focal_x",
168 | "type": "numeric",
169 | "primaryKey": false,
170 | "notNull": false
171 | },
172 | "focal_y": {
173 | "name": "focal_y",
174 | "type": "numeric",
175 | "primaryKey": false,
176 | "notNull": false
177 | }
178 | },
179 | "indexes": {
180 | "media_created_at_idx": {
181 | "name": "media_created_at_idx",
182 | "columns": [
183 | "created_at"
184 | ],
185 | "isUnique": false
186 | },
187 | "media_filename_idx": {
188 | "name": "media_filename_idx",
189 | "columns": [
190 | "filename"
191 | ],
192 | "isUnique": true
193 | }
194 | },
195 | "foreignKeys": {},
196 | "compositePrimaryKeys": {},
197 | "uniqueConstraints": {}
198 | },
199 | "payload_preferences": {
200 | "name": "payload_preferences",
201 | "schema": "",
202 | "columns": {
203 | "id": {
204 | "name": "id",
205 | "type": "serial",
206 | "primaryKey": true,
207 | "notNull": true
208 | },
209 | "key": {
210 | "name": "key",
211 | "type": "varchar",
212 | "primaryKey": false,
213 | "notNull": false
214 | },
215 | "value": {
216 | "name": "value",
217 | "type": "jsonb",
218 | "primaryKey": false,
219 | "notNull": false
220 | },
221 | "updated_at": {
222 | "name": "updated_at",
223 | "type": "timestamp(3) with time zone",
224 | "primaryKey": false,
225 | "notNull": true,
226 | "default": "now()"
227 | },
228 | "created_at": {
229 | "name": "created_at",
230 | "type": "timestamp(3) with time zone",
231 | "primaryKey": false,
232 | "notNull": true,
233 | "default": "now()"
234 | }
235 | },
236 | "indexes": {
237 | "payload_preferences_key_idx": {
238 | "name": "payload_preferences_key_idx",
239 | "columns": [
240 | "key"
241 | ],
242 | "isUnique": false
243 | },
244 | "payload_preferences_created_at_idx": {
245 | "name": "payload_preferences_created_at_idx",
246 | "columns": [
247 | "created_at"
248 | ],
249 | "isUnique": false
250 | }
251 | },
252 | "foreignKeys": {},
253 | "compositePrimaryKeys": {},
254 | "uniqueConstraints": {}
255 | },
256 | "payload_preferences_rels": {
257 | "name": "payload_preferences_rels",
258 | "schema": "",
259 | "columns": {
260 | "id": {
261 | "name": "id",
262 | "type": "serial",
263 | "primaryKey": true,
264 | "notNull": true
265 | },
266 | "order": {
267 | "name": "order",
268 | "type": "integer",
269 | "primaryKey": false,
270 | "notNull": false
271 | },
272 | "parent_id": {
273 | "name": "parent_id",
274 | "type": "integer",
275 | "primaryKey": false,
276 | "notNull": true
277 | },
278 | "path": {
279 | "name": "path",
280 | "type": "varchar",
281 | "primaryKey": false,
282 | "notNull": true
283 | },
284 | "users_id": {
285 | "name": "users_id",
286 | "type": "integer",
287 | "primaryKey": false,
288 | "notNull": false
289 | }
290 | },
291 | "indexes": {
292 | "payload_preferences_rels_order_idx": {
293 | "name": "payload_preferences_rels_order_idx",
294 | "columns": [
295 | "order"
296 | ],
297 | "isUnique": false
298 | },
299 | "payload_preferences_rels_parent_idx": {
300 | "name": "payload_preferences_rels_parent_idx",
301 | "columns": [
302 | "parent_id"
303 | ],
304 | "isUnique": false
305 | },
306 | "payload_preferences_rels_path_idx": {
307 | "name": "payload_preferences_rels_path_idx",
308 | "columns": [
309 | "path"
310 | ],
311 | "isUnique": false
312 | }
313 | },
314 | "foreignKeys": {
315 | "payload_preferences_rels_parent_fk": {
316 | "name": "payload_preferences_rels_parent_fk",
317 | "tableFrom": "payload_preferences_rels",
318 | "tableTo": "payload_preferences",
319 | "columnsFrom": [
320 | "parent_id"
321 | ],
322 | "columnsTo": [
323 | "id"
324 | ],
325 | "onDelete": "cascade",
326 | "onUpdate": "no action"
327 | },
328 | "payload_preferences_rels_users_fk": {
329 | "name": "payload_preferences_rels_users_fk",
330 | "tableFrom": "payload_preferences_rels",
331 | "tableTo": "users",
332 | "columnsFrom": [
333 | "users_id"
334 | ],
335 | "columnsTo": [
336 | "id"
337 | ],
338 | "onDelete": "cascade",
339 | "onUpdate": "no action"
340 | }
341 | },
342 | "compositePrimaryKeys": {},
343 | "uniqueConstraints": {}
344 | },
345 | "payload_migrations": {
346 | "name": "payload_migrations",
347 | "schema": "",
348 | "columns": {
349 | "id": {
350 | "name": "id",
351 | "type": "serial",
352 | "primaryKey": true,
353 | "notNull": true
354 | },
355 | "name": {
356 | "name": "name",
357 | "type": "varchar",
358 | "primaryKey": false,
359 | "notNull": false
360 | },
361 | "batch": {
362 | "name": "batch",
363 | "type": "numeric",
364 | "primaryKey": false,
365 | "notNull": false
366 | },
367 | "updated_at": {
368 | "name": "updated_at",
369 | "type": "timestamp(3) with time zone",
370 | "primaryKey": false,
371 | "notNull": true,
372 | "default": "now()"
373 | },
374 | "created_at": {
375 | "name": "created_at",
376 | "type": "timestamp(3) with time zone",
377 | "primaryKey": false,
378 | "notNull": true,
379 | "default": "now()"
380 | }
381 | },
382 | "indexes": {
383 | "payload_migrations_created_at_idx": {
384 | "name": "payload_migrations_created_at_idx",
385 | "columns": [
386 | "created_at"
387 | ],
388 | "isUnique": false
389 | }
390 | },
391 | "foreignKeys": {},
392 | "compositePrimaryKeys": {},
393 | "uniqueConstraints": {}
394 | }
395 | },
396 | "enums": {},
397 | "schemas": {},
398 | "_meta": {
399 | "schemas": {},
400 | "tables": {},
401 | "columns": {}
402 | }
403 | }
--------------------------------------------------------------------------------
/apps/cms/src/migrations/20240706_165421.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "b3bc25ac-2357-46f1-a0aa-620799492502",
3 | "prevId": "00000000-0000-0000-0000-000000000000",
4 | "version": "5",
5 | "dialect": "pg",
6 | "tables": {
7 | "users": {
8 | "name": "users",
9 | "schema": "",
10 | "columns": {
11 | "id": {
12 | "name": "id",
13 | "type": "serial",
14 | "primaryKey": true,
15 | "notNull": true
16 | },
17 | "username": {
18 | "name": "username",
19 | "type": "varchar",
20 | "primaryKey": false,
21 | "notNull": false
22 | },
23 | "updated_at": {
24 | "name": "updated_at",
25 | "type": "timestamp(3) with time zone",
26 | "primaryKey": false,
27 | "notNull": true,
28 | "default": "now()"
29 | },
30 | "created_at": {
31 | "name": "created_at",
32 | "type": "timestamp(3) with time zone",
33 | "primaryKey": false,
34 | "notNull": true,
35 | "default": "now()"
36 | },
37 | "email": {
38 | "name": "email",
39 | "type": "varchar",
40 | "primaryKey": false,
41 | "notNull": true
42 | },
43 | "reset_password_token": {
44 | "name": "reset_password_token",
45 | "type": "varchar",
46 | "primaryKey": false,
47 | "notNull": false
48 | },
49 | "reset_password_expiration": {
50 | "name": "reset_password_expiration",
51 | "type": "timestamp(3) with time zone",
52 | "primaryKey": false,
53 | "notNull": false
54 | },
55 | "salt": {
56 | "name": "salt",
57 | "type": "varchar",
58 | "primaryKey": false,
59 | "notNull": false
60 | },
61 | "hash": {
62 | "name": "hash",
63 | "type": "varchar",
64 | "primaryKey": false,
65 | "notNull": false
66 | },
67 | "login_attempts": {
68 | "name": "login_attempts",
69 | "type": "numeric",
70 | "primaryKey": false,
71 | "notNull": false
72 | },
73 | "lock_until": {
74 | "name": "lock_until",
75 | "type": "timestamp(3) with time zone",
76 | "primaryKey": false,
77 | "notNull": false
78 | }
79 | },
80 | "indexes": {
81 | "users_created_at_idx": {
82 | "name": "users_created_at_idx",
83 | "columns": [
84 | "created_at"
85 | ],
86 | "isUnique": false
87 | },
88 | "users_email_idx": {
89 | "name": "users_email_idx",
90 | "columns": [
91 | "email"
92 | ],
93 | "isUnique": true
94 | }
95 | },
96 | "foreignKeys": {},
97 | "compositePrimaryKeys": {},
98 | "uniqueConstraints": {}
99 | },
100 | "media": {
101 | "name": "media",
102 | "schema": "",
103 | "columns": {
104 | "id": {
105 | "name": "id",
106 | "type": "serial",
107 | "primaryKey": true,
108 | "notNull": true
109 | },
110 | "alt": {
111 | "name": "alt",
112 | "type": "varchar",
113 | "primaryKey": false,
114 | "notNull": true
115 | },
116 | "updated_at": {
117 | "name": "updated_at",
118 | "type": "timestamp(3) with time zone",
119 | "primaryKey": false,
120 | "notNull": true,
121 | "default": "now()"
122 | },
123 | "created_at": {
124 | "name": "created_at",
125 | "type": "timestamp(3) with time zone",
126 | "primaryKey": false,
127 | "notNull": true,
128 | "default": "now()"
129 | },
130 | "url": {
131 | "name": "url",
132 | "type": "varchar",
133 | "primaryKey": false,
134 | "notNull": false
135 | },
136 | "thumbnail_u_r_l": {
137 | "name": "thumbnail_u_r_l",
138 | "type": "varchar",
139 | "primaryKey": false,
140 | "notNull": false
141 | },
142 | "filename": {
143 | "name": "filename",
144 | "type": "varchar",
145 | "primaryKey": false,
146 | "notNull": false
147 | },
148 | "mime_type": {
149 | "name": "mime_type",
150 | "type": "varchar",
151 | "primaryKey": false,
152 | "notNull": false
153 | },
154 | "filesize": {
155 | "name": "filesize",
156 | "type": "numeric",
157 | "primaryKey": false,
158 | "notNull": false
159 | },
160 | "width": {
161 | "name": "width",
162 | "type": "numeric",
163 | "primaryKey": false,
164 | "notNull": false
165 | },
166 | "height": {
167 | "name": "height",
168 | "type": "numeric",
169 | "primaryKey": false,
170 | "notNull": false
171 | },
172 | "focal_x": {
173 | "name": "focal_x",
174 | "type": "numeric",
175 | "primaryKey": false,
176 | "notNull": false
177 | },
178 | "focal_y": {
179 | "name": "focal_y",
180 | "type": "numeric",
181 | "primaryKey": false,
182 | "notNull": false
183 | }
184 | },
185 | "indexes": {
186 | "media_created_at_idx": {
187 | "name": "media_created_at_idx",
188 | "columns": [
189 | "created_at"
190 | ],
191 | "isUnique": false
192 | },
193 | "media_filename_idx": {
194 | "name": "media_filename_idx",
195 | "columns": [
196 | "filename"
197 | ],
198 | "isUnique": true
199 | }
200 | },
201 | "foreignKeys": {},
202 | "compositePrimaryKeys": {},
203 | "uniqueConstraints": {}
204 | },
205 | "payload_preferences": {
206 | "name": "payload_preferences",
207 | "schema": "",
208 | "columns": {
209 | "id": {
210 | "name": "id",
211 | "type": "serial",
212 | "primaryKey": true,
213 | "notNull": true
214 | },
215 | "key": {
216 | "name": "key",
217 | "type": "varchar",
218 | "primaryKey": false,
219 | "notNull": false
220 | },
221 | "value": {
222 | "name": "value",
223 | "type": "jsonb",
224 | "primaryKey": false,
225 | "notNull": false
226 | },
227 | "updated_at": {
228 | "name": "updated_at",
229 | "type": "timestamp(3) with time zone",
230 | "primaryKey": false,
231 | "notNull": true,
232 | "default": "now()"
233 | },
234 | "created_at": {
235 | "name": "created_at",
236 | "type": "timestamp(3) with time zone",
237 | "primaryKey": false,
238 | "notNull": true,
239 | "default": "now()"
240 | }
241 | },
242 | "indexes": {
243 | "payload_preferences_key_idx": {
244 | "name": "payload_preferences_key_idx",
245 | "columns": [
246 | "key"
247 | ],
248 | "isUnique": false
249 | },
250 | "payload_preferences_created_at_idx": {
251 | "name": "payload_preferences_created_at_idx",
252 | "columns": [
253 | "created_at"
254 | ],
255 | "isUnique": false
256 | }
257 | },
258 | "foreignKeys": {},
259 | "compositePrimaryKeys": {},
260 | "uniqueConstraints": {}
261 | },
262 | "payload_preferences_rels": {
263 | "name": "payload_preferences_rels",
264 | "schema": "",
265 | "columns": {
266 | "id": {
267 | "name": "id",
268 | "type": "serial",
269 | "primaryKey": true,
270 | "notNull": true
271 | },
272 | "order": {
273 | "name": "order",
274 | "type": "integer",
275 | "primaryKey": false,
276 | "notNull": false
277 | },
278 | "parent_id": {
279 | "name": "parent_id",
280 | "type": "integer",
281 | "primaryKey": false,
282 | "notNull": true
283 | },
284 | "path": {
285 | "name": "path",
286 | "type": "varchar",
287 | "primaryKey": false,
288 | "notNull": true
289 | },
290 | "users_id": {
291 | "name": "users_id",
292 | "type": "integer",
293 | "primaryKey": false,
294 | "notNull": false
295 | }
296 | },
297 | "indexes": {
298 | "payload_preferences_rels_order_idx": {
299 | "name": "payload_preferences_rels_order_idx",
300 | "columns": [
301 | "order"
302 | ],
303 | "isUnique": false
304 | },
305 | "payload_preferences_rels_parent_idx": {
306 | "name": "payload_preferences_rels_parent_idx",
307 | "columns": [
308 | "parent_id"
309 | ],
310 | "isUnique": false
311 | },
312 | "payload_preferences_rels_path_idx": {
313 | "name": "payload_preferences_rels_path_idx",
314 | "columns": [
315 | "path"
316 | ],
317 | "isUnique": false
318 | }
319 | },
320 | "foreignKeys": {
321 | "payload_preferences_rels_parent_fk": {
322 | "name": "payload_preferences_rels_parent_fk",
323 | "tableFrom": "payload_preferences_rels",
324 | "tableTo": "payload_preferences",
325 | "columnsFrom": [
326 | "parent_id"
327 | ],
328 | "columnsTo": [
329 | "id"
330 | ],
331 | "onDelete": "cascade",
332 | "onUpdate": "no action"
333 | },
334 | "payload_preferences_rels_users_fk": {
335 | "name": "payload_preferences_rels_users_fk",
336 | "tableFrom": "payload_preferences_rels",
337 | "tableTo": "users",
338 | "columnsFrom": [
339 | "users_id"
340 | ],
341 | "columnsTo": [
342 | "id"
343 | ],
344 | "onDelete": "cascade",
345 | "onUpdate": "no action"
346 | }
347 | },
348 | "compositePrimaryKeys": {},
349 | "uniqueConstraints": {}
350 | },
351 | "payload_migrations": {
352 | "name": "payload_migrations",
353 | "schema": "",
354 | "columns": {
355 | "id": {
356 | "name": "id",
357 | "type": "serial",
358 | "primaryKey": true,
359 | "notNull": true
360 | },
361 | "name": {
362 | "name": "name",
363 | "type": "varchar",
364 | "primaryKey": false,
365 | "notNull": false
366 | },
367 | "batch": {
368 | "name": "batch",
369 | "type": "numeric",
370 | "primaryKey": false,
371 | "notNull": false
372 | },
373 | "updated_at": {
374 | "name": "updated_at",
375 | "type": "timestamp(3) with time zone",
376 | "primaryKey": false,
377 | "notNull": true,
378 | "default": "now()"
379 | },
380 | "created_at": {
381 | "name": "created_at",
382 | "type": "timestamp(3) with time zone",
383 | "primaryKey": false,
384 | "notNull": true,
385 | "default": "now()"
386 | }
387 | },
388 | "indexes": {
389 | "payload_migrations_created_at_idx": {
390 | "name": "payload_migrations_created_at_idx",
391 | "columns": [
392 | "created_at"
393 | ],
394 | "isUnique": false
395 | }
396 | },
397 | "foreignKeys": {},
398 | "compositePrimaryKeys": {},
399 | "uniqueConstraints": {}
400 | }
401 | },
402 | "enums": {},
403 | "schemas": {},
404 | "_meta": {
405 | "schemas": {},
406 | "tables": {},
407 | "columns": {}
408 | }
409 | }
--------------------------------------------------------------------------------
/apps/cms/src/migrations/20240706_173841.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "b9633e24-0c10-4e6f-b974-72165682f2e4",
3 | "prevId": "00000000-0000-0000-0000-000000000000",
4 | "version": "5",
5 | "dialect": "pg",
6 | "tables": {
7 | "users": {
8 | "name": "users",
9 | "schema": "",
10 | "columns": {
11 | "id": {
12 | "name": "id",
13 | "type": "serial",
14 | "primaryKey": true,
15 | "notNull": true
16 | },
17 | "username": {
18 | "name": "username",
19 | "type": "varchar",
20 | "primaryKey": false,
21 | "notNull": true
22 | },
23 | "updated_at": {
24 | "name": "updated_at",
25 | "type": "timestamp(3) with time zone",
26 | "primaryKey": false,
27 | "notNull": true,
28 | "default": "now()"
29 | },
30 | "created_at": {
31 | "name": "created_at",
32 | "type": "timestamp(3) with time zone",
33 | "primaryKey": false,
34 | "notNull": true,
35 | "default": "now()"
36 | },
37 | "email": {
38 | "name": "email",
39 | "type": "varchar",
40 | "primaryKey": false,
41 | "notNull": true
42 | },
43 | "reset_password_token": {
44 | "name": "reset_password_token",
45 | "type": "varchar",
46 | "primaryKey": false,
47 | "notNull": false
48 | },
49 | "reset_password_expiration": {
50 | "name": "reset_password_expiration",
51 | "type": "timestamp(3) with time zone",
52 | "primaryKey": false,
53 | "notNull": false
54 | },
55 | "salt": {
56 | "name": "salt",
57 | "type": "varchar",
58 | "primaryKey": false,
59 | "notNull": false
60 | },
61 | "hash": {
62 | "name": "hash",
63 | "type": "varchar",
64 | "primaryKey": false,
65 | "notNull": false
66 | },
67 | "login_attempts": {
68 | "name": "login_attempts",
69 | "type": "numeric",
70 | "primaryKey": false,
71 | "notNull": false
72 | },
73 | "lock_until": {
74 | "name": "lock_until",
75 | "type": "timestamp(3) with time zone",
76 | "primaryKey": false,
77 | "notNull": false
78 | }
79 | },
80 | "indexes": {
81 | "users_username_idx": {
82 | "name": "users_username_idx",
83 | "columns": [
84 | "username"
85 | ],
86 | "isUnique": true
87 | },
88 | "users_created_at_idx": {
89 | "name": "users_created_at_idx",
90 | "columns": [
91 | "created_at"
92 | ],
93 | "isUnique": false
94 | },
95 | "users_email_idx": {
96 | "name": "users_email_idx",
97 | "columns": [
98 | "email"
99 | ],
100 | "isUnique": true
101 | }
102 | },
103 | "foreignKeys": {},
104 | "compositePrimaryKeys": {},
105 | "uniqueConstraints": {}
106 | },
107 | "media": {
108 | "name": "media",
109 | "schema": "",
110 | "columns": {
111 | "id": {
112 | "name": "id",
113 | "type": "serial",
114 | "primaryKey": true,
115 | "notNull": true
116 | },
117 | "alt": {
118 | "name": "alt",
119 | "type": "varchar",
120 | "primaryKey": false,
121 | "notNull": true
122 | },
123 | "updated_at": {
124 | "name": "updated_at",
125 | "type": "timestamp(3) with time zone",
126 | "primaryKey": false,
127 | "notNull": true,
128 | "default": "now()"
129 | },
130 | "created_at": {
131 | "name": "created_at",
132 | "type": "timestamp(3) with time zone",
133 | "primaryKey": false,
134 | "notNull": true,
135 | "default": "now()"
136 | },
137 | "url": {
138 | "name": "url",
139 | "type": "varchar",
140 | "primaryKey": false,
141 | "notNull": false
142 | },
143 | "thumbnail_u_r_l": {
144 | "name": "thumbnail_u_r_l",
145 | "type": "varchar",
146 | "primaryKey": false,
147 | "notNull": false
148 | },
149 | "filename": {
150 | "name": "filename",
151 | "type": "varchar",
152 | "primaryKey": false,
153 | "notNull": false
154 | },
155 | "mime_type": {
156 | "name": "mime_type",
157 | "type": "varchar",
158 | "primaryKey": false,
159 | "notNull": false
160 | },
161 | "filesize": {
162 | "name": "filesize",
163 | "type": "numeric",
164 | "primaryKey": false,
165 | "notNull": false
166 | },
167 | "width": {
168 | "name": "width",
169 | "type": "numeric",
170 | "primaryKey": false,
171 | "notNull": false
172 | },
173 | "height": {
174 | "name": "height",
175 | "type": "numeric",
176 | "primaryKey": false,
177 | "notNull": false
178 | },
179 | "focal_x": {
180 | "name": "focal_x",
181 | "type": "numeric",
182 | "primaryKey": false,
183 | "notNull": false
184 | },
185 | "focal_y": {
186 | "name": "focal_y",
187 | "type": "numeric",
188 | "primaryKey": false,
189 | "notNull": false
190 | }
191 | },
192 | "indexes": {
193 | "media_created_at_idx": {
194 | "name": "media_created_at_idx",
195 | "columns": [
196 | "created_at"
197 | ],
198 | "isUnique": false
199 | },
200 | "media_filename_idx": {
201 | "name": "media_filename_idx",
202 | "columns": [
203 | "filename"
204 | ],
205 | "isUnique": true
206 | }
207 | },
208 | "foreignKeys": {},
209 | "compositePrimaryKeys": {},
210 | "uniqueConstraints": {}
211 | },
212 | "payload_preferences": {
213 | "name": "payload_preferences",
214 | "schema": "",
215 | "columns": {
216 | "id": {
217 | "name": "id",
218 | "type": "serial",
219 | "primaryKey": true,
220 | "notNull": true
221 | },
222 | "key": {
223 | "name": "key",
224 | "type": "varchar",
225 | "primaryKey": false,
226 | "notNull": false
227 | },
228 | "value": {
229 | "name": "value",
230 | "type": "jsonb",
231 | "primaryKey": false,
232 | "notNull": false
233 | },
234 | "updated_at": {
235 | "name": "updated_at",
236 | "type": "timestamp(3) with time zone",
237 | "primaryKey": false,
238 | "notNull": true,
239 | "default": "now()"
240 | },
241 | "created_at": {
242 | "name": "created_at",
243 | "type": "timestamp(3) with time zone",
244 | "primaryKey": false,
245 | "notNull": true,
246 | "default": "now()"
247 | }
248 | },
249 | "indexes": {
250 | "payload_preferences_key_idx": {
251 | "name": "payload_preferences_key_idx",
252 | "columns": [
253 | "key"
254 | ],
255 | "isUnique": false
256 | },
257 | "payload_preferences_created_at_idx": {
258 | "name": "payload_preferences_created_at_idx",
259 | "columns": [
260 | "created_at"
261 | ],
262 | "isUnique": false
263 | }
264 | },
265 | "foreignKeys": {},
266 | "compositePrimaryKeys": {},
267 | "uniqueConstraints": {}
268 | },
269 | "payload_preferences_rels": {
270 | "name": "payload_preferences_rels",
271 | "schema": "",
272 | "columns": {
273 | "id": {
274 | "name": "id",
275 | "type": "serial",
276 | "primaryKey": true,
277 | "notNull": true
278 | },
279 | "order": {
280 | "name": "order",
281 | "type": "integer",
282 | "primaryKey": false,
283 | "notNull": false
284 | },
285 | "parent_id": {
286 | "name": "parent_id",
287 | "type": "integer",
288 | "primaryKey": false,
289 | "notNull": true
290 | },
291 | "path": {
292 | "name": "path",
293 | "type": "varchar",
294 | "primaryKey": false,
295 | "notNull": true
296 | },
297 | "users_id": {
298 | "name": "users_id",
299 | "type": "integer",
300 | "primaryKey": false,
301 | "notNull": false
302 | }
303 | },
304 | "indexes": {
305 | "payload_preferences_rels_order_idx": {
306 | "name": "payload_preferences_rels_order_idx",
307 | "columns": [
308 | "order"
309 | ],
310 | "isUnique": false
311 | },
312 | "payload_preferences_rels_parent_idx": {
313 | "name": "payload_preferences_rels_parent_idx",
314 | "columns": [
315 | "parent_id"
316 | ],
317 | "isUnique": false
318 | },
319 | "payload_preferences_rels_path_idx": {
320 | "name": "payload_preferences_rels_path_idx",
321 | "columns": [
322 | "path"
323 | ],
324 | "isUnique": false
325 | }
326 | },
327 | "foreignKeys": {
328 | "payload_preferences_rels_parent_fk": {
329 | "name": "payload_preferences_rels_parent_fk",
330 | "tableFrom": "payload_preferences_rels",
331 | "tableTo": "payload_preferences",
332 | "columnsFrom": [
333 | "parent_id"
334 | ],
335 | "columnsTo": [
336 | "id"
337 | ],
338 | "onDelete": "cascade",
339 | "onUpdate": "no action"
340 | },
341 | "payload_preferences_rels_users_fk": {
342 | "name": "payload_preferences_rels_users_fk",
343 | "tableFrom": "payload_preferences_rels",
344 | "tableTo": "users",
345 | "columnsFrom": [
346 | "users_id"
347 | ],
348 | "columnsTo": [
349 | "id"
350 | ],
351 | "onDelete": "cascade",
352 | "onUpdate": "no action"
353 | }
354 | },
355 | "compositePrimaryKeys": {},
356 | "uniqueConstraints": {}
357 | },
358 | "payload_migrations": {
359 | "name": "payload_migrations",
360 | "schema": "",
361 | "columns": {
362 | "id": {
363 | "name": "id",
364 | "type": "serial",
365 | "primaryKey": true,
366 | "notNull": true
367 | },
368 | "name": {
369 | "name": "name",
370 | "type": "varchar",
371 | "primaryKey": false,
372 | "notNull": false
373 | },
374 | "batch": {
375 | "name": "batch",
376 | "type": "numeric",
377 | "primaryKey": false,
378 | "notNull": false
379 | },
380 | "updated_at": {
381 | "name": "updated_at",
382 | "type": "timestamp(3) with time zone",
383 | "primaryKey": false,
384 | "notNull": true,
385 | "default": "now()"
386 | },
387 | "created_at": {
388 | "name": "created_at",
389 | "type": "timestamp(3) with time zone",
390 | "primaryKey": false,
391 | "notNull": true,
392 | "default": "now()"
393 | }
394 | },
395 | "indexes": {
396 | "payload_migrations_created_at_idx": {
397 | "name": "payload_migrations_created_at_idx",
398 | "columns": [
399 | "created_at"
400 | ],
401 | "isUnique": false
402 | }
403 | },
404 | "foreignKeys": {},
405 | "compositePrimaryKeys": {},
406 | "uniqueConstraints": {}
407 | }
408 | },
409 | "enums": {},
410 | "schemas": {},
411 | "_meta": {
412 | "schemas": {},
413 | "tables": {},
414 | "columns": {}
415 | }
416 | }
--------------------------------------------------------------------------------