express-generator-typescript 2.4.6 → 2.4.8

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.
Files changed (33) hide show
  1. package/lib/express-generator-typescript.js +1 -1
  2. package/lib/project-files/package.json +4 -3
  3. package/lib/project-files/spec/support/apiCb.ts +76 -0
  4. package/lib/project-files/spec/tests/users.spec.ts +102 -127
  5. package/lib/project-files/spec/types/misc.ts +18 -0
  6. package/lib/project-files/src/models/User.ts +13 -27
  7. package/lib/project-files/src/public/scripts/http.js +3 -1
  8. package/lib/project-files/src/public/scripts/users.js +31 -65
  9. package/lib/project-files/src/public/stylesheets/users.css +4 -6
  10. package/lib/project-files/src/repos/UserRepo.ts +6 -1
  11. package/lib/project-files/src/repos/database.json +22 -1
  12. package/lib/project-files/src/routes/types/express/misc.ts +1 -5
  13. package/lib/project-files/src/routes/types/types.ts +0 -8
  14. package/lib/project-files/src/views/users.html +100 -5
  15. package/lib/project-files-auth/package.json +4 -3
  16. package/lib/project-files-auth/spec/support/apiCb.ts +76 -0
  17. package/lib/project-files-auth/spec/support/login.ts +6 -8
  18. package/lib/project-files-auth/spec/tests/auth.spec.ts +52 -59
  19. package/lib/project-files-auth/spec/tests/users.spec.ts +101 -133
  20. package/lib/project-files-auth/spec/types/misc.ts +18 -0
  21. package/lib/project-files-auth/src/models/User.ts +17 -12
  22. package/lib/project-files-auth/src/public/scripts/http.js +3 -1
  23. package/lib/project-files-auth/src/public/scripts/login.js +1 -3
  24. package/lib/project-files-auth/src/public/scripts/users.js +31 -63
  25. package/lib/project-files-auth/src/public/stylesheets/users.css +4 -11
  26. package/lib/project-files-auth/src/repos/UserRepo.ts +6 -1
  27. package/lib/project-files-auth/src/repos/database.json +1 -1
  28. package/lib/project-files-auth/src/services/UserService.ts +2 -8
  29. package/lib/project-files-auth/src/views/login.html +26 -3
  30. package/lib/project-files-auth/src/views/users.html +106 -8
  31. package/package.json +1 -1
  32. package/lib/project-files/spec/support/types.ts +0 -1
  33. package/lib/project-files-auth/spec/support/types.ts +0 -1
@@ -1,9 +1,18 @@
1
- // ***** Start **** //
1
+ // **** Variables **** //
2
2
 
3
- displayUsers();
3
+ const DateFormatter = new Intl.DateTimeFormat('en-US', {
4
+ year: 'numeric',
5
+ month: '2-digit',
6
+ day: '2-digit',
7
+ });
8
+
9
+ const formatDate = (date) => DateFormatter.format(new Date(date));
4
10
 
5
11
 
6
- // ***** Fetch and display users **** //
12
+ // **** Run **** //
13
+
14
+ // Start
15
+ displayUsers();
7
16
 
