Initial commit

This commit is contained in:
2026-02-10 01:25:57 +01:00
commit 7020a6a339
53 changed files with 1503 additions and 0 deletions

View File

@ -0,0 +1,9 @@
{
"name": "@cms/config",
"version": "0.0.1",
"private": true,
"exports": {
"./tsconfig/base": "./tsconfig/base.json",
"./tsconfig/next": "./tsconfig/next.json"
}
}

View File

@ -0,0 +1,17 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"allowJs": false,
"noEmit": true,
"esModuleInterop": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": "."
}
}

View File

@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"allowSyntheticDefaultImports": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
]
}
}

View File

@ -0,0 +1,22 @@
{
"name": "@cms/content",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "biome check src",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"zod": "latest"
},
"devDependencies": {
"@cms/config": "workspace:*",
"@biomejs/biome": "latest",
"typescript": "latest"
}
}

View File

@ -0,0 +1,23 @@
import { z } from "zod"
export const postStatusSchema = z.enum(["draft", "published"])
export const postSchema = z.object({
id: z.string().uuid(),
title: z.string().min(3).max(180),
slug: z.string().min(3).max(180),
excerpt: z.string().max(320).optional(),
body: z.string().min(1),
status: postStatusSchema,
createdAt: z.date(),
updatedAt: z.date(),
})
export const upsertPostSchema = postSchema.omit({
id: true,
createdAt: true,
updatedAt: true,
})
export type Post = z.infer<typeof postSchema>
export type UpsertPostInput = z.infer<typeof upsertPostSchema>

View File

@ -0,0 +1,8 @@
{
"extends": "@cms/config/tsconfig/base",
"compilerOptions": {
"noEmit": false,
"outDir": "dist"
},
"include": ["src/**/*.ts"]
}

37
packages/db/package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "@cms/db",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "biome check src prisma/seed.ts",
"typecheck": "tsc -p tsconfig.json --noEmit",
"db:generate": "bun --env-file=../../.env prisma generate",
"db:migrate": "bun --env-file=../../.env prisma migrate dev",
"db:push": "bun --env-file=../../.env prisma db push",
"db:studio": "bun --env-file=../../.env prisma studio",
"db:seed": "bun --env-file=../../.env prisma/seed.ts"
},
"dependencies": {
"@cms/content": "workspace:*",
"@prisma/adapter-pg": "latest",
"@prisma/client": "latest",
"pg": "latest",
"zod": "latest"
},
"devDependencies": {
"@cms/config": "workspace:*",
"@biomejs/biome": "latest",
"@types/node": "latest",
"@types/pg": "latest",
"prisma": "latest",
"typescript": "latest"
},
"prisma": {
"seed": "bun --env-file=../../.env prisma/seed.ts"
}
}

View File

@ -0,0 +1,12 @@
import { defineConfig, env } from "prisma/config"
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: "bun prisma/seed.ts",
},
datasource: {
url: env("DATABASE_URL"),
},
})

View File

@ -0,0 +1,18 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
}
model Post {
id String @id @default(uuid())
title String
slug String @unique
excerpt String?
body String
status String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

View File

@ -0,0 +1,24 @@
import { db } from "../src/client"
async function main() {
await db.post.upsert({
where: { slug: "welcome" },
update: {},
create: {
title: "Welcome to your CMS",
slug: "welcome",
excerpt: "Your first seeded post",
body: "Edit or delete this post from your admin area.",
status: "published",
},
})
}
main()
.catch((error) => {
console.error(error)
process.exit(1)
})
.finally(async () => {
await db.$disconnect()
})

22
packages/db/src/client.ts Normal file
View File

@ -0,0 +1,22 @@
import { PrismaPg } from "@prisma/adapter-pg"
import { PrismaClient } from "@prisma/client"
import { Pool } from "pg"
const connectionString = process.env.DATABASE_URL
if (!connectionString) {
throw new Error("DATABASE_URL is not set")
}
const pool = new Pool({ connectionString })
const adapter = new PrismaPg(pool)
declare global {
var prisma: PrismaClient | undefined
}
export const db = globalThis.prisma ?? new PrismaClient({ adapter })
if (process.env.NODE_ENV !== "production") {
globalThis.prisma = db
}

2
packages/db/src/index.ts Normal file
View File

@ -0,0 +1,2 @@
export { db } from "./client"
export { createPost, listPosts } from "./posts"

19
packages/db/src/posts.ts Normal file
View File

@ -0,0 +1,19 @@
import { upsertPostSchema } from "@cms/content"
import { db } from "./client"
export async function listPosts() {
return db.post.findMany({
orderBy: {
updatedAt: "desc",
},
})
}
export async function createPost(input: unknown) {
const payload = upsertPostSchema.parse(input)
return db.post.create({
data: payload,
})
}

View File

@ -0,0 +1,9 @@
{
"extends": "@cms/config/tsconfig/base",
"compilerOptions": {
"noEmit": false,
"types": ["node"],
"outDir": "dist"
},
"include": ["src/**/*.ts", "prisma/**/*.ts"]
}

View File

@ -0,0 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "../../apps/web/src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@cms/ui/components",
"utils": "@cms/ui/utils"
}
}

32
packages/ui/package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "@cms/ui",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./button": "./src/components/button.tsx",
"./utils": "./src/lib/utils.ts"
},
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "biome check src",
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"class-variance-authority": "latest",
"clsx": "latest",
"tailwind-merge": "latest"
},
"peerDependencies": {
"react": "latest",
"react-dom": "latest"
},
"devDependencies": {
"@cms/config": "workspace:*",
"@biomejs/biome": "latest",
"@types/react": "latest",
"@types/react-dom": "latest",
"typescript": "latest"
}
}

View File

@ -0,0 +1,34 @@
import { cva, type VariantProps } from "class-variance-authority"
import type { ButtonHTMLAttributes } from "react"
import { cn } from "../lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-white",
{
variants: {
variant: {
default: "bg-neutral-900 text-neutral-50 hover:bg-neutral-800",
secondary: "bg-neutral-100 text-neutral-900 hover:bg-neutral-200",
ghost: "hover:bg-neutral-100 hover:text-neutral-900",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
export function Button({ className, size, variant, ...props }: ButtonProps) {
return <button className={cn(buttonVariants({ variant, size }), className)} {...props} />
}

2
packages/ui/src/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from "./components/button"
export * from "./lib/utils"

View File

@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View File

@ -0,0 +1,9 @@
{
"extends": "@cms/config/tsconfig/base",
"compilerOptions": {
"jsx": "react-jsx",
"noEmit": false,
"outDir": "dist"
},
"include": ["src/**/*.ts", "src/**/*.tsx"]
}