-
Notifications
You must be signed in to change notification settings - Fork 303
impl node_auth-app #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
impl node_auth-app #232
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| name: Test | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [ master ] | ||
|
|
||
| jobs: | ||
| build: | ||
|
|
||
| runs-on: ubuntu-latest | ||
|
|
||
| strategy: | ||
| matrix: | ||
| node-version: [20.x] | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v2 | ||
| - name: Use Node.js ${{ matrix.node-version }} | ||
| uses: actions/setup-node@v1 | ||
| with: | ||
| node-version: ${{ matrix.node-version }} | ||
| - run: npm install | ||
| - run: npm test |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // This file was generated by Prisma, and assumes you have installed the following: | ||
| // npm install --save-dev prisma dotenv | ||
| import "dotenv/config"; | ||
| import { defineConfig } from "prisma/config"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The import path is incorrect and will cause a runtime/module resolution error. Replace this with the official Prisma import: |
||
|
|
||
| export default defineConfig({ | ||
| schema: "prisma/schema.prisma", | ||
| migrations: { | ||
| path: "prisma/migrations", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You set |
||
| }, | ||
| datasource: { | ||
| url: process.env["DATABASE_URL"], | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider validating |
||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| -- CreateTable | ||
| CREATE TABLE "users_auth" ( | ||
| "id" SERIAL NOT NULL, | ||
| "name" VARCHAR(255) NOT NULL, | ||
| "email" VARCHAR(255) NOT NULL, | ||
| "password" VARCHAR(255) NOT NULL, | ||
| "activationToken" VARCHAR(255), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| "createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMPTZ(6) NOT NULL, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| CONSTRAINT "users_auth_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "tokens_auth" ( | ||
| "id" SERIAL NOT NULL, | ||
| "refreshToken" VARCHAR(255) NOT NULL, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| "createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| "updatedAt" TIMESTAMPTZ(6) NOT NULL, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| "userId" INTEGER NOT NULL, | ||
|
|
||
| CONSTRAINT "tokens_auth_pkey" PRIMARY KEY ("id") | ||
| ); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "users_auth_email_key" ON "users_auth"("email"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "users_auth_activationToken_key" ON "users_auth"("activationToken"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "tokens_auth_refreshToken_key" ON "tokens_auth"("refreshToken"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "tokens_auth_userId_key" ON "tokens_auth"("userId"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a UNIQUE index on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a UNIQUE index on |
||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "tokens_auth" ADD CONSTRAINT "tokens_auth_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users_auth"("id") ON DELETE RESTRICT ON UPDATE CASCADE; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /* | ||
| Warnings: | ||
|
|
||
| - A unique constraint covering the columns `[resetToken]` on the table `users_auth` will be added. If there are existing duplicate values, this will fail. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The warning here is a bit misleading: because you're adding a new nullable column (no default), there cannot be pre-existing duplicate values in it. Consider clarifying or removing this line so it doesn't confuse reviewers or operators running the migration.
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good that the migration includes a warning about duplicates. Add an explicit pre-migration check (and instructions or SQL) so the migration fails fast with a clear message — e.g. run |
||
|
|
||
| */ | ||
| -- AlterTable | ||
| ALTER TABLE "users_auth" ADD COLUMN "resetToken" VARCHAR(255); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a timestamp column such as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Be explicit about the column nullability and intent. For reset tokens you likely want a nullable column; consider |
||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "users_auth_resetToken_key" ON "users_auth"("resetToken"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer creating a partial unique index that enforces uniqueness only for non-null tokens, e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a UNIQUE index may lock the table while it builds. For large tables consider |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /* | ||
| Warnings: | ||
|
|
||
| - A unique constraint covering the columns `[pendingEmail]` on the table `users_auth` will be added. If there are existing duplicate values, this will fail. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The top-of-file warning correctly notes potential failure due to duplicates; you should codify a migration-safe approach: either (a) add a pre-migration step that removes or resolves duplicate
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The top comment warns that adding a unique constraint could fail if there are existing duplicate |
||
|
|
||
| */ | ||
| -- AlterTable | ||
| ALTER TABLE "users_auth" ADD COLUMN "pendingEmail" VARCHAR(255); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ALTER TABLE adds the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ALTER TABLE adds |
||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "users_auth_pendingEmail_key" ON "users_auth"("pendingEmail"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a plain UNIQUE INDEX can fail if there are duplicate non-null
Comment on lines
+10
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The UNIQUE INDEX on
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /* | ||
| Warnings: | ||
|
|
||
| - You are about to drop the column `resetToken` on the `users_auth` table. All the data in the column will be lost. | ||
| - A unique constraint covering the columns `[passwordResetToken]` on the table `users_auth` will be added. If there are existing duplicate values, this will fail. | ||
| - A unique constraint covering the columns `[pendingEmailToken]` on the table `users_auth` will be added. If there are existing duplicate values, this will fail. | ||
|
|
||
| */ | ||
| -- DropIndex | ||
| DROP INDEX "users_auth_resetToken_key"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dropping the index There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dropping the index |
||
|
|
||
| -- AlterTable | ||
| ALTER TABLE "users_auth" DROP COLUMN "resetToken", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ALTER tries to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ALTER tries to |
||
| ADD COLUMN "passwordResetToken" VARCHAR(255), | ||
| ADD COLUMN "pendingEmailToken" VARCHAR(255); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You add |
||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "users_auth_passwordResetToken_key" ON "users_auth"("passwordResetToken"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a UNIQUE index on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a UNIQUE index on |
||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "users_auth_pendingEmailToken_key" ON "users_auth"("pendingEmailToken"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, a UNIQUE index on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, a UNIQUE index on |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Please do not edit this file manually | ||
| # It should be added in your version-control system (e.g., Git) | ||
| provider = "postgresql" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| generator client { | ||
| provider = "prisma-client-js" | ||
| } | ||
|
|
||
| datasource db { | ||
| provider = "postgresql" | ||
| } | ||
|
|
||
| model User { | ||
| id Int @id @default(autoincrement()) | ||
| name String @db.VarChar(255) | ||
| email String @unique @db.VarChar(255) | ||
| password String @db.VarChar(255) | ||
| activationToken String? @unique @db.VarChar(255) | ||
| createdAt DateTime @default(now()) @db.Timestamptz(6) | ||
| updatedAt DateTime @updatedAt @db.Timestamptz(6) | ||
| passwordResetToken String? @unique @db.VarChar(255) | ||
| pendingEmail String? @unique @db.VarChar(255) | ||
| pendingEmailToken String? @unique @db.VarChar(255) | ||
| tokens Token[] | ||
|
|
||
| @@map("users_auth") | ||
| } | ||
|
|
||
| model Token { | ||
| id Int @id @default(autoincrement()) | ||
| refreshToken String @unique @db.VarChar(255) | ||
| createdAt DateTime @default(now()) @db.Timestamptz(6) | ||
| updatedAt DateTime @updatedAt @db.Timestamptz(6) | ||
| userId Int @unique | ||
| users User @relation(fields: [userId], references: [id]) | ||
|
|
||
| @@map("tokens_auth") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| import { userService } from '../services/user.service.js'; | ||
| import { authService } from '../services/auth.service.js'; | ||
|
|
||
| const register = async (req, res) => { | ||
| const { name, email, password } = req.body; | ||
|
|
||
| await authService.register(name, email, password); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
|
||
| res.send({ message: 'Registration successful' }); | ||
|
Comment on lines
+4
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Registration handler doesn't communicate password rules or show them to the client. The task requires that users be informed of password rules and that they are enforced. Either (a) include a description of the password rules in the registration response, or (b) validate the password here and return a clear 4xx with the rule violations. If you rely on authService.register to enforce this, make sure it returns descriptive errors and document that here. |
||
| }; | ||
|
|
||
| const activate = async (req, res) => { | ||
| const { activationToken } = req.params; | ||
|
|
||
| const activatedUser = await authService.activate(activationToken); | ||
|
|
||
| res.send(userService.normalize(activatedUser)); | ||
|
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After activation you call
Comment on lines
+12
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Activation handler currently returns the activated user JSON. The specification requires redirecting to the Profile page after activation. Decide whether the server should perform the redirect (e.g. |
||
| }; | ||
|
|
||
| const sendAuthentication = async (res, user) => { | ||
| const { | ||
| user: normalizedUser, | ||
| accessToken, | ||
| refreshToken, | ||
| } = await authService.authenticate(user); | ||
|
|
||
| res.cookie('refreshToken', refreshToken, { | ||
| maxAge: 30 * 24 * 60 * 60 * 1000, | ||
| httpOnly: true, | ||
| }); | ||
| res.send({ user: normalizedUser, accessToken }); | ||
| }; | ||
|
|
||
| const login = async (req, res) => { | ||
| const { email, password } = req.body; | ||
|
|
||
| const user = await authService.login(email, password); | ||
|
|
||
| await sendAuthentication(res, user); | ||
| }; | ||
|
|
||
| const refresh = async (req, res) => { | ||
| const { refreshToken } = req.cookies; | ||
|
|
||
| const user = await authService.refresh(refreshToken); | ||
|
|
||
| await sendAuthentication(res, user); | ||
| }; | ||
|
|
||
| const logout = async (req, res) => { | ||
| const { refreshToken } = req.cookies; | ||
|
|
||
| await authService.logout(refreshToken); | ||
|
|
||
| res.clearCookie('refreshToken'); | ||
| res.send({ message: 'Logout successful' }); | ||
|
Comment on lines
+50
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logout and some other endpoints have access-control requirements (e.g. only authenticated users can logout; some flows only for non-authenticated). This controller does not enforce those checks itself — make sure route-level middleware or explicit checks are in place. If you prefer controller-level enforcement, add guards here (e.g. check |
||
| }; | ||
|
|
||
| const resetPasswordNotification = async (req, res) => { | ||
| const { email } = req.body; | ||
|
|
||
| await authService.resetPasswordNotification(email); | ||
|
|
||
| res.clearCookie('refreshToken'); | ||
| res.send({ message: 'Password reset email sent' }); | ||
| }; | ||
|
|
||
| const confirmPasswordReset = async (req, res) => { | ||
| const { passwordResetToken } = req.params; | ||
| const { password, confirmation } = req.body; | ||
|
|
||
| await authService.confirmPasswordReset( | ||
| passwordResetToken, | ||
| password, | ||
| confirmation, | ||
| ); | ||
|
Comment on lines
+72
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
+68
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Password reset confirmation does not validate that |
||
|
|
||
| res.clearCookie('refreshToken'); | ||
| res.send({ message: 'Password reset successful' }); | ||
| }; | ||
|
|
||
| const confirmEmailChange = async (req, res) => { | ||
| const { pendingEmailToken } = req.params; | ||
|
|
||
| await authService.confirmEmailChange(pendingEmailToken); | ||
|
Comment on lines
+82
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a |
||
|
|
||
| res.clearCookie('refreshToken'); | ||
| res.send({ message: 'Email updated successfully' }); | ||
|
Comment on lines
+82
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Email-change confirmation endpoint exists (good), but the task requires that the new email is confirmed before updating and that the OLD email is notified about the change. Ensure |
||
| }; | ||
|
|
||
| export const authController = { | ||
| register, | ||
| activate, | ||
| login, | ||
| refresh, | ||
| logout, | ||
| resetPasswordNotification, | ||
| confirmPasswordReset, | ||
| confirmEmailChange, | ||
| }; | ||
|
Comment on lines
+91
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The controller does not expose any handler to change the user's name on the Profile page. The previous review requested adding a route/controller/service to allow users to change their name. Please add an authenticated endpoint (e.g. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import { authService } from '../services/auth.service.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
|
|
||
| const getProfile = async (req, res) => { | ||
| const { refreshToken } = req.cookies; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These handlers require a |
||
| const profile = await userService.getProfile(req.userId, refreshToken); | ||
|
Comment on lines
+5
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| res.send(profile); | ||
|
Comment on lines
+4
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }; | ||
|
|
||
| const updateName = async (req, res) => { | ||
| const { name } = req.body; | ||
| const { refreshToken } = req.cookies; | ||
|
|
||
| await authService.updateName(req.userId, name, refreshToken); | ||
|
|
||
| res.send({ message: 'Name changed successfully' }); | ||
|
Comment on lines
+11
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }; | ||
|
|
||
| const updatePassword = async (req, res) => { | ||
| const { password, newPassword, confirmation } = req.body; | ||
| const { refreshToken } = req.cookies; | ||
|
|
||
| await authService.updatePassword( | ||
| refreshToken, | ||
| req.userId, | ||
| password, | ||
| newPassword, | ||
| confirmation, | ||
| ); | ||
|
|
||
| res.clearCookie('refreshToken'); | ||
| res.send({ message: 'Password changed successfully' }); | ||
|
Comment on lines
+20
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
+20
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also for
Comment on lines
+31
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }; | ||
|
|
||
| const updateEmailNotification = async (req, res) => { | ||
| const { newEmail, password } = req.body; | ||
| const { refreshToken } = req.cookies; | ||
|
|
||
| await authService.updateEmailNotification( | ||
| refreshToken, | ||
| req.userId, | ||
| newEmail, | ||
| password, | ||
| ); | ||
|
|
||
| res.send({ message: 'Change email notification sent' }); | ||
|
Comment on lines
+36
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Comment on lines
+36
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| }; | ||
|
|
||
| export const userController = { | ||
| getProfile, | ||
| updateName, | ||
| updatePassword, | ||
| updateEmailNotification, | ||
| }; | ||
|
Comment on lines
50
to
55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requirements specify that the profile page should allow a user to change their name. This controller is missing the necessary function to handle that update request. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,21 @@ | ||
| 'use strict'; | ||
| import cors from 'cors'; | ||
| import express from 'express'; | ||
| import cookieParser from 'cookie-parser'; | ||
| import { authRouter } from './routes/auth.route.js'; | ||
| import { userRouter } from './routes/user.route.js'; | ||
| import { authMiddleware } from './middlewares/auth.middleware.js'; | ||
| import { errorMiddleware } from './middlewares/error.middleware.js'; | ||
|
|
||
| const app = express(); | ||
| const PORT = process.env.PORT || 3005; | ||
|
|
||
| app.use(cors()); | ||
|
|
||
| app.use(express.json()); | ||
| app.use(cookieParser()); | ||
| app.use(authRouter); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure that endpoints which must only be accessible to non-authenticated users (registration, activation, login, password reset flows) are actually protected by middleware — either inside |
||
| app.use('/profile', authMiddleware, userRouter); | ||
| app.use(errorMiddleware); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires returning a 404 error for all pages not explicitly defined. Currently, the application relies on Express's default behavior for unhandled routes, which might not be a JSON response. It's better to add a catch-all middleware right before this line to handle any requests that didn't match a route and forward a proper There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing the required catch-all 404 handler. Per the task and the previous review, you must add a middleware before the error handler that returns a standardized JSON 404 for undefined routes. For example: app.use((req, res) => {
res.status(404).json({ status: 404, message: 'Not Found' });
});Add this before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file lacks the required global catch‑all 404 handler. The task requires returning a standardized JSON 404 for undefined API routes. Add a middleware after all routers and before the error handler that sends a 404 JSON response (for example: |
||
|
|
||
| app.listen(PORT); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { jwtService } from '../services/jwt.service.js'; | ||
| import { userService } from '../services/user.service.js'; | ||
| import { ApiError } from '../utils/api.error.js'; | ||
| import { logger } from '../utils/logger.js'; | ||
|
|
||
| export const authMiddleware = async (req, res, next) => { | ||
| const authHeader = req.headers.authorization ?? ''; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This middleware reads the Authorization header but elsewhere (profile endpoints) the app expects a |
||
|
|
||
| if (!authHeader.startsWith('Bearer ')) { | ||
| logger.error('Authorization header missing or malformed'); | ||
| throw ApiError.unauthorized(); | ||
|
Comment on lines
+9
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throwing |
||
| } | ||
|
|
||
| const token = authHeader.split(' ')[1]; | ||
| const userData = jwtService.verify(token); | ||
|
Comment on lines
+14
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling |
||
|
|
||
| if (!userData) { | ||
| logger.error('Invalid or expired token'); | ||
| throw ApiError.unauthorized(); | ||
|
Comment on lines
+17
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same problem as above: throwing inside the async function. Use try/catch and |
||
| } | ||
|
|
||
| const user = await userService.getById(userData.id); | ||
|
|
||
| if (!user) { | ||
| logger.error('User not found for token', { userId: userData.id }); | ||
| throw ApiError.notFound(); | ||
|
Comment on lines
+22
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the token's user cannot be found you return
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the user is not found you throw |
||
| } | ||
|
Comment on lines
+21
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider checking |
||
|
|
||
| req.userId = user.id; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After loading the user you only set |
||
| next(); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| export const catchError = (action) => { | ||
| return async (req, res, next) => { | ||
| try { | ||
| await action(req, res, next); | ||
| } catch (error) { | ||
| next(error); | ||
| } | ||
| }; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import { ApiError } from '../utils/api.error.js'; | ||
| import { logger } from '../utils/logger.js'; | ||
|
|
||
| export const checkNotLoggedIn = (req, res, next) => { | ||
| const { refreshToken } = req.cookies; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Relying only on the presence of the |
||
|
|
||
| if (refreshToken) { | ||
|
Comment on lines
+5
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The middleware only checks for a |
||
| logger.info('Already logged in user attempt', { | ||
| userId: req.userId, | ||
|
Comment on lines
+8
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You log
Comment on lines
+8
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You log |
||
| }); | ||
| throw ApiError.forbidden(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Throwing |
||
| } | ||
| next(); | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The import path is incorrect —
defineConfigshould be imported from theprismapackage, notprisma/config. This will cause a module resolution/runtime error. Change to:import { defineConfig } from "prisma"or the correct package export for your Prisma version.