├── src ├── config.ts.example ├── article │ ├── dto │ │ ├── create-comment.ts │ │ ├── index.ts │ │ └── create-article.dto.ts │ ├── comment.entity.ts │ ├── article.interface.ts │ ├── article.entity.ts │ ├── article.module.ts │ ├── article.controller.ts │ └── article.service.ts ├── user │ ├── dto │ │ ├── index.ts │ │ ├── update-user.dto.ts │ │ ├── login-user.dto.ts │ │ └── create-user.dto.ts │ ├── user.interface.ts │ ├── user.decorator.ts │ ├── user.module.ts │ ├── user.entity.ts │ ├── auth.middleware.ts │ ├── user.controller.ts │ └── user.service.ts ├── app.controller.ts ├── profile │ ├── profile.interface.ts │ ├── follows.entity.ts │ ├── profile.module.ts │ ├── profile.controller.ts │ └── profile.service.ts ├── tag │ ├── tag.entity.ts │ ├── tag.service.ts │ ├── tag.controller.ts │ ├── tag.module.ts │ └── tag.controller.spec.ts ├── shared │ ├── base.controller.ts │ └── pipes │ │ └── validation.pipe.ts ├── app.module.ts └── main.ts ├── index.js ├── nestconfig.json ├── project-logo.png ├── nodemon.json ├── dist ├── config.js.map ├── user │ ├── user.interface.js │ ├── user.interface.js.map │ ├── dto │ │ ├── update-user.dto.js.map │ │ ├── update-user.dto.js │ │ ├── index.js.map │ │ ├── login-user.dto.js.map │ │ ├── create-user.dto.js.map │ │ ├── index.js │ │ ├── login-user.dto.js │ │ └── create-user.dto.js │ ├── user.module.js.map │ ├── user.decorator.js │ ├── user.decorator.js.map │ ├── user.entity.js.map │ ├── auth.middleware.js.map │ ├── user.module.js │ ├── user.controller.js.map │ ├── auth.middleware.js │ ├── user.service.js.map │ ├── user.entity.js │ ├── user.controller.js │ └── user.service.js ├── article │ ├── article.interface.js │ ├── article.interface.js.map │ ├── dto │ │ ├── create-comment.js.map │ │ ├── create-article.dto.js.map │ │ ├── index.js.map │ │ ├── create-comment.js │ │ ├── create-article.dto.js │ │ └── index.js │ ├── comment.entity.js.map │ ├── article.module.js.map │ ├── article.entity.js.map │ ├── comment.entity.js │ ├── article.module.js │ ├── article.entity.js │ ├── article.controller.js.map │ ├── article.service.js.map │ ├── article.controller.js │ └── article.service.js ├── profile │ ├── profile.interface.js │ ├── profile.interface.js.map │ ├── follows.entity.js.map │ ├── profile.module.js.map │ ├── profile.controller.js.map │ ├── follows.entity.js │ ├── profile.module.js │ ├── profile.service.js.map │ ├── profile.controller.js │ └── profile.service.js ├── config.js ├── tag │ ├── tag.entity.js.map │ ├── tag.service.js.map │ ├── tag.module.js.map │ ├── tag.controller.js.map │ ├── tag.entity.js │ ├── tag.module.js │ ├── tag.service.js │ └── tag.controller.js ├── app.controller.js.map ├── shared │ ├── base.controller.js.map │ ├── base.controller.js │ └── pipes │ │ ├── validation.pipe.js.map │ │ └── validation.pipe.js ├── app.module.js.map ├── main.js.map ├── app.controller.js ├── main.js └── app.module.js ├── ormconfig.json.example ├── .gitignore ├── jest.json ├── prisma └── .env ├── tsconfig.json ├── package.json └── README.md /src/config.ts.example: -------------------------------------------------------------------------------- 1 | export const SECRET = 'secret-key'; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('ts-node/register'); 2 | require('./src/main'); -------------------------------------------------------------------------------- /nestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "entryFile": "src/main.ts" 4 | } -------------------------------------------------------------------------------- /src/article/dto/create-comment.ts: -------------------------------------------------------------------------------- 1 | export class CreateCommentDto { 2 | readonly body: string; 3 | } -------------------------------------------------------------------------------- /project-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRenzo0801/nestjs-realworld-example-app/HEAD/project-logo.png -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "ignore": ["src/**/*.spec.ts"], 5 | "exec": "node ./index" 6 | } -------------------------------------------------------------------------------- /src/article/dto/index.ts: -------------------------------------------------------------------------------- 1 | export { CreateArticleDto } from './create-article.dto'; 2 | export { CreateCommentDto } from './create-comment'; -------------------------------------------------------------------------------- /dist/config.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;AAAa,QAAA,MAAM,GAAG,iBAAiB,CAAC"} -------------------------------------------------------------------------------- /dist/user/user.interface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=user.interface.js.map -------------------------------------------------------------------------------- /dist/article/article.interface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=article.interface.js.map -------------------------------------------------------------------------------- /dist/profile/profile.interface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | //# sourceMappingURL=profile.interface.js.map -------------------------------------------------------------------------------- /dist/user/user.interface.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"user.interface.js","sourceRoot":"","sources":["../../src/user/user.interface.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.SECRET = 'asfdlajsdfaf234'; 4 | //# sourceMappingURL=config.js.map -------------------------------------------------------------------------------- /dist/article/article.interface.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"article.interface.js","sourceRoot":"","sources":["../../src/article/article.interface.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/profile/profile.interface.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"profile.interface.js","sourceRoot":"","sources":["../../src/profile/profile.interface.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /src/user/dto/index.ts: -------------------------------------------------------------------------------- 1 | export { CreateUserDto } from './create-user.dto'; 2 | export { LoginUserDto } from './login-user.dto'; 3 | export { UpdateUserDto } from './update-user.dto'; -------------------------------------------------------------------------------- /src/user/dto/update-user.dto.ts: -------------------------------------------------------------------------------- 1 | export class UpdateUserDto { 2 | readonly username: string; 3 | readonly email: string; 4 | readonly bio: string; 5 | readonly image: string; 6 | } -------------------------------------------------------------------------------- /dist/user/dto/update-user.dto.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"update-user.dto.js","sourceRoot":"","sources":["../../../src/user/dto/update-user.dto.ts"],"names":[],"mappings":";;AAAA,MAAa,aAAa;CAKzB;AALD,sCAKC"} -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Get, Controller } from '@nestjs/common'; 2 | 3 | @Controller() 4 | export class AppController { 5 | @Get() 6 | root(): string { 7 | return 'Hello World!'; 8 | } 9 | } -------------------------------------------------------------------------------- /src/article/dto/create-article.dto.ts: -------------------------------------------------------------------------------- 1 | export class CreateArticleDto { 2 | readonly title: string; 3 | readonly description: string; 4 | readonly body: string; 5 | readonly tagList: string[]; 6 | } 7 | -------------------------------------------------------------------------------- /dist/article/dto/create-comment.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"create-comment.js","sourceRoot":"","sources":["../../../src/article/dto/create-comment.ts"],"names":[],"mappings":";;AAAA,MAAa,gBAAgB;CAE5B;AAFD,4CAEC"} -------------------------------------------------------------------------------- /dist/article/dto/create-article.dto.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"create-article.dto.js","sourceRoot":"","sources":["../../../src/article/dto/create-article.dto.ts"],"names":[],"mappings":";;AAAA,MAAa,gBAAgB;CAK5B;AALD,4CAKC"} -------------------------------------------------------------------------------- /dist/article/dto/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/article/dto/index.ts"],"names":[],"mappings":";;AAAA,2DAAwD;AAA/C,gDAAA,gBAAgB,CAAA;AACzB,mDAAoD;AAA3C,4CAAA,gBAAgB,CAAA"} -------------------------------------------------------------------------------- /dist/user/dto/update-user.dto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | class UpdateUserDto { 4 | } 5 | exports.UpdateUserDto = UpdateUserDto; 6 | //# sourceMappingURL=update-user.dto.js.map -------------------------------------------------------------------------------- /src/profile/profile.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ProfileData { 2 | username: string; 3 | bio: string; 4 | image?: string; 5 | following?: boolean; 6 | } 7 | 8 | export interface ProfileRO { 9 | profile: ProfileData; 10 | } -------------------------------------------------------------------------------- /src/user/dto/login-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty } from 'class-validator'; 2 | 3 | export class LoginUserDto { 4 | 5 | @IsNotEmpty() 6 | readonly email: string; 7 | 8 | @IsNotEmpty() 9 | readonly password: string; 10 | } -------------------------------------------------------------------------------- /src/user/user.interface.ts: -------------------------------------------------------------------------------- 1 | export interface UserData { 2 | username: string; 3 | email: string; 4 | token: string; 5 | bio: string; 6 | image?: string; 7 | } 8 | 9 | export interface UserRO { 10 | user: UserData; 11 | } -------------------------------------------------------------------------------- /dist/article/dto/create-comment.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | class CreateCommentDto { 4 | } 5 | exports.CreateCommentDto = CreateCommentDto; 6 | //# sourceMappingURL=create-comment.js.map -------------------------------------------------------------------------------- /dist/user/dto/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/user/dto/index.ts"],"names":[],"mappings":";;AAAA,qDAAkD;AAAzC,0CAAA,aAAa,CAAA;AACtB,mDAAgD;AAAvC,wCAAA,YAAY,CAAA;AACrB,qDAAkD;AAAzC,0CAAA,aAAa,CAAA"} -------------------------------------------------------------------------------- /dist/article/dto/create-article.dto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | class CreateArticleDto { 4 | } 5 | exports.CreateArticleDto = CreateArticleDto; 6 | //# sourceMappingURL=create-article.dto.js.map -------------------------------------------------------------------------------- /src/tag/tag.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; 2 | 3 | @Entity('tag') 4 | export class TagEntity { 5 | 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @Column() 10 | tag: string; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /dist/user/dto/login-user.dto.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"login-user.dto.js","sourceRoot":"","sources":["../../../src/user/dto/login-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qDAA6C;AAE7C,MAAa,YAAY;CAOxB;AAJC;IADC,4BAAU,EAAE;;2CACU;AAGvB;IADC,4BAAU,EAAE;;8CACa;AAN5B,oCAOC"} -------------------------------------------------------------------------------- /ormconfig.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "type": "mysql", 3 | "host": "localhost", 4 | "port": 3306, 5 | "username": "your-mysql-username", 6 | "password": "your-mysql-password", 7 | "database": "nestjsrealworld", 8 | "entities": ["src/**/**.entity{.ts,.js}"], 9 | "synchronize": true 10 | } -------------------------------------------------------------------------------- /src/user/dto/create-user.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty } from 'class-validator'; 2 | 3 | export class CreateUserDto { 4 | 5 | @IsNotEmpty() 6 | readonly username: string; 7 | 8 | @IsNotEmpty() 9 | readonly email: string; 10 | 11 | @IsNotEmpty() 12 | readonly password: string; 13 | } -------------------------------------------------------------------------------- /dist/user/dto/create-user.dto.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"create-user.dto.js","sourceRoot":"","sources":["../../../src/user/dto/create-user.dto.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qDAA6C;AAE7C,MAAa,aAAa;CAUzB;AAPC;IADC,4BAAU,EAAE;;+CACa;AAG1B;IADC,4BAAU,EAAE;;4CACU;AAGvB;IADC,4BAAU,EAAE;;+CACa;AAT5B,sCAUC"} -------------------------------------------------------------------------------- /dist/tag/tag.entity.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag.entity.js","sourceRoot":"","sources":["../../src/tag/tag.entity.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qCAAiE;AAGjE,IAAa,SAAS,GAAtB,MAAa,SAAS;CAQrB,CAAA;AALC;IADC,gCAAsB,EAAE;;qCACd;AAGX;IADC,gBAAM,EAAE;;sCACG;AAND,SAAS;IADrB,gBAAM,CAAC,KAAK,CAAC;GACD,SAAS,CAQrB;AARY,8BAAS"} -------------------------------------------------------------------------------- /dist/app.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,2CAAiD;AAGjD,IAAa,aAAa,GAA1B,MAAa,aAAa;IAExB,IAAI;QACF,OAAO,cAAc,CAAC;IACxB,CAAC;CACF,CAAA;AAHC;IADC,YAAG,EAAE;;;;yCAGL;AAJU,aAAa;IADzB,mBAAU,EAAE;GACA,aAAa,CAKzB;AALY,sCAAa"} -------------------------------------------------------------------------------- /src/profile/follows.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; 2 | 3 | @Entity('follows') 4 | export class FollowsEntity { 5 | 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @Column() 10 | followerId: number; 11 | 12 | @Column() 13 | followingId: number; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | .DS_Store 5 | 6 | npm-debug.log* 7 | 8 | # Dependency directory 9 | node_modules 10 | 11 | # Optional npm cache directory 12 | .npm 13 | 14 | #IDEs 15 | .idea 16 | 17 | #config files 18 | ormconfig.json 19 | src/config.ts 20 | 21 | # code testing coverage 22 | coverage 23 | 24 | 25 | dist/* -------------------------------------------------------------------------------- /dist/profile/follows.entity.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"follows.entity.js","sourceRoot":"","sources":["../../src/profile/follows.entity.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qCAAiE;AAGjE,IAAa,aAAa,GAA1B,MAAa,aAAa;CAWzB,CAAA;AARC;IADC,gCAAsB,EAAE;;yCACd;AAGX;IADC,gBAAM,EAAE;;iDACU;AAGnB;IADC,gBAAM,EAAE;;kDACW;AATT,aAAa;IADzB,gBAAM,CAAC,SAAS,CAAC;GACL,aAAa,CAWzB;AAXY,sCAAa"} -------------------------------------------------------------------------------- /dist/article/dto/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var create_article_dto_1 = require("./create-article.dto"); 4 | exports.CreateArticleDto = create_article_dto_1.CreateArticleDto; 5 | var create_comment_1 = require("./create-comment"); 6 | exports.CreateCommentDto = create_comment_1.CreateCommentDto; 7 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /src/article/comment.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; 2 | import { ArticleEntity } from './article.entity'; 3 | 4 | @Entity() 5 | export class Comment { 6 | 7 | @PrimaryGeneratedColumn() 8 | id: number; 9 | 10 | @Column() 11 | body: string; 12 | 13 | @ManyToOne(type => ArticleEntity, article => article.comments) 14 | article: ArticleEntity; 15 | } -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "ts", 4 | "tsx", 5 | "js", 6 | "json" 7 | ], 8 | "transform": { 9 | "^.+\\.tsx?$": "/node_modules/ts-jest/preprocessor.js" 10 | }, 11 | "testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$", 12 | "collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"], 13 | "coverageReporters": ["json", "lcov"] 14 | } -------------------------------------------------------------------------------- /src/shared/base.controller.ts: -------------------------------------------------------------------------------- 1 | import { SECRET } from '../config'; 2 | import * as jwt from 'jsonwebtoken'; 3 | 4 | export class BaseController { 5 | 6 | constructor() {} 7 | 8 | protected getUserIdFromToken(authorization) { 9 | if (!authorization) return null; 10 | 11 | const token = authorization.split(' ')[1]; 12 | const decoded: any = jwt.verify(token, SECRET); 13 | return decoded.id; 14 | } 15 | } -------------------------------------------------------------------------------- /dist/article/comment.entity.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"comment.entity.js","sourceRoot":"","sources":["../../src/article/comment.entity.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qCAA4E;AAC5E,qDAAiD;AAGjD,IAAa,OAAO,GAApB,MAAa,OAAO;CAUnB,CAAA;AAPC;IADC,gCAAsB,EAAE;;mCACd;AAGX;IADC,gBAAM,EAAE;;qCACI;AAGb;IADC,mBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,8BAAa,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;8BACrD,8BAAa;wCAAC;AATZ,OAAO;IADnB,gBAAM,EAAE;GACI,OAAO,CAUnB;AAVY,0BAAO"} -------------------------------------------------------------------------------- /dist/shared/base.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"base.controller.js","sourceRoot":"","sources":["../../src/shared/base.controller.ts"],"names":[],"mappings":";;AAAA,sCAAmC;AACnC,oCAAoC;AAEpC,MAAa,cAAc;IAEzB,gBAAe,CAAC;IAEN,kBAAkB,CAAC,aAAa;QACxC,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QAEhC,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAQ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,eAAM,CAAC,CAAC;QAC/C,OAAO,OAAO,CAAC,EAAE,CAAC;IACpB,CAAC;CACF;AAXD,wCAWC"} -------------------------------------------------------------------------------- /prisma/.env: -------------------------------------------------------------------------------- 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 and SQLite. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="mysql://root:admin@localhost:3306/nestjsrealworld-prisma" -------------------------------------------------------------------------------- /dist/user/dto/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var create_user_dto_1 = require("./create-user.dto"); 4 | exports.CreateUserDto = create_user_dto_1.CreateUserDto; 5 | var login_user_dto_1 = require("./login-user.dto"); 6 | exports.LoginUserDto = login_user_dto_1.LoginUserDto; 7 | var update_user_dto_1 = require("./update-user.dto"); 8 | exports.UpdateUserDto = update_user_dto_1.UpdateUserDto; 9 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/tag/tag.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag.service.js","sourceRoot":"","sources":["../../src/tag/tag.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA2C;AAC3C,6CAAmD;AACnD,qCAAqC;AACrC,6CAAyC;AAGzC,IAAa,UAAU,GAAvB,MAAa,UAAU;IACrB,YAEmB,aAAoC;QAApC,kBAAa,GAAb,aAAa,CAAuB;IACpD,CAAC;IAEE,OAAO;;YACX,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;KAAA;CAEF,CAAA;AAVY,UAAU;IADtB,mBAAU,EAAE;IAGR,WAAA,0BAAgB,CAAC,sBAAS,CAAC,CAAA;qCACI,oBAAU;GAHjC,UAAU,CAUtB;AAVY,gCAAU"} -------------------------------------------------------------------------------- /dist/tag/tag.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag.module.js","sourceRoot":"","sources":["../../src/tag/tag.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,2CAAqF;AACrF,6CAAgD;AAChD,qDAAiD;AACjD,+CAA2C;AAC3C,6CAAyC;AACzC,qDAAiD;AAUjD,IAAa,SAAS,GAAtB,MAAa,SAAS;IACb,SAAS,CAAC,QAA4B;IAC7C,CAAC;CACF,CAAA;AAHY,SAAS;IARrB,eAAM,CAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,sBAAS,CAAC,CAAC,EAAE,wBAAU,CAAC;QAC5D,SAAS,EAAE,CAAC,wBAAU,CAAC;QACvB,WAAW,EAAE;YACX,8BAAa;SACd;QACD,OAAO,EAAE,EAAE;KACZ,CAAC;GACW,SAAS,CAGrB;AAHY,8BAAS"} -------------------------------------------------------------------------------- /src/tag/tag.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable} from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | import { TagEntity } from './tag.entity'; 5 | 6 | @Injectable() 7 | export class TagService { 8 | constructor( 9 | @InjectRepository(TagEntity) 10 | private readonly tagRepository: Repository 11 | ) {} 12 | 13 | async findAll(): Promise { 14 | return await this.tagRepository.find(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /dist/tag/tag.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag.controller.js","sourceRoot":"","sources":["../../src/tag/tag.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,2CAAgD;AAGhD,+CAA2C;AAE3C,6CAGyB;AAKzB,IAAa,aAAa,GAA1B,MAAa,aAAa;IAExB,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAGjD,OAAO;;YACX,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACzC,CAAC;KAAA;CAEF,CAAA;AAJC;IADC,YAAG,EAAE;;;;4CAGL;AAPU,aAAa;IAHzB,uBAAa,EAAE;IACf,oBAAU,CAAC,MAAM,CAAC;IAClB,mBAAU,CAAC,MAAM,CAAC;qCAGwB,wBAAU;GAFxC,aAAa,CASzB;AATY,sCAAa"} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "noImplicitAny": false, 6 | "removeComments": true, 7 | "noLib": false, 8 | "lib": ["es2017"], 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es6", 12 | "sourceMap": true, 13 | "allowJs": true, 14 | "outDir": "./dist" 15 | }, 16 | "include": [ 17 | "src/**/*" 18 | ], 19 | "exclude": [ 20 | "node_modules", 21 | "**/*.spec.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /dist/app.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,2CAAwC;AACxC,qDAAiD;AACjD,6DAAyD;AACzD,oDAAgD;AAChD,6CAAgD;AAChD,qCAAqC;AACrC,6DAAyD;AACzD,iDAA6C;AAe7C,IAAa,iBAAiB,GAA9B,MAAa,iBAAiB;IAC5B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;CACxD,CAAA;AAFY,iBAAiB;IAb7B,eAAM,CAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,OAAO,EAAE;YACvB,8BAAa;YACb,wBAAU;YACV,8BAAa;YACb,sBAAS;SACV;QACD,WAAW,EAAE;YACX,8BAAa;SACd;QACD,SAAS,EAAE,EAAE;KACd,CAAC;qCAEyC,oBAAU;GADxC,iBAAiB,CAE7B;AAFY,8CAAiB"} -------------------------------------------------------------------------------- /src/tag/tag.controller.ts: -------------------------------------------------------------------------------- 1 | import {Get, Controller } from '@nestjs/common'; 2 | 3 | import { TagEntity } from './tag.entity'; 4 | import { TagService } from './tag.service'; 5 | 6 | import { 7 | ApiBearerAuth, ApiTags, 8 | } from '@nestjs/swagger'; 9 | 10 | @ApiBearerAuth() 11 | @ApiTags('tags') 12 | @Controller('tags') 13 | export class TagController { 14 | 15 | constructor(private readonly tagService: TagService) {} 16 | 17 | @Get() 18 | async findAll(): Promise { 19 | return await this.tagService.findAll(); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /dist/shared/base.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const config_1 = require("../config"); 4 | const jwt = require("jsonwebtoken"); 5 | class BaseController { 6 | constructor() { } 7 | getUserIdFromToken(authorization) { 8 | if (!authorization) 9 | return null; 10 | const token = authorization.split(' ')[1]; 11 | const decoded = jwt.verify(token, config_1.SECRET); 12 | return decoded.id; 13 | } 14 | } 15 | exports.BaseController = BaseController; 16 | //# sourceMappingURL=base.controller.js.map -------------------------------------------------------------------------------- /src/tag/tag.module.ts: -------------------------------------------------------------------------------- 1 | import {MiddlewareConsumer, Module, NestModule, RequestMethod} from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { UserModule } from '../user/user.module'; 4 | import { TagService } from './tag.service'; 5 | import { TagEntity } from './tag.entity'; 6 | import { TagController } from './tag.controller'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([TagEntity]), UserModule], 10 | providers: [TagService], 11 | controllers: [ 12 | TagController 13 | ], 14 | exports: [] 15 | }) 16 | export class TagModule implements NestModule { 17 | public configure(consumer: MiddlewareConsumer) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dist/profile/profile.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"profile.module.js","sourceRoot":"","sources":["../../src/profile/profile.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,2CAAqF;AACrF,6DAAyD;AACzD,6CAAgD;AAChD,uDAAmD;AACnD,qDAAiD;AACjD,qDAA+C;AAC/C,qDAA+C;AAC/C,6DAAuD;AAUvD,IAAa,aAAa,GAA1B,MAAa,aAAa;IACjB,SAAS,CAAC,QAA4B;QAC3C,QAAQ;aACL,KAAK,CAAC,gCAAc,CAAC;aACrB,SAAS,CAAC,EAAC,IAAI,EAAE,2BAA2B,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAC,CAAC,CAAC;IAC/E,CAAC;CACF,CAAA;AANY,aAAa;IARzB,eAAM,CAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,wBAAU,EAAE,8BAAa,CAAC,CAAC,EAAE,wBAAU,CAAC;QAC5E,SAAS,EAAE,CAAC,gCAAc,CAAC;QAC3B,WAAW,EAAE;YACX,sCAAiB;SAClB;QACD,OAAO,EAAE,EAAE;KACZ,CAAC;GACW,aAAa,CAMzB;AANY,sCAAa"} -------------------------------------------------------------------------------- /dist/user/user.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"user.module.js","sourceRoot":"","sources":["../../src/user/user.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,2CAAqF;AACrF,uDAAmD;AACnD,6CAAgD;AAChD,+CAA2C;AAC3C,iDAA6C;AAC7C,uDAAmD;AAUnD,IAAa,UAAU,GAAvB,MAAa,UAAU;IACd,SAAS,CAAC,QAA4B;QAC3C,QAAQ;aACL,KAAK,CAAC,gCAAc,CAAC;aACrB,SAAS,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAC,CAAC,CAAC;IACrG,CAAC;CACF,CAAA;AANY,UAAU;IARtB,eAAM,CAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,wBAAU,CAAC,CAAC,CAAC;QACjD,SAAS,EAAE,CAAC,0BAAW,CAAC;QACxB,WAAW,EAAE;YACX,gCAAc;SACf;QACD,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAMtB;AANY,gCAAU"} -------------------------------------------------------------------------------- /dist/user/user.decorator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const common_1 = require("@nestjs/common"); 4 | const config_1 = require("../config"); 5 | const jwt = require("jsonwebtoken"); 6 | exports.User = common_1.createParamDecorator((data, req) => { 7 | if (!!req.user) { 8 | return !!data ? req.user[data] : req.user; 9 | } 10 | const token = req.headers.authorization ? req.headers.authorization.split(' ') : null; 11 | if (token && token[1]) { 12 | const decoded = jwt.verify(token[1], config_1.SECRET); 13 | return !!data ? decoded[data] : decoded.user; 14 | } 15 | }); 16 | //# sourceMappingURL=user.decorator.js.map -------------------------------------------------------------------------------- /dist/main.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,uCAA2C;AAC3C,6CAAiD;AACjD,6CAAiE;AAEjE,SAAe,SAAS;;QACtB,MAAM,UAAU,GAAG,EAAC,IAAI,EAAE,IAAI,EAAC,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,8BAAiB,EAAE,UAAU,CAAC,CAAC;QACpE,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAE3B,MAAM,OAAO,GAAG,IAAI,yBAAe,EAAE;aAClC,QAAQ,CAAC,8BAA8B,CAAC;aACxC,cAAc,CAAC,+BAA+B,CAAC;aAC/C,UAAU,CAAC,KAAK,CAAC;aACjB,WAAW,CAAC,KAAK,CAAC;aAClB,aAAa,EAAE;aACf,KAAK,EAAE,CAAC;QACX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC5D,uBAAa,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAE5C,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;CAAA;AACD,SAAS,EAAE,CAAC"} -------------------------------------------------------------------------------- /src/article/article.interface.ts: -------------------------------------------------------------------------------- 1 | import { UserData } from '../user/user.interface'; 2 | import { ArticleEntity } from './article.entity'; 3 | interface Comment { 4 | body: string; 5 | } 6 | 7 | interface ArticleData { 8 | slug: string; 9 | title: string; 10 | description: string; 11 | body?: string; 12 | tagList?: string[]; 13 | createdAt?: Date 14 | updatedAt?: Date 15 | favorited?: boolean; 16 | favoritesCount?: number; 17 | author?: UserData; 18 | } 19 | 20 | export interface CommentsRO { 21 | comments: Comment[]; 22 | } 23 | 24 | export interface ArticleRO { 25 | article: ArticleEntity; 26 | } 27 | 28 | export interface ArticlesRO { 29 | articles: ArticleEntity[]; 30 | articlesCount: number; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /dist/user/user.decorator.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"user.decorator.js","sourceRoot":"","sources":["../../src/user/user.decorator.ts"],"names":[],"mappings":";;AAAA,2CAAsD;AACtD,sCAAmC;AACnC,oCAAoC;AAEvB,QAAA,IAAI,GAAG,6BAAoB,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAErD,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE;QACd,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;KAC3C;IAGD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAE,GAAG,CAAC,OAAO,CAAC,aAAwB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClG,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE;QACrB,MAAM,OAAO,GAAQ,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,eAAM,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;KAC9C;AAEH,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { ArticleModule } from './article/article.module'; 4 | import { UserModule } from './user/user.module'; 5 | import { TypeOrmModule } from '@nestjs/typeorm'; 6 | import { Connection } from 'typeorm'; 7 | import { ProfileModule } from './profile/profile.module'; 8 | import { TagModule } from './tag/tag.module'; 9 | 10 | @Module({ 11 | imports: [ 12 | TypeOrmModule.forRoot(), 13 | ArticleModule, 14 | UserModule, 15 | ProfileModule, 16 | TagModule 17 | ], 18 | controllers: [ 19 | AppController 20 | ], 21 | providers: [] 22 | }) 23 | export class ApplicationModule { 24 | constructor(private readonly connection: Connection) {} 25 | } 26 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { ApplicationModule } from './app.module'; 3 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 4 | 5 | async function bootstrap() { 6 | const appOptions = {cors: true}; 7 | const app = await NestFactory.create(ApplicationModule, appOptions); 8 | app.setGlobalPrefix('api'); 9 | 10 | const options = new DocumentBuilder() 11 | .setTitle('NestJS Realworld Example App') 12 | .setDescription('The Realworld API description') 13 | .setVersion('1.0') 14 | .setBasePath('api') 15 | .addBearerAuth() 16 | .build(); 17 | const document = SwaggerModule.createDocument(app, options); 18 | SwaggerModule.setup('/docs', app, document); 19 | 20 | await app.listen(3000); 21 | } 22 | bootstrap(); -------------------------------------------------------------------------------- /src/user/user.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from '@nestjs/common'; 2 | import { SECRET } from '../config'; 3 | import * as jwt from 'jsonwebtoken'; 4 | 5 | export const User = createParamDecorator((data: any, ctx: ExecutionContext) => { 6 | const req = ctx.switchToHttp().getRequest(); 7 | // if route is protected, there is a user set in auth.middleware 8 | if (!!req.user) { 9 | return !!data ? req.user[data] : req.user; 10 | } 11 | 12 | // in case a route is not protected, we still want to get the optional auth user from jwt 13 | const token = req.headers.authorization ? (req.headers.authorization as string).split(' ') : null; 14 | if (token && token[1]) { 15 | const decoded: any = jwt.verify(token[1], SECRET); 16 | return !!data ? decoded[data] : decoded.user; 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import {MiddlewareConsumer, Module, NestModule, RequestMethod} from '@nestjs/common'; 2 | import { UserController } from './user.controller'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { UserEntity } from './user.entity'; 5 | import { UserService } from './user.service'; 6 | import { AuthMiddleware } from './auth.middleware'; 7 | 8 | @Module({ 9 | imports: [TypeOrmModule.forFeature([UserEntity])], 10 | providers: [UserService], 11 | controllers: [ 12 | UserController 13 | ], 14 | exports: [UserService] 15 | }) 16 | export class UserModule implements NestModule { 17 | public configure(consumer: MiddlewareConsumer) { 18 | consumer 19 | .apply(AuthMiddleware) 20 | .forRoutes({path: 'user', method: RequestMethod.GET}, {path: 'user', method: RequestMethod.PUT}); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dist/user/user.entity.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"user.entity.js","sourceRoot":"","sources":["../../src/user/user.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,qCAA+G;AAC/G,qDAA0C;AAC1C,iCAAiC;AACjC,8DAA0D;AAG1D,IAAa,UAAU,GAAvB,MAAa,UAAU;IAsBf,YAAY;;YAChB,IAAI,CAAC,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC;KAAA;CAQF,CAAA;AA7BC;IADC,gCAAsB,EAAE;;sCACd;AAGX;IADC,gBAAM,EAAE;;4CACQ;AAIjB;IAFC,gBAAM,EAAE;IACR,yBAAO,EAAE;;yCACI;AAGd;IADC,gBAAM,CAAC,EAAC,OAAO,EAAE,EAAE,EAAC,CAAC;;uCACV;AAGZ;IADC,gBAAM,CAAC,EAAC,OAAO,EAAE,EAAE,EAAC,CAAC;;yCACR;AAGd;IADC,gBAAM,EAAE;;4CACQ;AAGjB;IADC,sBAAY,EAAE;;;;8CAGd;AAID;IAFC,oBAAU,CAAC,IAAI,CAAC,EAAE,CAAC,8BAAa,CAAC;IACjC,mBAAS,EAAE;;6CACe;AAG3B;IADC,mBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,8BAAa,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;;4CAClC;AA/Bf,UAAU;IADtB,gBAAM,CAAC,MAAM,CAAC;GACF,UAAU,CAgCtB;AAhCY,gCAAU"} -------------------------------------------------------------------------------- /src/profile/profile.module.ts: -------------------------------------------------------------------------------- 1 | import {MiddlewareConsumer, Module, NestModule, RequestMethod} from '@nestjs/common'; 2 | import { ProfileController } from './profile.controller'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { ProfileService } from './profile.service'; 5 | import { UserModule } from '../user/user.module'; 6 | import {UserEntity} from "../user/user.entity"; 7 | import {FollowsEntity} from "./follows.entity"; 8 | import {AuthMiddleware} from "../user/auth.middleware"; 9 | 10 | @Module({ 11 | imports: [TypeOrmModule.forFeature([UserEntity, FollowsEntity]), UserModule], 12 | providers: [ProfileService], 13 | controllers: [ 14 | ProfileController 15 | ], 16 | exports: [] 17 | }) 18 | export class ProfileModule implements NestModule { 19 | public configure(consumer: MiddlewareConsumer) { 20 | consumer 21 | .apply(AuthMiddleware) 22 | .forRoutes({path: 'profiles/:username/follow', method: RequestMethod.ALL}); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/user/user.entity.ts: -------------------------------------------------------------------------------- 1 | import {Entity, PrimaryGeneratedColumn, Column, BeforeInsert, JoinTable, ManyToMany, OneToMany} from 'typeorm'; 2 | import { IsEmail } from 'class-validator'; 3 | import * as argon2 from 'argon2'; 4 | import { ArticleEntity } from '../article/article.entity'; 5 | 6 | @Entity('user') 7 | export class UserEntity { 8 | 9 | @PrimaryGeneratedColumn() 10 | id: number; 11 | 12 | @Column() 13 | username: string; 14 | 15 | @Column() 16 | @IsEmail() 17 | email: string; 18 | 19 | @Column({default: ''}) 20 | bio: string; 21 | 22 | @Column({default: ''}) 23 | image: string; 24 | 25 | @Column() 26 | password: string; 27 | 28 | @BeforeInsert() 29 | async hashPassword() { 30 | this.password = await argon2.hash(this.password); 31 | } 32 | 33 | @ManyToMany(type => ArticleEntity) 34 | @JoinTable() 35 | favorites: ArticleEntity[]; 36 | 37 | @OneToMany(type => ArticleEntity, article => article.author) 38 | articles: ArticleEntity[]; 39 | } 40 | -------------------------------------------------------------------------------- /dist/user/auth.middleware.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"auth.middleware.js","sourceRoot":"","sources":["../../src/user/auth.middleware.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAAA,6EAAyE;AACzE,2CAAwE;AAGxE,oCAAoC;AACpC,sCAAmC;AACnC,iDAA6C;AAG7C,IAAa,cAAc,GAA3B,MAAa,cAAc;IACzB,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAEnD,GAAG,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;;YACvD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;YAC9C,IAAI,WAAW,IAAK,WAAsB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE;gBACxD,MAAM,KAAK,GAAI,WAAsB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpD,MAAM,OAAO,GAAQ,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,eAAM,CAAC,CAAC;gBAC/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAEzD,IAAI,CAAC,IAAI,EAAE;oBACT,MAAM,IAAI,8BAAa,CAAC,iBAAiB,EAAE,mBAAU,CAAC,YAAY,CAAC,CAAC;iBACrE;gBAED,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;gBACrB,IAAI,EAAE,CAAC;aAER;iBAAM;gBACL,MAAM,IAAI,8BAAa,CAAC,iBAAiB,EAAE,mBAAU,CAAC,YAAY,CAAC,CAAC;aACrE;QACH,CAAC;KAAA;CACF,CAAA;AArBY,cAAc;IAD1B,mBAAU,EAAE;qCAE+B,0BAAW;GAD1C,cAAc,CAqB1B;AArBY,wCAAc"} -------------------------------------------------------------------------------- /dist/article/article.module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"article.module.js","sourceRoot":"","sources":["../../src/article/article.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,2CAAuF;AACvF,6DAAyD;AACzD,6CAAgD;AAChD,qDAAiD;AACjD,qDAA2C;AAC3C,qDAAiD;AACjD,8DAA0D;AAC1D,uDAAmD;AACnD,6DAAyD;AACzD,qDAAiD;AASjD,IAAa,aAAa,GAA1B,MAAa,aAAa;IACjB,SAAS,CAAC,QAA4B;QAC3C,QAAQ;aACL,KAAK,CAAC,gCAAc,CAAC;aACrB,SAAS,CACR,EAAC,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAC,EAClD,EAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,sBAAa,CAAC,IAAI,EAAC,EAC9C,EAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,sBAAa,CAAC,MAAM,EAAC,EACtD,EAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,sBAAa,CAAC,GAAG,EAAC,EACnD,EAAC,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,sBAAa,CAAC,IAAI,EAAC,EAC7D,EAAC,IAAI,EAAE,6BAA6B,EAAE,MAAM,EAAE,sBAAa,CAAC,MAAM,EAAC,EACnE,EAAC,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,sBAAa,CAAC,IAAI,EAAC,EAC7D,EAAC,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,sBAAa,CAAC,MAAM,EAAC,CAAC,CAAC;IACvE,CAAC;CACF,CAAA;AAdY,aAAa;IAPzB,eAAM,CAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC,UAAU,CAAC,CAAC,8BAAa,EAAE,wBAAO,EAAE,wBAAU,EAAE,8BAAa,CAAC,CAAC,EAAE,wBAAU,CAAC;QACpG,SAAS,EAAE,CAAC,gCAAc,CAAC;QAC3B,WAAW,EAAE;YACX,sCAAiB;SAClB;KACF,CAAC;GACW,aAAa,CAczB;AAdY,sCAAa"} -------------------------------------------------------------------------------- /dist/profile/profile.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"profile.controller.js","sourceRoot":"","sources":["../../src/profile/profile.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAsE;AAEtE,uDAAmD;AAEnD,2DAA8C;AAE9C,6CAGyB;AAKzB,IAAa,iBAAiB,GAA9B,MAAa,iBAAiB;IAE5B,YAA6B,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAAG,CAAC;IAGzD,UAAU,CAAa,MAAc,EAAqB,QAAgB;;YAC9E,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACjE,CAAC;KAAA;IAGK,MAAM,CAAgB,KAAa,EAAqB,QAAgB;;YAC5E,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC3D,CAAC;KAAA;IAGK,QAAQ,CAAa,MAAc,EAAsB,QAAgB;;YAC7E,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;KAAA;CAEF,CAAA;AAdC;IADC,YAAG,CAAC,WAAW,CAAC;IACC,WAAA,qBAAI,CAAC,IAAI,CAAC,CAAA,EAAkB,WAAA,cAAK,CAAC,UAAU,CAAC,CAAA;;;;mDAE9D;AAGD;IADC,aAAI,CAAC,kBAAkB,CAAC;IACX,WAAA,qBAAI,CAAC,OAAO,CAAC,CAAA,EAAiB,WAAA,cAAK,CAAC,UAAU,CAAC,CAAA;;;;+CAE5D;AAGD;IADC,eAAM,CAAC,kBAAkB,CAAC;IACX,WAAA,qBAAI,CAAC,IAAI,CAAC,CAAA,EAAmB,WAAA,cAAK,CAAC,UAAU,CAAC,CAAA;;;;iDAE7D;AAjBU,iBAAiB;IAH7B,uBAAa,EAAE;IACf,oBAAU,CAAC,UAAU,CAAC;IACtB,mBAAU,CAAC,UAAU,CAAC;qCAGwB,gCAAc;GAFhD,iBAAiB,CAmB7B;AAnBY,8CAAiB"} -------------------------------------------------------------------------------- /dist/article/article.entity.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"article.entity.js","sourceRoot":"","sources":["../../src/article/article.entity.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,qCAAwI;AACxI,qDAAiD;AACjD,qDAA2C;AAG3C,IAAa,aAAa,GAA1B,MAAa,aAAa;IAwBxB,eAAe;QACb,IAAI,CAAC,OAAO,GAAG,IAAI,IAAI,CAAC;IAC1B,CAAC;CAcF,CAAA;AArCC;IADC,gCAAsB,EAAE;;yCACd;AAGX;IADC,gBAAM,EAAE;;2CACI;AAGb;IADC,gBAAM,EAAE;;4CACK;AAGd;IADC,gBAAM,CAAC,EAAC,OAAO,EAAE,EAAE,EAAC,CAAC;;kDACF;AAGpB;IADC,gBAAM,CAAC,EAAC,OAAO,EAAE,EAAE,EAAC,CAAC;;2CACT;AAGb;IADC,gBAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAC,CAAC;8BACxD,IAAI;8CAAC;AAGd;IADC,gBAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAC,CAAC;8BACxD,IAAI;8CAAC;AAGd;IADC,sBAAY,EAAE;;;;oDAGd;AAGD;IADC,gBAAM,CAAC,cAAc,CAAC;;8CACL;AAGlB;IADC,mBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,wBAAU,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;8BAC7C,wBAAU;6CAAC;AAInB;IAFC,mBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,wBAAO,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;IACrE,oBAAU,EAAE;;+CACO;AAGpB;IADC,gBAAM,CAAC,EAAC,OAAO,EAAE,CAAC,EAAC,CAAC;;oDACC;AAvCX,aAAa;IADzB,gBAAM,CAAC,SAAS,CAAC;GACL,aAAa,CAwCzB;AAxCY,sCAAa"} -------------------------------------------------------------------------------- /src/article/article.entity.ts: -------------------------------------------------------------------------------- 1 | import { Entity, PrimaryGeneratedColumn, Column, OneToOne, ManyToOne, OneToMany, JoinColumn, AfterUpdate, BeforeUpdate } from 'typeorm'; 2 | import { UserEntity } from '../user/user.entity'; 3 | import { Comment } from './comment.entity'; 4 | 5 | @Entity('article') 6 | export class ArticleEntity { 7 | 8 | @PrimaryGeneratedColumn() 9 | id: number; 10 | 11 | @Column() 12 | slug: string; 13 | 14 | @Column() 15 | title: string; 16 | 17 | @Column({default: ''}) 18 | description: string; 19 | 20 | @Column({default: ''}) 21 | body: string; 22 | 23 | @Column({ type: 'timestamp', default: () => "CURRENT_TIMESTAMP"}) 24 | created: Date; 25 | 26 | @Column({ type: 'timestamp', default: () => "CURRENT_TIMESTAMP"}) 27 | updated: Date; 28 | 29 | @BeforeUpdate() 30 | updateTimestamp() { 31 | this.updated = new Date; 32 | } 33 | 34 | @Column('simple-array') 35 | tagList: string[]; 36 | 37 | @ManyToOne(type => UserEntity, user => user.articles) 38 | author: UserEntity; 39 | 40 | @OneToMany(type => Comment, comment => comment.article, {eager: true}) 41 | @JoinColumn() 42 | comments: Comment[]; 43 | 44 | @Column({default: 0}) 45 | favoriteCount: number; 46 | } -------------------------------------------------------------------------------- /src/profile/profile.controller.ts: -------------------------------------------------------------------------------- 1 | import { Get, Post, Delete, Param, Controller } from '@nestjs/common'; 2 | import { Request } from 'express'; 3 | import { ProfileService } from './profile.service'; 4 | import { ProfileRO } from './profile.interface'; 5 | import { User } from '../user/user.decorator'; 6 | 7 | import { 8 | ApiBearerAuth, ApiTags, 9 | } from '@nestjs/swagger'; 10 | 11 | @ApiBearerAuth() 12 | @ApiTags('profiles') 13 | @Controller('profiles') 14 | export class ProfileController { 15 | 16 | constructor(private readonly profileService: ProfileService) {} 17 | 18 | @Get(':username') 19 | async getProfile(@User('id') userId: number, @Param('username') username: string): Promise { 20 | return await this.profileService.findProfile(userId, username); 21 | } 22 | 23 | @Post(':username/follow') 24 | async follow(@User('email') email: string, @Param('username') username: string): Promise { 25 | return await this.profileService.follow(email, username); 26 | } 27 | 28 | @Delete(':username/follow') 29 | async unFollow(@User('id') userId: number, @Param('username') username: string): Promise { 30 | return await this.profileService.unFollow(userId, username); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/user/auth.middleware.ts: -------------------------------------------------------------------------------- 1 | import { HttpException } from '@nestjs/common/exceptions/http.exception'; 2 | import { NestMiddleware, HttpStatus, Injectable } from '@nestjs/common'; 3 | import { ExtractJwt, Strategy } from 'passport-jwt'; 4 | import { Request, Response, NextFunction } from 'express'; 5 | import * as jwt from 'jsonwebtoken'; 6 | import { SECRET } from '../config'; 7 | import { UserService } from './user.service'; 8 | 9 | @Injectable() 10 | export class AuthMiddleware implements NestMiddleware { 11 | constructor(private readonly userService: UserService) {} 12 | 13 | async use(req: Request, res: Response, next: NextFunction) { 14 | const authHeaders = req.headers.authorization; 15 | if (authHeaders && (authHeaders as string).split(' ')[1]) { 16 | const token = (authHeaders as string).split(' ')[1]; 17 | const decoded: any = jwt.verify(token, SECRET); 18 | const user = await this.userService.findById(decoded.id); 19 | 20 | if (!user) { 21 | throw new HttpException('User not found.', HttpStatus.UNAUTHORIZED); 22 | } 23 | 24 | req.user = user.user; 25 | next(); 26 | 27 | } else { 28 | throw new HttpException('Not authorized.', HttpStatus.UNAUTHORIZED); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dist/user/dto/login-user.dto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const class_validator_1 = require("class-validator"); 13 | class LoginUserDto { 14 | } 15 | __decorate([ 16 | class_validator_1.IsNotEmpty(), 17 | __metadata("design:type", String) 18 | ], LoginUserDto.prototype, "email", void 0); 19 | __decorate([ 20 | class_validator_1.IsNotEmpty(), 21 | __metadata("design:type", String) 22 | ], LoginUserDto.prototype, "password", void 0); 23 | exports.LoginUserDto = LoginUserDto; 24 | //# sourceMappingURL=login-user.dto.js.map -------------------------------------------------------------------------------- /dist/tag/tag.entity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const typeorm_1 = require("typeorm"); 13 | let TagEntity = class TagEntity { 14 | }; 15 | __decorate([ 16 | typeorm_1.PrimaryGeneratedColumn(), 17 | __metadata("design:type", Number) 18 | ], TagEntity.prototype, "id", void 0); 19 | __decorate([ 20 | typeorm_1.Column(), 21 | __metadata("design:type", String) 22 | ], TagEntity.prototype, "tag", void 0); 23 | TagEntity = __decorate([ 24 | typeorm_1.Entity('tag') 25 | ], TagEntity); 26 | exports.TagEntity = TagEntity; 27 | //# sourceMappingURL=tag.entity.js.map -------------------------------------------------------------------------------- /dist/app.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const common_1 = require("@nestjs/common"); 13 | let AppController = class AppController { 14 | root() { 15 | return 'Hello World!'; 16 | } 17 | }; 18 | __decorate([ 19 | common_1.Get(), 20 | __metadata("design:type", Function), 21 | __metadata("design:paramtypes", []), 22 | __metadata("design:returntype", String) 23 | ], AppController.prototype, "root", null); 24 | AppController = __decorate([ 25 | common_1.Controller() 26 | ], AppController); 27 | exports.AppController = AppController; 28 | //# sourceMappingURL=app.controller.js.map -------------------------------------------------------------------------------- /dist/shared/pipes/validation.pipe.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"validation.pipe.js","sourceRoot":"","sources":["../../../src/shared/pipes/validation.pipe.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAA4G;AAC5G,qDAA2C;AAC3C,yDAAiD;AACjD,6EAAuE;AAGvE,IAAa,cAAc,GAA3B,MAAa,cAAc;IACnB,SAAS,CAAC,KAAK,EAAE,QAA0B;;YAE/C,IAAI,CAAC,KAAK,EAAE;gBACV,MAAM,IAAI,4BAAmB,CAAC,mBAAmB,CAAC,CAAC;aACpD;YAED,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;YAC9B,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;gBAC3C,OAAO,KAAK,CAAC;aACd;YACD,MAAM,MAAM,GAAG,gCAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC7C,MAAM,MAAM,GAAG,MAAM,0BAAQ,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;gBACrB,MAAM,IAAI,8BAAa,CAAC,EAAC,OAAO,EAAE,8BAA8B,EAAE,MAAM,EAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAC,EAAE,mBAAU,CAAC,WAAW,CAAC,CAAC;aAC9H;YACD,OAAO,KAAK,CAAC;QACf,CAAC;KAAA;IAEO,UAAU,CAAC,MAAM;QACvB,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;YAClB,IAAI,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC;YACvB,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;gBAClD,MAAM,CAAC,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,UAAU,CAAC,QAAQ;QACzB,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACvD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC;IAClD,CAAC;CACF,CAAA;AAlCY,cAAc;IAD1B,mBAAU,EAAE;GACA,cAAc,CAkC1B;AAlCY,wCAAc"} -------------------------------------------------------------------------------- /src/tag/tag.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from '@nestjs/testing'; 2 | import { TagController } from './tag.controller'; 3 | import { TagService } from './tag.service'; 4 | import {TypeOrmModule} from "@nestjs/typeorm"; 5 | import {TagEntity} from "./tag.entity"; 6 | 7 | describe('TagController', () => { 8 | let tagController: TagController; 9 | let tagService: TagService; 10 | 11 | beforeEach(async () => { 12 | const module = await Test.createTestingModule({ 13 | imports: [TypeOrmModule.forRoot(), TypeOrmModule.forFeature([TagEntity])], 14 | controllers: [TagController], 15 | providers: [TagService], 16 | }).compile(); 17 | 18 | tagService = module.get(TagService); 19 | tagController = module.get(TagController); 20 | }); 21 | 22 | describe('findAll', () => { 23 | it('should return an array of tags', async () => { 24 | const tags : TagEntity[] = []; 25 | const createTag = (id, name) => { 26 | const tag = new TagEntity(); 27 | tag.id = id; 28 | tag.tag = name; 29 | return tag; 30 | } 31 | tags.push(createTag(1, 'angularjs')); 32 | tags.push(createTag(2, 'reactjs')); 33 | 34 | jest.spyOn(tagService, 'findAll').mockImplementation(() => Promise.resolve(tags)); 35 | 36 | const findAllResult = await tagController.findAll(); 37 | expect(findAllResult).toBe(tags); 38 | }); 39 | }); 40 | }); -------------------------------------------------------------------------------- /dist/user/dto/create-user.dto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const class_validator_1 = require("class-validator"); 13 | class CreateUserDto { 14 | } 15 | __decorate([ 16 | class_validator_1.IsNotEmpty(), 17 | __metadata("design:type", String) 18 | ], CreateUserDto.prototype, "username", void 0); 19 | __decorate([ 20 | class_validator_1.IsNotEmpty(), 21 | __metadata("design:type", String) 22 | ], CreateUserDto.prototype, "email", void 0); 23 | __decorate([ 24 | class_validator_1.IsNotEmpty(), 25 | __metadata("design:type", String) 26 | ], CreateUserDto.prototype, "password", void 0); 27 | exports.CreateUserDto = CreateUserDto; 28 | //# sourceMappingURL=create-user.dto.js.map -------------------------------------------------------------------------------- /src/shared/pipes/validation.pipe.ts: -------------------------------------------------------------------------------- 1 | import {PipeTransform, ArgumentMetadata, BadRequestException, HttpStatus, Injectable} from '@nestjs/common'; 2 | import { validate } from 'class-validator'; 3 | import { plainToClass } from 'class-transformer'; 4 | import { HttpException } from '@nestjs/common/exceptions/http.exception'; 5 | 6 | @Injectable() 7 | export class ValidationPipe implements PipeTransform { 8 | async transform(value, metadata: ArgumentMetadata) { 9 | 10 | if (!value) { 11 | throw new BadRequestException('No data submitted'); 12 | } 13 | 14 | const { metatype } = metadata; 15 | if (!metatype || !this.toValidate(metatype)) { 16 | return value; 17 | } 18 | const object = plainToClass(metatype, value); 19 | const errors = await validate(object); 20 | if (errors.length > 0) { 21 | throw new HttpException({message: 'Input data validation failed', errors: this.buildError(errors)}, HttpStatus.BAD_REQUEST); 22 | } 23 | return value; 24 | } 25 | 26 | private buildError(errors) { 27 | const result = {}; 28 | errors.forEach(el => { 29 | let prop = el.property; 30 | Object.entries(el.constraints).forEach(constraint => { 31 | result[prop + constraint[0]] = `${constraint[1]}`; 32 | }); 33 | }); 34 | return result; 35 | } 36 | 37 | private toValidate(metatype): boolean { 38 | const types = [String, Boolean, Number, Array, Object]; 39 | return !types.find((type) => metatype === type); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dist/tag/tag.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | const common_1 = require("@nestjs/common"); 10 | const typeorm_1 = require("@nestjs/typeorm"); 11 | const user_module_1 = require("../user/user.module"); 12 | const tag_service_1 = require("./tag.service"); 13 | const tag_entity_1 = require("./tag.entity"); 14 | const tag_controller_1 = require("./tag.controller"); 15 | let TagModule = class TagModule { 16 | configure(consumer) { 17 | } 18 | }; 19 | TagModule = __decorate([ 20 | common_1.Module({ 21 | imports: [typeorm_1.TypeOrmModule.forFeature([tag_entity_1.TagEntity]), user_module_1.UserModule], 22 | providers: [tag_service_1.TagService], 23 | controllers: [ 24 | tag_controller_1.TagController 25 | ], 26 | exports: [] 27 | }) 28 | ], TagModule); 29 | exports.TagModule = TagModule; 30 | //# sourceMappingURL=tag.module.js.map -------------------------------------------------------------------------------- /dist/profile/follows.entity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const typeorm_1 = require("typeorm"); 13 | let FollowsEntity = class FollowsEntity { 14 | }; 15 | __decorate([ 16 | typeorm_1.PrimaryGeneratedColumn(), 17 | __metadata("design:type", Number) 18 | ], FollowsEntity.prototype, "id", void 0); 19 | __decorate([ 20 | typeorm_1.Column(), 21 | __metadata("design:type", Number) 22 | ], FollowsEntity.prototype, "followerId", void 0); 23 | __decorate([ 24 | typeorm_1.Column(), 25 | __metadata("design:type", Number) 26 | ], FollowsEntity.prototype, "followingId", void 0); 27 | FollowsEntity = __decorate([ 28 | typeorm_1.Entity('follows') 29 | ], FollowsEntity); 30 | exports.FollowsEntity = FollowsEntity; 31 | //# sourceMappingURL=follows.entity.js.map -------------------------------------------------------------------------------- /src/article/article.module.ts: -------------------------------------------------------------------------------- 1 | import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common'; 2 | import { ArticleController } from './article.controller'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | import { ArticleEntity } from './article.entity'; 5 | import { Comment } from './comment.entity'; 6 | import { UserEntity } from '../user/user.entity'; 7 | import { FollowsEntity } from '../profile/follows.entity'; 8 | import { ArticleService } from './article.service'; 9 | import { AuthMiddleware } from '../user/auth.middleware'; 10 | import { UserModule } from '../user/user.module'; 11 | 12 | @Module({ 13 | imports: [TypeOrmModule.forFeature([ArticleEntity, Comment, UserEntity, FollowsEntity]), UserModule], 14 | providers: [ArticleService], 15 | controllers: [ 16 | ArticleController 17 | ] 18 | }) 19 | export class ArticleModule implements NestModule { 20 | public configure(consumer: MiddlewareConsumer) { 21 | consumer 22 | .apply(AuthMiddleware) 23 | .forRoutes( 24 | {path: 'articles/feed', method: RequestMethod.GET}, 25 | {path: 'articles', method: RequestMethod.POST}, 26 | {path: 'articles/:slug', method: RequestMethod.DELETE}, 27 | {path: 'articles/:slug', method: RequestMethod.PUT}, 28 | {path: 'articles/:slug/comments', method: RequestMethod.POST}, 29 | {path: 'articles/:slug/comments/:id', method: RequestMethod.DELETE}, 30 | {path: 'articles/:slug/favorite', method: RequestMethod.POST}, 31 | {path: 'articles/:slug/favorite', method: RequestMethod.DELETE}); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dist/article/comment.entity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const typeorm_1 = require("typeorm"); 13 | const article_entity_1 = require("./article.entity"); 14 | let Comment = class Comment { 15 | }; 16 | __decorate([ 17 | typeorm_1.PrimaryGeneratedColumn(), 18 | __metadata("design:type", Number) 19 | ], Comment.prototype, "id", void 0); 20 | __decorate([ 21 | typeorm_1.Column(), 22 | __metadata("design:type", String) 23 | ], Comment.prototype, "body", void 0); 24 | __decorate([ 25 | typeorm_1.ManyToOne(type => article_entity_1.ArticleEntity, article => article.comments), 26 | __metadata("design:type", article_entity_1.ArticleEntity) 27 | ], Comment.prototype, "article", void 0); 28 | Comment = __decorate([ 29 | typeorm_1.Entity() 30 | ], Comment); 31 | exports.Comment = Comment; 32 | //# sourceMappingURL=comment.entity.js.map -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | return new (P || (P = Promise))(function (resolve, reject) { 4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 7 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 8 | }); 9 | }; 10 | Object.defineProperty(exports, "__esModule", { value: true }); 11 | const core_1 = require("@nestjs/core"); 12 | const app_module_1 = require("./app.module"); 13 | const swagger_1 = require("@nestjs/swagger"); 14 | function bootstrap() { 15 | return __awaiter(this, void 0, void 0, function* () { 16 | const appOptions = { cors: true }; 17 | const app = yield core_1.NestFactory.create(app_module_1.ApplicationModule, appOptions); 18 | app.setGlobalPrefix('api'); 19 | const options = new swagger_1.DocumentBuilder() 20 | .setTitle('NestJS Realworld Example App') 21 | .setDescription('The Realworld API description') 22 | .setVersion('1.0') 23 | .setBasePath('api') 24 | .addBearerAuth() 25 | .build(); 26 | const document = swagger_1.SwaggerModule.createDocument(app, options); 27 | swagger_1.SwaggerModule.setup('/docs', app, document); 28 | yield app.listen(3000); 29 | }); 30 | } 31 | bootstrap(); 32 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /dist/user/user.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | const common_1 = require("@nestjs/common"); 10 | const user_controller_1 = require("./user.controller"); 11 | const typeorm_1 = require("@nestjs/typeorm"); 12 | const user_entity_1 = require("./user.entity"); 13 | const user_service_1 = require("./user.service"); 14 | const auth_middleware_1 = require("./auth.middleware"); 15 | let UserModule = class UserModule { 16 | configure(consumer) { 17 | consumer 18 | .apply(auth_middleware_1.AuthMiddleware) 19 | .forRoutes({ path: 'user', method: common_1.RequestMethod.GET }, { path: 'user', method: common_1.RequestMethod.PUT }); 20 | } 21 | }; 22 | UserModule = __decorate([ 23 | common_1.Module({ 24 | imports: [typeorm_1.TypeOrmModule.forFeature([user_entity_1.UserEntity])], 25 | providers: [user_service_1.UserService], 26 | controllers: [ 27 | user_controller_1.UserController 28 | ], 29 | exports: [user_service_1.UserService] 30 | }) 31 | ], UserModule); 32 | exports.UserModule = UserModule; 33 | //# sourceMappingURL=user.module.js.map -------------------------------------------------------------------------------- /dist/user/user.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"user.controller.js","sourceRoot":"","sources":["../../src/user/user.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,2CAA2F;AAE3F,iDAA6C;AAG7C,+BAAmE;AACnE,6EAAyE;AACzE,qDAAwC;AACxC,qEAAiE;AAEjE,6CAGyB;AAKzB,IAAa,cAAc,GAA3B,MAAa,cAAc;IAEzB,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAGnD,MAAM,CAAgB,KAAa;;YACvC,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACnD,CAAC;KAAA;IAGK,MAAM,CAAa,MAAc,EAAgB,QAAuB;;YAC5E,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACzD,CAAC;KAAA;IAIK,MAAM,CAAe,QAAuB;;YAChD,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC;KAAA;IAGK,MAAM,CAAU,MAAM;;YAC1B,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC;KAAA;IAIK,KAAK,CAAe,YAA0B;;YAClD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAE3D,MAAM,MAAM,GAAG,EAAC,IAAI,EAAE,YAAY,EAAC,CAAC;YACpC,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,8BAAa,CAAC,EAAC,MAAM,EAAC,EAAE,GAAG,CAAC,CAAC;YAEnD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACxD,MAAM,EAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAC,GAAG,KAAK,CAAC;YAC5C,MAAM,IAAI,GAAG,EAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAC,CAAC;YAClD,OAAO,EAAC,IAAI,EAAC,CAAA;QACf,CAAC;KAAA;CACF,CAAA;AAjCC;IADC,YAAG,CAAC,MAAM,CAAC;IACE,WAAA,qBAAI,CAAC,OAAO,CAAC,CAAA;;;;4CAE1B;AAGD;IADC,YAAG,CAAC,MAAM,CAAC;IACE,WAAA,qBAAI,CAAC,IAAI,CAAC,CAAA,EAAkB,WAAA,aAAI,CAAC,MAAM,CAAC,CAAA;;6CAAW,mBAAa;;4CAE7E;AAID;IAFC,iBAAQ,CAAC,IAAI,gCAAc,EAAE,CAAC;IAC9B,aAAI,CAAC,OAAO,CAAC;IACA,WAAA,aAAI,CAAC,MAAM,CAAC,CAAA;;qCAAW,mBAAa;;4CAEjD;AAGD;IADC,eAAM,CAAC,aAAa,CAAC;IACR,WAAA,cAAK,EAAE,CAAA;;;;4CAEpB;AAID;IAFC,iBAAQ,CAAC,IAAI,gCAAc,EAAE,CAAC;IAC9B,aAAI,CAAC,aAAa,CAAC;IACP,WAAA,aAAI,CAAC,MAAM,CAAC,CAAA;;qCAAe,kBAAY;;2CAUnD;AArCU,cAAc;IAH1B,uBAAa,EAAE;IACf,oBAAU,CAAC,MAAM,CAAC;IAClB,mBAAU,EAAE;qCAG+B,0BAAW;GAF1C,cAAc,CAsC1B;AAtCY,wCAAc"} -------------------------------------------------------------------------------- /dist/profile/profile.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | const common_1 = require("@nestjs/common"); 10 | const profile_controller_1 = require("./profile.controller"); 11 | const typeorm_1 = require("@nestjs/typeorm"); 12 | const profile_service_1 = require("./profile.service"); 13 | const user_module_1 = require("../user/user.module"); 14 | const user_entity_1 = require("../user/user.entity"); 15 | const follows_entity_1 = require("./follows.entity"); 16 | const auth_middleware_1 = require("../user/auth.middleware"); 17 | let ProfileModule = class ProfileModule { 18 | configure(consumer) { 19 | consumer 20 | .apply(auth_middleware_1.AuthMiddleware) 21 | .forRoutes({ path: 'profiles/:username/follow', method: common_1.RequestMethod.ALL }); 22 | } 23 | }; 24 | ProfileModule = __decorate([ 25 | common_1.Module({ 26 | imports: [typeorm_1.TypeOrmModule.forFeature([user_entity_1.UserEntity, follows_entity_1.FollowsEntity]), user_module_1.UserModule], 27 | providers: [profile_service_1.ProfileService], 28 | controllers: [ 29 | profile_controller_1.ProfileController 30 | ], 31 | exports: [] 32 | }) 33 | ], ProfileModule); 34 | exports.ProfileModule = ProfileModule; 35 | //# sourceMappingURL=profile.module.js.map -------------------------------------------------------------------------------- /src/user/user.controller.ts: -------------------------------------------------------------------------------- 1 | import { Get, Post, Body, Put, Delete, Param, Controller, UsePipes } from '@nestjs/common'; 2 | import { Request } from 'express'; 3 | import { UserService } from './user.service'; 4 | import { UserRO } from './user.interface'; 5 | import { CreateUserDto, UpdateUserDto, LoginUserDto } from './dto'; 6 | import { HttpException } from '@nestjs/common/exceptions/http.exception'; 7 | import { User } from './user.decorator'; 8 | import { ValidationPipe } from '../shared/pipes/validation.pipe'; 9 | 10 | import { 11 | ApiBearerAuth, ApiTags 12 | } from '@nestjs/swagger'; 13 | 14 | @ApiBearerAuth() 15 | @ApiTags('user') 16 | @Controller() 17 | export class UserController { 18 | 19 | constructor(private readonly userService: UserService) {} 20 | 21 | @Get('user') 22 | async findMe(@User('email') email: string): Promise { 23 | return await this.userService.findByEmail(email); 24 | } 25 | 26 | @Put('user') 27 | async update(@User('id') userId: number, @Body('user') userData: UpdateUserDto) { 28 | return await this.userService.update(userId, userData); 29 | } 30 | 31 | @UsePipes(new ValidationPipe()) 32 | @Post('users') 33 | async create(@Body('user') userData: CreateUserDto) { 34 | return this.userService.create(userData); 35 | } 36 | 37 | @Delete('users/:slug') 38 | async delete(@Param() params) { 39 | return await this.userService.delete(params.slug); 40 | } 41 | 42 | @UsePipes(new ValidationPipe()) 43 | @Post('users/login') 44 | async login(@Body('user') loginUserDto: LoginUserDto): Promise { 45 | const _user = await this.userService.findOne(loginUserDto); 46 | 47 | const errors = {User: ' not found'}; 48 | if (!_user) throw new HttpException({errors}, 401); 49 | 50 | const token = await this.userService.generateJWT(_user); 51 | const {email, username, bio, image} = _user; 52 | const user = {email, token, username, bio, image}; 53 | return {user} 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /dist/app.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const common_1 = require("@nestjs/common"); 13 | const app_controller_1 = require("./app.controller"); 14 | const article_module_1 = require("./article/article.module"); 15 | const user_module_1 = require("./user/user.module"); 16 | const typeorm_1 = require("@nestjs/typeorm"); 17 | const typeorm_2 = require("typeorm"); 18 | const profile_module_1 = require("./profile/profile.module"); 19 | const tag_module_1 = require("./tag/tag.module"); 20 | let ApplicationModule = class ApplicationModule { 21 | constructor(connection) { 22 | this.connection = connection; 23 | } 24 | }; 25 | ApplicationModule = __decorate([ 26 | common_1.Module({ 27 | imports: [ 28 | typeorm_1.TypeOrmModule.forRoot(), 29 | article_module_1.ArticleModule, 30 | user_module_1.UserModule, 31 | profile_module_1.ProfileModule, 32 | tag_module_1.TagModule 33 | ], 34 | controllers: [ 35 | app_controller_1.AppController 36 | ], 37 | providers: [] 38 | }), 39 | __metadata("design:paramtypes", [typeorm_2.Connection]) 40 | ], ApplicationModule); 41 | exports.ApplicationModule = ApplicationModule; 42 | //# sourceMappingURL=app.module.js.map -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-realworld-example-app", 3 | "version": "2.0.0", 4 | "description": "[![Build Status](https://travis-ci.org/anishkny/node-express-realworld-example-app.svg?branch=master)](https://travis-ci.org/anishkny/node-express-realworld-example-app)", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "start:watch": "nodemon", 9 | "prestart:prod": "tsc", 10 | "start:prod": "node dist/main.js", 11 | "test": "jest --config=jest.json", 12 | "test:watch": "jest --watch --config=jest.json", 13 | "test:coverage": "jest --config=jest.json --coverage --coverageDirectory=coverage" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/lujakob/nestjs-realworld-example-app.git" 18 | }, 19 | "author": "lu.jakob@googlemail.com", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/lujakob/nestjs-realworld-example-app/issues" 23 | }, 24 | "homepage": "https://github.com/lujakob/nestjs-realworld-example-app#readme", 25 | "dependencies": { 26 | "@nestjs/common": "^7.0.5", 27 | "@nestjs/core": "^7.0.5", 28 | "@nestjs/microservices": "^7.0.5", 29 | "@nestjs/platform-express": "^7.0.5", 30 | "@nestjs/swagger": "^4.4.0", 31 | "@nestjs/testing": "^7.0.5", 32 | "@nestjs/typeorm": "^7.0.0", 33 | "@nestjs/websockets": "^7.0.5", 34 | "argon2": "^0.26.2", 35 | "class-transformer": "^0.2.3", 36 | "class-validator": "^0.11.1", 37 | "crypto": "^1.0.1", 38 | "crypto-js": "^4.0.0", 39 | "jsonwebtoken": "^8.5.1", 40 | "mysql": "^2.18.1", 41 | "passport-jwt": "^4.0.0", 42 | "reflect-metadata": "^0.1.13", 43 | "rxjs": "^6.5.5", 44 | "slug": "^1.1.0", 45 | "swagger-ui-express": "^4.1.4", 46 | "typeorm": "^0.2.24", 47 | "typescript": "^3.8.3" 48 | }, 49 | "devDependencies": { 50 | "@types/jest": "^25.2.1", 51 | "@types/node": "^13.13.4", 52 | "atob": ">=2.1.0", 53 | "deep-extend": ">=0.5.1", 54 | "extend": ">=3.0.2", 55 | "jest": "^25.5.3", 56 | "nodemon": "^1.19.4", 57 | "supertest": "^3.4.2", 58 | "ts-jest": "^25.4.0", 59 | "ts-node": "^8.9.1" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /dist/tag/tag.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const typeorm_1 = require("@nestjs/typeorm"); 25 | const typeorm_2 = require("typeorm"); 26 | const tag_entity_1 = require("./tag.entity"); 27 | let TagService = class TagService { 28 | constructor(tagRepository) { 29 | this.tagRepository = tagRepository; 30 | } 31 | findAll() { 32 | return __awaiter(this, void 0, void 0, function* () { 33 | return yield this.tagRepository.find(); 34 | }); 35 | } 36 | }; 37 | TagService = __decorate([ 38 | common_1.Injectable(), 39 | __param(0, typeorm_1.InjectRepository(tag_entity_1.TagEntity)), 40 | __metadata("design:paramtypes", [typeorm_2.Repository]) 41 | ], TagService); 42 | exports.TagService = TagService; 43 | //# sourceMappingURL=tag.service.js.map -------------------------------------------------------------------------------- /dist/tag/tag.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 12 | return new (P || (P = Promise))(function (resolve, reject) { 13 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 14 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 15 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 16 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 17 | }); 18 | }; 19 | Object.defineProperty(exports, "__esModule", { value: true }); 20 | const common_1 = require("@nestjs/common"); 21 | const tag_service_1 = require("./tag.service"); 22 | const swagger_1 = require("@nestjs/swagger"); 23 | let TagController = class TagController { 24 | constructor(tagService) { 25 | this.tagService = tagService; 26 | } 27 | findAll() { 28 | return __awaiter(this, void 0, void 0, function* () { 29 | return yield this.tagService.findAll(); 30 | }); 31 | } 32 | }; 33 | __decorate([ 34 | common_1.Get(), 35 | __metadata("design:type", Function), 36 | __metadata("design:paramtypes", []), 37 | __metadata("design:returntype", Promise) 38 | ], TagController.prototype, "findAll", null); 39 | TagController = __decorate([ 40 | swagger_1.ApiBearerAuth(), 41 | swagger_1.ApiUseTags('tags'), 42 | common_1.Controller('tags'), 43 | __metadata("design:paramtypes", [tag_service_1.TagService]) 44 | ], TagController); 45 | exports.TagController = TagController; 46 | //# sourceMappingURL=tag.controller.js.map -------------------------------------------------------------------------------- /dist/article/article.module.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | Object.defineProperty(exports, "__esModule", { value: true }); 9 | const common_1 = require("@nestjs/common"); 10 | const article_controller_1 = require("./article.controller"); 11 | const typeorm_1 = require("@nestjs/typeorm"); 12 | const article_entity_1 = require("./article.entity"); 13 | const comment_entity_1 = require("./comment.entity"); 14 | const user_entity_1 = require("../user/user.entity"); 15 | const follows_entity_1 = require("../profile/follows.entity"); 16 | const article_service_1 = require("./article.service"); 17 | const auth_middleware_1 = require("../user/auth.middleware"); 18 | const user_module_1 = require("../user/user.module"); 19 | let ArticleModule = class ArticleModule { 20 | configure(consumer) { 21 | consumer 22 | .apply(auth_middleware_1.AuthMiddleware) 23 | .forRoutes({ path: 'articles/feed', method: common_1.RequestMethod.GET }, { path: 'articles', method: common_1.RequestMethod.POST }, { path: 'articles/:slug', method: common_1.RequestMethod.DELETE }, { path: 'articles/:slug', method: common_1.RequestMethod.PUT }, { path: 'articles/:slug/comments', method: common_1.RequestMethod.POST }, { path: 'articles/:slug/comments/:id', method: common_1.RequestMethod.DELETE }, { path: 'articles/:slug/favorite', method: common_1.RequestMethod.POST }, { path: 'articles/:slug/favorite', method: common_1.RequestMethod.DELETE }); 24 | } 25 | }; 26 | ArticleModule = __decorate([ 27 | common_1.Module({ 28 | imports: [typeorm_1.TypeOrmModule.forFeature([article_entity_1.ArticleEntity, comment_entity_1.Comment, user_entity_1.UserEntity, follows_entity_1.FollowsEntity]), user_module_1.UserModule], 29 | providers: [article_service_1.ArticleService], 30 | controllers: [ 31 | article_controller_1.ArticleController 32 | ] 33 | }) 34 | ], ArticleModule); 35 | exports.ArticleModule = ArticleModule; 36 | //# sourceMappingURL=article.module.js.map -------------------------------------------------------------------------------- /dist/profile/profile.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"profile.service.js","sourceRoot":"","sources":["../../src/profile/profile.service.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/user/auth.middleware.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 12 | return new (P || (P = Promise))(function (resolve, reject) { 13 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 14 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 15 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 16 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 17 | }); 18 | }; 19 | Object.defineProperty(exports, "__esModule", { value: true }); 20 | const http_exception_1 = require("@nestjs/common/exceptions/http.exception"); 21 | const common_1 = require("@nestjs/common"); 22 | const jwt = require("jsonwebtoken"); 23 | const config_1 = require("../config"); 24 | const user_service_1 = require("./user.service"); 25 | let AuthMiddleware = class AuthMiddleware { 26 | constructor(userService) { 27 | this.userService = userService; 28 | } 29 | use(req, res, next) { 30 | return __awaiter(this, void 0, void 0, function* () { 31 | const authHeaders = req.headers.authorization; 32 | if (authHeaders && authHeaders.split(' ')[1]) { 33 | const token = authHeaders.split(' ')[1]; 34 | const decoded = jwt.verify(token, config_1.SECRET); 35 | const user = yield this.userService.findById(decoded.id); 36 | if (!user) { 37 | throw new http_exception_1.HttpException('User not found.', common_1.HttpStatus.UNAUTHORIZED); 38 | } 39 | req.user = user.user; 40 | next(); 41 | } 42 | else { 43 | throw new http_exception_1.HttpException('Not authorized.', common_1.HttpStatus.UNAUTHORIZED); 44 | } 45 | }); 46 | } 47 | }; 48 | AuthMiddleware = __decorate([ 49 | common_1.Injectable(), 50 | __metadata("design:paramtypes", [user_service_1.UserService]) 51 | ], AuthMiddleware); 52 | exports.AuthMiddleware = AuthMiddleware; 53 | //# sourceMappingURL=auth.middleware.js.map -------------------------------------------------------------------------------- /dist/shared/pipes/validation.pipe.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 9 | return new (P || (P = Promise))(function (resolve, reject) { 10 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 11 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 12 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 13 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 14 | }); 15 | }; 16 | Object.defineProperty(exports, "__esModule", { value: true }); 17 | const common_1 = require("@nestjs/common"); 18 | const class_validator_1 = require("class-validator"); 19 | const class_transformer_1 = require("class-transformer"); 20 | const http_exception_1 = require("@nestjs/common/exceptions/http.exception"); 21 | let ValidationPipe = class ValidationPipe { 22 | transform(value, metadata) { 23 | return __awaiter(this, void 0, void 0, function* () { 24 | if (!value) { 25 | throw new common_1.BadRequestException('No data submitted'); 26 | } 27 | const { metatype } = metadata; 28 | if (!metatype || !this.toValidate(metatype)) { 29 | return value; 30 | } 31 | const object = class_transformer_1.plainToClass(metatype, value); 32 | const errors = yield class_validator_1.validate(object); 33 | if (errors.length > 0) { 34 | throw new http_exception_1.HttpException({ message: 'Input data validation failed', errors: this.buildError(errors) }, common_1.HttpStatus.BAD_REQUEST); 35 | } 36 | return value; 37 | }); 38 | } 39 | buildError(errors) { 40 | const result = {}; 41 | errors.forEach(el => { 42 | let prop = el.property; 43 | Object.entries(el.constraints).forEach(constraint => { 44 | result[prop + constraint[0]] = `${constraint[1]}`; 45 | }); 46 | }); 47 | return result; 48 | } 49 | toValidate(metatype) { 50 | const types = [String, Boolean, Number, Array, Object]; 51 | return !types.find((type) => metatype === type); 52 | } 53 | }; 54 | ValidationPipe = __decorate([ 55 | common_1.Injectable() 56 | ], ValidationPipe); 57 | exports.ValidationPipe = ValidationPipe; 58 | //# sourceMappingURL=validation.pipe.js.map -------------------------------------------------------------------------------- /dist/article/article.entity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const typeorm_1 = require("typeorm"); 13 | const user_entity_1 = require("../user/user.entity"); 14 | const comment_entity_1 = require("./comment.entity"); 15 | let ArticleEntity = class ArticleEntity { 16 | updateTimestamp() { 17 | this.updated = new Date; 18 | } 19 | }; 20 | __decorate([ 21 | typeorm_1.PrimaryGeneratedColumn(), 22 | __metadata("design:type", Number) 23 | ], ArticleEntity.prototype, "id", void 0); 24 | __decorate([ 25 | typeorm_1.Column(), 26 | __metadata("design:type", String) 27 | ], ArticleEntity.prototype, "slug", void 0); 28 | __decorate([ 29 | typeorm_1.Column(), 30 | __metadata("design:type", String) 31 | ], ArticleEntity.prototype, "title", void 0); 32 | __decorate([ 33 | typeorm_1.Column({ default: '' }), 34 | __metadata("design:type", String) 35 | ], ArticleEntity.prototype, "description", void 0); 36 | __decorate([ 37 | typeorm_1.Column({ default: '' }), 38 | __metadata("design:type", String) 39 | ], ArticleEntity.prototype, "body", void 0); 40 | __decorate([ 41 | typeorm_1.Column({ type: 'timestamp', default: () => "CURRENT_TIMESTAMP" }), 42 | __metadata("design:type", Date) 43 | ], ArticleEntity.prototype, "created", void 0); 44 | __decorate([ 45 | typeorm_1.Column({ type: 'timestamp', default: () => "CURRENT_TIMESTAMP" }), 46 | __metadata("design:type", Date) 47 | ], ArticleEntity.prototype, "updated", void 0); 48 | __decorate([ 49 | typeorm_1.BeforeUpdate(), 50 | __metadata("design:type", Function), 51 | __metadata("design:paramtypes", []), 52 | __metadata("design:returntype", void 0) 53 | ], ArticleEntity.prototype, "updateTimestamp", null); 54 | __decorate([ 55 | typeorm_1.Column('simple-array'), 56 | __metadata("design:type", Array) 57 | ], ArticleEntity.prototype, "tagList", void 0); 58 | __decorate([ 59 | typeorm_1.ManyToOne(type => user_entity_1.UserEntity, user => user.articles), 60 | __metadata("design:type", user_entity_1.UserEntity) 61 | ], ArticleEntity.prototype, "author", void 0); 62 | __decorate([ 63 | typeorm_1.OneToMany(type => comment_entity_1.Comment, comment => comment.article, { eager: true }), 64 | typeorm_1.JoinColumn(), 65 | __metadata("design:type", Array) 66 | ], ArticleEntity.prototype, "comments", void 0); 67 | __decorate([ 68 | typeorm_1.Column({ default: 0 }), 69 | __metadata("design:type", Number) 70 | ], ArticleEntity.prototype, "favoriteCount", void 0); 71 | ArticleEntity = __decorate([ 72 | typeorm_1.Entity('article') 73 | ], ArticleEntity); 74 | exports.ArticleEntity = ArticleEntity; 75 | //# sourceMappingURL=article.entity.js.map -------------------------------------------------------------------------------- /dist/user/user.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"user.service.js","sourceRoot":"","sources":["../../src/user/user.service.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/user/user.entity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 12 | return new (P || (P = Promise))(function (resolve, reject) { 13 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 14 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 15 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 16 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 17 | }); 18 | }; 19 | Object.defineProperty(exports, "__esModule", { value: true }); 20 | const typeorm_1 = require("typeorm"); 21 | const class_validator_1 = require("class-validator"); 22 | const argon2 = require("argon2"); 23 | const article_entity_1 = require("../article/article.entity"); 24 | let UserEntity = class UserEntity { 25 | hashPassword() { 26 | return __awaiter(this, void 0, void 0, function* () { 27 | this.password = yield argon2.hash(this.password); 28 | }); 29 | } 30 | }; 31 | __decorate([ 32 | typeorm_1.PrimaryGeneratedColumn(), 33 | __metadata("design:type", Number) 34 | ], UserEntity.prototype, "id", void 0); 35 | __decorate([ 36 | typeorm_1.Column(), 37 | __metadata("design:type", String) 38 | ], UserEntity.prototype, "username", void 0); 39 | __decorate([ 40 | typeorm_1.Column(), 41 | class_validator_1.IsEmail(), 42 | __metadata("design:type", String) 43 | ], UserEntity.prototype, "email", void 0); 44 | __decorate([ 45 | typeorm_1.Column({ default: '' }), 46 | __metadata("design:type", String) 47 | ], UserEntity.prototype, "bio", void 0); 48 | __decorate([ 49 | typeorm_1.Column({ default: '' }), 50 | __metadata("design:type", String) 51 | ], UserEntity.prototype, "image", void 0); 52 | __decorate([ 53 | typeorm_1.Column(), 54 | __metadata("design:type", String) 55 | ], UserEntity.prototype, "password", void 0); 56 | __decorate([ 57 | typeorm_1.BeforeInsert(), 58 | __metadata("design:type", Function), 59 | __metadata("design:paramtypes", []), 60 | __metadata("design:returntype", Promise) 61 | ], UserEntity.prototype, "hashPassword", null); 62 | __decorate([ 63 | typeorm_1.ManyToMany(type => article_entity_1.ArticleEntity), 64 | typeorm_1.JoinTable(), 65 | __metadata("design:type", Array) 66 | ], UserEntity.prototype, "favorites", void 0); 67 | __decorate([ 68 | typeorm_1.OneToMany(type => article_entity_1.ArticleEntity, article => article.author), 69 | __metadata("design:type", Array) 70 | ], UserEntity.prototype, "articles", void 0); 71 | UserEntity = __decorate([ 72 | typeorm_1.Entity('user') 73 | ], UserEntity); 74 | exports.UserEntity = UserEntity; 75 | //# sourceMappingURL=user.entity.js.map -------------------------------------------------------------------------------- /src/profile/profile.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpStatus, Injectable} from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | import { UserEntity } from '../user/user.entity'; 5 | import { DeepPartial } from 'typeorm/common/DeepPartial'; 6 | import { ProfileRO, ProfileData } from './profile.interface'; 7 | import {FollowsEntity} from "./follows.entity"; 8 | import {HttpException} from "@nestjs/common/exceptions/http.exception"; 9 | 10 | @Injectable() 11 | export class ProfileService { 12 | constructor( 13 | @InjectRepository(UserEntity) 14 | private readonly userRepository: Repository, 15 | @InjectRepository(FollowsEntity) 16 | private readonly followsRepository: Repository 17 | ) {} 18 | 19 | async findAll(): Promise { 20 | return await this.userRepository.find(); 21 | } 22 | 23 | async findOne(options?: DeepPartial): Promise { 24 | const user = await this.userRepository.findOne(options); 25 | delete user.id; 26 | if (user) delete user.password; 27 | return {profile: user}; 28 | } 29 | 30 | async findProfile(id: number, followingUsername: string): Promise { 31 | const _profile = await this.userRepository.findOne( {username: followingUsername}); 32 | 33 | if(!_profile) return; 34 | 35 | let profile: ProfileData = { 36 | username: _profile.username, 37 | bio: _profile.bio, 38 | image: _profile.image 39 | }; 40 | 41 | const follows = await this.followsRepository.findOne( {followerId: id, followingId: _profile.id}); 42 | 43 | if (id) { 44 | profile.following = !!follows; 45 | } 46 | 47 | return {profile}; 48 | } 49 | 50 | async follow(followerEmail: string, username: string): Promise { 51 | if (!followerEmail || !username) { 52 | throw new HttpException('Follower email and username not provided.', HttpStatus.BAD_REQUEST); 53 | } 54 | 55 | const followingUser = await this.userRepository.findOne({username}); 56 | const followerUser = await this.userRepository.findOne({email: followerEmail}); 57 | 58 | if (followingUser.email === followerEmail) { 59 | throw new HttpException('FollowerEmail and FollowingId cannot be equal.', HttpStatus.BAD_REQUEST); 60 | } 61 | 62 | const _follows = await this.followsRepository.findOne( {followerId: followerUser.id, followingId: followingUser.id}); 63 | 64 | if (!_follows) { 65 | const follows = new FollowsEntity(); 66 | follows.followerId = followerUser.id; 67 | follows.followingId = followingUser.id; 68 | await this.followsRepository.save(follows); 69 | } 70 | 71 | let profile: ProfileData = { 72 | username: followingUser.username, 73 | bio: followingUser.bio, 74 | image: followingUser.image, 75 | following: true 76 | }; 77 | 78 | return {profile}; 79 | } 80 | 81 | async unFollow(followerId: number, username: string): Promise { 82 | if (!followerId || !username) { 83 | throw new HttpException('FollowerId and username not provided.', HttpStatus.BAD_REQUEST); 84 | } 85 | 86 | const followingUser = await this.userRepository.findOne({username}); 87 | 88 | if (followingUser.id === followerId) { 89 | throw new HttpException('FollowerId and FollowingId cannot be equal.', HttpStatus.BAD_REQUEST); 90 | } 91 | const followingId = followingUser.id; 92 | await this.followsRepository.delete({followerId, followingId}); 93 | 94 | let profile: ProfileData = { 95 | username: followingUser.username, 96 | bio: followingUser.bio, 97 | image: followingUser.image, 98 | following: false 99 | }; 100 | 101 | return {profile}; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /dist/profile/profile.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const profile_service_1 = require("./profile.service"); 25 | const user_decorator_1 = require("../user/user.decorator"); 26 | const swagger_1 = require("@nestjs/swagger"); 27 | let ProfileController = class ProfileController { 28 | constructor(profileService) { 29 | this.profileService = profileService; 30 | } 31 | getProfile(userId, username) { 32 | return __awaiter(this, void 0, void 0, function* () { 33 | return yield this.profileService.findProfile(userId, username); 34 | }); 35 | } 36 | follow(email, username) { 37 | return __awaiter(this, void 0, void 0, function* () { 38 | return yield this.profileService.follow(email, username); 39 | }); 40 | } 41 | unFollow(userId, username) { 42 | return __awaiter(this, void 0, void 0, function* () { 43 | return yield this.profileService.unFollow(userId, username); 44 | }); 45 | } 46 | }; 47 | __decorate([ 48 | common_1.Get(':username'), 49 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Param('username')), 50 | __metadata("design:type", Function), 51 | __metadata("design:paramtypes", [Number, String]), 52 | __metadata("design:returntype", Promise) 53 | ], ProfileController.prototype, "getProfile", null); 54 | __decorate([ 55 | common_1.Post(':username/follow'), 56 | __param(0, user_decorator_1.User('email')), __param(1, common_1.Param('username')), 57 | __metadata("design:type", Function), 58 | __metadata("design:paramtypes", [String, String]), 59 | __metadata("design:returntype", Promise) 60 | ], ProfileController.prototype, "follow", null); 61 | __decorate([ 62 | common_1.Delete(':username/follow'), 63 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Param('username')), 64 | __metadata("design:type", Function), 65 | __metadata("design:paramtypes", [Number, String]), 66 | __metadata("design:returntype", Promise) 67 | ], ProfileController.prototype, "unFollow", null); 68 | ProfileController = __decorate([ 69 | swagger_1.ApiBearerAuth(), 70 | swagger_1.ApiUseTags('profiles'), 71 | common_1.Controller('profiles'), 72 | __metadata("design:paramtypes", [profile_service_1.ProfileService]) 73 | ], ProfileController); 74 | exports.ProfileController = ProfileController; 75 | //# sourceMappingURL=profile.controller.js.map -------------------------------------------------------------------------------- /dist/article/article.controller.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"article.controller.js","sourceRoot":"","sources":["../../src/article/article.controller.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository, getRepository, DeleteResult } from 'typeorm'; 4 | import { UserEntity } from './user.entity'; 5 | import {CreateUserDto, LoginUserDto, UpdateUserDto} from './dto'; 6 | const jwt = require('jsonwebtoken'); 7 | import { SECRET } from '../config'; 8 | import { UserRO } from './user.interface'; 9 | import { validate } from 'class-validator'; 10 | import { HttpException } from '@nestjs/common/exceptions/http.exception'; 11 | import { HttpStatus } from '@nestjs/common'; 12 | import * as argon2 from 'argon2'; 13 | 14 | @Injectable() 15 | export class UserService { 16 | constructor( 17 | @InjectRepository(UserEntity) 18 | private readonly userRepository: Repository 19 | ) {} 20 | 21 | async findAll(): Promise { 22 | return await this.userRepository.find(); 23 | } 24 | 25 | async findOne({email, password}: LoginUserDto): Promise { 26 | const user = await this.userRepository.findOne({email}); 27 | if (!user) { 28 | return null; 29 | } 30 | 31 | if (await argon2.verify(user.password, password)) { 32 | return user; 33 | } 34 | 35 | return null; 36 | } 37 | 38 | async create(dto: CreateUserDto): Promise { 39 | 40 | // check uniqueness of username/email 41 | const {username, email, password} = dto; 42 | const qb = await getRepository(UserEntity) 43 | .createQueryBuilder('user') 44 | .where('user.username = :username', { username }) 45 | .orWhere('user.email = :email', { email }); 46 | 47 | const user = await qb.getOne(); 48 | 49 | if (user) { 50 | const errors = {username: 'Username and email must be unique.'}; 51 | throw new HttpException({message: 'Input data validation failed', errors}, HttpStatus.BAD_REQUEST); 52 | 53 | } 54 | 55 | // create new user 56 | let newUser = new UserEntity(); 57 | newUser.username = username; 58 | newUser.email = email; 59 | newUser.password = password; 60 | newUser.articles = []; 61 | 62 | const errors = await validate(newUser); 63 | if (errors.length > 0) { 64 | const _errors = {username: 'Userinput is not valid.'}; 65 | throw new HttpException({message: 'Input data validation failed', _errors}, HttpStatus.BAD_REQUEST); 66 | 67 | } else { 68 | const savedUser = await this.userRepository.save(newUser); 69 | return this.buildUserRO(savedUser); 70 | } 71 | 72 | } 73 | 74 | async update(id: number, dto: UpdateUserDto): Promise { 75 | let toUpdate = await this.userRepository.findOne(id); 76 | delete toUpdate.password; 77 | delete toUpdate.favorites; 78 | 79 | let updated = Object.assign(toUpdate, dto); 80 | return await this.userRepository.save(updated); 81 | } 82 | 83 | async delete(email: string): Promise { 84 | return await this.userRepository.delete({ email: email}); 85 | } 86 | 87 | async findById(id: number): Promise{ 88 | const user = await this.userRepository.findOne(id); 89 | 90 | if (!user) { 91 | const errors = {User: ' not found'}; 92 | throw new HttpException({errors}, 401); 93 | } 94 | 95 | return this.buildUserRO(user); 96 | } 97 | 98 | async findByEmail(email: string): Promise{ 99 | const user = await this.userRepository.findOne({email: email}); 100 | return this.buildUserRO(user); 101 | } 102 | 103 | public generateJWT(user) { 104 | let today = new Date(); 105 | let exp = new Date(today); 106 | exp.setDate(today.getDate() + 60); 107 | 108 | return jwt.sign({ 109 | id: user.id, 110 | username: user.username, 111 | email: user.email, 112 | exp: exp.getTime() / 1000, 113 | }, SECRET); 114 | }; 115 | 116 | private buildUserRO(user: UserEntity) { 117 | const userRO = { 118 | id: user.id, 119 | username: user.username, 120 | email: user.email, 121 | bio: user.bio, 122 | token: this.generateJWT(user), 123 | image: user.image 124 | }; 125 | 126 | return {user: userRO}; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/article/article.controller.ts: -------------------------------------------------------------------------------- 1 | import {Get, Post, Body, Put, Delete, Query, Param, Controller} from '@nestjs/common'; 2 | import { Request } from 'express'; 3 | import { ArticleService } from './article.service'; 4 | import { CreateArticleDto, CreateCommentDto } from './dto'; 5 | import { ArticlesRO, ArticleRO } from './article.interface'; 6 | import { CommentsRO } from './article.interface'; 7 | import { User } from '../user/user.decorator'; 8 | 9 | import { 10 | ApiBearerAuth, 11 | ApiResponse, 12 | ApiOperation, ApiTags, 13 | } from '@nestjs/swagger'; 14 | 15 | @ApiBearerAuth() 16 | @ApiTags('articles') 17 | @Controller('articles') 18 | export class ArticleController { 19 | 20 | constructor(private readonly articleService: ArticleService) {} 21 | 22 | @ApiOperation({ summary: 'Get all articles' }) 23 | @ApiResponse({ status: 200, description: 'Return all articles.'}) 24 | @Get() 25 | async findAll(@Query() query): Promise { 26 | return await this.articleService.findAll(query); 27 | } 28 | 29 | 30 | @ApiOperation({ summary: 'Get article feed' }) 31 | @ApiResponse({ status: 200, description: 'Return article feed.'}) 32 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 33 | @Get('feed') 34 | async getFeed(@User('id') userId: number, @Query() query): Promise { 35 | return await this.articleService.findFeed(userId, query); 36 | } 37 | 38 | @Get(':slug') 39 | async findOne(@Param('slug') slug): Promise { 40 | return await this.articleService.findOne({slug}); 41 | } 42 | 43 | @Get(':slug/comments') 44 | async findComments(@Param('slug') slug): Promise { 45 | return await this.articleService.findComments(slug); 46 | } 47 | 48 | @ApiOperation({ summary: 'Create article' }) 49 | @ApiResponse({ status: 201, description: 'The article has been successfully created.'}) 50 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 51 | @Post() 52 | async create(@User('id') userId: number, @Body('article') articleData: CreateArticleDto) { 53 | return this.articleService.create(userId, articleData); 54 | } 55 | 56 | @ApiOperation({ summary: 'Update article' }) 57 | @ApiResponse({ status: 201, description: 'The article has been successfully updated.'}) 58 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 59 | @Put(':slug') 60 | async update(@Param() params, @Body('article') articleData: CreateArticleDto) { 61 | // Todo: update slug also when title gets changed 62 | return this.articleService.update(params.slug, articleData); 63 | } 64 | 65 | @ApiOperation({ summary: 'Delete article' }) 66 | @ApiResponse({ status: 201, description: 'The article has been successfully deleted.'}) 67 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 68 | @Delete(':slug') 69 | async delete(@Param() params) { 70 | return this.articleService.delete(params.slug); 71 | } 72 | 73 | @ApiOperation({ summary: 'Create comment' }) 74 | @ApiResponse({ status: 201, description: 'The comment has been successfully created.'}) 75 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 76 | @Post(':slug/comments') 77 | async createComment(@Param('slug') slug, @Body('comment') commentData: CreateCommentDto) { 78 | return await this.articleService.addComment(slug, commentData); 79 | } 80 | 81 | @ApiOperation({ summary: 'Delete comment' }) 82 | @ApiResponse({ status: 201, description: 'The article has been successfully deleted.'}) 83 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 84 | @Delete(':slug/comments/:id') 85 | async deleteComment(@Param() params) { 86 | const {slug, id} = params; 87 | return await this.articleService.deleteComment(slug, id); 88 | } 89 | 90 | @ApiOperation({ summary: 'Favorite article' }) 91 | @ApiResponse({ status: 201, description: 'The article has been successfully favorited.'}) 92 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 93 | @Post(':slug/favorite') 94 | async favorite(@User('id') userId: number, @Param('slug') slug) { 95 | return await this.articleService.favorite(userId, slug); 96 | } 97 | 98 | @ApiOperation({ summary: 'Unfavorite article' }) 99 | @ApiResponse({ status: 201, description: 'The article has been successfully unfavorited.'}) 100 | @ApiResponse({ status: 403, description: 'Forbidden.' }) 101 | @Delete(':slug/favorite') 102 | async unFavorite(@User('id') userId: number, @Param('slug') slug) { 103 | return await this.articleService.unFavorite(userId, slug); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Node/Express/Mongoose Example App](project-logo.png) 2 | 3 | [![Build Status](https://travis-ci.org/anishkny/node-express-realworld-example-app.svg?branch=master)](https://travis-ci.org/anishkny/node-express-realworld-example-app) 4 | 5 | > ### NestJS codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the [RealWorld](https://github.com/gothinkster/realworld-example-apps) API spec. 6 | 7 | 8 | ---------- 9 | 10 | # Getting started 11 | 12 | ## Installation 13 | 14 | Clone the repository 15 | 16 | git clone https://github.com/lujakob/nestjs-realworld-example-app.git 17 | 18 | Switch to the repo folder 19 | 20 | cd nestjs-realworld-example-app 21 | 22 | Install dependencies 23 | 24 | npm install 25 | 26 | Copy config file and set JsonWebToken secret key 27 | 28 | cp src/config.ts.example src/config.ts 29 | 30 | ---------- 31 | 32 | ## Database 33 | 34 | The codebase contains examples of two different database abstractions, namely [TypeORM](http://typeorm.io/) and [Prisma](https://www.prisma.io/). 35 | 36 | The branch `master` implements TypeORM with a mySQL database. 37 | 38 | The branch `prisma` implements Prisma with a mySQL database. 39 | 40 | ---------- 41 | 42 | ##### TypeORM 43 | 44 | ---------- 45 | 46 | Create a new mysql database with the name `nestjsrealworld`\ 47 | (or the name you specified in the ormconfig.json) 48 | 49 | Copy TypeORM config example file for database settings 50 | 51 | cp ormconfig.json.example 52 | 53 | Set mysql database settings in ormconfig.json 54 | 55 | { 56 | "type": "mysql", 57 | "host": "localhost", 58 | "port": 3306, 59 | "username": "your-mysql-username", 60 | "password": "your-mysql-password", 61 | "database": "nestjsrealworld", 62 | "entities": ["src/**/**.entity{.ts,.js}"], 63 | "synchronize": true 64 | } 65 | 66 | Start local mysql server and create new database 'nestjsrealworld' 67 | 68 | On application start, tables for all entities will be created. 69 | 70 | ---------- 71 | 72 | ##### Prisma 73 | 74 | ---------- 75 | 76 | To run the example with Prisma checkout branch `prisma`, remove the node_modules and run `npm install` 77 | 78 | Create a new mysql database with the name `nestjsrealworld-prisma` (or the name you specified in `prisma/.env`) 79 | 80 | Copy prisma config example file for database settings 81 | 82 | cp prisma/.env.example prisma/.env 83 | 84 | Set mysql database settings in prisma/.env 85 | 86 | DATABASE_URL="mysql://USER:PASSWORD@HOST:PORT/DATABASE" 87 | 88 | To create all tables in the new database make the database migration from the prisma schema defined in prisma/schema.prisma 89 | 90 | npx prisma migrate save --experimental 91 | npx prisma migrate up --experimental 92 | 93 | Now generate the prisma client from the migrated database with the following command 94 | 95 | npx prisma generate 96 | 97 | The database tables are now set up and the prisma client is generated. For more information see the docs: 98 | 99 | - https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project-typescript-mysql 100 | 101 | 102 | ---------- 103 | 104 | ## NPM scripts 105 | 106 | - `npm start` - Start application 107 | - `npm run start:watch` - Start application in watch mode 108 | - `npm run test` - run Jest test runner 109 | - `npm run start:prod` - Build application 110 | 111 | ---------- 112 | 113 | ## API Specification 114 | 115 | This application adheres to the api specifications set by the [Thinkster](https://github.com/gothinkster) team. This helps mix and match any backend with any other frontend without conflicts. 116 | 117 | > [Full API Spec](https://github.com/gothinkster/realworld/tree/master/api) 118 | 119 | More information regarding the project can be found here https://github.com/gothinkster/realworld 120 | 121 | ---------- 122 | 123 | ## Start application 124 | 125 | - `npm start` 126 | - Test api with `http://localhost:3000/api/articles` in your favourite browser 127 | 128 | ---------- 129 | 130 | # Authentication 131 | 132 | This applications uses JSON Web Token (JWT) to handle authentication. The token is passed with each request using the `Authorization` header with `Token` scheme. The JWT authentication middleware handles the validation and authentication of the token. Please check the following sources to learn more about JWT. 133 | 134 | ---------- 135 | 136 | # Swagger API docs 137 | 138 | This example repo uses the NestJS swagger module for API documentation. [NestJS Swagger](https://github.com/nestjs/swagger) - [www.swagger.io](https://swagger.io/) 139 | -------------------------------------------------------------------------------- /dist/user/user.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const user_service_1 = require("./user.service"); 25 | const dto_1 = require("./dto"); 26 | const http_exception_1 = require("@nestjs/common/exceptions/http.exception"); 27 | const user_decorator_1 = require("./user.decorator"); 28 | const validation_pipe_1 = require("../shared/pipes/validation.pipe"); 29 | const swagger_1 = require("@nestjs/swagger"); 30 | let UserController = class UserController { 31 | constructor(userService) { 32 | this.userService = userService; 33 | } 34 | findMe(email) { 35 | return __awaiter(this, void 0, void 0, function* () { 36 | return yield this.userService.findByEmail(email); 37 | }); 38 | } 39 | update(userId, userData) { 40 | return __awaiter(this, void 0, void 0, function* () { 41 | return yield this.userService.update(userId, userData); 42 | }); 43 | } 44 | create(userData) { 45 | return __awaiter(this, void 0, void 0, function* () { 46 | return this.userService.create(userData); 47 | }); 48 | } 49 | delete(params) { 50 | return __awaiter(this, void 0, void 0, function* () { 51 | return yield this.userService.delete(params.slug); 52 | }); 53 | } 54 | login(loginUserDto) { 55 | return __awaiter(this, void 0, void 0, function* () { 56 | const _user = yield this.userService.findOne(loginUserDto); 57 | const errors = { User: ' not found' }; 58 | if (!_user) 59 | throw new http_exception_1.HttpException({ errors }, 401); 60 | const token = yield this.userService.generateJWT(_user); 61 | const { email, username, bio, image } = _user; 62 | const user = { email, token, username, bio, image }; 63 | return { user }; 64 | }); 65 | } 66 | }; 67 | __decorate([ 68 | common_1.Get('user'), 69 | __param(0, user_decorator_1.User('email')), 70 | __metadata("design:type", Function), 71 | __metadata("design:paramtypes", [String]), 72 | __metadata("design:returntype", Promise) 73 | ], UserController.prototype, "findMe", null); 74 | __decorate([ 75 | common_1.Put('user'), 76 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Body('user')), 77 | __metadata("design:type", Function), 78 | __metadata("design:paramtypes", [Number, dto_1.UpdateUserDto]), 79 | __metadata("design:returntype", Promise) 80 | ], UserController.prototype, "update", null); 81 | __decorate([ 82 | common_1.UsePipes(new validation_pipe_1.ValidationPipe()), 83 | common_1.Post('users'), 84 | __param(0, common_1.Body('user')), 85 | __metadata("design:type", Function), 86 | __metadata("design:paramtypes", [dto_1.CreateUserDto]), 87 | __metadata("design:returntype", Promise) 88 | ], UserController.prototype, "create", null); 89 | __decorate([ 90 | common_1.Delete('users/:slug'), 91 | __param(0, common_1.Param()), 92 | __metadata("design:type", Function), 93 | __metadata("design:paramtypes", [Object]), 94 | __metadata("design:returntype", Promise) 95 | ], UserController.prototype, "delete", null); 96 | __decorate([ 97 | common_1.UsePipes(new validation_pipe_1.ValidationPipe()), 98 | common_1.Post('users/login'), 99 | __param(0, common_1.Body('user')), 100 | __metadata("design:type", Function), 101 | __metadata("design:paramtypes", [dto_1.LoginUserDto]), 102 | __metadata("design:returntype", Promise) 103 | ], UserController.prototype, "login", null); 104 | UserController = __decorate([ 105 | swagger_1.ApiBearerAuth(), 106 | swagger_1.ApiUseTags('user'), 107 | common_1.Controller(), 108 | __metadata("design:paramtypes", [user_service_1.UserService]) 109 | ], UserController); 110 | exports.UserController = UserController; 111 | //# sourceMappingURL=user.controller.js.map -------------------------------------------------------------------------------- /dist/profile/profile.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const typeorm_1 = require("@nestjs/typeorm"); 25 | const typeorm_2 = require("typeorm"); 26 | const user_entity_1 = require("../user/user.entity"); 27 | const follows_entity_1 = require("./follows.entity"); 28 | const http_exception_1 = require("@nestjs/common/exceptions/http.exception"); 29 | let ProfileService = class ProfileService { 30 | constructor(userRepository, followsRepository) { 31 | this.userRepository = userRepository; 32 | this.followsRepository = followsRepository; 33 | } 34 | findAll() { 35 | return __awaiter(this, void 0, void 0, function* () { 36 | return yield this.userRepository.find(); 37 | }); 38 | } 39 | findOne(options) { 40 | return __awaiter(this, void 0, void 0, function* () { 41 | const user = yield this.userRepository.findOne(options); 42 | delete user.id; 43 | if (user) 44 | delete user.password; 45 | return { profile: user }; 46 | }); 47 | } 48 | findProfile(id, followingUsername) { 49 | return __awaiter(this, void 0, void 0, function* () { 50 | const _profile = yield this.userRepository.findOne({ username: followingUsername }); 51 | if (!_profile) 52 | return; 53 | let profile = { 54 | username: _profile.username, 55 | bio: _profile.bio, 56 | image: _profile.image 57 | }; 58 | const follows = yield this.followsRepository.findOne({ followerId: id, followingId: _profile.id }); 59 | if (id) { 60 | profile.following = !!follows; 61 | } 62 | return { profile }; 63 | }); 64 | } 65 | follow(followerEmail, username) { 66 | return __awaiter(this, void 0, void 0, function* () { 67 | if (!followerEmail || !username) { 68 | throw new http_exception_1.HttpException('Follower email and username not provided.', common_1.HttpStatus.BAD_REQUEST); 69 | } 70 | const followingUser = yield this.userRepository.findOne({ username }); 71 | const followerUser = yield this.userRepository.findOne({ email: followerEmail }); 72 | if (followingUser.email === followerEmail) { 73 | throw new http_exception_1.HttpException('FollowerEmail and FollowingId cannot be equal.', common_1.HttpStatus.BAD_REQUEST); 74 | } 75 | const _follows = yield this.followsRepository.findOne({ followerId: followerUser.id, followingId: followingUser.id }); 76 | if (!_follows) { 77 | const follows = new follows_entity_1.FollowsEntity(); 78 | follows.followerId = followerUser.id; 79 | follows.followingId = followingUser.id; 80 | yield this.followsRepository.save(follows); 81 | } 82 | let profile = { 83 | username: followingUser.username, 84 | bio: followingUser.bio, 85 | image: followingUser.image, 86 | following: true 87 | }; 88 | return { profile }; 89 | }); 90 | } 91 | unFollow(followerId, username) { 92 | return __awaiter(this, void 0, void 0, function* () { 93 | if (!followerId || !username) { 94 | throw new http_exception_1.HttpException('FollowerId and username not provided.', common_1.HttpStatus.BAD_REQUEST); 95 | } 96 | const followingUser = yield this.userRepository.findOne({ username }); 97 | if (followingUser.id === followerId) { 98 | throw new http_exception_1.HttpException('FollowerId and FollowingId cannot be equal.', common_1.HttpStatus.BAD_REQUEST); 99 | } 100 | const followingId = followingUser.id; 101 | yield this.followsRepository.delete({ followerId, followingId }); 102 | let profile = { 103 | username: followingUser.username, 104 | bio: followingUser.bio, 105 | image: followingUser.image, 106 | following: false 107 | }; 108 | return { profile }; 109 | }); 110 | } 111 | }; 112 | ProfileService = __decorate([ 113 | common_1.Injectable(), 114 | __param(0, typeorm_1.InjectRepository(user_entity_1.UserEntity)), 115 | __param(1, typeorm_1.InjectRepository(follows_entity_1.FollowsEntity)), 116 | __metadata("design:paramtypes", [typeorm_2.Repository, 117 | typeorm_2.Repository]) 118 | ], ProfileService); 119 | exports.ProfileService = ProfileService; 120 | //# sourceMappingURL=profile.service.js.map -------------------------------------------------------------------------------- /dist/article/article.service.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"article.service.js","sourceRoot":"","sources":["../../src/article/article.service.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/user/user.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const typeorm_1 = require("@nestjs/typeorm"); 25 | const typeorm_2 = require("typeorm"); 26 | const user_entity_1 = require("./user.entity"); 27 | const jwt = require('jsonwebtoken'); 28 | const config_1 = require("../config"); 29 | const class_validator_1 = require("class-validator"); 30 | const http_exception_1 = require("@nestjs/common/exceptions/http.exception"); 31 | const common_2 = require("@nestjs/common"); 32 | const argon2 = require("argon2"); 33 | let UserService = class UserService { 34 | constructor(userRepository) { 35 | this.userRepository = userRepository; 36 | } 37 | findAll() { 38 | return __awaiter(this, void 0, void 0, function* () { 39 | return yield this.userRepository.find(); 40 | }); 41 | } 42 | findOne({ email, password }) { 43 | return __awaiter(this, void 0, void 0, function* () { 44 | const user = yield this.userRepository.findOne({ email }); 45 | if (!user) { 46 | return null; 47 | } 48 | if (yield argon2.verify(user.password, password)) { 49 | return user; 50 | } 51 | return null; 52 | }); 53 | } 54 | create(dto) { 55 | return __awaiter(this, void 0, void 0, function* () { 56 | const { username, email, password } = dto; 57 | const qb = yield typeorm_2.getRepository(user_entity_1.UserEntity) 58 | .createQueryBuilder('user') 59 | .where('user.username = :username', { username }) 60 | .orWhere('user.email = :email', { email }); 61 | const user = yield qb.getOne(); 62 | if (user) { 63 | const errors = { username: 'Username and email must be unique.' }; 64 | throw new http_exception_1.HttpException({ message: 'Input data validation failed', errors }, common_2.HttpStatus.BAD_REQUEST); 65 | } 66 | let newUser = new user_entity_1.UserEntity(); 67 | newUser.username = username; 68 | newUser.email = email; 69 | newUser.password = password; 70 | newUser.articles = []; 71 | const errors = yield class_validator_1.validate(newUser); 72 | if (errors.length > 0) { 73 | const _errors = { username: 'Userinput is not valid.' }; 74 | throw new http_exception_1.HttpException({ message: 'Input data validation failed', _errors }, common_2.HttpStatus.BAD_REQUEST); 75 | } 76 | else { 77 | const savedUser = yield this.userRepository.save(newUser); 78 | return this.buildUserRO(savedUser); 79 | } 80 | }); 81 | } 82 | update(id, dto) { 83 | return __awaiter(this, void 0, void 0, function* () { 84 | let toUpdate = yield this.userRepository.findOne(id); 85 | delete toUpdate.password; 86 | delete toUpdate.favorites; 87 | let updated = Object.assign(toUpdate, dto); 88 | return yield this.userRepository.save(updated); 89 | }); 90 | } 91 | delete(email) { 92 | return __awaiter(this, void 0, void 0, function* () { 93 | return yield this.userRepository.delete({ email: email }); 94 | }); 95 | } 96 | findById(id) { 97 | return __awaiter(this, void 0, void 0, function* () { 98 | const user = yield this.userRepository.findOne(id); 99 | if (!user) { 100 | const errors = { User: ' not found' }; 101 | throw new http_exception_1.HttpException({ errors }, 401); 102 | } 103 | return this.buildUserRO(user); 104 | }); 105 | } 106 | findByEmail(email) { 107 | return __awaiter(this, void 0, void 0, function* () { 108 | const user = yield this.userRepository.findOne({ email: email }); 109 | return this.buildUserRO(user); 110 | }); 111 | } 112 | generateJWT(user) { 113 | let today = new Date(); 114 | let exp = new Date(today); 115 | exp.setDate(today.getDate() + 60); 116 | return jwt.sign({ 117 | id: user.id, 118 | username: user.username, 119 | email: user.email, 120 | exp: exp.getTime() / 1000, 121 | }, config_1.SECRET); 122 | } 123 | ; 124 | buildUserRO(user) { 125 | const userRO = { 126 | id: user.id, 127 | username: user.username, 128 | email: user.email, 129 | bio: user.bio, 130 | token: this.generateJWT(user), 131 | image: user.image 132 | }; 133 | return { user: userRO }; 134 | } 135 | }; 136 | UserService = __decorate([ 137 | common_1.Injectable(), 138 | __param(0, typeorm_1.InjectRepository(user_entity_1.UserEntity)), 139 | __metadata("design:paramtypes", [typeorm_2.Repository]) 140 | ], UserService); 141 | exports.UserService = UserService; 142 | //# sourceMappingURL=user.service.js.map -------------------------------------------------------------------------------- /src/article/article.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository, getRepository, DeleteResult } from 'typeorm'; 4 | import { ArticleEntity } from './article.entity'; 5 | import { Comment } from './comment.entity'; 6 | import { UserEntity } from '../user/user.entity'; 7 | import { FollowsEntity } from '../profile/follows.entity'; 8 | import { CreateArticleDto } from './dto'; 9 | 10 | import {ArticleRO, ArticlesRO, CommentsRO} from './article.interface'; 11 | const slug = require('slug'); 12 | 13 | @Injectable() 14 | export class ArticleService { 15 | constructor( 16 | @InjectRepository(ArticleEntity) 17 | private readonly articleRepository: Repository, 18 | @InjectRepository(Comment) 19 | private readonly commentRepository: Repository, 20 | @InjectRepository(UserEntity) 21 | private readonly userRepository: Repository, 22 | @InjectRepository(FollowsEntity) 23 | private readonly followsRepository: Repository 24 | ) {} 25 | 26 | async findAll(query): Promise { 27 | 28 | const qb = await getRepository(ArticleEntity) 29 | .createQueryBuilder('article') 30 | .leftJoinAndSelect('article.author', 'author'); 31 | 32 | qb.where("1 = 1"); 33 | 34 | if ('tag' in query) { 35 | qb.andWhere("article.tagList LIKE :tag", { tag: `%${query.tag}%` }); 36 | } 37 | 38 | if ('author' in query) { 39 | const author = await this.userRepository.findOne({username: query.author}); 40 | qb.andWhere("article.authorId = :id", { id: author.id }); 41 | } 42 | 43 | if ('favorited' in query) { 44 | const author = await this.userRepository.findOne({username: query.favorited}); 45 | const ids = author.favorites.map(el => el.id); 46 | qb.andWhere("article.authorId IN (:ids)", { ids }); 47 | } 48 | 49 | qb.orderBy('article.created', 'DESC'); 50 | 51 | const articlesCount = await qb.getCount(); 52 | 53 | if ('limit' in query) { 54 | qb.limit(query.limit); 55 | } 56 | 57 | if ('offset' in query) { 58 | qb.offset(query.offset); 59 | } 60 | 61 | const articles = await qb.getMany(); 62 | 63 | return {articles, articlesCount}; 64 | } 65 | 66 | async findFeed(userId: number, query): Promise { 67 | const _follows = await this.followsRepository.find( {followerId: userId}); 68 | 69 | if (!(Array.isArray(_follows) && _follows.length > 0)) { 70 | return {articles: [], articlesCount: 0}; 71 | } 72 | 73 | const ids = _follows.map(el => el.followingId); 74 | 75 | const qb = await getRepository(ArticleEntity) 76 | .createQueryBuilder('article') 77 | .where('article.authorId IN (:ids)', { ids }); 78 | 79 | qb.orderBy('article.created', 'DESC'); 80 | 81 | const articlesCount = await qb.getCount(); 82 | 83 | if ('limit' in query) { 84 | qb.limit(query.limit); 85 | } 86 | 87 | if ('offset' in query) { 88 | qb.offset(query.offset); 89 | } 90 | 91 | const articles = await qb.getMany(); 92 | 93 | return {articles, articlesCount}; 94 | } 95 | 96 | async findOne(where): Promise { 97 | const article = await this.articleRepository.findOne(where); 98 | return {article}; 99 | } 100 | 101 | async addComment(slug: string, commentData): Promise { 102 | let article = await this.articleRepository.findOne({slug}); 103 | 104 | const comment = new Comment(); 105 | comment.body = commentData.body; 106 | 107 | article.comments.push(comment); 108 | 109 | await this.commentRepository.save(comment); 110 | article = await this.articleRepository.save(article); 111 | return {article} 112 | } 113 | 114 | async deleteComment(slug: string, id: string): Promise { 115 | let article = await this.articleRepository.findOne({slug}); 116 | 117 | const comment = await this.commentRepository.findOne(id); 118 | const deleteIndex = article.comments.findIndex(_comment => _comment.id === comment.id); 119 | 120 | if (deleteIndex >= 0) { 121 | const deleteComments = article.comments.splice(deleteIndex, 1); 122 | await this.commentRepository.delete(deleteComments[0].id); 123 | article = await this.articleRepository.save(article); 124 | return {article}; 125 | } else { 126 | return {article}; 127 | } 128 | 129 | } 130 | 131 | async favorite(id: number, slug: string): Promise { 132 | let article = await this.articleRepository.findOne({slug}); 133 | const user = await this.userRepository.findOne(id); 134 | 135 | const isNewFavorite = user.favorites.findIndex(_article => _article.id === article.id) < 0; 136 | if (isNewFavorite) { 137 | user.favorites.push(article); 138 | article.favoriteCount++; 139 | 140 | await this.userRepository.save(user); 141 | article = await this.articleRepository.save(article); 142 | } 143 | 144 | return {article}; 145 | } 146 | 147 | async unFavorite(id: number, slug: string): Promise { 148 | let article = await this.articleRepository.findOne({slug}); 149 | const user = await this.userRepository.findOne(id); 150 | 151 | const deleteIndex = user.favorites.findIndex(_article => _article.id === article.id); 152 | 153 | if (deleteIndex >= 0) { 154 | 155 | user.favorites.splice(deleteIndex, 1); 156 | article.favoriteCount--; 157 | 158 | await this.userRepository.save(user); 159 | article = await this.articleRepository.save(article); 160 | } 161 | 162 | return {article}; 163 | } 164 | 165 | async findComments(slug: string): Promise { 166 | const article = await this.articleRepository.findOne({slug}); 167 | return {comments: article.comments}; 168 | } 169 | 170 | async create(userId: number, articleData: CreateArticleDto): Promise { 171 | 172 | let article = new ArticleEntity(); 173 | article.title = articleData.title; 174 | article.description = articleData.description; 175 | article.slug = this.slugify(articleData.title); 176 | article.tagList = articleData.tagList || []; 177 | article.comments = []; 178 | 179 | const newArticle = await this.articleRepository.save(article); 180 | 181 | const author = await this.userRepository.findOne({ where: { id: userId }, relations: ['articles'] }); 182 | author.articles.push(article); 183 | 184 | await this.userRepository.save(author); 185 | 186 | return newArticle; 187 | 188 | } 189 | 190 | async update(slug: string, articleData: any): Promise { 191 | let toUpdate = await this.articleRepository.findOne({ slug: slug}); 192 | let updated = Object.assign(toUpdate, articleData); 193 | const article = await this.articleRepository.save(updated); 194 | return {article}; 195 | } 196 | 197 | async delete(slug: string): Promise { 198 | return await this.articleRepository.delete({ slug: slug}); 199 | } 200 | 201 | slugify(title: string) { 202 | return slug(title, {lower: true}) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36) 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /dist/article/article.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const article_service_1 = require("./article.service"); 25 | const dto_1 = require("./dto"); 26 | const user_decorator_1 = require("../user/user.decorator"); 27 | const swagger_1 = require("@nestjs/swagger"); 28 | let ArticleController = class ArticleController { 29 | constructor(articleService) { 30 | this.articleService = articleService; 31 | } 32 | findAll(query) { 33 | return __awaiter(this, void 0, void 0, function* () { 34 | return yield this.articleService.findAll(query); 35 | }); 36 | } 37 | getFeed(userId, query) { 38 | return __awaiter(this, void 0, void 0, function* () { 39 | return yield this.articleService.findFeed(userId, query); 40 | }); 41 | } 42 | findOne(slug) { 43 | return __awaiter(this, void 0, void 0, function* () { 44 | return yield this.articleService.findOne({ slug }); 45 | }); 46 | } 47 | findComments(slug) { 48 | return __awaiter(this, void 0, void 0, function* () { 49 | return yield this.articleService.findComments(slug); 50 | }); 51 | } 52 | create(userId, articleData) { 53 | return __awaiter(this, void 0, void 0, function* () { 54 | return this.articleService.create(userId, articleData); 55 | }); 56 | } 57 | update(params, articleData) { 58 | return __awaiter(this, void 0, void 0, function* () { 59 | return this.articleService.update(params.slug, articleData); 60 | }); 61 | } 62 | delete(params) { 63 | return __awaiter(this, void 0, void 0, function* () { 64 | return this.articleService.delete(params.slug); 65 | }); 66 | } 67 | createComment(slug, commentData) { 68 | return __awaiter(this, void 0, void 0, function* () { 69 | return yield this.articleService.addComment(slug, commentData); 70 | }); 71 | } 72 | deleteComment(params) { 73 | return __awaiter(this, void 0, void 0, function* () { 74 | const { slug, id } = params; 75 | return yield this.articleService.deleteComment(slug, id); 76 | }); 77 | } 78 | favorite(userId, slug) { 79 | return __awaiter(this, void 0, void 0, function* () { 80 | return yield this.articleService.favorite(userId, slug); 81 | }); 82 | } 83 | unFavorite(userId, slug) { 84 | return __awaiter(this, void 0, void 0, function* () { 85 | return yield this.articleService.unFavorite(userId, slug); 86 | }); 87 | } 88 | }; 89 | __decorate([ 90 | swagger_1.ApiOperation({ title: 'Get all articles' }), 91 | swagger_1.ApiResponse({ status: 200, description: 'Return all articles.' }), 92 | common_1.Get(), 93 | __param(0, common_1.Query()), 94 | __metadata("design:type", Function), 95 | __metadata("design:paramtypes", [Object]), 96 | __metadata("design:returntype", Promise) 97 | ], ArticleController.prototype, "findAll", null); 98 | __decorate([ 99 | swagger_1.ApiOperation({ title: 'Get article feed' }), 100 | swagger_1.ApiResponse({ status: 200, description: 'Return article feed.' }), 101 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 102 | common_1.Get('feed'), 103 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Query()), 104 | __metadata("design:type", Function), 105 | __metadata("design:paramtypes", [Number, Object]), 106 | __metadata("design:returntype", Promise) 107 | ], ArticleController.prototype, "getFeed", null); 108 | __decorate([ 109 | common_1.Get(':slug'), 110 | __param(0, common_1.Param('slug')), 111 | __metadata("design:type", Function), 112 | __metadata("design:paramtypes", [Object]), 113 | __metadata("design:returntype", Promise) 114 | ], ArticleController.prototype, "findOne", null); 115 | __decorate([ 116 | common_1.Get(':slug/comments'), 117 | __param(0, common_1.Param('slug')), 118 | __metadata("design:type", Function), 119 | __metadata("design:paramtypes", [Object]), 120 | __metadata("design:returntype", Promise) 121 | ], ArticleController.prototype, "findComments", null); 122 | __decorate([ 123 | swagger_1.ApiOperation({ title: 'Create article' }), 124 | swagger_1.ApiResponse({ status: 201, description: 'The article has been successfully created.' }), 125 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 126 | common_1.Post(), 127 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Body('article')), 128 | __metadata("design:type", Function), 129 | __metadata("design:paramtypes", [Number, dto_1.CreateArticleDto]), 130 | __metadata("design:returntype", Promise) 131 | ], ArticleController.prototype, "create", null); 132 | __decorate([ 133 | swagger_1.ApiOperation({ title: 'Update article' }), 134 | swagger_1.ApiResponse({ status: 201, description: 'The article has been successfully updated.' }), 135 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 136 | common_1.Put(':slug'), 137 | __param(0, common_1.Param()), __param(1, common_1.Body('article')), 138 | __metadata("design:type", Function), 139 | __metadata("design:paramtypes", [Object, dto_1.CreateArticleDto]), 140 | __metadata("design:returntype", Promise) 141 | ], ArticleController.prototype, "update", null); 142 | __decorate([ 143 | swagger_1.ApiOperation({ title: 'Delete article' }), 144 | swagger_1.ApiResponse({ status: 201, description: 'The article has been successfully deleted.' }), 145 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 146 | common_1.Delete(':slug'), 147 | __param(0, common_1.Param()), 148 | __metadata("design:type", Function), 149 | __metadata("design:paramtypes", [Object]), 150 | __metadata("design:returntype", Promise) 151 | ], ArticleController.prototype, "delete", null); 152 | __decorate([ 153 | swagger_1.ApiOperation({ title: 'Create comment' }), 154 | swagger_1.ApiResponse({ status: 201, description: 'The comment has been successfully created.' }), 155 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 156 | common_1.Post(':slug/comments'), 157 | __param(0, common_1.Param('slug')), __param(1, common_1.Body('comment')), 158 | __metadata("design:type", Function), 159 | __metadata("design:paramtypes", [Object, dto_1.CreateCommentDto]), 160 | __metadata("design:returntype", Promise) 161 | ], ArticleController.prototype, "createComment", null); 162 | __decorate([ 163 | swagger_1.ApiOperation({ title: 'Delete comment' }), 164 | swagger_1.ApiResponse({ status: 201, description: 'The article has been successfully deleted.' }), 165 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 166 | common_1.Delete(':slug/comments/:id'), 167 | __param(0, common_1.Param()), 168 | __metadata("design:type", Function), 169 | __metadata("design:paramtypes", [Object]), 170 | __metadata("design:returntype", Promise) 171 | ], ArticleController.prototype, "deleteComment", null); 172 | __decorate([ 173 | swagger_1.ApiOperation({ title: 'Favorite article' }), 174 | swagger_1.ApiResponse({ status: 201, description: 'The article has been successfully favorited.' }), 175 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 176 | common_1.Post(':slug/favorite'), 177 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Param('slug')), 178 | __metadata("design:type", Function), 179 | __metadata("design:paramtypes", [Number, Object]), 180 | __metadata("design:returntype", Promise) 181 | ], ArticleController.prototype, "favorite", null); 182 | __decorate([ 183 | swagger_1.ApiOperation({ title: 'Unfavorite article' }), 184 | swagger_1.ApiResponse({ status: 201, description: 'The article has been successfully unfavorited.' }), 185 | swagger_1.ApiResponse({ status: 403, description: 'Forbidden.' }), 186 | common_1.Delete(':slug/favorite'), 187 | __param(0, user_decorator_1.User('id')), __param(1, common_1.Param('slug')), 188 | __metadata("design:type", Function), 189 | __metadata("design:paramtypes", [Number, Object]), 190 | __metadata("design:returntype", Promise) 191 | ], ArticleController.prototype, "unFavorite", null); 192 | ArticleController = __decorate([ 193 | swagger_1.ApiBearerAuth(), 194 | swagger_1.ApiUseTags('articles'), 195 | common_1.Controller('articles'), 196 | __metadata("design:paramtypes", [article_service_1.ArticleService]) 197 | ], ArticleController); 198 | exports.ArticleController = ArticleController; 199 | //# sourceMappingURL=article.controller.js.map -------------------------------------------------------------------------------- /dist/article/article.service.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 3 | var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 4 | if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 5 | else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 6 | return c > 3 && r && Object.defineProperty(target, key, r), r; 7 | }; 8 | var __metadata = (this && this.__metadata) || function (k, v) { 9 | if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); 10 | }; 11 | var __param = (this && this.__param) || function (paramIndex, decorator) { 12 | return function (target, key) { decorator(target, key, paramIndex); } 13 | }; 14 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 15 | return new (P || (P = Promise))(function (resolve, reject) { 16 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 17 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 18 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } 19 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 20 | }); 21 | }; 22 | Object.defineProperty(exports, "__esModule", { value: true }); 23 | const common_1 = require("@nestjs/common"); 24 | const typeorm_1 = require("@nestjs/typeorm"); 25 | const typeorm_2 = require("typeorm"); 26 | const article_entity_1 = require("./article.entity"); 27 | const comment_entity_1 = require("./comment.entity"); 28 | const user_entity_1 = require("../user/user.entity"); 29 | const follows_entity_1 = require("../profile/follows.entity"); 30 | const slug = require('slug'); 31 | let ArticleService = class ArticleService { 32 | constructor(articleRepository, commentRepository, userRepository, followsRepository) { 33 | this.articleRepository = articleRepository; 34 | this.commentRepository = commentRepository; 35 | this.userRepository = userRepository; 36 | this.followsRepository = followsRepository; 37 | } 38 | findAll(query) { 39 | return __awaiter(this, void 0, void 0, function* () { 40 | const qb = yield typeorm_2.getRepository(article_entity_1.ArticleEntity) 41 | .createQueryBuilder('article') 42 | .leftJoinAndSelect('article.author', 'author'); 43 | qb.where("1 = 1"); 44 | if ('tag' in query) { 45 | qb.andWhere("article.tagList LIKE :tag", { tag: `%${query.tag}%` }); 46 | } 47 | if ('author' in query) { 48 | const author = yield this.userRepository.findOne({ username: query.author }); 49 | qb.andWhere("article.authorId = :id", { id: author.id }); 50 | } 51 | if ('favorited' in query) { 52 | const author = yield this.userRepository.findOne({ username: query.favorited }); 53 | const ids = author.favorites.map(el => el.id); 54 | qb.andWhere("article.authorId IN (:ids)", { ids }); 55 | } 56 | qb.orderBy('article.created', 'DESC'); 57 | const articlesCount = yield qb.getCount(); 58 | if ('limit' in query) { 59 | qb.limit(query.limit); 60 | } 61 | if ('offset' in query) { 62 | qb.offset(query.offset); 63 | } 64 | const articles = yield qb.getMany(); 65 | return { articles, articlesCount }; 66 | }); 67 | } 68 | findFeed(userId, query) { 69 | return __awaiter(this, void 0, void 0, function* () { 70 | const _follows = yield this.followsRepository.find({ followerId: userId }); 71 | if (!(Array.isArray(_follows) && _follows.length > 0)) { 72 | return { articles: [], articlesCount: 0 }; 73 | } 74 | const ids = _follows.map(el => el.followingId); 75 | const qb = yield typeorm_2.getRepository(article_entity_1.ArticleEntity) 76 | .createQueryBuilder('article') 77 | .where('article.authorId IN (:ids)', { ids }); 78 | qb.orderBy('article.created', 'DESC'); 79 | const articlesCount = yield qb.getCount(); 80 | if ('limit' in query) { 81 | qb.limit(query.limit); 82 | } 83 | if ('offset' in query) { 84 | qb.offset(query.offset); 85 | } 86 | const articles = yield qb.getMany(); 87 | return { articles, articlesCount }; 88 | }); 89 | } 90 | findOne(where) { 91 | return __awaiter(this, void 0, void 0, function* () { 92 | const article = yield this.articleRepository.findOne(where); 93 | return { article }; 94 | }); 95 | } 96 | addComment(slug, commentData) { 97 | return __awaiter(this, void 0, void 0, function* () { 98 | let article = yield this.articleRepository.findOne({ slug }); 99 | const comment = new comment_entity_1.Comment(); 100 | comment.body = commentData.body; 101 | article.comments.push(comment); 102 | yield this.commentRepository.save(comment); 103 | article = yield this.articleRepository.save(article); 104 | return { article }; 105 | }); 106 | } 107 | deleteComment(slug, id) { 108 | return __awaiter(this, void 0, void 0, function* () { 109 | let article = yield this.articleRepository.findOne({ slug }); 110 | const comment = yield this.commentRepository.findOne(id); 111 | const deleteIndex = article.comments.findIndex(_comment => _comment.id === comment.id); 112 | if (deleteIndex >= 0) { 113 | const deleteComments = article.comments.splice(deleteIndex, 1); 114 | yield this.commentRepository.delete(deleteComments[0].id); 115 | article = yield this.articleRepository.save(article); 116 | return { article }; 117 | } 118 | else { 119 | return { article }; 120 | } 121 | }); 122 | } 123 | favorite(id, slug) { 124 | return __awaiter(this, void 0, void 0, function* () { 125 | let article = yield this.articleRepository.findOne({ slug }); 126 | const user = yield this.userRepository.findOne(id); 127 | const isNewFavorite = user.favorites.findIndex(_article => _article.id === article.id) < 0; 128 | if (isNewFavorite) { 129 | user.favorites.push(article); 130 | article.favoriteCount++; 131 | yield this.userRepository.save(user); 132 | article = yield this.articleRepository.save(article); 133 | } 134 | return { article }; 135 | }); 136 | } 137 | unFavorite(id, slug) { 138 | return __awaiter(this, void 0, void 0, function* () { 139 | let article = yield this.articleRepository.findOne({ slug }); 140 | const user = yield this.userRepository.findOne(id); 141 | const deleteIndex = user.favorites.findIndex(_article => _article.id === article.id); 142 | if (deleteIndex >= 0) { 143 | user.favorites.splice(deleteIndex, 1); 144 | article.favoriteCount--; 145 | yield this.userRepository.save(user); 146 | article = yield this.articleRepository.save(article); 147 | } 148 | return { article }; 149 | }); 150 | } 151 | findComments(slug) { 152 | return __awaiter(this, void 0, void 0, function* () { 153 | const article = yield this.articleRepository.findOne({ slug }); 154 | return { comments: article.comments }; 155 | }); 156 | } 157 | create(userId, articleData) { 158 | return __awaiter(this, void 0, void 0, function* () { 159 | let article = new article_entity_1.ArticleEntity(); 160 | article.title = articleData.title; 161 | article.description = articleData.description; 162 | article.slug = this.slugify(articleData.title); 163 | article.tagList = articleData.tagList || []; 164 | article.comments = []; 165 | const newArticle = yield this.articleRepository.save(article); 166 | const author = yield this.userRepository.findOne({ where: { id: userId } }); 167 | if (Array.isArray(author.articles)) { 168 | author.articles.push(article); 169 | } 170 | else { 171 | author.articles = [article]; 172 | } 173 | yield this.userRepository.save(author); 174 | return newArticle; 175 | }); 176 | } 177 | update(slug, articleData) { 178 | return __awaiter(this, void 0, void 0, function* () { 179 | let toUpdate = yield this.articleRepository.findOne({ slug: slug }); 180 | let updated = Object.assign(toUpdate, articleData); 181 | const article = yield this.articleRepository.save(updated); 182 | return { article }; 183 | }); 184 | } 185 | delete(slug) { 186 | return __awaiter(this, void 0, void 0, function* () { 187 | return yield this.articleRepository.delete({ slug: slug }); 188 | }); 189 | } 190 | slugify(title) { 191 | return slug(title, { lower: true }) + '-' + (Math.random() * Math.pow(36, 6) | 0).toString(36); 192 | } 193 | }; 194 | ArticleService = __decorate([ 195 | common_1.Injectable(), 196 | __param(0, typeorm_1.InjectRepository(article_entity_1.ArticleEntity)), 197 | __param(1, typeorm_1.InjectRepository(comment_entity_1.Comment)), 198 | __param(2, typeorm_1.InjectRepository(user_entity_1.UserEntity)), 199 | __param(3, typeorm_1.InjectRepository(follows_entity_1.FollowsEntity)), 200 | __metadata("design:paramtypes", [typeorm_2.Repository, 201 | typeorm_2.Repository, 202 | typeorm_2.Repository, 203 | typeorm_2.Repository]) 204 | ], ArticleService); 205 | exports.ArticleService = ArticleService; 206 | //# sourceMappingURL=article.service.js.map --------------------------------------------------------------------------------