8
17
  /**
9
18
  * Call api
@@ -12,59 +21,22 @@ function displayUsers() {
12
21
  Http
13
22
  .get('/api/users/all')
14
23
  .then(resp => resp.json())
15
- .then((resp) => {
16
- var allUsers = resp.users;
17
- // Empty the anchor
24
+ .then(resp => {
25
+ var allUsersTemplate = document.getElementById('all-users-template'),
26
+ allUsersTemplateHtml = allUsersTemplate.innerHTML,
27
+ template = Handlebars.compile(allUsersTemplateHtml);
18
28
  var allUsersAnchor = document.getElementById('all-users-anchor');
19
- allUsersAnchor.innerHTML = '';
20
- // Append users to anchor
21
- allUsers.forEach((user) => {
22
- allUsersAnchor.innerHTML += getUserDisplayEle(user);
29
+ allUsersAnchor.innerHTML = template({
30
+ users: resp.users.map(user => ({
31
+ ...user,
32
+ createdFormatted: formatDate(user.created),
33
+ })),
23
34
  });
24
35
  });
25
36
  }
26
37
 
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
38
  // Setup event listener for button click
67
- document.addEventListener('click', function (event) {
39
+ document.addEventListener('click', event => {
68
40
  event.preventDefault();
69
41
  var ele = event.target;
70
42
  if (ele.matches('#add-user-btn')) {
@@ -77,8 +49,6 @@ document.addEventListener('click', function (event) {
77
49
  submitEdit(ele);
78
50
  } else if (ele.matches('.delete-user-btn')) {
79
51
  deleteUser(ele);
80
- } else if (ele.matches('#logout-btn')) {
81
- logoutUser();
82
52
  }
83
53
  }, false);
84
54
 
@@ -93,13 +63,17 @@ function addUser() {
93
63
  id: -1,
94
64
  name: nameInput.value,
95
65
  email: emailInput.value,
96
- role: 0,
66
+ created: new Date(),
97
67
  },
98
68
  };
99
69
  // Call api
100
70
  Http
101
71
  .post('/api/users/add', data)
102
- .then(() => displayUsers());
72
+ .then(() => {
73
+ nameInput.value = '';
74
+ emailInput.value = '';
75
+ displayUsers();
76
+ });
103
77
  }
104
78
 
105
79
  /**
@@ -130,13 +104,14 @@ function submitEdit(ele) {
130
104
  var nameInput = userEle.getElementsByClassName('name-edit-input')[0];
131
105
  var emailInput = userEle.getElementsByClassName('email-edit-input')[0];
132
106
  var id = ele.getAttribute('data-user-id');
133
- var role = ele.getAttribute('data-user-role');
107
+ var created = ele.getAttribute('data-user-created');
108
+ console.log(ele, created)
134
109
  var data = {
135
110
  user: {
136
111
  id: Number(id),
137
112
  name: nameInput.value,
138
113
  email: emailInput.value,
139
- role: Number(role),
114
+ created: new Date(created),
140
115
  },
141
116
  };
142
117
  Http
@@ -153,12 +128,3 @@ function deleteUser(ele) {
153
128
  .delete('/api/users/delete/' + id)
154
129
  .then(() => displayUsers());
155
130
  }
156
-
157
-
158
- // **** Logout **** //
159
-
160
- function logoutUser() {
161
- Http
162
- .get('/api/auth/logout')
163
- .then(() => window.location.href = '/');
164
- }
@@ -27,15 +27,13 @@ body .add-user-col #add-user-btn {
27
27
  margin-bottom: 10px;
28
28
  }
29
29
 
30
- body .add-user-col #logout-btn {
31
- margin-top: 25px;
32
- margin-bottom: 10px;
33
- font-size: 15px;
34
- }
35
-
36
30
 
37
31
  /** Users Display Column **/
38
32
 
33
+ .email-edit {
34
+ padding-bottom: 8px;
35
+ }
36
+
39
37
  body .users-column .user-display-ele {
40
38
  padding-bottom: 10px;
41
39
  }
@@ -56,7 +56,12 @@ async function update(user: IUser): Promise<void> {
56
56
  const db = await orm.openDb();
57
57
  for (let i = 0; i < db.users.length; i++) {
58
58
  if (db.users[i].id === user.id) {
59
- db.users[i] = user;
59
+ const dbUser = db.users[i];
60
+ db.users[i] = {
61
+ ...dbUser,
62
+ name: user.name,
63
+ email: user.email,
64
+ };
60
65
  return orm.saveDb(db);
61
66
  }
62
67
  }
@@ -1 +1,22 @@
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}]}
1
+ {
2
+ "users": [
3
+ {
4
+ "id": 366115170645,
5
+ "name": "Sean Maxwell",
6
+ "email": "smaxwell@example.com",
7
+ "created": "2024-03-22T05:14:36.252Z"
8
+ },
9
+ {
10
+ "id": 310946254456,
11
+ "name": "John Smith",
12
+ "email": "john.smith@example.com",
13
+ "created": "2024-03-22T05:20:55.079Z"
14
+ },
15
+ {
16
+ "id": 143027113460,
17
+ "name": "Gordan Freeman",
18
+ "email": "nova@prospect.com",
19
+ "created": "2024-03-22T05:42:18.895Z"
20
+ }
21
+ ]
22
+ }
@@ -1,7 +1,5 @@
1
1
  import * as e from 'express';
2
2
 
3
- import { ISessionUser } from '@src/models/User';
4
-
5
3
 
6
4
  // **** Express **** //
7
5
 
@@ -10,7 +8,5 @@ export interface IReq<T = void> extends e.Request {
10
8
  }
11
9
 
12
10
  export interface IRes extends e.Response {
13
- locals: {
14
- sessionUser?: ISessionUser;
15
- };
11
+ locals: Record<string, unknown>;
16
12
  }
@@ -1,8 +1,6 @@
1
1
  import * as e from 'express';
2
2
  import { Query } from 'express-serve-static-core';
3
3
 
4
- import { ISessionUser } from '@src/models/User';
5
-
6
4
 
7
5
  // **** Express **** //
8
6
 
@@ -14,9 +12,3 @@ export interface IReqQuery<T extends Query, U = void> extends e.Request {
14
12
  query: T;
15
13
  body: U;
16
14
  }
