express-generator-typescript 2.4.14 → 2.4.15
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 +1 -1
- package/lib/project-files/package.json +0 -1
- package/lib/project-files/spec/tests/users.spec.ts +3 -3
- package/lib/project-files/src/common/classes.ts +32 -0
- package/lib/project-files/src/models/User.ts +4 -5
- package/lib/project-files/src/routes/UserRoutes.ts +8 -7
- package/lib/project-files/src/routes/common/check.ts +143 -0
- package/lib/project-files/src/routes/common/types.ts +3 -7
- package/lib/project-files/src/routes/index.ts +6 -29
- package/lib/project-files/src/server.ts +1 -1
- package/lib/project-files/src/services/UserService.ts +1 -1
- package/lib/project-files-auth/package.json +0 -1
- package/lib/project-files-auth/spec/tests/users.spec.ts +3 -3
- package/lib/project-files-auth/src/common/classes.ts +31 -0
- package/lib/project-files-auth/src/models/User.ts +2 -3
- package/lib/project-files-auth/src/routes/AuthRoutes.ts +4 -12
- package/lib/project-files-auth/src/routes/UserRoutes.ts +8 -6
- package/lib/project-files-auth/src/routes/common/check.ts +143 -0
- package/lib/project-files-auth/src/routes/common/types.ts +4 -31
- package/lib/project-files-auth/src/routes/index.ts +11 -44
- package/lib/project-files-auth/src/server.ts +3 -3
- package/lib/project-files-auth/src/services/AuthService.ts +1 -1
- package/lib/project-files-auth/src/services/UserService.ts +1 -1
- package/lib/project-files-auth/src/util/SessionUtil.ts +6 -6
- package/package.json +1 -1
- package/lib/project-files/src/common/RouteError.ts +0 -20
- package/lib/project-files-auth/src/common/RouteError.ts +0 -20
|
@@ -20,7 +20,7 @@ const PROJECT_FOLDER_PATH = './project-files';
|
|
|
20
20
|
// Project-folder dependencies
|
|
21
21
|
const DEPENDENCIES = 'express dotenv morgan cookie-parser jet-logger ' +
|
|
22
22
|
'module-alias express-async-errors helmet jsonfile inserturlparams ' +
|
|
23
|
-
'
|
|
23
|
+
'ts-command-line-args jet-paths moment',
|
|
24
24
|
DEPENDENCIES_AUTH = ' bcrypt jsonwebtoken',
|
|
25
25
|
DEV_DEPENDENCIES = 'ts-node typescript nodemon find jasmine supertest ' +
|
|
26
26
|
'@types/node @types/express @types/jasmine @types/find @types/morgan ' +
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import supertest, { Test } from 'supertest';
|
|
2
2
|
import TestAgent from 'supertest/lib/agent';
|
|
3
|
-
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
4
3
|
import insertUrlParams from 'inserturlparams';
|
|
5
4
|
|
|
6
5
|
import app from '@src/server';
|
|
@@ -13,6 +12,7 @@ import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
|
13
12
|
import Paths from 'spec/support/Paths';
|
|
14
13
|
import apiCb from 'spec/support/apiCb';
|
|
15
14
|
import { TApiCb } from 'spec/types/misc';
|
|
15
|
+
import { ValidationErr } from '@src/common/classes';
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
// Dummy users for GET req
|
|
@@ -63,7 +63,7 @@ describe('UserRouter', () => {
|
|
|
63
63
|
// Test add user
|
|
64
64
|
describe(`"POST:${Paths.Users.Add}"`, () => {
|
|
65
65
|
|
|
66
|
-
const ERROR_MSG =
|
|
66
|
+
const ERROR_MSG = ValidationErr.GetMsg('user'),
|
|
67
67
|
DUMMY_USER = getDummyUsers()[0];
|
|
68
68
|
|
|
69
69
|
// Setup API
|
|
@@ -101,7 +101,7 @@ describe('UserRouter', () => {
|
|
|
101
101
|
// Update users
|
|
102
102
|
describe(`"PUT:${Paths.Users.Update}"`, () => {
|
|
103
103
|
|
|
104
|
-
const ERROR_MSG =
|
|
104
|
+
const ERROR_MSG = ValidationErr.GetMsg('user'),
|
|
105
105
|
DUMMY_USER = getDummyUsers()[0];
|
|
106
106
|
|
|
107
107
|
// Setup API
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Error with status code and message.
|
|
6
|
+
*/
|
|
7
|
+
export class RouteError extends Error {
|
|
8
|
+
|
|
9
|
+
public status: HttpStatusCodes;
|
|
10
|
+
|
|
11
|
+
public constructor(status: HttpStatusCodes, message: string) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* If route validation fails.
|
|
19
|
+
*/
|
|
20
|
+
export class ValidationErr extends RouteError {
|
|
21
|
+
|
|
22
|
+
public static MSG = 'The follow parameter were missing or invalid "';
|
|
23
|
+
|
|
24
|
+
public constructor(paramName: string) {
|
|
25
|
+
super(HttpStatusCodes.BAD_REQUEST, ValidationErr.GetMsg(paramName));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static GetMsg(param: string) {
|
|
29
|
+
return ValidationErr.MSG + param + '".';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -40,17 +40,16 @@ function new_(
|
|
|
40
40
|
* Get user instance from object.
|
|
41
41
|
*/
|
|
42
42
|
function from(param: object): IUser {
|
|
43
|
-
if (
|
|
44
|
-
|
|
43
|
+
if (isUser(param)) {
|
|
44
|
+
return new_(param.name, param.email, param.created, param.id);
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
return new_(p.name, p.email, p.created, p.id);
|
|
46
|
+
throw new Error(INVALID_CONSTRUCTOR_PARAM);
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
/**
|
|
51
50
|
* See if the param meets criteria to be a user.
|
|
52
51
|
*/
|
|
53
|
-
function isUser(arg: unknown):
|
|
52
|
+
function isUser(arg: unknown): arg is IUser {
|
|
54
53
|
return (
|
|
55
54
|
!!arg &&
|
|
56
55
|
typeof arg === 'object' &&
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
2
|
-
|
|
3
2
|
import UserService from '@src/services/UserService';
|
|
4
|
-
import
|
|
3
|
+
import User from '@src/models/User';
|
|
4
|
+
|
|
5
5
|
import { IReq, IRes } from './common/types';
|
|
6
|
+
import check from './common/check';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
// **** Functions **** //
|
|
@@ -18,8 +19,8 @@ async function getAll(_: IReq, res: IRes) {
|
|
|
18
19
|
/**
|
|
19
20
|
* Add one user.
|
|
20
21
|
*/
|
|
21
|
-
async function add(req: IReq
|
|
22
|
-
const
|
|
22
|
+
async function add(req: IReq, res: IRes) {
|
|
23
|
+
const user = check.isValid(req.body, 'user', User.isUser);
|
|
23
24
|
await UserService.addOne(user);
|
|
24
25
|
return res.status(HttpStatusCodes.CREATED).end();
|
|
25
26
|
}
|
|
@@ -27,8 +28,8 @@ async function add(req: IReq<{user: IUser}>, res: IRes) {
|
|
|
27
28
|
/**
|
|
28
29
|
* Update one user.
|
|
29
30
|
*/
|
|
30
|
-
async function update(req: IReq
|
|
31
|
-
const
|
|
31
|
+
async function update(req: IReq, res: IRes) {
|
|
32
|
+
const user = check.isValid(req.body, 'user', User.isUser);
|
|
32
33
|
await UserService.updateOne(user);
|
|
33
34
|
return res.status(HttpStatusCodes.OK).end();
|
|
34
35
|
}
|
|
@@ -37,7 +38,7 @@ async function update(req: IReq<{user: IUser}>, res: IRes) {
|
|
|
37
38
|
* Delete one user.
|
|
38
39
|
*/
|
|
39
40
|
async function delete_(req: IReq, res: IRes) {
|
|
40
|
-
const id =
|
|
41
|
+
const id = check.isNum(req.params, 'id');
|
|
41
42
|
await UserService.delete(id);
|
|
42
43
|
return res.status(HttpStatusCodes.OK).end();
|
|
43
44
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ValidationErr } from '@src/common/classes';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type TReqObj = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check that param/s is a string
|
|
9
|
+
*/
|
|
10
|
+
function isStr(reqObj: TReqObj, params: string): string;
|
|
11
|
+
function isStr(reqObj: TReqObj, params: ReadonlyArray<string>): string[];
|
|
12
|
+
function isStr(
|
|
13
|
+
reqObj: TReqObj,
|
|
14
|
+
params: string | ReadonlyArray<string>,
|
|
15
|
+
): string | string[] {
|
|
16
|
+
return _checkWrapper(reqObj, params, _checkStr);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check validator for string
|
|
21
|
+
*/
|
|
22
|
+
function _checkStr(val: unknown): string | undefined {
|
|
23
|
+
if (!!val && typeof val === 'string') {
|
|
24
|
+
return val;
|
|
25
|
+
} else {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check that param/s is a number.
|
|
32
|
+
*/
|
|
33
|
+
function isNum(reqObj: TReqObj, params: string): number;
|
|
34
|
+
function isNum(reqObj: TReqObj, params: ReadonlyArray<string>): number[];
|
|
35
|
+
function isNum(
|
|
36
|
+
reqObj: TReqObj,
|
|
37
|
+
params: string | ReadonlyArray<string>,
|
|
38
|
+
): number | number[] {
|
|
39
|
+
return _checkWrapper(reqObj, params, _checkNum);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check validator for string
|
|
44
|
+
*/
|
|
45
|
+
function _checkNum(val: unknown): number | undefined {
|
|
46
|
+
const valF = Number(val);
|
|
47
|
+
if (!isNaN(valF)) {
|
|
48
|
+
return valF;
|
|
49
|
+
} else {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check that param/s is a number
|
|
56
|
+
*/
|
|
57
|
+
function isBool(reqObj: TReqObj, params: string): boolean;
|
|
58
|
+
function isBool(reqObj: TReqObj, params: ReadonlyArray<string>): boolean[];
|
|
59
|
+
function isBool(
|
|
60
|
+
reqObj: TReqObj,
|
|
61
|
+
params: string | ReadonlyArray<string>,
|
|
62
|
+
): boolean | boolean[] {
|
|
63
|
+
return _checkWrapper(reqObj, params, _checkBool);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check validator for string
|
|
68
|
+
*/
|
|
69
|
+
function _checkBool(val: unknown): boolean | undefined {
|
|
70
|
+
if (typeof val === 'boolean') {
|
|
71
|
+
return val;
|
|
72
|
+
} else if (typeof val === 'string') {
|
|
73
|
+
val = val.toLowerCase();
|
|
74
|
+
if (val === 'true') {
|
|
75
|
+
return true;
|
|
76
|
+
} else if (val === 'false') {
|
|
77
|
+
return false;
|
|
78
|
+
} else {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check that param satisfies the validator function.
|
|
88
|
+
*/
|
|
89
|
+
function isValid<T>(
|
|
90
|
+
reqObj: TReqObj,
|
|
91
|
+
param: string,
|
|
92
|
+
validatorFn: (param: unknown) => param is T,
|
|
93
|
+
): T {
|
|
94
|
+
const val = reqObj[param];
|
|
95
|
+
if (validatorFn(val)) {
|
|
96
|
+
return val;
|
|
97
|
+
} else {
|
|
98
|
+
throw new ValidationErr(param);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// **** Shared Helpers **** //
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Do stuff based on whether or not params is an array
|
|
107
|
+
*/
|
|
108
|
+
function _checkWrapper<T>(
|
|
109
|
+
reqObj: TReqObj,
|
|
110
|
+
params: string | ReadonlyArray<string>,
|
|
111
|
+
checkFn: (val: unknown) => T | undefined,
|
|
112
|
+
): T | T[] {
|
|
113
|
+
// If is array
|
|
114
|
+
if (params instanceof Array) {
|
|
115
|
+
const retVal: T[] = [];
|
|
116
|
+
for (const param of params) {
|
|
117
|
+
const val = checkFn(reqObj[param]);
|
|
118
|
+
if (val !== undefined) {
|
|
119
|
+
retVal.push(val);
|
|
120
|
+
} else {
|
|
121
|
+
throw new ValidationErr(param);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return retVal;
|
|
125
|
+
}
|
|
126
|
+
// If not an array
|
|
127
|
+
const val = checkFn(reqObj[params]);
|
|
128
|
+
if (val !== undefined) {
|
|
129
|
+
return val;
|
|
130
|
+
}
|
|
131
|
+
// Throw error
|
|
132
|
+
throw new ValidationErr(params);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// **** Export Default **** //
|
|
137
|
+
|
|
138
|
+
export default {
|
|
139
|
+
isStr,
|
|
140
|
+
isNum,
|
|
141
|
+
isBool,
|
|
142
|
+
isValid,
|
|
143
|
+
} as const;
|
|
@@ -8,13 +8,9 @@ import { Response, Request } from 'express';
|
|
|
8
8
|
|
|
9
9
|
// **** Express **** //
|
|
10
10
|
|
|
11
|
-
type
|
|
11
|
+
type TObj = Record<string, unknown>;
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
body: T;
|
|
15
|
-
}
|
|
13
|
+
export type IReq = Request<TObj, void, TObj, TObj>;
|
|
16
14
|
|
|
17
|
-
export
|
|
18
|
-
locals: Record<string, unknown>;
|
|
19
|
-
}
|
|
15
|
+
export type IRes = Response<unknown, TObj>
|
|
20
16
|
|
|
@@ -1,47 +1,24 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import jetValidator from 'jet-validator';
|
|
3
2
|
|
|
4
3
|
import Paths from '../common/Paths';
|
|
5
|
-
import User from '@src/models/User';
|
|
6
4
|
import UserRoutes from './UserRoutes';
|
|
7
5
|
|
|
8
6
|
|
|
9
7
|
// **** Variables **** //
|
|
10
8
|
|
|
11
|
-
const apiRouter = Router()
|
|
12
|
-
validate = jetValidator();
|
|
9
|
+
const apiRouter = Router();
|
|
13
10
|
|
|
14
11
|
|
|
15
12
|
// ** Add UserRouter ** //
|
|
16
13
|
|
|
14
|
+
// Init router
|
|
17
15
|
const userRouter = Router();
|
|
18
16
|
|
|
19
17
|
// Get all users
|
|
20
|
-
userRouter.get(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
// Add one user
|
|
26
|
-
userRouter.post(
|
|
27
|
-
Paths.Users.Add,
|
|
28
|
-
validate(['user', User.isUser]),
|
|
29
|
-
UserRoutes.add,
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
// Update one user
|
|
33
|
-
userRouter.put(
|
|
34
|
-
Paths.Users.Update,
|
|
35
|
-
validate(['user', User.isUser]),
|
|
36
|
-
UserRoutes.update,
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
// Delete one user
|
|
40
|
-
userRouter.delete(
|
|
41
|
-
Paths.Users.Delete,
|
|
42
|
-
validate(['id', 'number', 'params']),
|
|
43
|
-
UserRoutes.delete,
|
|
44
|
-
);
|
|
18
|
+
userRouter.get(Paths.Users.Get, UserRoutes.getAll);
|
|
19
|
+
userRouter.post(Paths.Users.Add, UserRoutes.add);
|
|
20
|
+
userRouter.put(Paths.Users.Update, UserRoutes.update);
|
|
21
|
+
userRouter.delete(Paths.Users.Delete, UserRoutes.delete);
|
|
45
22
|
|
|
46
23
|
// Add UserRouter
|
|
47
24
|
apiRouter.use(Paths.Users.Base, userRouter);
|
|
@@ -16,7 +16,7 @@ import BaseRouter from '@src/routes';
|
|
|
16
16
|
import Paths from '@src/common/Paths';
|
|
17
17
|
import EnvVars from '@src/common/EnvVars';
|
|
18
18
|
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
19
|
-
import RouteError from '@src/common/
|
|
19
|
+
import { RouteError } from '@src/common/classes';
|
|
20
20
|
import { NodeEnvs } from '@src/common/misc';
|
|
21
21
|
|
|
22
22
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import supertest, { Test } from 'supertest';
|
|
2
2
|
import TestAgent from 'supertest/lib/agent';
|
|
3
|
-
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
4
3
|
import insertUrlParams from 'inserturlparams';
|
|
5
4
|
|
|
6
5
|
import app from '@src/server';
|
|
@@ -14,6 +13,7 @@ import Paths from 'spec/support/Paths';
|
|
|
14
13
|
import apiCb from 'spec/support/apiCb';
|
|
15
14
|
import login from 'spec/support/login';
|
|
16
15
|
import { TApiCb } from 'spec/types/misc';
|
|
16
|
+
import { ValidationErr } from '@src/common/classes';
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
// Dummy users for GET req
|
|
@@ -67,7 +67,7 @@ describe('UserRouter', () => {
|
|
|
67
67
|
// Test add user
|
|
68
68
|
describe(`"POST:${Paths.Users.Add}"`, () => {
|
|
69
69
|
|
|
70
|
-
const ERROR_MSG =
|
|
70
|
+
const ERROR_MSG = ValidationErr.GetMsg('user'),
|
|
71
71
|
DUMMY_USER = getDummyUsers()[0];
|
|
72
72
|
|
|
73
73
|
// Setup API
|
|
@@ -105,7 +105,7 @@ describe('UserRouter', () => {
|
|
|
105
105
|
// Update users
|
|
106
106
|
describe(`"PUT:${Paths.Users.Update}"`, () => {
|
|
107
107
|
|
|
108
|
-
const ERROR_MSG =
|
|
108
|
+
const ERROR_MSG = ValidationErr.GetMsg('user'),
|
|
109
109
|
DUMMY_USER = getDummyUsers()[0];
|
|
110
110
|
|
|
111
111
|
// Setup API
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Error with status code and message.
|
|
6
|
+
*/
|
|
7
|
+
export class RouteError extends Error {
|
|
8
|
+
|
|
9
|
+
public status: HttpStatusCodes;
|
|
10
|
+
|
|
11
|
+
public constructor(status: HttpStatusCodes, message: string) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* If route validation fails.
|
|
19
|
+
*/
|
|
20
|
+
export class ValidationErr extends RouteError {
|
|
21
|
+
|
|
22
|
+
public static MSG = 'The follow parameter were missing or invalid "';
|
|
23
|
+
|
|
24
|
+
public constructor(paramName: string) {
|
|
25
|
+
super(HttpStatusCodes.BAD_REQUEST, ValidationErr.GetMsg(paramName));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static GetMsg(param: string) {
|
|
29
|
+
return ValidationErr.MSG + param + '".';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -60,14 +60,13 @@ function from(param: object): IUser {
|
|
|
60
60
|
if (!isUser(param)) {
|
|
61
61
|
throw new Error(INVALID_CONSTRUCTOR_PARAM);
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
return new_(p.name, p.email, p.created, p.id);
|
|
63
|
+
return new_(param.name, param.email, param.created, param.id);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
/**
|
|
68
67
|
* See if the param meets criteria to be a user.
|
|
69
68
|
*/
|
|
70
|
-
function isUser(arg: unknown):
|
|
69
|
+
function isUser(arg: unknown): arg is IUser {
|
|
71
70
|
return (
|
|
72
71
|
!!arg &&
|
|
73
72
|
typeof arg === 'object' &&
|
|
@@ -3,14 +3,7 @@ import SessionUtil from '@src/util/SessionUtil';
|
|
|
3
3
|
import AuthService from '@src/services/AuthService';
|
|
4
4
|
|
|
5
5
|
import { IReq, IRes } from './common/types';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// **** Types **** //
|
|
9
|
-
|
|
10
|
-
interface ILoginReq {
|
|
11
|
-
email: string;
|
|
12
|
-
password: string;
|
|
13
|
-
}
|
|
6
|
+
import check from './common/check';
|
|
14
7
|
|
|
15
8
|
|
|
16
9
|
// **** Functions **** //
|
|
@@ -18,10 +11,9 @@ interface ILoginReq {
|
|
|
18
11
|
/**
|
|
19
12
|
* Login a user.
|
|
20
13
|
*/
|
|
21
|
-
async function login(req: IReq
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const user = await AuthService.login(email, password);
|
|
14
|
+
async function login(req: IReq, res: IRes) {
|
|
15
|
+
const [ email, password ] = check.isStr(req.body, ['email', 'password']),
|
|
16
|
+
user = await AuthService.login(email, password);
|
|
25
17
|
// Setup Admin Cookie
|
|
26
18
|
await SessionUtil.addSessionData(res, {
|
|
27
19
|
id: user.id,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
2
2
|
|
|
3
3
|
import UserService from '@src/services/UserService';
|
|
4
|
-
import
|
|
4
|
+
import User from '@src/models/User';
|
|
5
|
+
|
|
5
6
|
import { IReq, IRes } from './common/types';
|
|
7
|
+
import check from './common/check';
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
// **** Functions **** //
|
|
@@ -18,8 +20,8 @@ async function getAll(_: IReq, res: IRes) {
|
|
|
18
20
|
/**
|
|
19
21
|
* Add one user.
|
|
20
22
|
*/
|
|
21
|
-
async function add(req: IReq
|
|
22
|
-
const
|
|
23
|
+
async function add(req: IReq, res: IRes) {
|
|
24
|
+
const user = check.isValid(req.body, 'user', User.isUser);
|
|
23
25
|
await UserService.addOne(user);
|
|
24
26
|
return res.status(HttpStatusCodes.CREATED).end();
|
|
25
27
|
}
|
|
@@ -27,8 +29,8 @@ async function add(req: IReq<{user: IUser}>, res: IRes) {
|
|
|
27
29
|
/**
|
|
28
30
|
* Update one user.
|
|
29
31
|
*/
|
|
30
|
-
async function update(req: IReq
|
|
31
|
-
const
|
|
32
|
+
async function update(req: IReq, res: IRes) {
|
|
33
|
+
const user = check.isValid(req.body, 'user', User.isUser);
|
|
32
34
|
await UserService.updateOne(user);
|
|
33
35
|
return res.status(HttpStatusCodes.OK).end();
|
|
34
36
|
}
|
|
@@ -37,7 +39,7 @@ async function update(req: IReq<{user: IUser}>, res: IRes) {
|
|
|
37
39
|
* Delete one user.
|
|
38
40
|
*/
|
|
39
41
|
async function delete_(req: IReq, res: IRes) {
|
|
40
|
-
const id =
|
|
42
|
+
const id = check.isNum(req.params, 'id');
|
|
41
43
|
await UserService.delete(id);
|
|
42
44
|
return res.status(HttpStatusCodes.OK).end();
|
|
43
45
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ValidationErr } from '@src/common/classes';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
type TReqObj = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Check that param/s is a string
|
|
9
|
+
*/
|
|
10
|
+
function isStr(reqObj: TReqObj, params: string): string;
|
|
11
|
+
function isStr(reqObj: TReqObj, params: ReadonlyArray<string>): string[];
|
|
12
|
+
function isStr(
|
|
13
|
+
reqObj: TReqObj,
|
|
14
|
+
params: string | ReadonlyArray<string>,
|
|
15
|
+
): string | string[] {
|
|
16
|
+
return _checkWrapper(reqObj, params, _checkStr);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check validator for string
|
|
21
|
+
*/
|
|
22
|
+
function _checkStr(val: unknown): string | undefined {
|
|
23
|
+
if (!!val && typeof val === 'string') {
|
|
24
|
+
return val;
|
|
25
|
+
} else {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check that param/s is a number.
|
|
32
|
+
*/
|
|
33
|
+
function isNum(reqObj: TReqObj, params: string): number;
|
|
34
|
+
function isNum(reqObj: TReqObj, params: ReadonlyArray<string>): number[];
|
|
35
|
+
function isNum(
|
|
36
|
+
reqObj: TReqObj,
|
|
37
|
+
params: string | ReadonlyArray<string>,
|
|
38
|
+
): number | number[] {
|
|
39
|
+
return _checkWrapper(reqObj, params, _checkNum);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check validator for string
|
|
44
|
+
*/
|
|
45
|
+
function _checkNum(val: unknown): number | undefined {
|
|
46
|
+
const valF = Number(val);
|
|
47
|
+
if (!isNaN(valF)) {
|
|
48
|
+
return valF;
|
|
49
|
+
} else {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check that param/s is a number
|
|
56
|
+
*/
|
|
57
|
+
function isBool(reqObj: TReqObj, params: string): boolean;
|
|
58
|
+
function isBool(reqObj: TReqObj, params: ReadonlyArray<string>): boolean[];
|
|
59
|
+
function isBool(
|
|
60
|
+
reqObj: TReqObj,
|
|
61
|
+
params: string | ReadonlyArray<string>,
|
|
62
|
+
): boolean | boolean[] {
|
|
63
|
+
return _checkWrapper(reqObj, params, _checkBool);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Check validator for string
|
|
68
|
+
*/
|
|
69
|
+
function _checkBool(val: unknown): boolean | undefined {
|
|
70
|
+
if (typeof val === 'boolean') {
|
|
71
|
+
return val;
|
|
72
|
+
} else if (typeof val === 'string') {
|
|
73
|
+
val = val.toLowerCase();
|
|
74
|
+
if (val === 'true') {
|
|
75
|
+
return true;
|
|
76
|
+
} else if (val === 'false') {
|
|
77
|
+
return false;
|
|
78
|
+
} else {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check that param satisfies the validator function.
|
|
88
|
+
*/
|
|
89
|
+
function isValid<T>(
|
|
90
|
+
reqObj: TReqObj,
|
|
91
|
+
param: string,
|
|
92
|
+
validatorFn: (param: unknown) => param is T,
|
|
93
|
+
): T {
|
|
94
|
+
const val = reqObj[param];
|
|
95
|
+
if (validatorFn(val)) {
|
|
96
|
+
return val;
|
|
97
|
+
} else {
|
|
98
|
+
throw new ValidationErr(param);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// **** Shared Helpers **** //
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Do stuff based on whether or not params is an array
|
|
107
|
+
*/
|
|
108
|
+
function _checkWrapper<T>(
|
|
109
|
+
reqObj: TReqObj,
|
|
110
|
+
params: string | ReadonlyArray<string>,
|
|
111
|
+
checkFn: (val: unknown) => T | undefined,
|
|
112
|
+
): T | T[] {
|
|
113
|
+
// If is array
|
|
114
|
+
if (params instanceof Array) {
|
|
115
|
+
const retVal: T[] = [];
|
|
116
|
+
for (const param of params) {
|
|
117
|
+
const val = checkFn(reqObj[param]);
|
|
118
|
+
if (val !== undefined) {
|
|
119
|
+
retVal.push(val);
|
|
120
|
+
} else {
|
|
121
|
+
throw new ValidationErr(param);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return retVal;
|
|
125
|
+
}
|
|
126
|
+
// If not an array
|
|
127
|
+
const val = checkFn(reqObj[params]);
|
|
128
|
+
if (val !== undefined) {
|
|
129
|
+
return val;
|
|
130
|
+
}
|
|
131
|
+
// Throw error
|
|
132
|
+
throw new ValidationErr(params);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// **** Export Default **** //
|
|
137
|
+
|
|
138
|
+
export default {
|
|
139
|
+
isStr,
|
|
140
|
+
isNum,
|
|
141
|
+
isBool,
|
|
142
|
+
isValid,
|
|
143
|
+
} as const;
|
|
@@ -1,28 +1,3 @@
|
|
|
1
|
-
// import * as e from 'express';
|
|
2
|
-
// import { Query } from 'express-serve-static-core';
|
|
3
|
-
|
|
4
|
-
// import { ISessionUser } from '@src/models/User';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// // **** Express **** //
|
|
8
|
-
|
|
9
|
-
// export interface IReq<T = void> extends e.Request {
|
|
10
|
-
// body: T;
|
|
11
|
-
// }
|
|
12
|
-
|
|
13
|
-
// export interface IReqQuery<T extends Query, U = void> extends e.Request {
|
|
14
|
-
// query: T;
|
|
15
|
-
// body: U;
|
|
16
|
-
// }
|
|
17
|
-
|
|
18
|
-
// export interface IRes extends e.Response {
|
|
19
|
-
// locals: {
|
|
20
|
-
// sessionUser: ISessionUser;
|
|
21
|
-
// };
|
|
22
|
-
// }
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/* eslint-disable max-len */
|
|
26
1
|
/**
|
|
27
2
|
* Shared types for routes.
|
|
28
3
|
*/
|
|
@@ -33,17 +8,15 @@ import { ISessionUser } from '@src/models/User';
|
|
|
33
8
|
|
|
34
9
|
// **** Express **** //
|
|
35
10
|
|
|
36
|
-
type
|
|
11
|
+
type TObj = Record<string, unknown>;
|
|
37
12
|
|
|
38
|
-
export interface IReq
|
|
39
|
-
|
|
13
|
+
export interface IReq extends Request<TObj, void, TObj, TObj> {
|
|
14
|
+
signedCookies: Record<string, string>;
|
|
40
15
|
}
|
|
41
16
|
|
|
17
|
+
|
|
42
18
|
interface ILocals {
|
|
43
19
|
sessionUser: ISessionUser;
|
|
44
20
|
}
|
|
45
21
|
|
|
46
|
-
// I know since its a type I should use 'TRes' instead of 'IRes' but I'm
|
|
47
|
-
// leaving it as this cause it looks better alongside 'IReq'
|
|
48
22
|
export type IRes = Response<unknown, ILocals>;
|
|
49
|
-
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import jetValidator from 'jet-validator';
|
|
3
2
|
|
|
4
3
|
import Paths from '@src/common/Paths';
|
|
5
|
-
import User from '@src/models/User';
|
|
6
4
|
|
|
7
5
|
import adminMw from './middleware/adminMw';
|
|
8
6
|
import AuthRoutes from './AuthRoutes';
|
|
@@ -11,61 +9,30 @@ import UserRoutes from './UserRoutes';
|
|
|
11
9
|
|
|
12
10
|
// **** Variables **** //
|
|
13
11
|
|
|
14
|
-
const apiRouter = Router()
|
|
15
|
-
validate = jetValidator();
|
|
12
|
+
const apiRouter = Router();
|
|
16
13
|
|
|
17
14
|
|
|
18
|
-
// ****
|
|
15
|
+
// **** AuthRouter **** //
|
|
19
16
|
|
|
20
17
|
const authRouter = Router();
|
|
21
18
|
|
|
22
|
-
//
|
|
23
|
-
authRouter.post(
|
|
24
|
-
|
|
25
|
-
validate('email', 'password'),
|
|
26
|
-
AuthRoutes.login,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
// Logout user
|
|
30
|
-
authRouter.get(
|
|
31
|
-
Paths.Auth.Logout,
|
|
32
|
-
AuthRoutes.logout,
|
|
33
|
-
);
|
|
19
|
+
// Routes
|
|
20
|
+
authRouter.post(Paths.Auth.Login, AuthRoutes.login);
|
|
21
|
+
authRouter.get(Paths.Auth.Logout, AuthRoutes.logout);
|
|
34
22
|
|
|
35
23
|
// Add AuthRouter
|
|
36
24
|
apiRouter.use(Paths.Auth.Base, authRouter);
|
|
37
25
|
|
|
38
26
|
|
|
39
|
-
//
|
|
27
|
+
// **** UserRouter **** //
|
|
40
28
|
|
|
41
29
|
const userRouter = Router();
|
|
42
30
|
|
|
43
|
-
//
|
|
44
|
-
userRouter.get(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
// Add one user
|
|
50
|
-
userRouter.post(
|
|
51
|
-
Paths.Users.Add,
|
|
52
|
-
validate(['user', User.isUser]),
|
|
53
|
-
UserRoutes.add,
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
// Update one user
|
|
57
|
-
userRouter.put(
|
|
58
|
-
Paths.Users.Update,
|
|
59
|
-
validate(['user', User.isUser]),
|
|
60
|
-
UserRoutes.update,
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
// Delete one user
|
|
64
|
-
userRouter.delete(
|
|
65
|
-
Paths.Users.Delete,
|
|
66
|
-
validate(['id', 'number', 'params']),
|
|
67
|
-
UserRoutes.delete,
|
|
68
|
-
);
|
|
31
|
+
// User Routes
|
|
32
|
+
userRouter.get(Paths.Users.Get, UserRoutes.getAll);
|
|
33
|
+
userRouter.post(Paths.Users.Add, UserRoutes.add);
|
|
34
|
+
userRouter.put(Paths.Users.Update, UserRoutes.update);
|
|
35
|
+
userRouter.delete(Paths.Users.Delete, UserRoutes.delete);
|
|
69
36
|
|
|
70
37
|
// Add UserRouter
|
|
71
38
|
apiRouter.use(Paths.Users.Base, adminMw, userRouter);
|
|
@@ -17,8 +17,8 @@ import Paths from '@src/common/Paths';
|
|
|
17
17
|
import EnvVars from '@src/common/EnvVars';
|
|
18
18
|
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
19
19
|
import { NodeEnvs } from '@src/common/misc';
|
|
20
|
-
|
|
21
|
-
import
|
|
20
|
+
import { RouteError } from '@src/common/classes';
|
|
21
|
+
import { IReq, IRes } from './routes/common/types';
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
// **** Variables **** //
|
|
@@ -81,7 +81,7 @@ app.get('/', (_: Request, res: Response) => {
|
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
// Redirect to login if not logged in.
|
|
84
|
-
app.get('/users', (req:
|
|
84
|
+
app.get('/users', (req: IReq, res: IRes) => {
|
|
85
85
|
const jwt = req.signedCookies[EnvVars.CookieProps.Key];
|
|
86
86
|
if (!jwt) {
|
|
87
87
|
res.redirect('/');
|
|
@@ -4,7 +4,7 @@ import PwdUtil from '@src/util/PwdUtil';
|
|
|
4
4
|
import { tick } from '@src/util/misc';
|
|
5
5
|
|
|
6
6
|
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
7
|
-
import RouteError from '@src/common/
|
|
7
|
+
import { RouteError } from '@src/common/classes';
|
|
8
8
|
|
|
9
9
|
import { IUser } from '@src/models/User';
|
|
10
10
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Request, Response } from 'express';
|
|
2
1
|
import jsonwebtoken from 'jsonwebtoken';
|
|
3
2
|
|
|
4
3
|
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
5
|
-
import RouteError from '@src/common/
|
|
4
|
+
import { RouteError } from '@src/common/classes';
|
|
6
5
|
import EnvVars from '@src/common/EnvVars';
|
|
6
|
+
import { IReq, IRes } from '@src/routes/common/types';
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// **** Variables **** //
|
|
@@ -25,7 +25,7 @@ const Options = {
|
|
|
25
25
|
/**
|
|
26
26
|
* Get session data from request object (i.e. ISessionUser)
|
|
27
27
|
*/
|
|
28
|
-
function getSessionData<T>(req:
|
|
28
|
+
function getSessionData<T>(req: IReq): Promise<string | T | undefined> {
|
|
29
29
|
const { Key } = EnvVars.CookieProps,
|
|
30
30
|
jwt = req.signedCookies[Key];
|
|
31
31
|
return _decode(jwt);
|
|
@@ -52,9 +52,9 @@ function _decode<T>(jwt: string): Promise<string | undefined | T> {
|
|
|
52
52
|
* Add a JWT to the response
|
|
53
53
|
*/
|
|
54
54
|
async function addSessionData(
|
|
55
|
-
res:
|
|
55
|
+
res: IRes,
|
|
56
56
|
data: string | object,
|
|
57
|
-
): Promise<
|
|
57
|
+
): Promise<IRes> {
|
|
58
58
|
if (!res || !data) {
|
|
59
59
|
throw new RouteError(HttpStatusCodes.BAD_REQUEST, Errors.ParamFalsey);
|
|
60
60
|
}
|
|
@@ -79,7 +79,7 @@ function _sign(data: string | object | Buffer): Promise<string> {
|
|
|
79
79
|
/**
|
|
80
80
|
* Remove cookie
|
|
81
81
|
*/
|
|
82
|
-
function clearCookie(res:
|
|
82
|
+
function clearCookie(res: IRes): IRes {
|
|
83
83
|
const { Key, Options } = EnvVars.CookieProps;
|
|
84
84
|
return res.clearCookie(Key, Options);
|
|
85
85
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-generator-typescript",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.15",
|
|
4
4
|
"description": "Generate new Express applications similar to express-generate which but sets it up to use TypeScript instead",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"express-generator-typescript": "node bin/cli.js",
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Error with status code and message
|
|
6
|
-
*/
|
|
7
|
-
class RouteError extends Error {
|
|
8
|
-
|
|
9
|
-
public status: HttpStatusCodes;
|
|
10
|
-
|
|
11
|
-
public constructor(status: HttpStatusCodes, message: string) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.status = status;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// **** Export default **** //
|
|
19
|
-
|
|
20
|
-
export default RouteError;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import HttpStatusCodes from '@src/common/HttpStatusCodes';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Error with status code and message
|
|
6
|
-
*/
|
|
7
|
-
class RouteError extends Error {
|
|
8
|
-
|
|
9
|
-
public status: HttpStatusCodes;
|
|
10
|
-
|
|
11
|
-
public constructor(status: HttpStatusCodes, message: string) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.status = status;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// **** Export default **** //
|
|
19
|
-
|
|
20
|
-
export default RouteError;
|