@svton/cli 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +58 -27
- package/dist/index.mjs +58 -27
- package/package.json +1 -3
- package/templates/apps/admin/next-env.d.ts +0 -2
- package/templates/apps/admin/next.config.js +0 -15
- package/templates/apps/admin/package.json.tpl +0 -54
- package/templates/apps/admin/postcss.config.js +0 -6
- package/templates/apps/admin/src/app/globals.css +0 -37
- package/templates/apps/admin/src/app/layout.tsx +0 -19
- package/templates/apps/admin/src/app/login/page.tsx +0 -96
- package/templates/apps/admin/src/app/page.tsx +0 -8
- package/templates/apps/admin/src/app/users/page.tsx +0 -165
- package/templates/apps/admin/src/components/ui/switch.tsx +0 -29
- package/templates/apps/admin/src/hooks/useAPI.ts +0 -130
- package/templates/apps/admin/src/lib/api-client.ts +0 -112
- package/templates/apps/admin/src/lib/api-server.ts +0 -95
- package/templates/apps/admin/tailwind.config.js +0 -54
- package/templates/apps/admin/tsconfig.json +0 -22
- package/templates/apps/backend/.env.example +0 -29
- package/templates/apps/backend/nest-cli.json +0 -8
- package/templates/apps/backend/package.json.tpl +0 -57
- package/templates/apps/backend/prisma/schema.prisma +0 -72
- package/templates/apps/backend/prisma/seed.ts +0 -32
- package/templates/apps/backend/src/app.controller.ts +0 -15
- package/templates/apps/backend/src/app.module.ts +0 -85
- package/templates/apps/backend/src/app.service.ts +0 -12
- package/templates/apps/backend/src/auth/auth.controller.ts +0 -31
- package/templates/apps/backend/src/auth/auth.module.ts +0 -27
- package/templates/apps/backend/src/auth/auth.service.ts +0 -89
- package/templates/apps/backend/src/auth/jwt-auth.guard.ts +0 -5
- package/templates/apps/backend/src/auth/jwt.strategy.ts +0 -27
- package/templates/apps/backend/src/config/env.schema.ts +0 -35
- package/templates/apps/backend/src/main.ts +0 -51
- package/templates/apps/backend/src/object-storage/object-storage.controller.ts +0 -114
- package/templates/apps/backend/src/object-storage/object-storage.module.ts +0 -7
- package/templates/apps/backend/src/prisma/prisma.module.ts +0 -9
- package/templates/apps/backend/src/prisma/prisma.service.ts +0 -13
- package/templates/apps/backend/src/user/user.controller.ts +0 -50
- package/templates/apps/backend/src/user/user.module.ts +0 -12
- package/templates/apps/backend/src/user/user.service.ts +0 -117
- package/templates/apps/backend/tsconfig.json +0 -23
- package/templates/apps/mobile/babel.config.js +0 -8
- package/templates/apps/mobile/config/index.ts +0 -65
- package/templates/apps/mobile/package.json.tpl +0 -48
- package/templates/apps/mobile/project.config.json.tpl +0 -17
- package/templates/apps/mobile/src/app.config.ts +0 -9
- package/templates/apps/mobile/src/app.scss +0 -4
- package/templates/apps/mobile/src/app.ts +0 -8
- package/templates/apps/mobile/src/hooks/useAPI.ts +0 -285
- package/templates/apps/mobile/src/pages/index/index.scss +0 -7
- package/templates/apps/mobile/src/pages/index/index.tsx +0 -49
- package/templates/apps/mobile/src/services/api.ts +0 -155
- package/templates/apps/mobile/src/services/upload.service.ts +0 -41
- package/templates/apps/mobile/tsconfig.json +0 -21
- package/templates/configs/authz.config.ts +0 -10
- package/templates/configs/cache.config.ts +0 -14
- package/templates/configs/oauth.config.ts +0 -20
- package/templates/configs/payment.config.ts +0 -44
- package/templates/configs/queue.config.ts +0 -21
- package/templates/configs/rate-limit.config.ts +0 -16
- package/templates/configs/sms.config.ts +0 -11
- package/templates/configs/storage.config.ts +0 -14
- package/templates/examples/README.md +0 -258
- package/templates/examples/authz/README.md +0 -273
- package/templates/examples/authz/roles.guard.ts +0 -37
- package/templates/examples/authz/user.controller.ts +0 -116
- package/templates/examples/cache/README.md +0 -82
- package/templates/examples/cache/user.controller.ts +0 -42
- package/templates/examples/cache/user.service.ts +0 -78
- package/templates/examples/oauth/README.md +0 -192
- package/templates/examples/oauth/auth.controller.ts +0 -99
- package/templates/examples/oauth/auth.service.ts +0 -97
- package/templates/examples/payment/README.md +0 -151
- package/templates/examples/payment/order.controller.ts +0 -56
- package/templates/examples/payment/order.service.ts +0 -132
- package/templates/examples/payment/webhook.controller.ts +0 -73
- package/templates/examples/queue/README.md +0 -134
- package/templates/examples/queue/email.controller.ts +0 -34
- package/templates/examples/queue/email.processor.ts +0 -68
- package/templates/examples/queue/email.service.ts +0 -64
- package/templates/examples/rate-limit/README.md +0 -249
- package/templates/examples/rate-limit/api.controller.ts +0 -113
- package/templates/examples/sms/README.md +0 -121
- package/templates/examples/sms/sms.service.ts +0 -69
- package/templates/examples/sms/verification.controller.ts +0 -100
- package/templates/examples/storage/README.md +0 -224
- package/templates/examples/storage/upload.controller.ts +0 -117
- package/templates/examples/storage/upload.service.ts +0 -123
- package/templates/packages/types/package.json.tpl +0 -16
- package/templates/packages/types/src/api.ts +0 -88
- package/templates/packages/types/src/common.ts +0 -89
- package/templates/packages/types/src/index.ts +0 -3
- package/templates/packages/types/tsconfig.json +0 -16
- package/templates/skills/authz.skill.md +0 -42
- package/templates/skills/base.skill.md +0 -57
- package/templates/skills/cache.skill.md +0 -88
- package/templates/skills/oauth.skill.md +0 -41
- package/templates/skills/payment.skill.md +0 -129
- package/templates/skills/queue.skill.md +0 -140
- package/templates/skills/rate-limit.skill.md +0 -38
- package/templates/skills/sms.skill.md +0 -39
- package/templates/skills/storage.skill.md +0 -42
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { View } from '@tarojs/components';
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { usePersistFn, useMount } from '@svton/hooks';
|
|
4
|
-
import { NavBar, StatusBar, Loading, Empty } from '@svton/taro-ui';
|
|
5
|
-
import type { ContentVo } from '{{ORG_NAME}}/types';
|
|
6
|
-
import './index.scss';
|
|
7
|
-
|
|
8
|
-
export default function Index() {
|
|
9
|
-
const [loading, setLoading] = useState(true);
|
|
10
|
-
const [contents, setContents] = useState<ContentVo[]>([]);
|
|
11
|
-
|
|
12
|
-
const fetchContents = usePersistFn(async () => {
|
|
13
|
-
try {
|
|
14
|
-
setLoading(true);
|
|
15
|
-
// 这里应该使用 @svton/api-client 的 API
|
|
16
|
-
// const response = await apiClient.contents.list({ page: 1, pageSize: 10 });
|
|
17
|
-
// setContents(response.data.list);
|
|
18
|
-
|
|
19
|
-
// 模拟数据
|
|
20
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
21
|
-
setContents([]);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.error('获取内容列表失败', error);
|
|
24
|
-
} finally {
|
|
25
|
-
setLoading(false);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
useMount(() => {
|
|
30
|
-
fetchContents();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<View className="index">
|
|
35
|
-
<StatusBar />
|
|
36
|
-
<NavBar title="首页" />
|
|
37
|
-
|
|
38
|
-
<View className="content">
|
|
39
|
-
{loading && <Loading text="加载中..." />}
|
|
40
|
-
{!loading && contents.length === 0 && <Empty text="暂无内容" />}
|
|
41
|
-
{!loading && contents.length > 0 && (
|
|
42
|
-
<View className="content-list">
|
|
43
|
-
{/* 内容列表 */}
|
|
44
|
-
</View>
|
|
45
|
-
)}
|
|
46
|
-
</View>
|
|
47
|
-
</View>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mobile 端 API Client
|
|
3
|
-
* 使用 @svton/api-client 系统
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import Taro from '@tarojs/taro';
|
|
7
|
-
import { createApiClient, createTokenInterceptor } from '@svton/api-client';
|
|
8
|
-
// 引入类型定义以启用模块增强
|
|
9
|
-
import '{{ORG_NAME}}/types';
|
|
10
|
-
|
|
11
|
-
// API 基础 URL(从 Taro 配置文件的 defineConstants 中注入)
|
|
12
|
-
// 注意:API_BASE_URL 是一个全局常量,在编译时由 Taro 配置文件注入
|
|
13
|
-
declare const API_BASE_URL: string;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Taro 适配器
|
|
17
|
-
*/
|
|
18
|
-
const taroAdapter = {
|
|
19
|
-
async request(config: any) {
|
|
20
|
-
const response = await Taro.request({
|
|
21
|
-
url: config.url,
|
|
22
|
-
method: config.method as any,
|
|
23
|
-
data: config.method === 'GET' ? config.params : config.data,
|
|
24
|
-
header: config.headers,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const result = response.data;
|
|
28
|
-
|
|
29
|
-
// 后端统一包装格式:{ code: 200, message: 'success', data: T, ... }
|
|
30
|
-
if (result && typeof result === 'object' && 'code' in result) {
|
|
31
|
-
if ((result as any).code !== 200) {
|
|
32
|
-
const error: any = new Error((result as any).message || 'Request failed');
|
|
33
|
-
error.code = (result as any).code;
|
|
34
|
-
error.response = {
|
|
35
|
-
status: (result as any).code,
|
|
36
|
-
data: result,
|
|
37
|
-
};
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
return (result as any).data;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 兼容未包装响应
|
|
44
|
-
return result;
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 刷新 Token
|
|
50
|
-
*/
|
|
51
|
-
let isRefreshing = false;
|
|
52
|
-
let refreshSubscribers: Array<(token: string) => void> = [];
|
|
53
|
-
|
|
54
|
-
const subscribeTokenRefresh = (cb: (token: string) => void) => {
|
|
55
|
-
refreshSubscribers.push(cb);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const onRefreshed = (token: string) => {
|
|
59
|
-
refreshSubscribers.forEach((cb) => cb(token));
|
|
60
|
-
refreshSubscribers = [];
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const refreshAccessToken = async (): Promise<string | null> => {
|
|
64
|
-
const refreshToken = Taro.getStorageSync('refreshToken');
|
|
65
|
-
if (!refreshToken) {
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
const response = await Taro.request({
|
|
71
|
-
url: `${API_BASE_URL}/auth/refresh`,
|
|
72
|
-
method: 'POST',
|
|
73
|
-
data: { refreshToken },
|
|
74
|
-
header: {
|
|
75
|
-
'Content-Type': 'application/json',
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const result = response.data as any;
|
|
80
|
-
|
|
81
|
-
if (result && result.code === 200 && result.data && result.data.accessToken) {
|
|
82
|
-
const newToken = result.data.accessToken as string;
|
|
83
|
-
const newRefreshToken = result.data.refreshToken as string | undefined;
|
|
84
|
-
|
|
85
|
-
Taro.setStorageSync('token', newToken);
|
|
86
|
-
if (newRefreshToken) {
|
|
87
|
-
Taro.setStorageSync('refreshToken', newRefreshToken);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return newToken;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return null;
|
|
94
|
-
} catch {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Token 刷新拦截器(错误处理)
|
|
101
|
-
*/
|
|
102
|
-
const createTokenRefreshInterceptor = () => {
|
|
103
|
-
return async (error: any): Promise<void> => {
|
|
104
|
-
if (error.code === 401 || error.code === '401') {
|
|
105
|
-
const token = Taro.getStorageSync('token');
|
|
106
|
-
const refreshToken = Taro.getStorageSync('refreshToken');
|
|
107
|
-
|
|
108
|
-
if (!token || !refreshToken) {
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (!isRefreshing) {
|
|
113
|
-
isRefreshing = true;
|
|
114
|
-
const newToken = await refreshAccessToken();
|
|
115
|
-
isRefreshing = false;
|
|
116
|
-
|
|
117
|
-
if (newToken) {
|
|
118
|
-
onRefreshed(newToken);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return new Promise((resolve) => {
|
|
126
|
-
subscribeTokenRefresh(() => {
|
|
127
|
-
resolve();
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
throw error;
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* 创建 API 客户端实例
|
|
138
|
-
*/
|
|
139
|
-
const { api, apiAsync, runGenerator } = createApiClient(taroAdapter, {
|
|
140
|
-
baseURL: API_BASE_URL,
|
|
141
|
-
interceptors: {
|
|
142
|
-
request: [
|
|
143
|
-
createTokenInterceptor(() => {
|
|
144
|
-
try {
|
|
145
|
-
return Taro.getStorageSync('token');
|
|
146
|
-
} catch {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
}),
|
|
150
|
-
],
|
|
151
|
-
error: [createTokenRefreshInterceptor()],
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
export { api, apiAsync, runGenerator };
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import Taro from '@tarojs/taro';
|
|
2
|
-
|
|
3
|
-
// API 基础 URL(从 Taro 配置文件注入)
|
|
4
|
-
declare const API_BASE_URL: string;
|
|
5
|
-
|
|
6
|
-
export interface UploadResult {
|
|
7
|
-
url: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function uploadImage(filePath: string): Promise<UploadResult> {
|
|
11
|
-
const token = Taro.getStorageSync('token');
|
|
12
|
-
|
|
13
|
-
const res = await Taro.uploadFile({
|
|
14
|
-
url: `${API_BASE_URL}/upload/image`,
|
|
15
|
-
filePath,
|
|
16
|
-
name: 'file',
|
|
17
|
-
header: {
|
|
18
|
-
Authorization: token ? `Bearer ${token}` : '',
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (res.statusCode !== 200 && res.statusCode !== 201) {
|
|
23
|
-
throw new Error(`上传失败: ${res.statusCode}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let parsed: any;
|
|
27
|
-
try {
|
|
28
|
-
parsed = JSON.parse(res.data);
|
|
29
|
-
} catch {
|
|
30
|
-
parsed = res.data;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (parsed && typeof parsed === 'object' && 'code' in parsed) {
|
|
34
|
-
if (parsed.code !== 200) {
|
|
35
|
-
throw new Error(parsed.message || '上传失败');
|
|
36
|
-
}
|
|
37
|
-
return { url: parsed.data?.url };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return { url: parsed?.url };
|
|
41
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2017",
|
|
4
|
-
"module": "CommonJS",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"esModuleInterop": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"jsx": "react-jsx",
|
|
9
|
-
"allowSyntheticDefaultImports": true,
|
|
10
|
-
"resolveJsonModule": true,
|
|
11
|
-
"typeRoots": ["node_modules/@types"],
|
|
12
|
-
"noEmit": true,
|
|
13
|
-
"skipLibCheck": true,
|
|
14
|
-
"baseUrl": ".",
|
|
15
|
-
"paths": {
|
|
16
|
-
"@/*": ["./src/*"]
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*", "types/**/*", "config/**/*"],
|
|
20
|
-
"exclude": ["node_modules", "dist"]
|
|
21
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { AuthzModuleOptions } from '@svton/nestjs-authz';
|
|
3
|
-
|
|
4
|
-
export const useAuthzConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): AuthzModuleOptions => ({
|
|
7
|
-
// 权限配置
|
|
8
|
-
roles: ['admin', 'user', 'guest'],
|
|
9
|
-
defaultRole: 'guest',
|
|
10
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { CacheModuleOptions } from '@svton/nestjs-cache';
|
|
3
|
-
|
|
4
|
-
export const useCacheConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): CacheModuleOptions => ({
|
|
7
|
-
ttl: 3600, // 默认缓存时间 1 小时
|
|
8
|
-
prefix: 'cache', // 缓存 key 前缀
|
|
9
|
-
redis: {
|
|
10
|
-
host: configService.get('REDIS_HOST', 'localhost'),
|
|
11
|
-
port: parseInt(configService.get('REDIS_PORT', '6379'), 10),
|
|
12
|
-
password: configService.get('REDIS_PASSWORD'),
|
|
13
|
-
},
|
|
14
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { OAuthModuleOptions } from '@svton/nestjs-oauth';
|
|
3
|
-
|
|
4
|
-
export const useOAuthConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): OAuthModuleOptions => ({
|
|
7
|
-
wechat: [
|
|
8
|
-
{
|
|
9
|
-
platform: 'open',
|
|
10
|
-
appId: configService.get('WECHAT_OPEN_APP_ID'),
|
|
11
|
-
appSecret: configService.get('WECHAT_OPEN_APP_SECRET'),
|
|
12
|
-
callbackUrl: configService.get('WECHAT_OPEN_CALLBACK_URL'),
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
platform: 'miniprogram',
|
|
16
|
-
appId: configService.get('WECHAT_MINI_APP_ID'),
|
|
17
|
-
appSecret: configService.get('WECHAT_MINI_APP_SECRET'),
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { PaymentModuleOptions } from '@svton/nestjs-payment';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 读取密钥文件
|
|
7
|
-
* @param filePath 文件路径
|
|
8
|
-
* @returns 文件内容
|
|
9
|
-
*/
|
|
10
|
-
function readKeyFile(filePath: string): string {
|
|
11
|
-
try {
|
|
12
|
-
if (fs.existsSync(filePath)) {
|
|
13
|
-
return fs.readFileSync(filePath, 'utf-8');
|
|
14
|
-
}
|
|
15
|
-
throw new Error(`Key file not found: ${filePath}`);
|
|
16
|
-
} catch (error) {
|
|
17
|
-
throw new Error(
|
|
18
|
-
`Failed to read key file: ${filePath}. Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const usePaymentConfig = (
|
|
24
|
-
configService: ConfigService,
|
|
25
|
-
): PaymentModuleOptions => ({
|
|
26
|
-
wechat: {
|
|
27
|
-
mchId: configService.getOrThrow('WECHAT_MCH_ID'),
|
|
28
|
-
privateKey: readKeyFile(
|
|
29
|
-
configService.get('WECHAT_PRIVATE_KEY', './certs/apiclient_key.pem'),
|
|
30
|
-
),
|
|
31
|
-
serialNo: configService.getOrThrow('WECHAT_SERIAL_NO'),
|
|
32
|
-
apiV3Key: configService.getOrThrow('WECHAT_API_V3_KEY'),
|
|
33
|
-
appId: configService.getOrThrow('WECHAT_APP_ID'),
|
|
34
|
-
},
|
|
35
|
-
alipay: {
|
|
36
|
-
appId: configService.getOrThrow('ALIPAY_APP_ID'),
|
|
37
|
-
privateKey: readKeyFile(
|
|
38
|
-
configService.get('ALIPAY_PRIVATE_KEY', './certs/alipay_private_key.pem'),
|
|
39
|
-
),
|
|
40
|
-
alipayPublicKey: readKeyFile(
|
|
41
|
-
configService.get('ALIPAY_PUBLIC_KEY', './certs/alipay_public_key.pem'),
|
|
42
|
-
),
|
|
43
|
-
},
|
|
44
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { QueueModuleOptions } from '@svton/nestjs-queue';
|
|
3
|
-
|
|
4
|
-
export const useQueueConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): QueueModuleOptions => ({
|
|
7
|
-
connection: {
|
|
8
|
-
host: configService.get('REDIS_HOST', 'localhost'),
|
|
9
|
-
port: parseInt(configService.get('REDIS_PORT', '6379'), 10),
|
|
10
|
-
password: configService.get('REDIS_PASSWORD'),
|
|
11
|
-
},
|
|
12
|
-
defaultJobOptions: {
|
|
13
|
-
attempts: 3,
|
|
14
|
-
backoff: {
|
|
15
|
-
type: 'exponential',
|
|
16
|
-
delay: 1000,
|
|
17
|
-
},
|
|
18
|
-
removeOnComplete: true,
|
|
19
|
-
removeOnFail: false,
|
|
20
|
-
},
|
|
21
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { RateLimitModuleOptions } from '@svton/nestjs-rate-limit';
|
|
3
|
-
|
|
4
|
-
export const useRateLimitConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): RateLimitModuleOptions => ({
|
|
7
|
-
redis: {
|
|
8
|
-
host: configService.get('REDIS_HOST', 'localhost'),
|
|
9
|
-
port: parseInt(configService.get('REDIS_PORT', '6379'), 10),
|
|
10
|
-
password: configService.get('REDIS_PASSWORD'),
|
|
11
|
-
},
|
|
12
|
-
global: {
|
|
13
|
-
ttl: 60, // 时间窗口(秒)
|
|
14
|
-
limit: 100, // 最大请求数
|
|
15
|
-
},
|
|
16
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { SmsModuleOptions } from '@svton/nestjs-sms';
|
|
3
|
-
|
|
4
|
-
export const useSmsConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): SmsModuleOptions => ({
|
|
7
|
-
provider: configService.get('SMS_PROVIDER', 'aliyun'),
|
|
8
|
-
accessKeyId: configService.get('SMS_ACCESS_KEY_ID'),
|
|
9
|
-
accessKeySecret: configService.get('SMS_ACCESS_KEY_SECRET'),
|
|
10
|
-
signName: configService.get('SMS_SIGN_NAME'),
|
|
11
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ConfigService } from '@nestjs/config';
|
|
2
|
-
import { ObjectStorageModuleOptions } from '@svton/nestjs-object-storage';
|
|
3
|
-
|
|
4
|
-
export const useStorageConfig = (
|
|
5
|
-
configService: ConfigService,
|
|
6
|
-
): ObjectStorageModuleOptions => ({
|
|
7
|
-
provider: configService.get('STORAGE_PROVIDER', 'qiniu'),
|
|
8
|
-
qiniu: {
|
|
9
|
-
accessKey: configService.get('QINIU_ACCESS_KEY'),
|
|
10
|
-
secretKey: configService.get('QINIU_SECRET_KEY'),
|
|
11
|
-
bucket: configService.get('QINIU_BUCKET'),
|
|
12
|
-
domain: configService.get('QINIU_DOMAIN'),
|
|
13
|
-
},
|
|
14
|
-
});
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
# Svton 功能示例代码
|
|
2
|
-
|
|
3
|
-
本目录包含所有 Svton 功能模块的完整示例代码。
|
|
4
|
-
|
|
5
|
-
## 📦 已完成的示例
|
|
6
|
-
|
|
7
|
-
### 1. Cache - 缓存 ✅
|
|
8
|
-
|
|
9
|
-
**目录**: `cache/`
|
|
10
|
-
|
|
11
|
-
**文件**:
|
|
12
|
-
- `user.service.ts` - 缓存装饰器使用示例
|
|
13
|
-
- `user.controller.ts` - Controller 层示例
|
|
14
|
-
- `README.md` - 详细使用文档
|
|
15
|
-
|
|
16
|
-
**核心功能**:
|
|
17
|
-
- @Cacheable - 缓存查询结果
|
|
18
|
-
- @CacheEvict - 清除缓存
|
|
19
|
-
- @CachePut - 更新缓存
|
|
20
|
-
- 支持 Key 表达式和通配符
|
|
21
|
-
|
|
22
|
-
### 2. Queue - 消息队列 ✅
|
|
23
|
-
|
|
24
|
-
**目录**: `queue/`
|
|
25
|
-
|
|
26
|
-
**文件**:
|
|
27
|
-
- `email.processor.ts` - 队列处理器示例
|
|
28
|
-
- `email.service.ts` - 服务层示例
|
|
29
|
-
- `email.controller.ts` - Controller 层示例
|
|
30
|
-
- `README.md` - 详细使用文档
|
|
31
|
-
|
|
32
|
-
**核心功能**:
|
|
33
|
-
- 添加任务到队列
|
|
34
|
-
- 定义任务处理器
|
|
35
|
-
- 延迟执行、重试策略
|
|
36
|
-
- 定时任务、优先级控制
|
|
37
|
-
|
|
38
|
-
### 3. Payment - 支付 ✅
|
|
39
|
-
|
|
40
|
-
**目录**: `payment/`
|
|
41
|
-
|
|
42
|
-
**文件**:
|
|
43
|
-
- `order.service.ts` - 订单服务示例
|
|
44
|
-
- `order.controller.ts` - 支付接口示例
|
|
45
|
-
- `webhook.controller.ts` - 支付回调处理示例
|
|
46
|
-
- `README.md` - 详细使用文档
|
|
47
|
-
|
|
48
|
-
**核心功能**:
|
|
49
|
-
- 微信支付(JSAPI、Native、APP、H5)
|
|
50
|
-
- 支付宝(PC、H5、APP)
|
|
51
|
-
- 订单查询、退款
|
|
52
|
-
- 支付回调处理
|
|
53
|
-
|
|
54
|
-
### 4. SMS - 短信 ✅
|
|
55
|
-
|
|
56
|
-
**目录**: `sms/`
|
|
57
|
-
|
|
58
|
-
**文件**:
|
|
59
|
-
- `sms.service.ts` - 短信服务示例
|
|
60
|
-
- `verification.controller.ts` - 验证码控制器示例
|
|
61
|
-
- `README.md` - 详细使用文档
|
|
62
|
-
|
|
63
|
-
**核心功能**:
|
|
64
|
-
- 发送验证码
|
|
65
|
-
- 发送通知短信
|
|
66
|
-
- 批量发送
|
|
67
|
-
- 验证码验证
|
|
68
|
-
|
|
69
|
-
### 5. OAuth - OAuth 登录 ✅
|
|
70
|
-
|
|
71
|
-
**目录**: `oauth/`
|
|
72
|
-
|
|
73
|
-
**文件**:
|
|
74
|
-
- `auth.service.ts` - 认证服务示例
|
|
75
|
-
- `auth.controller.ts` - 认证控制器示例
|
|
76
|
-
- `README.md` - 详细使用文档
|
|
77
|
-
|
|
78
|
-
**核心功能**:
|
|
79
|
-
- 微信开放平台登录(PC 扫码)
|
|
80
|
-
- 微信公众号登录(网页授权)
|
|
81
|
-
- 微信小程序登录
|
|
82
|
-
- 小程序获取手机号
|
|
83
|
-
|
|
84
|
-
### 6. Storage - 对象存储 ✅
|
|
85
|
-
|
|
86
|
-
**目录**: `storage/`
|
|
87
|
-
|
|
88
|
-
**文件**:
|
|
89
|
-
- `upload.service.ts` - 上传服务示例
|
|
90
|
-
- `upload.controller.ts` - 上传控制器示例
|
|
91
|
-
- `README.md` - 详细使用文档
|
|
92
|
-
|
|
93
|
-
**核心功能**:
|
|
94
|
-
- 服务端上传文件
|
|
95
|
-
- 客户端直传
|
|
96
|
-
- 图片处理(缩略图、裁剪)
|
|
97
|
-
- 文件删除、移动、复制
|
|
98
|
-
- 私有文件访问
|
|
99
|
-
|
|
100
|
-
### 7. Rate Limit - 限流 ✅
|
|
101
|
-
|
|
102
|
-
**目录**: `rate-limit/`
|
|
103
|
-
|
|
104
|
-
**文件**:
|
|
105
|
-
- `api.controller.ts` - API 控制器示例
|
|
106
|
-
- `README.md` - 详细使用文档
|
|
107
|
-
|
|
108
|
-
**核心功能**:
|
|
109
|
-
- 接口限流
|
|
110
|
-
- 多种限流策略
|
|
111
|
-
- 防暴力破解
|
|
112
|
-
- 验证码防刷
|
|
113
|
-
- 自定义限流 Key
|
|
114
|
-
|
|
115
|
-
### 8. Authz - 权限控制 ✅
|
|
116
|
-
|
|
117
|
-
**目录**: `authz/`
|
|
118
|
-
|
|
119
|
-
**文件**:
|
|
120
|
-
- `user.controller.ts` - 用户控制器示例
|
|
121
|
-
- `roles.guard.ts` - 角色守卫示例
|
|
122
|
-
- `README.md` - 详细使用文档
|
|
123
|
-
|
|
124
|
-
**核心功能**:
|
|
125
|
-
- 角色权限控制
|
|
126
|
-
- 细粒度权限控制
|
|
127
|
-
- 动态权限检查
|
|
128
|
-
- 资源级权限
|
|
129
|
-
|
|
130
|
-
## 🎯 示例特点
|
|
131
|
-
|
|
132
|
-
### 1. 可直接运行
|
|
133
|
-
|
|
134
|
-
所有示例代码都是完整的、可运行的,包含:
|
|
135
|
-
- 完整的 Service 层实现
|
|
136
|
-
- 完整的 Controller 层实现
|
|
137
|
-
- 详细的代码注释
|
|
138
|
-
- 实际的业务场景
|
|
139
|
-
|
|
140
|
-
### 2. 详细文档
|
|
141
|
-
|
|
142
|
-
每个示例都包含 README.md,包括:
|
|
143
|
-
- 功能说明
|
|
144
|
-
- 核心 API 使用方式
|
|
145
|
-
- 测试接口示例
|
|
146
|
-
- 环境变量配置
|
|
147
|
-
- 最佳实践
|
|
148
|
-
- 常见场景
|
|
149
|
-
- 官方文档链接
|
|
150
|
-
|
|
151
|
-
### 3. 最佳实践
|
|
152
|
-
|
|
153
|
-
展示推荐的使用方式:
|
|
154
|
-
- 错误处理
|
|
155
|
-
- 参数验证
|
|
156
|
-
- 安全性考虑
|
|
157
|
-
- 性能优化
|
|
158
|
-
- 代码组织
|
|
159
|
-
|
|
160
|
-
### 4. 测试友好
|
|
161
|
-
|
|
162
|
-
提供完整的测试命令:
|
|
163
|
-
- curl 命令示例
|
|
164
|
-
- 请求参数示例
|
|
165
|
-
- 响应格式示例
|
|
166
|
-
|
|
167
|
-
## 📚 使用方式
|
|
168
|
-
|
|
169
|
-
### 1. 查看示例
|
|
170
|
-
|
|
171
|
-
```bash
|
|
172
|
-
cd src/examples/cache
|
|
173
|
-
cat README.md
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### 2. 运行示例
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
# 启动项目
|
|
180
|
-
pnpm dev
|
|
181
|
-
|
|
182
|
-
# 测试接口
|
|
183
|
-
curl http://localhost:3000/examples/users/1
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### 3. 学习代码
|
|
187
|
-
|
|
188
|
-
每个示例都有详细的注释,可以直接阅读代码学习。
|
|
189
|
-
|
|
190
|
-
### 4. 复制使用
|
|
191
|
-
|
|
192
|
-
可以直接复制示例代码到你的项目中使用。
|
|
193
|
-
|
|
194
|
-
## 🔧 环境配置
|
|
195
|
-
|
|
196
|
-
所有示例都需要配置相应的环境变量,请参考 `.env.example` 文件。
|
|
197
|
-
|
|
198
|
-
### 必需配置
|
|
199
|
-
|
|
200
|
-
- **Redis**(cache, queue, rate-limit)
|
|
201
|
-
```env
|
|
202
|
-
REDIS_HOST=localhost
|
|
203
|
-
REDIS_PORT=6379
|
|
204
|
-
REDIS_PASSWORD=
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### 可选配置
|
|
208
|
-
|
|
209
|
-
根据使用的功能配置:
|
|
210
|
-
|
|
211
|
-
- **支付**(payment)
|
|
212
|
-
```env
|
|
213
|
-
WECHAT_MCH_ID=
|
|
214
|
-
WECHAT_PRIVATE_KEY=
|
|
215
|
-
ALIPAY_APP_ID=
|
|
216
|
-
ALIPAY_PRIVATE_KEY=
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
- **短信**(sms)
|
|
220
|
-
```env
|
|
221
|
-
SMS_PROVIDER=aliyun
|
|
222
|
-
SMS_ACCESS_KEY_ID=
|
|
223
|
-
SMS_ACCESS_KEY_SECRET=
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
- **OAuth**(oauth)
|
|
227
|
-
```env
|
|
228
|
-
WECHAT_OPEN_APP_ID=
|
|
229
|
-
WECHAT_OPEN_APP_SECRET=
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
- **对象存储**(storage)
|
|
233
|
-
```env
|
|
234
|
-
QINIU_ACCESS_KEY=
|
|
235
|
-
QINIU_SECRET_KEY=
|
|
236
|
-
QINIU_BUCKET=
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
## 📖 文档资源
|
|
240
|
-
|
|
241
|
-
- Svton 官方文档:https://751848178.github.io/svton
|
|
242
|
-
- GitHub:https://github.com/751848178/svton
|
|
243
|
-
|
|
244
|
-
## 💡 开发建议
|
|
245
|
-
|
|
246
|
-
1. **先看 README**:每个示例都有详细的 README,先阅读了解功能
|
|
247
|
-
2. **查看代码**:阅读示例代码,理解实现方式
|
|
248
|
-
3. **运行测试**:使用提供的 curl 命令测试接口
|
|
249
|
-
4. **修改尝试**:根据自己的需求修改示例代码
|
|
250
|
-
5. **查看文档**:遇到问题查看官方文档
|
|
251
|
-
|
|
252
|
-
## 🤝 贡献
|
|
253
|
-
|
|
254
|
-
如果你有更好的示例或发现问题,欢迎提交 PR 或 Issue。
|
|
255
|
-
|
|
256
|
-
## 📝 许可
|
|
257
|
-
|
|
258
|
-
MIT License
|