17
-
18
- export interface IRes extends e.Response {
19
- locals: {
20
- sessionUser: ISessionUser;
21
- };
22
- }
@@ -1,10 +1,30 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
+
4
+ <!-- Head -->
3
5
  <head>
4
6
  <meta charset="UTF-8">
5
7
  <title>Users</title>
6
8
  <link rel="stylesheet" type="text/css" href="/stylesheets/users.css" />
9
+
10
+ <!-- Handlebars -->
11
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.8/handlebars.min.js"></script>
12
+
13
+ <!-- Bootstrap -->
14
+ <link
15
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
16
+ rel="stylesheet"
17
+ integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
18
+ crossorigin="anonymous"
19
+ />
20
+ <script
21
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
22
+ integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
23
+ crossorigin="anonymous"
24
+ ></script>
7
25
  </head>
26
+
27
+ <!-- Body -->
8
28
  <body>
9
29
 
10
30
  <!-- Add Users -->
@@ -13,13 +33,26 @@
13
33
  Add User:
14
34
  </div>
15
35
  <div>
16
- <input id="name-input" placeholder="Name" />
36
+ <input
37
+ id="name-input"
38
+ type="text"
39
+ class="form-control"
40
+ placeholder="Name"
41
+ />
17
42
  </div>
18
43
  <div>
19
- <input id="email-input" placeholder="Email" />
44
+ <input
45
+ id="email-input"
46
+ type="email"
47
+ class="form-control"
48
+ placeholder="Email"
49
+ />
20
50
  </div>
21
51
  <div>
22
- <button id="add-user-btn">
52
+ <button
53
+ id="add-user-btn"
54
+ class="btn btn-success"
55
+ >
23
56
  Add
24
57
  </button>
25
58
  </div>
@@ -27,11 +60,73 @@
27
60
 
28
61
  <!-- Display Users -->
29
62
  <div class="users-column">
30
- <div class="column-header">Users:</div>
63
+ <div class="column-header">
64
+ Users:
65
+ </div>
66
+
31
67
  <div id="all-users-anchor"></div>
