express-generator-typescript 3.0.4 → 3.2.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 +10 -11
- package/bin/cli.js +1 -20
- package/lib/template/.prettierrc.json +1 -1
- package/lib/template/README.md +13 -15
- package/lib/template/eslint.config.ts +10 -1
- package/lib/template/gitignore +0 -1
- package/lib/template/package.json +19 -29
- package/lib/template/src/common/classes/route-errors.ts +1 -14
- package/lib/template/src/common/constants/HttpStatusCodes.ts +1 -1
- package/lib/template/src/common/constants/Paths/Paths.ts +29 -0
- package/lib/template/src/common/constants/Paths/helpers.ts +29 -0
- package/lib/template/src/common/constants/Paths/index.ts +1 -0
- package/lib/template/src/common/constants/{environment-consts.ts → env.constants.ts} +13 -15
- package/lib/template/src/common/utils/{date-utils.ts → date.utils.ts} +10 -12
- package/lib/template/src/common/utils/dev-only.ts +1 -1
- package/lib/template/src/common/utils/validators.ts +0 -23
- package/lib/template/src/entities/User/index.ts +2 -2
- package/lib/template/src/entities/User/module.ts +77 -28
- package/lib/template/src/entities/User/types.ts +1 -1
- package/lib/template/src/entities/common/entity-base-types.ts +1 -1
- package/lib/template/src/main.ts +4 -4
- package/lib/template/src/public/scripts/lib/bootstrap.bundle.min.js +0 -1
- package/lib/template/src/public/scripts/users.js +19 -10
- package/lib/template/src/public/stylesheets/lib/bootstrap.min.css +0 -1
- package/lib/template/src/public/stylesheets/users.css +24 -1
- package/lib/template/src/repos/MockOrm.ts +6 -6
- package/lib/template/src/repos/UserRepo.ts +6 -16
- package/lib/template/src/repos/common/database.json +3 -3
- package/lib/template/src/{routes/apiRouter.ts → routers/api.router.ts} +4 -12
- package/lib/template/src/routers/user.router.ts +65 -0
- package/lib/template/src/server.ts +24 -11
- package/lib/template/src/services/UserService.ts +6 -3
- package/lib/template/src/views/users.html +8 -0
- package/lib/template/tests/frontend.test.ts +28 -17
- package/lib/template/tests/support/agent.ts +4 -4
- package/lib/template/tests/support/utils.ts +23 -0
- package/lib/template/tests/users.test.ts +136 -83
- package/lib/template/tsconfig.json +10 -13
- package/package.json +6 -6
- package/lib/template/src/common/constants/Paths.ts +0 -16
- package/lib/template/src/entities/User/_internal/validators.ts +0 -47
- package/lib/template/src/routes/UserRoutes.ts +0 -74
- package/lib/template/src/routes/common/express-types.ts +0 -9
- package/lib/template/src/routes/common/parseReq.ts +0 -23
- package/lib/template/tests/common/comparators.ts +0 -36
- package/lib/template/tests/common/supertest-types.ts +0 -11
- package/lib/template/tsconfig.prod.json +0 -30
- package/lib/template/vitest.config.ts +0 -25
|
@@ -1,30 +1,45 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import { getISOString, type ISOString } from '@src/common/utils/date-utils';
|
|
1
|
+
import { v7 as uuidv7 } from 'uuid';
|
|
2
|
+
import { z } from 'zod';
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import type { UserEntity } from './types';
|
|
5
|
+
getISOString,
|
|
6
|
+
type ISOString,
|
|
7
|
+
ISOStringSchema,
|
|
8
|
+
} from '#src/common/utils/date.utils.ts';
|
|
9
|
+
|
|
10
|
+
import type { UserEntity, UserInput } from './types.ts';
|
|
12
11
|
|
|
13
12
|
// ========================================================================= //
|
|
14
|
-
//
|
|
13
|
+
// CONSTANTS //
|
|
15
14
|
// ========================================================================= //
|
|
16
15
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
/** @ValueFactoryFunction */
|
|
17
|
+
const UserDefaults = () => ({
|
|
18
|
+
id: genId(),
|
|
19
|
+
name: '--',
|
|
20
|
+
email: '--@example.com',
|
|
21
|
+
created: getISOString(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ========================================================================= //
|
|
25
|
+
// EXEC //
|
|
26
|
+
// ========================================================================= //
|
|
27
|
+
|
|
28
|
+
const UserIdSchema = z.uuidv7('Not a valid uuid7 id');
|
|
29
|
+
|
|
30
|
+
const UserInputSchema = z.object({
|
|
31
|
+
name: z.string().min(1, 'Name cannot be empty'),
|
|
32
|
+
email: z.email('Not a valid email'),
|
|
33
|
+
}) satisfies z.ZodType<UserInput>;
|
|
34
|
+
|
|
35
|
+
const UserSchema = UserInputSchema.extend({
|
|
36
|
+
id: UserIdSchema,
|
|
37
|
+
created: ISOStringSchema,
|
|
38
|
+
}) satisfies z.ZodType<UserEntity>;
|
|
39
|
+
|
|
40
|
+
// ========================================================================= //
|
|
41
|
+
// FUNCTIONS //
|
|
42
|
+
// ========================================================================= //
|
|
28
43
|
|
|
29
44
|
/**
|
|
30
45
|
* Factory-function.
|
|
@@ -32,9 +47,7 @@ function getDefaults(): UserEntity {
|
|
|
32
47
|
* Create a `UserEntity` from a partial or `undefined`
|
|
33
48
|
*/
|
|
34
49
|
function create(user?: Partial<UserEntity>): UserEntity {
|
|
35
|
-
return
|
|
36
|
-
throw new Error('Setup new user failed ' + JSON.stringify(errors, null, 2));
|
|
37
|
-
});
|
|
50
|
+
return { ...UserDefaults(), ...user };
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
/**
|
|
@@ -43,13 +56,41 @@ function create(user?: Partial<UserEntity>): UserEntity {
|
|
|
43
56
|
* Create a `UserEntity` from individual properties
|
|
44
57
|
*/
|
|
45
58
|
function of(name: string, email?: string, created?: ISOString): UserEntity {
|
|
46
|
-
const retVal =
|
|
59
|
+
const retVal = UserDefaults();
|
|
47
60
|
if (name) retVal.name = name;
|
|
48
61
|
if (email) retVal.email = email;
|
|
49
62
|
if (created) retVal.created = created;
|
|
50
63
|
return retVal;
|
|
51
64
|
}
|
|
52
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Generate a uuid7 id.
|
|
68
|
+
*/
|
|
69
|
+
function genId(): string {
|
|
70
|
+
return uuidv7();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if a valid user object
|
|
75
|
+
*/
|
|
76
|
+
function isUser(val: unknown): val is UserEntity {
|
|
77
|
+
return UserSchema.safeParse(val).success;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a valid end users input fields are valid.
|
|
82
|
+
*/
|
|
83
|
+
function isUserInput(val: unknown): val is UserInput {
|
|
84
|
+
return UserInputSchema.safeParse(val).success;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Test if an id is a valid user id.
|
|
89
|
+
*/
|
|
90
|
+
function isUserId(val: unknown): val is string {
|
|
91
|
+
return UserIdSchema.safeParse(val).success;
|
|
92
|
+
}
|
|
93
|
+
|
|
53
94
|
// ========================================================================= //
|
|
54
95
|
// EXPORT //
|
|
55
96
|
// ========================================================================= //
|
|
@@ -57,7 +98,15 @@ function of(name: string, email?: string, created?: ISOString): UserEntity {
|
|
|
57
98
|
export default {
|
|
58
99
|
of,
|
|
59
100
|
create,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
genId,
|
|
102
|
+
is: {
|
|
103
|
+
id: isUserId,
|
|
104
|
+
user: isUser,
|
|
105
|
+
input: isUserInput,
|
|
106
|
+
},
|
|
107
|
+
Schemas: {
|
|
108
|
+
Main: UserSchema,
|
|
109
|
+
Input: UserInputSchema,
|
|
110
|
+
Id: UserIdSchema,
|
|
111
|
+
},
|
|
63
112
|
} as const;
|
package/lib/template/src/main.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import logger from 'jet-logger';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import server from './server';
|
|
3
|
+
import { Env } from './common/constants/env.constants.ts';
|
|
4
|
+
import server from './server.ts';
|
|
5
5
|
|
|
6
6
|
// ========================================================================= //
|
|
7
7
|
// CONSTANTS //
|
|
8
8
|
// ========================================================================= //
|
|
9
9
|
|
|
10
|
-
const SERVER_START_MESSAGE = `Express server started on port: ${
|
|
10
|
+
const SERVER_START_MESSAGE = `Express server started on port: ${Env.PORT}`;
|
|
11
11
|
|
|
12
12
|
// ========================================================================= //
|
|
13
13
|
// EXEC //
|
|
14
14
|
// ========================================================================= //
|
|
15
15
|
|
|
16
16
|
// Start the server
|
|
17
|
-
server.listen(
|
|
17
|
+
server.listen(Env.PORT, (err) => {
|
|
18
18
|
if (!!err) {
|
|
19
19
|
logger.err(err.message);
|
|
20
20
|
} else {
|
|
@@ -64,9 +64,6 @@ function onDocumentClick(event) {
|
|
|
64
64
|
function addUser() {
|
|
65
65
|
var nameInput = document.getElementById('name-input');
|
|
66
66
|
var emailInput = document.getElementById('email-input');
|
|
67
|
-
if (!nameInput.value || !emailInput.value) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
67
|
var data = {
|
|
71
68
|
user: {
|
|
72
69
|
name: nameInput.value,
|
|
@@ -163,7 +160,7 @@ function deleteUser(ele) {
|
|
|
163
160
|
// ============================= Shared Helpers ============================ //
|
|
164
161
|
|
|
165
162
|
/**
|
|
166
|
-
*
|
|
163
|
+
* Log failed responses and their full JSON bodies, then reject the request.
|
|
167
164
|
*
|
|
168
165
|
* Used by:
|
|
169
166
|
* {@link displayUsers}
|
|
@@ -175,14 +172,25 @@ function deleteUser(ele) {
|
|
|
175
172
|
* @returns {Response | Promise<never>} The response when it succeeded.
|
|
176
173
|
*/
|
|
177
174
|
function checkResponse(resp) {
|
|
178
|
-
if (resp.ok)
|
|
179
|
-
return resp;
|
|
180
|
-
}
|
|
175
|
+
if (resp.ok) return resp;
|
|
181
176
|
return resp
|
|
182
177
|
.json()
|
|
183
178
|
.catch(() => ({}))
|
|
184
179
|
.then((body) => {
|
|
185
|
-
throw
|
|
180
|
+
throw JSON.stringify(
|
|
181
|
+
{
|
|
182
|
+
url: resp.url,
|
|
183
|
+
status: resp.status,
|
|
184
|
+
statusText: resp.statusText,
|
|
185
|
+
ok: resp.ok,
|
|
186
|
+
redirected: resp.redirected,
|
|
187
|
+
type: resp.type,
|
|
188
|
+
headers: Object.fromEntries(resp.headers.entries()),
|
|
189
|
+
body,
|
|
190
|
+
},
|
|
191
|
+
null,
|
|
192
|
+
2,
|
|
193
|
+
);
|
|
186
194
|
});
|
|
187
195
|
}
|
|
188
196
|
|
|
@@ -195,9 +203,10 @@ function checkResponse(resp) {
|
|
|
195
203
|
* {@link submitEdit}
|
|
196
204
|
* {@link deleteUser}
|
|
197
205
|
*
|
|
198
|
-
* @param {Error} err
|
|
206
|
+
* @param {string | Error} err
|
|
199
207
|
* @returns {void}
|
|
200
208
|
*/
|
|
201
209
|
function showError(err) {
|
|
202
|
-
|
|
210
|
+
document.getElementById('error-display').textContent =
|
|
211
|
+
typeof err === 'string' ? err : err.stack || err.message;
|
|
203
212
|
}
|
|
@@ -11795,4 +11795,3 @@ fieldset:disabled .btn {
|
|
|
11795
11795
|
display: none !important;
|
|
11796
11796
|
}
|
|
11797
11797
|
}
|
|
11798
|
-
/*# sourceMappingURL=bootstrap.min.css.map */
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
body {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-wrap: wrap;
|
|
4
|
+
gap: 2em;
|
|
2
5
|
padding: 100px;
|
|
3
6
|
font:
|
|
4
7
|
14px 'Lucida Grande',
|
|
@@ -9,7 +12,6 @@ body {
|
|
|
9
12
|
|
|
10
13
|
body .users-column {
|
|
11
14
|
display: inline-block;
|
|
12
|
-
margin-right: 2em;
|
|
13
15
|
vertical-align: top;
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -48,3 +50,24 @@ body .users-column .user-display-ele button {
|
|
|
48
50
|
body .users-column .user-display-ele .edit-view {
|
|
49
51
|
display: none;
|
|
50
52
|
}
|
|
53
|
+
|
|
54
|
+
/** Error Display Column **/
|
|
55
|
+
|
|
56
|
+
body .errors-column {
|
|
57
|
+
flex: 1 1 320px;
|
|
58
|
+
min-width: 0;
|
|
59
|
+
color: #a61b29;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
body .errors-column #error-display {
|
|
63
|
+
margin: 0;
|
|
64
|
+
padding: 16px;
|
|
65
|
+
border: 1px solid #e5a4aa;
|
|
66
|
+
border-radius: 6px;
|
|
67
|
+
background-color: #fff2f3;
|
|
68
|
+
color: inherit;
|
|
69
|
+
white-space: pre-wrap;
|
|
70
|
+
overflow-wrap: anywhere;
|
|
71
|
+
max-height: 65vh;
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
|
-
import jsonfile from 'jsonfile';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
|
|
5
|
-
import {
|
|
6
|
-
import type { UserEntity } from '
|
|
4
|
+
import { Env, NodeEnvs } from '#src/common/constants/env.constants.ts';
|
|
5
|
+
import type { UserEntity } from '#src/entities/User/index.ts';
|
|
7
6
|
|
|
8
7
|
// ========================================================================= //
|
|
9
8
|
// CONSTANTS //
|
|
@@ -12,7 +11,7 @@ import type { UserEntity } from '@src/entities/User';
|
|
|
12
11
|
const DATABASE_FILE_PATH = path.join(
|
|
13
12
|
import.meta.dirname,
|
|
14
13
|
'common',
|
|
15
|
-
|
|
14
|
+
Env.NODE_ENV === NodeEnvs.TEST ? 'database.test.json' : 'database.json',
|
|
16
15
|
);
|
|
17
16
|
|
|
18
17
|
// ========================================================================= //
|
|
@@ -37,7 +36,8 @@ type Database = {
|
|
|
37
36
|
async function openDb(): Promise<Database> {
|
|
38
37
|
let db: Partial<Database>;
|
|
39
38
|
try {
|
|
40
|
-
|
|
39
|
+
const json = await fs.readFile(DATABASE_FILE_PATH, 'utf8');
|
|
40
|
+
db = JSON.parse(json) as Partial<Database>;
|
|
41
41
|
} catch (err) {
|
|
42
42
|
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
|
|
43
43
|
db = {};
|
|
@@ -50,7 +50,7 @@ async function openDb(): Promise<Database> {
|
|
|
50
50
|
*/
|
|
51
51
|
async function saveDb(db: Database): Promise<void> {
|
|
52
52
|
await fs.mkdir(path.dirname(DATABASE_FILE_PATH), { recursive: true });
|
|
53
|
-
|
|
53
|
+
await fs.writeFile(DATABASE_FILE_PATH, JSON.stringify(db, null, 2) + '\n');
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type UserEntity } from '#src/entities/User/index.ts';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import type { UserEntity } from '@src/entities/User';
|
|
5
|
-
|
|
6
|
-
import orm from './MockOrm';
|
|
3
|
+
import orm from './MockOrm.ts';
|
|
7
4
|
|
|
8
5
|
// ========================================================================= //
|
|
9
6
|
// FUNCTIONS //
|
|
@@ -15,9 +12,7 @@ import orm from './MockOrm';
|
|
|
15
12
|
async function persists(id: string): Promise<boolean> {
|
|
16
13
|
const db = await orm.openDb();
|
|
17
14
|
for (const user of db.users) {
|
|
18
|
-
if (user.id === id)
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
15
|
+
if (user.id === id) return true;
|
|
21
16
|
}
|
|
22
17
|
return false;
|
|
23
18
|
}
|
|
@@ -27,7 +22,7 @@ async function persists(id: string): Promise<boolean> {
|
|
|
27
22
|
*/
|
|
28
23
|
async function getAll(): Promise<UserEntity[]> {
|
|
29
24
|
const db = await orm.openDb();
|
|
30
|
-
return db.users;
|
|
25
|
+
return db.users.sort((a, b) => a.id.localeCompare(b.id));
|
|
31
26
|
}
|
|
32
27
|
|
|
33
28
|
/**
|
|
@@ -92,14 +87,9 @@ async function insertMultiple(
|
|
|
92
87
|
users: UserEntity[] | readonly UserEntity[],
|
|
93
88
|
): Promise<UserEntity[]> {
|
|
94
89
|
const db = await orm.openDb();
|
|
95
|
-
|
|
96
|
-
...user,
|
|
97
|
-
id: jetid(),
|
|
98
|
-
created: getISOString(),
|
|
99
|
-
}));
|
|
100
|
-
db.users = [...db.users, ...inserted];
|
|
90
|
+
db.users = [...db.users, ...users];
|
|
101
91
|
await orm.saveDb(db);
|
|
102
|
-
return
|
|
92
|
+
return [...users];
|
|
103
93
|
}
|
|
104
94
|
|
|
105
95
|
// ========================================================================= //
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"users": [
|
|
3
3
|
{
|
|
4
|
-
"id": "
|
|
4
|
+
"id": "01a12230-a18b-721e-bf21-0ee836fa989d",
|
|
5
5
|
"name": "Sean Maxwell",
|
|
6
6
|
"email": "smaxwell@example.com",
|
|
7
7
|
"created": "2024-03-22T05:14:36.252Z"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
|
-
"id": "
|
|
10
|
+
"id": "01a12230-a18c-7473-b1f0-845016f412d3",
|
|
11
11
|
"name": "John Smith",
|
|
12
12
|
"email": "john.smith@example.com",
|
|
13
13
|
"created": "2024-03-22T05:20:55.079Z"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
|
-
"id": "
|
|
16
|
+
"id": "01a12230-a18c-7473-b1f1-88ed3ea1d5ae",
|
|
17
17
|
"name": "Gordan Freeman",
|
|
18
18
|
"email": "nova@prospect.com",
|
|
19
19
|
"created": "2024-03-22T05:42:18.895Z"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
|
|
3
|
-
import Paths from '
|
|
3
|
+
import Paths from '#src/common/constants/Paths/index.ts';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import userRouter from './user.router.ts';
|
|
6
6
|
|
|
7
7
|
// ========================================================================= //
|
|
8
8
|
// EXEC //
|
|
@@ -10,16 +10,8 @@ import UserRoutes from './UserRoutes';
|
|
|
10
10
|
|
|
11
11
|
const apiRouter = Router();
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
const userRouter = Router();
|
|
16
|
-
|
|
17
|
-
userRouter.get(Paths.Users.Get.$path, UserRoutes.getAll);
|
|
18
|
-
userRouter.post(Paths.Users.Add.$path, UserRoutes.addOne);
|
|
19
|
-
userRouter.put(Paths.Users.Update.$path, UserRoutes.updateOne);
|
|
20
|
-
userRouter.delete(Paths.Users.Delete.$path, UserRoutes.deleteOne);
|
|
21
|
-
|
|
22
|
-
apiRouter.use(Paths.Users.$path, userRouter);
|
|
13
|
+
// Add more routers as your app grows (e.g. `postsRouter`)
|
|
14
|
+
apiRouter.use(Paths.Users.PATH, userRouter);
|
|
23
15
|
|
|
24
16
|
// ========================================================================= //
|
|
25
17
|
// EXPORT //
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import validate from 'express-zod-safe';
|
|
3
|
+
|
|
4
|
+
import HttpStatusCodes from '#src/common/constants/HttpStatusCodes.ts';
|
|
5
|
+
import Paths from '#src/common/constants/Paths/index.ts';
|
|
6
|
+
import User from '#src/entities/User/index.ts';
|
|
7
|
+
import UserService from '#src/services/UserService.ts';
|
|
8
|
+
|
|
9
|
+
// ========================================================================= //
|
|
10
|
+
// CONSTANTS //
|
|
11
|
+
// ========================================================================= //
|
|
12
|
+
|
|
13
|
+
// Shorthand for the path strings
|
|
14
|
+
const P = {
|
|
15
|
+
GET: Paths.Users.Get.PATH,
|
|
16
|
+
ADD: Paths.Users.Add.PATH,
|
|
17
|
+
UPDATE: Paths.Users.Update.PATH,
|
|
18
|
+
DELETE: Paths.Users.Delete.PATH,
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
// Middlware `validators`
|
|
22
|
+
const V = {
|
|
23
|
+
add: validate({ body: { user: User.Schemas.Input } }),
|
|
24
|
+
update: validate({ body: { user: User.Schemas.Main } }),
|
|
25
|
+
delete: validate({ params: { id: User.Schemas.Id } }),
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
// ========================================================================= //
|
|
29
|
+
// EXEC //
|
|
30
|
+
// ========================================================================= //
|
|
31
|
+
|
|
32
|
+
const router = Router();
|
|
33
|
+
|
|
34
|
+
// Get all users
|
|
35
|
+
router.get(P.GET, async (_, res) => {
|
|
36
|
+
const users = await UserService.getAll();
|
|
37
|
+
res.status(HttpStatusCodes.OK).json({ users });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Add one user
|
|
41
|
+
router.post(P.ADD, V.add, async (req, res) => {
|
|
42
|
+
const { user } = req.body;
|
|
43
|
+
const created = await UserService.addOne(user);
|
|
44
|
+
res.status(HttpStatusCodes.CREATED).json({ user: created });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Update one user
|
|
48
|
+
router.put(P.UPDATE, V.update, async (req, res) => {
|
|
49
|
+
const { user } = req.body;
|
|
50
|
+
await UserService.updateOne(user);
|
|
51
|
+
res.status(HttpStatusCodes.OK).end();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Delete one user
|
|
55
|
+
router.delete(P.DELETE, V.delete, async (req, res) => {
|
|
56
|
+
const { id } = req.params;
|
|
57
|
+
await UserService.deleteOne(id);
|
|
58
|
+
res.status(HttpStatusCodes.OK).end();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ========================================================================= //
|
|
62
|
+
// EXPORT //
|
|
63
|
+
// ========================================================================= //
|
|
64
|
+
|
|
65
|
+
export default router;
|
|
@@ -8,12 +8,12 @@ import logger from 'jet-logger';
|
|
|
8
8
|
import morgan from 'morgan';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
|
|
11
|
-
import { RouteError } from '
|
|
12
|
-
import HttpStatusCodes from '
|
|
13
|
-
import Paths from '
|
|
14
|
-
import BaseRouter from '
|
|
11
|
+
import { RouteError } from '#src/common/classes/route-errors.ts';
|
|
12
|
+
import HttpStatusCodes from '#src/common/constants/HttpStatusCodes.ts';
|
|
13
|
+
import Paths from '#src/common/constants/Paths/index.ts';
|
|
14
|
+
import BaseRouter from '#src/routers/api.router.ts';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { Env, NodeEnvs } from './common/constants/env.constants.ts';
|
|
17
17
|
|
|
18
18
|
// ========================================================================= //
|
|
19
19
|
// EXEC //
|
|
@@ -28,17 +28,17 @@ app.use(express.json());
|
|
|
28
28
|
app.use(express.urlencoded({ extended: true }));
|
|
29
29
|
|
|
30
30
|
// Show routes called in console during development
|
|
31
|
-
if (
|
|
31
|
+
if (Env.NODE_ENV === NodeEnvs.DEVELOPMENT) {
|
|
32
32
|
app.use(morgan('dev'));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Security
|
|
36
|
-
if (
|
|
36
|
+
if (Env.NODE_ENV === NodeEnvs.PRODUCTION) {
|
|
37
37
|
app.use(helmet());
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// Add APIs, must be after middleware
|
|
41
|
-
app.use(Paths
|
|
41
|
+
app.use(Paths.PATH, BaseRouter);
|
|
42
42
|
|
|
43
43
|
// Add error handler. `_next` must stay: Express only treats 4-arg middleware
|
|
44
44
|
// as an error handler.
|
|
@@ -48,7 +48,20 @@ app.use((err: Error, _: Request, res: Response, _next: NextFunction) => {
|
|
|
48
48
|
.status(err.status)
|
|
49
49
|
.json({ error: err.message, errors: err.errors });
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
// Body-parser errors are client errors; don't expose the rejected body.
|
|
52
|
+
if ('type' in err) {
|
|
53
|
+
if (err.type === 'entity.parse.failed') {
|
|
54
|
+
return res
|
|
55
|
+
.status(HttpStatusCodes.BAD_REQUEST)
|
|
56
|
+
.json({ error: 'Invalid JSON' });
|
|
57
|
+
}
|
|
58
|
+
if (err.type === 'entity.too.large') {
|
|
59
|
+
return res
|
|
60
|
+
.status(HttpStatusCodes.PAYLOAD_TOO_LARGE)
|
|
61
|
+
.json({ error: 'Request body too large' });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (Env.NODE_ENV !== NodeEnvs.TEST) {
|
|
52
65
|
logger.err(err, true);
|
|
53
66
|
}
|
|
54
67
|
return res
|
|
@@ -64,8 +77,8 @@ const staticDir = path.join(import.meta.dirname, 'public');
|
|
|
64
77
|
|
|
65
78
|
// Refresh the browser on changes. Imported lazily because livereload is a
|
|
66
79
|
// dev dependency and isn't installed in production.
|
|
67
|
-
if (
|
|
68
|
-
const { setupLiveReload } = await import('
|
|
80
|
+
if (Env.NODE_ENV === NodeEnvs.DEVELOPMENT) {
|
|
81
|
+
const { setupLiveReload } = await import('#src/common/utils/dev-only.ts');
|
|
69
82
|
setupLiveReload(app, [staticDir, viewsDir]);
|
|
70
83
|
}
|
|
71
84
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { NotFoundError } from '
|
|
2
|
-
import User, {
|
|
3
|
-
|
|
1
|
+
import { NotFoundError } from '#src/common/classes/route-errors.ts';
|
|
2
|
+
import User, {
|
|
3
|
+
type UserEntity,
|
|
4
|
+
type UserInput,
|
|
5
|
+
} from '#src/entities/User/index.ts';
|
|
6
|
+
import UserRepo from '#src/repos/UserRepo.ts';
|
|
4
7
|
|
|
5
8
|
// ========================================================================= //
|
|
6
9
|
// CONSTANTS //
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<!-- Head -->
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
7
|
<title>Users</title>
|
|
7
8
|
<link rel="stylesheet" type="text/css" href="stylesheets/users.css" />
|
|
8
9
|
|
|
@@ -50,6 +51,13 @@
|
|
|
50
51
|
<div id="all-users-anchor"></div>
|
|
51
52
|
</div>
|
|
52
53
|
|
|
54
|
+
<!-- Error Display -->
|
|
55
|
+
<aside class="users-column errors-column" aria-labelledby="errors-heading">
|
|
56
|
+
<div id="errors-heading" class="column-header">Error Messages:</div>
|
|
57
|
+
<pre id="error-display" role="status" aria-live="polite">
|
|
58
|
+
No errors to display.</pre>
|
|
59
|
+
</aside>
|
|
60
|
+
|
|
53
61
|
<script src="/scripts/HttpClient.js"></script>
|
|
54
62
|
<script src="/scripts/renderUsers.js"></script>
|
|
55
63
|
<script src="/scripts/users.js"></script>
|