Skip to content

Commit afa9755

Browse files
authored
Merge pull request #24 from ruiqi7/feature/user-authentication
2 parents c8d996a + c4c3664 commit afa9755

File tree

34 files changed

+1142
-195
lines changed

34 files changed

+1142
-195
lines changed

backend/question-service/src/models/Question.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,6 @@ const questionSchema: Schema<IQuestion> = new mongoose.Schema(
2020
},
2121
category: {
2222
type: [String],
23-
enum: [
24-
"Strings",
25-
"Algorithms",
26-
"Data Structures",
27-
"Bit Manipulation",
28-
"Recursion",
29-
"Databases",
30-
"Arrays",
31-
"Brainteaser",
32-
],
3323
required: true,
3424
},
3525
},

backend/user-service/.env.sample

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PORT=3001
55
JWT_SECRET=you-can-replace-this-with-your-own-secret
66

77
# admin default credentials
8+
ADMIN_FIRST_NAME=Admin
9+
ADMIN_LAST_NAME=User
810
ADMIN_USERNAME=administrator
911
ADMIN_EMAIL=[email protected]
1012
ADMIN_PASSWORD=Admin@123

backend/user-service/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@
4848

4949
- Body
5050

51-
- Required: `username` (string), `email` (string), `password` (string)
51+
- Required: `firstName` (string), `lastName` (string), `username` (string), `email` (string), `password` (string)
5252

