express-generator-typescript 2.0.2 → 2.0.5
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/bin/cli.js +24 -23
- package/lib/cli.js +24 -22
- package/lib/express-generator-typescript.js +19 -24
- package/lib/project-files/src/models/user-model.ts +16 -2
- package/lib/project-files/src/public/scripts/users.js +6 -2
- package/lib/project-files/src/routes/auth-router.ts +6 -8
- package/lib/project-files/src/routes/middleware.ts +2 -2
- package/lib/project-files/src/routes/user-router.ts +16 -19
- package/lib/project-files/src/routes/validators.ts +57 -0
- package/lib/project-files/src/services/auth-service.ts +2 -1
- package/lib/project-files/src/shared/errors.ts +10 -3
- package/lib/project-files/src/shared/functions.ts +2 -0
- package/lib/project-files/src/shared/types.ts +11 -3
- package/lib/project-files/src/types/express/index.d.ts +0 -9
- package/lib/project-files/src/util/jwt-util.ts +4 -4
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -10,26 +10,27 @@ const path = require('path');
|
|
|
10
10
|
const expressGenTs = require('../lib/express-generator-typescript');
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
destination =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
// Start
|
|
14
|
+
console.log('Setting up new Express/TypeScript project...');
|
|
15
|
+
|
|
16
|
+
// Setup use yarn
|
|
17
|
+
let useYarn = false;
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
let idx = -1;
|
|
20
|
+
idx = args.indexOf('--use-yarn');
|
|
21
|
+
if (idx > -1) {
|
|
22
|
+
useYarn = true;
|
|
23
|
+
args.splice(idx, 1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Setup destination
|
|
27
|
+
let destination = 'express-gen-ts';
|
|
28
|
+
if (args.length > 0) {
|
|
29
|
+
destination = args[0];
|
|
30
|
+
}
|
|
31
|
+
destination = path.join(process.cwd(), destination);
|
|
32
|
+
|
|
33
|
+
// Creating new project finished
|
|
34
|
+
expressGenTs(destination, useYarn).then(() => {
|
|
35
|
+
console.log('Project setup complete!');
|
|
36
|
+
});
|
package/lib/cli.js
CHANGED
|
@@ -10,25 +10,27 @@ const path = require('path');
|
|
|
10
10
|
const expressGenTs = require('../lib/express-generator-typescript');
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
destination =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
13
|
+
// Start
|
|
14
|
+
console.log('Setting up new Express/TypeScript project...');
|
|
15
|
+
|
|
16
|
+
// Setup use yarn
|
|
17
|
+
let useYarn = false;
|
|
18
|
+
const args = process.argv.slice(2);
|
|
19
|
+
let idx = -1;
|
|
20
|
+
idx = args.indexOf('--use-yarn');
|
|
21
|
+
if (idx > -1) {
|
|
22
|
+
useYarn = true;
|
|
23
|
+
args.splice(idx, 1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Setup destination
|
|
27
|
+
let destination = 'express-gen-ts';
|
|
28
|
+
if (args.length > 0) {
|
|
29
|
+
destination = args[0];
|
|
30
|
+
}
|
|
31
|
+
destination = path.join(process.cwd(), destination);
|
|
32
|
+
|
|
33
|
+
// Creating new project finished
|
|
34
|
+
expressGenTs(destination, useYarn).then(() => {
|
|
35
|
+
console.log('Project setup complete!');
|
|
36
|
+
});
|
|
@@ -13,14 +13,27 @@ const fs = require('fs');
|
|
|
13
13
|
|
|
14
14
|
// **** Variables **** //
|
|
15
15
|
|
|
16
|
+
// Misc
|
|
16
17
|
const prjFolder = './project-files';
|
|
17
18
|
|
|
19
|
+
// ncp options
|
|
18
20
|
const ncpOpts = {
|
|
19
21
|
filter: (fileName) => {
|
|
20
22
|
return !(fileName === 'package-lock.json' || fileName === 'node_modules');
|
|
21
23
|
},
|
|
22
24
|
};
|
|
23
25
|
|
|
26
|
+
// Project-folder dependencies
|
|
27
|
+
const dependencies = 'express dotenv http-status-codes morgan cookie-parser jet-logger ' +
|
|
28
|
+
'module-alias command-line-args express-async-errors helmet jsonfile bcrypt jsonwebtoken';
|
|
29
|
+
|
|
30
|
+
// Project-folder development dependencies
|
|
31
|
+
const devDependencies = 'ts-node typescript nodemon find jasmine supertest ' +
|
|
32
|
+
'@types/node @types/express @types/jasmine @types/find @types/morgan @types/cookie-parser ' +
|
|
33
|
+
'@types/supertest fs-extra tsconfig-paths @types/jsonfile @types/command-line-args ' +
|
|
34
|
+
'@typescript-eslint/eslint-plugin @typescript-eslint/parser eslint @types/fs-extra ' +
|
|
35
|
+
'@types/bcrypt @types/jsonwebtoken eslint-plugin-node';
|
|
36
|
+
|
|
24
37
|
|
|
25
38
|
// **** Functions **** //
|
|
26
39
|
|
|
@@ -32,8 +45,7 @@ async function expressGenTs(destination, useYarn) {
|
|
|
32
45
|
await copyProjectFiles(destination);
|
|
33
46
|
updatePackageJson(destination);
|
|
34
47
|
await renameGitigoreFile(destination);
|
|
35
|
-
|
|
36
|
-
downloadNodeModules(destination, depStrings, useYarn);
|
|
48
|
+
downloadNodeModules(destination, useYarn);
|
|
37
49
|
} catch (err) {
|
|
38
50
|
console.error(err);
|
|
39
51
|
}
|
|
@@ -75,36 +87,19 @@ function renameGitigoreFile(destination) {
|
|
|
75
87
|
});
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
/**
|
|
79
|
-
* Setup dependency strings. This way gets you the latest version of everything.
|
|
80
|
-
*/
|
|
81
|
-
function getDepStrings() {
|
|
82
|
-
let dependencies = 'express dotenv http-status-codes morgan cookie-parser jet-logger ' +
|
|
83
|
-
'module-alias command-line-args express-async-errors helmet jsonfile bcrypt ' +
|
|
84
|
-
'jsonwebtoken';
|
|
85
|
-
let devDependencies = 'ts-node typescript nodemon find jasmine supertest ' +
|
|
86
|
-
'@types/node @types/express @types/jasmine @types/find @types/morgan ' +
|
|
87
|
-
'@types/cookie-parser @types/supertest fs-extra tsconfig-paths @types/jsonfile ' +
|
|
88
|
-
'@types/command-line-args @typescript-eslint/eslint-plugin @typescript-eslint/parser ' +
|
|
89
|
-
'eslint @types/fs-extra @types/bcrypt @types/jsonwebtoken ' +
|
|
90
|
-
'eslint-plugin-node';
|
|
91
|
-
// Return
|
|
92
|
-
return { dependencies, devDependencies };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
90
|
/**
|
|
96
91
|
* Download the dependencies.
|
|
97
92
|
*/
|
|
98
|
-
function downloadNodeModules(destination,
|
|
93
|
+
function downloadNodeModules(destination, useYarn) {
|
|
99
94
|
const options = {cwd: destination};
|
|
100
95
|
let downloadLibCmd;
|
|
101
96
|
let downloadDepCmd;
|
|
102
97
|
if (useYarn) {
|
|
103
|
-
downloadLibCmd = 'yarn add ' +
|
|
104
|
-
downloadDepCmd = 'yarn add ' +
|
|
98
|
+
downloadLibCmd = 'yarn add ' + dependencies;
|
|
99
|
+
downloadDepCmd = 'yarn add ' + devDependencies + ' -D';
|
|
105
100
|
} else {
|
|
106
|
-
downloadLibCmd = 'npm i -s ' +
|
|
107
|
-
downloadDepCmd = 'npm i -D ' +
|
|
101
|
+
downloadLibCmd = 'npm i -s ' + dependencies;
|
|
102
|
+
downloadDepCmd = 'npm i -D ' + devDependencies;
|
|
108
103
|
}
|
|
109
104
|
childProcess.execSync(downloadLibCmd, options);
|
|
110
105
|
childProcess.execSync(downloadDepCmd, options);
|
|
@@ -9,8 +9,8 @@ export interface IUser {
|
|
|
9
9
|
id: number;
|
|
10
10
|
name: string;
|
|
11
11
|
email: string;
|
|
12
|
-
pwdHash
|
|
13
|
-
role
|
|
12
|
+
pwdHash?: string;
|
|
13
|
+
role?: UserRoles;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
|
|
@@ -47,10 +47,24 @@ function copy(user: IUser): IUser {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* See if an object is an instance of User.
|
|
52
|
+
*/
|
|
53
|
+
function instanceOf(arg: object): boolean {
|
|
54
|
+
return (
|
|
55
|
+
typeof arg === 'object' &&
|
|
56
|
+
'id' in arg &&
|
|
57
|
+
'email' in arg &&
|
|
58
|
+
'name' in arg &&
|
|
59
|
+
'role' in arg
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
50
63
|
|
|
51
64
|
// **** Export default **** //
|
|
52
65
|
|
|
53
66
|
export default {
|
|
54
67
|
new: _new,
|
|
55
68
|
copy,
|
|
69
|
+
instanceOf,
|
|
56
70
|
};
|
|
@@ -34,7 +34,7 @@ function getUserDisplayEle(user) {
|
|
|
34
34
|
<div class="normal-view">
|
|
35
35
|
<div>Name: ${user.name}</div>
|
|
36
36
|
<div>Email: ${user.email}</div>
|
|
37
|
-
<button class="edit-user-btn" data-user-id="${user.id}">
|
|
37
|
+
<button class="edit-user-btn" data-user-id="${user.id}" data-user-role="${user.role}">
|
|
38
38
|
Edit
|
|
39
39
|
</button>
|
|
40
40
|
<button class="delete-user-btn" data-user-id="${user.id}">
|
|
@@ -90,8 +90,10 @@ function addUser() {
|
|
|
90
90
|
var emailInput = document.getElementById('email-input');
|
|
91
91
|
var data = {
|
|
92
92
|
user: {
|
|
93
|
+
id: -1,
|
|
93
94
|
name: nameInput.value,
|
|
94
95
|
email: emailInput.value,
|
|
96
|
+
role: 0,
|
|
95
97
|
},
|
|
96
98
|
};
|
|
97
99
|
// Call api
|
|
@@ -128,11 +130,13 @@ function submitEdit(ele) {
|
|
|
128
130
|
var nameInput = userEle.getElementsByClassName('name-edit-input')[0];
|
|
129
131
|
var emailInput = userEle.getElementsByClassName('email-edit-input')[0];
|
|
130
132
|
var id = ele.getAttribute('data-user-id');
|
|
133
|
+
var role = ele.getAttribute('data-user-role');
|
|
131
134
|
var data = {
|
|
132
135
|
user: {
|
|
136
|
+
id: Number(id),
|
|
133
137
|
name: nameInput.value,
|
|
134
138
|
email: emailInput.value,
|
|
135
|
-
|
|
139
|
+
role: Number(role),
|
|
136
140
|
},
|
|
137
141
|
};
|
|
138
142
|
Http
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Router } from 'express';
|
|
2
2
|
import StatusCodes from 'http-status-codes';
|
|
3
3
|
|
|
4
4
|
import authService from '@services/auth-service';
|
|
5
|
-
import { ParamMissingError } from '@shared/errors';
|
|
6
5
|
import envVars from 'src/shared/env-vars';
|
|
7
|
-
import { IReq } from '@shared/types';
|
|
6
|
+
import { IReq, IRes } from '@shared/types';
|
|
7
|
+
import validate from './validators';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
// **** Types **** //
|
|
@@ -34,11 +34,9 @@ export const p = {
|
|
|
34
34
|
/**
|
|
35
35
|
* Login a user.
|
|
36
36
|
*/
|
|
37
|
-
router.post(p.login, async (req: IReq<ILoginReq>, res:
|
|
37
|
+
router.post(p.login, async (req: IReq<ILoginReq>, res: IRes) => {
|
|
38
38
|
const { email, password } = req.body;
|
|
39
|
-
|
|
40
|
-
throw new ParamMissingError();
|
|
41
|
-
}
|
|
39
|
+
validate(email, password);
|
|
42
40
|
// Add jwt to cookie
|
|
43
41
|
const jwt = await authService.login(email, password);
|
|
44
42
|
const { key, options } = envVars.cookieProps;
|
|
@@ -50,7 +48,7 @@ router.post(p.login, async (req: IReq<ILoginReq>, res: Response) => {
|
|
|
50
48
|
/**
|
|
51
49
|
* Logout the user.
|
|
52
50
|
*/
|
|
53
|
-
router.get(p.logout, (_:
|
|
51
|
+
router.get(p.logout, (_: IReq, res: IRes) => {
|
|
54
52
|
const { key, options } = envVars.cookieProps;
|
|
55
53
|
res.clearCookie(key, options);
|
|
56
54
|
return res.status(OK).end();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import StatusCodes from 'http-status-codes';
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { JwtPayload } from 'jsonwebtoken';
|
|
3
4
|
|
|
4
5
|
import { IUser, UserRoles } from '@models/user-model';
|
|
5
6
|
import envVars from 'src/shared/env-vars';
|
|
6
7
|
import jwtUtil from '@util/jwt-util';
|
|
7
|
-
import { JwtPayload } from 'jsonwebtoken';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
// **** Variables **** //
|
|
@@ -38,7 +38,7 @@ export async function adminMw(req: Request, res: Response, next: NextFunction) {
|
|
|
38
38
|
}
|
|
39
39
|
// Make sure user role is an admin
|
|
40
40
|
const clientData = await jwtUtil.decode<ISessionUser>(jwt);
|
|
41
|
-
if (clientData.role === UserRoles.Admin) {
|
|
41
|
+
if (typeof clientData === 'object' && clientData.role === UserRoles.Admin) {
|
|
42
42
|
res.locals.sessionUser = clientData;
|
|
43
43
|
next();
|
|
44
44
|
} else {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import StatusCodes from 'http-status-codes';
|
|
2
|
-
import {
|
|
2
|
+
import { Router } from 'express';
|
|
3
3
|
|
|
4
4
|
import userService from '@services/user-service';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
5
|
+
import User, { IUser } from '@models/user-model';
|
|
6
|
+
import { IReq, IRes } from '@shared/types';
|
|
7
|
+
import validate from './validators';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
// **** Variables **** //
|
|
@@ -27,7 +27,7 @@ export const p = {
|
|
|
27
27
|
/**
|
|
28
28
|
* Get all users.
|
|
29
29
|
*/
|
|
30
|
-
router.get(p.get, async (_:
|
|
30
|
+
router.get(p.get, async (_: IReq, res: IRes) => {
|
|
31
31
|
const users = await userService.getAll();
|
|
32
32
|
return res.status(OK).json({ users });
|
|
33
33
|
});
|
|
@@ -35,11 +35,10 @@ router.get(p.get, async (_: Request, res: Response) => {
|
|
|
35
35
|
/**
|
|
36
36
|
* Add one user.
|
|
37
37
|
*/
|
|
38
|
-
router.post(p.add, async (req: IReq<{user: IUser}>, res:
|
|
38
|
+
router.post(p.add, async (req: IReq<{user: IUser}>, res: IRes) => {
|
|
39
39
|
const { user } = req.body;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
40
|
+
validate([user, User.instanceOf]);
|
|
41
|
+
// Add user
|
|
43
42
|
await userService.addOne(user);
|
|
44
43
|
return res.status(CREATED).end();
|
|
45
44
|
});
|
|
@@ -47,11 +46,10 @@ router.post(p.add, async (req: IReq<{user: IUser}>, res: Response) => {
|
|
|
47
46
|
/**
|
|
48
47
|
* Update one user.
|
|
49
48
|
*/
|
|
50
|
-
router.put(p.update, async (req: IReq<{user: IUser}>, res:
|
|
49
|
+
router.put(p.update, async (req: IReq<{user: IUser}>, res: IRes) => {
|
|
51
50
|
const { user } = req.body;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
51
|
+
validate([user, User.instanceOf]);
|
|
52
|
+
// Edit user
|
|
55
53
|
await userService.updateOne(user);
|
|
56
54
|
return res.status(OK).end();
|
|
57
55
|
});
|
|
@@ -59,12 +57,11 @@ router.put(p.update, async (req: IReq<{user: IUser}>, res: Response) => {
|
|
|
59
57
|
/**
|
|
60
58
|
* Delete one user.
|
|
61
59
|
*/
|
|
62
|
-
router.delete(p.delete, async (req:
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
await userService.delete(Number(id));
|
|
60
|
+
router.delete(p.delete, async (req: IReq, res: IRes) => {
|
|
61
|
+
const id = Number(req.params.id);
|
|
62
|
+
validate([id, 'number']);
|
|
63
|
+
// Delete user
|
|
64
|
+
await userService.delete(id);
|
|
68
65
|
return res.status(OK).end();
|
|
69
66
|
});
|
|
70
67
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Put other validator functions here too that may be passed to the validate() function
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ParamInvalidError } from '@shared/errors';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// **** Types **** //
|
|
9
|
+
|
|
10
|
+
type TParam = string | number | boolean | null | object;
|
|
11
|
+
type TParamFull = TParam | ((arg: TParam) => boolean);
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// **** Functions **** //
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Make sure parameter of the specified type. If the type if not provided then string is
|
|
18
|
+
* used as the default type to check. Instead of a type you can also pass a validator function
|
|
19
|
+
* which must return 'true' or 'false'.
|
|
20
|
+
*
|
|
21
|
+
* Example 1: validate(email, [user, 'object'], [isAdmin, 'boolean'])
|
|
22
|
+
* Example 2: validate(password) // will check that password is a string
|
|
23
|
+
* Example 3: validate([isAdmin, 'boolean'])
|
|
24
|
+
* Example 4: validate([user, isInstanceOfUser])
|
|
25
|
+
*/
|
|
26
|
+
function validate(...params: Array<TParam | TParamFull[]>): void {
|
|
27
|
+
for (const param of params) {
|
|
28
|
+
// If param is array check provided type
|
|
29
|
+
if (param instanceof Array) {
|
|
30
|
+
// Validator function provided
|
|
31
|
+
if (typeof param[1] === 'function') {
|
|
32
|
+
const fn = param[1];
|
|
33
|
+
if (!fn(param[0])) {
|
|
34
|
+
throw new Error(`Validator function ${fn.name} failed.`);
|
|
35
|
+
}
|
|
36
|
+
// If type is a number, need to do isNaN, cause type NaN is a 'number'.
|
|
37
|
+
// This could be an issue if you do Number(undefined)
|
|
38
|
+
} else if (param[1] === 'number') {
|
|
39
|
+
if (isNaN(param[0] as number)) {
|
|
40
|
+
throw new ParamInvalidError();
|
|
41
|
+
}
|
|
42
|
+
} else if (typeof param[0] !== param[1]) {
|
|
43
|
+
throw new ParamInvalidError();
|
|
44
|
+
}
|
|
45
|
+
// String is default if type is not provided
|
|
46
|
+
} else {
|
|
47
|
+
if (typeof param !== 'string') {
|
|
48
|
+
throw new ParamInvalidError();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// **** Export default **** //
|
|
56
|
+
|
|
57
|
+
export default validate;
|
|
@@ -17,7 +17,8 @@ async function login(email: string, password: string): Promise<string> {
|
|
|
17
17
|
throw new UnauthorizedError();
|
|
18
18
|
}
|
|
19
19
|
// Check password
|
|
20
|
-
const
|
|
20
|
+
const hash = (user.pwdHash ?? '');
|
|
21
|
+
const pwdPassed = await bcrypt.compare(password, hash);
|
|
21
22
|
if (!pwdPassed) {
|
|
22
23
|
throw new UnauthorizedError();
|
|
23
24
|
}
|
|
@@ -11,7 +11,6 @@ export abstract class CustomError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
export class ParamMissingError extends CustomError {
|
|
16
15
|
|
|
17
16
|
public static readonly Msg = 'One or more of the required parameters was missing.';
|
|
@@ -22,6 +21,15 @@ export class ParamMissingError extends CustomError {
|
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
export class ParamInvalidError extends CustomError {
|
|
25
|
+
|
|
26
|
+
public static readonly Msg = 'One or more of the required was missing or invalid.';
|
|
27
|
+
public static readonly HttpStatus = HttpStatusCodes.BAD_REQUEST;
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
super(ParamMissingError.Msg, ParamMissingError.HttpStatus);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
25
33
|
|
|
26
34
|
export class UserNotFoundError extends CustomError {
|
|
27
35
|
|
|
@@ -33,7 +41,6 @@ export class UserNotFoundError extends CustomError {
|
|
|
33
41
|
}
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
|
|
37
44
|
export class UnauthorizedError extends CustomError {
|
|
38
45
|
|
|
39
46
|
public static readonly Msg = 'Login failed';
|
|
@@ -42,4 +49,4 @@ export class UnauthorizedError extends CustomError {
|
|
|
42
49
|
constructor() {
|
|
43
50
|
super(UnauthorizedError.Msg, UnauthorizedError.HttpStatus);
|
|
44
51
|
}
|
|
45
|
-
}
|
|
52
|
+
}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
import * as e from 'express';
|
|
2
2
|
import { Query } from 'express-serve-static-core';
|
|
3
3
|
|
|
4
|
+
import { ISessionUser } from '@routes/middleware';
|
|
4
5
|
|
|
5
|
-
// **** Generics for Request Object **** //
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// **** Express **** //
|
|
8
|
+
|
|
9
|
+
export interface IReq<T = void> extends e.Request {
|
|
8
10
|
body: T;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export interface IReqQuery<T extends Query, U = void> extends e.Request {
|
|
12
14
|
query: T;
|
|
13
15
|
body: U;
|
|
14
|
-
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IRes extends e.Response {
|
|
19
|
+
locals: {
|
|
20
|
+
sessionUser: ISessionUser;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import 'express';
|
|
2
2
|
|
|
3
|
-
import { ISessionUser } from '@routes/middleware';
|
|
4
|
-
|
|
5
|
-
|
|
6
3
|
|
|
7
4
|
// **** Declaration Merging **** //
|
|
8
5
|
|
|
@@ -11,10 +8,4 @@ declare module 'express' {
|
|
|
11
8
|
export interface Request {
|
|
12
9
|
signedCookies: Record<string, string>;
|
|
13
10
|
}
|
|
14
|
-
|
|
15
|
-
export interface Response {
|
|
16
|
-
locals: {
|
|
17
|
-
sessionUser: ISessionUser;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import jsonwebtoken from 'jsonwebtoken';
|
|
1
|
+
import jsonwebtoken, { JwtPayload } from 'jsonwebtoken';
|
|
2
2
|
import envVars from '../shared/env-vars';
|
|
3
3
|
|
|
4
4
|
|
|
@@ -21,9 +21,9 @@ const options = {
|
|
|
21
21
|
* Encrypt data and return jwt.
|
|
22
22
|
*/
|
|
23
23
|
function sign(data: string | object | Buffer): Promise<string> {
|
|
24
|
-
return new Promise((
|
|
24
|
+
return new Promise((res, rej) => {
|
|
25
25
|
jsonwebtoken.sign(data, envVars.jwt.secret, options, (err, token) => {
|
|
26
|
-
return err ?
|
|
26
|
+
return err ? rej(err) : res(token || '');
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
}
|
|
@@ -31,7 +31,7 @@ function sign(data: string | object | Buffer): Promise<string> {
|
|
|
31
31
|
/**
|
|
32
32
|
* Decrypt JWT and extract client data.
|
|
33
33
|
*/
|
|
34
|
-
function decode<T>(jwt: string): Promise<T> {
|
|
34
|
+
async function decode<T extends JwtPayload>(jwt: string): Promise<string | undefined | T> {
|
|
35
35
|
return new Promise((res, rej) => {
|
|
36
36
|
jsonwebtoken.verify(jwt, envVars.jwt.secret, (err, decoded) => {
|
|
37
37
|
return err ? rej(errors.validation) : res(decoded as T);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-generator-typescript",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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",
|