typescript-express-starter 10.0.1 → 10.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/node-postgres/.env.development.local +7 -0
- package/lib/node-postgres/.env.production.local +7 -0
- package/lib/node-postgres/.env.test.local +7 -0
- package/lib/node-postgres/.swcrc +0 -1
- package/lib/node-postgres/src/app.ts +0 -6
- package/lib/node-postgres/src/config/index.ts +1 -1
- package/lib/node-postgres/src/database/index.ts +6 -2
- package/lib/node-postgres/src/database/init.sql +11 -108
- package/lib/node-postgres/src/middlewares/auth.middleware.ts +12 -4
- package/lib/node-postgres/src/routes/auth.route.ts +2 -2
- package/lib/node-postgres/src/routes/users.route.ts +2 -2
- package/lib/node-postgres/src/services/auth.service.ts +66 -15
- package/lib/node-postgres/src/services/users.service.ts +103 -21
- package/lib/node-postgres/src/test/auth.test.ts +8 -3
- package/lib/node-postgres/src/test/users.test.ts +16 -15
- package/lib/node-postgres/tsconfig.json +0 -1
- package/lib/typeorm/.env.development.local +5 -5
- package/lib/typeorm/.env.production.local +5 -5
- package/lib/typeorm/.env.test.local +5 -5
- package/lib/typeorm/docker-compose.yml +8 -8
- package/lib/typeorm/src/config/index.ts +1 -1
- package/lib/typeorm/src/database/index.ts +6 -6
- package/package.json +1 -1
- package/lib/node-postgres/src/models/users.model.ts +0 -9
package/lib/node-postgres/.swcrc
CHANGED
|
@@ -9,7 +9,6 @@ import morgan from 'morgan';
|
|
|
9
9
|
import swaggerJSDoc from 'swagger-jsdoc';
|
|
10
10
|
import swaggerUi from 'swagger-ui-express';
|
|
11
11
|
import { NODE_ENV, PORT, LOG_FORMAT, ORIGIN, CREDENTIALS } from '@config';
|
|
12
|
-
import { client } from '@database';
|
|
13
12
|
import { Routes } from '@interfaces/routes.interface';
|
|
14
13
|
import { ErrorMiddleware } from '@middlewares/error.middleware';
|
|
15
14
|
import { logger, stream } from '@utils/logger';
|
|
@@ -24,7 +23,6 @@ export class App {
|
|
|
24
23
|
this.env = NODE_ENV || 'development';
|
|
25
24
|
this.port = PORT || 3000;
|
|
26
25
|
|
|
27
|
-
this.connectToDatabase();
|
|
28
26
|
this.initializeMiddlewares();
|
|
29
27
|
this.initializeRoutes(routes);
|
|
30
28
|
this.initializeSwagger();
|
|
@@ -44,10 +42,6 @@ export class App {
|
|
|
44
42
|
return this.app;
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
private async connectToDatabase() {
|
|
48
|
-
await client.connect();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
45
|
private initializeMiddlewares() {
|
|
52
46
|
this.app.use(morgan(LOG_FORMAT, { stream }));
|
|
53
47
|
this.app.use(cors({ origin: ORIGIN, credentials: CREDENTIALS }));
|
|
@@ -3,4 +3,4 @@ config({ path: `.env.${process.env.NODE_ENV || 'development'}.local` });
|
|
|
3
3
|
|
|
4
4
|
export const CREDENTIALS = process.env.CREDENTIALS === 'true';
|
|
5
5
|
export const { NODE_ENV, PORT, SECRET_KEY, LOG_FORMAT, LOG_DIR, ORIGIN } = process.env;
|
|
6
|
-
export const {
|
|
6
|
+
export const { POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB } = process.env;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
|
-
import {
|
|
2
|
+
import { POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB } from '@config';
|
|
3
3
|
|
|
4
4
|
export const client = new Client({
|
|
5
|
-
connectionString: `postgres://${
|
|
5
|
+
connectionString: `postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}`,
|
|
6
6
|
});
|
|
7
|
+
|
|
8
|
+
client.connect();
|
|
9
|
+
|
|
10
|
+
export default client;
|
|
@@ -1,110 +1,13 @@
|
|
|
1
|
-
--
|
|
2
|
-
DROP TABLE IF EXISTS
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-- 강사 테이블 생성
|
|
12
|
-
CREATE TABLE teachers(
|
|
13
|
-
"teacherId" SERIAL PRIMARY KEY,
|
|
14
|
-
-- 강사 ID
|
|
15
|
-
"teacherName" VARCHAR(32) NOT NULL,
|
|
16
|
-
-- 강사명
|
|
17
|
-
"teacherAbout" VARCHAR(48) -- 강사 정보
|
|
18
|
-
);
|
|
19
|
-
-- 강사 데이터 생성
|
|
20
|
-
INSERT INTO teachers(
|
|
21
|
-
"teacherId",
|
|
22
|
-
"teacherName",
|
|
23
|
-
"teacherAbout"
|
|
24
|
-
)
|
|
25
|
-
VALUES (1, '조현영', '웹 개발 강사'),
|
|
26
|
-
(2, '개복치개발자', '안드로이드 코틀린 강사'),
|
|
27
|
-
(3, '이고잉', '생활코딩 운영진 겸 강사'),
|
|
28
|
-
(4, '김태원', '파이썬 알고리즘 강사'),
|
|
29
|
-
(5, '윤재성', 'Kotiln 기반 안드로이드 강사'),
|
|
30
|
-
(6, '조훈', '쿠버네티스 강사'),
|
|
31
|
-
(7, 'Rookiss', '얼리언 엔진 전문 강사'),
|
|
32
|
-
(8, '유용한IT학습', 'SQLD 자격증 취득 강사'),
|
|
33
|
-
(9, '김태민', '쿠버네티스 강사'),
|
|
34
|
-
(10, '큰돌', '알고리즘 강사');
|
|
35
|
-
SELECT SETVAL(
|
|
36
|
-
'"teachers_teacherId_seq"',
|
|
37
|
-
(
|
|
38
|
-
SELECT max("teacherId")
|
|
39
|
-
FROM teachers
|
|
40
|
-
)
|
|
41
|
-
);
|
|
42
|
-
-- ===================
|
|
43
|
-
-- 강의 카테고리 테이블
|
|
44
|
-
-- ===================
|
|
45
|
-
-- 강의 카테고리 테이블 생성
|
|
46
|
-
CREATE TABLE classes_category(
|
|
47
|
-
"categoryId" SERIAL PRIMARY KEY,
|
|
48
|
-
-- 카테고리 ID
|
|
49
|
-
"categoryName" VARCHAR(32) UNIQUE NOT NULL -- 카테고리 명
|
|
50
|
-
);
|
|
51
|
-
-- 강의 카테고리 데이터 생성
|
|
52
|
-
INSERT INTO classes_category("categoryId", "categoryName")
|
|
53
|
-
VALUES (1, '웹'),
|
|
54
|
-
(2, '앱'),
|
|
55
|
-
(3, '게임'),
|
|
56
|
-
(4, '알고리즘'),
|
|
57
|
-
(5, '인프라'),
|
|
58
|
-
(6, '데이터베이스');
|
|
59
|
-
SELECT SETVAL(
|
|
60
|
-
'"classes_category_categoryId_seq"',
|
|
61
|
-
(
|
|
62
|
-
SELECT max("categoryId")
|
|
63
|
-
FROM classes_category
|
|
64
|
-
)
|
|
65
|
-
);
|
|
66
|
-
-- ============
|
|
67
|
-
-- 강의 테이블
|
|
68
|
-
-- ============
|
|
69
|
-
-- 강의 테이블 생성
|
|
70
|
-
CREATE TABLE classes(
|
|
71
|
-
"classId" SERIAL PRIMARY KEY,
|
|
72
|
-
-- 강의 ID
|
|
73
|
-
"className" VARCHAR(32) UNIQUE NOT NULL,
|
|
74
|
-
-- 강의명
|
|
75
|
-
"classPrice" INTEGER NOT NULL DEFAULT 0,
|
|
76
|
-
-- 가격
|
|
77
|
-
"introduce" TEXT,
|
|
78
|
-
-- 강의 소개
|
|
79
|
-
"active" BOOLEAN NOT NULL DEFAULT false,
|
|
80
|
-
-- 강의 활성화 (true: 공개, false, 비공개)
|
|
1
|
+
-- If Exists Table Drop
|
|
2
|
+
DROP TABLE IF EXISTS users cascade;
|
|
3
|
+
-- ================
|
|
4
|
+
-- TABLE [users]
|
|
5
|
+
-- ================
|
|
6
|
+
-- create users table
|
|
7
|
+
CREATE TABLE users(
|
|
8
|
+
"id" SERIAL PRIMARY KEY,
|
|
9
|
+
"email" VARCHAR(32) UNIQUE NOT NULL,
|
|
10
|
+
"password" VARCHAR(48) NOT NULL,
|
|
81
11
|
"createdAt" TIMESTAMP WITHOUT TIME ZONE DEFAULT(NOW() AT TIME ZONE 'utc'),
|
|
82
|
-
|
|
83
|
-
"updatedAt" TIMESTAMP WITHOUT TIME ZONE,
|
|
84
|
-
-- 강의 수정 일자
|
|
85
|
-
"teacherId" INTEGER REFERENCES teachers("teacherId") ON DELETE CASCADE,
|
|
86
|
-
-- 강사 ID
|
|
87
|
-
"categoryId" INTEGER REFERENCES classes_category("categoryId") ON DELETE CASCADE -- 강사 ID
|
|
88
|
-
);
|
|
89
|
-
-- ==============
|
|
90
|
-
-- 수강생 테이블
|
|
91
|
-
-- ==============
|
|
92
|
-
-- 수강생 테이블 생성
|
|
93
|
-
CREATE TABLE students(
|
|
94
|
-
"studentId" SERIAL PRIMARY KEY,
|
|
95
|
-
-- 수강생 ID
|
|
96
|
-
"email" VARCHAR(48) UNIQUE NOT NULL,
|
|
97
|
-
-- 수강생 이메일
|
|
98
|
-
"nickname" VARCHAR(32) NOT NULL -- 수강생 닉네임
|
|
99
|
-
);
|
|
100
|
-
-- ===============
|
|
101
|
-
-- 수강신청 테이블
|
|
102
|
-
-- ===============
|
|
103
|
-
-- 수강신청 테이블 생성
|
|
104
|
-
CREATE TABLE enrolment(
|
|
105
|
-
"classId" INTEGER REFERENCES classes("classId") ON DELETE CASCADE,
|
|
106
|
-
-- 강의 ID
|
|
107
|
-
"studentId" INTEGER REFERENCES students("studentId") ON DELETE CASCADE,
|
|
108
|
-
-- 수강생 ID
|
|
109
|
-
"applicationAt" TIMESTAMP WITHOUT TIME ZONE DEFAULT(NOW() AT TIME ZONE 'utc') -- 신청 일자
|
|
12
|
+
"updatedAt" TIMESTAMP WITHOUT TIME ZONE
|
|
110
13
|
);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { NextFunction, Response } from 'express';
|
|
2
2
|
import { verify } from 'jsonwebtoken';
|
|
3
3
|
import { SECRET_KEY } from '@config';
|
|
4
|
+
import pg from '@database';
|
|
4
5
|
import { HttpException } from '@exceptions/httpException';
|
|
5
6
|
import { DataStoredInToken, RequestWithUser } from '@interfaces/auth.interface';
|
|
6
|
-
import { UserModel } from '@models/users.model';
|
|
7
7
|
|
|
8
8
|
const getAuthorization = req => {
|
|
9
9
|
const coockie = req.cookies['Authorization'];
|
|
@@ -21,10 +21,18 @@ export const AuthMiddleware = async (req: RequestWithUser, res: Response, next:
|
|
|
21
21
|
|
|
22
22
|
if (Authorization) {
|
|
23
23
|
const { id } = (await verify(Authorization, SECRET_KEY)) as DataStoredInToken;
|
|
24
|
-
const
|
|
24
|
+
const { rows, rowCount } = await pg.query(`
|
|
25
|
+
SELECT
|
|
26
|
+
"email",
|
|
27
|
+
"password"
|
|
28
|
+
FROM
|
|
29
|
+
users
|
|
30
|
+
WHERE
|
|
31
|
+
"id" = $1
|
|
32
|
+
`, id);
|
|
25
33
|
|
|
26
|
-
if (
|
|
27
|
-
req.user =
|
|
34
|
+
if (rowCount) {
|
|
35
|
+
req.user = rows[0];
|
|
28
36
|
next();
|
|
29
37
|
} else {
|
|
30
38
|
next(new HttpException(401, 'Wrong authentication token'));
|
|
@@ -14,8 +14,8 @@ export class AuthRoute implements Routes {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
private initializeRoutes() {
|
|
17
|
-
this.router.post('/signup', ValidationMiddleware(CreateUserDto
|
|
18
|
-
this.router.post('/login', ValidationMiddleware(CreateUserDto
|
|
17
|
+
this.router.post('/signup', ValidationMiddleware(CreateUserDto), this.auth.signUp);
|
|
18
|
+
this.router.post('/login', ValidationMiddleware(CreateUserDto), this.auth.logIn);
|
|
19
19
|
this.router.post('/logout', AuthMiddleware, this.auth.logOut);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -16,8 +16,8 @@ export class UserRoute implements Routes {
|
|
|
16
16
|
private initializeRoutes() {
|
|
17
17
|
this.router.get(`${this.path}`, this.user.getUsers);
|
|
18
18
|
this.router.get(`${this.path}/:id(\\d+)`, this.user.getUserById);
|
|
19
|
-
this.router.post(`${this.path}`, ValidationMiddleware(CreateUserDto
|
|
20
|
-
this.router.put(`${this.path}/:id(\\d+)`, ValidationMiddleware(CreateUserDto,
|
|
19
|
+
this.router.post(`${this.path}`, ValidationMiddleware(CreateUserDto), this.user.createUser);
|
|
20
|
+
this.router.put(`${this.path}/:id(\\d+)`, ValidationMiddleware(CreateUserDto, true), this.user.updateUser);
|
|
21
21
|
this.router.delete(`${this.path}/:id(\\d+)`, this.user.deleteUser);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -2,10 +2,10 @@ import { hash, compare } from 'bcrypt';
|
|
|
2
2
|
import { sign } from 'jsonwebtoken';
|
|
3
3
|
import { Service } from 'typedi';
|
|
4
4
|
import { SECRET_KEY } from '@config';
|
|
5
|
+
import pg from '@database';
|
|
5
6
|
import { HttpException } from '@exceptions/httpException';
|
|
6
7
|
import { DataStoredInToken, TokenData } from '@interfaces/auth.interface';
|
|
7
8
|
import { User } from '@interfaces/users.interface';
|
|
8
|
-
import { UserModel } from '@models/users.model';
|
|
9
9
|
|
|
10
10
|
const createToken = (user: User): TokenData => {
|
|
11
11
|
const dataStoredInToken: DataStoredInToken = { id: user.id };
|
|
@@ -21,32 +21,83 @@ const createCookie = (tokenData: TokenData): string => {
|
|
|
21
21
|
@Service()
|
|
22
22
|
export class AuthService {
|
|
23
23
|
public async signup(userData: User): Promise<User> {
|
|
24
|
-
const
|
|
25
|
-
if (findUser) throw new HttpException(409, `This email ${userData.email} already exists`);
|
|
24
|
+
const { email, password } = userData;
|
|
26
25
|
|
|
27
|
-
const
|
|
28
|
-
|
|
26
|
+
const { rows: findUser } = await pg.query(
|
|
27
|
+
`
|
|
28
|
+
SELECT EXISTS(
|
|
29
|
+
SELECT
|
|
30
|
+
"email"
|
|
31
|
+
FROM
|
|
32
|
+
users
|
|
33
|
+
WHERE
|
|
34
|
+
"email" = $1
|
|
35
|
+
)`,
|
|
36
|
+
[email],
|
|
37
|
+
);
|
|
38
|
+
if (findUser[0].exists) throw new HttpException(409, `This email ${userData.email} already exists`);
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
const hashedPassword = await hash(password, 10);
|
|
41
|
+
const { rows: signUpUserData } = await pg.query(
|
|
42
|
+
`
|
|
43
|
+
INSERT INTO
|
|
44
|
+
users(
|
|
45
|
+
"email",
|
|
46
|
+
"password"
|
|
47
|
+
)
|
|
48
|
+
VALUES ($1, $2)
|
|
49
|
+
RETURNING "email", "password"
|
|
50
|
+
`,
|
|
51
|
+
[email, hashedPassword],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return signUpUserData[0];
|
|
31
55
|
}
|
|
32
56
|
|
|
33
57
|
public async login(userData: User): Promise<{ cookie: string; findUser: User }> {
|
|
34
|
-
const
|
|
35
|
-
|
|
58
|
+
const { email, password } = userData;
|
|
59
|
+
|
|
60
|
+
const { rows, rowCount } = await pg.query(
|
|
61
|
+
`
|
|
62
|
+
SELECT
|
|
63
|
+
"email",
|
|
64
|
+
"password"
|
|
65
|
+
FROM
|
|
66
|
+
users
|
|
67
|
+
WHERE
|
|
68
|
+
"email" = $1
|
|
69
|
+
`,
|
|
70
|
+
[email],
|
|
71
|
+
);
|
|
72
|
+
if (!rowCount) throw new HttpException(409, `This email ${email} was not found`);
|
|
36
73
|
|
|
37
|
-
const isPasswordMatching: boolean = await compare(
|
|
74
|
+
const isPasswordMatching: boolean = await compare(password, rows[0].password);
|
|
38
75
|
if (!isPasswordMatching) throw new HttpException(409, "You're password not matching");
|
|
39
76
|
|
|
40
|
-
const tokenData = createToken(
|
|
77
|
+
const tokenData = createToken(rows[0]);
|
|
41
78
|
const cookie = createCookie(tokenData);
|
|
42
|
-
|
|
43
|
-
return { cookie, findUser };
|
|
79
|
+
return { cookie, findUser: rows[0] };
|
|
44
80
|
}
|
|
45
81
|
|
|
46
82
|
public async logout(userData: User): Promise<User> {
|
|
47
|
-
const
|
|
48
|
-
|
|
83
|
+
const { email, password } = userData;
|
|
84
|
+
|
|
85
|
+
const { rows, rowCount } = await pg.query(
|
|
86
|
+
`
|
|
87
|
+
SELECT
|
|
88
|
+
"email",
|
|
89
|
+
"password"
|
|
90
|
+
FROM
|
|
91
|
+
users
|
|
92
|
+
WHERE
|
|
93
|
+
"email" = $1
|
|
94
|
+
AND
|
|
95
|
+
"password" = $2
|
|
96
|
+
`,
|
|
97
|
+
[email, password],
|
|
98
|
+
);
|
|
99
|
+
if (!rowCount) throw new HttpException(409, "User doesn't exist");
|
|
49
100
|
|
|
50
|
-
return
|
|
101
|
+
return rows[0];
|
|
51
102
|
}
|
|
52
103
|
}
|
|
@@ -1,51 +1,133 @@
|
|
|
1
1
|
import { hash } from 'bcrypt';
|
|
2
2
|
import { Service } from 'typedi';
|
|
3
|
+
import pg from '@database';
|
|
3
4
|
import { HttpException } from '@exceptions/httpException';
|
|
4
5
|
import { User } from '@interfaces/users.interface';
|
|
5
|
-
import { UserModel } from '@models/users.model';
|
|
6
6
|
|
|
7
7
|
@Service()
|
|
8
8
|
export class UserService {
|
|
9
9
|
public async findAllUser(): Promise<User[]> {
|
|
10
|
-
const
|
|
11
|
-
|
|
10
|
+
const { rows } = await pg.query(`
|
|
11
|
+
SELECT
|
|
12
|
+
*
|
|
13
|
+
FROM
|
|
14
|
+
users
|
|
15
|
+
`);
|
|
16
|
+
return rows;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
public async findUserById(userId: number): Promise<User> {
|
|
15
|
-
const
|
|
16
|
-
|
|
20
|
+
const { rows, rowCount } = await pg.query(
|
|
21
|
+
`
|
|
22
|
+
SELECT
|
|
23
|
+
*
|
|
24
|
+
FROM
|
|
25
|
+
users
|
|
26
|
+
WHERE
|
|
27
|
+
id = $1
|
|
28
|
+
`,
|
|
29
|
+
[userId],
|
|
30
|
+
);
|
|
31
|
+
if (!rowCount) throw new HttpException(409, "User doesn't exist");
|
|
17
32
|
|
|
18
|
-
return
|
|
33
|
+
return rows[0];
|
|
19
34
|
}
|
|
20
35
|
|
|
21
36
|
public async createUser(userData: User): Promise<User> {
|
|
22
|
-
const
|
|
23
|
-
if (findUser) throw new HttpException(409, `This email ${userData.email} already exists`);
|
|
37
|
+
const { email, password } = userData;
|
|
24
38
|
|
|
25
|
-
const
|
|
26
|
-
|
|
39
|
+
const { rows } = await pg.query(
|
|
40
|
+
`
|
|
41
|
+
SELECT EXISTS(
|
|
42
|
+
SELECT
|
|
43
|
+
"email"
|
|
44
|
+
FROM
|
|
45
|
+
users
|
|
46
|
+
WHERE
|
|
47
|
+
"email" = $1
|
|
48
|
+
)`,
|
|
49
|
+
[email],
|
|
50
|
+
);
|
|
51
|
+
if (rows[0].exists) throw new HttpException(409, `This email ${email} already exists`);
|
|
27
52
|
|
|
28
|
-
|
|
53
|
+
const hashedPassword = await hash(password, 10);
|
|
54
|
+
const { rows: createUserData } = await pg.query(
|
|
55
|
+
`
|
|
56
|
+
INSERT INTO
|
|
57
|
+
users(
|
|
58
|
+
"email",
|
|
59
|
+
"password"
|
|
60
|
+
)
|
|
61
|
+
VALUES ($1, $2)
|
|
62
|
+
RETURNING "email", "password"
|
|
63
|
+
`,
|
|
64
|
+
[email, hashedPassword],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return createUserData[0];
|
|
29
68
|
}
|
|
30
69
|
|
|
31
70
|
public async updateUser(userId: number, userData: User): Promise<User[]> {
|
|
32
|
-
const
|
|
33
|
-
|
|
71
|
+
const { rows: findUser } = await pg.query(
|
|
72
|
+
`
|
|
73
|
+
SELECT EXISTS(
|
|
74
|
+
SELECT
|
|
75
|
+
"id"
|
|
76
|
+
FROM
|
|
77
|
+
users
|
|
78
|
+
WHERE
|
|
79
|
+
"id" = $1
|
|
80
|
+
)`,
|
|
81
|
+
[userId],
|
|
82
|
+
);
|
|
83
|
+
if (findUser[0].exists) throw new HttpException(409, "User doesn't exist");
|
|
34
84
|
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
85
|
+
const { email, password } = userData;
|
|
86
|
+
const hashedPassword = await hash(password, 10);
|
|
87
|
+
const { rows: updateUserData } = await pg.query(
|
|
88
|
+
`
|
|
89
|
+
UPDATE
|
|
90
|
+
users
|
|
91
|
+
SET
|
|
92
|
+
"email" = $2,
|
|
93
|
+
"password" = $3
|
|
94
|
+
WHERE
|
|
95
|
+
"id" = $1
|
|
96
|
+
RETURNING "email", "password"
|
|
97
|
+
`,
|
|
98
|
+
[userId, email, hashedPassword],
|
|
99
|
+
);
|
|
40
100
|
|
|
41
101
|
return updateUserData;
|
|
42
102
|
}
|
|
43
103
|
|
|
44
104
|
public async deleteUser(userId: number): Promise<User[]> {
|
|
45
|
-
const
|
|
46
|
-
|
|
105
|
+
const { rows: findUser } = await pg.query(
|
|
106
|
+
`
|
|
107
|
+
SELECT EXISTS(
|
|
108
|
+
SELECT
|
|
109
|
+
"id"
|
|
110
|
+
FROM
|
|
111
|
+
users
|
|
112
|
+
WHERE
|
|
113
|
+
"id" = $1
|
|
114
|
+
)`,
|
|
115
|
+
[userId],
|
|
116
|
+
);
|
|
117
|
+
if (findUser[0].exists) throw new HttpException(409, "User doesn't exist");
|
|
118
|
+
|
|
119
|
+
const { rows: deleteUserData } = await pg.query(
|
|
120
|
+
`
|
|
121
|
+
DELETE
|
|
122
|
+
FROM
|
|
123
|
+
users
|
|
124
|
+
WHERE
|
|
125
|
+
id = $1
|
|
126
|
+
RETURNING "email", "password"
|
|
127
|
+
`,
|
|
128
|
+
[userId],
|
|
129
|
+
);
|
|
47
130
|
|
|
48
|
-
const deleteUserData: User[] = UserModel.filter(user => user.id !== findUser.id);
|
|
49
131
|
return deleteUserData;
|
|
50
132
|
}
|
|
51
133
|
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import request from 'supertest';
|
|
2
2
|
import { App } from '@/app';
|
|
3
|
+
import pg from '@database';
|
|
3
4
|
import { CreateUserDto } from '@dtos/users.dto';
|
|
4
5
|
import { AuthRoute } from '@routes/auth.route';
|
|
5
6
|
|
|
6
7
|
afterAll(async () => {
|
|
7
8
|
await new Promise<void>(resolve => setTimeout(() => resolve(), 500));
|
|
9
|
+
pg.end();
|
|
8
10
|
});
|
|
9
11
|
|
|
10
12
|
describe('Testing Auth', () => {
|
|
11
13
|
describe('[POST] /signup', () => {
|
|
12
|
-
it('response should have the Create userData', () => {
|
|
14
|
+
it('response should have the Create userData', async () => {
|
|
13
15
|
const userData: CreateUserDto = {
|
|
14
16
|
email: 'example@email.com',
|
|
15
17
|
password: 'password',
|
|
@@ -17,7 +19,10 @@ describe('Testing Auth', () => {
|
|
|
17
19
|
const authRoute = new AuthRoute();
|
|
18
20
|
const app = new App([authRoute]);
|
|
19
21
|
|
|
20
|
-
return request(app.getServer())
|
|
22
|
+
return await request(app.getServer())
|
|
23
|
+
.post('/signup')
|
|
24
|
+
.send(userData)
|
|
25
|
+
.expect(201);
|
|
21
26
|
});
|
|
22
27
|
});
|
|
23
28
|
|
|
@@ -31,7 +36,7 @@ describe('Testing Auth', () => {
|
|
|
31
36
|
const authRoute = new AuthRoute();
|
|
32
37
|
const app = new App([authRoute]);
|
|
33
38
|
|
|
34
|
-
return request(app.getServer())
|
|
39
|
+
return await request(app.getServer())
|
|
35
40
|
.post('/login')
|
|
36
41
|
.send(userData)
|
|
37
42
|
.expect('Set-Cookie', /^Authorization=.+/);
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
import request from 'supertest';
|
|
2
|
-
import App from '@/app';
|
|
2
|
+
import { App } from '@/app';
|
|
3
|
+
import pg from '@database';
|
|
3
4
|
import { CreateUserDto } from '@dtos/users.dto';
|
|
4
|
-
import { User } from '@interfaces/users.interface';
|
|
5
|
-
import { UserModel } from '@models/users.model';
|
|
6
5
|
import { UserRoute } from '@routes/users.route';
|
|
7
6
|
|
|
8
7
|
afterAll(async () => {
|
|
9
8
|
await new Promise<void>(resolve => setTimeout(() => resolve(), 500));
|
|
9
|
+
pg.end();
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
describe('Testing Users', () => {
|
|
13
13
|
describe('[GET] /users', () => {
|
|
14
|
-
it('response statusCode 200 / findAll', () => {
|
|
15
|
-
const findUser: User[] = UserModel;
|
|
14
|
+
it('response statusCode 200 / findAll', async () => {
|
|
16
15
|
const usersRoute = new UserRoute();
|
|
17
16
|
const app = new App([usersRoute]);
|
|
18
17
|
|
|
19
|
-
return request(app.getServer()).get(`${usersRoute.path}`).expect(200
|
|
18
|
+
return await request(app.getServer()).get(`${usersRoute.path}`).expect(200);
|
|
20
19
|
});
|
|
21
20
|
});
|
|
22
21
|
|
|
23
22
|
describe('[GET] /users/:id', () => {
|
|
24
|
-
it('response statusCode 200 / findOne', () => {
|
|
25
|
-
const userId = 1;
|
|
26
|
-
const findUser: User = UserModel.find(user => user.id === userId);
|
|
23
|
+
it('response statusCode 200 / findOne', async () => {
|
|
27
24
|
const usersRoute = new UserRoute();
|
|
28
25
|
const app = new App([usersRoute]);
|
|
29
26
|
|
|
30
|
-
return request(app.getServer())
|
|
27
|
+
return await request(app.getServer())
|
|
28
|
+
.get(`${usersRoute.path}`)
|
|
29
|
+
.query({
|
|
30
|
+
userId: 1,
|
|
31
|
+
})
|
|
32
|
+
.expect(200);
|
|
31
33
|
});
|
|
32
34
|
});
|
|
33
35
|
|
|
@@ -40,7 +42,7 @@ describe('Testing Users', () => {
|
|
|
40
42
|
const usersRoute = new UserRoute();
|
|
41
43
|
const app = new App([usersRoute]);
|
|
42
44
|
|
|
43
|
-
return request(app.getServer()).post(`${usersRoute.path}`).send(userData).expect(201);
|
|
45
|
+
return await request(app.getServer()).post(`${usersRoute.path}`).send(userData).expect(201);
|
|
44
46
|
});
|
|
45
47
|
});
|
|
46
48
|
|
|
@@ -54,18 +56,17 @@ describe('Testing Users', () => {
|
|
|
54
56
|
const usersRoute = new UserRoute();
|
|
55
57
|
const app = new App([usersRoute]);
|
|
56
58
|
|
|
57
|
-
return request(app.getServer()).put(`${usersRoute.path}/${userId}`).send(userData).expect(200);
|
|
59
|
+
return await request(app.getServer()).put(`${usersRoute.path}/${userId}`).send(userData).expect(200);
|
|
58
60
|
});
|
|
59
61
|
});
|
|
60
62
|
|
|
61
63
|
describe('[DELETE] /users/:id', () => {
|
|
62
|
-
it('response statusCode 200 / deleted', () => {
|
|
64
|
+
it('response statusCode 200 / deleted', async () => {
|
|
63
65
|
const userId = 1;
|
|
64
|
-
const deleteUser: User[] = UserModel.filter(user => user.id !== userId);
|
|
65
66
|
const usersRoute = new UserRoute();
|
|
66
67
|
const app = new App([usersRoute]);
|
|
67
68
|
|
|
68
|
-
return request(app.getServer()).delete(`${usersRoute.path}/${userId}`).expect(200
|
|
69
|
+
return await request(app.getServer()).delete(`${usersRoute.path}/${userId}`).expect(200);
|
|
69
70
|
});
|
|
70
71
|
});
|
|
71
72
|
});
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
PORT = 3000
|
|
3
3
|
|
|
4
4
|
# DATABASE
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
POSTGRES_USER = root
|
|
6
|
+
POSTGRES_PASSWORD = password
|
|
7
|
+
POSTGRES_HOST = localhost
|
|
8
|
+
POSTGRES_PORT = 5432
|
|
9
|
+
POSTGRES_DATABASE = dev
|
|
10
10
|
|
|
11
11
|
# TOKEN
|
|
12
12
|
SECRET_KEY = secretKey
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
PORT = 3000
|
|
3
3
|
|
|
4
4
|
# DATABASE
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
POSTGRES_USER = root
|
|
6
|
+
POSTGRES_PASSWORD = password
|
|
7
|
+
POSTGRES_HOST = localhost
|
|
8
|
+
POSTGRES_PORT = 5432
|
|
9
|
+
POSTGRES_DATABASE = dev
|
|
10
10
|
|
|
11
11
|
# TOKEN
|
|
12
12
|
SECRET_KEY = secretKey
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
PORT = 3000
|
|
3
3
|
|
|
4
4
|
# DATABASE
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
POSTGRES_USER = root
|
|
6
|
+
POSTGRES_PASSWORD = password
|
|
7
|
+
POSTGRES_HOST = localhost
|
|
8
|
+
POSTGRES_PORT = 5432
|
|
9
|
+
POSTGRES_DATABASE = dev
|
|
10
10
|
|
|
11
11
|
# TOKEN
|
|
12
12
|
SECRET_KEY = secretKey
|
|
@@ -20,11 +20,11 @@ services:
|
|
|
20
20
|
ports:
|
|
21
21
|
- "3000:3000"
|
|
22
22
|
environment:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
POSTGRES_USER: root
|
|
24
|
+
POSTGRES_PASSWORD: password
|
|
25
|
+
POSTGRES_HOST: pg
|
|
26
|
+
POSTGRES_PORT: 5432
|
|
27
|
+
POSTGRES_DATABASE: dev
|
|
28
28
|
volumes:
|
|
29
29
|
- ./:/app
|
|
30
30
|
- /app/node_modules
|
|
@@ -40,9 +40,9 @@ services:
|
|
|
40
40
|
container_name: pg
|
|
41
41
|
image: postgres:14.5-alpine
|
|
42
42
|
environment:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
POSTGRES_USER: root
|
|
44
|
+
POSTGRES_PASSWORD: password
|
|
45
|
+
POSTGRES_DB: dev
|
|
46
46
|
ports:
|
|
47
47
|
- "5432:5432"
|
|
48
48
|
networks:
|
|
@@ -3,4 +3,4 @@ config({ path: `.env.${process.env.NODE_ENV || 'development'}.local` });
|
|
|
3
3
|
|
|
4
4
|
export const CREDENTIALS = process.env.CREDENTIALS === 'true';
|
|
5
5
|
export const { NODE_ENV, PORT, SECRET_KEY, LOG_FORMAT, LOG_DIR, ORIGIN } = process.env;
|
|
6
|
-
export const {
|
|
6
|
+
export const { POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DATABASE } = process.env;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
2
|
import { createConnection, ConnectionOptions } from 'typeorm';
|
|
3
|
-
import {
|
|
3
|
+
import { POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DATABASE } from '@config';
|
|
4
4
|
|
|
5
5
|
export const dbConnection = async () => {
|
|
6
6
|
const dbConfig: ConnectionOptions = {
|
|
7
7
|
type: 'postgres',
|
|
8
|
-
username:
|
|
9
|
-
password:
|
|
10
|
-
host:
|
|
11
|
-
port:
|
|
12
|
-
database:
|
|
8
|
+
username: POSTGRES_USER,
|
|
9
|
+
password: POSTGRES_PASSWORD,
|
|
10
|
+
host: POSTGRES_HOST,
|
|
11
|
+
port: +POSTGRES_PORT,
|
|
12
|
+
database: POSTGRES_DATABASE,
|
|
13
13
|
synchronize: true,
|
|
14
14
|
logging: false,
|
|
15
15
|
entities: [join(__dirname, '../**/*.entity{.ts,.js}')],
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { User } from '@interfaces/users.interface';
|
|
2
|
-
|
|
3
|
-
// password: password
|
|
4
|
-
export const UserModel: User[] = [
|
|
5
|
-
{ id: 1, email: 'example1@email.com', password: '$2b$10$TBEfaCe1oo.2jfkBDWcj/usBj4oECsW2wOoDXpCa2IH9xqCpEK/hC' },
|
|
6
|
-
{ id: 2, email: 'example2@email.com', password: '$2b$10$TBEfaCe1oo.2jfkBDWcj/usBj4oECsW2wOoDXpCa2IH9xqCpEK/hC' },
|
|
7
|
-
{ id: 3, email: 'example3@email.com', password: '$2b$10$TBEfaCe1oo.2jfkBDWcj/usBj4oECsW2wOoDXpCa2IH9xqCpEK/hC' },
|
|
8
|
-
{ id: 4, email: 'example4@email.com', password: '$2b$10$TBEfaCe1oo.2jfkBDWcj/usBj4oECsW2wOoDXpCa2IH9xqCpEK/hC' },
|
|
9
|
-
];
|