32
- </div>
68
+ <script
69
+ id="all-users-template"
70
+ type="text/x-handlebars-template"
71
+ >
72
+ {{#each users}}
73
+ <div class="user-display-ele">
74
+ <div class="normal-view">
75
+ <div><b>Name:</b> {{name}}</div>
76
+ <div><b>Email:</b> {{email}}</div>
77
+ <div><b>Created:</b> {{createdFormatted}}</div>
78
+ <button
79
+ type="button"
80
+ class="btn btn-primary edit-user-btn"
81
+ data-user-id="{{id}}"
82
+ >
83
+ Edit
84
+ </button>
85
+ <button
86
+ class="btn btn-danger delete-user-btn"
87
+ data-user-id="{{id}}"
88
+ >
89
+ Delete
90
+ </button>
91
+ </div>
33
92
 
93
+ <div class="edit-view">
94
+ <div>
95
+ Name:&nbsp;
96
+ <input
97
+ type="text"
98
+ class="form-control name-edit-input"
99
+ value="{{name}}"
100
+ />
101
+ </div>
102
+ <div class="email-edit">
103
+ Email:&nbsp;
104
+ <input
105
+ type="email"
106
+ class="form-control email-edit-input"
107
+ value="{{email}}"
108
+ />
109
+ </div>
110
+ <button
111
+ class="btn btn-primary submit-edit-btn"
112
+ data-user-id="{{id}}"
113
+ data-user-created="{{created}}"
114
+ >
115
+ Submit
116
+ </button>
117
+ <button
118
+ class="btn btn-secondary cancel-edit-btn"
119
+ data-user-id="{{id}}"
120
+ >
121
+ Cancel
122
+ </button>
123
+ </div>
124
+ </div>
125
+ {{/each}}
126
+ </script>
127
+ </div>
34
128
  </body>
129
+
35
130
  <script src="scripts/http.js"></script>
36
131
  <script src="scripts/users.js"></script>
37
132
  </html>
@@ -35,11 +35,12 @@
35
35
  "helmet": "^6.0.0",
36
36
  "inserturlparams": "^1.0.1",
37
37
  "jet-logger": "^1.3.1",
38
- "jet-validator": "^1.0.3",
39
38
  "jet-paths": "^1.0.5",
39
+ "jet-validator": "^1.0.3",
40
40
  "jsonfile": "^6.1.0",
41
41
  "jsonwebtoken": "^8.5.1",
42
42
  "module-alias": "^2.2.2",
43
+ "moment": "^2.30.1",
43
44
  "morgan": "^1.10.0",
44
45
  "ts-command-line-args": "^2.4.2"
45
46
  },
@@ -54,7 +55,7 @@
54
55
  "@types/jsonwebtoken": "^8.5.9",
55
56
  "@types/morgan": "^1.9.3",
56
57
  "@types/node": "^18.7.14",
57
- "@types/supertest": "^2.0.12",
58
+ "@types/supertest": "^6.0.2",
58
59
  "@typescript-eslint/eslint-plugin": "^5.36.1",
59
60
  "@typescript-eslint/parser": "^5.36.1",
60
61
  "eslint": "^8.23.0",
@@ -63,7 +64,7 @@
63
64
  "fs-extra": "^10.1.0",
64
65
  "jasmine": "^4.3.0",
65
66
  "nodemon": "^2.0.19",
66
- "supertest": "^6.2.4",
67
+ "supertest": "^6.3.4",
67
68
  "ts-node": "^10.9.1",
68
69
  "tsconfig-paths": "^4.1.0",
69
70
  "typescript": "^4.8.2"
@@ -0,0 +1,76 @@
1
+ import { CallbackHandler } from 'supertest';
2
+ import moment from 'moment';
3
+ import logger from 'jet-logger';
4
+
5
+ import { TApiCb, TRes } from 'spec/types/misc';
6
+
7
+
8
+ /**
9
+ * API callback function.
10
+ */
11
+ function apiCb(
12
+ cb: TApiCb,
13
+ dateParam = 'created',
14
+ printErr?: boolean,
15
+ ): CallbackHandler {
16
+ return (err: Error, res: TRes) => {
17
+ if (printErr) {
18
+ logger.err(err);
19
+ }
20
+ _strToDate(res.body, dateParam);
21
+ return cb(res);
22
+ };
23
+ }
24
+
25
+ /**
26
+ * When date objects get sent through supertest they are converted to
27
+ * iso-strings. This will cause "toEqual()"" tests to fail, so we need to
28
+ * convert them back to Date objects.
29
+ */
30
+ function _strToDate(
31
+ param: unknown,
32
+ prop: string,
33
+ ): void {
34
+ return _iterate(param, prop);
35
+ }
36
+
37
+ /**
38
+ * Interate object recursively and convert string-dates to "Date" objects.
39
+ */
40
+ function _iterate(param: unknown, prop: string): void {
41
+ if (!param || typeof param !== 'object') {
42
+ return;
43
+ }
44
+ const paramF = param as Record<string, unknown>;
45
+ // For Arrays
46
+ if (Array.isArray(paramF)) {
47
+ for (const item of paramF) {
48
+ _iterate(item, prop);
49
+ }
50
+ return;
51
+ }
52
+ // Check valid string or Date object. If undefined just skip
53
+ const val = paramF[prop];
54
+ if (
55
+ (typeof val !== 'undefined') &&
56
+ !((typeof val === 'string') || (val instanceof Date)) &&
57
+ !moment(val as string | Date).isValid()
58
+ ) {
59
+ throw new Error('Property must be a valid date-string or Date() object');
60
+ }
61
+ // Convert and iterate
62
+ if (typeof val !== 'undefined') {
63
+ paramF[prop] = new Date(val as string | Date);
64
+ }
65
+ for (const key in paramF) {
66
+ const oval = paramF[key];
67
+ if (typeof oval === 'object' && key !== prop) {
68
+ _iterate(oval, prop);
69
+ }
70
+ }
71
+ }
72
+
73
+
74
+ // **** Export default **** //
75
+
76
+ export default apiCb;
@@ -1,4 +1,5 @@
1
- import { SuperTest, Test, Response } from 'supertest';
1
+ import { Test, Response } from 'supertest';
2
+ import TestAgent from 'supertest/lib/agent';
2
3
 
3
4
  import User, { UserRoles } from '@src/models/User';
4
5
  import UserRepo from '@src/repos/UserRepo';
@@ -7,24 +8,21 @@ import PwdUtil from '@src/util/PwdUtil';
7
8
  import Paths from './Paths';
8
9
 
9
10
 
10
- // **** Variables **** //
11
-
11
+ // Login Credentials
12
12
  const LoginCreds = {
13
13
  email: 'jsmith@gmail.com',
14
14
  password: 'Password@1',
15
15
  } as const;
16
16
 
17
-
18
- // **** Functions **** //
19
-
20
17
  /**
21
18
  * Login a user.
22
19
  */
23
- function login(beforeAgent: SuperTest<Test>, done: (arg: string) => void) {
20
+ function login(beforeAgent: TestAgent<Test>, done: (arg: string) => void) {
24
21
  // Setup dummy data
25
22
  const role = UserRoles.Admin,
26
23
  pwdHash = PwdUtil.hashSync(LoginCreds.password),
27
- loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
24
+ loginUser = User.new('john smith', LoginCreds.email, new Date(), role,
25
+ pwdHash);
28
26
  // Add spy
29
27
  spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
30
28
  // Call Login API