Commit 38e7d8fd authored by Phạm Quang Bảo's avatar Phạm Quang Bảo

feat: refactor src code and add swagger ui

parent aff2faee
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('classes', {
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: true
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
created_by: {
type: DataTypes.UUID,
allowNull: true
},
updated_by: {
type: DataTypes.UUID,
allowNull: true
},
course_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'courses',
key: 'id'
}
},
status: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
sequelize,
tableName: 'classes',
schema: 'public',
timestamps: true,
indexes: [
{
name: "classes_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
};
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('courses', {
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: true
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
created_by: {
type: DataTypes.UUID,
allowNull: true
},
status: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
sequelize,
tableName: 'courses',
schema: 'public',
timestamps: true,
indexes: [
{
name: "courses_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
};
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('enrollments', {
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
user_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'users',
key: 'id'
}
},
class_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'classes',
key: 'id'
}
},
status: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
sequelize,
tableName: 'enrollments',
schema: 'public',
timestamps: true
});
};
var DataTypes = require("sequelize").DataTypes;
var _classes = require("./classes");
var _courses = require("./courses");
var _enrollments = require("./enrollments");
var _roles = require("./roles");
var _user_auth = require("./user_auth");
var _users = require("./users");
function initModels(sequelize) {
var classes = _classes(sequelize, DataTypes);
var courses = _courses(sequelize, DataTypes);
var enrollments = _enrollments(sequelize, DataTypes);
var roles = _roles(sequelize, DataTypes);
var user_auth = _user_auth(sequelize, DataTypes);
var users = _users(sequelize, DataTypes);
enrollments.belongsTo(classes, { as: "class", foreignKey: "class_id"});
classes.hasMany(enrollments, { as: "enrollments", foreignKey: "class_id"});
classes.belongsTo(courses, { as: "course", foreignKey: "course_id"});
courses.hasMany(classes, { as: "classes", foreignKey: "course_id"});
users.belongsTo(roles, { as: "role", foreignKey: "role_id"});
roles.hasMany(users, { as: "users", foreignKey: "role_id"});
enrollments.belongsTo(users, { as: "user", foreignKey: "user_id"});
users.hasMany(enrollments, { as: "enrollments", foreignKey: "user_id"});
user_auth.belongsTo(users, { as: "user", foreignKey: "user_id"});
users.hasMany(user_auth, { as: "user_auths", foreignKey: "user_id"});
return {
classes,
courses,
enrollments,
roles,
user_auth,
users,
};
}
module.exports = initModels;
module.exports.initModels = initModels;
module.exports.default = initModels;
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('roles', {
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
created_by: {
type: DataTypes.UUID,
allowNull: true
}
}, {
sequelize,
tableName: 'roles',
schema: 'public',
timestamps: true,
indexes: [
{
name: "roles_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
};
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('users', {
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
date_of_birth: {
type: DataTypes.DATE,
allowNull: true
},
phone: {
type: DataTypes.TEXT,
allowNull: true
},
email: {
type: DataTypes.STRING(255),
allowNull: true
},
address: {
type: DataTypes.TEXT,
allowNull: true
},
role_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'roles',
key: 'id'
}
}
}, {
sequelize,
tableName: 'users',
schema: 'public',
timestamps: true,
indexes: [
{
name: "users_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
};
This diff is collapsed.
{
"name": "code",
"name": "be-challenge",
"version": "1.0.0",
"description": "",
"license": "ISC",
"author": "",
"type": "commonjs",
"main": "index.js",
"author": "PhamQuangBao",
"main": "./src/index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"dev": "tsx watch ./src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1",
"gen:db": "sequelize-auto -h localhost -d challenge_db -u postgres -x 123456 -p 2550 --dialect postgres --lang ts -o ./src/models",
"gen:swagger": "tsx src/scripts/swagger-gen.ts"
},
"dependencies": {
"express": "^5.2.1",
"express-automatic-routes": "^1.1.0",
"module-alias": "^2.3.4",
"pg": "^8.20.0",
"pg-hstore": "^2.3.4",
"sequelize": "^6.37.8",
"sequelize-auto": "^0.8.8"
"sequelize-auto": "^0.8.8",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1"
},
"devDependencies": {
"@types/express": "^5.0.6",
"@types/node": "^25.7.0",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.8",
"ts-node": "^10.9.2",
"tsx": "^4.21.0",
"typescript": "^6.0.3"
}
}
}
\ No newline at end of file
This diff is collapsed.
import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { ClassesProvider } from "#providers/ClassesProvider";
export default (_express: Application) => {
const classesProvider = new ClassesProvider();
return <Resource>{
/**
* @openapi
* /api/v1.0/classes:
* get:
* tags: [Classes]
* summary: Lấy danh sách lớp học
* description: Trả về danh sách lớp học.
* responses:
* 200:
* description: Trả về danh sách lớp học thành công
* content:
* application/json:
* schema:
* type: array
* items:
* $ref: "#/components/schemas/Class"
*/
get: {
handler: async (req, res) => {
const classes = await classesProvider.getAllClasses();
return res.json(classes);
}
}
};
};
\ No newline at end of file
import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { ClassesProvider } from "#providers/ClassesProvider.js";
export default (_express: Application) => {
const classesProvider = new ClassesProvider();
return <Resource>{
get: {
handler: async (req, res) => {
const classId = Number(req.params.id);
const classData = await classesProvider.getClassById(classId);
return res.json(classData);
}
}
};
};
\ No newline at end of file
import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { CoursesProvider } from "#providers/CoursesProvider.js";
export default (_express: Application) => {
const coursesProvider = new CoursesProvider();
return <Resource>{
get: {
handler: async (req, res) => {
// #swagger.tags = ['Courses']
// #swagger.summary = 'Lấy danh sách khóa học'
/* #swagger.responses[200] = {
description: 'Lấy dữ liệu thành công',
schema: { $ref: '#/definitions/Course' }
} */
const courses = await coursesProvider.getAllCourses();
return res.json(courses);
}
}
};
};
\ No newline at end of file
import type { Application } from "express";
import type { Resource } from "express-automatic-routes";
import { CoursesProvider } from "#providers/CoursesProvider.js";
export default (_express: Application) => {
const coursesProvider = new CoursesProvider();
return <Resource>{
get: {
handler: async (req, res) => {
const courseId = Number(req.params.id);
const course = await coursesProvider.getCourseById(courseId);
return res.json(course);
}
}
};
};
\ No newline at end of file
{
"openapi": "3.1.0",
"info": {
"title": "Backend Challenges API",
"version": "1.0.0"
},
"components": {
"schemas": {
"Class": {
"type": "object",
"example": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Lớp 12A1",
"description": "Lớp chuyên toán",
"created_at": "2026-05-16T08:00:00.000Z",
"created_by": "2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11",
"updated_at": "2026-05-16T09:30:00.000Z",
"updated_by": "2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11",
"course_id": "7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10",
"status": "active"
},
"properties": {
"id": {
"type": "string",
"format": "uuid",
"example": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
"name": {
"type": "string",
"example": "Lớp 12A1"
},
"description": {
"type": "string",
"nullable": true,
"example": "Lớp chuyên toán"
},
"created_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"created_by": {
"type": "string",
"format": "uuid",
"nullable": true,
"example": "2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11"
},
"updated_at": {
"type": "string",
"format": "date-time",
"nullable": true
},
"updated_by": {
"type": "string",
"format": "uuid",
"nullable": true,
"example": "2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11"
},
"course_id": {
"type": "string",
"format": "uuid",
"nullable": true,
"example": "7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10"
},
"status": {
"type": "string",
"nullable": true,
"example": "active"
}
}
}
}
},
"paths": {
"/api/v1.0/classes": {
"get": {
"tags": [
"Classes"
],
"summary": "Lấy danh sách lớp học",
"description": "Trả về danh sách lớp học.",
"responses": {
"200": {
"description": "Trả về danh sách lớp học thành công",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Class"
}
}
}
}
}
}
}
}
},
"tags": []
}
\ No newline at end of file
import express from 'express';
import { resolve } from 'path';
import _autoroutes from 'express-automatic-routes';
import swaggerUi from 'swagger-ui-express';
import swaggerFile from '#/docs/swagger/swagger-output.json';
const app = express()
const port = 3000
_autoroutes(app, {
dir: resolve(__dirname, './controllers'),
log: true
});
const swaggerOptions = {
swaggerOptions: {
url: '/swagger.json',
},
};
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerFile, swaggerOptions));
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
import * as Sequelize from 'sequelize';
import { DataTypes, Model, Optional } from 'sequelize';
import type { courses, coursesId } from './courses';
import type { enrollments, enrollmentsId } from './enrollments';
export interface classesAttributes {
id: string;
name?: string;
description?: string;
created_at?: Date;
created_by?: string;
updated_at?: Date;
updated_by?: string;
course_id?: string;
status?: string;
}
export type classesPk = "id";
export type classesId = classes[classesPk];
export type classesOptionalAttributes = "id" | "name" | "description" | "created_at" | "created_by" | "updated_at" | "updated_by" | "course_id" | "status";
export type classesCreationAttributes = Optional<classesAttributes, classesOptionalAttributes>;
export class classes extends Model<classesAttributes, classesCreationAttributes> implements classesAttributes {
id!: string;
name?: string;
description?: string;
created_at?: Date;
created_by?: string;
updated_at?: Date;
updated_by?: string;
course_id?: string;
status?: string;
// classes hasMany enrollments via class_id
enrollments!: enrollments[];
getEnrollments!: Sequelize.HasManyGetAssociationsMixin<enrollments>;
setEnrollments!: Sequelize.HasManySetAssociationsMixin<enrollments, enrollmentsId>;
addEnrollment!: Sequelize.HasManyAddAssociationMixin<enrollments, enrollmentsId>;
addEnrollments!: Sequelize.HasManyAddAssociationsMixin<enrollments, enrollmentsId>;
createEnrollment!: Sequelize.HasManyCreateAssociationMixin<enrollments>;
removeEnrollment!: Sequelize.HasManyRemoveAssociationMixin<enrollments, enrollmentsId>;
removeEnrollments!: Sequelize.HasManyRemoveAssociationsMixin<enrollments, enrollmentsId>;
hasEnrollment!: Sequelize.HasManyHasAssociationMixin<enrollments, enrollmentsId>;
hasEnrollments!: Sequelize.HasManyHasAssociationsMixin<enrollments, enrollmentsId>;
countEnrollments!: Sequelize.HasManyCountAssociationsMixin;
// classes belongsTo courses via course_id
course!: courses;
getCourse!: Sequelize.BelongsToGetAssociationMixin<courses>;
setCourse!: Sequelize.BelongsToSetAssociationMixin<courses, coursesId>;
createCourse!: Sequelize.BelongsToCreateAssociationMixin<courses>;
static initModel(sequelize: Sequelize.Sequelize): typeof classes {
return classes.init({
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: true
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
created_by: {
type: DataTypes.UUID,
allowNull: true
},
updated_by: {
type: DataTypes.UUID,
allowNull: true
},
course_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'courses',
key: 'id'
}
},
status: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
sequelize,
tableName: 'classes',
schema: 'public',
timestamps: true,
indexes: [
{
name: "classes_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
}
}
import * as Sequelize from 'sequelize';
import { DataTypes, Model, Optional } from 'sequelize';
import type { classes, classesId } from './classes';
export interface coursesAttributes {
id: string;
name?: string;
description?: string;
created_at?: Date;
created_by?: string;
status?: string;
}
export type coursesPk = "id";
export type coursesId = courses[coursesPk];
export type coursesOptionalAttributes = "id" | "name" | "description" | "created_at" | "created_by" | "status";
export type coursesCreationAttributes = Optional<coursesAttributes, coursesOptionalAttributes>;
export class courses extends Model<coursesAttributes, coursesCreationAttributes> implements coursesAttributes {
id!: string;
name?: string;
description?: string;
created_at?: Date;
created_by?: string;
status?: string;
// courses hasMany classes via course_id
classes!: classes[];
getClasses!: Sequelize.HasManyGetAssociationsMixin<classes>;
setClasses!: Sequelize.HasManySetAssociationsMixin<classes, classesId>;
addClass!: Sequelize.HasManyAddAssociationMixin<classes, classesId>;
addClasses!: Sequelize.HasManyAddAssociationsMixin<classes, classesId>;
createClass!: Sequelize.HasManyCreateAssociationMixin<classes>;
removeClass!: Sequelize.HasManyRemoveAssociationMixin<classes, classesId>;
removeClasses!: Sequelize.HasManyRemoveAssociationsMixin<classes, classesId>;
hasClass!: Sequelize.HasManyHasAssociationMixin<classes, classesId>;
hasClasses!: Sequelize.HasManyHasAssociationsMixin<classes, classesId>;
countClasses!: Sequelize.HasManyCountAssociationsMixin;
static initModel(sequelize: Sequelize.Sequelize): typeof courses {
return courses.init({
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: true
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
created_by: {
type: DataTypes.UUID,
allowNull: true
},
status: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
sequelize,
tableName: 'courses',
schema: 'public',
timestamps: true,
indexes: [
{
name: "courses_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
}
}
import * as Sequelize from 'sequelize';
import { DataTypes, Model, Optional } from 'sequelize';
import type { classes, classesId } from './classes';
import type { users, usersId } from './users';
export interface enrollmentsAttributes {
id: string;
user_id?: string;
class_id?: string;
created_at?: Date;
status?: string;
}
export type enrollmentsPk = "id";
export type enrollmentsId = enrollments[enrollmentsPk];
export type enrollmentsOptionalAttributes = "id" | "user_id" | "class_id" | "created_at" | "status";
export type enrollmentsCreationAttributes = Optional<enrollmentsAttributes, enrollmentsOptionalAttributes>;
export class enrollments extends Model<enrollmentsAttributes, enrollmentsCreationAttributes> implements enrollmentsAttributes {
id!: string;
user_id?: string;
class_id?: string;
created_at?: Date;
status?: string;
// enrollments belongsTo classes via class_id
class!: classes;
getClass!: Sequelize.BelongsToGetAssociationMixin<classes>;
setClass!: Sequelize.BelongsToSetAssociationMixin<classes, classesId>;
createClass!: Sequelize.BelongsToCreateAssociationMixin<classes>;
// enrollments belongsTo users via user_id
user!: users;
getUser!: Sequelize.BelongsToGetAssociationMixin<users>;
setUser!: Sequelize.BelongsToSetAssociationMixin<users, usersId>;
createUser!: Sequelize.BelongsToCreateAssociationMixin<users>;
static initModel(sequelize: Sequelize.Sequelize): typeof enrollments {
return enrollments.init({
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
user_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'users',
key: 'id'
}
},
class_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'classes',
key: 'id'
}
},
status: {
type: DataTypes.STRING(50),
allowNull: true
}
}, {
sequelize,
tableName: 'enrollments',
schema: 'public',
timestamps: true
});
}
}
import type { Sequelize } from "sequelize";
import { classes as _classes } from "./classes";
import type { classesAttributes, classesCreationAttributes } from "./classes";
import { courses as _courses } from "./courses";
import type { coursesAttributes, coursesCreationAttributes } from "./courses";
import { enrollments as _enrollments } from "./enrollments";
import type { enrollmentsAttributes, enrollmentsCreationAttributes } from "./enrollments";
import { roles as _roles } from "./roles";
import type { rolesAttributes, rolesCreationAttributes } from "./roles";
import { user_auth as _user_auth } from "./user_auth";
import type { user_authAttributes, user_authCreationAttributes } from "./user_auth";
import { users as _users } from "./users";
import type { usersAttributes, usersCreationAttributes } from "./users";
export {
_classes as classes,
_courses as courses,
_enrollments as enrollments,
_roles as roles,
_user_auth as user_auth,
_users as users,
};
export type {
classesAttributes,
classesCreationAttributes,
coursesAttributes,
coursesCreationAttributes,
enrollmentsAttributes,
enrollmentsCreationAttributes,
rolesAttributes,
rolesCreationAttributes,
user_authAttributes,
user_authCreationAttributes,
usersAttributes,
usersCreationAttributes,
};
export function initModels(sequelize: Sequelize) {
const classes = _classes.initModel(sequelize);
const courses = _courses.initModel(sequelize);
const enrollments = _enrollments.initModel(sequelize);
const roles = _roles.initModel(sequelize);
const user_auth = _user_auth.initModel(sequelize);
const users = _users.initModel(sequelize);
enrollments.belongsTo(classes, { as: "class", foreignKey: "class_id"});
classes.hasMany(enrollments, { as: "enrollments", foreignKey: "class_id"});
classes.belongsTo(courses, { as: "course", foreignKey: "course_id"});
courses.hasMany(classes, { as: "classes", foreignKey: "course_id"});
users.belongsTo(roles, { as: "role", foreignKey: "role_id"});
roles.hasMany(users, { as: "users", foreignKey: "role_id"});
enrollments.belongsTo(users, { as: "user", foreignKey: "user_id"});
users.hasMany(enrollments, { as: "enrollments", foreignKey: "user_id"});
user_auth.belongsTo(users, { as: "user", foreignKey: "user_id"});
users.hasMany(user_auth, { as: "user_auths", foreignKey: "user_id"});
return {
classes: classes,
courses: courses,
enrollments: enrollments,
roles: roles,
user_auth: user_auth,
users: users,
};
}
import * as Sequelize from 'sequelize';
import { DataTypes, Model, Optional } from 'sequelize';
import type { users, usersId } from './users';
export interface rolesAttributes {
id: string;
name: string;
description?: string;
created_at?: Date;
created_by?: string;
}
export type rolesPk = "id";
export type rolesId = roles[rolesPk];
export type rolesOptionalAttributes = "id" | "description" | "created_at" | "created_by";
export type rolesCreationAttributes = Optional<rolesAttributes, rolesOptionalAttributes>;
export class roles extends Model<rolesAttributes, rolesCreationAttributes> implements rolesAttributes {
id!: string;
name!: string;
description?: string;
created_at?: Date;
created_by?: string;
// roles hasMany users via role_id
users!: users[];
getUsers!: Sequelize.HasManyGetAssociationsMixin<users>;
setUsers!: Sequelize.HasManySetAssociationsMixin<users, usersId>;
addUser!: Sequelize.HasManyAddAssociationMixin<users, usersId>;
addUsers!: Sequelize.HasManyAddAssociationsMixin<users, usersId>;
createUser!: Sequelize.HasManyCreateAssociationMixin<users>;
removeUser!: Sequelize.HasManyRemoveAssociationMixin<users, usersId>;
removeUsers!: Sequelize.HasManyRemoveAssociationsMixin<users, usersId>;
hasUser!: Sequelize.HasManyHasAssociationMixin<users, usersId>;
hasUsers!: Sequelize.HasManyHasAssociationsMixin<users, usersId>;
countUsers!: Sequelize.HasManyCountAssociationsMixin;
static initModel(sequelize: Sequelize.Sequelize): typeof roles {
return roles.init({
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
created_by: {
type: DataTypes.UUID,
allowNull: true
}
}, {
sequelize,
tableName: 'roles',
schema: 'public',
timestamps: true,
indexes: [
{
name: "roles_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
}
}
const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('user_auth', {
import * as Sequelize from 'sequelize';
import { DataTypes, Model, Optional } from 'sequelize';
import type { users, usersId } from './users';
export interface user_authAttributes {
id: string;
user_id?: string;
password_hash?: string;
created_at?: Date;
}
export type user_authPk = "id";
export type user_authId = user_auth[user_authPk];
export type user_authOptionalAttributes = "id" | "user_id" | "password_hash" | "created_at";
export type user_authCreationAttributes = Optional<user_authAttributes, user_authOptionalAttributes>;
export class user_auth extends Model<user_authAttributes, user_authCreationAttributes> implements user_authAttributes {
id!: string;
user_id?: string;
password_hash?: string;
created_at?: Date;
// user_auth belongsTo users via user_id
user!: users;
getUser!: Sequelize.BelongsToGetAssociationMixin<users>;
setUser!: Sequelize.BelongsToSetAssociationMixin<users, usersId>;
createUser!: Sequelize.BelongsToCreateAssociationMixin<users>;
static initModel(sequelize: Sequelize.Sequelize): typeof user_auth {
return user_auth.init({
id: {
type: DataTypes.UUID,
allowNull: false,
......@@ -34,4 +61,5 @@ module.exports = function(sequelize, DataTypes) {
},
]
});
};
}
}
import * as Sequelize from 'sequelize';
import { DataTypes, Model, Optional } from 'sequelize';
import type { enrollments, enrollmentsId } from './enrollments';
import type { roles, rolesId } from './roles';
import type { user_auth, user_authId } from './user_auth';
export interface usersAttributes {
id: string;
name: string;
date_of_birth?: Date;
phone?: string;
email?: string;
address?: string;
created_at?: Date;
role_id?: string;
}
export type usersPk = "id";
export type usersId = users[usersPk];
export type usersOptionalAttributes = "id" | "date_of_birth" | "phone" | "email" | "address" | "created_at" | "role_id";
export type usersCreationAttributes = Optional<usersAttributes, usersOptionalAttributes>;
export class users extends Model<usersAttributes, usersCreationAttributes> implements usersAttributes {
id!: string;
name!: string;
date_of_birth?: Date;
phone?: string;
email?: string;
address?: string;
created_at?: Date;
role_id?: string;
// users belongsTo roles via role_id
role!: roles;
getRole!: Sequelize.BelongsToGetAssociationMixin<roles>;
setRole!: Sequelize.BelongsToSetAssociationMixin<roles, rolesId>;
createRole!: Sequelize.BelongsToCreateAssociationMixin<roles>;
// users hasMany enrollments via user_id
enrollments!: enrollments[];
getEnrollments!: Sequelize.HasManyGetAssociationsMixin<enrollments>;
setEnrollments!: Sequelize.HasManySetAssociationsMixin<enrollments, enrollmentsId>;
addEnrollment!: Sequelize.HasManyAddAssociationMixin<enrollments, enrollmentsId>;
addEnrollments!: Sequelize.HasManyAddAssociationsMixin<enrollments, enrollmentsId>;
createEnrollment!: Sequelize.HasManyCreateAssociationMixin<enrollments>;
removeEnrollment!: Sequelize.HasManyRemoveAssociationMixin<enrollments, enrollmentsId>;
removeEnrollments!: Sequelize.HasManyRemoveAssociationsMixin<enrollments, enrollmentsId>;
hasEnrollment!: Sequelize.HasManyHasAssociationMixin<enrollments, enrollmentsId>;
hasEnrollments!: Sequelize.HasManyHasAssociationsMixin<enrollments, enrollmentsId>;
countEnrollments!: Sequelize.HasManyCountAssociationsMixin;
// users hasMany user_auth via user_id
user_auths!: user_auth[];
getUser_auths!: Sequelize.HasManyGetAssociationsMixin<user_auth>;
setUser_auths!: Sequelize.HasManySetAssociationsMixin<user_auth, user_authId>;
addUser_auth!: Sequelize.HasManyAddAssociationMixin<user_auth, user_authId>;
addUser_auths!: Sequelize.HasManyAddAssociationsMixin<user_auth, user_authId>;
createUser_auth!: Sequelize.HasManyCreateAssociationMixin<user_auth>;
removeUser_auth!: Sequelize.HasManyRemoveAssociationMixin<user_auth, user_authId>;
removeUser_auths!: Sequelize.HasManyRemoveAssociationsMixin<user_auth, user_authId>;
hasUser_auth!: Sequelize.HasManyHasAssociationMixin<user_auth, user_authId>;
hasUser_auths!: Sequelize.HasManyHasAssociationsMixin<user_auth, user_authId>;
countUser_auths!: Sequelize.HasManyCountAssociationsMixin;
static initModel(sequelize: Sequelize.Sequelize): typeof users {
return users.init({
id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: false
},
date_of_birth: {
type: DataTypes.DATE,
allowNull: true
},
phone: {
type: DataTypes.TEXT,
allowNull: true
},
email: {
type: DataTypes.STRING(255),
allowNull: true
},
address: {
type: DataTypes.TEXT,
allowNull: true
},
role_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: 'roles',
key: 'id'
}
}
}, {
sequelize,
tableName: 'users',
schema: 'public',
timestamps: true,
indexes: [
{
name: "users_pkey",
unique: true,
fields: [
{ name: "id" },
]
},
]
});
}
}
import { models } from '#scripts/database-gen.js';
export class ClassesProvider {
async getAllClasses() {
try {
const data = await models.classes.findAll({
attributes: ['id', 'name'],
order: [['id', 'DESC']]
});
return data;
} catch (error) {
console.error("Lỗi khi lấy danh sách classes:", error);
throw error;
}
}
async getClassById(classId: number) {
const classData = await models.classes.findByPk(classId);
return classData;
}
}
\ No newline at end of file
import { models } from '#scripts/database-gen.js';
export class CoursesProvider {
async getAllCourses() {
try {
const data = await models.courses.findAll({
attributes: ['id', 'name'],
order: [['id', 'DESC']]
});
return data;
} catch (error) {
console.error("Lỗi khi lấy danh sách courses:", error);
throw error;
}
}
async getCourseById(courseId: number) {
const course = await models.courses.findByPk(courseId);
return course;
}
}
\ No newline at end of file
import { Sequelize } from 'sequelize';
import { initModels } from '#/models/init-models.js';
const sequelize = new Sequelize('challenge_db', 'postgres', '123456', {
host: 'localhost',
port: 2550,
dialect: 'postgres',
logging: false,
});
const models = initModels(sequelize);
export { sequelize, models };
\ No newline at end of file
import swaggerJSDoc from 'swagger-jsdoc';
import fs from 'node:fs';
import path from 'node:path';
// Nạp file cấu hình
import swaggerOptions from '#/templates/swagger/config.js';
try {
console.log('--- Đang bắt đầu quét mã nguồn để tạo tài liệu API ---');
// Tạo OpenAPI specs từ JSDoc comments
const specs = swaggerJSDoc(swaggerOptions);
// Đường dẫn thư mục và file output
const outputDir = path.join(process.cwd(), 'src', 'docs', 'swagger');
const outputFile = path.join(outputDir, 'swagger-output.json');
// Tạo thư mục nếu chưa tồn tại
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
console.log(`- Đã tạo thư mục: ${outputDir}`);
}
// Ghi file JSON
fs.writeFileSync(outputFile, JSON.stringify(specs, null, 2), 'utf-8');
console.log('✅ Thành công!');
console.log(`📍 File đã được lưu tại: ${outputFile}`);
} catch (error) {
console.error('❌ Đã có lỗi xảy ra:');
console.error(error);
process.exit(1);
}
\ No newline at end of file
const classSchemas = {
Class: {
type: 'object',
example: {
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
name: 'Lớp 12A1',
description: 'Lớp chuyên toán',
created_at: '2026-05-16T08:00:00.000Z',
created_by: '2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11',
updated_at: '2026-05-16T09:30:00.000Z',
updated_by: '2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11',
course_id: '7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10',
status: 'active',
},
properties: {
id: {
type: 'string',
format: 'uuid',
example: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
},
name: {
type: 'string',
example: 'Lớp 12A1',
},
description: {
type: 'string',
nullable: true,
example: 'Lớp chuyên toán',
},
created_at: {
type: 'string',
format: 'date-time',
nullable: true,
},
created_by: {
type: 'string',
format: 'uuid',
nullable: true,
example: '2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11',
},
updated_at: {
type: 'string',
format: 'date-time',
nullable: true,
},
updated_by: {
type: 'string',
format: 'uuid',
nullable: true,
example: '2b4f6f8e-19f6-4c5d-93c2-4d7a7c3d1e11',
},
course_id: {
type: 'string',
format: 'uuid',
nullable: true,
example: '7c1a4d9c-3a2b-4c58-9df4-8e2c2d9a3c10',
},
status: {
type: 'string',
nullable: true,
example: 'active',
},
},
},
};
export default classSchemas;
\ No newline at end of file
import path from 'node:path';
import classSchemas from './classes/schemas.js';
import type { Options } from 'swagger-jsdoc';
const swaggerOptions: Options = {
definition: {
openapi: '3.1.0',
info: {
title: 'Backend Challenges API',
version: '1.0.0',
},
components: {
schemas: {
...classSchemas,
},
},
},
apis: [path.join(process.cwd(), 'src/controllers/**/*.ts')],
};
export default swaggerOptions;
\ No newline at end of file
{
"compilerOptions": {
"module": "NodeNext",
"target": "ES2022",
"lib": [
"ES2022"
],
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"incremental": true,
"noEmit": false,
"paths": {
"#dto/*": [
"./src/dto/*"
],
"#providers/*": [
"./src/providers/*"
],
"#services/*": [
"./src/services/*"
],
"#middlewares/*": [
"./src/middlewares/*"
],
"#models/*": [
"./src/models/*"
],
"#interfaces/*": [
"./src/interfaces/*"
],
"#*": [
"./src/*"
]
},
"types": [
"node"
]
},
"include": [
"src/**/*",
"swagger.js"
],
"exclude": [
"node_modules",
"dist",
"storage",
"**/*.test.ts",
"**/*.spec.ts"
]
}
\ 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