关于 team-saas
team-saas 是用于构建多租户 SaaS 应用的架构技能,面向需要团队、工作区和成员邀请功能的开发者。它提供 Next.js 项目的架构范式与可直接采用的模板文件,涵盖认证、后台任务和部署配置。
主要功能
- 01多租户团队与成员角色
- 02认证与团队邀请流程
- 03API 路由权限模式
- 04后台任务队列配置
- 05界面主题与组件模板
- 06Docker 与 Railway 部署配置
适用场景
- 01启动带团队工作区的 SaaS 产品
- 02实现团队成员邀请和授权
- 03为 Next.js 应用添加后台任务
- 04准备 Railway 部署配置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ blink-new/ claude --skill team-saas安装后在 Claude Code 中输入/team-saas确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/api/accept-invitation-route.tsinvitations-route.tsteams-route.tsuploads-proxy-route.tsassets/components/auth-layout.tsxdashboard-layout.tsxproviders.tsxroot-layout.tsxteam-switcher.tsxtheme-toggle.tsxassets/config/components.jsonDockerfileglobals.cssnext.config.tsprisma.config.tsproxy.tsrailway.tomltsconfig-paths.jsonassets/hooks/use-teams.tsassets/lib/api-helpers.tsauth.config.tsauth.tsboss.tsprisma.tsquery-client.tsresend.tss3.tsutils.tsassets/prisma/schema.prismaassets/types/next-auth.d.ts详细介绍
团队 SaaS 架构技能
构建生产级多租户 SaaS 应用,支持团队工作区、成员邀请、身份验证和现代化 UI。基于经过验证、为真实生产应用提供支持的架构。
何时使用此技能
适用于:
- 构建具有团队/工作区功能的新 SaaS 产品
- 设置多租户身份验证和授权
- 实现团队成员邀请流程
- 创建 Linear/Vercel 风格的现代化 UI,支持浅色/深色模式
- 为 SaaS 设置后台作业处理
- 将全栈 Next.js 应用部署到 Railway
技术栈概览
| 层 | 技术 | 版本 |
|---|---|---|
| 框架 | Next.js(App Router) | 16.x |
| React | React | 19.x |
| 运行时 | Bun | 最新 |
| 数据库 | PostgreSQL(Railway) | - |
| ORM | Prisma | 7.x |
| 身份验证 | NextAuth v5(Auth.js) | 5.0.0-beta |
| UI | shadcn/ui(new-york) | 最新 |
| 样式 | Tailwind CSS | v4 |
| 主题 | next-themes | 最新 |
| 图标 | Lucide React | 最新 |
| 状态管理 | TanStack React Query | 5.x |
| 验证 | Zod | 4.x |
| 电子邮件 | Resend | 6.x |
| 存储 | Railway S3-compatible | - |
| 作业 | pg-boss | 12.x |
| 托管 | Railway | - |
架构图
┌─────────────────────────────────────────────────────────────────────┐
│ Next.js Application │
├─────────────────────────────────────────────────────────────────────┤
│ Landing Page │ Auth Pages │ Dashboard (Protected) │
│ / │ /login │ /teams/[teamId]/* │
│ /pricing │ /register │ /dashboard │
│ │ /invite/[t] │ /settings │
├─────────────────────────────────────────────────────────────────────┤
│ API Routes │
│ /api/auth/* │ /api/teams/* │ /api/uploads/* │
│ /api/cron/* │ /api/webhooks/* │ /api/invitations/* │
├─────────────────────────────────────────────────────────────────────┤
│ proxy.ts (Security Headers) │ layout.tsx (Auth Protection) │
├─────────────────────────────────────────────────────────────────────┤
│ Services Layer │
│ Prisma (DB) │ Resend (Email) │ S3 (Storage) │ pg-boss (Jobs) │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Railway Infrastructure │
│ PostgreSQL │ S3 Bucket │ Redis (optional) │
└─────────────────────────────────────────────────────────────────────┘
核心模式
1. 多租户团队结构
每个数据实体都属于一个团队。用户可以加入多个团队,并拥有不同角色。
User ─┬─> TeamMember ─> Team ─┬─> Creator
│ ├─> Campaign
└─> TeamMember ─> Team ├─> Content
└─> ... (all team resources)
2. 路由组
src/app/
├── (auth)/ # Public auth pages (login, register)
│ └── layout.tsx # Client component, styling only
├── (dashboard)/ # Protected authenticated routes
│ ├── layout.tsx # Server component with auth() check + redirect
│ └── teams/[teamId]/ # Team-scoped pages
├── (marketing)/ # Public marketing pages
├── invite/[token]/ # Team invitation acceptance
└── api/ # API routes
3. 路由保护模式(基于布局,而非中间件)
重要: 身份验证保护通过服务器组件布局检查实现,而非 Edge 中间件。
// src/app/(dashboard)/layout.tsx
import { auth } from "@/lib/auth";
import { redirect } from "next/navigation";
export default async function DashboardLayout({ children }) {
const session = await auth();
if (!session?.user) {
redirect("/login");
}
return <DashboardShell session={session}>{children}</DashboardShell>;
}
4. API 路由权限模式
// Use api-helpers for clean, consistent patterns
import { requireTeamMember, parseJsonBody, withErrorHandler, ApiError } from "@/lib/api-helpers";
type RouteParams = {
params: Promise<{ teamId: string }>; // Next.js 15+ async params
};
export const POST = withErrorHandler(async (req: NextRequest, { params }: RouteParams) => {
const { teamId } = await params; // MUST await params
const { userId, role } = await requireTeamMember(teamId); // Throws 401/403
if (role !== "ADMIN") {
throw new ApiError("Admin access required", 403);
}
const data = await parseJsonBody(req, mySchema); // Validates with Zod
// ... business logic
return NextResponse.json(result);
});
文件结构
src/
├── app/
│ ├── (auth)/ # Auth pages (public)
│ │ ├── login/page.tsx
│ │ ├── register/page.tsx
│ │ └── layout.tsx # Client component, no auth
│ ├── (dashboard)/ # Protected pages
│ │ ├── layout.tsx # Server component, auth check
│ │ ├── dashboard-shell.tsx # Sidebar + nav
│ │ └── teams/[teamId]/
│ ├── invite/[token]/page.tsx # Invitation acceptance
│ ├── api/
│ │ ├── auth/[...nextauth]/route.ts
│ │ ├── teams/
│ │ │ ├── route.ts # List/create teams
│ │ │ └── [teamId]/
│ │ │ ├── route.ts # Team CRUD
│ │ │ ├── members/
│ │ │ └── invitations/
│ │ ├── invitations/
│ │ │ └── [token]/accept/route.ts
│ │ └── uploads/[...path]/route.ts # S3 proxy
│ ├── globals.css # Design system
│ ├── layout.tsx # Root layout with Providers
│ └── page.tsx # Landing page
├── components/
│ ├── ui/ # shadcn components
│ ├── teams/ # Team management
│ ├── invitations/ # Invitation UI
│ ├── shared/ # Reusable patterns
│ ├── providers.tsx # App providers
│ └── theme-toggle.tsx # Theme switcher
├── hooks/
│ ├── use-teams.ts # Team hooks
│ └── ...
├── lib/
│ ├── auth.ts # NextAuth config (Node.js runtime)
│ ├── auth.config.ts # Auth config (callbacks, pages)
│ ├── api-helpers.ts # withErrorHandler, requireTeamMember, etc.
│ ├── prisma.ts # Prisma client (lazy proxy)
│ ├── s3.ts # S3 utilities
│ ├── resend.ts # Email client
│ ├── query-client.ts # React Query
│ ├── utils.ts # cn(), getInitials(), etc.
│ └── jobs/ # pg-boss setup
├── proxy.ts # Security headers (NOT auth)
├── types/
│ └── next-auth.d.ts # Auth type extensions
└── generated/
└── prisma/ # Prisma client output
相关资源文件
此技能包含可直接使用的模板:
| 资源 | 描述 |
|---|---|
assets/lib/auth.ts | 使用 Credentials provider 的 NextAuth v5 |
assets/lib/auth.config.ts | 身份验证配置(回调、自定义页面) |
assets/lib/api-helpers.ts | withErrorHandler、requireTeamMember、ApiError |
assets/lib/prisma.ts | 延迟加载并使用 pg adapter 的 Prisma 客户端 |
assets/lib/s3.ts | 带预签名 URL 的 S3 工具函数 |
assets/lib/resend.ts | Resend 电子邮件客户端和模板 |
assets/lib/query-client.ts | React Query 设置 |
assets/lib/boss.ts | pg-boss 作业队列 |
assets/lib/utils.ts | 工具函数 |
assets/components/providers.tsx | 应用提供程序 |
assets/components/theme-toggle.tsx | 浅色/深色/系统模式切换 |
assets/components/team-switcher.tsx | 团队下拉菜单 |
assets/config/globals.css | Linear 风格设计系统 |
assets/config/Dockerfile | 生产环境 Docker 构建 |
assets/config/railway.toml | Railway 部署配置 |
assets/config/next.config.ts | Next.js 配置 |
assets/config/proxy.ts | 安全标头 |
assets/config/.env.example | 环境变量 |
assets/config/components.json | shadcn 配置 |
assets/prisma/schema.prisma | 基础团队架构 |
assets/api/teams-route.ts | 团队 API 模板 |
assets/api/invitations-route.ts | 邀请 API 模板 |
assets/hooks/use-teams.ts | React Query 钩子 |
assets/types/next-auth.d.ts | 类型扩展 |
设置说明
1. 初始化项目
bunx create-next-app@latest my-saas --typescript --tailwind --eslint --app --src-dir
cd my-saas
2. 安装依赖
# Core
bun add next-auth@beta @auth/prisma-adapter bcryptjs
bun add @prisma/client @prisma/adapter-pg pg
bun add -D prisma @types/bcryptjs
# UI
bun add lucide-react
bun add next-themes class-variance-authority clsx tailwind-merge
bun add @tanstack/react-query
# Validation
bun add zod
# Email
bun add resend
# Storage
bun add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
# Jobs
bun add pg-boss
# shadcn
bunx shadcn@latest init
bunx shadcn@latest add button card dialog input label sonner tooltip dropdown-menu avatar badge form command popover
3. 配置环境
复制 .env.example 并填写相应值:
# Required
DATABASE_URL="postgresql://..."
AUTH_SECRET="openssl rand -base64 32"
NEXTAUTH_URL="http://localhost:3000"
RESEND_API_KEY="re_..."
EMAIL_FROM="[email protected]"
# S3 Storage (Railway)
AWS_ENDPOINT_URL="https://..."
AWS_ACCESS_KEY_ID="..."
AWS_SECRET_ACCESS_KEY="..."
AWS_S3_BUCKET_NAME="..."
AWS_DEFAULT_REGION="auto"
4. 设置 Prisma
# Initialize
bunx prisma init
# Copy the schema from assets/prisma/schema.prisma
# Ensure generator uses "prisma-client" (not "prisma-client-js")
# Ensure output is "../src/generated/prisma"
# Push to database
bunx prisma db push
bunx prisma generate
5. 复制资源文件
将 assets/ 中的模板文件复制到项目中,并根据需要调整导入路径。
设计系统
品牌颜色
主色紫色:#8b5cf6(HSL:262 83% 58%)
浅色模式
- 背景:
#ffffff - 前景:
hsl(240 10% 10%) - 边框:
hsl(240 6% 90%)
深色模式(Linear 风格)
- 背景:
#0A0A0B(hsl 240 5% 4%) - 前景:
hsl(0 0% 95%) - 边框:
rgba(255,255,255,0.06)
主题切换
仅显示图标的三选项分段控件:
- 太阳图标 → 浅色
- 月亮图标 → 深色
- 显示器图标 → 系统
位于侧边栏页脚或页眉。使用 next-themes 和 attribute="class"。
字体排版
- 字体:Geist Sans / Geist Mono
- 紧凑的字母间距:正文为 -0.011em,标题为 -0.02em
- 字体平滑:抗锯齿
API 路由模式
路由处理程序签名(Next.js 15+)
重要: 在 Next.js 15+ 中,params 是一个 Promise,必须等待其解析:
type RouteParams = {
params: Promise<{ teamId: string }>;
};
export async function GET(req: NextRequest, { params }: RouteParams) {
const { teamId } = await params; // MUST await!
// ...
}
使用 withErrorHandler(推荐)
import { NextRequest, NextResponse } from "next/server";
import { requireTeamMember, parseJsonBody, withErrorHandler, ApiError } from "@/lib/api-helpers";
import { prisma } from "@/lib/prisma";
import { z } from "zod";
const createSchema = z.object({
name: z.string().min(1),
});
type RouteParams = {
params: Promise<{ teamId: string }>;
};
export const POST = withErrorHandler(async (req: NextRequest, { params }: RouteParams) => {
const { teamId } = await params;
// Auth check - throws 401/403
await requireTeamMember(teamId);
// Parse and validate body - throws 400
const { name } = await parseJsonBody(req, createSchema);
// Business logic
const result = await prisma.someModel.create({
data: { name, teamId },
});
return NextResponse.json(result, { status: 201 });
});
React Query 键
// Factory pattern for query keys
export const teamKeys = {
all: ["teams"] as const,
lists: () => [...teamKeys.all, "list"] as const,
details: () => [...teamKeys.all, "detail"] as const,
detail: (id: string) => [...teamKeys.details(), id] as const,
members: (id: string) => [...teamKeys.detail(id), "members"] as const,
};
Mutation 模式(简单失效)
export function useCreateTeam() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createTeam,
onSuccess: (newTeam) => {
queryClient.invalidateQueries({ queryKey: teamKeys.lists() });
queryClient.setQueryData(teamKeys.detail(newTeam.id), newTeam);
},
});
}
后台任务
pg-boss 设置
任务通过启动脚本与 Next.js 在同一容器中运行。
队列模式:
// Create job
await boss.send(QUEUES.SEND_EMAIL, payload, DEFAULT_JOB_OPTIONS);
// Worker handles job
boss.work(QUEUES.SEND_EMAIL, { batchSize: 1 }, async (job) => {
await handleSendEmail(job.data);
});
定时任务
// Schedule recurring jobs on worker startup
await boss.schedule(QUEUES.CLEANUP, "0 6 * * *", {}); // Daily at 6 AM UTC
部署
Dockerfile
单个容器同时运行 Next.js 服务器和 pg-boss 工作进程:
FROM node:22-alpine
RUN npm install -g bun
# ... build steps ...
CMD ["/app/start.sh"] # Runs both processes
Railway 配置
[build]
builder = "dockerfile"
[deploy]
healthcheckPath = "/"
healthcheckTimeout = 300
restartPolicyType = "on_failure"
next.config.ts 重写规则
async rewrites() {
return [
{
source: "/uploads/:path*",
destination: "/api/uploads/:path*",
},
];
}
检查清单
- 使用 Next.js + TypeScript 初始化项目
- 安装依赖项(参见上方列表)
- 配置环境变量
- Prisma 架构包含 User、Team、TeamMember、Invitation
- NextAuth v5 配置 Credentials 提供程序
- 仪表板布局包含服务端身份验证检查
- API 辅助函数(withErrorHandler、requireTeamMember 等)
- 用于安全响应头的 proxy.ts
- 创建和切换团队
- 成员邀请流程
- 浅色/深色模式切换
- 配置 React Query
- Resend 邮件服务
- S3 存储搭配 /uploads 代理
- 使用 pg-boss 处理后台任务
- 用于 Railway 部署的 Dockerfile
评论
支持 Markdown