Repository: bhaidar/nestjs-todo-app
Branch: master
Commit: 9b9c01f4f0a9
Files: 149
Total size: 108.3 KB
Directory structure:
gitextract_f_7atx7c/
├── .gitignore
├── .vscode/
│ └── launch.json
├── README.md
├── server/
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── db/
│ │ └── initdb.d/
│ │ └── init-users-db.sh
│ ├── docker-compose.yml
│ ├── nest-cli.json
│ ├── nodemon-debug.json
│ ├── nodemon.json
│ ├── ormconfig.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── auth/
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── interfaces/
│ │ │ │ ├── login-status.interface.ts
│ │ │ │ ├── payload.interface.ts
│ │ │ │ └── regisration-status.interface.ts
│ │ │ └── jwt.strategy.ts
│ │ ├── core/
│ │ │ ├── core.module.ts
│ │ │ └── http-exception.filter.ts
│ │ ├── main.ts
│ │ ├── migration/
│ │ │ ├── 1551865385236-InitialCreate.ts
│ │ │ ├── 1552392671960-AddUpdatedOnFieldToTodoEntity.ts
│ │ │ ├── 1555148302681-AddUser.ts
│ │ │ ├── 1555166680617-AddOwnerFieldToTodoEntity.ts
│ │ │ └── 1565812987671-SeedUserRecord.ts
│ │ ├── mock/
│ │ │ └── todos.mock.ts
│ │ ├── shared/
│ │ │ ├── mapper.ts
│ │ │ └── utils.ts
│ │ ├── todo/
│ │ │ ├── dto/
│ │ │ │ ├── task.create.dto.ts
│ │ │ │ ├── task.dto.ts
│ │ │ │ ├── task.list.dto.ts
│ │ │ │ ├── todo.create.dto.ts
│ │ │ │ ├── todo.dto.ts
│ │ │ │ └── todo.list.dto.ts
│ │ │ ├── entity/
│ │ │ │ ├── task.entity.ts
│ │ │ │ └── todo.entity.ts
│ │ │ ├── task/
│ │ │ │ ├── task.controller.spec.ts
│ │ │ │ ├── task.controller.ts
│ │ │ │ ├── task.service.spec.ts
│ │ │ │ └── task.service.ts
│ │ │ ├── todo.controller.spec.ts
│ │ │ ├── todo.controller.ts
│ │ │ ├── todo.module.ts
│ │ │ ├── todo.service.spec.ts
│ │ │ └── todo.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ ├── user-login.dto.ts
│ │ │ ├── user.create.dto.ts
│ │ │ └── user.dto.ts
│ │ ├── entity/
│ │ │ └── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tslint.json
└── todo-client/
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── browserslist
├── e2e/
│ ├── protractor.conf.js
│ ├── src/
│ │ ├── app.e2e-spec.ts
│ │ └── app.po.ts
│ └── tsconfig.json
├── karma.conf.js
├── package.json
├── projects/
│ ├── app-common/
│ │ ├── README.md
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── lib/
│ │ │ │ ├── action.ts
│ │ │ │ └── app-common.module.ts
│ │ │ ├── public-api.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── auth/
│ │ ├── README.md
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── lib/
│ │ │ │ ├── auth.guard.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── components/
│ │ │ │ │ └── login/
│ │ │ │ │ ├── login.component.css
│ │ │ │ │ ├── login.component.html
│ │ │ │ │ └── login.component.ts
│ │ │ │ └── services/
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── error.interceptor.ts
│ │ │ │ └── jwt-interceptor.ts
│ │ │ ├── public-api.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ └── todo/
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src/
│ │ ├── lib/
│ │ │ ├── components/
│ │ │ │ ├── task-create/
│ │ │ │ │ └── task-create.component.ts
│ │ │ │ ├── task-list/
│ │ │ │ │ └── task-list.component.ts
│ │ │ │ ├── task.component.ts
│ │ │ │ ├── todo-create/
│ │ │ │ │ └── todo-create.component.ts
│ │ │ │ ├── todo-list/
│ │ │ │ │ └── todo-list.component.ts
│ │ │ │ └── todo.component.ts
│ │ │ ├── models/
│ │ │ │ ├── task.model.ts
│ │ │ │ └── todo.model.ts
│ │ │ ├── services/
│ │ │ │ ├── task.service.ts
│ │ │ │ └── todo.service.ts
│ │ │ ├── todo-home.component.ts
│ │ │ ├── todo.module.ts
│ │ │ └── todo.service.spec.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── proxy.conf.json
├── src/
│ ├── app/
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── shared/
│ │ ├── home/
│ │ │ └── home.component.ts
│ │ └── master/
│ │ ├── master.component.html
│ │ └── master.component.ts
│ ├── assets/
│ │ └── .gitkeep
│ ├── environments/
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ └── test.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/node,windows,visualstudiocode
# Edit at https://www.gitignore.io/?templates=node,windows,visualstudiocode
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.gitignore.io/api/node,windows,visualstudiocode
dist/
.DS_Store
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/server/src/auth/auth.module.ts",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
================================================
FILE: README.md
================================================
# nestjs-todo-app
A full stack application written in Nest.js, Angular and PostgreSQL.
================================================
FILE: server/.gitignore
================================================
.idea/
.vscode/
.DS_Store
node_modules/
build/
tmp/
temp/
================================================
FILE: server/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: server/README.md
================================================
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
</p>
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
[travis-url]: https://travis-ci.org/nestjs/nest
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
[linux-url]: https://travis-ci.org/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#5" alt="Coverage" /></a>
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-dc3d53.svg"/></a>
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[](https://opencollective.com/nest#backer)
[](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: server/db/initdb.d/init-users-db.sh
================================================
#!/usr/bin/env bash
set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER todo;
CREATE DATABASE todo_db ENCODING UTF8;
GRANT ALL PRIVILEGES ON DATABASE todo_db TO todo;
ALTER USER todo WITH PASSWORD 'password123';
ALTER USER todo WITH SUPERUSER;
EOSQL
================================================
FILE: server/docker-compose.yml
================================================
version: '3'
services:
db:
container_name: todo_db
image: postgres:10.7
volumes:
- ./db/initdb.d:/docker-entrypoint-initdb.d
ports:
- '5445:5432'
================================================
FILE: server/nest-cli.json
================================================
{
"language": "ts",
"collection": "@nestjs/schematics",
"sourceRoot": "src"
}
================================================
FILE: server/nodemon-debug.json
================================================
{
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts"],
"exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts"
}
================================================
FILE: server/nodemon.json
================================================
{
"watch": ["src"],
"ext": "ts",
"ignore": ["src/**/*.spec.ts"],
"exec": "ts-node -r tsconfig-paths/register src/main.ts"
}
================================================
FILE: server/ormconfig.json
================================================
[
{
"name": "production",
"type": "postgres",
"synchronize": false,
"dropSchema": false,
"logging": true
},
{
"name": "development",
"type": "postgres",
"host": "localhost",
"port": 5445,
"username": "todo",
"password": "password123",
"database": "todo_db",
"synchronize": false,
"logging": true,
"entities": ["src/**/*.entity.ts"],
"migrations": ["src/migration/**/*.ts"],
"subscribers": ["src/subscriber/**/*.ts"],
"cli": {
"entitiesDir": "src/**/*.entity.ts",
"migrationsDir": "src/migration",
"subscribersDir": "src/subscriber"
}
},
{
"type": "postgres",
"host": "localhost",
"port": 5445,
"username": "todo",
"password": "password123",
"database": "todo_db",
"synchronize": false,
"logging": true,
"entities": ["src/**/*.entity.ts"],
"migrations": ["src/migration/**/*.ts"],
"subscribers": ["src/subscriber/**/*.ts"],
"cli": {
"entitiesDir": "src",
"migrationsDir": "src/migration",
"subscribersDir": "src/subscriber"
}
}
]
================================================
FILE: server/package.json
================================================
{
"name": "server",
"version": "1.0.0",
"description": "A nest todo app",
"author": "Bilal Haidar",
"license": "MIT",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"format": "prettier --write \"src/**/*.ts\"",
"run:services": "docker-compose up -d && exit 0",
"stop:services": "docker-compose kill",
"start": "ts-node src/index.ts",
"start:dev": "nodemon",
"start:debug": "nodemon --config nodemon-debug.json",
"typeorm:cli": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js -f ormconfig.json",
"db:migration:generate": "npm-run-all -s -l clean build:server && yarn typeorm:cli migration:generate",
"db:migration:create": "yarn typeorm:cli migration:create",
"db:migrate": "npm-run-all -s -l clean build:server && yarn typeorm:cli migration:run",
"query": "yarn typeorm:cli query",
"prestart:prod": "rimraf dist && npm run build",
"start:prod": "node dist/main.js",
"lint": "tslint -p tsconfig.json -c tslint.json",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "6.2.3",
"@nestjs/core": "6.2.3",
"@nestjs/jwt": "6.1.0",
"@nestjs/passport": "6.1.0",
"@nestjs/platform-express": "6.2.3",
"@nestjs/typeorm": "^6.1.1",
"bcrypt": "^3.0.6",
"class-transformer": "0.2.3",
"class-validator": "0.9.1",
"cookie-parser": "^1.4.4",
"cors": "^2.8.5",
"csurf": "^1.9.0",
"dotenv": "7.0.0",
"express-rate-limit": "^3.4.0",
"global": "^4.3.2",
"helmet": "^3.16.0",
"passport": "^0.4.0",
"passport-http-bearer": "^1.0.1",
"passport-jwt": "^4.0.0",
"pg": "7.9.0",
"reflect-metadata": "0.1.12",
"rxjs": "6.4.0",
"typeorm": "0.2.16",
"uuid": "3.3.2"
},
"devDependencies": {
"@nestjs/testing": "6.2.4",
"@types/bcrypt": "^3.0.0",
"@types/cookie-parser": "^1.4.1",
"@types/cors": "^2.8.4",
"@types/csurf": "^1.9.35",
"@types/dotenv": "6.1.1",
"@types/express": "4.16.1",
"@types/express-rate-limit": "^3.3.0",
"@types/helmet": "^0.0.43",
"@types/jest": "24.0.11",
"@types/node": "10.14.4",
"@types/passport": "^1.0.0",
"@types/passport-jwt": "^3.0.1",
"@types/supertest": "2.0.7",
"@types/uuid": "3.4.4",
"jest": "^23.5.0",
"nodemon": "1.18.11",
"prettier": "1.17.0",
"supertest": "^3.1.0",
"ts-jest": "^23.1.3",
"ts-loader": "5.3.3",
"ts-node": "8.0.3",
"tsconfig-paths": "3.8.0",
"tslint": "5.15.0",
"typescript": "3.4.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: server/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get<AppController>(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: server/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: server/src/app.module.ts
================================================
import { Module, DynamicModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoModule } from './todo/todo.module';
import { ConnectionOptions } from 'typeorm';
import { UsersModule } from './users/users.module';
import { CoreModule } from './core/core.module';
import { AuthModule } from './auth/auth.module';
@Module({})
export class AppModule {
static forRoot(connOptions: ConnectionOptions): DynamicModule {
return {
module: AppModule,
controllers: [AppController],
imports: [
AuthModule,
TodoModule,
UsersModule,
CoreModule,
TypeOrmModule.forRoot(connOptions),
],
providers: [AppService],
};
}
}
================================================
FILE: server/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
================================================
FILE: server/src/auth/auth.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('Auth Controller', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get<AuthController>(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: server/src/auth/auth.controller.ts
================================================
import {
Controller,
Body,
Post,
HttpException,
HttpStatus,
UsePipes,
Get,
Req,
UseGuards,
} from '@nestjs/common';
import { CreateUserDto } from '@user/dto/user.create.dto';
import { RegistrationStatus } from './interfaces/regisration-status.interface';
import { AuthService } from './auth.service';
import { LoginStatus } from './interfaces/login-status.interface';
import { LoginUserDto } from '../users/dto/user-login.dto';
import { JwtPayload } from './interfaces/payload.interface';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('register')
public async register(
@Body() createUserDto: CreateUserDto,
): Promise<RegistrationStatus> {
const result: RegistrationStatus = await this.authService.register(
createUserDto,
);
if (!result.success) {
throw new HttpException(result.message, HttpStatus.BAD_REQUEST);
}
return result;
}
@Post('login')
public async login(@Body() loginUserDto: LoginUserDto): Promise<LoginStatus> {
return await this.authService.login(loginUserDto);
}
@Get('whoami')
@UseGuards(AuthGuard())
public async testAuth(@Req() req: any): Promise<JwtPayload> {
return req.user;
}
}
================================================
FILE: server/src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UsersModule } from '@user/users.module';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
UsersModule,
PassportModule.register({
defaultStrategy: 'jwt',
property: 'user',
session: false,
}),
JwtModule.register({
secret: process.env.SECRETKEY,
signOptions: {
expiresIn: process.env.EXPIRESIN,
},
}),
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [PassportModule, JwtModule],
})
export class AuthModule {}
================================================
FILE: server/src/auth/auth.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get<AuthService>(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: server/src/auth/auth.service.ts
================================================
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { CreateUserDto } from '@user/dto/user.create.dto';
import { RegistrationStatus } from './interfaces/regisration-status.interface';
import { UsersService } from '@user/users.service';
import { LoginStatus } from './interfaces/login-status.interface';
import { LoginUserDto } from '../users/dto/user-login.dto';
import { UserDto } from '@user/dto/user.dto';
import { JwtPayload } from './interfaces/payload.interface';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private readonly usersService: UsersService,
private readonly jwtService: JwtService,
) {}
async register(userDto: CreateUserDto): Promise<RegistrationStatus> {
let status: RegistrationStatus = {
success: true,
message: 'user registered',
};
try {
await this.usersService.create(userDto);
} catch (err) {
status = {
success: false,
message: err,
};
}
return status;
}
async login(loginUserDto: LoginUserDto): Promise<LoginStatus> {
// find user in db
const user = await this.usersService.findByLogin(loginUserDto);
// generate and sign token
const token = this._createToken(user);
return {
username: user.username,
...token,
};
}
async validateUser(payload: JwtPayload): Promise<UserDto> {
const user = await this.usersService.findByPayload(payload);
if (!user) {
throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
}
return user;
}
private _createToken({ username }: UserDto): any {
const expiresIn = process.env.EXPIRESIN;
const user: JwtPayload = { username };
const accessToken = this.jwtService.sign(user);
return {
expiresIn,
accessToken,
};
}
}
================================================
FILE: server/src/auth/interfaces/login-status.interface.ts
================================================
import { UserDto } from '@user/dto/user.dto';
export interface LoginStatus {
username: string;
accessToken: any;
expiresIn: any;
}
================================================
FILE: server/src/auth/interfaces/payload.interface.ts
================================================
export interface JwtPayload {
username: string;
}
================================================
FILE: server/src/auth/interfaces/regisration-status.interface.ts
================================================
export interface RegistrationStatus {
success: boolean;
message: string;
}
================================================
FILE: server/src/auth/jwt.strategy.ts
================================================
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { JwtPayload } from './interfaces/payload.interface';
import { UserDto } from '@user/dto/user.dto';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.SECRETKEY,
});
}
async validate(payload: JwtPayload): Promise<UserDto> {
const user = await this.authService.validateUser(payload);
if (!user) {
throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
}
return user;
}
}
================================================
FILE: server/src/core/core.module.ts
================================================
import { Module } from '@nestjs/common';
import { HttpExceptionFilter } from './http-exception.filter';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class CoreModule {}
================================================
FILE: server/src/core/http-exception.filter.ts
================================================
import {
ExceptionFilter,
HttpException,
HttpStatus,
ArgumentsHost,
Catch,
Logger,
} from '@nestjs/common';
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const errorResponse = {
code: status,
timestamp: new Date().toLocaleDateString(),
path: request.url,
method: request.method,
message:
status !== HttpStatus.INTERNAL_SERVER_ERROR
? exception.message.error || exception.message || null
: 'Internal server error',
};
Logger.error(
`${request.method} ${request.url}`,
exception.stack,
'HttpExceptionFilter',
);
response.status(status).json(errorResponse);
}
}
================================================
FILE: server/src/main.ts
================================================
import 'dotenv/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger, ValidationPipe } from '@nestjs/common';
import { getDbConnectionOptions, runDbMigrations } from '@shared/utils';
import * as helmet from 'helmet';
import * as rateLimit from 'express-rate-limit';
const port = process.env.PORT;
async function bootstrap() {
const app = await NestFactory.create(
AppModule.forRoot(await getDbConnectionOptions(process.env.NODE_ENV)),
{
// logger: Boolean(process.env.ENABLELOGGING),
logger: console,
},
);
/**
* Helmet can help protect your app from some well-known
* web vulnerabilities by setting HTTP headers appropriately.
* Generally, Helmet is just a collection of 12 smaller
* middleware functions that set security-related HTTP headers
*
* https://github.com/helmetjs/helmet#how-it-works
*/
app.use(helmet());
app.enableCors();
// /**
// * we need this because "cookie" is true in csrfProtection
// */
// app.use(cookieParser());
// app.use(csurf({ cookie: true }));
/**
* To protect your applications from brute-force attacks
*/
app.use(
new rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
}),
);
/**
* Apply validation for all inputs globally
*/
app.useGlobalPipes(
new ValidationPipe({
/**
* Strip away all none-object existing properties
*/
whitelist: true,
/***
* Transform input objects to their corresponding DTO objects
*/
transform: true,
}),
);
/**
* Run DB migrations
*/
await runDbMigrations();
await app.listen(port);
Logger.log(`Server started running on http://localhost:${port}`, 'Bootstrap');
}
bootstrap();
================================================
FILE: server/src/migration/1551865385236-InitialCreate.ts
================================================
import {MigrationInterface, QueryRunner} from "typeorm";
export class InitialCreate1551865385236 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`CREATE TABLE "todo" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "description" text, "createdOn" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_d429b7114371f6a35c5cb4776a7" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "task" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "createdOn" TIMESTAMP NOT NULL DEFAULT now(), "todoId" uuid, CONSTRAINT "PK_fb213f79ee45060ba925ecd576e" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "task" ADD CONSTRAINT "FK_91440d017e7b30d2ac16a27d762" FOREIGN KEY ("todoId") REFERENCES "todo"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "task" DROP CONSTRAINT "FK_91440d017e7b30d2ac16a27d762"`);
await queryRunner.query(`DROP TABLE "task"`);
await queryRunner.query(`DROP TABLE "todo"`);
}
}
================================================
FILE: server/src/migration/1552392671960-AddUpdatedOnFieldToTodoEntity.ts
================================================
import {MigrationInterface, QueryRunner} from "typeorm";
export class AddUpdatedOnFieldToTodoEntity1552392671960 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "todo" ADD "updatedOn" TIMESTAMP NOT NULL DEFAULT now()`);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "todo" DROP COLUMN "updatedOn"`);
}
}
================================================
FILE: server/src/migration/1555148302681-AddUser.ts
================================================
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddUser1555148302681 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(
// tslint:disable-next-line: max-line-length
`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "username" character varying NOT NULL, "password" character varying NOT NULL, "email" character varying NOT NULL, "createdOn" TIMESTAMP NOT NULL DEFAULT now(), "updatedOn" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_78a916df40e02a9deb1c4b75edb" UNIQUE ("username"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`DROP TABLE "users"`);
}
}
================================================
FILE: server/src/migration/1555166680617-AddOwnerFieldToTodoEntity.ts
================================================
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddOwnerFieldToTodoEntity1555166680617
implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(`ALTER TABLE "todo" ADD "ownerId" uuid`);
await queryRunner.query(
// tslint:disable-next-line: max-line-length
`ALTER TABLE "todo" ADD CONSTRAINT "FK_05552e862619dc4ad7ec8fc9cb8" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.query(
`ALTER TABLE "todo" DROP CONSTRAINT "FK_05552e862619dc4ad7ec8fc9cb8"`,
);
await queryRunner.query(`ALTER TABLE "todo" DROP COLUMN "ownerId"`);
}
}
================================================
FILE: server/src/migration/1565812987671-SeedUserRecord.ts
================================================
// https://github.com/typeorm/typeorm/blob/master/docs/transactions.md
import { MigrationInterface, QueryRunner } from 'typeorm';
import { UserEntity } from '../users/entity/user.entity';
export class SeedUserRecord1565812987671 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
const userRepo = queryRunner.manager.getRepository(UserEntity);
const user = userRepo.create({
username: 'bhaidar',
password: '@dF%^hGb03W~',
email: 'bhaidar@gmail.com',
});
await userRepo.save(user);
}
// tslint:disable-next-line: no-empty
public async down(queryRunner: QueryRunner): Promise<any> {}
}
================================================
FILE: server/src/mock/todos.mock.ts
================================================
import { TodoEntity } from 'src/todo/entity/todo.entity';
export const todos: TodoEntity[] = [
{
id: 'eac400ba-3c78-11e9-b210-d663bd873d93',
name: 'Supermarket Todo list',
tasks: [
{
id: 'b91a5400-3cce-11e9-b210-d663bd873d93',
name: 'Bring coffee',
},
{
id: 'b91a56c6-3cce-11e9-b210-d663bd873d93',
name: 'Bring banana',
},
],
},
{
id: 'eac40736-3c78-11e9-b210-d663bd873d93',
name: 'Office Todo list',
tasks: [
{
id: 'b91a5a90-3cce-11e9-b210-d663bd873d93',
name: 'Bring chairs',
},
{
id: 'b91a5bf8-3cce-11e9-b210-d663bd873d93',
name: 'Bring tables',
},
],
},
{
id: 'eac408d0-3c78-11e9-b210-d663bd873d93',
name: 'Traveling Todo list',
},
{
id: 'eac40a7e-3c78-11e9-b210-d663bd873d93',
name: 'Studying Todo list',
},
{
id: 'eac40c90-3c78-11e9-b210-d663bd873d93',
name: 'Monday Todo list',
},
{
id: '5ea5f9ed-dd64-4e08-bdb6-d3d5-354fe48',
name: 'My awesome todo list',
description: 'A new awesome and cool todo list',
},
];
================================================
FILE: server/src/shared/mapper.ts
================================================
import { TaskDto } from '@todo/dto/task.dto';
import { TodoEntity } from '@todo/entity/todo.entity';
import { TodoDto } from '@todo/dto/todo.dto';
import { TaskEntity } from '@todo/entity/task.entity';
import { UserEntity } from '@user/entity/user.entity';
import { UserDto } from '@user/dto/user.dto';
export const toTodoDto = (data: TodoEntity): TodoDto => {
const { id, name, description, tasks, owner } = data;
let todoDto: TodoDto = {
id,
name,
description,
owner: owner ? toUserDto(owner) : null,
};
if (tasks) {
todoDto = {
...todoDto,
tasks: tasks.map((task: TaskEntity) => toTaskDto(task)),
};
}
return todoDto;
};
export const toTaskDto = (data: TaskEntity): TaskDto => {
const { id, name } = data;
let taskDto: TaskDto = {
id,
name,
};
return taskDto;
};
export const toUserDto = (data: UserEntity): UserDto => {
const { id, username, email } = data;
let userDto: UserDto = {
id,
username,
email,
};
return userDto;
};
================================================
FILE: server/src/shared/utils.ts
================================================
import { getConnectionOptions, getConnection } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { Logger } from '@nestjs/common';
export const toPromise = <T>(data: T): Promise<T> => {
return new Promise<T>(resolve => {
resolve(data);
});
};
export const getDbConnectionOptions = async (
connectionName: string = 'default',
) => {
const options = await getConnectionOptions(
process.env.NODE_ENV || 'development',
);
return {
...options,
name: connectionName,
};
};
export const getDbConnection = async (connectionName: string = 'default') => {
return await getConnection(connectionName);
};
export const runDbMigrations = async (connectionName: string = 'default') => {
const conn = await getDbConnection(connectionName);
await conn.runMigrations();
};
export const comparePasswords = async (userPassword, currentPassword) => {
return await bcrypt.compare(currentPassword, userPassword);
};
================================================
FILE: server/src/todo/dto/task.create.dto.ts
================================================
import { IsNotEmpty } from 'class-validator';
export class CreateTaskDto {
@IsNotEmpty()
name: string;
}
================================================
FILE: server/src/todo/dto/task.dto.ts
================================================
import { IsNotEmpty, IsString } from 'class-validator';
export class TaskDto {
@IsNotEmpty()
id: string;
@IsNotEmpty()
@IsString()
name: string;
createdOn?: Date;
}
================================================
FILE: server/src/todo/dto/task.list.dto.ts
================================================
import { TaskDto } from './task.dto';
export class TaskListDto {
tasks: TaskDto[];
}
================================================
FILE: server/src/todo/dto/todo.create.dto.ts
================================================
import { IsNotEmpty, MaxLength, IsOptional } from 'class-validator';
import { UserDto } from '@user/dto/user.dto';
export class CreateTodoDto {
@IsNotEmpty()
name: string;
@IsOptional()
@MaxLength(500)
description?: string;
}
================================================
FILE: server/src/todo/dto/todo.dto.ts
================================================
import { TaskDto } from './task.dto';
import { IsNotEmpty } from 'class-validator';
import { UserDto } from '@user/dto/user.dto';
export class TodoDto {
@IsNotEmpty()
id: string;
@IsNotEmpty()
name: string;
createdOn?: Date;
description?: string;
owner: UserDto;
tasks?: TaskDto[];
}
================================================
FILE: server/src/todo/dto/todo.list.dto.ts
================================================
import { TodoDto } from './todo.dto';
export class TodoListDto {
todos: TodoDto[];
}
================================================
FILE: server/src/todo/entity/task.entity.ts
================================================
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
ManyToOne,
} from 'typeorm';
import { TodoEntity } from '@todo/entity/todo.entity';
@Entity('task')
export class TaskEntity {
@PrimaryGeneratedColumn('uuid') id: string;
@Column({ type: 'varchar', nullable: false }) name: string;
@CreateDateColumn() createdOn?: Date;
@ManyToOne(type => TodoEntity, todo => todo.tasks)
todo?: TodoEntity;
}
================================================
FILE: server/src/todo/entity/todo.entity.ts
================================================
import { TaskEntity } from '@todo/entity/task.entity';
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
OneToMany,
ManyToOne,
JoinTable,
} from 'typeorm';
import { UserEntity } from '@user/entity/user.entity';
@Entity('todo')
export class TodoEntity {
@PrimaryGeneratedColumn('uuid') id: string;
@Column({ type: 'varchar', nullable: false }) name: string;
@Column({ type: 'text', nullable: true }) description?: string;
@CreateDateColumn() createdOn?: Date;
@CreateDateColumn() updatedOn?: Date;
@ManyToOne(type => UserEntity)
owner?: UserEntity;
@OneToMany(type => TaskEntity, task => task.todo)
tasks?: TaskEntity[];
}
================================================
FILE: server/src/todo/task/task.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { TaskController } from './task.controller';
describe('Task Controller', () => {
let controller: TaskController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [TaskController],
}).compile();
controller = module.get<TaskController>(TaskController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: server/src/todo/task/task.controller.ts
================================================
import {
Controller,
Param,
Get,
Post,
Body,
Delete,
UsePipes,
UseGuards,
} from '@nestjs/common';
import { TaskService } from './task.service';
import { TaskListDto } from '../dto/task.list.dto';
import { TaskDto } from '../dto/task.dto';
import { CreateTaskDto } from '@todo/dto/task.create.dto';
import { AuthGuard } from '@nestjs/passport';
@Controller('api/tasks')
export class TaskController {
constructor(private taskService: TaskService) {}
@Get(':id')
async findOneTask(@Param('id') id: string): Promise<TaskDto> {
return await this.taskService.getTask(id);
}
@Get('todo/:id')
async findTasksByTodo(@Param('id') id: string): Promise<TaskListDto> {
const tasks = await this.taskService.getTasksByTodo(id);
return { tasks };
}
@Post('todo/:id')
@UseGuards(AuthGuard())
async create(
@Param('id') todo: string,
@Body() createTaskDto: CreateTaskDto,
): Promise<TaskDto> {
return await this.taskService.createTask(todo, createTaskDto);
}
@Delete(':id')
@UseGuards(AuthGuard())
async destory(@Param('id') id: string): Promise<TaskDto> {
return await this.taskService.destoryTask(id);
}
}
================================================
FILE: server/src/todo/task/task.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { TaskService } from './task.service';
describe('TaskService', () => {
let service: TaskService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TaskService],
}).compile();
service = module.get<TaskService>(TaskService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: server/src/todo/task/task.service.ts
================================================
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { CreateTaskDto } from '../dto/task.create.dto';
import { TaskDto } from '../dto/task.dto';
import { TaskEntity } from '@todo/entity/task.entity';
import { toTaskDto } from '@shared/mapper';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TodoEntity } from '@todo/entity/todo.entity';
@Injectable()
export class TaskService {
constructor(
@InjectRepository(TaskEntity)
private readonly taskRepo: Repository<TaskEntity>,
@InjectRepository(TodoEntity)
private readonly todoRepo: Repository<TodoEntity>,
) {}
async getTask(id: string): Promise<TaskDto> {
const task: TaskEntity = await this.taskRepo.findOne({ where: { id } });
if (!task) {
throw new HttpException(`Task doesn't exist`, HttpStatus.BAD_REQUEST);
}
return toTaskDto(task);
}
async getTasksByTodo(id: string): Promise<TaskDto[]> {
const tasks: TaskEntity[] = await this.taskRepo.find({
where: { todo: { id } },
relations: ['todo'],
});
return tasks.map(task => toTaskDto(task));
}
async createTask(todoId: string, taskDto: CreateTaskDto): Promise<TaskDto> {
const { name } = taskDto;
const todo: TodoEntity = await this.todoRepo.findOne({
where: { id: todoId },
relations: ['tasks', 'owner'],
});
const task: TaskEntity = await this.taskRepo.create({
name,
todo,
});
await this.taskRepo.save(task);
return toTaskDto(task);
}
async destoryTask(id: string): Promise<TaskDto> {
const task: TaskEntity = await this.taskRepo.findOne({ where: { id } });
if (!task) {
throw new HttpException(`Task doesn't exist`, HttpStatus.BAD_REQUEST);
}
await this.taskRepo.delete({ id });
return toTaskDto(task);
}
}
================================================
FILE: server/src/todo/todo.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { TodoController } from './todo.controller';
describe('Todo Controller', () => {
let controller: TodoController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [TodoController],
}).compile();
controller = module.get<TodoController>(TodoController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: server/src/todo/todo.controller.ts
================================================
import {
Controller,
Get,
Param,
Post,
Body,
Put,
Delete,
UsePipes,
UseGuards,
Req,
} from '@nestjs/common';
import { TodoListDto } from './dto/todo.list.dto';
import { TodoDto } from './dto/todo.dto';
import { CreateTodoDto } from './dto/todo.create.dto';
import { TodoService } from './todo.service';
import { AuthGuard } from '@nestjs/passport';
import { UserDto } from '@user/dto/user.dto';
@Controller('api/todos')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Get()
async findAll(@Req() req: any): Promise<TodoListDto> {
const todos = await this.todoService.getAllTodo();
return { todos };
}
@Get(':id')
async findOne(@Param('id') id: string): Promise<TodoDto> {
return await this.todoService.getOneTodo(id);
}
@Post()
@UseGuards(AuthGuard())
async create(
@Body() createTodoDto: CreateTodoDto,
@Req() req: any,
): Promise<TodoDto> {
const user = req.user as UserDto;
return await this.todoService.createTodo(user, createTodoDto);
}
@Put(':id')
@UseGuards(AuthGuard())
async update(
@Param('id') id: string,
@Body() todoDto: TodoDto,
): Promise<TodoDto> {
return await this.todoService.updateTodo(id, todoDto);
}
@Delete(':id')
@UseGuards(AuthGuard())
async destory(@Param('id') id: string): Promise<TodoDto> {
return await this.todoService.destoryTodo(id);
}
}
================================================
FILE: server/src/todo/todo.module.ts
================================================
import { Module } from '@nestjs/common';
import { TodoController } from './todo.controller';
import { TodoService } from './todo.service';
import { TaskController } from './task/task.controller';
import { TaskService } from './task/task.service';
import { TodoEntity } from '@todo/entity/todo.entity';
import { TaskEntity } from '@todo/entity/task.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserEntity } from '@user/entity/user.entity';
import { UsersModule } from '@user/users.module';
import { AuthModule } from 'src/auth/auth.module';
@Module({
imports: [
UsersModule,
AuthModule,
TypeOrmModule.forFeature([TodoEntity, TaskEntity, UserEntity]),
],
controllers: [TodoController, TaskController],
providers: [TodoService, TaskService],
})
export class TodoModule {}
================================================
FILE: server/src/todo/todo.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { TodoService } from './todo.service';
describe('TodoService', () => {
let service: TodoService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TodoService],
}).compile();
service = module.get<TodoService>(TodoService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: server/src/todo/todo.service.ts
================================================
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { TodoEntity } from '@todo/entity/todo.entity';
import { TodoDto } from './dto/todo.dto';
import { toTodoDto } from '@shared/mapper';
import { CreateTodoDto } from './dto/todo.create.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserDto } from '@user/dto/user.dto';
import { UsersService } from '@user/users.service';
@Injectable()
export class TodoService {
constructor(
@InjectRepository(TodoEntity)
private readonly todoRepo: Repository<TodoEntity>,
private readonly usersService: UsersService,
) {}
async getAllTodo(): Promise<TodoDto[]> {
const todos = await this.todoRepo.find({ relations: ['tasks', 'owner'] });
return todos.map(todo => toTodoDto(todo));
}
async getOneTodo(id: string): Promise<TodoDto> {
const todo = await this.todoRepo.findOne({
where: { id },
relations: ['tasks', 'owner'],
});
if (!todo) {
throw new HttpException(
`Todo list doesn't exist`,
HttpStatus.BAD_REQUEST,
);
}
return toTodoDto(todo);
}
async createTodo(
{ username }: UserDto,
createTodoDto: CreateTodoDto,
): Promise<TodoDto> {
const { name, description } = createTodoDto;
// get the user from db
const owner = await this.usersService.findOne({ where: { username } });
const todo: TodoEntity = await this.todoRepo.create({
name,
description,
owner,
});
await this.todoRepo.save(todo);
return toTodoDto(todo);
}
async updateTodo(id: string, todoDto: TodoDto): Promise<TodoDto> {
const { name, description } = todoDto;
let todo: TodoEntity = await this.todoRepo.findOne({ where: { id } });
if (!todo) {
throw new HttpException(
`Todo list doesn't exist`,
HttpStatus.BAD_REQUEST,
);
}
todo = {
id,
name,
description,
};
await this.todoRepo.update({ id }, todo); // update
todo = await this.todoRepo.findOne({
where: { id },
relations: ['tasks', 'owner'],
}); // re-query
return toTodoDto(todo);
}
async destoryTodo(id: string): Promise<TodoDto> {
const todo: TodoEntity = await this.todoRepo.findOne({
where: { id },
relations: ['tasks', 'owner'],
});
if (!todo) {
throw new HttpException(
`Todo list doesn't exist`,
HttpStatus.BAD_REQUEST,
);
}
if (todo.tasks && todo.tasks.length > 0) {
throw new HttpException(
`Cannot delete this Todo list, it has existing tasks`,
HttpStatus.FORBIDDEN,
);
}
await this.todoRepo.delete({ id }); // delete todo list
return toTodoDto(todo);
}
}
================================================
FILE: server/src/users/dto/user-login.dto.ts
================================================
import { IsNotEmpty } from 'class-validator';
export class LoginUserDto {
@IsNotEmpty()
readonly username: string;
@IsNotEmpty()
readonly password: string;
}
================================================
FILE: server/src/users/dto/user.create.dto.ts
================================================
import { IsNotEmpty, IsEmail } from 'class-validator';
export class CreateUserDto {
@IsNotEmpty()
username: string;
@IsNotEmpty()
password: string;
@IsNotEmpty()
@IsEmail()
email: string;
}
================================================
FILE: server/src/users/dto/user.dto.ts
================================================
import { IsNotEmpty, IsEmail } from 'class-validator';
export class UserDto {
@IsNotEmpty()
id: string;
@IsNotEmpty()
username: string;
@IsNotEmpty()
@IsEmail()
email: string;
createdOn?: Date;
}
================================================
FILE: server/src/users/entity/user.entity.ts
================================================
import { TaskEntity } from '@todo/entity/task.entity';
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
OneToMany,
BeforeInsert,
} from 'typeorm';
import * as bcrypt from 'bcrypt';
@Entity('users')
export class UserEntity {
@PrimaryGeneratedColumn('uuid') id: string;
@Column({ type: 'varchar', nullable: false, unique: true }) username: string;
@Column({ type: 'varchar', nullable: false }) password: string;
@Column({ type: 'varchar', nullable: false }) email: string;
@CreateDateColumn() createdOn?: Date;
@CreateDateColumn() updatedOn?: Date;
@BeforeInsert()
async hashPassword() {
this.password = await bcrypt.hash(this.password, 10);
}
}
================================================
FILE: server/src/users/users.module.ts
================================================
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UserEntity } from './entity/user.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [TypeOrmModule.forFeature([UserEntity])],
controllers: [],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
================================================
FILE: server/src/users/users.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: server/src/users/users.service.ts
================================================
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UserDto } from './dto/user.dto';
import { UserEntity } from '@user/entity/user.entity';
import { toUserDto } from '@shared/mapper';
import { CreateUserDto } from './dto/user.create.dto';
import { LoginUserDto } from './dto/user-login.dto';
import { comparePasswords } from '@shared/utils';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(UserEntity)
private readonly userRepo: Repository<UserEntity>,
) {}
async findOne(options?: object): Promise<UserDto> {
const user = await this.userRepo.findOne(options);
return toUserDto(user);
}
async findByLogin({ username, password }: LoginUserDto): Promise<UserDto> {
const user = await this.userRepo.findOne({ where: { username } });
if (!user) {
throw new HttpException('User not found', HttpStatus.UNAUTHORIZED);
}
// compare passwords
const areEqual = await comparePasswords(user.password, password);
if (!areEqual) {
throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
}
return toUserDto(user);
}
async findByPayload({ username }: any): Promise<UserDto> {
return await this.findOne({ where: { username } });
}
async create(userDto: CreateUserDto): Promise<UserDto> {
const { username, password, email } = userDto;
// check if the user exists in the db
const userInDb = await this.userRepo.findOne({ where: { username } });
if (userInDb) {
throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
}
const user: UserEntity = await this.userRepo.create({
username,
password,
email,
});
await this.userRepo.save(user);
return toUserDto(user);
}
private _sanitizeUser(user: UserEntity) {
delete user.password;
return user;
}
}
================================================
FILE: server/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: server/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: server/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "**/*spec.ts"]
}
================================================
FILE: server/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./dist",
"baseUrl": "./",
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true,
"lib": ["es2015"],
"paths": {
"@todo/*": ["src/todo/*"],
"@shared/*": ["src/shared/*"],
"@user/*": ["src/users/*"]
}
},
"exclude": ["node_modules"]
}
================================================
FILE: server/tslint.json
================================================
{
"defaultSeverity": "error",
"extends": ["tslint:recommended"],
"jsRules": {
"no-unused-expression": true
},
"rules": {
"quotemark": [true, "single"],
"member-access": [false],
"ordered-imports": [false],
"max-line-length": [true, 150],
"member-ordering": [false],
"interface-name": [false],
"arrow-parens": false,
"object-literal-sort-keys": false
},
"rulesDirectory": []
}
================================================
FILE: todo-client/.editorconfig
================================================
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: todo-client/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: todo-client/README.md
================================================
# TodoClient
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.1.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: todo-client/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"todo-client": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/todo-client",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/bootstrap/dist/js/bootstrap.min.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "todo-client:build"
},
"configurations": {
"production": {
"browserTarget": "todo-client:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "todo-client:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "todo-client:serve"
},
"configurations": {
"production": {
"devServerTarget": "todo-client:serve:production"
}
}
}
}
},
"auth": {
"projectType": "library",
"root": "projects/auth",
"sourceRoot": "projects/auth/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/auth/tsconfig.lib.json",
"project": "projects/auth/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/auth/src/test.ts",
"tsConfig": "projects/auth/tsconfig.spec.json",
"karmaConfig": "projects/auth/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/auth/tsconfig.lib.json",
"projects/auth/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"app-common": {
"projectType": "library",
"root": "projects/app-common",
"sourceRoot": "projects/app-common/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/app-common/tsconfig.lib.json",
"project": "projects/app-common/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/app-common/src/test.ts",
"tsConfig": "projects/app-common/tsconfig.spec.json",
"karmaConfig": "projects/app-common/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/app-common/tsconfig.lib.json",
"projects/app-common/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"todo": {
"projectType": "library",
"root": "projects/todo",
"sourceRoot": "projects/todo/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/todo/tsconfig.lib.json",
"project": "projects/todo/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/todo/src/test.ts",
"tsConfig": "projects/todo/tsconfig.spec.json",
"karmaConfig": "projects/todo/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/todo/tsconfig.lib.json",
"projects/todo/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "todo-client"
}
================================================
FILE: todo-client/browserslist
================================================
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.
================================================
FILE: todo-client/e2e/protractor.conf.js
================================================
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: todo-client/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to todo-client!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
================================================
FILE: todo-client/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}
================================================
FILE: todo-client/e2e/tsconfig.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: todo-client/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/todo-client'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
================================================
FILE: todo-client/package.json
================================================
{
"name": "todo-client",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.1.1",
"@angular/common": "~8.1.1",
"@angular/compiler": "~8.1.1",
"@angular/core": "~8.1.1",
"@angular/forms": "~8.1.1",
"@angular/platform-browser": "~8.1.1",
"@angular/platform-browser-dynamic": "~8.1.1",
"@angular/router": "~8.1.1",
"bootstrap": "^4.3.1",
"jquery": "^3.4.1",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.801.1",
"@angular-devkit/build-ng-packagr": "~0.801.3",
"@angular/cli": "~8.1.1",
"@angular/compiler-cli": "~8.1.1",
"@angular/language-service": "~8.1.1",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"ng-packagr": "^5.1.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tsickle": "^0.35.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}
================================================
FILE: todo-client/projects/app-common/README.md
================================================
# AppCommon
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.
## Code scaffolding
Run `ng generate component component-name --project app-common` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project app-common`.
> Note: Don't forget to add `--project app-common` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build app-common` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build app-common`, go to the dist folder `cd dist/app-common` and run `npm publish`.
## Running unit tests
Run `ng test app-common` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: todo-client/projects/app-common/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/app-common'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
================================================
FILE: todo-client/projects/app-common/ng-package.json
================================================
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/app-common",
"lib": {
"entryFile": "src/public-api.ts"
}
}
================================================
FILE: todo-client/projects/app-common/package.json
================================================
{
"name": "app-common",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^8.1.3",
"@angular/core": "^8.1.3"
}
}
================================================
FILE: todo-client/projects/app-common/src/lib/action.ts
================================================
export interface DoAction {
type: string;
payload: any;
}
================================================
FILE: todo-client/projects/app-common/src/lib/app-common.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [],
imports: [
CommonModule,
FormsModule,
HttpClientModule,
ReactiveFormsModule,
RouterModule
],
exports: [
CommonModule,
FormsModule,
HttpClientModule,
ReactiveFormsModule,
RouterModule
]
})
export class AppCommonModule {}
================================================
FILE: todo-client/projects/app-common/src/public-api.ts
================================================
/*
* Public API Surface of app-common
*/
export * from './lib/app-common.module';
export * from './lib/action';
================================================
FILE: todo-client/projects/app-common/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: todo-client/projects/app-common/tsconfig.lib.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: todo-client/projects/app-common/tsconfig.spec.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: todo-client/projects/app-common/tslint.json
================================================
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}
================================================
FILE: todo-client/projects/auth/README.md
================================================
# Auth
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.
## Code scaffolding
Run `ng generate component component-name --project auth` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project auth`.
> Note: Don't forget to add `--project auth` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build auth` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build auth`, go to the dist folder `cd dist/auth` and run `npm publish`.
## Running unit tests
Run `ng test auth` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: todo-client/projects/auth/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/auth'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
================================================
FILE: todo-client/projects/auth/ng-package.json
================================================
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/auth",
"lib": {
"entryFile": "src/public-api.ts"
}
}
================================================
FILE: todo-client/projects/auth/package.json
================================================
{
"name": "auth",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^8.1.3",
"@angular/core": "^8.1.3"
}
}
================================================
FILE: todo-client/projects/auth/src/lib/auth.guard.ts
================================================
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
CanActivate,
Router
} from '@angular/router';
import { AuthService } from './services/auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentUser = this.authService.currentUserValue;
if (currentUser) {
// logged in so return true
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
return false;
}
}
================================================
FILE: todo-client/projects/auth/src/lib/auth.module.ts
================================================
import { NgModule } from '@angular/core';
import { LoginComponent } from './components/login/login.component';
import { AppCommonModule } from 'projects/app-common/src/public-api';
@NgModule({
declarations: [LoginComponent],
imports: [AppCommonModule],
exports: [LoginComponent]
})
export class AuthModule {}
================================================
FILE: todo-client/projects/auth/src/lib/components/login/login.component.css
================================================
:host {
display: flex;
justify-content: center;
}
.alert-danger {
background-color: #f44336; /* Red */
}
.alert {
padding: 20px;
color: white;
margin-bottom: 15px;
text-align: center;
}
================================================
FILE: todo-client/projects/auth/src/lib/components/login/login.component.html
================================================
<div class="container my-3">
<div class="row text-center mb-5">
<div class="col-md-12 bg-light p-3">
<h2>Login</h2>
</div>
</div>
<div class="row">
<div class="col-md-6 offset-md-3">
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" autocomplete="off">
<div class="form-group">
<label for="usernam">Username</label>
<input
type="text"
class="form-control"
id="username"
formControlName="username"
placeholder="Username"
/>
<div *ngIf="submitted && f.username.errors">
<small
*ngIf="f.username.errors.required"
class="form-text text-muted"
>Username is required</small
>
</div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
class="form-control"
id="password"
placeholder="Password"
formControlName="password"
/>
<div *ngIf="submitted && f.password.errors">
<small
*ngIf="f.password.errors.required"
class="form-text text-muted"
>Password is required</small
>
</div>
</div>
<div *ngIf="error" class="my-3">
<small class="form-text text-muted">{{ error | json }}</small>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
================================================
FILE: todo-client/projects/auth/src/lib/components/login/login.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { first } from 'rxjs/operators';
import { AuthService } from '../../services/auth.service';
@Component({
templateUrl: 'login.component.html',
styleUrls: ['login.component.css']
})
export class LoginComponent implements OnInit {
loginForm: FormGroup;
submitted = false;
returnUrl: string;
error: string;
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private authService: AuthService
) {}
ngOnInit() {
this.loginForm = this.formBuilder.group({
username: ['bhaidar', Validators.required],
password: ['@dF%^hGb03W~', Validators.required]
});
// reset login status
this.authService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/';
}
get f() {
return this.loginForm.controls;
}
onSubmit() {
this.submitted = true;
// stop here if form is invalid
if (this.loginForm.invalid) {
return;
}
this.authService
.login(this.f.username.value, this.f.password.value)
.pipe(first())
.subscribe(
data => {
this.error = '';
this.router.navigate([this.returnUrl]);
},
error => {
this.error = error;
}
);
}
}
================================================
FILE: todo-client/projects/auth/src/lib/services/auth.service.ts
================================================
/**
* Based on
* https://github.com/cornflourblue/angular-7-jwt-authentication-example
*/
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
export interface ApplicationUser {
accessToken: string;
expiresIn: Date;
username: string;
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
private currentUserSubject: BehaviorSubject<ApplicationUser>;
public currentUser: Observable<ApplicationUser>;
constructor(private readonly http: HttpClient) {
this.currentUserSubject = new BehaviorSubject<ApplicationUser>(
JSON.parse(localStorage.getItem('currentUser'))
);
this.currentUser = this.currentUserSubject.asObservable();
}
public get currentUserValue(): ApplicationUser {
return this.currentUserSubject.value;
}
login(username: string, password: string) {
return this.http.post<any>('/auth/login', { username, password }).pipe(
map(user => {
// login successful if there's a jwt token in the response
if (user && user.accessToken) {
// store; user; details; and; jwt; token in local
// storage; to; keep; user; logged in between; page; refreshes;
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
}
return user;
})
);
}
logout() {
// remove user from local storage to log user out
localStorage.removeItem('currentUser');
this.currentUserSubject.next(null);
}
}
================================================
FILE: todo-client/projects/auth/src/lib/services/error.interceptor.ts
================================================
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpErrorResponse,
HTTP_INTERCEPTORS
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthService } from './auth.service';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((err: HttpErrorResponse) => {
if (err.status === 401 && !window.location.href.includes('/login')) {
// auto logout if 401 response returned from api
this.authService.logout();
location.reload();
}
const error = err.error.error || err.error.message || err.statusText;
alert(error);
return throwError(error);
})
);
}
}
export const errorInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true
};
================================================
FILE: todo-client/projects/auth/src/lib/services/jwt-interceptor.ts
================================================
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HTTP_INTERCEPTORS
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
@Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(
request: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
const currentUser = this.authService.currentUserValue;
if (currentUser && currentUser.accessToken) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.accessToken}`
}
});
}
return next.handle(request);
}
}
export const jwtInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
};
================================================
FILE: todo-client/projects/auth/src/public-api.ts
================================================
/*
* Public API Surface of auth
*/
export * from './lib/services/auth.service';
export * from './lib/components/login/login.component';
export * from './lib/services/jwt-interceptor';
export * from './lib/auth.module';
================================================
FILE: todo-client/projects/auth/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: todo-client/projects/auth/tsconfig.lib.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: todo-client/projects/auth/tsconfig.spec.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: todo-client/projects/auth/tslint.json
================================================
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}
================================================
FILE: todo-client/projects/todo/README.md
================================================
# Todo
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.
## Code scaffolding
Run `ng generate component component-name --project todo` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project todo`.
> Note: Don't forget to add `--project todo` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build todo` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build todo`, go to the dist folder `cd dist/todo` and run `npm publish`.
## Running unit tests
Run `ng test todo` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: todo-client/projects/todo/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/todo'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
================================================
FILE: todo-client/projects/todo/ng-package.json
================================================
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/todo",
"lib": {
"entryFile": "src/public-api.ts"
}
}
================================================
FILE: todo-client/projects/todo/package.json
================================================
{
"name": "todo",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^8.1.3",
"@angular/core": "^8.1.3"
}
}
================================================
FILE: todo-client/projects/todo/src/lib/components/task-create/task-create.component.ts
================================================
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
import { DoAction } from 'projects/app-common/src/public-api';
@Component({
selector: 'lib-task-create',
template: `
<div class="row my-2 mb-4">
<div class="col-md-8 offset-md-2">
<input
[(ngModel)]="task"
(keyup.enter)="OnEnter()"
class="form-control"
placeholder="Type a Task and hit (Enter)"
/>
</div>
</div>
`
})
export class TaskCreateComponent implements OnInit {
public task = '';
@Output()
public action: EventEmitter<DoAction> = new EventEmitter();
constructor() {}
ngOnInit() {}
public OnEnter() {
this.action.emit({ type: 'add-task', payload: this.task });
this.task = '';
}
}
================================================
FILE: todo-client/projects/todo/src/lib/components/task-list/task-list.component.ts
================================================
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { DoAction } from 'projects/app-common/src/public-api';
import { Task } from '../../models/task.model';
@Component({
selector: 'lib-task-list',
template: `
<div *ngIf="!tasks?.length; else show"><p>No tasks yet!</p></div>
<ng-template #show>
<div class="list-group">
<div
*ngFor="let task of tasks; let i = index; trackBy: trackByFn"
class="tasks"
>
<div class="action">
<button
(click)="doAction(task)"
class="btn btn-danger btn-lg"
title="Delete {{ task?.name }}"
>
<i class="fa fa-trash"></i>
</button>
</div>
<div class="task">
<div class="list-group-item">({{ i + 1 }}) {{ task?.name }}</div>
</div>
</div>
</div>
</ng-template>
`,
styles: [
`
.tasks {
display: flex;
justify-content: center;
}
.tasks .task {
flex-grow: 1;
flex-shrink: 0;
}
.tasks .action {
margin-right: 5px;
}
`
]
})
export class TaskListComponent implements OnInit {
@Input()
public tasks: Task[];
@Output()
public action: EventEmitter<DoAction> = new EventEmitter();
constructor() {}
ngOnInit() {}
public trackByFn(index: number, item: Task) {
return index;
}
public doAction(task: Task): void {
this.action.emit({ type: 'delete-task', payload: task });
}
}
================================================
FILE: todo-client/projects/todo/src/lib/components/task.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { Task } from '../models/task.model';
import { switchMap, startWith, tap } from 'rxjs/operators';
import { DoAction } from 'projects/app-common/src/lib/action';
import { TaskService } from '../services/task.service';
import { ActivatedRoute, Params } from '@angular/router';
@Component({
selector: 'lib-task',
template: `
<lib-task-create (action)="doAction($event)"></lib-task-create>
<lib-task-list
[tasks]="tasks$ | async"
(action)="doAction($event)"
></lib-task-list>
`
})
export class TaskComponent implements OnInit {
constructor(
private readonly activeRoute: ActivatedRoute,
private readonly taskService: TaskService
) {}
public tasks$: Observable<Task[]>;
private refresh$ = new BehaviorSubject<any>('');
private activeRoute$: Observable<Params>;
private todoId = '';
ngOnInit() {
this.activeRoute$ = this.activeRoute.params;
this.tasks$ = combineLatest(this.activeRoute$, this.refresh$).pipe(
tap(([param, _]) => (this.todoId = param.id)),
switchMap(([param, _]) => this.taskService.findAll(param.id))
);
}
public doAction({ type, payload }: DoAction): void {
switch (type) {
case 'add-task':
this.createTask(payload);
break;
case 'delete-task':
this.deleteTask(payload);
break;
default:
console.log('Unknown action type');
}
}
private createTask(task: string): void {
this.taskService
.create(this.todoId, { name: task })
.subscribe(() => this.refresh$.next(''));
}
private deleteTask(task: Task): void {
if (confirm('Are you sure you want to delete this item?')) {
this.taskService.delete(task.id).subscribe(() => this.refresh$.next(''));
}
}
}
================================================
FILE: todo-client/projects/todo/src/lib/components/todo-create/todo-create.component.ts
================================================
import { Component, OnInit, EventEmitter, Output } from '@angular/core';
import { DoAction } from 'projects/app-common/src/public-api';
@Component({
selector: 'lib-todo-create',
template: `
<div class="row my-2 mb-4">
<div class="col-md-8 offset-md-2">
<input
[(ngModel)]="todo"
(keyup.enter)="OnEnter()"
class="form-control"
placeholder="Type a Todo and hit (Enter)"
/>
</div>
</div>
`
})
export class TodoCreateComponent implements OnInit {
public todo = '';
@Output()
public action: EventEmitter<DoAction> = new EventEmitter();
constructor() {}
ngOnInit() {}
public OnEnter() {
this.action.emit({ type: 'add-todo', payload: this.todo });
this.todo = '';
}
}
================================================
FILE: todo-client/projects/todo/src/lib/components/todo-list/todo-list.component.ts
================================================
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Todo } from '../../models/todo.model';
import { DoAction } from 'projects/app-common/src/public-api';
@Component({
selector: 'lib-todo-list',
template: `
<div *ngIf="!todos?.length; else show">No todos yet!</div>
<ng-template #show>
<div class="list-group">
<div
*ngFor="let todo of todos; let i = index; trackBy: trackByFn"
class="todos"
>
<div class="action">
<button
(click)="doAction(todo)"
class="btn btn-danger btn-lg"
title="Delete {{ todo?.name }}"
>
<i class="fa fa-trash"></i>
</button>
</div>
<div class="todo">
<a
href="#"
[routerLink]="['tasks', todo.id]"
routerLinkActive="list-group-item-primary"
class="list-group-item list-group-item-action"
>
({{ i + 1 }}) {{ todo?.name }}
</a>
</div>
</div>
</div>
</ng-template>
`,
styles: [
`
.todos {
display: flex;
justify-content: center;
}
.todos .todo {
flex-grow: 1;
flex-shrink: 0;
max-width: 90%;
}
.todos .action {
margin-right: 5px;
}
`
]
})
export class TodoListComponent implements OnInit {
@Input()
public todos: Todo[];
@Output()
public action: EventEmitter<DoAction> = new EventEmitter();
constructor() {}
ngOnInit() {}
public trackByFn(index: number, item: Todo) {
return index;
}
public doAction(todo: Todo): void {
this.action.emit({ type: 'delete-todo', payload: todo });
}
}
================================================
FILE: todo-client/projects/todo/src/lib/components/todo.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { DoAction } from 'projects/app-common/src/public-api';
import { Todo } from '../models/todo.model';
import { TodoService } from '../services/todo.service';
import { Router } from '@angular/router';
@Component({
selector: 'lib-todo',
template: `
<lib-todo-create (action)="doAction($event)"></lib-todo-create>
<lib-todo-list
[todos]="todos$ | async"
(action)="doAction($event)"
></lib-todo-list>
`
})
export class TodoComponent implements OnInit {
public todos$: Observable<Todo[]>;
private refresh$ = new BehaviorSubject<any>('');
constructor(
private readonly router: Router,
private readonly todoService: TodoService
) {}
ngOnInit() {
this.todos$ = this.refresh$.pipe(
switchMap(() => this.todoService.findAll())
);
}
public doAction({ type, payload }: DoAction): void {
switch (type) {
case 'add-todo':
this.createTodo(payload);
break;
case 'delete-todo':
this.deleteTodo(payload);
break;
default:
console.log('Unknown action type');
}
}
private createTodo(todo: string): void {
this.todoService
.create({ name: todo })
.subscribe(() => this.refresh$.next(''));
}
private deleteTodo(todo: Todo): void {
if (confirm('Are you sure you want to delete this item?')) {
this.todoService.delete(todo.id).subscribe(() => {
this.refresh$.next('');
this.router.navigate(['/todo']);
});
}
}
}
================================================
FILE: todo-client/projects/todo/src/lib/models/task.model.ts
================================================
export interface Task {
id?: string;
name: string;
createdOn?: Date;
}
================================================
FILE: todo-client/projects/todo/src/lib/models/todo.model.ts
================================================
export interface Todo {
id?: string;
name: string;
createdOn?: Date;
}
================================================
FILE: todo-client/projects/todo/src/lib/services/task.service.ts
================================================
import { Injectable } from '@angular/core';
import {
HttpHeaders,
HttpClient,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { Task } from '../models/task.model';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'my-auth-token'
})
};
@Injectable({
providedIn: 'root'
})
export class TaskService {
private baseUrl = 'api/tasks'; // URL to web api
constructor(private readonly http: HttpClient) {}
public create(todoId: string, task: Task): Observable<Task> {
return this.http
.post<Task>(`${this.baseUrl}/todo/${todoId}`, task, httpOptions)
.pipe(catchError(this.handleError));
}
public findAll(todoId: string): Observable<Task[]> {
return this.http
.get<Task[]>(`${this.baseUrl}/todo/${todoId}`, httpOptions)
.pipe(
map((results: any) => results.tasks),
catchError(this.handleError)
);
}
public delete(id: string): Observable<{}> {
const url = `${this.baseUrl}/${id}`; // DELETE api/tasks/42-5c-...
return this.http
.delete(url, httpOptions)
.pipe(catchError(this.handleError));
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occured. Handle it accordingly
console.error('An error occured:', error.error.message);
} else {
// The backend returned an unsuccessful respone code.
// The response body may contain clues as to what was wrong
console.log(
`Backend returned code ${error.status}, body was: ${error.status}`
);
}
// return an observable wuth a user-facing error message
return throwError('Something bad happened; please try again later.');
}
}
================================================
FILE: todo-client/projects/todo/src/lib/services/todo.service.ts
================================================
import { Injectable } from '@angular/core';
import {
HttpHeaders,
HttpClient,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { Todo } from '../models/todo.model';
import { catchError, map } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
@Injectable({
providedIn: 'root'
})
export class TodoService {
private baseUrl = 'api/todos'; // URL to web api
constructor(private readonly http: HttpClient) {}
public create(todo: Todo): Observable<Todo> {
return this.http
.post<Todo>(this.baseUrl, todo, httpOptions)
.pipe(catchError(this.handleError));
}
public findAll(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.baseUrl, httpOptions).pipe(
map((results: any) => results.todos),
catchError(this.handleError)
);
}
public delete(id: string): Observable<{}> {
const url = `${this.baseUrl}/${id}`; // DELETE api/todos/42-5c-...
return this.http
.delete(url, httpOptions)
.pipe(catchError(this.handleError));
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occured. Handle it accordingly
console.error('An error occured:', error.error.message);
} else {
// The backend returned an unsuccessful respone code.
// The response body may contain clues as to what was wrong
console.log(
`Backend returned code ${error.status}, body was: ${error.status}`
);
}
// return an observable wuth a user-facing error message
return throwError('Something bad happened; please try again later.');
}
}
================================================
FILE: todo-client/projects/todo/src/lib/todo-home.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'lib-todo-home',
template: `
<div class="row my-2">
<div class="col-md-6">
<div class="row">
<div class="col-md-12 bg-light py-3 text-center">
<h5>Todo Lists</h5>
</div>
</div>
<div class="row">
<div class="col-md-12 my-2">
<lib-todo></lib-todo>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row">
<div
class="col-md-12 bg-light py-3 border-left border-3 border-primary text-center"
>
<h5>Tasks</h5>
</div>
</div>
<div class="row">
<div class="col-md-12 my-2">
<router-outlet></router-outlet>
</div>
</div>
</div>
</div>
`,
styles: [
`
.border-3 {
border-width: 3px !important;
}
`
]
})
export class TodoHomeComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: todo-client/projects/todo/src/lib/todo.module.ts
================================================
import { NgModule } from '@angular/core';
import { AppCommonModule } from 'projects/app-common/src/public-api';
import { TodoListComponent } from './components/todo-list/todo-list.component';
import { TodoCreateComponent } from './components/todo-create/todo-create.component';
import { TaskComponent } from './components/task.component';
import { TodoHomeComponent } from './todo-home.component';
import { TodoComponent } from './components/todo.component';
import { TaskCreateComponent } from './components/task-create/task-create.component';
import { TaskListComponent } from './components/task-list/task-list.component';
@NgModule({
declarations: [
TodoComponent,
TodoCreateComponent,
TodoListComponent,
TaskComponent,
TodoHomeComponent,
TaskCreateComponent,
TaskListComponent
],
imports: [AppCommonModule],
exports: [TodoHomeComponent, TaskComponent]
})
export class TodoModule {}
================================================
FILE: todo-client/projects/todo/src/lib/todo.service.spec.ts
================================================
import { TestBed } from '@angular/core/testing';
import { TodoService } from './todo.service';
describe('TodoService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: TodoService = TestBed.get(TodoService);
expect(service).toBeTruthy();
});
});
================================================
FILE: todo-client/projects/todo/src/public-api.ts
================================================
/*
* Public API Surface of todo
*/
export * from './lib/todo-home.component';
export * from './lib/components/task.component';
export * from './lib/todo.module';
================================================
FILE: todo-client/projects/todo/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: todo-client/projects/todo/tsconfig.lib.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: todo-client/projects/todo/tsconfig.spec.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: todo-client/projects/todo/tslint.json
================================================
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}
================================================
FILE: todo-client/proxy.conf.json
================================================
{
"/api": {
"target": "http://localhost:4000",
"secure": false
},
"/auth": {
"target": "http://localhost:4000/auth/",
"secure": false,
"pathRewrite": {
"^/auth": ""
}
}
}
================================================
FILE: todo-client/src/app/app-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from 'projects/auth/src/public-api';
import { MasterComponent } from './shared/master/master.component';
import { HomeComponent } from './shared/home/home.component';
import { AuthGuard } from 'projects/auth/src/lib/auth.guard';
import { TodoHomeComponent, TaskComponent } from 'projects/todo/src/public-api';
const routes: Routes = [
{
path: '',
component: MasterComponent,
canActivate: [AuthGuard],
children: [
{
path: '',
component: HomeComponent
},
{
path: 'todo',
component: TodoHomeComponent,
children: [
{
path: 'tasks/:id',
component: TaskComponent
}
]
}
]
},
{
path: '',
children: [
{
path: 'login',
component: LoginComponent
}
]
},
{ path: '**', redirectTo: '' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
================================================
FILE: todo-client/src/app/app.component.html
================================================
<router-outlet></router-outlet>
================================================
FILE: todo-client/src/app/app.component.scss
================================================
================================================
FILE: todo-client/src/app/app.component.spec.ts
================================================
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'todo-client'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('todo-client');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to todo-client!');
});
});
================================================
FILE: todo-client/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'todo-client';
}
================================================
FILE: todo-client/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {
AuthModule,
jwtInterceptorProvider
} from 'projects/auth/src/public-api';
import { AppCommonModule } from 'projects/app-common/src/public-api';
import { MasterComponent } from './shared/master/master.component';
import { HomeComponent } from './shared/home/home.component';
import { TodoModule } from 'projects/todo/src/public-api';
import { errorInterceptorProvider } from 'projects/auth/src/lib/services/error.interceptor';
@NgModule({
declarations: [AppComponent, MasterComponent, HomeComponent],
imports: [
BrowserModule,
AppRoutingModule,
AppCommonModule,
AuthModule,
TodoModule
],
providers: [jwtInterceptorProvider, errorInterceptorProvider],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: todo-client/src/app/shared/home/home.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
template: `
<div class="row text-center">
<div class="col-md-12">
<h2 class="">Welcome to Todozz App!</h2>
<p>Here you can manage your Todo Lists in a breeze!</p>
</div>
</div>
`
})
export class HomeComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: todo-client/src/app/shared/master/master.component.html
================================================
<div class="navbar navbar-expand-lg navbar-light bg-light shadow fixed-top">
<div class="container">
<a class="navbar-brand" href="#">
<i class="fas fa-tasks"></i> Todozz</a
>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/']">
Home
</a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/todo']">Todo</a>
</li>
<li *ngIf="loggedIn" class="nav-item" routerLinkActive="active">
<a class="nav-link" (click)="logout()" href="">Logout</a>
</li>
</ul>
</div>
</div>
</div>
<section class="py-5 mt-5">
<div class="container">
<router-outlet></router-outlet>
</div>
</section>
================================================
FILE: todo-client/src/app/shared/master/master.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { AuthService } from 'projects/auth/src/public-api';
import { Router } from '@angular/router';
@Component({
selector: 'app-master',
template: `
<div class="navbar navbar-expand-lg navbar-light bg-light shadow fixed-top">
<div class="container">
<a class="navbar-brand" href="#">
<i class="fas fa-tasks"></i> Todozz</a
>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarResponsive"
aria-controls="navbarResponsive"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a
class="nav-link"
[routerLink]="['/']"
[routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="active"
>
Home
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
[routerLink]="['/todo']"
[routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="active"
>Todo</a
>
</li>
<li *ngIf="loggedIn" class="nav-item">
<a class="nav-link" (click)="logout()" href="">Logout</a>
</li>
</ul>
</div>
</div>
</div>
<section class="py-5 mt-5">
<div class="container">
<router-outlet></router-outlet>
</div>
</section>
`
})
export class MasterComponent implements OnInit {
public loggedIn = false;
constructor(
private readonly authService: AuthService,
private readonly router: Router
) {}
ngOnInit() {
this.loggedIn = !!this.authService.currentUserValue;
}
public logout(): void {
this.authService.logout();
this.router.navigate(['/login']);
}
}
================================================
FILE: todo-client/src/assets/.gitkeep
================================================
================================================
FILE: todo-client/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: todo-client/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: todo-client/src/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>TodoClient</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.7.0/css/all.css"
integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ"
crossorigin="anonymous"
/>
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>
================================================
FILE: todo-client/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: todo-client/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: todo-client/src/styles.scss
================================================
/* You can add global styles to this file, and also import other style files */
@import '~bootstrap/dist/css/bootstrap.min.css';
================================================
FILE: todo-client/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: todo-client/tsconfig.app.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/test.ts",
"src/**/*.spec.ts"
]
}
================================================
FILE: todo-client/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
],
"paths": {
"auth": [
"dist/auth"
],
"auth/*": [
"dist/auth/*"
],
"common": [
"dist/common",
"dist/common",
"dist/common"
],
"common/*": [
"dist/common/*",
"dist/common/*",
"dist/common/*"
],
"app-common": [
"dist/app-common"
],
"app-common/*": [
"dist/app-common/*"
],
"todo": [
"dist/todo"
],
"todo/*": [
"dist/todo/*"
]
}
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
================================================
FILE: todo-client/tsconfig.spec.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
================================================
FILE: todo-client/tslint.json
================================================
{
"extends": ["tslint:recommended"],
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warning"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [true, "attribute", "app", "camelCase"],
"component-selector": [true, "element", "app", "kebab-case"],
"import-blacklist": [true, "rxjs/Rx"],
"indent": [true, "tabs", 2],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [true, 120],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
"no-empty": false,
"no-inferrable-types": [true, "ignore-params"],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [true, "as-needed"],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [true, "single"],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": ["codelyzer"]
}
gitextract_f_7atx7c/
├── .gitignore
├── .vscode/
│ └── launch.json
├── README.md
├── server/
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── db/
│ │ └── initdb.d/
│ │ └── init-users-db.sh
│ ├── docker-compose.yml
│ ├── nest-cli.json
│ ├── nodemon-debug.json
│ ├── nodemon.json
│ ├── ormconfig.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── auth/
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── interfaces/
│ │ │ │ ├── login-status.interface.ts
│ │ │ │ ├── payload.interface.ts
│ │ │ │ └── regisration-status.interface.ts
│ │ │ └── jwt.strategy.ts
│ │ ├── core/
│ │ │ ├── core.module.ts
│ │ │ └── http-exception.filter.ts
│ │ ├── main.ts
│ │ ├── migration/
│ │ │ ├── 1551865385236-InitialCreate.ts
│ │ │ ├── 1552392671960-AddUpdatedOnFieldToTodoEntity.ts
│ │ │ ├── 1555148302681-AddUser.ts
│ │ │ ├── 1555166680617-AddOwnerFieldToTodoEntity.ts
│ │ │ └── 1565812987671-SeedUserRecord.ts
│ │ ├── mock/
│ │ │ └── todos.mock.ts
│ │ ├── shared/
│ │ │ ├── mapper.ts
│ │ │ └── utils.ts
│ │ ├── todo/
│ │ │ ├── dto/
│ │ │ │ ├── task.create.dto.ts
│ │ │ │ ├── task.dto.ts
│ │ │ │ ├── task.list.dto.ts
│ │ │ │ ├── todo.create.dto.ts
│ │ │ │ ├── todo.dto.ts
│ │ │ │ └── todo.list.dto.ts
│ │ │ ├── entity/
│ │ │ │ ├── task.entity.ts
│ │ │ │ └── todo.entity.ts
│ │ │ ├── task/
│ │ │ │ ├── task.controller.spec.ts
│ │ │ │ ├── task.controller.ts
│ │ │ │ ├── task.service.spec.ts
│ │ │ │ └── task.service.ts
│ │ │ ├── todo.controller.spec.ts
│ │ │ ├── todo.controller.ts
│ │ │ ├── todo.module.ts
│ │ │ ├── todo.service.spec.ts
│ │ │ └── todo.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ ├── user-login.dto.ts
│ │ │ ├── user.create.dto.ts
│ │ │ └── user.dto.ts
│ │ ├── entity/
│ │ │ └── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tslint.json
└── todo-client/
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── browserslist
├── e2e/
│ ├── protractor.conf.js
│ ├── src/
│ │ ├── app.e2e-spec.ts
│ │ └── app.po.ts
│ └── tsconfig.json
├── karma.conf.js
├── package.json
├── projects/
│ ├── app-common/
│ │ ├── README.md
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── lib/
│ │ │ │ ├── action.ts
│ │ │ │ └── app-common.module.ts
│ │ │ ├── public-api.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── auth/
│ │ ├── README.md
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── lib/
│ │ │ │ ├── auth.guard.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── components/
│ │ │ │ │ └── login/
│ │ │ │ │ ├── login.component.css
│ │ │ │ │ ├── login.component.html
│ │ │ │ │ └── login.component.ts
│ │ │ │ └── services/
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── error.interceptor.ts
│ │ │ │ └── jwt-interceptor.ts
│ │ │ ├── public-api.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ └── todo/
│ ├── README.md
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src/
│ │ ├── lib/
│ │ │ ├── components/
│ │ │ │ ├── task-create/
│ │ │ │ │ └── task-create.component.ts
│ │ │ │ ├── task-list/
│ │ │ │ │ └── task-list.component.ts
│ │ │ │ ├── task.component.ts
│ │ │ │ ├── todo-create/
│ │ │ │ │ └── todo-create.component.ts
│ │ │ │ ├── todo-list/
│ │ │ │ │ └── todo-list.component.ts
│ │ │ │ └── todo.component.ts
│ │ │ ├── models/
│ │ │ │ ├── task.model.ts
│ │ │ │ └── todo.model.ts
│ │ │ ├── services/
│ │ │ │ ├── task.service.ts
│ │ │ │ └── todo.service.ts
│ │ │ ├── todo-home.component.ts
│ │ │ ├── todo.module.ts
│ │ │ └── todo.service.spec.ts
│ │ ├── public-api.ts
│ │ └── test.ts
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── proxy.conf.json
├── src/
│ ├── app/
│ │ ├── app-routing.module.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ └── shared/
│ │ ├── home/
│ │ │ └── home.component.ts
│ │ └── master/
│ │ ├── master.component.html
│ │ └── master.component.ts
│ ├── assets/
│ │ └── .gitkeep
│ ├── environments/
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.scss
│ └── test.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json
SYMBOL INDEX (177 symbols across 64 files)
FILE: server/src/app.controller.ts
class AppController (line 5) | class AppController {
method constructor (line 6) | constructor(private readonly appService: AppService) {}
method getHello (line 9) | getHello(): string {
FILE: server/src/app.module.ts
class AppModule (line 12) | class AppModule {
method forRoot (line 13) | static forRoot(connOptions: ConnectionOptions): DynamicModule {
FILE: server/src/app.service.ts
class AppService (line 4) | class AppService {
method getHello (line 5) | getHello(): string {
FILE: server/src/auth/auth.controller.ts
class AuthController (line 21) | class AuthController {
method constructor (line 22) | constructor(private readonly authService: AuthService) {}
method register (line 25) | public async register(
method login (line 40) | public async login(@Body() loginUserDto: LoginUserDto): Promise<LoginS...
method testAuth (line 46) | public async testAuth(@Req() req: any): Promise<JwtPayload> {
FILE: server/src/auth/auth.module.ts
class AuthModule (line 28) | class AuthModule {}
FILE: server/src/auth/auth.service.ts
class AuthService (line 12) | class AuthService {
method constructor (line 13) | constructor(
method register (line 18) | async register(userDto: CreateUserDto): Promise<RegistrationStatus> {
method login (line 36) | async login(loginUserDto: LoginUserDto): Promise<LoginStatus> {
method validateUser (line 49) | async validateUser(payload: JwtPayload): Promise<UserDto> {
method _createToken (line 57) | private _createToken({ username }: UserDto): any {
FILE: server/src/auth/interfaces/login-status.interface.ts
type LoginStatus (line 3) | interface LoginStatus {
FILE: server/src/auth/interfaces/payload.interface.ts
type JwtPayload (line 1) | interface JwtPayload {
FILE: server/src/auth/interfaces/regisration-status.interface.ts
type RegistrationStatus (line 1) | interface RegistrationStatus {
FILE: server/src/auth/jwt.strategy.ts
class JwtStrategy (line 9) | class JwtStrategy extends PassportStrategy(Strategy) {
method constructor (line 10) | constructor(private readonly authService: AuthService) {
method validate (line 17) | async validate(payload: JwtPayload): Promise<UserDto> {
FILE: server/src/core/core.module.ts
class CoreModule (line 13) | class CoreModule {}
FILE: server/src/core/http-exception.filter.ts
class HttpExceptionFilter (line 11) | class HttpExceptionFilter implements ExceptionFilter {
method catch (line 12) | catch(exception: HttpException, host: ArgumentsHost) {
FILE: server/src/main.ts
function bootstrap (line 12) | async function bootstrap() {
FILE: server/src/migration/1551865385236-InitialCreate.ts
class InitialCreate1551865385236 (line 3) | class InitialCreate1551865385236 implements MigrationInterface {
method up (line 5) | public async up(queryRunner: QueryRunner): Promise<any> {
method down (line 11) | public async down(queryRunner: QueryRunner): Promise<any> {
FILE: server/src/migration/1552392671960-AddUpdatedOnFieldToTodoEntity.ts
class AddUpdatedOnFieldToTodoEntity1552392671960 (line 3) | class AddUpdatedOnFieldToTodoEntity1552392671960 implements MigrationInt...
method up (line 5) | public async up(queryRunner: QueryRunner): Promise<any> {
method down (line 9) | public async down(queryRunner: QueryRunner): Promise<any> {
FILE: server/src/migration/1555148302681-AddUser.ts
class AddUser1555148302681 (line 3) | class AddUser1555148302681 implements MigrationInterface {
method up (line 4) | public async up(queryRunner: QueryRunner): Promise<any> {
method down (line 11) | public async down(queryRunner: QueryRunner): Promise<any> {
FILE: server/src/migration/1555166680617-AddOwnerFieldToTodoEntity.ts
class AddOwnerFieldToTodoEntity1555166680617 (line 3) | class AddOwnerFieldToTodoEntity1555166680617
method up (line 5) | public async up(queryRunner: QueryRunner): Promise<any> {
method down (line 13) | public async down(queryRunner: QueryRunner): Promise<any> {
FILE: server/src/migration/1565812987671-SeedUserRecord.ts
class SeedUserRecord1565812987671 (line 6) | class SeedUserRecord1565812987671 implements MigrationInterface {
method up (line 7) | public async up(queryRunner: QueryRunner): Promise<any> {
method down (line 20) | public async down(queryRunner: QueryRunner): Promise<any> {}
FILE: server/src/todo/dto/task.create.dto.ts
class CreateTaskDto (line 3) | class CreateTaskDto {
FILE: server/src/todo/dto/task.dto.ts
class TaskDto (line 3) | class TaskDto {
FILE: server/src/todo/dto/task.list.dto.ts
class TaskListDto (line 3) | class TaskListDto {
FILE: server/src/todo/dto/todo.create.dto.ts
class CreateTodoDto (line 4) | class CreateTodoDto {
FILE: server/src/todo/dto/todo.dto.ts
class TodoDto (line 5) | class TodoDto {
FILE: server/src/todo/dto/todo.list.dto.ts
class TodoListDto (line 3) | class TodoListDto {
FILE: server/src/todo/entity/task.entity.ts
class TaskEntity (line 11) | class TaskEntity {
FILE: server/src/todo/entity/todo.entity.ts
class TodoEntity (line 14) | class TodoEntity {
FILE: server/src/todo/task/task.controller.ts
class TaskController (line 18) | class TaskController {
method constructor (line 19) | constructor(private taskService: TaskService) {}
method findOneTask (line 22) | async findOneTask(@Param('id') id: string): Promise<TaskDto> {
method findTasksByTodo (line 27) | async findTasksByTodo(@Param('id') id: string): Promise<TaskListDto> {
method create (line 34) | async create(
method destory (line 43) | async destory(@Param('id') id: string): Promise<TaskDto> {
FILE: server/src/todo/task/task.service.ts
class TaskService (line 11) | class TaskService {
method constructor (line 12) | constructor(
method getTask (line 19) | async getTask(id: string): Promise<TaskDto> {
method getTasksByTodo (line 29) | async getTasksByTodo(id: string): Promise<TaskDto[]> {
method createTask (line 38) | async createTask(todoId: string, taskDto: CreateTaskDto): Promise<Task...
method destoryTask (line 56) | async destoryTask(id: string): Promise<TaskDto> {
FILE: server/src/todo/todo.controller.ts
class TodoController (line 21) | class TodoController {
method constructor (line 22) | constructor(private readonly todoService: TodoService) {}
method findAll (line 25) | async findAll(@Req() req: any): Promise<TodoListDto> {
method findOne (line 31) | async findOne(@Param('id') id: string): Promise<TodoDto> {
method create (line 37) | async create(
method update (line 48) | async update(
method destory (line 57) | async destory(@Param('id') id: string): Promise<TodoDto> {
FILE: server/src/todo/todo.module.ts
class TodoModule (line 22) | class TodoModule {}
FILE: server/src/todo/todo.service.ts
class TodoService (line 14) | class TodoService {
method constructor (line 15) | constructor(
method getAllTodo (line 21) | async getAllTodo(): Promise<TodoDto[]> {
method getOneTodo (line 26) | async getOneTodo(id: string): Promise<TodoDto> {
method createTodo (line 42) | async createTodo(
method updateTodo (line 62) | async updateTodo(id: string, todoDto: TodoDto): Promise<TodoDto> {
method destoryTodo (line 90) | async destoryTodo(id: string): Promise<TodoDto> {
FILE: server/src/users/dto/user-login.dto.ts
class LoginUserDto (line 3) | class LoginUserDto {
FILE: server/src/users/dto/user.create.dto.ts
class CreateUserDto (line 3) | class CreateUserDto {
FILE: server/src/users/dto/user.dto.ts
class UserDto (line 3) | class UserDto {
FILE: server/src/users/entity/user.entity.ts
class UserEntity (line 13) | class UserEntity {
method hashPassword (line 22) | async hashPassword() {
FILE: server/src/users/users.module.ts
class UsersModule (line 12) | class UsersModule {}
FILE: server/src/users/users.service.ts
class UsersService (line 12) | class UsersService {
method constructor (line 13) | constructor(
method findOne (line 18) | async findOne(options?: object): Promise<UserDto> {
method findByLogin (line 23) | async findByLogin({ username, password }: LoginUserDto): Promise<UserD...
method findByPayload (line 40) | async findByPayload({ username }: any): Promise<UserDto> {
method create (line 44) | async create(userDto: CreateUserDto): Promise<UserDto> {
method _sanitizeUser (line 64) | private _sanitizeUser(user: UserEntity) {
FILE: todo-client/e2e/protractor.conf.js
method onPrepare (line 26) | onPrepare() {
FILE: todo-client/e2e/src/app.po.ts
class AppPage (line 3) | class AppPage {
method navigateTo (line 4) | navigateTo() {
method getTitleText (line 8) | getTitleText() {
FILE: todo-client/projects/app-common/src/lib/action.ts
type DoAction (line 1) | interface DoAction {
FILE: todo-client/projects/app-common/src/lib/app-common.module.ts
class AppCommonModule (line 24) | class AppCommonModule {}
FILE: todo-client/projects/auth/src/lib/auth.guard.ts
class AuthGuard (line 14) | class AuthGuard implements CanActivate {
method constructor (line 15) | constructor(private router: Router, private authService: AuthService) {}
method canActivate (line 17) | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
FILE: todo-client/projects/auth/src/lib/auth.module.ts
class AuthModule (line 10) | class AuthModule {}
FILE: todo-client/projects/auth/src/lib/components/login/login.component.ts
class LoginComponent (line 11) | class LoginComponent implements OnInit {
method constructor (line 17) | constructor(
method ngOnInit (line 24) | ngOnInit() {
method f (line 37) | get f() {
method onSubmit (line 41) | onSubmit() {
FILE: todo-client/projects/auth/src/lib/services/auth.service.ts
type ApplicationUser (line 11) | interface ApplicationUser {
class AuthService (line 20) | class AuthService {
method constructor (line 24) | constructor(private readonly http: HttpClient) {
method currentUserValue (line 31) | public get currentUserValue(): ApplicationUser {
method login (line 35) | login(username: string, password: string) {
method logout (line 52) | logout() {
FILE: todo-client/projects/auth/src/lib/services/error.interceptor.ts
class ErrorInterceptor (line 15) | class ErrorInterceptor implements HttpInterceptor {
method constructor (line 16) | constructor(private authService: AuthService) {}
method intercept (line 18) | intercept(
FILE: todo-client/projects/auth/src/lib/services/jwt-interceptor.ts
class JwtInterceptor (line 13) | class JwtInterceptor implements HttpInterceptor {
method constructor (line 14) | constructor(private authService: AuthService) {}
method intercept (line 16) | intercept(
FILE: todo-client/projects/todo/src/lib/components/task-create/task-create.component.ts
class TaskCreateComponent (line 19) | class TaskCreateComponent implements OnInit {
method constructor (line 25) | constructor() {}
method ngOnInit (line 27) | ngOnInit() {}
method OnEnter (line 29) | public OnEnter() {
FILE: todo-client/projects/todo/src/lib/components/task-list/task-list.component.ts
class TaskListComponent (line 49) | class TaskListComponent implements OnInit {
method constructor (line 56) | constructor() {}
method ngOnInit (line 58) | ngOnInit() {}
method trackByFn (line 60) | public trackByFn(index: number, item: Task) {
method doAction (line 64) | public doAction(task: Task): void {
FILE: todo-client/projects/todo/src/lib/components/task.component.ts
class TaskComponent (line 19) | class TaskComponent implements OnInit {
method constructor (line 20) | constructor(
method ngOnInit (line 29) | ngOnInit() {
method doAction (line 38) | public doAction({ type, payload }: DoAction): void {
method createTask (line 51) | private createTask(task: string): void {
method deleteTask (line 57) | private deleteTask(task: Task): void {
FILE: todo-client/projects/todo/src/lib/components/todo-create/todo-create.component.ts
class TodoCreateComponent (line 19) | class TodoCreateComponent implements OnInit {
method constructor (line 25) | constructor() {}
method ngOnInit (line 27) | ngOnInit() {}
method OnEnter (line 29) | public OnEnter() {
FILE: todo-client/projects/todo/src/lib/components/todo-list/todo-list.component.ts
class TodoListComponent (line 57) | class TodoListComponent implements OnInit {
method constructor (line 64) | constructor() {}
method ngOnInit (line 66) | ngOnInit() {}
method trackByFn (line 68) | public trackByFn(index: number, item: Todo) {
method doAction (line 72) | public doAction(todo: Todo): void {
FILE: todo-client/projects/todo/src/lib/components/todo.component.ts
class TodoComponent (line 19) | class TodoComponent implements OnInit {
method constructor (line 23) | constructor(
method ngOnInit (line 28) | ngOnInit() {
method doAction (line 34) | public doAction({ type, payload }: DoAction): void {
method createTodo (line 47) | private createTodo(todo: string): void {
method deleteTodo (line 53) | private deleteTodo(todo: Todo): void {
FILE: todo-client/projects/todo/src/lib/models/task.model.ts
type Task (line 1) | interface Task {
FILE: todo-client/projects/todo/src/lib/models/todo.model.ts
type Todo (line 1) | interface Todo {
FILE: todo-client/projects/todo/src/lib/services/task.service.ts
class TaskService (line 21) | class TaskService {
method constructor (line 24) | constructor(private readonly http: HttpClient) {}
method create (line 26) | public create(todoId: string, task: Task): Observable<Task> {
method findAll (line 32) | public findAll(todoId: string): Observable<Task[]> {
method delete (line 41) | public delete(id: string): Observable<{}> {
method handleError (line 48) | private handleError(error: HttpErrorResponse) {
FILE: todo-client/projects/todo/src/lib/services/todo.service.ts
class TodoService (line 20) | class TodoService {
method constructor (line 23) | constructor(private readonly http: HttpClient) {}
method create (line 25) | public create(todo: Todo): Observable<Todo> {
method findAll (line 31) | public findAll(): Observable<Todo[]> {
method delete (line 38) | public delete(id: string): Observable<{}> {
method handleError (line 45) | private handleError(error: HttpErrorResponse) {
FILE: todo-client/projects/todo/src/lib/todo-home.component.ts
class TodoHomeComponent (line 43) | class TodoHomeComponent implements OnInit {
method constructor (line 44) | constructor() {}
method ngOnInit (line 46) | ngOnInit() {}
FILE: todo-client/projects/todo/src/lib/todo.module.ts
class TodoModule (line 24) | class TodoModule {}
FILE: todo-client/src/app/app-routing.module.ts
class AppRoutingModule (line 47) | class AppRoutingModule {}
FILE: todo-client/src/app/app.component.ts
class AppComponent (line 8) | class AppComponent {
FILE: todo-client/src/app/app.module.ts
class AppModule (line 28) | class AppModule {}
FILE: todo-client/src/app/shared/home/home.component.ts
class HomeComponent (line 15) | class HomeComponent implements OnInit {
method constructor (line 16) | constructor() {}
method ngOnInit (line 18) | ngOnInit() {}
FILE: todo-client/src/app/shared/master/master.component.ts
class MasterComponent (line 59) | class MasterComponent implements OnInit {
method constructor (line 62) | constructor(
method ngOnInit (line 67) | ngOnInit() {
method logout (line 71) | public logout(): void {
Condensed preview — 149 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (129K chars).
[
{
"path": ".gitignore",
"chars": 1887,
"preview": "\n# Created by https://www.gitignore.io/api/node,windows,visualstudiocode\n# Edit at https://www.gitignore.io/?templates=n"
},
{
"path": ".vscode/launch.json",
"chars": 538,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": "README.md",
"chars": 87,
"preview": "# nestjs-todo-app\nA full stack application written in Nest.js, Angular and PostgreSQL.\n"
},
{
"path": "server/.gitignore",
"chars": 57,
"preview": ".idea/\n.vscode/\n.DS_Store\nnode_modules/\nbuild/\ntmp/\ntemp/"
},
{
"path": "server/.prettierrc",
"chars": 51,
"preview": "{\n \"singleQuote\": true,\n \"trailingComma\": \"all\"\n}"
},
{
"path": "server/README.md",
"chars": 3369,
"preview": "<p align=\"center\">\n <a href=\"http://nestjs.com/\" target=\"blank\"><img src=\"https://nestjs.com/img/logo_text.svg\" width=\""
},
{
"path": "server/db/initdb.d/init-users-db.sh",
"chars": 325,
"preview": "#!/usr/bin/env bash\n\nset -e\n\npsql -v ON_ERROR_STOP=1 --username \"$POSTGRES_USER\" --dbname \"$POSTGRES_DB\" <<-EOSQL\n CR"
},
{
"path": "server/docker-compose.yml",
"chars": 176,
"preview": "version: '3'\nservices:\n db:\n container_name: todo_db\n image: postgres:10.7\n volumes:\n - ./db/initdb.d:/do"
},
{
"path": "server/nest-cli.json",
"chars": 84,
"preview": "{\n \"language\": \"ts\",\n \"collection\": \"@nestjs/schematics\",\n \"sourceRoot\": \"src\"\n}\n"
},
{
"path": "server/nodemon-debug.json",
"chars": 163,
"preview": "{\n \"watch\": [\"src\"],\n \"ext\": \"ts\",\n \"ignore\": [\"src/**/*.spec.ts\"],\n \"exec\": \"node --inspect-brk -r ts-node/register"
},
{
"path": "server/nodemon.json",
"chars": 132,
"preview": "{\n \"watch\": [\"src\"],\n \"ext\": \"ts\",\n \"ignore\": [\"src/**/*.spec.ts\"],\n \"exec\": \"ts-node -r tsconfig-paths/register src"
},
{
"path": "server/ormconfig.json",
"chars": 1105,
"preview": "[\n {\n \"name\": \"production\",\n \"type\": \"postgres\",\n \"synchronize\": false,\n \"dropSchema\": false,\n \"logging\""
},
{
"path": "server/package.json",
"chars": 3036,
"preview": "{\n \"name\": \"server\",\n \"version\": \"1.0.0\",\n \"description\": \"A nest todo app\",\n \"author\": \"Bilal Haidar\",\n \"license\":"
},
{
"path": "server/src/app.controller.spec.ts",
"chars": 617,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { AppController } from './app.controller';\nimport { AppSer"
},
{
"path": "server/src/app.controller.ts",
"chars": 274,
"preview": "import { Controller, Get } from '@nestjs/common';\nimport { AppService } from './app.service';\n\n@Controller()\nexport clas"
},
{
"path": "server/src/app.module.ts",
"chars": 816,
"preview": "import { Module, DynamicModule } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { AppCon"
},
{
"path": "server/src/app.service.ts",
"chars": 142,
"preview": "import { Injectable } from '@nestjs/common';\n\n@Injectable()\nexport class AppService {\n getHello(): string {\n return "
},
{
"path": "server/src/auth/auth.controller.spec.ts",
"chars": 479,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { AuthController } from './auth.controller';\n\ndescribe('Au"
},
{
"path": "server/src/auth/auth.controller.ts",
"chars": 1309,
"preview": "import {\n Controller,\n Body,\n Post,\n HttpException,\n HttpStatus,\n UsePipes,\n Get,\n Req,\n UseGuards,\n} from '@ne"
},
{
"path": "server/src/auth/auth.module.ts",
"chars": 773,
"preview": "import { Module } from '@nestjs/common';\nimport { AuthController } from './auth.controller';\nimport { AuthService } from"
},
{
"path": "server/src/auth/auth.service.spec.ts",
"chars": 446,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { AuthService } from './auth.service';\n\ndescribe('AuthServ"
},
{
"path": "server/src/auth/auth.service.ts",
"chars": 1858,
"preview": "import { Injectable, HttpException, HttpStatus } from '@nestjs/common';\nimport { CreateUserDto } from '@user/dto/user.cr"
},
{
"path": "server/src/auth/interfaces/login-status.interface.ts",
"chars": 138,
"preview": "import { UserDto } from '@user/dto/user.dto';\n\nexport interface LoginStatus {\n username: string;\n accessToken: any;\n "
},
{
"path": "server/src/auth/interfaces/payload.interface.ts",
"chars": 52,
"preview": "export interface JwtPayload {\n username: string;\n}\n"
},
{
"path": "server/src/auth/interfaces/regisration-status.interface.ts",
"chars": 79,
"preview": "export interface RegistrationStatus {\n success: boolean;\n message: string;\n}\n"
},
{
"path": "server/src/auth/jwt.strategy.ts",
"chars": 838,
"preview": "import { PassportStrategy } from '@nestjs/passport';\nimport { ExtractJwt, Strategy } from 'passport-jwt';\nimport { AuthS"
},
{
"path": "server/src/core/core.module.ts",
"chars": 285,
"preview": "import { Module } from '@nestjs/common';\nimport { HttpExceptionFilter } from './http-exception.filter';\nimport { APP_FIL"
},
{
"path": "server/src/core/http-exception.filter.ts",
"chars": 981,
"preview": "import {\n ExceptionFilter,\n HttpException,\n HttpStatus,\n ArgumentsHost,\n Catch,\n Logger,\n} from '@nestjs/common';\n"
},
{
"path": "server/src/main.ts",
"chars": 1789,
"preview": "import 'dotenv/config';\n\nimport { NestFactory } from '@nestjs/core';\nimport { AppModule } from './app.module';\nimport { "
},
{
"path": "server/src/migration/1551865385236-InitialCreate.ts",
"chars": 1208,
"preview": "import {MigrationInterface, QueryRunner} from \"typeorm\";\n\nexport class InitialCreate1551865385236 implements MigrationIn"
},
{
"path": "server/src/migration/1552392671960-AddUpdatedOnFieldToTodoEntity.ts",
"chars": 472,
"preview": "import {MigrationInterface, QueryRunner} from \"typeorm\";\n\nexport class AddUpdatedOnFieldToTodoEntity1552392671960 implem"
},
{
"path": "server/src/migration/1555148302681-AddUser.ts",
"chars": 810,
"preview": "import { MigrationInterface, QueryRunner } from 'typeorm';\n\nexport class AddUser1555148302681 implements MigrationInterf"
},
{
"path": "server/src/migration/1555166680617-AddOwnerFieldToTodoEntity.ts",
"chars": 787,
"preview": "import { MigrationInterface, QueryRunner } from 'typeorm';\n\nexport class AddOwnerFieldToTodoEntity1555166680617\n implem"
},
{
"path": "server/src/migration/1565812987671-SeedUserRecord.ts",
"chars": 670,
"preview": "// https://github.com/typeorm/typeorm/blob/master/docs/transactions.md\n\nimport { MigrationInterface, QueryRunner } from "
},
{
"path": "server/src/mock/todos.mock.ts",
"chars": 1125,
"preview": "import { TodoEntity } from 'src/todo/entity/todo.entity';\n\nexport const todos: TodoEntity[] = [\n {\n id: 'eac400ba-3c"
},
{
"path": "server/src/shared/mapper.ts",
"chars": 1024,
"preview": "import { TaskDto } from '@todo/dto/task.dto';\nimport { TodoEntity } from '@todo/entity/todo.entity';\nimport { TodoDto } "
},
{
"path": "server/src/shared/utils.ts",
"chars": 943,
"preview": "import { getConnectionOptions, getConnection } from 'typeorm';\nimport * as bcrypt from 'bcrypt';\nimport { Logger } from "
},
{
"path": "server/src/todo/dto/task.create.dto.ts",
"chars": 110,
"preview": "import { IsNotEmpty } from 'class-validator';\n\nexport class CreateTaskDto {\n @IsNotEmpty()\n name: string;\n}\n"
},
{
"path": "server/src/todo/dto/task.dto.ts",
"chars": 180,
"preview": "import { IsNotEmpty, IsString } from 'class-validator';\n\nexport class TaskDto {\n @IsNotEmpty()\n id: string;\n\n @IsNotE"
},
{
"path": "server/src/todo/dto/task.list.dto.ts",
"chars": 88,
"preview": "import { TaskDto } from './task.dto';\n\nexport class TaskListDto {\n tasks: TaskDto[];\n}\n"
},
{
"path": "server/src/todo/dto/todo.create.dto.ts",
"chars": 238,
"preview": "import { IsNotEmpty, MaxLength, IsOptional } from 'class-validator';\nimport { UserDto } from '@user/dto/user.dto';\n\nexpo"
},
{
"path": "server/src/todo/dto/todo.dto.ts",
"chars": 305,
"preview": "import { TaskDto } from './task.dto';\nimport { IsNotEmpty } from 'class-validator';\nimport { UserDto } from '@user/dto/u"
},
{
"path": "server/src/todo/dto/todo.list.dto.ts",
"chars": 88,
"preview": "import { TodoDto } from './todo.dto';\n\nexport class TodoListDto {\n todos: TodoDto[];\n}\n"
},
{
"path": "server/src/todo/entity/task.entity.ts",
"chars": 429,
"preview": "import {\n Entity,\n PrimaryGeneratedColumn,\n Column,\n CreateDateColumn,\n ManyToOne,\n} from 'typeorm';\nimport { TodoE"
},
{
"path": "server/src/todo/entity/todo.entity.ts",
"chars": 674,
"preview": "import { TaskEntity } from '@todo/entity/task.entity';\nimport {\n Entity,\n PrimaryGeneratedColumn,\n Column,\n CreateDa"
},
{
"path": "server/src/todo/task/task.controller.spec.ts",
"chars": 479,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { TaskController } from './task.controller';\n\ndescribe('Ta"
},
{
"path": "server/src/todo/task/task.controller.ts",
"chars": 1172,
"preview": "import {\n Controller,\n Param,\n Get,\n Post,\n Body,\n Delete,\n UsePipes,\n UseGuards,\n} from '@nestjs/common';\nimpor"
},
{
"path": "server/src/todo/task/task.service.spec.ts",
"chars": 446,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { TaskService } from './task.service';\n\ndescribe('TaskServ"
},
{
"path": "server/src/todo/task/task.service.ts",
"chars": 1864,
"preview": "import { Injectable, HttpException, HttpStatus } from '@nestjs/common';\nimport { CreateTaskDto } from '../dto/task.creat"
},
{
"path": "server/src/todo/todo.controller.spec.ts",
"chars": 479,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { TodoController } from './todo.controller';\n\ndescribe('To"
},
{
"path": "server/src/todo/todo.controller.ts",
"chars": 1424,
"preview": "import {\n Controller,\n Get,\n Param,\n Post,\n Body,\n Put,\n Delete,\n UsePipes,\n UseGuards,\n Req,\n} from '@nestjs/"
},
{
"path": "server/src/todo/todo.module.ts",
"chars": 812,
"preview": "import { Module } from '@nestjs/common';\nimport { TodoController } from './todo.controller';\nimport { TodoService } from"
},
{
"path": "server/src/todo/todo.service.spec.ts",
"chars": 446,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { TodoService } from './todo.service';\n\ndescribe('TodoServ"
},
{
"path": "server/src/todo/todo.service.ts",
"chars": 2788,
"preview": "import { Injectable, HttpException, HttpStatus } from '@nestjs/common';\n\nimport { TodoEntity } from '@todo/entity/todo.e"
},
{
"path": "server/src/users/dto/user-login.dto.ts",
"chars": 168,
"preview": "import { IsNotEmpty } from 'class-validator';\n\nexport class LoginUserDto {\n @IsNotEmpty()\n readonly username: string;\n"
},
{
"path": "server/src/users/dto/user.create.dto.ts",
"chars": 207,
"preview": "import { IsNotEmpty, IsEmail } from 'class-validator';\n\nexport class CreateUserDto {\n @IsNotEmpty()\n username: string;"
},
{
"path": "server/src/users/dto/user.dto.ts",
"chars": 216,
"preview": "import { IsNotEmpty, IsEmail } from 'class-validator';\n\nexport class UserDto {\n @IsNotEmpty()\n id: string;\n\n @IsNotEm"
},
{
"path": "server/src/users/entity/user.entity.ts",
"chars": 698,
"preview": "import { TaskEntity } from '@todo/entity/task.entity';\nimport {\n Entity,\n PrimaryGeneratedColumn,\n Column,\n CreateDa"
},
{
"path": "server/src/users/users.module.ts",
"chars": 359,
"preview": "import { Module } from '@nestjs/common';\nimport { UsersService } from './users.service';\nimport { UserEntity } from './e"
},
{
"path": "server/src/users/users.service.spec.ts",
"chars": 453,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport { UsersService } from './users.service';\n\ndescribe('UsersS"
},
{
"path": "server/src/users/users.service.ts",
"chars": 1972,
"preview": "import { Injectable, HttpException, HttpStatus } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeor"
},
{
"path": "server/test/app.e2e-spec.ts",
"chars": 561,
"preview": "import { Test, TestingModule } from '@nestjs/testing';\nimport * as request from 'supertest';\nimport { AppModule } from '"
},
{
"path": "server/test/jest-e2e.json",
"chars": 183,
"preview": "{\n \"moduleFileExtensions\": [\"js\", \"json\", \"ts\"],\n \"rootDir\": \".\",\n \"testEnvironment\": \"node\",\n \"testRegex\": \".e2e-sp"
},
{
"path": "server/tsconfig.build.json",
"chars": 89,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"exclude\": [\"node_modules\", \"test\", \"**/*spec.ts\"]\n}\n"
},
{
"path": "server/tsconfig.json",
"chars": 455,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es6\",\n \"module\": \"commonjs\",\n \"moduleResolution\": \"node\",\n \"outDir\": \"."
},
{
"path": "server/tslint.json",
"chars": 426,
"preview": "{\n \"defaultSeverity\": \"error\",\n \"extends\": [\"tslint:recommended\"],\n \"jsRules\": {\n \"no-unused-expression\": true\n }"
},
{
"path": "todo-client/.editorconfig",
"chars": 246,
"preview": "# Editor configuration, see https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size ="
},
{
"path": "todo-client/.gitignore",
"chars": 629,
"preview": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/dist\n/tmp\n/out-tsc\n# Only "
},
{
"path": "todo-client/README.md",
"chars": 1027,
"preview": "# TodoClient\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.1.\n\n## D"
},
{
"path": "todo-client/angular.json",
"chars": 6668,
"preview": "{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"version\": 1,\n \"newProjectRoot\": \"projects\",\n \""
},
{
"path": "todo-client/browserslist",
"chars": 429,
"preview": "# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.\n# For addit"
},
{
"path": "todo-client/e2e/protractor.conf.js",
"chars": 810,
"preview": "// @ts-check\n// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/bl"
},
{
"path": "todo-client/e2e/src/app.e2e-spec.ts",
"chars": 640,
"preview": "import { AppPage } from './app.po';\nimport { browser, logging } from 'protractor';\n\ndescribe('workspace-project App', ()"
},
{
"path": "todo-client/e2e/src/app.po.ts",
"chars": 251,
"preview": "import { browser, by, element } from 'protractor';\n\nexport class AppPage {\n navigateTo() {\n return browser.get(brows"
},
{
"path": "todo-client/e2e/tsconfig.json",
"chars": 214,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../out-tsc/e2e\",\n \"module\": \"commonjs\",\n "
},
{
"path": "todo-client/karma.conf.js",
"chars": 1023,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "todo-client/package.json",
"chars": 1472,
"preview": "{\n \"name\": \"todo-client\",\n \"version\": \"0.0.0\",\n \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"ng serve --proxy-config pr"
},
{
"path": "todo-client/projects/app-common/README.md",
"chars": 1018,
"preview": "# AppCommon\n\nThis library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.\n\n## Co"
},
{
"path": "todo-client/projects/app-common/karma.conf.js",
"chars": 1026,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "todo-client/projects/app-common/ng-package.json",
"chars": 159,
"preview": "{\n \"$schema\": \"../../node_modules/ng-packagr/ng-package.schema.json\",\n \"dest\": \"../../dist/app-common\",\n \"lib\": {\n "
},
{
"path": "todo-client/projects/app-common/package.json",
"chars": 140,
"preview": "{\n \"name\": \"app-common\",\n \"version\": \"0.0.1\",\n \"peerDependencies\": {\n \"@angular/common\": \"^8.1.3\",\n \"@angular/c"
},
{
"path": "todo-client/projects/app-common/src/lib/action.ts",
"chars": 60,
"preview": "export interface DoAction {\n\ttype: string;\n\tpayload: any;\n}\n"
},
{
"path": "todo-client/projects/app-common/src/lib/app-common.module.ts",
"chars": 538,
"preview": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule, Reactive"
},
{
"path": "todo-client/projects/app-common/src/public-api.ts",
"chars": 115,
"preview": "/*\n * Public API Surface of app-common\n */\n\nexport * from './lib/app-common.module';\nexport * from './lib/action';\n"
},
{
"path": "todo-client/projects/app-common/src/test.ts",
"chars": 670,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "todo-client/projects/app-common/tsconfig.lib.json",
"chars": 549,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../out-tsc/lib\",\n \"target\": \"es2015\",\n"
},
{
"path": "todo-client/projects/app-common/tsconfig.spec.json",
"chars": 246,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../out-tsc/spec\",\n \"types\": [\n \"j"
},
{
"path": "todo-client/projects/app-common/tslint.json",
"chars": 247,
"preview": "{\n \"extends\": \"../../tslint.json\",\n \"rules\": {\n \"directive-selector\": [\n true,\n \"attribute\",\n \"lib\","
},
{
"path": "todo-client/projects/auth/README.md",
"chars": 971,
"preview": "# Auth\n\nThis library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.\n\n## Code sc"
},
{
"path": "todo-client/projects/auth/karma.conf.js",
"chars": 1020,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "todo-client/projects/auth/ng-package.json",
"chars": 153,
"preview": "{\n \"$schema\": \"../../node_modules/ng-packagr/ng-package.schema.json\",\n \"dest\": \"../../dist/auth\",\n \"lib\": {\n \"entr"
},
{
"path": "todo-client/projects/auth/package.json",
"chars": 134,
"preview": "{\n \"name\": \"auth\",\n \"version\": \"0.0.1\",\n \"peerDependencies\": {\n \"@angular/common\": \"^8.1.3\",\n \"@angular/core\": "
},
{
"path": "todo-client/projects/auth/src/lib/auth.guard.ts",
"chars": 734,
"preview": "import { Injectable } from '@angular/core';\n\nimport {\n\tActivatedRouteSnapshot,\n\tRouterStateSnapshot,\n\tCanActivate,\n\tRout"
},
{
"path": "todo-client/projects/auth/src/lib/auth.module.ts",
"chars": 313,
"preview": "import { NgModule } from '@angular/core';\nimport { LoginComponent } from './components/login/login.component';\nimport { "
},
{
"path": "todo-client/projects/auth/src/lib/components/login/login.component.css",
"chars": 201,
"preview": ":host {\n display: flex;\n justify-content: center;\n}\n\n.alert-danger {\n background-color: #f44336; /* Red */\n}\n.alert {"
},
{
"path": "todo-client/projects/auth/src/lib/components/login/login.component.html",
"chars": 1579,
"preview": "<div class=\"container my-3\">\n <div class=\"row text-center mb-5\">\n <div class=\"col-md-12 bg-light p-3\">\n <h2>Log"
},
{
"path": "todo-client/projects/auth/src/lib/components/login/login.component.ts",
"chars": 1421,
"preview": "import { Component, OnInit } from '@angular/core';\nimport { FormGroup, FormBuilder, Validators } from '@angular/forms';\n"
},
{
"path": "todo-client/projects/auth/src/lib/services/auth.service.ts",
"chars": 1539,
"preview": "/**\n * Based on\n * https://github.com/cornflourblue/angular-7-jwt-authentication-example\n */\n\nimport { Injectable } from"
},
{
"path": "todo-client/projects/auth/src/lib/services/error.interceptor.ts",
"chars": 1066,
"preview": "import { Injectable } from '@angular/core';\nimport {\n\tHttpInterceptor,\n\tHttpRequest,\n\tHttpHandler,\n\tHttpEvent,\n\tHttpErro"
},
{
"path": "todo-client/projects/auth/src/lib/services/jwt-interceptor.ts",
"chars": 892,
"preview": "import {\n\tHttpInterceptor,\n\tHttpRequest,\n\tHttpHandler,\n\tHttpEvent,\n\tHTTP_INTERCEPTORS\n} from '@angular/common/http';\nimp"
},
{
"path": "todo-client/projects/auth/src/public-api.ts",
"chars": 222,
"preview": "/*\n * Public API Surface of auth\n */\n\nexport * from './lib/services/auth.service';\nexport * from './lib/components/login"
},
{
"path": "todo-client/projects/auth/src/test.ts",
"chars": 666,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "todo-client/projects/auth/tsconfig.lib.json",
"chars": 549,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../out-tsc/lib\",\n \"target\": \"es2015\",\n"
},
{
"path": "todo-client/projects/auth/tsconfig.spec.json",
"chars": 246,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../out-tsc/spec\",\n \"types\": [\n \"j"
},
{
"path": "todo-client/projects/auth/tslint.json",
"chars": 247,
"preview": "{\n \"extends\": \"../../tslint.json\",\n \"rules\": {\n \"directive-selector\": [\n true,\n \"attribute\",\n \"lib\","
},
{
"path": "todo-client/projects/todo/README.md",
"chars": 971,
"preview": "# Todo\n\nThis library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.1.3.\n\n## Code sc"
},
{
"path": "todo-client/projects/todo/karma.conf.js",
"chars": 1020,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "todo-client/projects/todo/ng-package.json",
"chars": 153,
"preview": "{\n \"$schema\": \"../../node_modules/ng-packagr/ng-package.schema.json\",\n \"dest\": \"../../dist/todo\",\n \"lib\": {\n \"entr"
},
{
"path": "todo-client/projects/todo/package.json",
"chars": 134,
"preview": "{\n \"name\": \"todo\",\n \"version\": \"0.0.1\",\n \"peerDependencies\": {\n \"@angular/common\": \"^8.1.3\",\n \"@angular/core\": "
},
{
"path": "todo-client/projects/todo/src/lib/components/task-create/task-create.component.ts",
"chars": 757,
"preview": "import { Component, OnInit, EventEmitter, Output } from '@angular/core';\nimport { DoAction } from 'projects/app-common/s"
},
{
"path": "todo-client/projects/todo/src/lib/components/task-list/task-list.component.ts",
"chars": 1544,
"preview": "import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';\nimport { DoAction } from 'projects/app-c"
},
{
"path": "todo-client/projects/todo/src/lib/components/task.component.ts",
"chars": 1803,
"preview": "import { Component, OnInit } from '@angular/core';\nimport { Observable, BehaviorSubject, combineLatest, Subject } from '"
},
{
"path": "todo-client/projects/todo/src/lib/components/todo-create/todo-create.component.ts",
"chars": 757,
"preview": "import { Component, OnInit, EventEmitter, Output } from '@angular/core';\nimport { DoAction } from 'projects/app-common/s"
},
{
"path": "todo-client/projects/todo/src/lib/components/todo-list/todo-list.component.ts",
"chars": 1763,
"preview": "import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';\nimport { Todo } from '../../models/todo."
},
{
"path": "todo-client/projects/todo/src/lib/components/todo.component.ts",
"chars": 1561,
"preview": "import { Component, OnInit } from '@angular/core';\nimport { Observable, BehaviorSubject } from 'rxjs';\nimport { switchMa"
},
{
"path": "todo-client/projects/todo/src/lib/models/task.model.ts",
"chars": 74,
"preview": "export interface Task {\n\tid?: string;\n\tname: string;\n\tcreatedOn?: Date;\n}\n"
},
{
"path": "todo-client/projects/todo/src/lib/models/todo.model.ts",
"chars": 74,
"preview": "export interface Todo {\n\tid?: string;\n\tname: string;\n\tcreatedOn?: Date;\n}\n"
},
{
"path": "todo-client/projects/todo/src/lib/services/task.service.ts",
"chars": 1785,
"preview": "import { Injectable } from '@angular/core';\nimport {\n\tHttpHeaders,\n\tHttpClient,\n\tHttpErrorResponse\n} from '@angular/comm"
},
{
"path": "todo-client/projects/todo/src/lib/services/todo.service.ts",
"chars": 1670,
"preview": "import { Injectable } from '@angular/core';\nimport {\n\tHttpHeaders,\n\tHttpClient,\n\tHttpErrorResponse\n} from '@angular/comm"
},
{
"path": "todo-client/projects/todo/src/lib/todo-home.component.ts",
"chars": 1037,
"preview": "import { Component, OnInit } from '@angular/core';\n\n@Component({\n\tselector: 'lib-todo-home',\n\ttemplate: `\n <div class"
},
{
"path": "todo-client/projects/todo/src/lib/todo.module.ts",
"chars": 905,
"preview": "import { NgModule } from '@angular/core';\nimport { AppCommonModule } from 'projects/app-common/src/public-api';\nimport {"
},
{
"path": "todo-client/projects/todo/src/lib/todo.service.spec.ts",
"chars": 323,
"preview": "import { TestBed } from '@angular/core/testing';\n\nimport { TodoService } from './todo.service';\n\ndescribe('TodoService',"
},
{
"path": "todo-client/projects/todo/src/public-api.ts",
"chars": 165,
"preview": "/*\n * Public API Surface of todo\n */\n\nexport * from './lib/todo-home.component';\nexport * from './lib/components/task.co"
},
{
"path": "todo-client/projects/todo/src/test.ts",
"chars": 670,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "todo-client/projects/todo/tsconfig.lib.json",
"chars": 549,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../out-tsc/lib\",\n \"target\": \"es2015\",\n"
},
{
"path": "todo-client/projects/todo/tsconfig.spec.json",
"chars": 246,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../out-tsc/spec\",\n \"types\": [\n \"j"
},
{
"path": "todo-client/projects/todo/tslint.json",
"chars": 247,
"preview": "{\n \"extends\": \"../../tslint.json\",\n \"rules\": {\n \"directive-selector\": [\n true,\n \"attribute\",\n \"lib\","
},
{
"path": "todo-client/proxy.conf.json",
"chars": 209,
"preview": "{\n \"/api\": {\n \"target\": \"http://localhost:4000\",\n \"secure\": false\n },\n \"/auth\": {\n \"target\": \"http://localho"
},
{
"path": "todo-client/src/app/app-routing.module.ts",
"chars": 1010,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { LoginComponen"
},
{
"path": "todo-client/src/app/app.component.html",
"chars": 32,
"preview": "<router-outlet></router-outlet>\n"
},
{
"path": "todo-client/src/app/app.component.scss",
"chars": 0,
"preview": ""
},
{
"path": "todo-client/src/app/app.component.spec.ts",
"chars": 1110,
"preview": "import { TestBed, async } from '@angular/core/testing';\nimport { RouterTestingModule } from '@angular/router/testing';\ni"
},
{
"path": "todo-client/src/app/app.component.ts",
"chars": 216,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n"
},
{
"path": "todo-client/src/app/app.module.ts",
"chars": 939,
"preview": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutin"
},
{
"path": "todo-client/src/app/shared/home/home.component.ts",
"chars": 393,
"preview": "import { Component, OnInit } from '@angular/core';\n\n@Component({\n\tselector: 'app-home',\n\ttemplate: `\n <div class=\"row"
},
{
"path": "todo-client/src/app/shared/master/master.component.html",
"chars": 1192,
"preview": "<div class=\"navbar navbar-expand-lg navbar-light bg-light shadow fixed-top\">\n <div class=\"container\">\n <a class=\"nav"
},
{
"path": "todo-client/src/app/shared/master/master.component.ts",
"chars": 2137,
"preview": "import { Component, OnInit } from '@angular/core';\nimport { AuthService } from 'projects/auth/src/public-api';\nimport { "
},
{
"path": "todo-client/src/assets/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "todo-client/src/environments/environment.prod.ts",
"chars": 51,
"preview": "export const environment = {\n production: true\n};\n"
},
{
"path": "todo-client/src/environments/environment.ts",
"chars": 662,
"preview": "// This file can be replaced during build by using the `fileReplacements` array.\n// `ng build --prod` replaces `environm"
},
{
"path": "todo-client/src/index.html",
"chars": 555,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>TodoClient</title>\n <base href=\"/\" "
},
{
"path": "todo-client/src/main.ts",
"chars": 372,
"preview": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynami"
},
{
"path": "todo-client/src/polyfills.ts",
"chars": 2838,
"preview": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfi"
},
{
"path": "todo-client/src/styles.scss",
"chars": 128,
"preview": "/* You can add global styles to this file, and also import other style files */\n@import '~bootstrap/dist/css/bootstrap.m"
},
{
"path": "todo-client/src/test.ts",
"chars": 642,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "todo-client/tsconfig.app.json",
"chars": 210,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/app\",\n \"types\": []\n },\n \"include\""
},
{
"path": "todo-client/tsconfig.json",
"chars": 1067,
"preview": "{\n \"compileOnSave\": false,\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist/out-tsc\",\n \"sourceMap\":"
},
{
"path": "todo-client/tsconfig.spec.json",
"chars": 270,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/spec\",\n \"types\": [\n \"jasmine\","
},
{
"path": "todo-client/tslint.json",
"chars": 1824,
"preview": "{\n \"extends\": [\"tslint:recommended\"],\n \"rules\": {\n \"array-type\": false,\n \"arrow-parens\": false,\n \"deprecation"
}
]
About this extraction
This page contains the full source code of the bhaidar/nestjs-todo-app GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 149 files (108.3 KB), approximately 32.8k tokens, and a symbol index with 177 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.