@@ -19,22 +19,32 @@ import authRoutes from "./routes/auth.js";
1919import auctionRoutes from "./routes/auctionRoutes" ;
2020import paymentRoutes from "./routes/paymentRoutes" ;
2121
22- import { signAccessToken , signRefreshToken , verifyRefreshToken } from "./controllers/auth.js" ;
23- import { authenticate , AuthRequest } from "./middleware/authMiddleware.js" ;
22+ import {
23+ signAccessToken ,
24+ signRefreshToken ,
25+ verifyRefreshToken
26+ } from "./controllers/auth.js" ;
27+
28+ import {
29+ authenticate ,
30+ authorizeRole ,
31+ authorizeRoles ,
32+ AuthRequest
33+ } from "./middleware/authMiddleware.js" ;
2434
2535dotenv . config ( ) ;
2636
2737const app : Application = express ( ) ;
2838
29- // Middleware
39+ // ------------------- MIDDLEWARE -------------------
3040app . use ( cors ( ) ) ;
3141app . use ( morgan ( "dev" ) ) ;
3242app . use ( bodyParser . json ( ) ) ;
3343app . use ( bodyParser . urlencoded ( { extended : true } ) ) ;
3444app . use ( cookieParser ( ) ) ;
35-
3645app . use ( requestLogger ) ;
37- // Interface and mock data (temporary)
46+
47+ // ------------------- MOCK USER DATA -------------------
3848interface User {
3949 id : string ;
4050 username : string ;
@@ -46,7 +56,13 @@ interface User {
4656}
4757
4858const users : User [ ] = [
49- { id :
"1" , username :
"alice" , passwordHash :
bcrypt . hashSync ( "password" , 8 ) , role :
"admin" , email :
"[email protected] " } , 59+ {
60+ id : "1" ,
61+ username : "alice" ,
62+ passwordHash : bcrypt . hashSync ( "password" , 8 ) ,
63+ role : "admin" ,
64+ 65+ } ,
5066] ;
5167
5268const refreshTokens = new Map < string , string > ( ) ;
@@ -55,6 +71,7 @@ const refreshTokens = new Map<string, string>();
5571
5672app . post ( "/login" , async ( req : Request , res : Response ) => {
5773 const { username, password } = req . body ;
74+
5875 const user = users . find ( ( u ) => u . username === username ) ;
5976 if ( ! user ) return res . status ( 401 ) . json ( { error : "Invalid credentials" } ) ;
6077
@@ -65,10 +82,8 @@ app.post("/login", async (req: Request, res: Response) => {
6582 const accessToken = signAccessToken ( payload ) ;
6683 const refreshToken = signRefreshToken ( payload ) ;
6784
68-
69- // app.get("/protected", authenticate, (req: AuthRequest, res: Response) => {
70- // res.json({ message: "Protected route accessed", user: req.user });
71- // });
85+ // Store refresh token for session control
86+ refreshTokens . set ( user . id , refreshToken ) ;
7287
7388 res . json ( { accessToken } ) ;
7489} ) ;
@@ -84,13 +99,12 @@ app.post("/refresh", (req: Request, res: Response) => {
8499 const payload = verifyRefreshToken ( token ) ;
85100 const storedToken = refreshTokens . get ( payload . sub ) ;
86101
87- if ( ! storedToken ) {
88- return res . status ( 401 ) . json ( { error : "Session not found or already logged out" } ) ;
89- }
102+ // Validate session
103+ if ( ! storedToken )
104+ return res . status ( 401 ) . json ( { error : "Session not found or expired" } ) ;
90105
91- if ( storedToken !== token ) {
92- return res . status ( 401 ) . json ( { error : "Token used is not the latest valid token" } ) ;
93- }
106+ if ( storedToken !== token )
107+ return res . status ( 401 ) . json ( { error : "Invalid refresh token" } ) ;
94108
95109 const cleanPayload = {
96110 sub : payload . sub ,
@@ -111,9 +125,10 @@ app.post("/refresh", (req: Request, res: Response) => {
111125 } ) ;
112126
113127 res . json ( { accessToken : newAccess } ) ;
128+
114129 } catch ( error ) {
115- console . error ( "Refresh token verification failed :" , error ) ;
116- res . status ( 401 ) . json ( { error : "Refresh token is expired or invalid " } ) ;
130+ console . error ( "Refresh token error :" , error ) ;
131+ res . status ( 401 ) . json ( { error : "Refresh token invalid or expired " } ) ;
117132 }
118133} ) ;
119134
@@ -123,16 +138,13 @@ app.post("/logout", authenticate, (req: AuthRequest, res: Response) => {
123138 res . status ( 204 ) . send ( ) ;
124139} ) ;
125140
141+ // Example protected route
126142app . get ( "/protected" , authenticate , ( req : AuthRequest , res : Response ) => {
127143 res . json ( { message : "Protected route accessed" , user : req . user } ) ;
128144} ) ;
129145
130146// ------------------- PASSWORD RESET ROUTES -------------------
131147
132- /**
133- * Step 1: Request password reset
134- * Generates token, sets expiry, and emails reset link.
135- */
136148app . post ( "/api/request-reset" , async ( req : Request , res : Response ) => {
137149 const { email } = req . body ;
138150 const user = users . find ( ( u ) => u . email === email ) ;
@@ -143,16 +155,13 @@ app.post("/api/request-reset", async (req: Request, res: Response) => {
143155
144156 const token = crypto . randomBytes ( 32 ) . toString ( "hex" ) ;
145157 user . resetPasswordToken = token ;
146- user . resetPasswordExpires = new Date ( Date . now ( ) + 3600000 ) ; // 1 hour
158+ user . resetPasswordExpires = new Date ( Date . now ( ) + 3600000 ) ;
147159
148- const resetLink = `${ process . env . FRONTEND_URL || "http://localhost:5173" } /reset-password/${ token } ` ;
160+ const resetLink = `${ process . env . FRONTEND_URL } /reset-password/${ token } ` ;
149161
150162 const transporter = nodemailer . createTransport ( {
151163 service : "gmail" ,
152- auth : {
153- user : process . env . EMAIL_USER ,
154- pass : process . env . EMAIL_PASS ,
155- } ,
164+ auth : { user : process . env . EMAIL_USER , pass : process . env . EMAIL_PASS }
156165 } ) ;
157166
158167 const mailOptions = {
@@ -166,21 +175,20 @@ app.post("/api/request-reset", async (req: Request, res: Response) => {
166175 await transporter . sendMail ( mailOptions ) ;
167176 res . json ( { msg : "Reset link sent to email" } ) ;
168177 } catch ( error ) {
169- console . error ( "Email send failed :" , error ) ;
170- res . status ( 500 ) . json ( { msg : "Failed to send reset email" } ) ;
178+ console . error ( "Email error :" , error ) ;
179+ res . status ( 500 ) . json ( { msg : "Failed to send email" } ) ;
171180 }
172181} ) ;
173182
174- /**
175- * Step 2: Reset password with token
176- * Validates token, hashes new password, updates it.
177- */
178183app . post ( "/api/reset-password/:token" , async ( req : Request , res : Response ) => {
179184 const { token } = req . params ;
180185 const { password } = req . body ;
181186
182187 const user = users . find (
183- ( u ) => u . resetPasswordToken === token && u . resetPasswordExpires && u . resetPasswordExpires > new Date ( )
188+ ( u ) =>
189+ u . resetPasswordToken === token &&
190+ u . resetPasswordExpires &&
191+ u . resetPasswordExpires > new Date ( )
184192 ) ;
185193
186194 if ( ! user ) {
@@ -194,33 +202,46 @@ app.post("/api/reset-password/:token", async (req: Request, res: Response) => {
194202 res . json ( { msg : "Password updated successfully" } ) ;
195203} ) ;
196204
197- // ------------------- ROUTES -------------------
205+ // ------------------- ROUTES WITH RBAC -------------------
198206
207+ // Public
199208app . use ( "/api/health" , healthRoutes ) ;
200- app . use ( "/api/products" , productRoutes ) ;
201- app . use ( "/api/users" , userRoutes ) ;
202- app . use ( "/api/cart" , cartRoutes ) ;
203- app . use ( "/api/auctions" , auctionRoutes ) ;
204- app . use ( "/api/payments" , paymentRoutes ) ;
205- app . use ( "/api" , authRoutes ) ;
206209
207- // ------------------- DEFAULT -------------------
210+ // Products — junior, senior, admin
211+ app . use ( "/api/products" , authenticate , authorizeRoles ( "junior" , "senior" , "admin" ) , productRoutes ) ;
212+
213+ // Users — admin only
214+ app . use ( "/api/users" , authenticate , authorizeRole ( "admin" ) , userRoutes ) ;
215+
216+ // Cart — all authenticated roles
217+ app . use ( "/api/cart" , authenticate , authorizeRoles ( "junior" , "senior" , "admin" ) , cartRoutes ) ;
208218
219+ // Auctions — senior + admin
220+ app . use ( "/api/auctions" , authenticate , authorizeRoles ( "senior" , "admin" ) , auctionRoutes ) ;
221+
222+ // Payments — senior + admin
223+ app . use ( "/api/payments" , authenticate , authorizeRoles ( "senior" , "admin" ) , paymentRoutes ) ;
224+
225+ // Auth
226+ app . use ( "/api" , authRoutes ) ;
227+
228+ // ------------------- DEFAULT ROUTE -------------------
209229app . get ( "/" , ( _req , res ) => {
210230 res . send ( "API is running" ) ;
211231} ) ;
212- app . use ( "/api/users" , userRoutes ) ;
213232
214233// ------------------- DATABASE -------------------
215-
216234const mongoUri = process . env . MONGO_URI ;
235+
217236if ( mongoUri && ( mongoUri . startsWith ( "mongodb://" ) || mongoUri . startsWith ( "mongodb+srv://" ) ) ) {
218237 mongoose
219238 . connect ( mongoUri )
220239 . then ( ( ) => console . log ( "MongoDB connected" ) )
221240 . catch ( ( err ) => console . error ( "MongoDB connection error:" , err ) ) ;
222241} else {
223- console . warn ( "MONGO_URI not set or invalid. Skipping MongoDB connection. Set MONGO_URI in .env to enable DB ." ) ;
242+ console . warn ( "Invalid or missing MONGO_URI." ) ;
224243}
244+
225245app . use ( errorHandler ) ;
246+
226247export default app ;
0 commit comments