Showing
29 changed files
with
741 additions
and
0 deletions
.env
0 → 100644
1 | +# Environment variables declared in this file are automatically made available to Prisma. | ||
2 | +# See the documentation for more detail: https://pris.ly/d/prisma-schema#using-environment-variables | ||
3 | + | ||
4 | +# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server and MongoDB (Preview). | ||
5 | +# See the documentation for all the connection string options: https://pris.ly/d/connection-strings | ||
6 | + | ||
7 | +DATABASE_URL="postgresql://postgres:12345@localhost:5432/postgres" | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
.eslintrc.js
0 → 100644
1 | +module.exports = { | ||
2 | + parser: '@typescript-eslint/parser', | ||
3 | + parserOptions: { | ||
4 | + project: 'tsconfig.json', | ||
5 | + sourceType: 'module', | ||
6 | + }, | ||
7 | + plugins: ['@typescript-eslint/eslint-plugin'], | ||
8 | + extends: [ | ||
9 | + 'plugin:@typescript-eslint/recommended', | ||
10 | + 'plugin:prettier/recommended', | ||
11 | + ], | ||
12 | + root: true, | ||
13 | + env: { | ||
14 | + node: true, | ||
15 | + jest: true, | ||
16 | + }, | ||
17 | + ignorePatterns: ['.eslintrc.js'], | ||
18 | + rules: { | ||
19 | + '@typescript-eslint/interface-name-prefix': 'off', | ||
20 | + '@typescript-eslint/explicit-function-return-type': 'off', | ||
21 | + '@typescript-eslint/explicit-module-boundary-types': 'off', | ||
22 | + '@typescript-eslint/no-explicit-any': 'off', | ||
23 | + }, | ||
24 | +}; |
.gitignore
0 → 100644
1 | +# compiled output | ||
2 | +/dist | ||
3 | +/node_modules | ||
4 | + | ||
5 | +# Logs | ||
6 | +logs | ||
7 | +*.log | ||
8 | +npm-debug.log* | ||
9 | +pnpm-debug.log* | ||
10 | +yarn-debug.log* | ||
11 | +yarn-error.log* | ||
12 | +lerna-debug.log* | ||
13 | + | ||
14 | +# OS | ||
15 | +.DS_Store | ||
16 | + | ||
17 | +# Tests | ||
18 | +/coverage | ||
19 | +/.nyc_output | ||
20 | + | ||
21 | +# IDEs and editors | ||
22 | +/.idea | ||
23 | +.project | ||
24 | +.classpath | ||
25 | +.c9/ | ||
26 | +*.launch | ||
27 | +.settings/ | ||
28 | +*.sublime-workspace | ||
29 | + | ||
30 | +# IDE - VSCode | ||
31 | +.vscode/* | ||
32 | +!.vscode/settings.json | ||
33 | +!.vscode/tasks.json | ||
34 | +!.vscode/launch.json | ||
35 | +!.vscode/extensions.json | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
.prettierrc
0 → 100644
README.md
0 → 100644
1 | +<p align="center"> | ||
2 | + <a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a> | ||
3 | +</p> | ||
4 | + | ||
5 | +[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 | ||
6 | +[circleci-url]: https://circleci.com/gh/nestjs/nest | ||
7 | + | ||
8 | + <p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p> | ||
9 | + <p align="center"> | ||
10 | +<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a> | ||
11 | +<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a> | ||
12 | +<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a> | ||
13 | +<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a> | ||
14 | +<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a> | ||
15 | +<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a> | ||
16 | +<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a> | ||
17 | +<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a> | ||
18 | + <a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a> | ||
19 | + <a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a> | ||
20 | + <a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a> | ||
21 | +</p> | ||
22 | + <!--[](https://opencollective.com/nest#backer) | ||
23 | + [](https://opencollective.com/nest#sponsor)--> | ||
24 | + | ||
25 | +## Description | ||
26 | + | ||
27 | +[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. | ||
28 | + | ||
29 | +## Installation | ||
30 | + | ||
31 | +```bash | ||
32 | +$ npm install | ||
33 | +``` | ||
34 | + | ||
35 | +## Running the app | ||
36 | + | ||
37 | +```bash | ||
38 | +# development | ||
39 | +$ npm run start | ||
40 | + | ||
41 | +# watch mode | ||
42 | +$ npm run start:dev | ||
43 | + | ||
44 | +# production mode | ||
45 | +$ npm run start:prod | ||
46 | +``` | ||
47 | + | ||
48 | +## Test | ||
49 | + | ||
50 | +```bash | ||
51 | +# unit tests | ||
52 | +$ npm run test | ||
53 | + | ||
54 | +# e2e tests | ||
55 | +$ npm run test:e2e | ||
56 | + | ||
57 | +# test coverage | ||
58 | +$ npm run test:cov | ||
59 | +``` | ||
60 | + | ||
61 | +## Support | ||
62 | + | ||
63 | +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). | ||
64 | + | ||
65 | +## Stay in touch | ||
66 | + | ||
67 | +- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) | ||
68 | +- Website - [https://nestjs.com](https://nestjs.com/) | ||
69 | +- Twitter - [@nestframework](https://twitter.com/nestframework) | ||
70 | + | ||
71 | +## License | ||
72 | + | ||
73 | +Nest is [MIT licensed](LICENSE). |
nest-cli.json
0 → 100644
package-lock.json
0 → 100644
This diff could not be displayed because it is too large.
package.json
0 → 100644
1 | +{ | ||
2 | + "name": "learn-in-web-backend", | ||
3 | + "version": "0.0.1", | ||
4 | + "description": "", | ||
5 | + "author": "", | ||
6 | + "private": true, | ||
7 | + "license": "UNLICENSED", | ||
8 | + "scripts": { | ||
9 | + "prebuild": "rimraf dist", | ||
10 | + "build": "nest build", | ||
11 | + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", | ||
12 | + "start": "nest start", | ||
13 | + "start:dev": "nest start --watch", | ||
14 | + "start:debug": "nest start --debug --watch", | ||
15 | + "start:prod": "node dist/main", | ||
16 | + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", | ||
17 | + "test": "jest", | ||
18 | + "test:watch": "jest --watch", | ||
19 | + "test:cov": "jest --coverage", | ||
20 | + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", | ||
21 | + "test:e2e": "jest --config ./test/jest-e2e.json", | ||
22 | + "migrate:dev": "npx prisma migrate dev", | ||
23 | + "migrate:dev:create": "npx prisma migrate dev --create-only", | ||
24 | + "migrate:deploy": "npx prisma migrate deploy", | ||
25 | + "prisma:generate": "npx prisma generate", | ||
26 | + "prisma:studio": "npx prisma studio", | ||
27 | + "prisma:seed": "npx prisma db seed --preview-feature" | ||
28 | + }, | ||
29 | + "dependencies": { | ||
30 | + "@nestjs/common": "^8.0.0", | ||
31 | + "@nestjs/core": "^8.0.0", | ||
32 | + "@nestjs/jwt": "^8.0.0", | ||
33 | + "@nestjs/platform-express": "^8.0.0", | ||
34 | + "@prisma/client": "^3.4.2", | ||
35 | + "bcrypt": "^5.0.1", | ||
36 | + "nestjs-prisma": "^0.14.3", | ||
37 | + "reflect-metadata": "^0.1.13", | ||
38 | + "rimraf": "^3.0.2", | ||
39 | + "rxjs": "^7.2.0" | ||
40 | + }, | ||
41 | + "devDependencies": { | ||
42 | + "@nestjs/cli": "^8.0.0", | ||
43 | + "@nestjs/schematics": "^8.0.0", | ||
44 | + "@nestjs/testing": "^8.0.0", | ||
45 | + "@types/bcrypt": "^5.0.0", | ||
46 | + "@types/express": "^4.17.13", | ||
47 | + "@types/jest": "^27.0.1", | ||
48 | + "@types/node": "^16.0.0", | ||
49 | + "@types/supertest": "^2.0.11", | ||
50 | + "@typescript-eslint/eslint-plugin": "^5.0.0", | ||
51 | + "@typescript-eslint/parser": "^5.0.0", | ||
52 | + "eslint": "^8.0.1", | ||
53 | + "eslint-config-prettier": "^8.3.0", | ||
54 | + "eslint-plugin-prettier": "^4.0.0", | ||
55 | + "jest": "^27.2.5", | ||
56 | + "prettier": "^2.3.2", | ||
57 | + "prisma": "latest", | ||
58 | + "source-map-support": "^0.5.20", | ||
59 | + "supertest": "^6.1.3", | ||
60 | + "ts-jest": "^27.0.3", | ||
61 | + "ts-loader": "^9.2.3", | ||
62 | + "ts-node": "^10.0.0", | ||
63 | + "tsconfig-paths": "^3.10.1", | ||
64 | + "typescript": "^4.3.5" | ||
65 | + }, | ||
66 | + "jest": { | ||
67 | + "moduleFileExtensions": [ | ||
68 | + "js", | ||
69 | + "json", | ||
70 | + "ts" | ||
71 | + ], | ||
72 | + "rootDir": "src", | ||
73 | + "testRegex": ".*\\.spec\\.ts$", | ||
74 | + "transform": { | ||
75 | + "^.+\\.(t|j)s$": "ts-jest" | ||
76 | + }, | ||
77 | + "collectCoverageFrom": [ | ||
78 | + "**/*.(t|j)s" | ||
79 | + ], | ||
80 | + "coverageDirectory": "../coverage", | ||
81 | + "testEnvironment": "node" | ||
82 | + }, | ||
83 | + "prisma": { | ||
84 | + "seed": "ts-node prisma/seed.ts" | ||
85 | + } | ||
86 | +} |
1 | +-- CreateEnum | ||
2 | +CREATE TYPE "Level" AS ENUM ('LOW', 'MEDIUM', 'HIGH'); | ||
3 | + | ||
4 | +-- CreateEnum | ||
5 | +CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN'); | ||
6 | + | ||
7 | +-- CreateTable | ||
8 | +CREATE TABLE "User" ( | ||
9 | + "id" UUID NOT NULL DEFAULT gen_random_uuid(), | ||
10 | + "email" TEXT NOT NULL, | ||
11 | + "name" TEXT NOT NULL, | ||
12 | + "password" TEXT NOT NULL, | ||
13 | + "role" "Role" NOT NULL DEFAULT E'USER', | ||
14 | + | ||
15 | + CONSTRAINT "User_pkey" PRIMARY KEY ("id") | ||
16 | +); | ||
17 | + | ||
18 | +-- CreateTable | ||
19 | +CREATE TABLE "Post" ( | ||
20 | + "id" UUID NOT NULL DEFAULT gen_random_uuid(), | ||
21 | + "title" TEXT NOT NULL, | ||
22 | + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
23 | + "private" BOOLEAN NOT NULL DEFAULT true, | ||
24 | + "authorId" UUID NOT NULL, | ||
25 | + "level" "Level" NOT NULL DEFAULT E'MEDIUM', | ||
26 | + "content" TEXT NOT NULL, | ||
27 | + | ||
28 | + CONSTRAINT "Post_pkey" PRIMARY KEY ("id") | ||
29 | +); | ||
30 | + | ||
31 | +-- CreateTable | ||
32 | +CREATE TABLE "Comment" ( | ||
33 | + "id" UUID NOT NULL DEFAULT gen_random_uuid(), | ||
34 | + "content" TEXT NOT NULL, | ||
35 | + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
36 | + "authorId" UUID NOT NULL, | ||
37 | + "postId" UUID NOT NULL, | ||
38 | + | ||
39 | + CONSTRAINT "Comment_pkey" PRIMARY KEY ("id") | ||
40 | +); | ||
41 | + | ||
42 | +-- CreateIndex | ||
43 | +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); | ||
44 | + | ||
45 | +-- AddForeignKey | ||
46 | +ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||
47 | + | ||
48 | +-- AddForeignKey | ||
49 | +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||
50 | + | ||
51 | +-- AddForeignKey | ||
52 | +ALTER TABLE "Comment" ADD CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE; |
1 | +/* | ||
2 | + Warnings: | ||
3 | + | ||
4 | + - A unique constraint covering the columns `[id]` on the table `Comment` will be added. If there are existing duplicate values, this will fail. | ||
5 | + - A unique constraint covering the columns `[id]` on the table `Post` will be added. If there are existing duplicate values, this will fail. | ||
6 | + - A unique constraint covering the columns `[id]` on the table `User` will be added. If there are existing duplicate values, this will fail. | ||
7 | + | ||
8 | +*/ | ||
9 | +-- CreateIndex | ||
10 | +CREATE UNIQUE INDEX "Comment_id_key" ON "Comment"("id"); | ||
11 | + | ||
12 | +-- CreateIndex | ||
13 | +CREATE UNIQUE INDEX "Post_id_key" ON "Post"("id"); | ||
14 | + | ||
15 | +-- CreateIndex | ||
16 | +CREATE UNIQUE INDEX "User_id_key" ON "User"("id"); |
prisma/migrations/migration_lock.toml
0 → 100644
prisma/schema.prisma
0 → 100644
1 | +// This is your Prisma schema file, | ||
2 | +// learn more about it in the docs: https://pris.ly/d/prisma-schema | ||
3 | + | ||
4 | +generator client { | ||
5 | + provider = "prisma-client-js" | ||
6 | +} | ||
7 | + | ||
8 | +datasource db { | ||
9 | + provider = "postgres" | ||
10 | + url = env("DATABASE_URL") | ||
11 | +} | ||
12 | + | ||
13 | +model User { | ||
14 | + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid @unique | ||
15 | + email String @unique | ||
16 | + name String | ||
17 | + password String | ||
18 | + role Role @default(USER) | ||
19 | + | ||
20 | + posts Post[] | ||
21 | + comments Comment[] | ||
22 | +} | ||
23 | + | ||
24 | +model Post { | ||
25 | + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid @unique | ||
26 | + title String | ||
27 | + createdAt DateTime @default(now()) | ||
28 | + private Boolean @default(true) | ||
29 | + author User @relation(fields: [authorId], references: [id]) | ||
30 | + authorId String @db.Uuid | ||
31 | + level Level @default(MEDIUM) | ||
32 | + content String | ||
33 | + | ||
34 | + comments Comment[] | ||
35 | +} | ||
36 | + | ||
37 | +model Comment { | ||
38 | + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid @unique | ||
39 | + content String | ||
40 | + createdAt DateTime @default(now()) | ||
41 | + author User @relation(fields: [authorId], references: [id]) | ||
42 | + authorId String @db.Uuid | ||
43 | + post Post @relation(fields: [postId], references: [id]) | ||
44 | + postId String @db.Uuid | ||
45 | +} | ||
46 | + | ||
47 | +enum Level { | ||
48 | + LOW | ||
49 | + MEDIUM | ||
50 | + HIGH | ||
51 | +} | ||
52 | + | ||
53 | +enum Role { | ||
54 | + USER | ||
55 | + ADMIN | ||
56 | +} |
src/app.controller.spec.ts
0 → 100644
1 | +import { Test, TestingModule } from '@nestjs/testing'; | ||
2 | +import { AppController } from './app.controller'; | ||
3 | +import { AppService } from './app.service'; | ||
4 | + | ||
5 | +describe('AppController', () => { | ||
6 | + let appController: AppController; | ||
7 | + | ||
8 | + beforeEach(async () => { | ||
9 | + const app: TestingModule = await Test.createTestingModule({ | ||
10 | + controllers: [AppController], | ||
11 | + providers: [AppService], | ||
12 | + }).compile(); | ||
13 | + | ||
14 | + appController = app.get<AppController>(AppController); | ||
15 | + }); | ||
16 | + | ||
17 | + describe('root', () => { | ||
18 | + it('should return "Hello World!"', () => { | ||
19 | + expect(appController.getHello()).toBe('Hello World!'); | ||
20 | + }); | ||
21 | + }); | ||
22 | +}); |
src/app.controller.ts
0 → 100644
1 | +import { Controller, Get } from '@nestjs/common'; | ||
2 | +import { AppService } from './app.service'; | ||
3 | + | ||
4 | +@Controller() | ||
5 | +export class AppController { | ||
6 | + constructor(private readonly appService: AppService) {} | ||
7 | + | ||
8 | + @Get() | ||
9 | + getHello(): string { | ||
10 | + return this.appService.getHello(); | ||
11 | + } | ||
12 | +} |
src/app.module.ts
0 → 100644
1 | +import { Module } from '@nestjs/common'; | ||
2 | +import { AppController } from './app.controller'; | ||
3 | +import { AppService } from './app.service'; | ||
4 | +import { PrismaModule } from 'nestjs-prisma'; | ||
5 | +import { PrismaService } from './prisma.service'; | ||
6 | +import { AuthModule } from './auth/auth.module'; | ||
7 | +import { Auth } from './auth'; | ||
8 | + | ||
9 | +@Module({ | ||
10 | + imports: [PrismaModule, AuthModule], | ||
11 | + controllers: [AppController], | ||
12 | + providers: [AppService, PrismaService, Auth], | ||
13 | +}) | ||
14 | +export class AppModule {} |
src/app.service.ts
0 → 100644
src/auth.spec.ts
0 → 100644
1 | +import { Test, TestingModule } from '@nestjs/testing'; | ||
2 | +import { Auth } from './auth'; | ||
3 | + | ||
4 | +describe('Auth', () => { | ||
5 | + let provider: Auth; | ||
6 | + | ||
7 | + beforeEach(async () => { | ||
8 | + const module: TestingModule = await Test.createTestingModule({ | ||
9 | + providers: [Auth], | ||
10 | + }).compile(); | ||
11 | + | ||
12 | + provider = module.get<Auth>(Auth); | ||
13 | + }); | ||
14 | + | ||
15 | + it('should be defined', () => { | ||
16 | + expect(provider).toBeDefined(); | ||
17 | + }); | ||
18 | +}); |
src/auth.ts
0 → 100644
src/auth/auth.controller.spec.ts
0 → 100644
1 | +import { Test, TestingModule } from '@nestjs/testing'; | ||
2 | +import { AuthController } from './auth.controller'; | ||
3 | + | ||
4 | +describe('AuthController', () => { | ||
5 | + let controller: AuthController; | ||
6 | + | ||
7 | + beforeEach(async () => { | ||
8 | + const module: TestingModule = await Test.createTestingModule({ | ||
9 | + controllers: [AuthController], | ||
10 | + }).compile(); | ||
11 | + | ||
12 | + controller = module.get<AuthController>(AuthController); | ||
13 | + }); | ||
14 | + | ||
15 | + it('should be defined', () => { | ||
16 | + expect(controller).toBeDefined(); | ||
17 | + }); | ||
18 | +}); |
src/auth/auth.controller.ts
0 → 100644
1 | +import { Body, Controller, Post } from '@nestjs/common'; | ||
2 | +import { AuthService } from './auth.service'; | ||
3 | + | ||
4 | +@Controller('auth') | ||
5 | +export class AuthController { | ||
6 | + constructor(private readonly AuthService: AuthService) {} | ||
7 | + | ||
8 | + @Post('signup') | ||
9 | + signUp(@Body() body) { | ||
10 | + return this.AuthService.createUser(body.name, body.email, body.password); | ||
11 | + } | ||
12 | + | ||
13 | + @Post('signin') | ||
14 | + signIn(@Body() body) { | ||
15 | + return this.AuthService.login(body.email, body.password); | ||
16 | + } | ||
17 | + | ||
18 | + @Post('refresh') | ||
19 | + refresh(@Body() body) { | ||
20 | + return this.AuthService.refreshTokens(body.token); | ||
21 | + } | ||
22 | + | ||
23 | + @Post('validate') | ||
24 | + validate(@Body() body) { | ||
25 | + return this.AuthService.validateUser(body.token); | ||
26 | + } | ||
27 | +} |
src/auth/auth.module.ts
0 → 100644
1 | +import { Module } from '@nestjs/common'; | ||
2 | +import { AuthController } from './auth.controller'; | ||
3 | +import { PrismaModule } from 'nestjs-prisma'; | ||
4 | +import { AuthService } from './auth.service'; | ||
5 | +import { JwtModule, JwtService } from '@nestjs/jwt'; | ||
6 | + | ||
7 | +@Module({ | ||
8 | + imports: [PrismaModule, JwtModule.register({ secret: 'secret' })], | ||
9 | + controllers: [AuthController], | ||
10 | + providers: [AuthService], | ||
11 | +}) | ||
12 | +export class AuthModule {} |
src/auth/auth.service.spec.ts
0 → 100644
1 | +import { Test, TestingModule } from '@nestjs/testing'; | ||
2 | +import { AuthService } from './auth.service'; | ||
3 | + | ||
4 | +describe('AuthService', () => { | ||
5 | + let service: AuthService; | ||
6 | + | ||
7 | + beforeEach(async () => { | ||
8 | + const module: TestingModule = await Test.createTestingModule({ | ||
9 | + providers: [AuthService], | ||
10 | + }).compile(); | ||
11 | + | ||
12 | + service = module.get<AuthService>(AuthService); | ||
13 | + }); | ||
14 | + | ||
15 | + it('should be defined', () => { | ||
16 | + expect(service).toBeDefined(); | ||
17 | + }); | ||
18 | +}); |
src/auth/auth.service.ts
0 → 100644
1 | +import { | ||
2 | + BadRequestException, | ||
3 | + Injectable, | ||
4 | + NotFoundException, | ||
5 | +} from '@nestjs/common'; | ||
6 | +import { PrismaService } from 'nestjs-prisma'; | ||
7 | +import { JwtService } from '@nestjs/jwt'; | ||
8 | +import { User } from '@prisma/client'; | ||
9 | +import * as bcrypt from 'bcrypt'; | ||
10 | + | ||
11 | +@Injectable() | ||
12 | +export class AuthService { | ||
13 | + constructor( | ||
14 | + private readonly prisma: PrismaService, | ||
15 | + private readonly jwtService: JwtService, | ||
16 | + ) {} | ||
17 | + | ||
18 | + async createUser(name: string, email: string, password: string) { | ||
19 | + const hashedPassword = await bcrypt.hash(password, 10); | ||
20 | + const user = await this.prisma.user.create({ | ||
21 | + data: { | ||
22 | + name, | ||
23 | + email, | ||
24 | + password: hashedPassword, | ||
25 | + }, | ||
26 | + }); | ||
27 | + return this.generateTokens(user); | ||
28 | + } | ||
29 | + | ||
30 | + async login(email: string, password: string) { | ||
31 | + const user = await this.prisma.user.findUnique({ where: { email } }); | ||
32 | + | ||
33 | + if (!user) { | ||
34 | + throw new NotFoundException('No user found'); | ||
35 | + } | ||
36 | + | ||
37 | + const passwordMatch = await bcrypt.compare(password, user.password); | ||
38 | + | ||
39 | + if (!passwordMatch) { | ||
40 | + throw new BadRequestException('Wrong password'); | ||
41 | + } | ||
42 | + | ||
43 | + return this.generateTokens(user); | ||
44 | + } | ||
45 | + | ||
46 | + validateUser(userId: string): Promise<User> { | ||
47 | + return this.prisma.user.findUnique({ where: { id: userId } }); | ||
48 | + } | ||
49 | + | ||
50 | + getUserFromToken(token: string) { | ||
51 | + const id = this.jwtService.decode(token)['id']; | ||
52 | + return this.prisma.user.findUnique({ where: { id: id } }); | ||
53 | + } | ||
54 | + | ||
55 | + generateTokens(user: User) { | ||
56 | + const payload = { | ||
57 | + id: user.id, | ||
58 | + name: user.name, | ||
59 | + email: user.email, | ||
60 | + }; | ||
61 | + return { | ||
62 | + access_token: this.jwtService.sign(payload), | ||
63 | + }; | ||
64 | + } | ||
65 | + | ||
66 | + async refreshTokens(token: string) { | ||
67 | + const _user = await this.getUserFromToken(token); | ||
68 | + const user = { | ||
69 | + id: _user.id, | ||
70 | + name: _user.name, | ||
71 | + email: _user.email, | ||
72 | + password: _user.password, | ||
73 | + role: _user.role, | ||
74 | + }; | ||
75 | + return this.generateTokens(user); | ||
76 | + } | ||
77 | + | ||
78 | + async deleteUser(userId: string) { | ||
79 | + return this.prisma.user.delete({ where: { id: userId } }); | ||
80 | + } | ||
81 | + | ||
82 | + async updateUser(token: string, name: string, email: string) { | ||
83 | + return this.prisma.user.update({ | ||
84 | + where: { id: (await this.getUserFromToken(token)).id }, | ||
85 | + data: { | ||
86 | + name, | ||
87 | + email, | ||
88 | + }, | ||
89 | + }); | ||
90 | + } | ||
91 | + | ||
92 | + async updatePassword(token: string, password: string) { | ||
93 | + const hashedPassword = await bcrypt.hash(password, 10); | ||
94 | + return this.prisma.user.update({ | ||
95 | + where: { id: (await this.getUserFromToken(token)).id }, | ||
96 | + data: { | ||
97 | + password: hashedPassword, | ||
98 | + }, | ||
99 | + }); | ||
100 | + } | ||
101 | + | ||
102 | + async AmIAdmin(token: string) { | ||
103 | + const user = await this.prisma.user.findUnique({ | ||
104 | + where: { id: (await this.getUserFromToken(token)).id }, | ||
105 | + }); | ||
106 | + if (user.role === 'ADMIN') { | ||
107 | + return true; | ||
108 | + } else { | ||
109 | + return false; | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + async _changeUserPassword( | ||
114 | + token: string, | ||
115 | + userId: string, | ||
116 | + name?: string, | ||
117 | + email?: string, | ||
118 | + password?: string, | ||
119 | + ) { | ||
120 | + if (this.AmIAdmin(token)) { | ||
121 | + const hashedPassword = await bcrypt.hash(password, 10); | ||
122 | + return this.prisma.user.update({ | ||
123 | + where: { id: userId }, | ||
124 | + data: { | ||
125 | + name: name, | ||
126 | + email: email, | ||
127 | + password: hashedPassword, | ||
128 | + }, | ||
129 | + }); | ||
130 | + } else { | ||
131 | + throw new BadRequestException('You are not admin'); | ||
132 | + } | ||
133 | + } | ||
134 | + | ||
135 | + // return the newly saved user | ||
136 | +} |
src/main.ts
0 → 100644
src/prisma.service.ts
0 → 100644
1 | +import { | ||
2 | + INestApplication, | ||
3 | + Injectable, | ||
4 | + OnModuleInit, | ||
5 | + OnModuleDestroy, | ||
6 | +} from '@nestjs/common'; | ||
7 | +import { PrismaClient } from '@prisma/client'; | ||
8 | + | ||
9 | +@Injectable() | ||
10 | +export class PrismaService extends PrismaClient implements OnModuleInit { | ||
11 | + async onModuleInit() { | ||
12 | + await this.$connect(); | ||
13 | + } | ||
14 | + | ||
15 | + async enableShutdownHooks(app: INestApplication) { | ||
16 | + this.$on('beforeExit', async () => { | ||
17 | + await app.close(); | ||
18 | + }); | ||
19 | + } | ||
20 | +} |
test/app.e2e-spec.ts
0 → 100644
1 | +import { Test, TestingModule } from '@nestjs/testing'; | ||
2 | +import { INestApplication } from '@nestjs/common'; | ||
3 | +import * as request from 'supertest'; | ||
4 | +import { AppModule } from './../src/app.module'; | ||
5 | + | ||
6 | +describe('AppController (e2e)', () => { | ||
7 | + let app: INestApplication; | ||
8 | + | ||
9 | + beforeEach(async () => { | ||
10 | + const moduleFixture: TestingModule = await Test.createTestingModule({ | ||
11 | + imports: [AppModule], | ||
12 | + }).compile(); | ||
13 | + | ||
14 | + app = moduleFixture.createNestApplication(); | ||
15 | + await app.init(); | ||
16 | + }); | ||
17 | + | ||
18 | + it('/ (GET)', () => { | ||
19 | + return request(app.getHttpServer()) | ||
20 | + .get('/') | ||
21 | + .expect(200) | ||
22 | + .expect('Hello World!'); | ||
23 | + }); | ||
24 | +}); |
test/jest-e2e.json
0 → 100644
tsconfig.build.json
0 → 100644
tsconfig.json
0 → 100644
1 | +{ | ||
2 | + "compilerOptions": { | ||
3 | + "module": "commonjs", | ||
4 | + "declaration": true, | ||
5 | + "removeComments": true, | ||
6 | + "emitDecoratorMetadata": true, | ||
7 | + "experimentalDecorators": true, | ||
8 | + "allowSyntheticDefaultImports": true, | ||
9 | + "target": "es2017", | ||
10 | + "sourceMap": true, | ||
11 | + "outDir": "./dist", | ||
12 | + "baseUrl": "./", | ||
13 | + "incremental": true, | ||
14 | + "skipLibCheck": true, | ||
15 | + "strictNullChecks": false, | ||
16 | + "noImplicitAny": false, | ||
17 | + "strictBindCallApply": false, | ||
18 | + "forceConsistentCasingInFileNames": false, | ||
19 | + "noFallthroughCasesInSwitch": false | ||
20 | + } | ||
21 | +} |
-
Please register or login to post a comment