express-generator-typescript 2.2.9 → 2.3.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/express-generator-typescript.js +12 -12
- package/lib/project-files/.eslintrc.json +1 -0
- package/lib/project-files/env/development.env +1 -0
- package/lib/project-files/env/production.env +1 -0
- package/lib/project-files/env/test.env +1 -0
- package/lib/project-files/package.json +4 -4
- package/lib/project-files/spec/index.ts +32 -12
- package/lib/project-files/spec/support/login.ts +43 -0
- package/lib/project-files/spec/support/types.ts +1 -0
- package/lib/project-files/spec/tests/auth.spec.ts +75 -89
- package/lib/project-files/spec/tests/users.spec.ts +133 -212
- package/lib/project-files/src/constants/EnvVars.ts +28 -0
- package/lib/project-files/src/{declarations/major → constants}/HttpStatusCodes.ts +0 -0
- package/lib/project-files/src/{declarations/enums.ts → constants/misc.ts} +2 -6
- package/lib/project-files/src/index.ts +6 -4
- package/lib/project-files/src/models/User.ts +9 -2
- package/lib/project-files/src/{declarations → other}/classes.ts +1 -1
- package/lib/project-files/src/other/types.ts +4 -0
- package/lib/project-files/src/pre-start.ts +17 -15
- package/lib/project-files/src/repos/{mock-orm.ts → MockOrm.ts} +4 -4
- package/lib/project-files/src/repos/{user-repo.ts → UserRepo.ts} +4 -4
- package/lib/project-files/src/routes/AuthRoutes.ts +47 -0
- package/lib/project-files/src/routes/UserRoutes.ts +53 -0
- package/lib/project-files/src/routes/api.ts +27 -20
- package/lib/project-files/src/routes/constants/FullPaths.ts +40 -0
- package/lib/project-files/src/routes/constants/Paths.ts +28 -0
- package/lib/project-files/src/routes/{shared → middleware}/adminMw.ts +12 -22
- package/lib/project-files/src/{types → routes/types}/express/index.d.ts +0 -0
- package/lib/project-files/src/routes/types/express/misc.ts +16 -0
- package/lib/project-files/src/routes/{shared → types}/types.ts +1 -1
- package/lib/project-files/src/routes/user-routes.ts +2 -8
- package/lib/project-files/src/server.ts +28 -21
- package/lib/project-files/src/services/{auth-service.ts → AuthService.ts} +16 -14
- package/lib/project-files/src/services/{user-service.ts → UserService.ts} +12 -12
- package/lib/project-files/src/util/{jwt-util.ts → JwtUtil.ts} +8 -8
- package/lib/project-files/src/util/{pwd-util.ts → PwdUtil.ts} +3 -3
- package/lib/project-files/src/{declarations/functions.ts → util/misc.ts} +1 -0
- package/package.json +1 -1
- package/lib/project-files/spec/load-env.ts +0 -12
- package/lib/project-files/spec/support/login-agent.ts +0 -46
- package/lib/project-files/src/declarations/major/EnvVars.ts +0 -22
- package/lib/project-files/src/routes/auth-routes.ts +0 -57
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
2
|
+
|
|
3
|
+
import AuthService from '@src/services/AuthService';
|
|
4
|
+
import EnvVars from '@src/constants/EnvVars';
|
|
5
|
+
import { IReq, IRes } from './types/express/misc';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// **** Types **** //
|
|
10
|
+
|
|
11
|
+
interface ILoginReq {
|
|
12
|
+
email: string;
|
|
13
|
+
password: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// **** Functions **** //
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Login a user.
|
|
21
|
+
*/
|
|
22
|
+
async function login(req: IReq<ILoginReq>, res: IRes) {
|
|
23
|
+
const { email, password } = req.body;
|
|
24
|
+
// Add jwt to cookie
|
|
25
|
+
const jwt = await AuthService.getJwt(email, password);
|
|
26
|
+
const { Key, Options } = EnvVars.CookieProps;
|
|
27
|
+
res.cookie(Key, jwt, Options);
|
|
28
|
+
// Return
|
|
29
|
+
return res.status(HttpStatusCodes.OK).end();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Logout the user.
|
|
34
|
+
*/
|
|
35
|
+
function logout(_: IReq, res: IRes) {
|
|
36
|
+
const { Key, Options } = EnvVars.CookieProps;
|
|
37
|
+
res.clearCookie(Key, Options);
|
|
38
|
+
return res.status(HttpStatusCodes.OK).end();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// **** Export default **** //
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
login,
|
|
46
|
+
logout,
|
|
47
|
+
} as const;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
2
|
+
|
|
3
|
+
import UserService from '@src/services/UserService';
|
|
4
|
+
import { IUser } from '@src/models/User';
|
|
5
|
+
import { IReq, IRes } from './types/express/misc';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// **** Functions **** //
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get all users.
|
|
12
|
+
*/
|
|
13
|
+
async function getAll(_: IReq, res: IRes) {
|
|
14
|
+
const users = await UserService.getAll();
|
|
15
|
+
return res.status(HttpStatusCodes.OK).json({ users });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Add one user.
|
|
20
|
+
*/
|
|
21
|
+
async function add(req: IReq<{user: IUser}>, res: IRes) {
|
|
22
|
+
const { user } = req.body;
|
|
23
|
+
await UserService.addOne(user);
|
|
24
|
+
return res.status(HttpStatusCodes.CREATED).end();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Update one user.
|
|
29
|
+
*/
|
|
30
|
+
async function update(req: IReq<{user: IUser}>, res: IRes) {
|
|
31
|
+
const { user } = req.body;
|
|
32
|
+
await UserService.updateOne(user);
|
|
33
|
+
return res.status(HttpStatusCodes.OK).end();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Delete one user.
|
|
38
|
+
*/
|
|
39
|
+
async function delete_(req: IReq, res: IRes) {
|
|
40
|
+
const id = +req.params.id;
|
|
41
|
+
await UserService.delete(id);
|
|
42
|
+
return res.status(HttpStatusCodes.OK).end();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// **** Export default **** //
|
|
47
|
+
|
|
48
|
+
export default {
|
|
49
|
+
getAll,
|
|
50
|
+
add,
|
|
51
|
+
update,
|
|
52
|
+
delete: delete_,
|
|
53
|
+
} as const;
|
|
@@ -1,66 +1,73 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import jetValidator from 'jet-validator';
|
|
3
3
|
|
|
4
|
-
import adminMw from './
|
|
4
|
+
import adminMw from './middleware/adminMw';
|
|
5
|
+
import Paths from './constants/Paths';
|
|
5
6
|
import User from '@src/models/User';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
7
|
+
import AuthRoutes from './AuthRoutes';
|
|
8
|
+
import UserRoutes from './UserRoutes';
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
// ****
|
|
11
|
+
// **** Variables **** //
|
|
11
12
|
|
|
12
13
|
const apiRouter = Router(),
|
|
13
14
|
validate = jetValidator();
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
// **** Setup
|
|
17
|
+
// **** Setup **** //
|
|
17
18
|
|
|
18
19
|
const authRouter = Router();
|
|
19
20
|
|
|
20
21
|
// Login user
|
|
21
22
|
authRouter.post(
|
|
22
|
-
|
|
23
|
+
Paths.Auth.Login,
|
|
23
24
|
validate('email', 'password'),
|
|
24
|
-
|
|
25
|
+
AuthRoutes.login,
|
|
25
26
|
);
|
|
26
27
|
|
|
27
28
|
// Logout user
|
|
28
|
-
authRouter.get(
|
|
29
|
+
authRouter.get(
|
|
30
|
+
Paths.Auth.Logout,
|
|
31
|
+
AuthRoutes.logout,
|
|
32
|
+
);
|
|
29
33
|
|
|
30
|
-
// Add
|
|
31
|
-
apiRouter.use(
|
|
34
|
+
// Add AuthRouter
|
|
35
|
+
apiRouter.use(Paths.Auth.Base, authRouter);
|
|
32
36
|
|
|
33
37
|
|
|
34
|
-
//
|
|
38
|
+
// ** Add UserRouter ** //
|
|
35
39
|
|
|
36
40
|
const userRouter = Router();
|
|
37
41
|
|
|
38
42
|
// Get all users
|
|
39
|
-
userRouter.get(
|
|
43
|
+
userRouter.get(
|
|
44
|
+
Paths.Users.Get,
|
|
45
|
+
UserRoutes.getAll,
|
|
46
|
+
);
|
|
40
47
|
|
|
41
48
|
// Add one user
|
|
42
49
|
userRouter.post(
|
|
43
|
-
|
|
50
|
+
Paths.Users.Add,
|
|
44
51
|
validate(['user', User.instanceOf]),
|
|
45
|
-
|
|
52
|
+
UserRoutes.add,
|
|
46
53
|
);
|
|
47
54
|
|
|
48
55
|
// Update one user
|
|
49
56
|
userRouter.put(
|
|
50
|
-
|
|
57
|
+
Paths.Users.Update,
|
|
51
58
|
validate(['user', User.instanceOf]),
|
|
52
|
-
|
|
59
|
+
UserRoutes.update,
|
|
53
60
|
);
|
|
54
61
|
|
|
55
62
|
// Delete one user
|
|
56
63
|
userRouter.delete(
|
|
57
|
-
|
|
64
|
+
Paths.Users.Delete,
|
|
58
65
|
validate(['id', 'number', 'params']),
|
|
59
|
-
|
|
66
|
+
UserRoutes.delete,
|
|
60
67
|
);
|
|
61
68
|
|
|
62
|
-
// Add
|
|
63
|
-
apiRouter.use(
|
|
69
|
+
// Add UserRouter
|
|
70
|
+
apiRouter.use(Paths.Users.Base, adminMw, userRouter);
|
|
64
71
|
|
|
65
72
|
|
|
66
73
|
// **** Export default **** //
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Convert paths to full paths.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Paths, { TPaths } from './Paths';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
interface IPathObj {
|
|
10
|
+
Base: string;
|
|
11
|
+
[key: string]: string | IPathObj;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The recursive function.
|
|
16
|
+
*/
|
|
17
|
+
function getFullPaths(
|
|
18
|
+
parent: IPathObj,
|
|
19
|
+
baseUrl: string,
|
|
20
|
+
): IPathObj {
|
|
21
|
+
const url = (baseUrl + parent.Base),
|
|
22
|
+
keys = Object.keys(parent),
|
|
23
|
+
retVal: IPathObj = { Base: url };
|
|
24
|
+
// Iterate keys
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const pval = parent[key];
|
|
27
|
+
if (key !== 'Base' && typeof pval === 'string') {
|
|
28
|
+
retVal[key] = (url + pval);
|
|
29
|
+
} else if (typeof pval === 'object') {
|
|
30
|
+
retVal[key] = getFullPaths(pval, url);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Return
|
|
34
|
+
return retVal;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// **** Export default **** //
|
|
39
|
+
|
|
40
|
+
export default getFullPaths(Paths, '') as TPaths;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express router paths go here.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Immutable } from '@src/other/types';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
const Paths = {
|
|
9
|
+
Base: '/api',
|
|
10
|
+
Auth: {
|
|
11
|
+
Base: '/auth',
|
|
12
|
+
Login: '/login',
|
|
13
|
+
Logout: '/logout',
|
|
14
|
+
},
|
|
15
|
+
Users: {
|
|
16
|
+
Base: '/users',
|
|
17
|
+
Get: '/all',
|
|
18
|
+
Add: '/add',
|
|
19
|
+
Update: '/update',
|
|
20
|
+
Delete: '/delete/:id',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// **** Export **** //
|
|
26
|
+
|
|
27
|
+
export type TPaths = Immutable<typeof Paths>;
|
|
28
|
+
export default Paths as TPaths;
|
|
@@ -5,27 +5,17 @@
|
|
|
5
5
|
import { Request, Response, NextFunction } from 'express';
|
|
6
6
|
import { JwtPayload } from 'jsonwebtoken';
|
|
7
7
|
|
|
8
|
-
import HttpStatusCodes from '@src/
|
|
9
|
-
import EnvVars from '@src/
|
|
10
|
-
import jwtUtil from '@src/util/jwt-util';
|
|
11
|
-
import { IUser, UserRoles } from '@src/models/User';
|
|
8
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
9
|
+
import EnvVars from '@src/constants/EnvVars';
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const jwtNotPresentErr = 'JWT not present in signed cookie.',
|
|
17
|
-
userUnauthErr = 'User not authorized to perform this action';
|
|
11
|
+
import JwtUtil from '@src/util/JwtUtil';
|
|
12
|
+
import { ISessionUser, UserRoles } from '@src/models/User';
|
|
18
13
|
|
|
19
14
|
|
|
15
|
+
// **** Variables **** //
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export interface ISessionUser extends JwtPayload {
|
|
24
|
-
id: number;
|
|
25
|
-
email: string;
|
|
26
|
-
name: string;
|
|
27
|
-
role: IUser['role'];
|
|
28
|
-
}
|
|
17
|
+
const JWT_NOT_PRESENT_ERR = 'JWT not present in signed cookie.',
|
|
18
|
+
USER_UNAUTHORIZED_ERR = 'User not authorized to perform this action';
|
|
29
19
|
|
|
30
20
|
|
|
31
21
|
// **** Functions **** //
|
|
@@ -39,15 +29,15 @@ async function adminMw(
|
|
|
39
29
|
next: NextFunction,
|
|
40
30
|
) {
|
|
41
31
|
// Extract the token
|
|
42
|
-
const
|
|
43
|
-
jwt = req.signedCookies[
|
|
32
|
+
const { Key } = EnvVars.CookieProps,
|
|
33
|
+
jwt = req.signedCookies[Key];
|
|
44
34
|
if (!jwt) {
|
|
45
35
|
return res
|
|
46
36
|
.status(HttpStatusCodes.UNAUTHORIZED)
|
|
47
|
-
.json({ error:
|
|
37
|
+
.json({ error: JWT_NOT_PRESENT_ERR });
|
|
48
38
|
}
|
|
49
39
|
// Make sure user role is an admin
|
|
50
|
-
const clientData = await
|
|
40
|
+
const clientData = await JwtUtil.decode<ISessionUser & JwtPayload>(jwt);
|
|
51
41
|
if (
|
|
52
42
|
typeof clientData === 'object' &&
|
|
53
43
|
clientData.role === UserRoles.Admin
|
|
@@ -58,7 +48,7 @@ async function adminMw(
|
|
|
58
48
|
} else {
|
|
59
49
|
return res
|
|
60
50
|
.status(HttpStatusCodes.UNAUTHORIZED)
|
|
61
|
-
.json({ error:
|
|
51
|
+
.json({ error: USER_UNAUTHORIZED_ERR });
|
|
62
52
|
}
|
|
63
53
|
}
|
|
64
54
|
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as e from 'express';
|
|
2
|
+
|
|
3
|
+
import { ISessionUser } from '@src/models/User';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// **** Express **** //
|
|
7
|
+
|
|
8
|
+
export interface IReq<T = void> extends e.Request {
|
|
9
|
+
body: T;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IRes extends e.Response {
|
|
13
|
+
locals: {
|
|
14
|
+
sessionUser?: ISessionUser;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import HttpStatusCodes from '@src/
|
|
1
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
2
2
|
|
|
3
3
|
import userService from '@src/services/user-service';
|
|
4
4
|
import { IUser } from '@src/models/User';
|
|
@@ -8,13 +8,7 @@ import { IReq, IRes } from './shared/types';
|
|
|
8
8
|
// **** Variables **** //
|
|
9
9
|
|
|
10
10
|
// Paths
|
|
11
|
-
|
|
12
|
-
basePath: '/users',
|
|
13
|
-
get: '/all',
|
|
14
|
-
add: '/add',
|
|
15
|
-
update: '/update',
|
|
16
|
-
delete: '/delete/:id',
|
|
17
|
-
} as const;
|
|
11
|
+
|
|
18
12
|
|
|
19
13
|
|
|
20
14
|
// **** Functions **** //
|
|
@@ -1,47 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup express server.
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import cookieParser from 'cookie-parser';
|
|
2
6
|
import morgan from 'morgan';
|
|
3
7
|
import path from 'path';
|
|
4
8
|
import helmet from 'helmet';
|
|
5
9
|
import express, { Request, Response, NextFunction } from 'express';
|
|
10
|
+
import logger from 'jet-logger';
|
|
6
11
|
|
|
7
12
|
import 'express-async-errors';
|
|
8
13
|
|
|
9
|
-
import BaseRouter from '
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import { RouteError } from '@src/declarations/classes';
|
|
14
|
+
import BaseRouter from '@src/routes/api';
|
|
15
|
+
import Paths from '@src/routes/constants/Paths';
|
|
16
|
+
|
|
17
|
+
import EnvVars from '@src/constants/EnvVars';
|
|
18
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
15
19
|
|
|
20
|
+
import { NodeEnvs } from '@src/constants/misc';
|
|
21
|
+
import { RouteError } from '@src/other/classes';
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
// **** Variables **** //
|
|
18
25
|
|
|
19
26
|
const app = express();
|
|
20
27
|
|
|
21
28
|
|
|
22
|
-
// ****
|
|
29
|
+
// **** Setup **** //
|
|
23
30
|
|
|
31
|
+
// Basic middleware
|
|
24
32
|
app.use(express.json());
|
|
25
33
|
app.use(express.urlencoded({extended: true}));
|
|
26
|
-
app.use(cookieParser(EnvVars.
|
|
34
|
+
app.use(cookieParser(EnvVars.CookieProps.Secret));
|
|
27
35
|
|
|
28
36
|
// Show routes called in console during development
|
|
29
|
-
if (EnvVars.
|
|
37
|
+
if (EnvVars.NodeEnv === NodeEnvs.Dev) {
|
|
30
38
|
app.use(morgan('dev'));
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
// Security
|
|
34
|
-
if (EnvVars.
|
|
42
|
+
if (EnvVars.NodeEnv === NodeEnvs.Production) {
|
|
35
43
|
app.use(helmet());
|
|
36
44
|
}
|
|
37
45
|
|
|
46
|
+
// Add APIs, must be after middleware
|
|
47
|
+
app.use(Paths.Base, BaseRouter);
|
|
38
48
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
// Add APIs
|
|
42
|
-
app.use('/api', BaseRouter);
|
|
43
|
-
|
|
44
|
-
// Setup error handler
|
|
49
|
+
// Add error handler
|
|
45
50
|
app.use((
|
|
46
51
|
err: Error,
|
|
47
52
|
_: Request,
|
|
@@ -49,7 +54,9 @@ app.use((
|
|
|
49
54
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
50
55
|
next: NextFunction,
|
|
51
56
|
) => {
|
|
52
|
-
|
|
57
|
+
if (EnvVars.NodeEnv !== NodeEnvs.Test) {
|
|
58
|
+
logger.err(err, true);
|
|
59
|
+
}
|
|
53
60
|
let status = HttpStatusCodes.BAD_REQUEST;
|
|
54
61
|
if (err instanceof RouteError) {
|
|
55
62
|
status = err.status;
|
|
@@ -58,7 +65,7 @@ app.use((
|
|
|
58
65
|
});
|
|
59
66
|
|
|
60
67
|
|
|
61
|
-
//
|
|
68
|
+
// ** Front-End Content ** //
|
|
62
69
|
|
|
63
70
|
// Set views directory (html)
|
|
64
71
|
const viewsDir = path.join(__dirname, 'views');
|
|
@@ -70,12 +77,12 @@ app.use(express.static(staticDir));
|
|
|
70
77
|
|
|
71
78
|
// Nav to login pg by default
|
|
72
79
|
app.get('/', (_: Request, res: Response) => {
|
|
73
|
-
res.sendFile('login.html', {root: viewsDir});
|
|
80
|
+
res.sendFile('login.html', { root: viewsDir });
|
|
74
81
|
});
|
|
75
82
|
|
|
76
83
|
// Redirect to login if not logged in.
|
|
77
84
|
app.get('/users', (req: Request, res: Response) => {
|
|
78
|
-
const jwt = req.signedCookies[EnvVars.
|
|
85
|
+
const jwt = req.signedCookies[EnvVars.CookieProps.Key];
|
|
79
86
|
if (!jwt) {
|
|
80
87
|
res.redirect('/');
|
|
81
88
|
} else {
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
1
|
+
import UserRepo from '@src/repos/UserRepo';
|
|
2
|
+
|
|
3
|
+
import JwtUtil from '@src/util/JwtUtil';
|
|
4
|
+
import PwdUtil from '@src/util/PwdUtil';
|
|
5
|
+
import { tick } from '@src/util/misc';
|
|
6
|
+
|
|
7
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
8
|
+
import { RouteError } from '@src/other/classes';
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
// **** Variables **** //
|
|
10
12
|
|
|
11
13
|
// Errors
|
|
12
|
-
export const
|
|
13
|
-
|
|
14
|
+
export const Errors = {
|
|
15
|
+
Unauth: 'Unauthorized',
|
|
14
16
|
emailNotFound: (email: string) => `User with email "${email}" not found`,
|
|
15
17
|
} as const;
|
|
16
18
|
|
|
@@ -22,26 +24,26 @@ export const errors = {
|
|
|
22
24
|
*/
|
|
23
25
|
async function getJwt(email: string, password: string): Promise<string> {
|
|
24
26
|
// Fetch user
|
|
25
|
-
const user = await
|
|
27
|
+
const user = await UserRepo.getOne(email);
|
|
26
28
|
if (!user) {
|
|
27
29
|
throw new RouteError(
|
|
28
30
|
HttpStatusCodes.UNAUTHORIZED,
|
|
29
|
-
|
|
31
|
+
Errors.emailNotFound(email),
|
|
30
32
|
);
|
|
31
33
|
}
|
|
32
34
|
// Check password
|
|
33
|
-
const hash = (user.pwdHash ?? '')
|
|
34
|
-
|
|
35
|
+
const hash = (user.pwdHash ?? ''),
|
|
36
|
+
pwdPassed = await PwdUtil.compare(password, hash);
|
|
35
37
|
if (!pwdPassed) {
|
|
36
38
|
// If password failed, wait 500ms this will increase security
|
|
37
39
|
await tick(500);
|
|
38
40
|
throw new RouteError(
|
|
39
41
|
HttpStatusCodes.UNAUTHORIZED,
|
|
40
|
-
|
|
42
|
+
Errors.Unauth,
|
|
41
43
|
);
|
|
42
44
|
}
|
|
43
45
|
// Setup Admin Cookie
|
|
44
|
-
return
|
|
46
|
+
return JwtUtil.sign({
|
|
45
47
|
id: user.id,
|
|
46
48
|
email: user.name,
|
|
47
49
|
name: user.name,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import UserRepo from '@src/repos/UserRepo';
|
|
2
2
|
import { IUser } from '@src/models/User';
|
|
3
|
-
import { RouteError } from '@src/
|
|
4
|
-
import HttpStatusCodes from '@src/
|
|
3
|
+
import { RouteError } from '@src/other/classes';
|
|
4
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
// **** Variables **** //
|
|
8
8
|
|
|
9
|
-
export const
|
|
9
|
+
export const USER_NOT_FOUND_ERR = 'User not found';
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
// **** Functions **** //
|
|
@@ -15,44 +15,44 @@ export const userNotFoundErr = 'User not found';
|
|
|
15
15
|
* Get all users.
|
|
16
16
|
*/
|
|
17
17
|
function getAll(): Promise<IUser[]> {
|
|
18
|
-
return
|
|
18
|
+
return UserRepo.getAll();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Add one user.
|
|
23
23
|
*/
|
|
24
24
|
function addOne(user: IUser): Promise<void> {
|
|
25
|
-
return
|
|
25
|
+
return UserRepo.add(user);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Update one user.
|
|
30
30
|
*/
|
|
31
31
|
async function updateOne(user: IUser): Promise<void> {
|
|
32
|
-
const persists = await
|
|
32
|
+
const persists = await UserRepo.persists(user.id);
|
|
33
33
|
if (!persists) {
|
|
34
34
|
throw new RouteError(
|
|
35
35
|
HttpStatusCodes.NOT_FOUND,
|
|
36
|
-
|
|
36
|
+
USER_NOT_FOUND_ERR,
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
// Return user
|
|
40
|
-
return
|
|
40
|
+
return UserRepo.update(user);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Delete a user by their id.
|
|
45
45
|
*/
|
|
46
46
|
async function _delete(id: number): Promise<void> {
|
|
47
|
-
const persists = await
|
|
47
|
+
const persists = await UserRepo.persists(id);
|
|
48
48
|
if (!persists) {
|
|
49
49
|
throw new RouteError(
|
|
50
50
|
HttpStatusCodes.NOT_FOUND,
|
|
51
|
-
|
|
51
|
+
USER_NOT_FOUND_ERR,
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
54
|
// Delete user
|
|
55
|
-
return
|
|
55
|
+
return UserRepo.delete(id);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import jsonwebtoken from 'jsonwebtoken';
|
|
2
|
-
import EnvVars from '../
|
|
2
|
+
import EnvVars from '../constants/EnvVars';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
// **** Variables **** //
|
|
6
6
|
|
|
7
7
|
// Errors
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const Errors = {
|
|
9
|
+
Validation: 'JSON-web-token validation failed.',
|
|
10
10
|
} as const;
|
|
11
11
|
|
|
12
12
|
// Options
|
|
13
|
-
const
|
|
14
|
-
expiresIn: EnvVars.
|
|
13
|
+
const Options = {
|
|
14
|
+
expiresIn: EnvVars.Jwt.Exp,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
|
|
@@ -22,7 +22,7 @@ const options = {
|
|
|
22
22
|
*/
|
|
23
23
|
function sign(data: string | object | Buffer): Promise<string> {
|
|
24
24
|
return new Promise((res, rej) => {
|
|
25
|
-
jsonwebtoken.sign(data, EnvVars.
|
|
25
|
+
jsonwebtoken.sign(data, EnvVars.Jwt.Secret, Options, (err, token) => {
|
|
26
26
|
return err ? rej(err) : res(token || '');
|
|
27
27
|
});
|
|
28
28
|
});
|
|
@@ -33,8 +33,8 @@ function sign(data: string | object | Buffer): Promise<string> {
|
|
|
33
33
|
*/
|
|
34
34
|
function decode<T>(jwt: string): Promise<string | undefined | T> {
|
|
35
35
|
return new Promise((res, rej) => {
|
|
36
|
-
jsonwebtoken.verify(jwt, EnvVars.
|
|
37
|
-
return err ? rej(
|
|
36
|
+
jsonwebtoken.verify(jwt, EnvVars.Jwt.Secret, (err, decoded) => {
|
|
37
|
+
return err ? rej(Errors.Validation) : res(decoded as T);
|
|
38
38
|
});
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -3,7 +3,7 @@ import bcrypt from 'bcrypt';
|
|
|
3
3
|
|
|
4
4
|
// **** Variables **** //
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const SALT_ROUNDS = 12;
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// **** Functions **** //
|
|
@@ -12,14 +12,14 @@ const saltRounds = 12;
|
|
|
12
12
|
* Get a hash from the password.
|
|
13
13
|
*/
|
|
14
14
|
function getHash(pwd: string): Promise<string> {
|
|
15
|
-
return bcrypt.hash(pwd,
|
|
15
|
+
return bcrypt.hash(pwd, SALT_ROUNDS);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Useful for testing.
|
|
20
20
|
*/
|
|
21
21
|
function hashSync(pwd: string): string {
|
|
22
|
-
return bcrypt.hashSync(pwd,
|
|
22
|
+
return bcrypt.hashSync(pwd, SALT_ROUNDS);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|