Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增nest模板、新增server类型的vite配置 #4743

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
15 changes: 15 additions & 0 deletions apps/backend-nest/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 适配器
VITE_ADAPTER=nest

# 应用名称
VITE_APP_NAME=Vben Server

# 应用入口
VITE_APP_PATH=src

# 应用入口的导出变量
VITE_EXPORT_NAME=default

# 立即启动
VITE_IMMEDIATE=true

1 change: 1 addition & 0 deletions apps/backend-nest/.env.analyze
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_VISUALIZER=true
8 changes: 8 additions & 0 deletions apps/backend-nest/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 端口号
VITE_PORT=5777

# 是否开启 SWC
VITE_SWC=true

# JWT密钥
VITE_JWT_SECRET=your_jwt_secret
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 8 additions & 0 deletions apps/backend-nest/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# 端口号
VITE_PORT=3000

# 是否开启 SWC
VITE_SWC=false

# JWT密钥
VITE_JWT_SECRET=your_jwt_secret
7 changes: 7 additions & 0 deletions apps/backend-nest/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"explorer.fileNesting.patterns": {
"*.ts": "${capture}.module.ts,${capture}.service.ts,${capture}.dto.ts,${capture}.controller.ts",
"*.module.ts": "${capture}.module.ts,${capture}.service.ts,${capture}.dto.ts",
"*.service.ts": "${capture}.dto.ts"
}
}
39 changes: 39 additions & 0 deletions apps/backend-nest/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@vben/backend-nest",
"version": "0.0.1",
"description": "",
"private": true,
"license": "MIT",
"author": "",
"scripts": {
"build": "pnpm vite build --mode production",
"build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck"
},
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^8.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.7",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@types/cookie-parser": "^1.4.7",
"@types/express": "^4.17.17",
"@types/node": "catalog:",
"@types/passport-jwt": "^4.0.1",
"@types/passport-local": "^1.0.38"
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
}
11 changes: 11 additions & 0 deletions apps/backend-nest/src/app/index.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';

import guards from '#/guards';
import { RoutesModule } from '#/routes';

@Module({
imports: [RoutesModule],
providers: [...guards],
})
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class AppModule {}
35 changes: 35 additions & 0 deletions apps/backend-nest/src/app/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { INestApplication } from '@nestjs/common';

import { NestFactory } from '@nestjs/core';

import plugins from '#/plugins';

import { AppModule } from './index.module';
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

let app: INestApplication;

