Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,590 changes: 2,399 additions & 191 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import { AppService } from './app.service';
import { PostsService } from './modules/posts/posts.service';
import { PrismaService } from './prisma.service';
import { PostsModule } from './modules/posts/posts.module';
import { AuthModule } from './modules/auth/auth.module';
import { UsersModule } from './modules/users/users.module';
import { UsersController } from './modules/users/users.controller';
import { UsersService } from './modules/users/users.service';
import { AuthModule } from './modules/auth/auth.module';
import { AuthService } from './modules/auth/auth.service';
import { AuthController } from './modules/auth/auth.controller';

@Module({
imports: [PostsModule, AuthModule, UsersModule],
controllers: [AppController, UsersController],
providers: [AppService, PrismaService, PostsService, UsersService],
controllers: [AppController, UsersController, AuthController],
providers: [
AppService,
PrismaService,
PostsService,
UsersService,
AuthService,
],
exports: [PrismaService],
})
export class AppModule {}
18 changes: 18 additions & 0 deletions src/modules/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from '../auth/auth.controller';

describe('AuthController', () => {
let controller: AuthController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();

controller = module.get<AuthController>(AuthController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
24 changes: 17 additions & 7 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
import {
Body,
Controller,
Get,
Post,
Request,
UseGuards,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthGuard } from './guards/auth.guard';

@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}

@Post('login')
async login(@Body() body: { username: string; password: string }) {
const user = await this.authService.validateUser(body.username, body.password);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
return this.authService.login(user);
login(@Body() input: { username: string; password: string }) {
return this.authService.authenticate(input);
}

@UseGuards(AuthGuard)
@Get('profile')
getUserInfo(@Request() request) {
return request.user;
}
}
23 changes: 12 additions & 11 deletions src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { PrismaService } from 'src/prisma.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
import { PrismaService } from 'src/prisma.service';
import { JwtModule } from '@nestjs/jwt';
import { JWT_SECRET } from '../config/jwt-secret';
import { JwtStrategy } from './strategy/jwt.strategy';

@Module({
providers: [AuthService, PrismaService, JwtStrategy],
controllers: [AuthController],
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
UsersModule,
JwtModule.register({
secret: 'secretKey',
signOptions: { expiresIn: '1h' },
global: true,
secret: JWT_SECRET,
signOptions: { expiresIn: '1d' },
}),
],
controllers: [AuthController],
providers: [AuthService, PrismaService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
18 changes: 18 additions & 0 deletions src/modules/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';

describe('AuthService', () => {
let service: AuthService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();

service = module.get<AuthService>(AuthService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
57 changes: 40 additions & 17 deletions src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,61 @@
import { Injectable } from '@nestjs/common';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcryptjs';
import { PrismaService } from 'src/prisma.service';

type AuthInput = { username: string; password: string };
type SignInData = { userId: number; username: string; role: string };
type AuthResult = {
accessToken: string;
userId: number;
username: string;
role: string;
};

@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
private prismaService: PrismaService,
) {}

async validateUser(username: string, password: string): Promise<any> {
const user = await this.prismaService.user.findFirst({
where: { name: username },
});
async authenticate(input: AuthInput): Promise<AuthResult> {
const user = await this.validateUser(input);

if (!user) {
return null;
throw new UnauthorizedException();
}

// const isMatch = await bcrypt.compare(password, user.passwordHash);
const isMatch = password == user.passwordHash;
if (isMatch) {
const { passwordHash, ...result } = user;
return result;
return this.signIn(user);
}

async validateUser(input: AuthInput): Promise<SignInData | null> {
const user = await this.usersService.findUserByName(input.username);

if (user && user.passwordHash === input.password) {
return {
userId: user.id,
username: user.name,
role: user.role,
};
}

return null;
}

async login(user: any) {
const payload = { username: user.name, sub: user.id, role: user.role };
async signIn(user: SignInData): Promise<AuthResult> {
const tokenPayload = {
sub: user.userId,
username: user.username,
role: user.role,
};

const accessToken = await this.jwtService.signAsync(tokenPayload);

return {
access_token: this.jwtService.sign(payload),
accessToken,
username: user.username,
userId: user.userId,
role: user.role,
};
}
}
34 changes: 34 additions & 0 deletions src/modules/auth/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}

async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const authorization = request.headers.authorization;
const token = authorization?.split(' ')[1];

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

try {
const tokenPayload = await this.jwtService.verifyAsync(token);
request.user = {
userId: tokenPayload.sub,
username: tokenPayload.username,
role: tokenPayload.role,
};
return true;
} catch (error) {
throw new UnauthorizedException();
}
}
}
4 changes: 0 additions & 4 deletions src/modules/auth/roles.decorator.ts

This file was deleted.

20 changes: 0 additions & 20 deletions src/modules/auth/roles.guard.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { JWT_SECRET } from 'src/modules/config/jwt-secret';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: 'secretKey',
secretOrKey: JWT_SECRET,
});
}

async validate(payload: any) {
return { userId: payload.sub, username: payload.username, role: payload.role };
return { id: payload.sub, username: payload.username, role: payload.role };
}
}
1 change: 1 addition & 0 deletions src/modules/config/jwt-secret.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const JWT_SECRET = '';
15 changes: 2 additions & 13 deletions src/modules/posts/posts.controller.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { Body, Controller, Get, Param, UseGuards } from '@nestjs/common';
import { Controller, Get, Param } from '@nestjs/common';
import { PostsService } from './posts.service';
import { Post as PostModel } from '@prisma/client';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { CreatePostDto } from './dto/create-post.dto';

@Controller('posts')
export class PostsController {
constructor(private readonly postService: PostsService) {}

// @UseGuards(JwtAuthGuard)
@Get('published')
async findPublishedPosts(): Promise<PostModel[]> {
return this.postService.posts({
Expand All @@ -35,15 +32,7 @@ export class PostsController {
}

@Get(':slug')
async findPostBySlug(@Param('slug') slug: string): Promise<PostModel> {
async getPostBySlug(@Param('slug') slug: string): Promise<PostModel> {
return this.postService.findPostBySlug(slug);
}

// @UseGuards(JwtAuthGuard)
// @Post('create')
// async createPost(
// @Body() postData: CreatePostDto,
// ): Promise<PostModel> {
// return this.postService.createPost(postData);
// }
}
10 changes: 8 additions & 2 deletions src/modules/posts/posts.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma.service';
import { Post, Prisma } from '@prisma/client';

Expand All @@ -7,7 +7,7 @@ export class PostsService {
constructor(private prisma: PrismaService) {}

async findPostBySlug(slug: string): Promise<Post | null> {
return this.prisma.post.findUnique({
const post = await this.prisma.post.findUnique({
where: { slug },
include: {
category: {
Expand All @@ -22,6 +22,12 @@ export class PostsService {
},
},
});

if (!post) {
throw new NotFoundException('Post not found!');
}

return post;
}

async posts(params: {
Expand Down
1 change: 1 addition & 0 deletions src/modules/users/users.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import { PrismaService } from 'src/prisma.service';
@Module({
controllers: [UsersController],
providers: [UsersService, PrismaService],
exports: [UsersService],
})
export class UsersModule {}
14 changes: 13 additions & 1 deletion src/modules/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class UsersService {

async findAll() {
return this.prisma.user.findMany({
where: { deletedAt: null },
where: { deletedAt: null, role: 'creator' },
select: {
id: true,
name: true,
Expand Down Expand Up @@ -44,6 +44,18 @@ export class UsersService {
return user;
}

async findUserByName(name: string) {
const user = this.prisma.user.findFirst({ where: { name } });

if (!user) {
throw new NotFoundException(
`Usuário com o nome "${name}" não encontrado.`,
);
}

return user;
}

async update(id: number, data: UpdateUserDto) {
const findUser = await this.findOne(id);

Expand Down
Loading