Commit 712345b5 authored by Nguyễn Thị Nguyệt Quế's avatar Nguyễn Thị Nguyệt Quế

Merge branch 'feat/challenge_8_author' into 'develop'

feat(challenge_8): authorization and add api get/set role

See merge request !8
parents aed6385b 294fe7dc
......@@ -3,6 +3,8 @@ import type { Resource } from "express-automatic-routes";
import { ClassesProvider } from "#providers/ClassesProvider";
import { Req, Res } from "#interface/IApi";
import queryModifier from "#middlewares/request";
import { authorize } from "#middlewares/authorization";
import { authMiddleware } from "#middlewares/authentication";
export default (_express: Application) => {
const classesProvider = new ClassesProvider();
......@@ -13,6 +15,8 @@ export default (_express: Application) => {
* /api/v1.0/classes:
* get:
* tags: [Classes]
* security:
* - bearerAuth: []
* parameters:
* - $ref: '#/components/parameters/filters'
* - $ref: '#/components/parameters/sort'
......@@ -27,7 +31,7 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/ClassListResponse"
*/
get: {
middleware: [queryModifier],
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
try {
......@@ -63,13 +67,15 @@ export default (_express: Application) => {
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/ClassResponse"
* $ref: "#/components/schemas/ClassResponse"
* 400:
* description: Dữ liệu đầu vào không hợp lệ (Thiếu trường, sai định dạng)
* 500:
* description: Lỗi hệ thống phía Server
*/
post: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
try {
const newClass = await classesProvider.createClass(req.body);
......
......@@ -2,6 +2,9 @@ import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { ClassesProvider } from "#providers/ClassesProvider.js";
import { Req, Res } from "#interface/IApi";
import { authorize } from "#middlewares/authorization";
import queryModifier from "#middlewares/request";
import { authMiddleware } from "#middlewares/authentication";
export default (_express: Application) => {
const classesProvider = new ClassesProvider();
......@@ -12,6 +15,8 @@ export default (_express: Application) => {
* /api/v1.0/classes/{id}:
* get:
* tags: [Classes]
* security:
* - bearerAuth: []
* parameters:
* - name: id
* in: path
......@@ -28,6 +33,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/ClassResponse"
*/
get: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
const { id } = req.params;
......@@ -57,6 +64,8 @@ export default (_express: Application) => {
* /api/v1.0/classes/{id}:
* put:
* tags: [Classes]
* security:
* - bearerAuth: []
* parameters:
* - name: id
* in: path
......@@ -80,6 +89,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/ClassResponse"
*/
put: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
const { id } = req.params;
......@@ -123,6 +134,8 @@ export default (_express: Application) => {
* description: Xóa lớp học thành công
*/
delete: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
const { id } = req.params;
......
......@@ -2,6 +2,9 @@ import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { CoursesProvider } from "#providers/CoursesProvider.js";
import { Req, Res } from "#interface/IApi";
import queryModifier from "#middlewares/request";
import { authorize } from "#middlewares/authorization";
import { authMiddleware } from "#middlewares/authentication";
export default (_express: Application) => {
const coursesProvider = new CoursesProvider();
......@@ -12,6 +15,8 @@ export default (_express: Application) => {
* /api/v1.0/courses:
* get:
* tags: [Courses]
* security:
* - bearerAuth: []
* parameters:
* - $ref: '#/components/parameters/filters'
* - $ref: '#/components/parameters/sort'
......@@ -23,9 +28,11 @@ export default (_express: Application) => {
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/CourseListResponse"
* $ref: "#/components/schemas/CourseListResponse"
*/
get: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
try {
const courses = await coursesProvider.getAllCourses(req.payload);
......@@ -48,6 +55,8 @@ export default (_express: Application) => {
* post:
* tags: [Courses]
* description: Thêm một khóa học mới vào hệ thống
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
......@@ -63,6 +72,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/CourseResponse"
*/
post: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
try {
const newCourse = await coursesProvider.createCourse(req.body);
......
......@@ -2,6 +2,9 @@ import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { CoursesProvider } from "#providers/CoursesProvider.js";
import { Req, Res } from "#interface/IApi";
import queryModifier from "#middlewares/request";
import { authorize } from "#middlewares/authorization";
import { authMiddleware } from "#middlewares/authentication";
export default (_express: Application) => {
const coursesProvider = new CoursesProvider();
......@@ -12,6 +15,8 @@ export default (_express: Application) => {
* /api/v1.0/courses/{id}:
* get:
* tags: [Courses]
* security:
* - bearerAuth: []
* parameters:
* - name: id
* in: path
......@@ -28,6 +33,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/CourseResponse"
*/
get: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
const id = req.params.id;
......@@ -49,6 +56,8 @@ export default (_express: Application) => {
* /api/v1.0/courses/{id}:
* put:
* tags: [Courses]
* security:
* - bearerAuth: []
* parameters:
* - name: id
* in: path
......@@ -72,6 +81,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/CourseResponse"
*/
put: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
const { id } = req.params;
......@@ -101,6 +112,8 @@ export default (_express: Application) => {
* /api/v1.0/courses/{id}:
* delete:
* tags: [Courses]
* security:
* - bearerAuth: []
* parameters:
* - name: id
* in: path
......@@ -117,6 +130,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/CourseResponse"
*/
delete: {
middleware: [queryModifier, authMiddleware, authorize('admin', 'instructor')],
handler: async (req: Req, res: Res) => {
const { id } = req.params;
......
......@@ -2,6 +2,7 @@ import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { EnrollProvider } from "#providers/EnrollProvider.js";
import { Req, Res } from "#interface/IApi";
import queryModifier from "#middlewares/request";
export default (_express: Application) => {
const enrollProvider = new EnrollProvider();
......@@ -11,7 +12,7 @@ export default (_express: Application) => {
* @openapi
* /api/v1.0/enrollments/all-student-in-class:
* post:
* tags: [enrollments]
* tags: [Enrollments]
* description: Xem tất cả học sinh có trong lớp học
* requestBody:
* required: true
......@@ -30,6 +31,8 @@ export default (_express: Application) => {
* $ref: "#/components/schemas/AllStudentInClassOutput"
*/
post: {
middleware: [queryModifier],
handler: async (req: Req, res: Res) => {
try {
const students = await enrollProvider.getAllStudentInClass(req.body.class_id);
......
......@@ -4,6 +4,7 @@ import { EnrollProvider } from "#providers/EnrollProvider.js";
import { authorize } from "#middlewares/authorization";
import { authMiddleware } from "#middlewares/authentication";
import { Req, Res } from "#interface/IApi";
import queryModifier from "#middlewares/request";
export default (_express: Application) => {
const enrollProvider = new EnrollProvider();
......@@ -13,7 +14,7 @@ export default (_express: Application) => {
* @openapi
* /api/v1.0/enrollments/enroll:
* post:
* tags: [enrollments]
* tags: [Enrollments]
* security:
* - bearerAuth: []
* description: Tham gia khoá học
......@@ -29,12 +30,10 @@ export default (_express: Application) => {
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Enrollment"
* $ref: "#/components/schemas/Enrollment"
*/
post: {
middleware: [authMiddleware, authorize("student")],
middleware: [queryModifier, authMiddleware, authorize("student")],
handler: async (req: Req, res: Res) => {
try {
......
......@@ -3,6 +3,7 @@ import type { Resource } from "express-automatic-routes";
import { EnrollProvider } from "#providers/EnrollProvider.js";
import { authorize } from "#middlewares/authorization";
import { Req, Res } from "#interface/IApi";
import queryModifier from "#middlewares/request";
export default (_express: Application) => {
const enrollProvider = new EnrollProvider();
......@@ -12,7 +13,7 @@ export default (_express: Application) => {
* @openapi
* /api/v1.0/enrollments/unenroll:
* post:
* tags: [enrollments]
* tags: [Enrollments]
* description: Hủy đăng ký khóa học
* requestBody:
* required: true
......@@ -26,11 +27,10 @@ export default (_express: Application) => {
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Unenrollment"
* $ref: "#/components/schemas/Unenrollment"
*/
post: {
middleware: [queryModifier],
handler: async (req: Req, res: Res) => {
try {
const enroll = await enrollProvider.unEnroll(req.body.userId, req.body.classId);
......
import { Req, Res } from "#interface/IApi";
import { RolesProvider } from "#providers/RolesProvider.js";
import { Application } from "express";
import { Resource } from "express-automatic-routes";
import queryModifier from "#middlewares/request";
export default (_express: Application) => {
const rolesProvider = new RolesProvider();
return <Resource>{
/**
* @openapi
* /api/v1.0/roles:
* get:
* tags: [Roles]
* parameters:
* - $ref: '#/components/parameters/filters'
* - $ref: '#/components/parameters/sort'
* - $ref: '#/components/parameters/page'
* - $ref: '#/components/parameters/pageSize'
* responses:
* 200:
* description: Trả về danh sách vai trò thành công
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/RoleListResponse"
*/
get: {
middleware: [queryModifier],
handler: async (req: Req, res: Res) => {
try {
const roles = await rolesProvider.getRoles(req.payload);
res.sendOk({
data: roles,
message: "Lấy danh sách vai trò thành công",
message_en: "Roles fetched successfully",
status: 200
});
} catch (error) {
console.error("Error fetching roles:", error);
res.sendError({
message: "Lấy danh sách vai trò thất bại",
message_en: "Failed to fetch roles",
status: 500
});
}
}
}
};
};
\ No newline at end of file
import { Req, Res } from "#interface/IApi";
import { RolesProvider } from "#providers/RolesProvider.js";
import { Application } from "express";
import { Resource } from "express-automatic-routes";
import { authorize } from "#middlewares/authorization";
import { authMiddleware } from "#middlewares/authentication";
export default (_express: Application) => {
const rolesProvider = new RolesProvider();
return <Resource>{
/**
* @openapi
* /api/v1.0/roles/set-role:
* post:
* tags: [Roles]
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/setRoleRequest"
* responses:
* 201:
* description: Cập nhật vai trò thành công
* content:
* application/json:
* schema:
* $ref: "#/components/schemas/SetRoleResponse"
*/
post: {
middleware: [authMiddleware, authorize("admin")],
handler: async (req: Req, res: Res) => {
try {
const setRole = await rolesProvider.setRole(req.body);
res.sendOk({
data: setRole,
message: "Cập nhật vai trò thành công",
message_en: "Role updated successfully",
status: 201
});
} catch (error) {
console.error("Error updating role:", error);
res.sendError({
message: "Cập nhật vai trò thất bại",
message_en: "Failed to update role",
status: 500
});
}
}
}
};
};
\ No newline at end of file
......@@ -284,23 +284,7 @@
"type": "string",
"example": "2024-02-26 03:12:45"
},
"violations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"code": {
"type": "number"
},
"message": {
"type": "string"
},
"action": {
"nullable": true
}
}
}
}
"violations": {}
}
},
"CourseListResponse": {
......@@ -598,18 +582,26 @@
},
"EnrollmentInput": {
"type": "object",
"required": [
"class_id"
],
"example": {
"class_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"properties": {
"class_id": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
},
"UnenrollmentInput": {
"type": "object",
"required": [
"userId",
"classId"
],
"example": {
"userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"classId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
......@@ -617,10 +609,12 @@
"properties": {
"userId": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"classId": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
......@@ -629,24 +623,45 @@
"type": "object",
"example": {
"message": "Hủy đăng ký khóa học thành công"
},
"properties": {
"message": {
"type": "string",
"example": "Hủy đăng ký khóa học thành công"
}
}
},
"Enrollment": {
"type": "object",
"example": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"class_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"created_at": "2026-05-20T00:00:00.000Z",
"status": "active"
},
"properties": {
"id": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"user_id": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"class_id": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"created_at": {
"type": "string",
"format": "date-time",
"nullable": true,
"example": "2026-05-20T00:00:00.000Z"
},
"status": {
"type": "string",
"example": "active"
......@@ -655,12 +670,16 @@
},
"AllStudentInClassInput": {
"type": "object",
"required": [
"class_id"
],
"example": {
"class_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"properties": {
"class_id": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
......@@ -686,6 +705,126 @@
"example": "nguyenvana@example.com"
}
}
},
"RoleListResponse": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
},
"rows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Role"
}
}
}
},
"Role": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"created_by": {
"type": "string"
}
}
},
"setRoleRequest": {
"type": "object",
"properties": {
"user_id": {
"type": "string"
},
"role_id": {
"type": "string"
}
},
"required": [
"user_id",
"role_id"
]
},
"SetRoleResponse": {
"type": "object",
"properties": {
"message": {
"type": "string",
"example": "Cập nhật vai trò thành công"
},
"message_en": {
"type": "string",
"example": "Role updated successfully"
},
"responseData": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "123e4567-e89b-12d3-a456-426614174000"
},
"name": {
"type": "string",
"example": "John Doe"
},
"date_of_birth": {
"type": "string",
"format": "date-time",
"example": "1990-09-30T17:00:00.000Z"
},
"phone": {
"type": "string",
"example": "0901234567"
},
"email": {
"type": "string",
"format": "email",
"example": "john.doe@example.com"
},
"address": {
"type": "string",
"example": "123 Main St, City, Country"
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2023-01-01T00:00:00.000Z"
},
"role_id": {
"type": "string",
"example": "41d5ac41-3ee4-45cb-a381-a83b07694b99"
}
}
},
"status": {
"type": "string",
"example": "true"
},
"timeStamp": {
"type": "string",
"example": "2026-05-20 13:37:24"
},
"violations": {
"type": "array"
}
}
}
},
"parameters": {
......@@ -911,6 +1050,11 @@
"tags": [
"Classes"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "id",
......@@ -939,6 +1083,11 @@
"tags": [
"Classes"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "id",
......@@ -1002,6 +1151,11 @@
"tags": [
"Classes"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"$ref": "#/components/parameters/filters"
......@@ -1069,6 +1223,11 @@
"tags": [
"Courses"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "id",
......@@ -1096,6 +1255,11 @@
"tags": [
"Courses"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "id",
......@@ -1133,6 +1297,11 @@
"tags": [
"Courses"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "id",
......@@ -1162,6 +1331,11 @@
"tags": [
"Courses"
],
"security": [
{
"bearerAuth": []
}
],
"parameters": [
{
"$ref": "#/components/parameters/filters"
......@@ -1194,6 +1368,11 @@
"Courses"
],
"description": "Thêm một khóa học mới vào hệ thống",
"security": [
{
"bearerAuth": []
}
],
"requestBody": {
"required": true,
"content": {
......@@ -1221,7 +1400,7 @@
"/api/v1.0/enrollments/all-student-in-class": {
"post": {
"tags": [
"enrollments"
"Enrollments"
],
"description": "Xem tất cả học sinh có trong lớp học",
"requestBody": {
......@@ -1254,7 +1433,7 @@
"/api/v1.0/enrollments/enroll": {
"post": {
"tags": [
"enrollments"
"Enrollments"
],
"security": [
{
......@@ -1278,10 +1457,7 @@
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Enrollment"
}
"$ref": "#/components/schemas/Enrollment"
}
}
}
......@@ -1292,7 +1468,7 @@
"/api/v1.0/enrollments/unenroll": {
"post": {
"tags": [
"enrollments"
"Enrollments"
],
"description": "Hủy đăng ký khóa học",
"requestBody": {
......@@ -1311,10 +1487,74 @@
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Unenrollment"
}
"$ref": "#/components/schemas/Unenrollment"
}
}
}
}
}
}
},
"/api/v1.0/roles": {
"get": {
"tags": [
"Roles"
],
"parameters": [
{
"$ref": "#/components/parameters/filters"
},
{
"$ref": "#/components/parameters/sort"
},
{
"$ref": "#/components/parameters/page"
},
{
"$ref": "#/components/parameters/pageSize"
}
],
"responses": {
"200": {
"description": "Trả về danh sách vai trò thành công",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RoleListResponse"
}
}
}
}
}
}
},
"/api/v1.0/roles/set-role": {
"post": {
"tags": [
"Roles"
],
"security": [
{
"bearerAuth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/setRoleRequest"
}
}
}
},
"responses": {
"201": {
"description": "Cập nhật vai trò thành công",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SetRoleResponse"
}
}
}
......
......@@ -17,21 +17,28 @@ export default function queryModifier(req: Req, _res: Res, next: NextFunction) {
const arrFilters = filters.split(",");
arrFilters.forEach((element) => {
// Mỗi phần tử sẽ có dạng: "status|=|published" hoặc "title|like|NodeJS"
const parts = element.split("|");
// Mỗi phần tử sẽ có dạng: "status == published" hoặc "title @= NodeJS"
const trimmedElement = element.trim();
const parts = trimmedElement.match(/^(\S+)\s*(==|@=|>=|<=|>|<|\||&)\s*(.+)$/i);
// Phải đủ 3 thành phần: [Tên cột, Toán tử, Giá trị]
if (parts.length === 3) {
const [field, operator, value] = parts as [string, string, string];
if (parts) {
const field = parts[1]?.trim();
const operator = parts[2]?.trim().toLowerCase();
const value = parts[3]?.trim();
if (!field || !operator || value === undefined) {
return;
}
let condition: any = {};
// Dịch toán tử từ URL sang toán tử Sequelize tương ứng
switch (operator.toLowerCase()) {
case "=":
switch (operator) {
case "==":
condition[field] = { [Op.eq]: value };
break;
case "like":
case "@=":
condition[field] = { [Op.iLike]: `%${value}%` };
break;
case ">":
......@@ -46,6 +53,18 @@ export default function queryModifier(req: Req, _res: Res, next: NextFunction) {
case "<=":
condition[field] = { [Op.lte]: value };
break;
case "|":
const orValues = value.split("/").map(v => v.trim());
condition[field] = { [Op.or]: orValues };
break;
case "&":
const andValues = value.split("/").map(v => v.trim());
const andConditions = andValues.map(v => ({
[Op.iLike]: `%${v}%`
}));
condition[field] = { [Op.and]: andConditions };
break;
default:
condition[field] = { [Op.eq]: value };
}
......
......@@ -13,7 +13,8 @@ export class CoursesProvider {
where: params.filters,
order: params.sortBy ? [[params.sortBy, params.sortOrder]] : [['created_at', 'DESC']],
limit: params.pageSize,
offset: (params.page - 1) * params.pageSize
offset: (params.page - 1) * params.pageSize,
raw: true,
});
return {
count,
......
import { payload } from "#interface/IApi";
import { models } from "#models/sequelize-config.js";
export class RolesProvider {
async getRoles(params: payload) {
const { count, rows } = await models.roles.findAndCountAll({
where: params.filters,
order: params.sortBy ? [[params.sortBy, params.sortOrder]] : [['created_at', 'DESC']],
limit: params.pageSize,
offset: (params.page - 1) * params.pageSize,
raw: true
});
return {
count,
page: params.page,
pageSize: params.pageSize,
rows
};
}
async setRole(body: { user_id: string; role_id: string }) {
const [affectedRows, updatedUsers] = await models.users.update(
{ role_id: body.role_id },
{
where: {
id: body.user_id
},
returning: true,
},
);
return updatedUsers[0];
}
}
......@@ -10,6 +10,7 @@ import sendOTPSchemas from './sendOTP/schema.js';
import verifyOTPSchemas from './verifyOTP/schema.js';
import logoutSchemas from './logout/schema.js';
import enrollmentSchemas from './enrollment/schema.js';
import rolesSchemas from './roles/schema.js';
const swaggerOptions: Options = {
definition: {
......@@ -36,6 +37,7 @@ const swaggerOptions: Options = {
...verifyOTPSchemas,
...logoutSchemas,
...enrollmentSchemas,
...rolesSchemas,
},
parameters: {
filters: {
......
......@@ -10,12 +10,31 @@ const courseSchemas = {
created_by: '2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11',
},
properties: {
id: { type: 'string', format: 'uuid' },
name: { type: 'string' },
description: { type: 'string', nullable: true },
status: { type: 'string', nullable: true },
created_at: { type: 'string', format: 'date-time', nullable: true },
created_by: { type: 'string', format: 'uuid', nullable: true },
id: {
type: 'string',
format: 'uuid'
},
name: {
type: 'string'
},
description: {
type: 'string',
nullable: true
},
status: {
type: 'string',
nullable: true
},
created_at: {
type: 'string',
format: 'date-time',
nullable: true
},
created_by: {
type: 'string',
format: 'uuid',
nullable: true
},
},
},
CourseResponse: {
......@@ -40,23 +59,7 @@ const courseSchemas = {
type: 'string',
example: '2024-02-26 03:12:45',
},
violations: {
type: 'array',
items: {
type: 'object',
properties: {
code: {
type: 'number',
},
message: {
type: 'string',
},
action: {
nullable: true,
},
},
},
},
violations: {},
},
},
......
const enrollmentSchemas = {
EnrollmentInput: {
type: 'object',
required: ['class_id'],
example: {
class_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
properties: {
class_id: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
},
......@@ -14,6 +16,7 @@ const enrollmentSchemas = {
UnenrollmentInput: {
type: 'object',
required: ['userId', 'classId'],
example: {
userId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
classId: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
......@@ -21,10 +24,12 @@ const enrollmentSchemas = {
properties: {
userId: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
classId: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
},
......@@ -35,24 +40,45 @@ const enrollmentSchemas = {
example: {
message: 'Hủy đăng ký khóa học thành công',
},
properties: {
message: {
type: 'string',
example: 'Hủy đăng ký khóa học thành công',
},
},
},
Enrollment: {
type: 'object',
example: {
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
user_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
class_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
created_at: '2026-05-20T00:00:00.000Z',
status: 'active',
},
properties: {
id: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
user_id: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
class_id: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
created_at: {
type: 'string',
format: 'date-time',
nullable: true,
example: '2026-05-20T00:00:00.000Z',
},
status: {
type: 'string',
example: 'active',
......@@ -62,12 +88,14 @@ const enrollmentSchemas = {
AllStudentInClassInput: {
type: 'object',
required: ['class_id'],
example: {
class_id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
properties: {
class_id: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
},
......
const rolesSchemas = {
RoleListResponse: {
type: "object",
properties: {
count: {
type: "integer"
},
page: {
type: "integer"
},
pageSize: {
type: "integer"
},
rows: {
type: "array",
items: {
$ref: "#/components/schemas/Role"
}
}
}
},
Role: {
type: "object",
properties: {
id: {
type: "string"
},
name: {
type: "string"
},
description: {
type: "string"
},
created_at: {
type: "string",
format: "date-time"
},
created_by: {
type: "string"
}
}
},
setRoleRequest: {
type: "object",
properties: {
user_id: {
type: "string"
},
role_id: {
type: "string"
}
},
required: ["user_id", "role_id"]
},
SetRoleResponse: {
type: "object",
properties: {
message: {
type: "string",
example: "Cập nhật vai trò thành công"
},
message_en: {
type: "string",
example: "Role updated successfully"
},
responseData: {
type: "object",
properties: {
id: {
type: "string",
example: "123e4567-e89b-12d3-a456-426614174000"
},
name: {
type: "string",
example: "John Doe"
},
date_of_birth: {
type: "string",
format: "date-time",
example: "1990-09-30T17:00:00.000Z"
},
phone: {
type: "string",
example: "0901234567"
},
email: {
type: "string",
format: "email",
example: "john.doe@example.com"
},
address: {
type: "string",
example: "123 Main St, City, Country"
},
created_at: {
type: "string",
format: "date-time",
example: "2023-01-01T00:00:00.000Z"
},
role_id: {
type: "string",
example: "41d5ac41-3ee4-45cb-a381-a83b07694b99"
}
}
},
status: {
type: "string",
example: "true"
},
timeStamp: {
type: "string",
example: "2026-05-20 13:37:24"
},
violations: {
type: "array",
}
}
}
}
export default rolesSchemas;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment