express-generator-typescript 2.3.7 → 2.4.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.md +5 -34
- package/bin/cli.js +22 -7
- package/lib/cli.js +22 -7
- package/lib/express-generator-typescript.js +38 -28
- package/lib/project-files/spec/tests/users.spec.ts +9 -20
- package/lib/project-files/src/models/User.ts +50 -53
- package/lib/project-files/src/repos/database.json +1 -1
- package/lib/project-files/src/routes/api.ts +1 -24
- package/lib/project-files/src/routes/constants/Paths.ts +0 -5
- package/lib/project-files/src/server.ts +4 -9
- package/lib/project-files/src/views/users.html +0 -5
- package/lib/project-files-auth/.eslintignore +1 -0
- package/lib/project-files-auth/.eslintrc.json +44 -0
- package/lib/project-files-auth/README.md +43 -0
- package/lib/project-files-auth/build.ts +64 -0
- package/lib/project-files-auth/env/development.env +24 -0
- package/lib/project-files-auth/env/production.env +25 -0
- package/lib/project-files-auth/env/test.env +24 -0
- package/lib/project-files-auth/gitignore +3 -0
- package/lib/project-files-auth/package.json +70 -0
- package/lib/project-files-auth/spec/index.ts +77 -0
- package/lib/project-files-auth/spec/nodemon.json +6 -0
- package/lib/project-files-auth/spec/support/jasmine.json +11 -0
- package/lib/{project-files → project-files-auth}/spec/support/login.ts +1 -1
- package/lib/project-files-auth/spec/support/types.ts +1 -0
- package/lib/{project-files → project-files-auth}/spec/tests/auth.spec.ts +2 -2
- package/lib/project-files-auth/spec/tests/users.spec.ts +224 -0
- package/lib/project-files-auth/spec/types/supertest/index.d.ts +14 -0
- package/lib/project-files-auth/src/constants/EnvVars.ts +28 -0
- package/lib/project-files-auth/src/constants/HttpStatusCodes.ts +386 -0
- package/lib/project-files-auth/src/constants/misc.ts +5 -0
- package/lib/project-files-auth/src/index.ts +13 -0
- package/lib/project-files-auth/src/models/User.ts +85 -0
- package/lib/project-files-auth/src/other/classes.ts +17 -0
- package/lib/project-files-auth/src/other/types.ts +4 -0
- package/lib/project-files-auth/src/pre-start.ts +37 -0
- package/lib/project-files-auth/src/public/scripts/http.js +24 -0
- package/lib/project-files-auth/src/public/scripts/users.js +164 -0
- package/lib/project-files-auth/src/public/stylesheets/users.css +50 -0
- package/lib/project-files-auth/src/repos/MockOrm.ts +42 -0
- package/lib/project-files-auth/src/repos/UserRepo.ts +88 -0
- package/lib/project-files-auth/src/repos/database.json +1 -0
- package/lib/project-files-auth/src/routes/UserRoutes.ts +53 -0
- package/lib/project-files-auth/src/routes/api.ts +75 -0
- package/lib/project-files-auth/src/routes/constants/FullPaths.ts +40 -0
- package/lib/project-files-auth/src/routes/constants/Paths.ts +28 -0
- package/lib/project-files-auth/src/routes/types/express/index.d.ts +11 -0
- package/lib/project-files-auth/src/routes/types/express/misc.ts +16 -0
- package/lib/project-files-auth/src/routes/types/types.ts +22 -0
- package/lib/project-files-auth/src/server.ts +96 -0
- package/lib/project-files-auth/src/services/AuthService.ts +56 -0
- package/lib/project-files-auth/src/services/UserService.ts +66 -0
- package/lib/project-files-auth/src/util/PwdUtil.ts +39 -0
- package/lib/project-files-auth/src/util/SessionUtil.ts +92 -0
- package/lib/project-files-auth/src/util/misc.ts +22 -0
- package/lib/project-files-auth/src/views/users.html +42 -0
- package/lib/project-files-auth/tsconfig.json +83 -0
- package/lib/project-files-auth/tsconfig.prod.json +12 -0
- package/package.json +1 -1
- /package/lib/{project-files → project-files-auth}/src/public/scripts/login.js +0 -0
- /package/lib/{project-files → project-files-auth}/src/public/stylesheets/login.css +0 -0
- /package/lib/{project-files → project-files-auth}/src/routes/AuthRoutes.ts +0 -0
- /package/lib/{project-files → project-files-auth}/src/routes/middleware/adminMw.ts +0 -0
- /package/lib/{project-files → project-files-auth}/src/views/login.html +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// ***** Start **** //
|
|
2
|
+
|
|
3
|
+
displayUsers();
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// ***** Fetch and display users **** //
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Call api
|
|
10
|
+
*/
|
|
11
|
+
function displayUsers() {
|
|
12
|
+
Http
|
|
13
|
+
.get('/api/users/all')
|
|
14
|
+
.then(resp => resp.json())
|
|
15
|
+
.then((resp) => {
|
|
16
|
+
var allUsers = resp.users;
|
|
17
|
+
// Empty the anchor
|
|
18
|
+
var allUsersAnchor = document.getElementById('all-users-anchor');
|
|
19
|
+
allUsersAnchor.innerHTML = '';
|
|
20
|
+
// Append users to anchor
|
|
21
|
+
allUsers.forEach((user) => {
|
|
22
|
+
allUsersAnchor.innerHTML += getUserDisplayEle(user);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get user display element
|
|
29
|
+
*/
|
|
30
|
+
function getUserDisplayEle(user) {
|
|
31
|
+
return (
|
|
32
|
+
`<div class="user-display-ele">
|
|
33
|
+
|
|
34
|
+
<div class="normal-view">
|
|
35
|
+
<div>Name: ${user.name}</div>
|
|
36
|
+
<div>Email: ${user.email}</div>
|
|
37
|
+
<button class="edit-user-btn" data-user-id="${user.id}" data-user-role="${user.role}">
|
|
38
|
+
Edit
|
|
39
|
+
</button>
|
|
40
|
+
<button class="delete-user-btn" data-user-id="${user.id}">
|
|
41
|
+
Delete
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="edit-view">
|
|
46
|
+
<div>
|
|
47
|
+
Name: <input class="name-edit-input" value="${user.name}">
|
|
48
|
+
</div>
|
|
49
|
+
<div>
|
|
50
|
+
Email: <input class="email-edit-input" value="${user.email}">
|
|
51
|
+
</div>
|
|
52
|
+
<button class="submit-edit-btn" data-user-id="${user.id}">
|
|
53
|
+
Submit
|
|
54
|
+
</button>
|
|
55
|
+
<button class="cancel-edit-btn" data-user-id="${user.id}">
|
|
56
|
+
Cancel
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
</div>`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// **** Add, Edit, and Delete Users **** //
|
|
65
|
+
|
|
66
|
+
// Setup event listener for button click
|
|
67
|
+
document.addEventListener('click', function (event) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
var ele = event.target;
|
|
70
|
+
if (ele.matches('#add-user-btn')) {
|
|
71
|
+
addUser();
|
|
72
|
+
} else if (ele.matches('.edit-user-btn')) {
|
|
73
|
+
showEditView(ele.parentNode.parentNode);
|
|
74
|
+
} else if (ele.matches('.cancel-edit-btn')) {
|
|
75
|
+
cancelEdit(ele.parentNode.parentNode);
|
|
76
|
+
} else if (ele.matches('.submit-edit-btn')) {
|
|
77
|
+
submitEdit(ele);
|
|
78
|
+
} else if (ele.matches('.delete-user-btn')) {
|
|
79
|
+
deleteUser(ele);
|
|
80
|
+
} else if (ele.matches('#logout-btn')) {
|
|
81
|
+
logoutUser();
|
|
82
|
+
}
|
|
83
|
+
}, false);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Add a new user.
|
|
87
|
+
*/
|
|
88
|
+
function addUser() {
|
|
89
|
+
var nameInput = document.getElementById('name-input');
|
|
90
|
+
var emailInput = document.getElementById('email-input');
|
|
91
|
+
var data = {
|
|
92
|
+
user: {
|
|
93
|
+
id: -1,
|
|
94
|
+
name: nameInput.value,
|
|
95
|
+
email: emailInput.value,
|
|
96
|
+
role: 0,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
// Call api
|
|
100
|
+
Http
|
|
101
|
+
.post('/api/users/add', data)
|
|
102
|
+
.then(() => displayUsers());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Show edit view.
|
|
107
|
+
*/
|
|
108
|
+
function showEditView(userEle) {
|
|
109
|
+
var normalView = userEle.getElementsByClassName('normal-view')[0];
|
|
110
|
+
var editView = userEle.getElementsByClassName('edit-view')[0];
|
|
111
|
+
normalView.style.display = 'none';
|
|
112
|
+
editView.style.display = 'block';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Cancel edit.
|
|
117
|
+
*/
|
|
118
|
+
function cancelEdit(userEle) {
|
|
119
|
+
var normalView = userEle.getElementsByClassName('normal-view')[0];
|
|
120
|
+
var editView = userEle.getElementsByClassName('edit-view')[0];
|
|
121
|
+
normalView.style.display = 'block';
|
|
122
|
+
editView.style.display = 'none';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Submit edit.
|
|
127
|
+
*/
|
|
128
|
+
function submitEdit(ele) {
|
|
129
|
+
var userEle = ele.parentNode.parentNode;
|
|
130
|
+
var nameInput = userEle.getElementsByClassName('name-edit-input')[0];
|
|
131
|
+
var emailInput = userEle.getElementsByClassName('email-edit-input')[0];
|
|
132
|
+
var id = ele.getAttribute('data-user-id');
|
|
133
|
+
var role = ele.getAttribute('data-user-role');
|
|
134
|
+
var data = {
|
|
135
|
+
user: {
|
|
136
|
+
id: Number(id),
|
|
137
|
+
name: nameInput.value,
|
|
138
|
+
email: emailInput.value,
|
|
139
|
+
role: Number(role),
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
Http
|
|
143
|
+
.put('/api/users/update', data)
|
|
144
|
+
.then(() => displayUsers());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Delete a user
|
|
149
|
+
*/
|
|
150
|
+
function deleteUser(ele) {
|
|
151
|
+
var id = ele.getAttribute('data-user-id');
|
|
152
|
+
Http
|
|
153
|
+
.delete('/api/users/delete/' + id)
|
|
154
|
+
.then(() => displayUsers());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// **** Logout **** //
|
|
159
|
+
|
|
160
|
+
function logoutUser() {
|
|
161
|
+
Http
|
|
162
|
+
.get('/api/auth/logout')
|
|
163
|
+
.then(() => window.location.href = '/');
|
|
164
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
body {
|
|
2
|
+
padding: 100px;
|
|
3
|
+
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
body .users-column {
|
|
7
|
+
display: inline-block;
|
|
8
|
+
margin-right: 2em;
|
|
9
|
+
vertical-align: top;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
body .users-column .column-header {
|
|
13
|
+
padding-bottom: 5px;
|
|
14
|
+
font-weight: 700;
|
|
15
|
+
font-size: 1.2em;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/** Add User Column **/
|
|
20
|
+
|
|
21
|
+
body .add-user-col input {
|
|
22
|
+
margin-bottom: 10px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body .add-user-col #add-user-btn {
|
|
26
|
+
margin-top: 2px;
|
|
27
|
+
margin-bottom: 10px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
body .add-user-col #logout-btn {
|
|
31
|
+
margin-top: 25px;
|
|
32
|
+
margin-bottom: 10px;
|
|
33
|
+
font-size: 15px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/** Users Display Column **/
|
|
38
|
+
|
|
39
|
+
body .users-column .user-display-ele {
|
|
40
|
+
padding-bottom: 10px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
body .users-column .user-display-ele button {
|
|
44
|
+
margin-top: 2px;
|
|
45
|
+
margin-bottom: 10px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
body .users-column .user-display-ele .edit-view {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import jsonfile from 'jsonfile';
|
|
4
|
+
|
|
5
|
+
import { IUser } from '@src/models/User';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// **** Variables **** //
|
|
9
|
+
|
|
10
|
+
const DB_FILE_NAME = 'database.json';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// **** Types **** //
|
|
14
|
+
|
|
15
|
+
interface IDb {
|
|
16
|
+
users: IUser[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// **** Functions **** //
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fetch the json from the file.
|
|
24
|
+
*/
|
|
25
|
+
function openDb(): Promise<IDb> {
|
|
26
|
+
return jsonfile.readFile(__dirname + '/' + DB_FILE_NAME) as Promise<IDb>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Update the file.
|
|
31
|
+
*/
|
|
32
|
+
function saveDb(db: IDb): Promise<void> {
|
|
33
|
+
return jsonfile.writeFile((__dirname + '/' + DB_FILE_NAME), db);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// **** Export default **** //
|
|
38
|
+
|
|
39
|
+
export default {
|
|
40
|
+
openDb,
|
|
41
|
+
saveDb,
|
|
42
|
+
} as const;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { IUser } from '@src/models/User';
|
|
2
|
+
import { getRandomInt } from '@src/util/misc';
|
|
3
|
+
import orm from './MockOrm';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// **** Functions **** //
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get one user.
|
|
10
|
+
*/
|
|
11
|
+
async function getOne(email: string): Promise<IUser | null> {
|
|
12
|
+
const db = await orm.openDb();
|
|
13
|
+
for (const user of db.users) {
|
|
14
|
+
if (user.email === email) {
|
|
15
|
+
return user;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* See if a user with the given id exists.
|
|
23
|
+
*/
|
|
24
|
+
async function persists(id: number): Promise<boolean> {
|
|
25
|
+
const db = await orm.openDb();
|
|
26
|
+
for (const user of db.users) {
|
|
27
|
+
if (user.id === id) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get all users.
|
|
36
|
+
*/
|
|
37
|
+
async function getAll(): Promise<IUser[]> {
|
|
38
|
+
const db = await orm.openDb();
|
|
39
|
+
return db.users;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Add one user.
|
|
44
|
+
*/
|
|
45
|
+
async function add(user: IUser): Promise<void> {
|
|
46
|
+
const db = await orm.openDb();
|
|
47
|
+
user.id = getRandomInt();
|
|
48
|
+
db.users.push(user);
|
|
49
|
+
return orm.saveDb(db);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Update a user.
|
|
54
|
+
*/
|
|
55
|
+
async function update(user: IUser): Promise<void> {
|
|
56
|
+
const db = await orm.openDb();
|
|
57
|
+
for (let i = 0; i < db.users.length; i++) {
|
|
58
|
+
if (db.users[i].id === user.id) {
|
|
59
|
+
db.users[i] = user;
|
|
60
|
+
return orm.saveDb(db);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Delete one user.
|
|
67
|
+
*/
|
|
68
|
+
async function delete_(id: number): Promise<void> {
|
|
69
|
+
const db = await orm.openDb();
|
|
70
|
+
for (let i = 0; i < db.users.length; i++) {
|
|
71
|
+
if (db.users[i].id === id) {
|
|
72
|
+
db.users.splice(i, 1);
|
|
73
|
+
return orm.saveDb(db);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
// **** Export default **** //
|
|
80
|
+
|
|
81
|
+
export default {
|
|
82
|
+
getOne,
|
|
83
|
+
persists,
|
|
84
|
+
getAll,
|
|
85
|
+
add,
|
|
86
|
+
update,
|
|
87
|
+
delete: delete_,
|
|
88
|
+
} as const;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"users":[{"name":"Sean Maxwell","email":"sean.maxwell@gmail.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":1,"id":159123164363},{"name":"Gordan Freeman","email":"gordan.freeman@halflife.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":906524522143},{"name":"John Smith","email":"jsmith@yahoo.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":357437875835}]}
|
|
@@ -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;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import jetValidator from 'jet-validator';
|
|
3
|
+
|
|
4
|
+
import adminMw from './middleware/adminMw';
|
|
5
|
+
import Paths from './constants/Paths';
|
|
6
|
+
import User from '@src/models/User';
|
|
7
|
+
import AuthRoutes from './AuthRoutes';
|
|
8
|
+
import UserRoutes from './UserRoutes';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// **** Variables **** //
|
|
12
|
+
|
|
13
|
+
const apiRouter = Router(),
|
|
14
|
+
validate = jetValidator();
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// **** Setup AuthRouter **** //
|
|
18
|
+
|
|
19
|
+
const authRouter = Router();
|
|
20
|
+
|
|
21
|
+
// Login user
|
|
22
|
+
authRouter.post(
|
|
23
|
+
Paths.Auth.Login,
|
|
24
|
+
validate('email', 'password'),
|
|
25
|
+
AuthRoutes.login,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// Logout user
|
|
29
|
+
authRouter.get(
|
|
30
|
+
Paths.Auth.Logout,
|
|
31
|
+
AuthRoutes.logout,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Add AuthRouter
|
|
35
|
+
apiRouter.use(Paths.Auth.Base, authRouter);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// ** Add UserRouter ** //
|
|
39
|
+
|
|
40
|
+
const userRouter = Router();
|
|
41
|
+
|
|
42
|
+
// Get all users
|
|
43
|
+
userRouter.get(
|
|
44
|
+
Paths.Users.Get,
|
|
45
|
+
UserRoutes.getAll,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Add one user
|
|
49
|
+
userRouter.post(
|
|
50
|
+
Paths.Users.Add,
|
|
51
|
+
validate(['user', User.isUser]),
|
|
52
|
+
UserRoutes.add,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Update one user
|
|
56
|
+
userRouter.put(
|
|
57
|
+
Paths.Users.Update,
|
|
58
|
+
validate(['user', User.isUser]),
|
|
59
|
+
UserRoutes.update,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Delete one user
|
|
63
|
+
userRouter.delete(
|
|
64
|
+
Paths.Users.Delete,
|
|
65
|
+
validate(['id', 'number', 'params']),
|
|
66
|
+
UserRoutes.delete,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Add UserRouter
|
|
70
|
+
apiRouter.use(Paths.Users.Base, adminMw, userRouter);
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
// **** Export default **** //
|
|
74
|
+
|
|
75
|
+
export default apiRouter;
|
|
@@ -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;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup express server.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import cookieParser from 'cookie-parser';
|
|
6
|
+
import morgan from 'morgan';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import helmet from 'helmet';
|
|
9
|
+
import express, { Request, Response, NextFunction } from 'express';
|
|
10
|
+
import logger from 'jet-logger';
|
|
11
|
+
|
|
12
|
+
import 'express-async-errors';
|
|
13
|
+
|
|
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';
|
|
19
|
+
|
|
20
|
+
import { NodeEnvs } from '@src/constants/misc';
|
|
21
|
+
import { RouteError } from '@src/other/classes';
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// **** Variables **** //
|
|
25
|
+
|
|
26
|
+
const app = express();
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// **** Setup **** //
|
|
30
|
+
|
|
31
|
+
// Basic middleware
|
|
32
|
+
app.use(express.json());
|
|
33
|
+
app.use(express.urlencoded({extended: true}));
|
|
34
|
+
app.use(cookieParser(EnvVars.CookieProps.Secret));
|
|
35
|
+
|
|
36
|
+
// Show routes called in console during development
|
|
37
|
+
if (EnvVars.NodeEnv === NodeEnvs.Dev) {
|
|
38
|
+
app.use(morgan('dev'));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Security
|
|
42
|
+
if (EnvVars.NodeEnv === NodeEnvs.Production) {
|
|
43
|
+
app.use(helmet());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add APIs, must be after middleware
|
|
47
|
+
app.use(Paths.Base, BaseRouter);
|
|
48
|
+
|
|
49
|
+
// Add error handler
|
|
50
|
+
app.use((
|
|
51
|
+
err: Error,
|
|
52
|
+
_: Request,
|
|
53
|
+
res: Response,
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
55
|
+
next: NextFunction,
|
|
56
|
+
) => {
|
|
57
|
+
if (EnvVars.NodeEnv !== NodeEnvs.Test) {
|
|
58
|
+
logger.err(err, true);
|
|
59
|
+
}
|
|
60
|
+
let status = HttpStatusCodes.BAD_REQUEST;
|
|
61
|
+
if (err instanceof RouteError) {
|
|
62
|
+
status = err.status;
|
|
63
|
+
}
|
|
64
|
+
return res.status(status).json({ error: err.message });
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
// ** Front-End Content ** //
|
|
69
|
+
|
|
70
|
+
// Set views directory (html)
|
|
71
|
+
const viewsDir = path.join(__dirname, 'views');
|
|
72
|
+
app.set('views', viewsDir);
|
|
73
|
+
|
|
74
|
+
// Set static directory (js and css).
|
|
75
|
+
const staticDir = path.join(__dirname, 'public');
|
|
76
|
+
app.use(express.static(staticDir));
|
|
77
|
+
|
|
78
|
+
// Nav to login pg by default
|
|
79
|
+
app.get('/', (_: Request, res: Response) => {
|
|
80
|
+
res.sendFile('login.html', { root: viewsDir });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Redirect to login if not logged in.
|
|
84
|
+
app.get('/users', (req: Request, res: Response) => {
|
|
85
|
+
const jwt = req.signedCookies[EnvVars.CookieProps.Key];
|
|
86
|
+
if (!jwt) {
|
|
87
|
+
res.redirect('/');
|
|
88
|
+
} else {
|
|
89
|
+
res.sendFile('users.html', {root: viewsDir});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// **** Export default **** //
|
|
95
|
+
|
|
96
|
+
export default app;
|