Skip to content

Commit

Permalink
feat: add TransformInterceptor and HttpExceptionFilter
Browse files Browse the repository at this point in the history
  • Loading branch information
SolidZORO committed May 16, 2020
1 parent 8e08a49 commit 4866653
Show file tree
Hide file tree
Showing 29 changed files with 459 additions and 377 deletions.
8 changes: 4 additions & 4 deletions packages/leaa-api/src/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Permissions, PermissionsMetadataKey } from './permissions.decorator';
export { CurrentUser } from './current-user.decorator';
export { Language } from './language.decorator';
export { GqlCtx } from './gql-ctx.decorator';
export * from './permissions.decorator';
export * from './current-user.decorator';
export * from './language.decorator';
export * from './gql-ctx.decorator';
32 changes: 32 additions & 0 deletions packages/leaa-api/src/filters/http-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';

export interface IHttpException {
statusCode: number;
message: string;
lang: string;
}

@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const res = ctx.getResponse();
const req = ctx.getRequest();

const statusCode = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
const lang = req.language || '';

// @ts-ignore
let message = exception?.sqlMessage || exception?.message || 'KERNEL PANIC!!!';

if (exception instanceof HttpException) {
message = exception.message;
}

res.status(statusCode).json({
statusCode,
message,
lang,
});
}
}
1 change: 1 addition & 0 deletions packages/leaa-api/src/filters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './http-exception.filter';
5 changes: 2 additions & 3 deletions packages/leaa-api/src/guards/jwt.guard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { errorMsg } from '@leaa/api/src/utils';

