chore: 初始化 monorepo 项目脚手架

- 配置 pnpm workspace + Turborepo
- 创建 Next.js 15 前端应用 (apps/web)
- 创建 NestJS 10 后端应用 (apps/api)
- 集成 Prisma ORM + Swagger + JWT 认证
- 添加共享包 (packages/shared, eslint-config, typescript-config)
- 添加项目文档 (README, CLAUDE.md, docs/design.md)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
charilezhou
2025-12-28 14:51:40 +08:00
commit 74ced8c0c6
62 changed files with 11151 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'import'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'prettier',
],
rules: {
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
'import/no-unresolved': 'off',
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
},
ignorePatterns: ['node_modules/', 'dist/', '.next/', '.turbo/'],
};

View File

@@ -0,0 +1,13 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ['./index.js'],
env: {
node: true,
jest: true,
},
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
},
};

View File

@@ -0,0 +1,8 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ['./index.js', 'next/core-web-vitals'],
rules: {
'@next/next/no-html-link-for-pages': 'off',
'react/jsx-key': 'error',
},
};

View File

@@ -0,0 +1,23 @@
{
"name": "@seclusion/eslint-config",
"version": "0.0.1",
"private": true,
"main": "index.js",
"files": [
"index.js",
"next.js",
"nest.js"
],
"dependencies": {
"@typescript-eslint/eslint-plugin": "^8.18.2",
"@typescript-eslint/parser": "^8.18.2",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.31.0"
},
"peerDependencies": {
"eslint": "^8.0.0 || ^9.0.0"
},
"devDependencies": {
"eslint": "^8.57.0"
}
}

View File

@@ -0,0 +1,37 @@
{
"name": "@seclusion/shared",
"version": "0.0.1",
"private": true,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./types": {
"types": "./dist/types/index.d.ts",
"import": "./dist/types/index.mjs",
"require": "./dist/types/index.js"
},
"./utils": {
"types": "./dist/utils/index.d.ts",
"import": "./dist/utils/index.mjs",
"require": "./dist/utils/index.js"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"clean": "rm -rf dist",
"lint": "eslint src --ext .ts,.tsx"
},
"devDependencies": {
"@seclusion/eslint-config": "workspace:*",
"@seclusion/typescript-config": "workspace:*",
"tsup": "^8.3.5",
"typescript": "^5.7.2"
}
}

View File

@@ -0,0 +1,5 @@
// 导出所有类型
export * from './types';
// 导出所有工具函数
export * from './utils';

View File

@@ -0,0 +1,61 @@
// 通用 API 响应类型
export interface ApiResponse<T = unknown> {
success: boolean;
data?: T;
message?: string;
error?: string;
}
// 分页请求参数
export interface PaginationParams {
page?: number;
pageSize?: number;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
}
// 分页响应
export interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
// 用户基础类型
export interface User {
id: string;
email: string;
name: string | null;
createdAt: Date;
updatedAt: Date;
}
// 用户创建请求
export interface CreateUserDto {
email: string;
password: string;
name?: string;
}
// 用户登录请求
export interface LoginDto {
email: string;
password: string;
}
// 认证响应
export interface AuthResponse {
accessToken: string;
refreshToken: string;
user: Omit<User, 'createdAt' | 'updatedAt'>;
}
// Token 载荷
export interface TokenPayload {
sub: string;
email: string;
iat?: number;
exp?: number;
}

View File

@@ -0,0 +1,75 @@
// 日期格式化
export function formatDate(date: Date | string, format = 'YYYY-MM-DD'): string {
const d = typeof date === 'string' ? new Date(date) : date;
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
const seconds = String(d.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', String(year))
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
// 延迟函数
export function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 生成随机 ID
export function generateId(length = 12): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// 深拷贝
export function deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') {
return obj;
}
return JSON.parse(JSON.stringify(obj));
}
// 判断是否为空对象
export function isEmpty(obj: unknown): boolean {
if (obj === null || obj === undefined) return true;
if (typeof obj === 'string') return obj.trim().length === 0;
if (Array.isArray(obj)) return obj.length === 0;
if (typeof obj === 'object') return Object.keys(obj).length === 0;
return false;
}
// 安全解析 JSON
export function safeJsonParse<T>(json: string, fallback: T): T {
try {
return JSON.parse(json) as T;
} catch {
return fallback;
}
}
// 首字母大写
export function capitalize(str: string): string {
if (!str) return '';
return str.charAt(0).toUpperCase() + str.slice(1);
}
// 驼峰转短横线
export function camelToKebab(str: string): string {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
// 短横线转驼峰
export function kebabToCamel(str: string): string {
return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
}

View File

@@ -0,0 +1,9 @@
{
"extends": "@seclusion/typescript-config/base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -0,0 +1,13 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: {
index: 'src/index.ts',
'types/index': 'src/types/index.ts',
'utils/index': 'src/utils/index.ts',
},
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
});

View File

@@ -0,0 +1,26 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"isolatedModules": true
},
"exclude": ["node_modules", "dist", ".next", ".turbo"]
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "ES2022",
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
}
}

View File

@@ -0,0 +1,18 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"jsx": "preserve",
"module": "ESNext",
"moduleResolution": "bundler",
"allowJs": true,
"noEmit": true,
"incremental": true,
"plugins": [
{
"name": "next"
}
]
}
}

View File

@@ -0,0 +1,14 @@
{
"name": "@seclusion/typescript-config",
"version": "0.0.1",
"private": true,
"license": "MIT",
"publishConfig": {
"access": "public"
},
"files": [
"base.json",
"nextjs.json",
"nestjs.json"
]
}