Compare commits
8 Commits
3f61a16324
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
78807ffd61
|
|||
|
d4a9519cbf
|
|||
|
577d96d68c
|
|||
|
73ee4e2894
|
|||
|
f62fb6d687
|
|||
|
5619ebfc17
|
|||
|
7f1440ed65
|
|||
|
5a6470a3ce
|
@@ -4,7 +4,7 @@
|
||||
"enabled": false
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"performance": {
|
||||
@@ -21,5 +21,10 @@
|
||||
"indentWidth": 2,
|
||||
"lineWidth": 80,
|
||||
"formatWithErrors": false
|
||||
},
|
||||
"javascript": {
|
||||
"parser": {
|
||||
"unsafeParameterDecoratorsEnabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/config": "^3.2.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@prisma/client": "^5.10.2",
|
||||
"argon2": "^0.40.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
@@ -35,6 +43,7 @@
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"prisma": "^5.10.2",
|
||||
|
||||
43
prisma/migrations/20240227151536_relation/migration.sql
Normal file
43
prisma/migrations/20240227151536_relation/migration.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `Bookmark` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropTable
|
||||
DROP TABLE `Bookmark`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `User`;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `users` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
`email` VARCHAR(191) NOT NULL,
|
||||
`hash` VARCHAR(191) NOT NULL,
|
||||
`firstName` VARCHAR(191) NULL,
|
||||
`lastName` VARCHAR(191) NULL,
|
||||
|
||||
UNIQUE INDEX `users_id_key`(`id`),
|
||||
UNIQUE INDEX `users_email_key`(`email`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE `bookmarks` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
`title` VARCHAR(191) NOT NULL,
|
||||
`description` VARCHAR(191) NULL,
|
||||
`link` VARCHAR(191) NOT NULL,
|
||||
`userId` INTEGER NOT NULL,
|
||||
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE `bookmarks` ADD CONSTRAINT `bookmarks_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `users`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -14,15 +14,18 @@ datasource db {
|
||||
}
|
||||
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
id Int @id @unique @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
email String
|
||||
email String @unique
|
||||
hash String
|
||||
|
||||
firstName String?
|
||||
lastName String?
|
||||
|
||||
@@map("users")
|
||||
bookmarks Bookmark[]
|
||||
}
|
||||
|
||||
model Bookmark {
|
||||
@@ -33,4 +36,9 @@ model Bookmark {
|
||||
title String
|
||||
description String?
|
||||
link String
|
||||
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@map("bookmarks")
|
||||
}
|
||||
|
||||
@@ -5,10 +5,17 @@ import { BookmarkModule } from './bookmark/bookmark.module';
|
||||
import { AuthService } from './auth/auth.service';
|
||||
import { AuthController } from './auth/auth.controller';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
|
||||
@Module({
|
||||
imports: [AuthModule, PrismaModule, UserModule, BookmarkModule],
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
AuthModule,
|
||||
PrismaModule,
|
||||
UserModule,
|
||||
BookmarkModule,
|
||||
],
|
||||
providers: [AuthService],
|
||||
controllers: [AuthController]
|
||||
controllers: [AuthController],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -6,15 +6,13 @@ import { AuthDto } from "./dto";
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@Post('signup')
|
||||
signup(@Body() dto: AuthDto) {
|
||||
console.log({dto});
|
||||
return this.authService.signup()
|
||||
@Post("register")
|
||||
async signup(@Body() dto: AuthDto) {
|
||||
return await this.authService.register(dto);
|
||||
}
|
||||
|
||||
@Post('signin')
|
||||
signin(@Body() dto: AuthDto) {
|
||||
console.log({dto});
|
||||
return this.authService.signin()
|
||||
@Post("login")
|
||||
async signin(@Body() dto: AuthDto) {
|
||||
return await this.authService.login(dto);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@ import { Module } from "@nestjs/common";
|
||||
import { PrismaModule } from "src/prisma/prisma.module";
|
||||
import { AuthController } from "./auth.controller";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { JwtModule } from "@nestjs/jwt";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, JwtModule.register({})],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService]
|
||||
})
|
||||
export class AuthModule {}
|
||||
export class AuthModule { }
|
||||
|
||||
@@ -1,17 +1,80 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { ForbiddenException, Injectable } from "@nestjs/common";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { AuthDto } from "./dto";
|
||||
import * as argon from "argon2";
|
||||
import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { User } from "@prisma/client";
|
||||
import { time } from "console";
|
||||
|
||||
@Injectable({})
|
||||
export class AuthService {
|
||||
constructor(private prisma: PrismaService) {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private jwt: JwtService,
|
||||
) {}
|
||||
|
||||
async login(dto: AuthDto) {
|
||||
const User = await this.prisma.user.findUnique({
|
||||
where: {
|
||||
email: dto.email,
|
||||
},
|
||||
});
|
||||
if (!User) {
|
||||
console.warn(`ACCESS: Refused login for "${dto.email}" (email not used)`);
|
||||
throw new ForbiddenException("Credential(s) invalid.");
|
||||
}
|
||||
|
||||
signin() {
|
||||
return {response: "Sign IN"}
|
||||
const pwMatches: boolean = await argon.verify(User.hash, dto.password);
|
||||
if (!pwMatches) {
|
||||
console.warn(
|
||||
`ACCESS: Refused login for "${dto.email}" (invalid password)`,
|
||||
);
|
||||
throw new ForbiddenException("Credential(s) invalid.");
|
||||
}
|
||||
|
||||
signup() {
|
||||
return {response: "Sign UP"}
|
||||
delete User.hash;
|
||||
console.info(`ACCESS: Granted login for "${dto.email}"`);
|
||||
return User;
|
||||
}
|
||||
|
||||
async register(dto: AuthDto) {
|
||||
const userPasswordHash = await argon.hash(dto.password);
|
||||
try {
|
||||
const User = await this.prisma.user.create({
|
||||
data: {
|
||||
email: dto.email,
|
||||
hash: userPasswordHash,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
},
|
||||
});
|
||||
//delete User.hash;
|
||||
return User;
|
||||
} catch (error) {
|
||||
if (error instanceof PrismaClientKnownRequestError) {
|
||||
if (error.code === "P2002") {
|
||||
throw new ForbiddenException("Credential(s) taken.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async generateAuthToken(targetUser: User, dayToLive: number) {
|
||||
const timestamp = Date.now();
|
||||
|
||||
const jwtPayload = {
|
||||
sub: targetUser.id,
|
||||
iat: timestamp,
|
||||
};
|
||||
|
||||
return this.jwt.signAsync(jwtPayload, {
|
||||
expiresIn: `${dayToLive}d`,
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
export interface AuthDto {
|
||||
email: string,
|
||||
password: string
|
||||
import { IsEmail, IsNotEmpty, IsStrongPassword } from "class-validator";
|
||||
|
||||
export class AuthDto {
|
||||
@IsEmail()
|
||||
@IsNotEmpty()
|
||||
email: string;
|
||||
|
||||
@IsStrongPassword({
|
||||
minLength: 8,
|
||||
minLowercase: 1,
|
||||
minUppercase: 1,
|
||||
minNumbers: 1,
|
||||
minSymbols: 1,
|
||||
})
|
||||
password: string;
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from "@nestjs/common";
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
}),
|
||||
);
|
||||
await app.listen(3333);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient {
|
||||
constructor() {
|
||||
constructor(config: ConfigService) {
|
||||
super({
|
||||
datasources: {
|
||||
db: {
|
||||
url: "mysql://avnyr:orpmocclealis8havele@127.0.0.1:3306/bookmarks"
|
||||
}
|
||||
}
|
||||
})
|
||||
url: config.get("DATABASE_URL"),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user