@Injectable()
export class JwtGuard extends AuthGuard('jwt') {
Expand All @@ -11,7 +10,7 @@ export class JwtGuard extends AuthGuard('jwt') {
handleRequest(err: Error, user: any, info: any, context: any) {
const { t } = context.getRequest();

if (err || !user) throw errorMsg(t('_error:unauthorized'));
if (err || !user) throw new UnauthorizedException(t('_error:unauthorized'));

return user;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/leaa-api/src/guards/permissions.guard.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IPermissionSlug } from '@leaa/common/src/interfaces';
import { IRequest } from '@leaa/api/src/interfaces';
Expand Down
3 changes: 2 additions & 1 deletion packages/leaa-api/src/interceptors/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from '@leaa/api/src/interceptors/http-cache.interceptor';
export * from './http-cache.interceptor';
export * from './transform.interceptor';
50 changes: 26 additions & 24 deletions packages/leaa-api/src/interceptors/transform.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import { Injectable, HttpException, NestInterceptor, CallHandler, ExecutionContext, HttpStatus } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, HttpStatus } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { IResponse, IRequest } from '@leaa/api/src/interfaces';

@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
// constructor(private readonly reflector: Reflector) {}
export interface IHttpData<T> {
statusCode: number;
message: string;
data: T;
lang: string;
}

intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> {
const call$ = next.handle();
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, IHttpData<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<IHttpData<T>> {
const ctx = context.switchToHttp();
const res: IResponse = ctx.getResponse();
const req: IRequest = ctx.getRequest();

return call$.pipe(
catchError((error: any): any => {
if (error instanceof HttpException) {
return Promise.resolve({
code: error.getStatus(),
message: error.getResponse(),
});
}
const statusCode = res.statusCode || HttpStatus.OK;
const message = 'Success';
const lang = req.language || '';

if (error.code && error.details) {
return Promise.resolve({
code: error.code,
message: error.details,
});
}
}),
return next.handle().pipe(
map((data) => ({
statusCode,
message,
lang,
data,
})),
);
}
}
2 changes: 1 addition & 1 deletion packages/leaa-api/src/interfaces/decorator.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { User } from '@leaa/common/src/entrys';
import { IRequest } from '@leaa/api/src/interfaces/express.interface';
import { IRequest } from '@leaa/api/src/interfaces/http.interface';
import { TFunction } from 'i18next';

export interface IGqlCtx {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import ICore from 'express-serve-static-core';
import { TFunction } from 'i18next';

import { User, Auth } from '@leaa/common/src/entrys';
import { CrudRequest, RoutesOptions, BaseRoute } from '@nestjsx/crud';
import { IHttpException } from '@leaa/api/src/filters';
import { IHttpData } from '@leaa/api/src/interceptors';
import { CrudRequest, BaseRoute } from '@nestjsx/crud';

export interface IRequest extends ICore.Request {
user?: User;
Expand All @@ -12,6 +14,13 @@ export interface IRequest extends ICore.Request {
t: TFunction;
}

export interface IResponse extends ICore.Response {}

export declare type IApiResponse<T> = IHttpData<T> | IHttpException;

//
//

export interface ICrudRequest extends CrudRequest {
user?: User;
body: any;
Expand All @@ -22,21 +31,12 @@ export interface ICrudRequest extends CrudRequest {
t: TFunction;
}

export interface IResponse extends ICore.Response {
// user?: User;
// headers: {
// lang?: string;
// authorization?: string;
// };
// body: any;
// query: any;
}
//
//

export interface IRequestGithubCallback extends ICore.Request {
user?: {
userInfo: User;
userAuth: Auth;
};
}

// export type IResponse = Response;
2 changes: 1 addition & 1 deletion packages/leaa-api/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export * from './config.interface';
export * from './setting.interface';
export * from './auth.interface';
//
export * from './express.interface';
export * from './http.interface';
export * from './article.interface';
export * from './zan.interface';
export * from './product.interface';
Expand Down
5 changes: 4 additions & 1 deletion packages/leaa-api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { TagService } from '@leaa/api/src/modules/tag/tag.service';
import { ConfigService } from '@leaa/api/src/modules/config/config.service';
import { LoggerService } from '@leaa/api/src/modules/logger/logger.service';
import { I18nextMiddleware } from '@leaa/api/src/middlewares';
import { HttpExceptionFilter } from '@leaa/api/src/filters';
import { TransformInterceptor } from '@leaa/api/src/interceptors';

import { envInfoForCli } from '@leaa/api/src/utils';

Expand All @@ -22,9 +24,10 @@ import { envInfoForCli } from '@leaa/api/src/utils';
const configService = await app.get(ConfigService);
const publicPath = path.resolve(__dirname, `../${configService.PUBLIC_DIR}`);

// app.useStaticAssets(publicPath);
app.useStaticAssets(publicPath);
app.useGlobalPipes(new ValidationPipe());
app.useGlobalFilters(new HttpExceptionFilter());
app.useGlobalInterceptors(new TransformInterceptor());

app.disable('x-powered-by');
app.enableCors({
Expand Down
12 changes: 7 additions & 5 deletions packages/leaa-api/src/modules/action/action.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ import { ActionService } from './action.service';
alwaysPaginate: true,
},
routes: {
getManyBase: { decorators: [Permissions('action.list-read')] },
getOneBase: { decorators: [Permissions('action.item-read')] },
createOneBase: { decorators: [Permissions('action.item-create')] },
deleteOneBase: { decorators: [Permissions('action.item-delete')], returnDeleted: true },
getManyBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('action.list-read')] },
getOneBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('action.item-read')] },
createOneBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('action.item-create')] },
deleteOneBase: {
decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('action.item-delete')],
returnDeleted: true,
},
},
dto: {
create: CreateActionInput,
update: UpdateActionInput,
},
})
@UseGuards(JwtGuard, PermissionsGuard)
@Controller('/actions')
export class ActionController implements CrudController<Action> {
constructor(public service: ActionService) {}
Expand Down
8 changes: 5 additions & 3 deletions packages/leaa-api/src/modules/article/article.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ import { ArticleService } from './article.service';
routes: {
// getManyBase: { decorators: [Permissions('article.list-read')] },
// getOneBase: { decorators: [Permissions('article.item-read')] },
createOneBase: { decorators: [Permissions('article.item-create')] },
deleteOneBase: { decorators: [Permissions('article.item-delete')], returnDeleted: true },
createOneBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('article.item-create')] },
deleteOneBase: {
decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('article.item-delete')],
returnDeleted: true,
},
},
dto: {
create: CreateArticleInput,
update: UpdateArticleInput,
},
})
@UseGuards(JwtGuard, PermissionsGuard)
@Controller('/articles')
export class ArticleController implements CrudController<Article> {
constructor(public service: ArticleService) {}
Expand Down
23 changes: 4 additions & 19 deletions packages/leaa-api/src/modules/auth/auth-local.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import svgCaptcha from 'svg-captcha';
import xss from 'xss';
import bcryptjs from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';
import { Injectable } from '@nestjs/common';
import { Injectable, UnauthorizedException, HttpException, NotFoundException } from '@nestjs/common';
import { Repository, Between, In } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';

Expand Down Expand Up @@ -47,11 +47,11 @@ export class AuthLocalService extends TypeOrmCrudService<Auth> {
async login(req: ICrudRequest, body: AuthLoginInput): Promise<User | undefined> {
const { t } = req;

if (!req || !req?.ip) throw errorMsg(t('_error:notFoundIp'));
if (!req || !req?.ip) throw new NotFoundException(t('_error:notFoundIp'));

const account = xss.filterXSS(body.email.trim().toLowerCase());

const findUser = await this.userRepository.findOne({
const findUser = await this.userRepository.findOneOrFail({
select: ['id', 'email', 'name', 'status', 'password', 'avatar_url'],
where: {
email: account,
Expand All @@ -60,9 +60,6 @@ export class AuthLocalService extends TypeOrmCrudService<Auth> {
relations: ['roles'],
});

console.log('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ');
console.log('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ', req.options);

// log
const loginAction = await this.actionService.logAction(req, {
ip: req?.ip,
Expand All @@ -71,16 +68,6 @@ export class AuthLocalService extends TypeOrmCrudService<Auth> {
token: body.guestToken || 'NO-TOKEN',
account,
});
// {
// const loginAction = await this.actionService.createOne(req, {
// // ip: req?.ip,
// module: 'auth',
// action: 'login',
// // token: body.guestToken || 'NO-TOKEN',
// // account,
// });

console.log('XXXXXXXXXX', loginAction);

const user = checkAvailableUser(findUser);

Expand Down Expand Up @@ -126,9 +113,7 @@ export class AuthLocalService extends TypeOrmCrudService<Auth> {
const msg = `User (${account}) Info Not Match`;
logger.log(msg, CLS_NAME);

// throw errorMsg(t('_error:userInfoNotMatch'));
throw errorMsg(t('_error:userInfoNotMatch'), { statusCode: 401 });
// return Error('111111111111');
throw new UnauthorizedException(t('_error:userInfoNotMatch'));
}

if (user.password) delete user.password;
Expand Down
16 changes: 0 additions & 16 deletions packages/leaa-api/src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,14 @@ import { JwtGuard, PermissionsGuard } from '@leaa/api/src/guards';
// export class UserController {
// @UseGuards(JwtGuard)
export class AuthController {
// constructor(public authLocalService: AuthLocalService) {}

constructor(public authLocalService: AuthLocalService) {}

// constructor(
// private readonly authLocalService: AuthLocalService,
// private readonly authGithubService: AuthGithubService,
// ) {}

@HttpCode(200)
@Post('/login')
async login(@Req() req: ICrudRequest, @Body() body: AuthLoginInput): Promise<any> {
return this.authLocalService.login(req, body);
}

@HttpCode(200)
@Permissions('action.list-read')
@UseGuards(JwtGuard, PermissionsGuard)
@Post('/login2')
async login2(@Req() req: ICrudRequest, @Body() body: AuthLoginInput): Promise<any> {
console.log(req.user);
return JSON.stringify(req.user?.flatPermissions);
}

// //
// // wechat
// @Get('/wechat/verify')
Expand Down
43 changes: 43 additions & 0 deletions packages/leaa-api/src/modules/category/category.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Controller, UseGuards, Get, Req, Query } from '@nestjs/common';
import { Crud, CrudController, ParsedRequest, CrudRequest } from '@nestjsx/crud';

import { Permissions } from '@leaa/api/src/decorators';
// import { curdConfig } from '@leaa/api/src/configs';
import { CreateCategoryInput, UpdateCategoryInput } from '@leaa/common/src/dtos/category';
import { JwtGuard, PermissionsGuard } from '@leaa/api/src/guards';
import { Category } from '@leaa/common/src/entrys';
import { CategoryService } from './category.service';
import { QueryBuilder } from 'typeorm';

@Crud({
model: {
type: Category,
},
query: {
maxLimit: 1000,
alwaysPaginate: true,
},
routes: {
getManyBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard)] },
getOneBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard)] },
createOneBase: { decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('category.item-create')] },
deleteOneBase: {
decorators: [UseGuards(JwtGuard, PermissionsGuard), Permissions('category.item-delete')],
returnDeleted: true,
},
},
dto: {
create: CreateCategoryInput,
update: UpdateCategoryInput,
},
})
@Controller('/categories')
export class CategoryController implements CrudController<Category> {
constructor(public service: CategoryService) {}

@Get('tree')
// async tree(@ParsedRequest() req: CrudRequest) {
async tree(@Req() req: CrudRequest, @Query() query: any) {
return this.service.tree();
}
}

0 comments on commit 4866653

Please sign in to comment.