mcpField
T
team-saas

team-saas

github.com/blink-new开发57 次安装
代码仓库

关于 team-saas

team-saas 是用于构建多租户 SaaS 应用的架构技能,面向需要团队、工作区和成员邀请功能的开发者。它提供 Next.js 项目的架构范式与可直接采用的模板文件,涵盖认证、后台任务和部署配置。

主要功能

  1. 01多租户团队与成员角色
  2. 02认证与团队邀请流程
  3. 03API 路由权限模式
  4. 04后台任务队列配置
  5. 05界面主题与组件模板
  6. 06Docker 与 Railway 部署配置

适用场景

  1. 01启动带团队工作区的 SaaS 产品
  2. 02实现团队成员邀请和授权
  3. 03为 Next.js 应用添加后台任务
  4. 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.ts
invitations-route.ts
teams-route.ts
uploads-proxy-route.ts
assets/components/
auth-layout.tsx
dashboard-layout.tsx
providers.tsx
root-layout.tsx
team-switcher.tsx
theme-toggle.tsx
assets/config/
components.json
Dockerfile
globals.css
next.config.ts
prisma.config.ts
proxy.ts
railway.toml
tsconfig-paths.json
assets/hooks/
use-teams.ts
assets/lib/
api-helpers.ts
auth.config.ts
auth.ts
boss.ts
prisma.ts
query-client.ts
resend.ts
s3.ts
utils.ts
assets/prisma/
schema.prisma
assets/types/
next-auth.d.ts

详细介绍

团队 SaaS 架构技能

构建生产级多租户 SaaS 应用,支持团队工作区、成员邀请、身份验证和现代化 UI。基于经过验证、为真实生产应用提供支持的架构。

何时使用此技能

适用于:

  • 构建具有团队/工作区功能的新 SaaS 产品
  • 设置多租户身份验证和授权
  • 实现团队成员邀请流程
  • 创建 Linear/Vercel 风格的现代化 UI,支持浅色/深色模式
  • 为 SaaS 设置后台作业处理
  • 将全栈 Next.js 应用部署到 Railway

技术栈概览

层技术版本
框架Next.js(App Router)16.x
ReactReact19.x
运行时Bun最新
数据库PostgreSQL(Railway)-
ORMPrisma7.x
身份验证NextAuth v5(Auth.js)5.0.0-beta
UIshadcn/ui(new-york)最新
样式Tailwind CSSv4
主题next-themes最新
图标Lucide React最新
状态管理TanStack React Query5.x
验证Zod4.x
电子邮件Resend6.x
存储Railway S3-compatible-
作业pg-boss12.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.tswithErrorHandler、requireTeamMember、ApiError
assets/lib/prisma.ts延迟加载并使用 pg adapter 的 Prisma 客户端
assets/lib/s3.ts带预签名 URL 的 S3 工具函数
assets/lib/resend.tsResend 电子邮件客户端和模板
assets/lib/query-client.tsReact Query 设置
assets/lib/boss.tspg-boss 作业队列
assets/lib/utils.ts工具函数
assets/components/providers.tsx应用提供程序
assets/components/theme-toggle.tsx浅色/深色/系统模式切换
assets/components/team-switcher.tsx团队下拉菜单
assets/config/globals.cssLinear 风格设计系统
assets/config/Dockerfile生产环境 Docker 构建
assets/config/railway.tomlRailway 部署配置
assets/config/next.config.tsNext.js 配置
assets/config/proxy.ts安全标头
assets/config/.env.example环境变量
assets/config/components.jsonshadcn 配置
assets/prisma/schema.prisma基础团队架构
assets/api/teams-route.ts团队 API 模板
assets/api/invitations-route.ts邀请 API 模板
assets/hooks/use-teams.tsReact 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