Skip to main content

saas-scaffolder

"Generate — Generates complete, production-ready SaaS project boilerplate including authentication, database schemas, billing"

Zur Installation springen

Quellinformationen

Repository
thiagofernandes1987-create/APEX
Letzte Quellaktivität
18. April 2026 um 09:35
Erkannte Sprache von SKILL.md
Englisch
Sterne
2
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
saas-scaffolder
description
"Generate — Generates complete, production-ready SaaS project boilerplate including authentication, database schemas, billing"
# SaaS Scaffolder **Tier:** POWERFUL **Category:** Product Team **Domain:** Full-Stack Development / Project Bootstrapping --- ## Input Format ``` Product: [name] Description: [1-3 sentences] Auth: nextauth | clerk | supabase Database: neondb | supabase | planetscale Payments: stripe | lemonsqueezy | none Features: [comma-separated list] ``` --- ## File Tree Output ``` my-saas/ ├── app/ │ ├── (auth)/ │ │ ├── login/page.tsx │ │ ├── register/page.tsx │ │ └── layout.tsx │ ├── (dashboard)/ │ │ ├── dashboard/page.tsx │ │ ├── settings/page.tsx │ │ ├── billing/page.tsx │ │ └── layout.tsx │ ├── (marketing)/ │ │ ├── page.tsx │ │ ├── pricing/page.tsx │ │ └── layout.tsx │ ├── api/ │ │ ├── auth/[...nextauth]/route.ts │ │ ├── webhooks/stripe/route.ts │ │ ├── billing/checkout/route.ts │ │ └── billing/portal/route.ts │ └── layout.tsx ├── components/ │ ├── ui/ │ ├── auth/ │ │ ├── login-form.tsx │ │ └── register-form.tsx │ ├── dashboard/ │ │ ├── sidebar.tsx │ │ ├── header.tsx │ │ └── stats-card.tsx │ ├── marketing/ │ │ ├── hero.tsx │ │ ├── features.tsx │ │ ├── pricing.tsx │ │ └── footer.tsx │ └── billing/ │ ├── plan-card.tsx │ └── usage-meter.tsx ├── lib/ │ ├── auth.ts │ ├── db.ts │ ├── stripe.ts │ ├── validations.ts │ └── utils.ts ├── db/ │ ├── schema.ts │ └── migrations/ ├── hooks/ │ ├── use-subscription.ts │ └── use-user.ts ├── types/index.ts ├── middleware.ts ├── .env.example ├── drizzle.config.ts └── next.config.ts ``` --- ## Key Component Patterns ### Auth Config (NextAuth) ```typescript // lib/auth.ts import { NextAuthOptions } from "next-auth" import GoogleProvider from "next-auth/providers/google" import { DrizzleAdapter } from "@auth/drizzle-adapter" import { db } from "./db" export const authOptions: NextAuthOptions = { adapter: DrizzleAdapter(db), providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, }), ], callbacks: { session: async ({ session, user }) => ({ ...session, user: { ...session.user, id: user.id, subscriptionStatus: user.subscriptionStatus, }, }), }, pages: { signIn: "/login" }, } ``` ### Database Schema (Drizzle + NeonDB) ```typescript // db/schema.ts import { pgTable, text, timestamp, integer } from "drizzle-orm/pg-core" export const users = pgTable("users", { id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()), name: text("name"), email: text("email").notNull().unique(), emailVerified: timestamp("emailVerified"), image: text("image"), stripeCustomerId: text("stripe_customer_id").unique(), stripeSubscriptionId: text("stripe_subscription_id"), stripePriceId: text("stripe_price_id"), stripeCurrentPeriodEnd: timestamp("stripe_current_period_end"), createdAt: timestamp("created_at").defaultNow().notNull(), }) export const accounts = pgTable("accounts", { userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), type: text("type").notNull(), provider: text("provider").notNull(), providerAccountId: text("provider_account_id").notNull(), refresh_token: text("refresh_token"), access_token: text("access_token"), expires_at: integer("expires_at"), }) ``` ### Stripe Checkout Route ```typescript // app/api/billing/checkout/route.ts import { NextResponse } from "next/server" import { getServerSession } from "next-auth" import { authOptions } from "@/lib/auth" import { stripe } from "@/lib/stripe" import { db } from "@/lib/db" import { users } from "@/db/schema" import { eq } from "drizzle-orm" export async function POST(req: Request) { const session = await getServerSession(authOptions) if (!session?.user) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }) const { priceId } = await req.json() const [user] = await db.select().from(users).where(eq(users.id, session.user.id)) let customerId = user.stripeCustomerId if (!customerId) { const customer = await stripe.customers.create({ email: session.user.email! }) customerId = customer.id await db.update(users).set({ stripeCustomerId: customerId }).where(eq(users.id, user.id)) } const checkoutSession = await stripe.checkout.sessions.create({ customer: customerId, mode: "subscription", payment_method_types: ["card"], line_items: [{ price: priceId, quantity: 1 }], success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?upgraded=true`, cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`, subscription_data: { trial_period_days: 14 }, }) return NextResponse.json({ url: checkoutSession.url }) } ``` ### Middleware ```typescript // middleware.ts import { withAuth } from "next-auth/middleware" import { NextResponse } from "next/server" export default withAuth( function middleware(req) { const token = req.nextauth.token if (req.nextUrl.pathname.startsWith("/dashboard") && !token) { return NextResponse.redirect(new URL("/login", req.url)) } }, { callbacks: { authorized: ({ token }) => !!token } } ) export const config = { matcher: ["/dashboard/:path*", "/settings/:path*", "/billing/:path*"], } ``` ### Environment Variables Template ```bash # .env.example NEXT_PUBLIC_APP_URL=http://localhost:3000 DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-1.aws.neon.tech/neondb?sslmode=require NEXTAUTH_SECRET=generate-with-openssl-rand-base64-32 NEXTAUTH_URL=http://localhost:3000 GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= STRIPE_SECRET_KEY=sk_test_... STRIPE_WEBHOOK_SECRET=whsec_... NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_... STRIPE_PRO_PRICE_ID=price_... ``` --- ## Scaffold Checklist The following phases must be completed in order. **Validate at the end of each phase before proceeding.** ### Phase 1 — Foundation - [ ] 1. Next.js initialized with TypeScript and App Router - [ ] 2. Tailwind CSS configured with custom theme tokens - [ ] 3. shadcn/ui installed and configured - [ ] 4. ESLint + Prettier configured - [ ] 5. `.env.example` created with all required variables ✅ **Validate:** Run `npm run build` — no TypeScript or lint errors should appear. 🔧 **If build fails:** Check `tsconfig.json` paths and that all shadcn/ui peer dependencies are installed. ### Phase 2 — Database - [ ] 6. Drizzle ORM installed and configured - [ ] 7. Schema written (users, accounts, sessions, verification_tokens) - [ ] 8. Initial migration generated and applied - [ ] 9. DB client singleton exported from `lib/db.ts` - [ ] 10. DB connection tested in local environment ✅ **Validate:** Run a simple `db.select().from(users)` in a test script — it should return an empty array without throwing. 🔧 **If DB connection fails:** Verify `DATABASE_URL` format includes `?sslmode=require` for NeonDB/Supabase. Check that the migration has been applied with `drizzle-kit push` (dev) or `drizzle-kit migrate` (prod). ### Phase 3 — Authentication - [ ] 11. Auth provider installed (NextAuth / Clerk / Supabase) - [ ] 12. OAuth provider configured (Google / GitHub) - [ ] 13. Auth API route created - [ ] 14. Session callback adds user ID and subscription status - [ ] 15. Middleware protects dashboard routes - [ ] 16. Login and register pages built with error states ✅ **Validate:** Sign in via OAuth, confirm session user has `id` and `subscriptionStatus`. Attempt to access `/dashboard` without a session — you should be redirected to `/login`. 🔧 **If sign-out loops occur in production:** Ensure `NEXTAUTH_SECRET` is set and consistent across deployments. Add `declare module "next-auth"` to extend session types if TypeScript errors appear. ### Phase 4 — Payments - [ ] 17. Stripe client initialized with TypeScript types - [ ] 18. Checkout session route created - [ ] 19. Customer portal route created - [ ] 20. Stripe webhook handler with signature verification - [ ] 21. Webhook updates user subscription status in DB idempotently ✅ **Validate:** Complete a Stripe test checkout using a `4242 4242 4242 4242` card. Confirm `stripeSubscriptionId` is written to the DB. Replay the `checkout.session.completed` webhook event and confirm idempotency (no duplicate DB writes). 🔧 **If webhook signature fails:** Use `stripe listen --forward-to localhost:3000/api/webhooks/stripe` locally — never hardcode the raw webhook secret. Verify `STRIPE_WEBHOOK_SECRET` matches the listener output. ### Phase 5 — UI - [ ] 22. Landing page with hero, features, pricing sections - [ ] 23. Dashboard layout with sidebar and responsive header - [ ] 24. Billing page showing current plan and upgrade options - [ ] 25. Settings page with profile update form and success states ✅ **Validate:** Run `npm run build` for a final production build check. Navigate all routes manually and confirm no broken layouts, missing session data, or hydration errors. --- ## Reference Files For additional guidance, generate the following companion reference files alongside the scaffold: - **`CUSTOMIZATION.md`** — Auth providers, database options, ORM alternatives, payment providers, UI themes, and billing models (per-seat, flat-rate, usage-based). - **`PITFALLS.md`** — Common failure modes: missing `NEXTAUTH_SECRET`, webhook secret mismatches, Edge runtime conflicts with Drizzle, unextended session types, and migration strategy differences between dev and prod. - **`BEST_PRACTICES.md`** — Stripe singleton pattern, server actions for form mutations, idempotent webhook handlers, `Suspense` boundaries for async dashboard data, server-side feature gating via `stripeCurrentPeriodEnd`, and rate limiting on auth routes with Upstash Redis + `@upstash/ratelimit`. ## Diff History - **v00.33.0**: Ingested from claude-skills-main --- ## Why This Skill Exists Generate — Generates complete, production-ready SaaS project boilerplate including authentication, database schemas, billing <!-- SR_40: auto-generated from frontmatter `purpose`/`description` (OPP-Phase3). Expand with domain-specific rationale. --> ## When to Use Use this skill when the task requires saas scaffolder capabilities. <!-- SR_40: auto-generated from frontmatter `when`/`description` (OPP-Phase3). --> ## What If Fails - condition: Código não disponível para análise <!-- SR_40: auto-generated from frontmatter `what_if_fails` (OPP-Phase3). -->
Auf GitHub ansehen