Public

demo

Commits

Initial commit

Minertti · Oct 23, 2026 · 732e949d109786055170bf44cf4dc21e79613a54

2 files changed

+205−0
README.mdAdded+94−0
@@ -0,0 +1,94 @@
1https://www.achromatic.dev/docs/starter-kits/pro-nextjs-drizzle
2npm install
3npm run db:migrate
4npm run dev
5
6# SaaS Template
7
8A production-ready SaaS boilerplate with authentication, billing, organizations and more.
9
10## Tech Stack
11
12| Category | Technologies |
13| ----------------- | ------------------------------------------ |
14| **Framework** | Next.js 16.3, React 19, TypeScript 7 |
15| **Styling** | Tailwind CSS 4, Base UI, Lucide Icons |
16| **Data** | tRPC, React Query, Drizzle ORM, PostgreSQL |
17| **Auth** | Better Auth (email, Google OAuth, 2FA) |
18| **Billing** | Stripe (subscriptions, per-seat, one-time) |
19| **Email** | Resend, React Email |
20| **Storage** | S3-compatible (AWS S3 / Cloudflare R2) |
21| **Observability** | Sentry, Google Analytics 4 |
22
23## Quick Start
24
25```bash
26npm install
27cp .env.example .env
28# Edit .env: set DATABASE_URL and BETTER_AUTH_SECRET
29npm run db:migrate
30npm run dev
31```
32
33Open [http://localhost:3000](http://localhost:3000)
34
35**Full setup guide:** [README_QUICKSTART.md](./README_QUICKSTART.md)
36
37## Documentation
38
39| Document | Description |
40| ---------------------------------------------------- | -------------------------------------------------------------- |
41| [README_QUICKSTART.md](./README_QUICKSTART.md) | Step-by-step setup guide |
42| [README_DATABASE.md](./README_DATABASE.md) | Drizzle ORM, schema, migrations |
43| [README_AUTH.md](./README_AUTH.md) | Authentication, roles, sessions, 2FA |
44| [README_TRPC.md](./README_TRPC.md) | tRPC API layer and procedures |
45| [README_BILLING.md](./README_BILLING.md) | Stripe billing, subscriptions, webhooks |
46| [README_STRIPE.md](./README_STRIPE.md) | Stripe local testing, production webhooks, and required events |
47| [README_EMAIL.md](./README_EMAIL.md) | Resend email system and templates |
48| [README_MCP.md](./README_MCP.md) | Local read-only MCP server |
49| [README_STORAGE.md](./README_STORAGE.md) | S3-compatible file storage |
50| [README_CONTENT.md](./README_CONTENT.md) | Blog, docs and marketing pages |
51| [README_AI.md](./README_AI.md) | AI chatbot system |
52| [README_OBSERVABILITY.md](./README_OBSERVABILITY.md) | Sentry, logging, analytics |
53| [README_DEPLOYMENT.md](./README_DEPLOYMENT.md) | Vercel deployment guide |
54| [AGENTS.md](./AGENTS.md) | AI agent guidelines (Cursor, Claude, Copilot) |
55| [.env.example](./.env.example) | All environment variables |
56| [LICENSE](./LICENSE) | Achromatic License |
57
58## Commands
59
60| Command | Description |
61| ----------------------- | ------------------------------- |
62| `npm run dev` | Start development server |
63| `npm run build` | Build for production |
64| `npm run db:migrate` | Run database migrations |
65| `npm run db:studio` | Open Drizzle Studio (DB GUI) |
66| `npm run docker:up` | Start PostgreSQL |
67| `npm run stripe:listen` | Forward Stripe webhooks locally |
68| `npm run mcp:build` | Compile the local MCP server |
69| `npm run mcp:start` | Start the MCP server over stdio |
70| `npm run deps:check` | Check for dependency updates |
71| `npm run deps:update` | Update package.json versions |
72
73## Configuration
74
75### AI Credits
76
77Stripe products and prices are generated from the app's billing configuration. For Stripe keys, local webhook testing, and production setup, follow [README_STRIPE.md](./README_STRIPE.md).
78
79## Project Structure
80
81```
82app/
83├── (marketing)/ # Public pages (landing, blog, docs)
84├── (saas)/ # Protected app (dashboard, settings)
85└── api/ # API routes (auth, webhooks, tRPC)
86components/ # React components
87config/ # App, auth, billing configuration
88lib/ # Core libraries (auth, billing, db, email)
89trpc/ # tRPC API routers
90```
91
92## License
93
94[Achromatic License](./LICENSE)
content-collections.tsAdded+111−0
@@ -0,0 +1,111 @@
1import { defineCollection, defineConfig } from "@content-collections/core";
2import { compileMDX } from "@content-collections/mdx";
3import {
4 createDocSchema,
5 createMetaSchema,
6 transformMDX,
7} from "@fumadocs/content-collections/configuration";
8import rehypeShiki from "@shikijs/rehype";
9import { remarkImage } from "fumadocs-core/mdx-plugins";
10import { z } from "zod/v4";
11
12function sanitizePath(path: string) {
13 return path
14 .replace(/(\.[a-zA-Z-]{2,5})$/, "")
15 .replace(/^\//, "")
16 .replace(/\/$/, "")
17 .replace(/index$/, "");
18}
19
20// Blog posts collection
21const posts = defineCollection({
22 name: "posts",
23 directory: "content/posts",
24 include: "**/*.{mdx,md}",
25 schema: z.object({
26 title: z.string(),
27 date: z.string(),
28 image: z.string().optional(),
29 authorName: z.string(),
30 authorImage: z.string().optional(),
31 authorLink: z.string().optional(),
32 excerpt: z.string().optional(),
33 tags: z.array(z.string()),
34 published: z.boolean(),
35 content: z.string(),
36 }),
37 transform: async (document, context) => {
38 const body = await compileMDX(context, document, {
39 rehypePlugins: [
40 [
41 rehypeShiki,
42 {
43 theme: "nord",
44 },
45 ],
46 ],
47 });
48
49 return {
50 ...document,
51 body,
52 rawBody: document.content,
53 path: sanitizePath(document._meta.path),
54 };
55 },
56});
57
58// Legal pages collection (privacy, terms, etc.)
59const legalPages = defineCollection({
60 name: "legalPages",
61 directory: "content/legal",
62 include: "**/*.{mdx,md}",
63 schema: z.object({
64 title: z.string(),
65 content: z.string(),
66 }),
67 transform: async (document, context) => {
68 const body = await compileMDX(context, document);
69
70 return {
71 ...document,
72 body,
73 path: sanitizePath(document._meta.path),
74 };
75 },
76});
77
78// Documentation collection (using fumadocs)
79const docs = defineCollection({
80 name: "docs",
81 directory: "content/docs",
82 include: "**/*.mdx",
83 schema: z.object({
84 ...createDocSchema(z),
85 content: z.string(),
86 }),
87 transform: async (document, context) =>
88 transformMDX(document, context, {
89 remarkPlugins: [
90 [
91 remarkImage,
92 {
93 publicDir: "public",
94 },
95 ],
96 ],
97 }),
98});
99
100// Documentation metadata collection
101const docsMeta = defineCollection({
102 name: "docsMeta",
103 directory: "content/docs",
104 include: "**/meta*.json",
105 parser: "json",
106 schema: z.object(createMetaSchema(z)),
107});
108
109export default defineConfig({
110 content: [posts, legalPages, docs, docsMeta],
111});