typescript-express-starter 6.3.0 → 7.0.0
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/README.kr.md +13 -11
- package/README.md +12 -10
- package/lib/graphql/.dockerignore +18 -0
- package/lib/graphql/.editorconfig +9 -0
- package/lib/graphql/.env +1 -0
- package/lib/graphql/.eslintignore +1 -0
- package/lib/graphql/.eslintrc +18 -0
- package/lib/graphql/.huskyrc +5 -0
- package/lib/graphql/.lintstagedrc.json +5 -0
- package/lib/graphql/.prettierrc +8 -0
- package/lib/graphql/.swcrc +39 -0
- package/lib/graphql/.vscode/launch.json +35 -0
- package/lib/graphql/.vscode/settings.json +6 -0
- package/lib/graphql/Dockerfile +24 -0
- package/lib/graphql/Makefile +6 -0
- package/lib/graphql/docker-compose.yml +50 -0
- package/lib/graphql/ecosystem.config.js +59 -0
- package/lib/graphql/jest.config.js +12 -0
- package/lib/graphql/nginx.conf +40 -0
- package/lib/graphql/nodemon.json +12 -0
- package/lib/graphql/package.json +79 -0
- package/lib/graphql/src/app.ts +105 -0
- package/lib/graphql/src/configs/development.json +19 -0
- package/lib/graphql/src/configs/production.json +19 -0
- package/lib/graphql/src/configs/test.json +19 -0
- package/lib/graphql/src/databases/index.ts +24 -0
- package/lib/graphql/src/dtos/users.dto.ts +14 -0
- package/lib/graphql/src/entities/users.entity.ts +26 -0
- package/lib/graphql/src/exceptions/HttpException.ts +10 -0
- package/lib/graphql/src/http/auth.http +49 -0
- package/lib/graphql/src/http/users.http +78 -0
- package/lib/graphql/src/index.ts +1 -0
- package/lib/graphql/src/interfaces/auth.interface.ts +14 -0
- package/lib/graphql/src/interfaces/db.interface.ts +7 -0
- package/lib/graphql/src/interfaces/users.interface.ts +5 -0
- package/lib/graphql/src/middlewares/auth.middleware.ts +32 -0
- package/lib/graphql/src/middlewares/error.middleware.ts +17 -0
- package/lib/graphql/src/repositories/auth.repository.ts +61 -0
- package/lib/graphql/src/repositories/users.repository.ts +60 -0
- package/lib/graphql/src/resolvers/auth.resolver.ts +32 -0
- package/lib/graphql/src/resolvers/users.resolver.ts +47 -0
- package/lib/graphql/src/server.ts +14 -0
- package/lib/graphql/src/tests/auth.test.ts +52 -0
- package/lib/graphql/src/tests/index.test.ts +18 -0
- package/lib/graphql/src/tests/users.test.ts +71 -0
- package/lib/graphql/src/typedefs/users.type.ts +13 -0
- package/lib/graphql/src/utils/logger.ts +75 -0
- package/lib/graphql/src/utils/util.ts +19 -0
- package/lib/graphql/src/utils/validateEnv.ts +10 -0
- package/lib/graphql/tsconfig.json +39 -0
- package/package.json +3 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import '@/index';
|
|
3
|
+
import { ApolloServerPluginLandingPageProductionDefault, ApolloServerPluginLandingPageLocalDefault } from 'apollo-server-core';
|
|
4
|
+
import { ApolloServer } from 'apollo-server-express';
|
|
5
|
+
import compression from 'compression';
|
|
6
|
+
import cookieParser from 'cookie-parser';
|
|
7
|
+
import cors from 'cors';
|
|
8
|
+
import config from 'config';
|
|
9
|
+
import express from 'express';
|
|
10
|
+
import helmet from 'helmet';
|
|
11
|
+
import hpp from 'hpp';
|
|
12
|
+
import { buildSchema } from 'type-graphql';
|
|
13
|
+
import { createConnection } from 'typeorm';
|
|
14
|
+
import { dbConnection } from '@databases';
|
|
15
|
+
import { authMiddleware, authChecker } from '@middlewares/auth.middleware';
|
|
16
|
+
import errorMiddleware from '@middlewares/error.middleware';
|
|
17
|
+
import { logger, responseLogger, errorLogger } from '@utils/logger';
|
|
18
|
+
|
|
19
|
+
class App {
|
|
20
|
+
public app: express.Application;
|
|
21
|
+
public port: string | number;
|
|
22
|
+
public env: string;
|
|
23
|
+
|
|
24
|
+
constructor(resolvers) {
|
|
25
|
+
this.app = express();
|
|
26
|
+
this.port = process.env.PORT || 3000;
|
|
27
|
+
this.env = process.env.NODE_ENV || 'development';
|
|
28
|
+
|
|
29
|
+
this.connectToDatabase();
|
|
30
|
+
this.initializeMiddlewares();
|
|
31
|
+
this.initApolloServer(resolvers);
|
|
32
|
+
this.initializeErrorHandling();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public async listen() {
|
|
36
|
+
this.app.listen(this.port, () => {
|
|
37
|
+
logger.info(`=================================`);
|
|
38
|
+
logger.info(`======= ENV: ${this.env} =======`);
|
|
39
|
+
logger.info(`🚀 App listening on the port ${this.port}`);
|
|
40
|
+
logger.info(`🎮 http://localhost:${this.port}/graphql`);
|
|
41
|
+
logger.info(`=================================`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public getServer() {
|
|
46
|
+
return this.app;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private connectToDatabase() {
|
|
50
|
+
createConnection(dbConnection);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private initializeMiddlewares() {
|
|
54
|
+
this.app.use(cors({ origin: config.get('cors.origin'), credentials: config.get('cors.credentials') }));
|
|
55
|
+
this.app.use(hpp());
|
|
56
|
+
this.app.use(helmet());
|
|
57
|
+
this.app.use(compression());
|
|
58
|
+
this.app.use(express.json());
|
|
59
|
+
this.app.use(express.urlencoded({ extended: true }));
|
|
60
|
+
this.app.use(cookieParser());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private async initApolloServer(resolvers) {
|
|
64
|
+
const schema = await buildSchema({
|
|
65
|
+
resolvers: resolvers,
|
|
66
|
+
authChecker: authChecker,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const apolloServer = new ApolloServer({
|
|
70
|
+
schema: schema,
|
|
71
|
+
plugins: [
|
|
72
|
+
process.env.NODE_ENV === 'production'
|
|
73
|
+
? ApolloServerPluginLandingPageProductionDefault({ footer: false })
|
|
74
|
+
: ApolloServerPluginLandingPageLocalDefault({ footer: false }),
|
|
75
|
+
],
|
|
76
|
+
context: async ({ req }) => {
|
|
77
|
+
try {
|
|
78
|
+
const user = await authMiddleware(req);
|
|
79
|
+
return { user };
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new Error(error);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
formatResponse: (response, request) => {
|
|
85
|
+
responseLogger(request);
|
|
86
|
+
|
|
87
|
+
return response;
|
|
88
|
+
},
|
|
89
|
+
formatError: error => {
|
|
90
|
+
errorLogger(error);
|
|
91
|
+
|
|
92
|
+
return error;
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await apolloServer.start();
|
|
97
|
+
apolloServer.applyMiddleware({ app: this.app, cors: true, path: '/graphql' });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private initializeErrorHandling() {
|
|
101
|
+
this.app.use(errorMiddleware);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default App;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": "development",
|
|
3
|
+
"dbConfig": {
|
|
4
|
+
"host": "localhost",
|
|
5
|
+
"user": "root",
|
|
6
|
+
"port": 5432,
|
|
7
|
+
"password": "password",
|
|
8
|
+
"database": "test"
|
|
9
|
+
},
|
|
10
|
+
"secretKey": "secretKey",
|
|
11
|
+
"log": {
|
|
12
|
+
"format": "dev",
|
|
13
|
+
"dir": "../logs"
|
|
14
|
+
},
|
|
15
|
+
"cors": {
|
|
16
|
+
"origin": true,
|
|
17
|
+
"credentials": true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": "production",
|
|
3
|
+
"dbConfig": {
|
|
4
|
+
"host": "localhost",
|
|
5
|
+
"user": "root",
|
|
6
|
+
"port": 5432,
|
|
7
|
+
"password": "password",
|
|
8
|
+
"database": "test"
|
|
9
|
+
},
|
|
10
|
+
"secretKey": "secretKey",
|
|
11
|
+
"log": {
|
|
12
|
+
"format": "combined",
|
|
13
|
+
"dir": "../logs"
|
|
14
|
+
},
|
|
15
|
+
"cors": {
|
|
16
|
+
"origin": "your.domain.com",
|
|
17
|
+
"credentials": true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": "test",
|
|
3
|
+
"dbConfig": {
|
|
4
|
+
"host": "localhost",
|
|
5
|
+
"user": "root",
|
|
6
|
+
"port": 5432,
|
|
7
|
+
"password": "password",
|
|
8
|
+
"database": "test"
|
|
9
|
+
},
|
|
10
|
+
"secretKey": "secretKey",
|
|
11
|
+
"log": {
|
|
12
|
+
"format": "dev",
|
|
13
|
+
"dir": "../logs"
|
|
14
|
+
},
|
|
15
|
+
"cors": {
|
|
16
|
+
"origin": true,
|
|
17
|
+
"credentials": true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import config from 'config';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { ConnectionOptions } from 'typeorm';
|
|
4
|
+
import { dbConfig } from '@interfaces/db.interface';
|
|
5
|
+
|
|
6
|
+
const { host, port, user, password, database }: dbConfig = config.get('dbConfig');
|
|
7
|
+
export const dbConnection: ConnectionOptions = {
|
|
8
|
+
type: 'postgres',
|
|
9
|
+
host: host,
|
|
10
|
+
port: port,
|
|
11
|
+
username: user,
|
|
12
|
+
password: password,
|
|
13
|
+
database: database,
|
|
14
|
+
synchronize: true,
|
|
15
|
+
logging: false,
|
|
16
|
+
entities: [join(__dirname, '../**/*.entity{.ts,.js}')],
|
|
17
|
+
migrations: [join(__dirname, '../**/*.migration{.ts,.js}')],
|
|
18
|
+
subscribers: [join(__dirname, '../**/*.subscriber{.ts,.js}')],
|
|
19
|
+
cli: {
|
|
20
|
+
entitiesDir: 'src/entities',
|
|
21
|
+
migrationsDir: 'src/migration',
|
|
22
|
+
subscribersDir: 'src/subscriber',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IsEmail, IsString } from 'class-validator';
|
|
2
|
+
import { InputType, Field } from 'type-graphql';
|
|
3
|
+
import { User } from '@typedefs/users.type';
|
|
4
|
+
|
|
5
|
+
@InputType()
|
|
6
|
+
export class CreateUserDto implements Partial<User> {
|
|
7
|
+
@Field()
|
|
8
|
+
@IsEmail()
|
|
9
|
+
email: string;
|
|
10
|
+
|
|
11
|
+
@Field()
|
|
12
|
+
@IsString()
|
|
13
|
+
password: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IsNotEmpty } from 'class-validator';
|
|
2
|
+
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, Unique, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
|
3
|
+
import { User } from '@interfaces/users.interface';
|
|
4
|
+
|
|
5
|
+
@Entity()
|
|
6
|
+
export class UserEntity extends BaseEntity implements User {
|
|
7
|
+
@PrimaryGeneratedColumn()
|
|
8
|
+
id: number;
|
|
9
|
+
|
|
10
|
+
@Column()
|
|
11
|
+
@IsNotEmpty()
|
|
12
|
+
@Unique()
|
|
13
|
+
email: string;
|
|
14
|
+
|
|
15
|
+
@Column()
|
|
16
|
+
@IsNotEmpty()
|
|
17
|
+
password: string;
|
|
18
|
+
|
|
19
|
+
@Column()
|
|
20
|
+
@CreateDateColumn()
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
|
|
23
|
+
@Column()
|
|
24
|
+
@UpdateDateColumn()
|
|
25
|
+
updatedAt: Date;
|
|
26
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# baseURL
|
|
2
|
+
@baseURL = http://localhost:3000/graphql
|
|
3
|
+
|
|
4
|
+
###
|
|
5
|
+
# User Signup
|
|
6
|
+
POST {{ baseURL }}
|
|
7
|
+
Content-Type: application/json
|
|
8
|
+
X-REQUEST-TYPE: GraphQL
|
|
9
|
+
|
|
10
|
+
mutation {
|
|
11
|
+
signup (userData: {
|
|
12
|
+
email: "example@email.com",
|
|
13
|
+
password: "password"
|
|
14
|
+
}) {
|
|
15
|
+
id,
|
|
16
|
+
email,
|
|
17
|
+
password
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
###
|
|
22
|
+
# User Login
|
|
23
|
+
POST {{ baseURL }}
|
|
24
|
+
Content-Type: application/json
|
|
25
|
+
X-REQUEST-TYPE: GraphQL
|
|
26
|
+
|
|
27
|
+
mutation {
|
|
28
|
+
login (userData: {
|
|
29
|
+
email: "example@email.com",
|
|
30
|
+
password: "password"
|
|
31
|
+
}) {
|
|
32
|
+
email,
|
|
33
|
+
password
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
###
|
|
38
|
+
# User Logout
|
|
39
|
+
POST {{ baseURL }}
|
|
40
|
+
Content-Type: application/json
|
|
41
|
+
X-REQUEST-TYPE: GraphQL
|
|
42
|
+
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiaWF0IjoxNjM5MDQ3MjM3LCJleHAiOjE2MzkwNTA4Mzd9.HD0AvZl1s-mycKxo0IJ1QT-oSKhjeArXrlNBffZUanY;
|
|
43
|
+
|
|
44
|
+
mutation {
|
|
45
|
+
logout {
|
|
46
|
+
email,
|
|
47
|
+
password
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# baseURL
|
|
2
|
+
@baseURL = http://localhost:3000/graphql
|
|
3
|
+
|
|
4
|
+
###
|
|
5
|
+
# Find All Users
|
|
6
|
+
POST {{ baseURL }}
|
|
7
|
+
Content-Type: application/json
|
|
8
|
+
X-REQUEST-TYPE: GraphQL
|
|
9
|
+
|
|
10
|
+
query {
|
|
11
|
+
getUsers {
|
|
12
|
+
id,
|
|
13
|
+
email,
|
|
14
|
+
password
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
###
|
|
19
|
+
# Find User By Id
|
|
20
|
+
POST {{ baseURL }}
|
|
21
|
+
Content-Type: application/json
|
|
22
|
+
X-REQUEST-TYPE: GraphQL
|
|
23
|
+
|
|
24
|
+
query {
|
|
25
|
+
getUserById(userId: 1) {
|
|
26
|
+
id,
|
|
27
|
+
email,
|
|
28
|
+
password
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
###
|
|
33
|
+
# Create User
|
|
34
|
+
POST {{ baseURL }}
|
|
35
|
+
Content-Type: application/json
|
|
36
|
+
X-REQUEST-TYPE: GraphQL
|
|
37
|
+
|
|
38
|
+
mutation {
|
|
39
|
+
createUser (userData: {
|
|
40
|
+
email: "example@email.com",
|
|
41
|
+
password: "password"
|
|
42
|
+
}) {
|
|
43
|
+
id,
|
|
44
|
+
email,
|
|
45
|
+
password
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
###
|
|
50
|
+
# Modify User By Id
|
|
51
|
+
POST {{ baseURL }}
|
|
52
|
+
Content-Type: application/json
|
|
53
|
+
X-REQUEST-TYPE: GraphQL
|
|
54
|
+
|
|
55
|
+
mutation {
|
|
56
|
+
updateUser (userId: 1, userData: {
|
|
57
|
+
email: "example@email.com",
|
|
58
|
+
password: "password"
|
|
59
|
+
}) {
|
|
60
|
+
id,
|
|
61
|
+
email,
|
|
62
|
+
password
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
###
|
|
67
|
+
# Delete User By Id
|
|
68
|
+
POST {{ baseURL }}
|
|
69
|
+
Content-Type: application/json
|
|
70
|
+
X-REQUEST-TYPE: GraphQL
|
|
71
|
+
|
|
72
|
+
mutation {
|
|
73
|
+
deleteUser (userId: 1) {
|
|
74
|
+
id,
|
|
75
|
+
email,
|
|
76
|
+
password
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
process.env['NODE_CONFIG_DIR'] = __dirname + '/configs';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import config from 'config';
|
|
2
|
+
import { verify } from 'jsonwebtoken';
|
|
3
|
+
import { AuthChecker } from 'type-graphql';
|
|
4
|
+
import { getRepository } from 'typeorm';
|
|
5
|
+
import { UserEntity } from '@entities/users.entity';
|
|
6
|
+
import { HttpException } from '@exceptions/HttpException';
|
|
7
|
+
import { RequestWithUser, DataStoredInToken } from '@interfaces/auth.interface';
|
|
8
|
+
|
|
9
|
+
export const authMiddleware = async req => {
|
|
10
|
+
try {
|
|
11
|
+
const Authorization = req.cookies['Authorization'] || (req.header('Authorization') ? req.header('Authorization').split('Bearer ')[1] : null);
|
|
12
|
+
if (Authorization) {
|
|
13
|
+
const secretKey: string = config.get('secretKey');
|
|
14
|
+
const { id } = (await verify(Authorization, secretKey)) as DataStoredInToken;
|
|
15
|
+
const userRepository = getRepository(UserEntity);
|
|
16
|
+
const findUser = await userRepository.findOne(id, { select: ['id', 'email', 'password'] });
|
|
17
|
+
return findUser;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new HttpException(401, 'Wrong authentication token');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const authChecker: AuthChecker<RequestWithUser> = async ({ context: { user } }) => {
|
|
27
|
+
if (!user) {
|
|
28
|
+
throw new HttpException(404, 'Authentication token missing');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
|
+
import { HttpException } from '@exceptions/HttpException';
|
|
3
|
+
import { logger } from '@utils/logger';
|
|
4
|
+
|
|
5
|
+
const errorMiddleware = (error: HttpException, req: Request, res: Response, next: NextFunction) => {
|
|
6
|
+
try {
|
|
7
|
+
const status: number = error.status || 500;
|
|
8
|
+
const message: string = error.message || 'Something went wrong';
|
|
9
|
+
|
|
10
|
+
logger.error(`[${req.method}] ${req.path} >> StatusCode:: ${status}, Message:: ${message}`);
|
|
11
|
+
res.status(status).json({ message });
|
|
12
|
+
} catch (error) {
|
|
13
|
+
next(error);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default errorMiddleware;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { hash, compare } from 'bcrypt';
|
|
2
|
+
import config from 'config';
|
|
3
|
+
import { sign } from 'jsonwebtoken';
|
|
4
|
+
import { EntityRepository } from 'typeorm';
|
|
5
|
+
import { CreateUserDto } from '@dtos/users.dto';
|
|
6
|
+
import { UserEntity } from '@entities/users.entity';
|
|
7
|
+
import { HttpException } from '@exceptions/HttpException';
|
|
8
|
+
import { DataStoredInToken, TokenData } from '@interfaces/auth.interface';
|
|
9
|
+
import { User } from '@interfaces/users.interface';
|
|
10
|
+
import { isEmpty } from '@utils/util';
|
|
11
|
+
|
|
12
|
+
@EntityRepository(UserEntity)
|
|
13
|
+
export default class AuthRepository {
|
|
14
|
+
public async userSignUp(userData: CreateUserDto): Promise<User> {
|
|
15
|
+
if (isEmpty(userData)) throw new HttpException(400, "You're not userData");
|
|
16
|
+
|
|
17
|
+
const findUser: User = await UserEntity.findOne({ where: { email: userData.email } });
|
|
18
|
+
if (findUser) throw new HttpException(409, `You're email ${userData.email} already exists`);
|
|
19
|
+
|
|
20
|
+
const hashedPassword = await hash(userData.password, 10);
|
|
21
|
+
const createUserData: User = await UserEntity.save({ ...userData, password: hashedPassword });
|
|
22
|
+
|
|
23
|
+
return createUserData;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async userLogIn(userData: CreateUserDto): Promise<{ cookie: string; findUser: User }> {
|
|
27
|
+
if (isEmpty(userData)) throw new HttpException(400, "You're not userData");
|
|
28
|
+
|
|
29
|
+
const findUser: User = await UserEntity.findOne({ where: { email: userData.email } });
|
|
30
|
+
if (!findUser) throw new HttpException(409, `You're email ${userData.email} not found`);
|
|
31
|
+
|
|
32
|
+
const isPasswordMatching: boolean = await compare(userData.password, findUser.password);
|
|
33
|
+
if (!isPasswordMatching) throw new HttpException(409, "You're password not matching");
|
|
34
|
+
|
|
35
|
+
const tokenData = this.createToken(findUser);
|
|
36
|
+
const cookie = this.createCookie(tokenData);
|
|
37
|
+
|
|
38
|
+
return { cookie, findUser };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async userLogOut(userId: number): Promise<User> {
|
|
42
|
+
if (isEmpty(userId)) throw new HttpException(400, "You're not userId");
|
|
43
|
+
|
|
44
|
+
const findUser: User = await UserEntity.findOne({ where: { id: userId } });
|
|
45
|
+
if (!findUser) throw new HttpException(409, "You're not user");
|
|
46
|
+
|
|
47
|
+
return findUser;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public createToken(user: User): TokenData {
|
|
51
|
+
const dataStoredInToken: DataStoredInToken = { id: user.id };
|
|
52
|
+
const secretKey: string = config.get('secretKey');
|
|
53
|
+
const expiresIn: number = 60 * 60;
|
|
54
|
+
|
|
55
|
+
return { expiresIn, token: sign(dataStoredInToken, secretKey, { expiresIn }) };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public createCookie(tokenData: TokenData): string {
|
|
59
|
+
return `Authorization=${tokenData.token}; HttpOnly; Max-Age=${tokenData.expiresIn};`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { hash } from 'bcrypt';
|
|
2
|
+
import { EntityRepository } from 'typeorm';
|
|
3
|
+
import { CreateUserDto } from '@dtos/users.dto';
|
|
4
|
+
import { UserEntity } from '@entities/users.entity';
|
|
5
|
+
import { HttpException } from '@exceptions/HttpException';
|
|
6
|
+
import { User } from '@interfaces/users.interface';
|
|
7
|
+
import { isEmpty } from '@utils/util';
|
|
8
|
+
|
|
9
|
+
@EntityRepository()
|
|
10
|
+
export default class UserRepository {
|
|
11
|
+
public async userFindAll(): Promise<User[]> {
|
|
12
|
+
const users: User[] = await UserEntity.find();
|
|
13
|
+
|
|
14
|
+
return users;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public async userFindById(userId: number): Promise<User> {
|
|
18
|
+
if (isEmpty(userId)) throw new HttpException(400, "You're not userId");
|
|
19
|
+
|
|
20
|
+
const user: User = await UserEntity.findOne({ where: { id: userId } });
|
|
21
|
+
if (!user) throw new HttpException(409, "You're not user");
|
|
22
|
+
|
|
23
|
+
return user;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async userCreate(userData: CreateUserDto): Promise<User> {
|
|
27
|
+
if (isEmpty(userData)) throw new HttpException(400, "You're not userData");
|
|
28
|
+
|
|
29
|
+
const findUser: User = await UserEntity.findOne({ where: { email: userData.email } });
|
|
30
|
+
if (findUser) throw new HttpException(409, `You're email ${userData.email} already exists`);
|
|
31
|
+
|
|
32
|
+
const hashedPassword = await hash(userData.password, 10);
|
|
33
|
+
const createUserData: User = await UserEntity.save({ ...userData, password: hashedPassword });
|
|
34
|
+
|
|
35
|
+
return createUserData;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async userUpdate(userId: number, userData: CreateUserDto): Promise<User> {
|
|
39
|
+
if (isEmpty(userData)) throw new HttpException(400, "You're not userData");
|
|
40
|
+
|
|
41
|
+
const findUser: User = await UserEntity.findOne({ where: { id: userId } });
|
|
42
|
+
if (!findUser) throw new HttpException(409, "You're not user");
|
|
43
|
+
|
|
44
|
+
const hashedPassword = await hash(userData.password, 10);
|
|
45
|
+
await UserEntity.update(userId, { ...userData, password: hashedPassword });
|
|
46
|
+
|
|
47
|
+
const updateUser: User = await UserEntity.findOne({ where: { id: userId } });
|
|
48
|
+
return updateUser;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public async userDelete(userId: number): Promise<User> {
|
|
52
|
+
if (isEmpty(userId)) throw new HttpException(400, "You're not userId");
|
|
53
|
+
|
|
54
|
+
const findUser: User = await UserEntity.findOne({ where: { id: userId } });
|
|
55
|
+
if (!findUser) throw new HttpException(409, "You're not user");
|
|
56
|
+
|
|
57
|
+
await UserEntity.delete({ id: userId });
|
|
58
|
+
return findUser;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Authorized, Arg, Ctx, Mutation, Resolver } from 'type-graphql';
|
|
2
|
+
import { CreateUserDto } from '@dtos/users.dto';
|
|
3
|
+
import AuthRepository from '@repositories/auth.repository';
|
|
4
|
+
import { User } from '@typedefs/users.type';
|
|
5
|
+
|
|
6
|
+
@Resolver()
|
|
7
|
+
export class authResolver extends AuthRepository {
|
|
8
|
+
@Mutation(() => User, {
|
|
9
|
+
description: 'User signup',
|
|
10
|
+
})
|
|
11
|
+
async signup(@Arg('userData') userData: CreateUserDto): Promise<User> {
|
|
12
|
+
const user: User = await this.userSignUp(userData);
|
|
13
|
+
return user;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Mutation(() => User, {
|
|
17
|
+
description: 'User login',
|
|
18
|
+
})
|
|
19
|
+
async login(@Arg('userData') userData: CreateUserDto): Promise<User> {
|
|
20
|
+
const { findUser } = await this.userLogIn(userData);
|
|
21
|
+
return findUser;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Authorized()
|
|
25
|
+
@Mutation(() => User, {
|
|
26
|
+
description: 'User logout',
|
|
27
|
+
})
|
|
28
|
+
async logout(@Ctx('user') userData: any): Promise<User> {
|
|
29
|
+
const user = await this.userLogOut(userData);
|
|
30
|
+
return user;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Arg, Mutation, Query, Resolver } from 'type-graphql';
|
|
2
|
+
import { CreateUserDto } from '@dtos/users.dto';
|
|
3
|
+
import UserRepository from '@repositories/users.repository';
|
|
4
|
+
import { User } from '@typedefs/users.type';
|
|
5
|
+
|
|
6
|
+
@Resolver()
|
|
7
|
+
export class userResolver extends UserRepository {
|
|
8
|
+
@Query(() => [User], {
|
|
9
|
+
description: 'User find list',
|
|
10
|
+
})
|
|
11
|
+
async getUsers(): Promise<User[]> {
|
|
12
|
+
const users: User[] = await this.userFindAll();
|
|
13
|
+
return users;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Query(() => User, {
|
|
17
|
+
description: 'User find by id',
|
|
18
|
+
})
|
|
19
|
+
async getUserById(@Arg('userId') userId: number): Promise<User> {
|
|
20
|
+
const user: User = await this.userFindById(userId);
|
|
21
|
+
return user;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Mutation(() => User, {
|
|
25
|
+
description: 'User create',
|
|
26
|
+
})
|
|
27
|
+
async createUser(@Arg('userData') userData: CreateUserDto): Promise<User> {
|
|
28
|
+
const user: User = await this.userCreate(userData);
|
|
29
|
+
return user;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Mutation(() => User, {
|
|
33
|
+
description: 'User update',
|
|
34
|
+
})
|
|
35
|
+
async updateUser(@Arg('userId') userId: number, @Arg('userData') userData: CreateUserDto): Promise<User> {
|
|
36
|
+
const user: User = await this.userUpdate(userId, userData);
|
|
37
|
+
return user;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Mutation(() => User, {
|
|
41
|
+
description: 'User delete',
|
|
42
|
+
})
|
|
43
|
+
async deleteUser(@Arg('userId') userId: number): Promise<User> {
|
|
44
|
+
const user: User = await this.userDelete(userId);
|
|
45
|
+
return user;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import '@/index';
|
|
4
|
+
import App from '@/app';
|
|
5
|
+
import validateEnv from '@utils/validateEnv';
|
|
6
|
+
|
|
7
|
+
import { authResolver } from '@resolvers/auth.resolver';
|
|
8
|
+
import { userResolver } from '@resolvers/users.resolver';
|
|
9
|
+
|
|
10
|
+
validateEnv();
|
|
11
|
+
|
|
12
|
+
const app = new App([authResolver, userResolver]);
|
|
13
|
+
|
|
14
|
+
app.listen();
|