async function createApp() {
const app = await NestFactory.create(AppModule);

app.enableCors();

for (const plugin of plugins) {
app.use(plugin);
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

return app;
}

export async function useApp() {
if (!app) {
app = await createApp();
}

return app;
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

export async function bootstrap() {
const app = await useApp();

await app.listen(import.meta.env.VITE_PORT);
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
19 changes: 19 additions & 0 deletions apps/backend-nest/src/auth/index.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';

import { AuthService } from './index.service';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';

@Module({
imports: [
PassportModule,
JwtModule.register({
secret: import.meta.env.VITE_JWT_SECRET,
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
}),
],
providers: [AuthService, LocalStrategy, JwtStrategy],
})
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class AuthModule {}
90 changes: 90 additions & 0 deletions apps/backend-nest/src/auth/index.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

export interface UserInfo {
id: number;
password: string;
realName: string;
roles: string[];
username: string;
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

@Injectable()
export class AuthService {
// TODO: Replace with your own secret key
static ACCESS_TOKEN_SECRET = 'access_token_secret';
static MOCK_CODES = [
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
// super
{
codes: ['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010'],
username: 'vben',
},
{
// admin
codes: ['AC_100010', 'AC_100020', 'AC_100030'],
username: 'admin',
},
{
// user
codes: ['AC_1000001', 'AC_1000002'],
username: 'jack',
},
];
static MOCK_USERS: UserInfo[] = [
{
id: 0,
password: '123456',
realName: 'Vben',
roles: ['super'],
username: 'vben',
},
{
id: 1,
password: '123456',
realName: 'Admin',
roles: ['admin'],
username: 'admin',
},
{
id: 2,
password: '123456',
realName: 'Jack',
roles: ['user'],
username: 'jack',
},
];
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
static REFRESH_TOKEN_SECRET = 'refresh_token_secret';

constructor(private readonly JwtService: JwtService) {}

public getAccessToken(user: UserInfo) {
return this.JwtService.sign(user, {
expiresIn: '7d',
});
}

public getRefreshToken(user: UserInfo) {
return this.JwtService.sign(user, {
expiresIn: '30d',
});
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

public async validateUser(username: string, password: string) {
const findUser = AuthService.MOCK_USERS.find(
(item) => item.username === username && item.password === password,
);

if (!findUser) {
return;
}

return findUser;
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
}

declare global {
export namespace Express {
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface User extends UserInfo {}
}
}
4 changes: 4 additions & 0 deletions apps/backend-nest/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { AuthModule } from './index.module';
export { AuthService } from './index.service';

export type { UserInfo } from './index.service';
18 changes: 18 additions & 0 deletions apps/backend-nest/src/auth/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: import.meta.env.VITE_JWT_SECRET,
});
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

async validate(payload: any) {
return { ...payload };
}
}
22 changes: 22 additions & 0 deletions apps/backend-nest/src/auth/local.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local';

import { AuthService } from './index.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private readonly AuthService: AuthService) {
super();
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
}

async validate(username: string, password: string): Promise<any> {
const findUser = await this.AuthService.validateUser(username, password);

if (!findUser) {
throw new UnauthorizedException();
}

return findUser;
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
}
30 changes: 30 additions & 0 deletions apps/backend-nest/src/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { Reflector } from '@nestjs/core';

import { type ExecutionContext, Injectable, SetMetadata } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private readonly Reflector: Reflector) {
super();
}

override canActivate(context: ExecutionContext) {
const isPublic = this.Reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);

if (isPublic) {
return true;
}

return super.canActivate(context);
}
}
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

// 默认导出,便于glob导入
export default JwtAuthGuard;
15 changes: 15 additions & 0 deletions apps/backend-nest/src/guards/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { ClassProvider, Type } from '@nestjs/common';

import { APP_GUARD } from '@nestjs/core';

const glob_result = import.meta.glob<Type>('./*.guard.ts', {
import: 'default',
eager: true,
});

export default Object.values(glob_result).map<ClassProvider>((useClass) => ({
provide: APP_GUARD,
useClass,
}));

export { Public } from './auth.guard';
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions apps/backend-nest/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { bootstrap, useApp } from './app';

if (import.meta.env.PROD) bootstrap();
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

export default useApp();
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved
28 changes: 28 additions & 0 deletions apps/backend-nest/src/plugins/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { createParamDecorator, type ExecutionContext } from '@nestjs/common';
import cookieParser from 'cookie-parser';

export default cookieParser();

export const Cookies = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request: Express.Request = ctx.switchToHttp().getRequest();
return data ? request.cookies?.[data] : request.cookies;
},
);
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

declare global {
export namespace Express {
export interface Request {
/**
* 请求密钥
* [可选] 如果向`cookie-parser`传递了密钥,则此属性将包含密钥。
* 可以被其他中间件使用
*/
secret?: string | undefined;
/** 解析尚未签名的cookie */
cookies: Record<string, any>;
/** 解析已签名的cookie */
signedCookies: Record<string, any>;
}
}
}
8 changes: 8 additions & 0 deletions apps/backend-nest/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const glob_result = import.meta.glob<any>(['./*.ts', '!./index.ts'], {
import: 'default',
eager: true,
});
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

export default Object.values(glob_result);
MZ-Dlovely marked this conversation as resolved.
Show resolved Hide resolved

export { Cookies } from './cookies';
Loading
Loading