better api

This commit is contained in:
StarAppeal
2025-09-13 12:12:38 +02:00
parent 2756d7a87d
commit 6a9ffde9f6
2 changed files with 30 additions and 17 deletions
+20 -17
View File
@@ -4,8 +4,8 @@ import {IUser} from "../db/models/user";
import {JwtAuthenticator} from "../utils/jwtAuthenticator";
import crypto from "crypto";
import {PasswordUtils} from "../utils/passwordUtils";
import { asyncHandler } from "./middleware/asyncHandler";
import { validateBody, v } from "./middleware/validate";
import {asyncHandler} from "./middleware/asyncHandler";
import {validateBody, v} from "./middleware/validate";
import {ok, badRequest, unauthorized, created, conflict, notFound} from "./utils/responses";
export class RestAuth {
@@ -15,24 +15,27 @@ export class RestAuth {
router.post(
"/register",
validateBody({
username: { required: true, validator: v.isString({ nonEmpty: true, min: 3 }) },
password: { required: true, validator: v.isString({ nonEmpty: true, min: 8 }) },
timezone: { required: true, validator: v.isString({ nonEmpty: true }) },
location: { required: true, validator: v.isString({ nonEmpty: true }) },
username: {required: true, validator: v.isString({nonEmpty: true, min: 3})},
password: {required: true, validator: v.isString({nonEmpty: true, min: 8})},
timezone: {required: true, validator: v.isString({nonEmpty: true})},
location: {required: true, validator: v.isString({nonEmpty: true})},
}),
asyncHandler(async (req, res) => {
const { username, password, timezone, location } = req.body as {
const {username, password, timezone, location} = req.body as {
username: string; password: string; timezone: string; location: string;
};
const userService = await UserService.create();
if (await userService.existsUserByName(username)) {
return conflict(res, "Username already exists");
return conflict(res, "Username already exists", {field: "username", code: "USERNAME_TAKEN"});
}
const passwordValidation = PasswordUtils.validatePassword(password);
if (!passwordValidation.valid) {
return badRequest(res, passwordValidation.message ?? "Invalid password");
return badRequest(res, passwordValidation.message ?? "Invalid password", {
field: "password",
code: "INVALID_PASSWORD_FORMAT"
});
}
const hashedPassword = await PasswordUtils.hashPassword(password);
@@ -50,28 +53,28 @@ export class RestAuth {
};
const result = await userService.createUser(newUser);
return created(res, {user: result });
return created(res, {user: result});
})
);
router.post(
"/login",
validateBody({
username: { required: true, validator: v.isString({ nonEmpty: true }) },
password: { required: true, validator: v.isString({ nonEmpty: true }) },
username: {required: true, validator: v.isString({nonEmpty: true})},
password: {required: true, validator: v.isString({nonEmpty: true})},
}),
asyncHandler(async (req, res) => {
const { username, password } = req.body as { username: string; password: string };
const {username, password} = req.body as { username: string; password: string };
const userService = await UserService.create();
const user = await userService.getUserAuthByName(username);
if (!user) {
return notFound(res, "User not found");
return notFound(res, "User not found", {field: "username", code: "INVALID_USER"});
}
const isValid = await PasswordUtils.comparePassword(password, user.password!);
if (!isValid) {
return unauthorized(res, "Invalid password");
return unauthorized(res, "Invalid password", {field: "password", code: "INVALID_PASSWORD"});
}
const jwtToken = new JwtAuthenticator(process.env.SECRET_KEY!)
@@ -88,7 +91,7 @@ export class RestAuth {
maxAge: 24 * 60 * 60 * 1000
});
return ok(res, { token: jwtToken });
return ok(res, {token: jwtToken});
})
);
@@ -96,7 +99,7 @@ export class RestAuth {
"/logout",
asyncHandler(async (req, res) => {
res.clearCookie('auth-token');
return ok(res, { message: "Successfully logged out" });
return ok(res, {message: "Successfully logged out"});
})
);
+10
View File
@@ -103,6 +103,8 @@ describe("RestAuth", () => {
const response = await request(app).post("/auth/register").send(validRegistrationData).expect(409);
expect(response.body.ok).toBe(false);
expect(response.body.data.message).toBe("Username already exists");
expect(response.body.data.details.field).toBe("username");
expect(response.body.data.details.code).toBe("USERNAME_TAKEN");
});
it("should return bad request for invalid password", async () => {
@@ -115,6 +117,9 @@ describe("RestAuth", () => {
expect(response.body.ok).toBe(false);
expect(response.body.data.message).toBe("Password is not valid.");
expect(mockPasswordUtils.hashPassword).not.toHaveBeenCalled();
expect(response.body.data.details.field).toBe("password");
expect(response.body.data.details.code).toBe("INVALID_PASSWORD_FORMAT");
});
it.each([
@@ -221,6 +226,9 @@ describe("RestAuth", () => {
const response = await request(app).post("/auth/login").send(validLoginData).expect(404);
expect(response.body.ok).toBe(false);
expect(response.body.data.message).toBe("User not found");
expect(response.body.data.details.field).toBe("username")
expect(response.body.data.details.code).toBe("INVALID_USER")
});
it("should return unauthorized for invalid password", async () => {
@@ -230,6 +238,8 @@ describe("RestAuth", () => {
const response = await request(app).post("/auth/login").send(validLoginData).expect(401);
expect(response.body.ok).toBe(false);
expect(response.body.data.message).toBe("Invalid password");
expect(response.body.data.details.field).toBe("password")
expect(response.body.data.details.code).toBe("INVALID_PASSWORD")
});
it.each([