From 8e385887de973d48f3073b373330935ef82016c6 Mon Sep 17 00:00:00 2001 From: andwati Date: Tue, 13 Jan 2026 18:29:46 +0300 Subject: [PATCH] added get users admin only endpoint --- src/app.ts | 2 + src/controllers/user.controller.ts | 13 +++++++ src/middleware/validate.middleware.ts | 17 ++++++++- src/repositories/user.repo.ts | 4 ++ src/routes/auth.routes.ts | 32 +++++++++++++++- src/routes/user.routes.ts | 53 +++++++++++++++++++++++++++ src/services/user.service.ts | 14 +++++++ 7 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/controllers/user.controller.ts create mode 100644 src/routes/user.routes.ts create mode 100644 src/services/user.service.ts diff --git a/src/app.ts b/src/app.ts index a02c0ea..ae6e069 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,6 +7,7 @@ import { errorHandler } from "./middleware/error.middleware.js"; import authRoutes from "./routes/auth.routes.js"; import productRoutes from "./routes/product.routes.js"; import orderRoutes from "./routes/order.routes.js"; +import userRoutes from "./routes/user.routes.js"; import docsRoutes from "./routes/docs.routes.js"; const app: Application = express(); @@ -25,6 +26,7 @@ app.get("/health", (req, res) => { app.use("/auth", authRoutes); app.use("/products", productRoutes); app.use("/orders", orderRoutes); +app.use("/users", userRoutes); // swagger ui app.use("/docs", docsRoutes); diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts new file mode 100644 index 0000000..8a75307 --- /dev/null +++ b/src/controllers/user.controller.ts @@ -0,0 +1,13 @@ +import type { Request, Response, NextFunction } from "express"; +import { UserService } from "../services/user.service.js"; + +export class UserController { + static async getAll(_req: Request, res: Response, next: NextFunction) { + try { + const users = await UserService.getAll(); + res.status(200).json(users); + } catch (err) { + next(err); + } + } +} diff --git a/src/middleware/validate.middleware.ts b/src/middleware/validate.middleware.ts index a7b5a78..4c38d00 100644 --- a/src/middleware/validate.middleware.ts +++ b/src/middleware/validate.middleware.ts @@ -1,16 +1,29 @@ import type { ZodSchema } from "zod"; import type { Request, Response, NextFunction } from "express"; +interface ParsedRequest { + body?: Record; + params?: Record; + query?: Record; +} + export const validate = (schema: ZodSchema) => (req: Request, _res: Response, next: NextFunction) => { try { - schema.parse({ + const parsed = schema.parse({ body: req.body, params: req.params, query: req.query, - }); + }) as ParsedRequest; + + // Update request with parsed values (including defaults) + if (parsed.body) req.body = parsed.body; + if (parsed.params) req.params = parsed.params; + if (parsed.query) req.query = parsed.query as Record; + next(); } catch (err) { next(err); } }; + diff --git a/src/repositories/user.repo.ts b/src/repositories/user.repo.ts index 25cd646..f0f9da0 100644 --- a/src/repositories/user.repo.ts +++ b/src/repositories/user.repo.ts @@ -3,6 +3,10 @@ import { UserModel } from "../models/user.model.js"; import type { UserDocument } from "../models/user.model.js"; export class UserRepository { + static findAll(): Promise { + return UserModel.find().select("-passwordHash"); + } + static findByEmail(email: string): Promise { return UserModel.findOne({ email }); } diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts index 26d3e37..38406d5 100644 --- a/src/routes/auth.routes.ts +++ b/src/routes/auth.routes.ts @@ -13,22 +13,52 @@ const router: Router = Router(); * tags: * - Auth * summary: Register a new user + * security: [] * requestBody: * required: true * content: * application/json: * schema: * type: object + * required: + * - email + * - password * properties: * email: * type: string + * format: email + * description: User's email address * password: * type: string + * minLength: 8 + * description: User's password (minimum 8 characters) + * role: + * type: string + * enum: [customer, admin] + * default: customer + * description: Account type (defaults to customer) * responses: * 201: - * description: User created + * description: User created successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * id: + * type: string + * description: The user ID + * email: + * type: string + * description: The user's email + * role: + * type: string + * enum: [customer, admin] + * description: The user's role * 400: * description: Invalid input + * 409: + * description: Email already registered */ router.post( "/register", diff --git a/src/routes/user.routes.ts b/src/routes/user.routes.ts new file mode 100644 index 0000000..d841bb2 --- /dev/null +++ b/src/routes/user.routes.ts @@ -0,0 +1,53 @@ +import { Router } from "express"; +import { UserController } from "../controllers/user.controller.js"; +import { authMiddleware } from "../middleware/auth.middleware.js"; +import { requireRole } from "../middleware/role.middleware.js"; + +const router: Router = Router(); + +/** + * @openapi + * /users: + * get: + * tags: + * - Users + * summary: Get all users (Admin only) + * description: Retrieve a list of all registered users. This endpoint is restricted to administrators only. + * security: + * - BearerAuth: [] + * responses: + * 200: + * description: List of users retrieved successfully + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * description: The user ID + * email: + * type: string + * description: The user's email + * role: + * type: string + * enum: [customer, admin] + * description: The user's role + * createdAt: + * type: string + * format: date-time + * description: Account creation date + * updatedAt: + * type: string + * format: date-time + * description: Last update date + * 401: + * description: Unauthorized - Missing or invalid token + * 403: + * description: Forbidden - Admin access required + */ +router.get("/", authMiddleware, requireRole("admin"), UserController.getAll); + +export default router; diff --git a/src/services/user.service.ts b/src/services/user.service.ts new file mode 100644 index 0000000..1d7836c --- /dev/null +++ b/src/services/user.service.ts @@ -0,0 +1,14 @@ +import { UserRepository } from "../repositories/user.repo.js"; + +export class UserService { + static async getAll() { + const users = await UserRepository.findAll(); + return users.map((user) => ({ + id: user._id, + email: user.email, + role: user.role, + createdAt: user.createdAt, + updatedAt: user.updatedAt, + })); + } +}