5353
```json
5454
{
55+
"firstName": "SampleFirstName",
56+
"lastName": "SampleLastName",
5557
"username": "SampleUserName",
5658
"email": "[email protected]",
5759
"password": "SecurePassword"

backend/user-service/controller/user-controller.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,27 @@ export async function createUser(
2626
res: Response
2727
): Promise<Response> {
2828
try {
29-
const { username, email, password, firstName, lastName } = req.body;
29+
const { firstName, lastName, username, email, password } = req.body;
3030
const existingUser = await _findUserByUsernameOrEmail(username, email);
3131
if (existingUser) {
3232
return res
3333
.status(409)
34-
.json({ message: "username or email already exists" });
34+
.json({ message: "Username or email already exists" });
3535
}
3636

37-
if (username && email && password && firstName && lastName) {
37+
if (firstName && lastName && username && email && password) {
38+
const { isValid: isValidFirstName, message: firstNameMessage } =
39+
validateName(firstName, "first name");
40+
if (!isValidFirstName) {
41+
return res.status(400).json({ message: firstNameMessage });
42+
}
43+
44+
const { isValid: isValidLastName, message: lastNameMessage } =
45+
validateName(lastName, "last name");
46+
if (!isValidLastName) {
47+
return res.status(400).json({ message: lastNameMessage });
48+
}
49+
3850
const { isValid: isValidUsername, message: usernameMessage } =
3951
validateUsername(username);
4052
if (!isValidUsername) {
@@ -56,36 +68,22 @@ export async function createUser(
5668
const salt = bcrypt.genSaltSync(10);
5769
const hashedPassword = bcrypt.hashSync(password, salt);
5870

59-
const { isValid: isValidFirstName, message: firstNameMessage } =
60-
validateName(firstName, "first name");
61-
if (!isValidFirstName) {
62-
return res.status(400).json({ message: firstNameMessage });
63-
}
64-
65-
const { isValid: isValidLastName, message: lastNameMessage } =
66-
validateName(lastName, "last name");
67-
if (!isValidLastName) {
68-
return res.status(400).json({ message: lastNameMessage });
69-
}
70-
7171
const createdUser = await _createUser(
72+
firstName,
73+
lastName,
7274
username,
7375
email,
7476
hashedPassword,
75-
firstName,
76-
lastName
7777
);
7878
return res.status(201).json({
7979
message: `Created new user ${username} successfully`,
8080
data: formatUserResponse(createdUser),
8181
});
8282
} else {
83-
return res
84-
.status(400)
85-
.json({
86-
message:
87-
"username and/or email and/or password and/or first name and/or last name are missing",
88-
});
83+
return res.status(400).json({
84+
message:
85+
"At least one of first name, last name, username, email and password are missing",
86+
});
8987
}
9088
} catch (err) {
9189
console.error(err);

backend/user-service/model/repository.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ export async function connectToDB() {
1313
}
1414

1515
export async function createUser(
16+
firstName: string,
17+
lastName: string,
1618
username: string,
1719
email: string,
1820
password: string,
19-
firstName: string,
20-
lastName: string,
2121
isAdmin: boolean = false
2222
): Promise<IUser> {
2323
return new UserModel({
24+
firstName,
25+
lastName,
2426
username,
2527
email,
2628
password,
27-
firstName,
28-
lastName,
2929
isAdmin,
3030
}).save();
3131
}

backend/user-service/scripts/seed.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,21 @@ import { connectToDB, createUser, findUserByEmail } from "../model/repository";
44
export async function seedAdminAccount() {
55
await connectToDB();
66

7+
const adminFirstName = process.env.ADMIN_FIRST_NAME || "Admin";
8+
const adminLastName = process.env.ADMIN_LAST_NAME || "User";
79
const adminUsername = process.env.ADMIN_USERNAME || "administrator";
810
const adminEmail = process.env.ADMIN_EMAIL || "[email protected]";
911
const adminPassword = process.env.ADMIN_PASSWORD || "Admin@123";
1012

1113
if (
14+
!process.env.ADMIN_FIRST_NAME ||
15+
!process.env.ADMIN_LAST_NAME ||
1216
!process.env.ADMIN_USERNAME ||
1317
!process.env.ADMIN_EMAIL ||
1418
!process.env.ADMIN_PASSWORD
1519
) {
1620
console.error(
17-
"Admin account not seeded in .env. Using default admin account credentials (username: administrator, email: [email protected], password: Admin@123)"
21+
"Admin account not seeded in .env. Using default admin account credentials (first name: Admin, last name: User, username: administrator, email: [email protected], password: Admin@123)"
1822
);
1923
}
2024

@@ -28,7 +32,7 @@ export async function seedAdminAccount() {
2832
const salt = bcrypt.genSaltSync(10);
2933
const hashedPassword = bcrypt.hashSync(adminPassword, salt);
3034

31-
await createUser(adminUsername, adminEmail, hashedPassword, true);
35+
await createUser(adminFirstName, adminLastName, adminUsername, adminEmail, hashedPassword, true);
3236

3337
console.log("Admin account created successfully.");
3438
process.exit(0);

backend/user-service/swagger.yml

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ components:
7575
properties:
7676
message:
7777
type: string
78+
ServerErrorResponse:
79+
properties:
80+
message:
81+
type: string
82+
error:
83+
type: string
7884

7985
paths:
8086
/:
@@ -102,7 +108,23 @@ paths:
102108
content:
103109
application/json:
104110
schema:
105-
$ref: "#/components/schemas/User"
111+
type: object
112+
properties:
113+
firstName:
114+
type: string
115+
required: true
116+
lastName:
117+
type: string
118+
required: true
119+
username:
120+
type: string
121+
required: true
122+
email:
123+
type: string
124+
required: true
125+
password:
126+
type: string
127+
required: true
106128
responses:
107129
201:
108130
description: Successful Response
@@ -213,13 +235,13 @@ paths:
213235
application/json:
214236
schema:
215237
$ref: "#/components/schemas/ErrorResponse"
216-
404:
238+
404:
217239
description: Not Found
218240
content:
219241
application/json:
220242
schema:
221243
$ref: "#/components/schemas/ErrorResponse"
222-
500:
244+
500:
223245
description: Internal Server Error
224246
content:
225247
application/json:
@@ -270,9 +292,52 @@ paths:
270292
user:
271293
type: object
272294
$ref: "#/definitions/UserOutput"
295+
400:
296+
description: Bad Request
297+
content:
298+
application/json:
299+
schema:
300+
$ref: "#/components/schemas/ErrorResponse"
301+
401:
302+
description: Unauthorized
303+
content:
304+
application/json:
305+
schema:
306+
$ref: "#/components/schemas/ErrorResponse"
307+
500:
308+
description: Internal Server Error
309+
content:
310+
application/json:
311+
schema:
312+
$ref: "#/components/schemas/ServerErrorResponse"
273313

274314
/api/auth/verify-token:
275315
get:
276316
summary: Verify token
277317
tags:
278318
- auth
319+
requestBody:
320+
required: true
321+
content:
322+
application/json:
323+
schema:
324+
$ref: "#/components/schemas/User"
325+
responses:
326+
200:
327+
description: Successful Response
328+
content:
329+
application/json:
330+
schema:
331+
$ref: "#/components/schemas/UserResponse"
332+
401:
333+
description: Unauthorized
334+
content:
335+
application/json:
336+
schema:
337+
$ref: "#/components/schemas/ErrorResponse"
338+
500:
339+
description: Internal Server Error
340+
content:
341+
application/json:
342+
schema:
343+
$ref: "#/components/schemas/ServerErrorResponse"

backend/user-service/utils/validators.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export function validateUsername(username: string): {
6868
return {
6969
isValid: false,
7070
message:
71-
"Username must only contain alphanumeric characters, underscores, and full stops",
71+
"Username must only contain alphanumeric characters, underscores and full stops",
7272
};
7373
}
7474

@@ -86,10 +86,10 @@ export function validateName(
8686
};
8787
}
8888

89-
if (!/^[a-zA-Z0-9\s]+$/.test(name)) {
89+
if (!/^[a-zA-Z\s-]*$/.test(name)) {
9090
return {
9191
isValid: false,
92-
message: `${type} must only contain alphanumeric characters and white spaces`,
92+
message: `${type} must only contain alphabetical, hypen and white space characters`,
9393
};
9494
}
9595

0 commit comments

Comments
 (0)