diff --git a/.vscode/settings.json b/.vscode/settings.json index 103d981..57b4677 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -44,5 +44,8 @@ "MIDDLAWARE", "Middlawre", "mutilp" - ] + ], + "[dockerfile]": { + "editor.defaultFormatter": "ms-azuretools.vscode-docker" + } } diff --git a/backend-manager-student/docker-compose-dev.yml b/backend-manager-student/docker-compose-dev.yml index 227ba89..0572f2e 100644 --- a/backend-manager-student/docker-compose-dev.yml +++ b/backend-manager-student/docker-compose-dev.yml @@ -2,6 +2,7 @@ #! Description: Docker Compose Dev #!@ Created_At : 20-12-2022. #!@ Update_At: 21-12-2022, 22-12-2022, 16-01-2023, 12-03-2023, 18-03-2023. +#!@ Updated_At: 12-04-2023 version: '3.7' @@ -171,12 +172,43 @@ services: timeout: 3s retries: 3 + ###! mongo + mongodb: + container_name: mongodb + image: mongo:latest + build: + context: . + dockerfile: dockerfile.mongo + restart: unless-stopped + environment: + MONGO_INITDB_ROOT_USERNAME: '${MONGO_INITDB_ROOT_USERNAME}' + MONGO_INITDB_ROOT_PASSWORD: '${MONGO_INITDB_ROOT_PASSWORD}' + MONGO_INITDB_DATABASE: '${MONGO_INITDB_DATABASE}' + volumes: + - mongo_db_data:/data/db + ports: + - ${MONGO_INITDB_PORT}:${MONGO_INITDB_PORT} + env_file: + - .env + networks: + - libary_school + healthcheck: + test: + [ + 'CMD', + 'mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --eval "printjson(db.serverStatus())"', + ] + interval: 10s + timeout: 3s + retries: 3 + ###! WebServer:NGINX ### nginx: image: nginx:1.21.3 container_name: nginx_libary_school depends_on: - postgresql + - mongodb - redis-master - redis-slave - admin_api @@ -206,6 +238,8 @@ networks: volumes: db_data: driver: local + mongo_db_data: + driver: local frontend-manager-student: driver: local redis-master: diff --git a/backend-manager-student/docker-compose.yml b/backend-manager-student/docker-compose.yml index 5a773d4..97ea497 100644 --- a/backend-manager-student/docker-compose.yml +++ b/backend-manager-student/docker-compose.yml @@ -1,6 +1,7 @@ #!@ Author: Nguyễn Tiến Tài. #! Description: Docker Compose Production -#!@ Created_At : 20-12-2022. +#!@ Created_At: 20-12-2022. +#!@ Updated_At: 12-04-2023 #!@ Update_At: 21-12-2022, 22-12-2022, 16-01-2023, 12-03-2023, 18-03-2023. version: '3.7' @@ -161,12 +162,43 @@ services: timeout: 3s retries: 3 + ###! mongo + mongodb: + container_name: mongodb + image: mongo:latest + build: + context: . + dockerfile: dockerfile.mongo + restart: unless-stopped + environment: + MONGO_INITDB_ROOT_USERNAME: '${MONGO_INITDB_ROOT_USERNAME}' + MONGO_INITDB_ROOT_PASSWORD: '${MONGO_INITDB_ROOT_PASSWORD}' + MONGO_INITDB_DATABASE: '${MONGO_INITDB_DATABASE}' + volumes: + - mongo_db_data:/data/db + ports: + - ${MONGO_INITDB_PORT}:${MONGO_INITDB_PORT} + env_file: + - .env + networks: + - libary_school + healthcheck: + test: + [ + 'CMD', + 'mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --eval "printjson(db.serverStatus())"', + ] + interval: 10s + timeout: 3s + retries: 3 + ###! WebServer:NGINX ### nginx: image: nginx:1.21.3 container_name: nginx_libary_school depends_on: - postgresql + - mongodb - redis-master - admin_api - user_api @@ -195,6 +227,8 @@ networks: volumes: db_data: driver: local + mongo_db_data: + driver: local frontend-manager-student: driver: local redis-master: diff --git a/backend-manager-student/dockerfile.mongo b/backend-manager-student/dockerfile.mongo new file mode 100644 index 0000000..34942f7 --- /dev/null +++ b/backend-manager-student/dockerfile.mongo @@ -0,0 +1,13 @@ +FROM mongo + +# copy javascript +COPY /mongo/init-mongo.js /docker-entrypoint-initdb.d/ + +# Public port 0 0 0 0 +CMD ["mongod", "--bind_ip_all"] + + + + + + diff --git a/backend-manager-student/dockerfile.redis.dev b/backend-manager-student/dockerfile.redis.dev index 053cfbb..5edad7f 100644 --- a/backend-manager-student/dockerfile.redis.dev +++ b/backend-manager-student/dockerfile.redis.dev @@ -9,14 +9,14 @@ FROM redis AS redis-master # Create foder RUN \ if [ ! -d /usr/local/bin/scrip ]; then \ - mkdir -p /usr/local/bin/scrip;\ + mkdir -p /usr/local/bin/scrip;\ fi # Check file Grant permission file RUN \ if [ ! -f /usr/local/bin/scrip/log_script_redis_date.txt ]; then \ - touch /usr/local/bin/scrip/log_script_redis_date.txt && \ - chmod 600 /usr/local/bin/scrip/log_script_redis_date.txt;\ + touch /usr/local/bin/scrip/log_script_redis_date.txt && \ + chmod 600 /usr/local/bin/scrip/log_script_redis_date.txt;\ fi # Copy code script @@ -25,7 +25,7 @@ COPY scrip /usr/local/bin/scrip/ # Check logrotate exit and Install logrotate RUN \ if [ ! -x "$(which logrotate)" ]; then \ - apt-get update && apt-get install logrotate -y && \ - chmod +x /usr/local/bin/scrip/scrip_redis.sh &&\ - chmod 755 /usr/local/bin/scrip \ + apt-get update && apt-get install logrotate -y && \ + chmod +x /usr/local/bin/scrip/scrip_redis.sh &&\ + chmod 755 /usr/local/bin/scrip \ ; fi \ No newline at end of file diff --git a/backend-manager-student/mongo/init-mongo.js b/backend-manager-student/mongo/init-mongo.js new file mode 100644 index 0000000..86873e1 --- /dev/null +++ b/backend-manager-student/mongo/init-mongo.js @@ -0,0 +1,9 @@ +const db = db.getSiblingDB('library_school_service'); +const user = db.getUser('docker'); +if (!db && !user) { + db.createUser({ + user: 'docker', + pwd: 'docker', + roles: [{ role: 'readWrite', db: 'library_school_service' }], + }); +} diff --git a/backend-manager-student/package.json b/backend-manager-student/package.json index aac718a..f0ec675 100644 --- a/backend-manager-student/package.json +++ b/backend-manager-student/package.json @@ -56,13 +56,11 @@ "express-rate-limit": "^6.7.0", "express-session": "^1.17.3", "geoip-lite": "^1.4.6", - "google-auth-library": "^8.7.0", "helmet": "^6.0.1", "ioredis": "^5.2.4", "jsonwebtoken": "^8.5.1", "knex": "^2.3.0", "node-cron": "^3.0.2", - "node-fetch": "^3.3.0", "nodemailer": "^6.8.0", "nodemailer-express-handlebars": "^6.0.0", "sonyflake": "^1.1.2", @@ -76,7 +74,8 @@ "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^4.6.0", "ua-parser-js": "^1.0.33", - "crypto": "^1.0.1" + "crypto": "^1.0.1", + "mongoose": "^7.0.3" }, "devDependencies": { "jest": "^29.3.1" diff --git a/backend-manager-student/src/share/configs/config.js b/backend-manager-student/src/share/configs/config.js index 9c7ce84..66ef5e5 100644 --- a/backend-manager-student/src/share/configs/config.js +++ b/backend-manager-student/src/share/configs/config.js @@ -63,13 +63,6 @@ module.exports = { POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD, POSTGRES_DB: process.env.POSTGRES_DB, POSTGRES_PORT: process.env.POSTGRES_PORT, - /** - * @author Nguyễn Tiến Tài - * @created_at 22/01/2023 - * @description MAX MIN CONNECT DB - */ - POSTGRES_CONNECT_MIN: process.env.POSTGRES_CONNECT_MIN, - POSTGRES_CONNECT_MAX: process.env.POSTGRES_CONNECT_MAX, /** * @author Nguyễn Tiến Tài * @created_at 22/01/2023 @@ -140,4 +133,15 @@ module.exports = { */ IPA_API_RATE_LIMIT_DURATION: process.env.IPA_API_RATE_LIMIT_DURATION, IPA_API_RATE_LIMIT: process.env.IPA_API_RATE_LIMIT, + /** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Configs Mongo + */ + MONGO_INIT_DB_ROOT_USERNAME: process.env.MONGO_INITDB_ROOT_USERNAME, + MONGO_INI_TDB_ROOT_PASSWORD: process.env.MONGO_INITDB_ROOT_PASSWORD, + MONGO_INIT_DB_DATABASE: process.env.MONGO_INITDB_DATABASE, + MONGO_INIT_MONGO_PORT: process.env.MONGO_INITDB_PORT, + MONGO_INIT_MONGO_HOST: process.env.MONGO_INITDB_HOST, + MONGO_INIT_DB_SOURCE: process.env.MONGO_INITDB_SOURCE, }; diff --git a/backend-manager-student/src/share/configs/configs.mongodb.js b/backend-manager-student/src/share/configs/configs.mongodb.js new file mode 100644 index 0000000..8fdf0b6 --- /dev/null +++ b/backend-manager-student/src/share/configs/configs.mongodb.js @@ -0,0 +1,27 @@ +//! CONFIG +const CONFIGS = require('./config'); + +const DEVELOPER = { + db: { + host: CONFIGS.MONGO_INIT_MONGO_HOST, + port: CONFIGS.MONGO_INIT_MONGO_PORT, + db: CONFIGS.MONGO_INIT_DB_DATABASE, + user: CONFIGS.MONGO_INIT_DB_ROOT_USERNAME, + password: CONFIGS.MONGO_INI_TDB_ROOT_PASSWORD, + source: CONFIGS.MONGO_INIT_DB_SOURCE, + }, +}; +const PRODUCTION = { + db: { + host: CONFIGS.MONGO_INIT_MONGO_HOST, + port: CONFIGS.MONGO_INIT_MONGO_PORT, + db: CONFIGS.MONGO_INIT_DB_DATABASE, + user: CONFIGS.MONGO_INIT_DB_ROOT_USERNAME, + password: CONFIGS.MONGO_INI_TDB_ROOT_PASSWORD, + source: CONFIGS.MONGO_INIT_DB_SOURCE, + }, +}; +const config = { DEVELOPER, PRODUCTION }; +const env = process.env.NODE_ENV || 'DEVELOPER'; + +module.exports = config[env]; diff --git a/backend-manager-student/src/share/configs/constants.js b/backend-manager-student/src/share/configs/constants.js index cd41946..2515b63 100644 --- a/backend-manager-student/src/share/configs/constants.js +++ b/backend-manager-student/src/share/configs/constants.js @@ -480,6 +480,7 @@ module.exports = { REDIS_SERVER_ADMIN: 'admin_unErrorServer', REDIS_SERVER_STUDENT: 'user_unErrorServer', REDIS_SERVER_CRON: 'cron_unErrorServer', + REDIS_DB: 'db_fail', }, /** * @author Nguyễn Tiến Tài @@ -498,5 +499,33 @@ module.exports = { ADMIN: 'admin', STUDENT: 'student', CRON: 'cron', + DB: 'database', + }, + /** + * @author Nguyễn Tiến Tài + * @created_at 10/04/2023 + * @description NAME_DATABASE + */ + NAME_DATABASE: { + PG: 'PostgreSQL', + MONGO: 'MongoDB', + REDIS: 'REDIS', + }, + /** + * @author Nguyễn Tiến Tài + * @created_at 10/04/2023 + * @description Setup Mongo + */ + MONGO: { + POOL_SIZE: 50, + STRING_CONNECT: 'mongodb://${user}:${password}@${host}:${port}/${db}?authSource=${source}', + }, + PG: { + POSTGRES_CONNECT_MIN: 2, + POSTGRES_CONNECT_MAX: 10, + ACQUIRE_TIMEOUT_MILLIS: 30000, + CREATE_TIMEOUT_MILLIS: 30000, + IDLE_TIMEOUT_MILLIS: 30000, + REAP_INTERVAL_MILLIS: 1000, }, }; diff --git a/backend-manager-student/src/share/db/init.mongodb.js b/backend-manager-student/src/share/db/init.mongodb.js new file mode 100644 index 0000000..0144d7e --- /dev/null +++ b/backend-manager-student/src/share/db/init.mongodb.js @@ -0,0 +1,67 @@ +//! LIBRARY +const mongoose = require('mongoose'); + +//! CONFIGS +const CONSTANTS = require('../configs/constants'); +const HELPERS = require('../utils/helper'); +const { db: { host, port, db, user, password, source } } = require('../configs/configs.mongodb'); + +//! PUBSUB +const { handleException } = require('../utils/redis_pub_sub_helper'); + +/** + * @author Nguyễn Tiến Tài + * @created_at 10/04/2023 + * @description setup nosql MongoDb + */ + +const connectString = HELPERS.getURIFromTemplate(CONSTANTS.MONGO.STRING_CONNECT, { + user, + password, + host, + port, + db, + source, +}); + +// Singleton +class Database { + constructor() { + this.connect(); + } + + // Connect + // eslint-disable-next-line class-methods-use-this + connect() { + //! DEVELOPERS + // eslint-disable-next-line no-constant-condition, no-self-compare + if (1 === 1) { + mongoose.set('debug', CONSTANTS.DELETED_ENABLE); + mongoose.set('debug', { color: CONSTANTS.DELETED_ENABLE }); + } + + mongoose.connect(connectString, { + maxPoolSize: CONSTANTS.MONGO.POOL_SIZE, + useNewUrlParser: CONSTANTS.DELETED_ENABLE, + useUnifiedTopology: CONSTANTS.DELETED_ENABLE, + // eslint-disable-next-line no-unused-vars + }).then((_) => { + console.info('CONNECTED TO MONGODB SUCCESS !!'); + }).catch((err) => { + handleException(err, CONSTANTS.NAME_SERVER.DB, CONSTANTS.NAME_DATABASE.MONGO); + console.error(err); + }); + } + + static getInstance() { + if (!Database.instance) { + Database.instance = new Database(); + } + + return Database.instance; + } +} + +const instanceMongoDb = Database.getInstance(); + +module.exports = instanceMongoDb; diff --git a/backend-manager-student/src/share/db/init_multiple_redis.js b/backend-manager-student/src/share/db/init_multiple_redis.js index 5fa7f57..6ec0b82 100644 --- a/backend-manager-student/src/share/db/init_multiple_redis.js +++ b/backend-manager-student/src/share/db/init_multiple_redis.js @@ -11,16 +11,16 @@ const CONFIGS = require('../configs/config'); * @description Connect Cache Redis Master and Slave */ const ConnectionRedis = (REDIS) => { - REDIS.on('connect', function () { + REDIS.on('connect', function() { console.info(`Client connected to redis Push ${JSON.stringify(this.options.user)}`); }); - REDIS.on('ready', function () { + REDIS.on('ready', function() { console.info(`Client connected to redis push and ready to use ${JSON.stringify(this.options.host)}...`); }); REDIS.on('error', (error) => { console.info(error); }); - REDIS.on('end', function () { + REDIS.on('end', function() { console.info(`Client disconnected from redis push ${JSON.stringify(this.options.user)}`); }); REDIS.on('SIGINT', () => { diff --git a/backend-manager-student/src/share/db/postgresql.js b/backend-manager-student/src/share/db/postgresql.js index 15abdbf..1fecd4e 100644 --- a/backend-manager-student/src/share/db/postgresql.js +++ b/backend-manager-student/src/share/db/postgresql.js @@ -1,8 +1,14 @@ +//! CONFIGS const CONFIGS = require('../configs/config'); const CONSTANTS = require('../configs/constants'); + +//! PUBSUB +const { handleException } = require('../utils/redis_pub_sub_helper'); + /** * @author Nguyễn Tiến Tài * @created_at 22/01/2023 + * @updated_at 10/04/2023 * @description Connect DB Postgresql */ const KNEX = require('knex')({ @@ -14,8 +20,22 @@ const KNEX = require('knex')({ database: CONFIGS.POSTGRES_DB, port: CONFIGS.POSTGRES_PORT, }, - pool: { min: Number(CONFIGS.POSTGRES_CONNECT_MIN), max: Number(CONFIGS.POSTGRES_CONNECT_MAX) }, + pool: { + min: Number(CONSTANTS.PG.POSTGRES_CONNECT_MIN), + max: Number(CONSTANTS.PG.POSTGRES_CONNECT_MAX), + acquireTimeoutMillis: Number(CONSTANTS.PG.ACQUIRE_TIMEOUT_MILLIS), + createTimeoutMillis: Number(CONSTANTS.PG.CREATE_TIMEOUT_MILLIS), + idleTimeoutMillis: Number(CONSTANTS.PG.IDLE_TIMEOUT_MILLIS), + reapIntervalMillis: Number(CONSTANTS.PG.REAP_INTERVAL_MILLIS), + }, debug: CONFIGS.NODE_ENV !== CONSTANTS.ENVIRONMENT_PRODUCT, }); +KNEX.raw('SELECT 1') + .then(() => console.info('CONNECTED TO POSTGRESQL SUCCESS !!')) + .catch((err) => { + console.error('Failed to connect to PostgreSQL database', err); + return handleException(err, CONSTANTS.NAME_SERVER.DB, CONSTANTS.NAME_DATABASE.PG); + }); + module.exports = KNEX; diff --git a/backend-manager-student/src/share/models/comment.model.js b/backend-manager-student/src/share/models/comment.model.js new file mode 100644 index 0000000..32b46a0 --- /dev/null +++ b/backend-manager-student/src/share/models/comment.model.js @@ -0,0 +1,42 @@ +//! DATABASE +const knex = require('../db/postgresql'); + +module.exports = { + /** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Comment Create + */ + createComment: (data) => + new Promise((resolve, reject) => { + try { + const result = knex('book_comments ') + .insert(data) + .onConflict('comment_id') + .merge() + .returning(['comment_id']); + resolve(result); + } catch (error) { + reject(error); + } + }), + + /** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description get Comment id + */ + getCommentById: async (student_query, return_data) => { + const result = await knex('book_comments').select(return_data).where(student_query); + return result; + }, + /** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Get all comment + */ + getAllComment: async (student_query, return_data) => { + const result = await knex('book_comments').select(return_data).where(student_query).orderBy('updated_at', 'desc'); + return result; + }, +}; diff --git a/backend-manager-student/src/share/models/favorite.model.js b/backend-manager-student/src/share/models/favorite.model.js index 62c8374..0173b9a 100644 --- a/backend-manager-student/src/share/models/favorite.model.js +++ b/backend-manager-student/src/share/models/favorite.model.js @@ -41,9 +41,19 @@ module.exports = { */ getAllFavorite: async (student_query, return_data) => { const result = await knex('favorite_book') - .select(return_data) + .join('books', 'books.book_id', '=', 'favorite_book.book_id') + .select( + { + name_book: 'books.name', + image_book: 'books.image_uri', + description_book: 'books.description', + quantity_book: 'books.quantity', + status_book: 'books.status', + }, + return_data, + ) .where(student_query) - .orderBy('updated_at', 'desc'); + .orderBy('favorite_book.updated_at', 'desc'); return result; }, /** diff --git a/backend-manager-student/src/share/utils/redis_pub_sub_helper.js b/backend-manager-student/src/share/utils/redis_pub_sub_helper.js index d5955e0..c16693e 100644 --- a/backend-manager-student/src/share/utils/redis_pub_sub_helper.js +++ b/backend-manager-student/src/share/utils/redis_pub_sub_helper.js @@ -72,8 +72,26 @@ const handleException = (err, name, port) => { errorName: err.name, errorMessage: err.message, }); + let message_queue; + switch (name) { + case CONSTANTS.NAME_SERVER.STUDENT: + message_queue = CONSTANTS.QUEUE.REDIS_SERVER_STUDENT; + break; + case CONSTANTS.NAME_SERVER.ADMIN: + message_queue = CONSTANTS.QUEUE.REDIS_SERVER_ADMIN; + break; + case CONSTANTS.NAME_SERVER.CRON: + message_queue = CONSTANTS.QUEUE.REDIS_SERVER_CRON; + break; + case CONSTANTS.NAME_SERVER.DB: + message_queue = CONSTANTS.QUEUE.REDIS_DB; + break; + default: + message_queue = CONSTANTS.QUEUE.REDIS_SERVER_CRON; + break; + } // Publish data queue Redis - return queueMessageTelegram(CONSTANTS.QUEUE.REDIS_SERVER_CRON, { + return queueMessageTelegram(message_queue, { message, }); }; diff --git a/backend-manager-student/src/user_api/app.js b/backend-manager-student/src/user_api/app.js index 7479a5a..45a68ff 100644 --- a/backend-manager-student/src/user_api/app.js +++ b/backend-manager-student/src/user_api/app.js @@ -25,6 +25,9 @@ const DEVICE_MIDDLEWARE = require('../share/middleware/device.middleware'); //! IMPORT ROUTES const USER_API = require('./v1/routes/index.router'); +//! INIT DB +require('../share/db/init.mongodb'); + //! USED LIBRARY const app = express(); if (CONFIGS.NODE_ENV === CONSTANTS.ENVIRONMENT_PRODUCT) { diff --git a/backend-manager-student/src/user_api/v1/controllers/comment.controllers/comment.controller.js b/backend-manager-student/src/user_api/v1/controllers/comment.controllers/comment.controller.js new file mode 100644 index 0000000..631e61d --- /dev/null +++ b/backend-manager-student/src/user_api/v1/controllers/comment.controllers/comment.controller.js @@ -0,0 +1,74 @@ +//! SHARE +const CONSTANTS = require('../../../../share/configs/constants'); +const MESSAGES = require('../../../../share/configs/message'); +const HELPER = require('../../../../share/utils/helper'); + +//! MIDDLEWARE +const { returnReasons } = require('../../../../share/middleware/handle_error'); + +//! MODEL +const comment_model = require('../../../../share/models/comment.model'); + +const commentController = { + /** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description List Comment + * @function createComment + */ + listComment: async (req, res) => { + const { book_id, slug } = req.body.input.comment_input; + + // Check input + if (!HELPER.validateBigInt(book_id)) { + return res.status(CONSTANTS.HTTP.STATUS_4XX_BAD_REQUEST).json({ + status: CONSTANTS.HTTP.STATUS_4XX_BAD_REQUEST, + message: returnReasons(CONSTANTS.HTTP.STATUS_4XX_BAD_REQUEST), + element: { + result: MESSAGES.GENERAL.INVALID_INPUT, + }, + }); + } + try { + const match = { + book_id, + isdeleted: CONSTANTS.DELETED_DISABLE, + }; + if (slug !== '') { + // eslint-disable-next-line dot-notation + match['full_slug'] = new RegExp(slug, 'i'); + } + // create comment database + let err; + let result; + [err, result] = await HELPER.handleRequest( + comment_model.getAllComment(match, '*'), + ); + if (result) { + return res.status(CONSTANTS.HTTP.STATUS_2XX_OK).json({ + status: CONSTANTS.HTTP.STATUS_2XX_OK, + message: returnReasons(CONSTANTS.HTTP.STATUS_2XX_OK), + element: { + result, + }, + }); + } + if (err) { + return res.status(CONSTANTS.HTTP.STATUS_5XX_INTERNAL_SERVER_ERROR).json({ + status: CONSTANTS.HTTP.STATUS_5XX_INTERNAL_SERVER_ERROR, + message: returnReasons(CONSTANTS.HTTP.STATUS_5XX_INTERNAL_SERVER_ERROR), + }); + } + } catch (error) { + console.error(error); + return res.status(CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE).json({ + status: CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE, + message: returnReasons(CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE), + element: { + result: MESSAGES.GENERAL.SERVER_OUT_OF_SERVICE, + }, + }); + } + }, +}; +module.exports = commentController; diff --git a/backend-manager-student/src/user_api/v1/controllers/comment.controllers/comment.private.route.js b/backend-manager-student/src/user_api/v1/controllers/comment.controllers/comment.private.route.js new file mode 100644 index 0000000..ed7bd3a --- /dev/null +++ b/backend-manager-student/src/user_api/v1/controllers/comment.controllers/comment.private.route.js @@ -0,0 +1,106 @@ +//! SHARE +const CONSTANTS = require('../../../../share/configs/constants'); +const MESSAGES = require('../../../../share/configs/message'); +const HELPER = require('../../../../share/utils/helper'); +const RANDOMS = require('../../../../share/utils/random'); + +//! MIDDLEWARE +const { returnReasons } = require('../../../../share/middleware/handle_error'); + +//! MODEL +const comment_model = require('../../../../share/models/comment.model'); + +const commentController = { + /** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description create Comment + * @function createComment + */ + createComment: async (req, res) => { + const { book_id, parent_slug, content } = req.body.input.comment_input; + + // Check input + if (!HELPER.validateBigInt(book_id) || !content) { + return res.status(CONSTANTS.HTTP.STATUS_4XX_BAD_REQUEST).json({ + status: CONSTANTS.HTTP.STATUS_4XX_BAD_REQUEST, + message: returnReasons(CONSTANTS.HTTP.STATUS_4XX_BAD_REQUEST), + element: { + result: MESSAGES.GENERAL.INVALID_INPUT, + }, + }); + } + // Take user Id + const { id } = req.auth_user; + + try { + let slug = RANDOMS.createID(); + const posted = new Date(); + + let full_slug = `${posted.toISOString()}:${slug}`; + + let new_slug; + let search_paren_slug = { + book_id, + isdeleted: CONSTANTS.DELETED_DISABLE, + slug: parent_slug, + }; + const parenSlug = await comment_model.getCommentById( + search_paren_slug, + { + full_slug: 'full_slug', + slug: 'slug', + comment_replies_num: 'comment_replies_num', + }, + ); + let total_replies = 0; + if (parenSlug[0]) { + // comment child + full_slug = `${parenSlug[0].full_slug}/${full_slug}`; + new_slug = `${parenSlug[0].slug}/${slug}`; + total_replies = parenSlug[0].comment_replies_num + 1; + } + + // create comment database + let err; + let result; + [err, result] = await HELPER.handleRequest( + comment_model.createComment({ + comment_id: RANDOMS.createID(), + book_id, + user_id: id, + slug: new_slug ? new_slug : slug, + parent_slug, + comment_replies_num: total_replies, + full_slug, + content, + }), + ); + if (result) { + return res.status(CONSTANTS.HTTP.STATUS_2XX_OK).json({ + status: CONSTANTS.HTTP.STATUS_2XX_OK, + message: returnReasons(CONSTANTS.HTTP.STATUS_2XX_OK), + element: { + result: result[0].comment_id, + }, + }); + } + if (err) { + return res.status(CONSTANTS.HTTP.STATUS_5XX_INTERNAL_SERVER_ERROR).json({ + status: CONSTANTS.HTTP.STATUS_5XX_INTERNAL_SERVER_ERROR, + message: returnReasons(CONSTANTS.HTTP.STATUS_5XX_INTERNAL_SERVER_ERROR), + }); + } + } catch (error) { + console.error(error); + return res.status(CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE).json({ + status: CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE, + message: returnReasons(CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE), + element: { + result: MESSAGES.GENERAL.SERVER_OUT_OF_SERVICE, + }, + }); + } + }, +}; +module.exports = commentController; diff --git a/backend-manager-student/src/user_api/v1/controllers/favorite.controllers/favorite.controller.js b/backend-manager-student/src/user_api/v1/controllers/favorite.controllers/favorite.controller.js index 3a3da3c..93e384e 100644 --- a/backend-manager-student/src/user_api/v1/controllers/favorite.controllers/favorite.controller.js +++ b/backend-manager-student/src/user_api/v1/controllers/favorite.controllers/favorite.controller.js @@ -170,5 +170,45 @@ const FavoriteController = { }); } }, + /** + * @author Nguyễn Tiến Tài + * @created_at 04/04/2023 + * @description get all Favorite + * @function createFavorites + * @return {Object:{Number,String}} + */ + getallFavorites: async (req, res) => { + try { + // Take user Id + const { id } = req.auth_user; + + // Take data db + const result_favorite = await favorite_model.getAllFavorite( + { + 'favorite_book.user_id': id, + 'favorite_book.isdeleted': CONSTANTS.DELETED_DISABLE, + }, + 'favorite_book.*', + ); + if (result_favorite) { + return res.status(CONSTANTS.HTTP.STATUS_2XX_OK).json({ + status: CONSTANTS.HTTP.STATUS_2XX_OK, + message: returnReasons(CONSTANTS.HTTP.STATUS_2XX_OK), + element: { + result: result_favorite, + }, + }); + } + } catch (error) { + console.error(error); + return res.status(CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE).json({ + status: CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE, + message: returnReasons(CONSTANTS.HTTP.STATUS_5XX_SERVICE_UNAVAILABLE), + element: { + result: MESSAGES.GENERAL.SERVER_OUT_OF_SERVICE, + }, + }); + } + }, }; module.exports = FavoriteController; diff --git a/backend-manager-student/src/user_api/v1/routes/comment.routes/comment.private.route.js b/backend-manager-student/src/user_api/v1/routes/comment.routes/comment.private.route.js new file mode 100644 index 0000000..0555687 --- /dev/null +++ b/backend-manager-student/src/user_api/v1/routes/comment.routes/comment.private.route.js @@ -0,0 +1,15 @@ +//! LIBRARY +const router = require('express').Router(); + +//! CONTROLLER COMMENT +const commentController = require('../../controllers/comment.controllers/comment.private.route'); + +/** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Route Create Comment book + * @param {('POST')} [method='POST'] The request's method + */ +router.post('/create', commentController.createComment); + +module.exports = router; diff --git a/backend-manager-student/src/user_api/v1/routes/comment.routes/comment.route.js b/backend-manager-student/src/user_api/v1/routes/comment.routes/comment.route.js new file mode 100644 index 0000000..e24ad51 --- /dev/null +++ b/backend-manager-student/src/user_api/v1/routes/comment.routes/comment.route.js @@ -0,0 +1,15 @@ +//! LIBRARY +const router = require('express').Router(); + +//! CONTROLLER COMMENT +const commentController = require('../../controllers/comment.controllers/comment.controller'); + +/** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Route Take Comment book + * @param {('POST')} [method='POST'] The request's method + */ +router.post('/list', commentController.listComment); + +module.exports = router; diff --git a/backend-manager-student/src/user_api/v1/routes/favorite.routes/favorite.private.route.js b/backend-manager-student/src/user_api/v1/routes/favorite.routes/favorite.private.route.js index a426f93..d24df2d 100644 --- a/backend-manager-student/src/user_api/v1/routes/favorite.routes/favorite.private.route.js +++ b/backend-manager-student/src/user_api/v1/routes/favorite.routes/favorite.private.route.js @@ -20,4 +20,12 @@ router.post('/create', favoriteBookController.createFavorites); */ router.post('/delete', favoriteBookController.deleteFavorites); +/** + * @author Nguyễn Tiến Tài + * @created_at 12/04/2023 + * @description Route Get All Favorite + * @param {('GET')} [method='GET'] The request's method + */ +router.get('/get/all', favoriteBookController.getallFavorites); + module.exports = router; diff --git a/backend-manager-student/src/user_api/v1/routes/general.routes.js b/backend-manager-student/src/user_api/v1/routes/general.routes.js index 58f4f85..77d8e6e 100644 --- a/backend-manager-student/src/user_api/v1/routes/general.routes.js +++ b/backend-manager-student/src/user_api/v1/routes/general.routes.js @@ -6,6 +6,7 @@ const userRouter = require('./users.routes/user.route'); const bookRouter = require('./books.routes/book.route'); const authorRouter = require('./author.routes/author.route'); const categoriesRouter = require('./categories.routes/categories.route'); +const commentsRouter = require('./comment.routes/comment.route'); /** * @author Nguyễn Tiến Tài @@ -35,4 +36,11 @@ router_general.use('/author', authorRouter); */ router_general.use('/categories', categoriesRouter); +/** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Route comment + */ +router_general.use('/comment', commentsRouter); + module.exports = router_general; diff --git a/backend-manager-student/src/user_api/v1/routes/private.routes.js b/backend-manager-student/src/user_api/v1/routes/private.routes.js index ab16c51..a25cb2b 100644 --- a/backend-manager-student/src/user_api/v1/routes/private.routes.js +++ b/backend-manager-student/src/user_api/v1/routes/private.routes.js @@ -6,6 +6,7 @@ const userPrivateRouter = require('./users.routes/user.private.route'); const borrowBookPrivateRouter = require('./borrow_book.routes/borrow_book_private.route'); const ratingBookPrivateRouter = require('./rating.routes/rating.private.route'); const favoritePrivateRouter = require('./favorite.routes/favorite.private.route'); +const commentPrivateRouter = require('./comment.routes/comment.private.route'); /** * @author Nguyễn Tiến Tài @@ -35,4 +36,11 @@ router_private.use('/rating', ratingBookPrivateRouter); */ router_private.use('/favorite', favoritePrivateRouter); +/** + * @author Nguyễn Tiến Tài + * @created_at 09/04/2023 + * @description Route Comment + */ +router_private.use('/comment', commentPrivateRouter); + module.exports = router_private; diff --git a/server-send-email-student/src/send_email_user/v1/redis_sub_queue.js b/server-send-email-student/src/send_email_user/v1/redis_sub_queue.js index 29672f0..c1bb62e 100644 --- a/server-send-email-student/src/send_email_user/v1/redis_sub_queue.js +++ b/server-send-email-student/src/send_email_user/v1/redis_sub_queue.js @@ -50,6 +50,12 @@ cronChannelHandlers.set( CONSTANTS.KEY_SERVER.REDIS_SERVER_CRON, telegram_sender_message.handleException, ); +//! New Map DB +const dbChannelHandlers = new Map(); +cronChannelHandlers.set( + CONSTANTS.KEY_SERVER.REDIS_DB, telegram_sender_message.handleException, +); + //! Start Subscribe to user and admin channels REDIS_MASTER.on('ready', async () => { try { @@ -57,8 +63,9 @@ REDIS_MASTER.on('ready', async () => { REDIS_MASTER.psubscribe(`${CONSTANTS.KEY_ADMIN_EXIT_A}`); REDIS_MASTER.psubscribe(`${CONSTANTS.KEY_MEDIA_EXIT_M}`); REDIS_MASTER.psubscribe(`${CONSTANTS.KEY_CRON_EXIT_C}`); + REDIS_MASTER.psubscribe(`${CONSTANTS.KEY_CRON_EXIT_DB}`); - console.info(`Redis subscribed to all channels starting with ${CONSTANTS.KEY_USER_EXIT_U} or ${CONSTANTS.KEY_ADMIN_EXIT_A} or ${CONSTANTS.KEY_MEDIA_EXIT_M} or ${CONSTANTS.KEY_CRON_EXIT_C} `); + console.info(`Redis subscribed to all channels starting with ${CONSTANTS.KEY_USER_EXIT_U} or ${CONSTANTS.KEY_ADMIN_EXIT_A} or ${CONSTANTS.KEY_MEDIA_EXIT_M} or ${CONSTANTS.KEY_CRON_EXIT_C} or ${CONSTANTS.KEY_CRON_EXIT_DB} `); } catch (error) { console.error('Failed to subscribe to Redis channels:', error); } @@ -70,7 +77,8 @@ REDIS_MASTER.on('pmessage', async (pattern, channel, message) => { const handler = userChannelHandlers.get(channel) || adminChannelHandlers.get(channel) || mediaChannelHandlers.get(channel) - || cronChannelHandlers.get(channel); + || cronChannelHandlers.get(channel) + || dbChannelHandlers.get(channel); if (handler) { try { handler(JSON.parse(message)); diff --git a/server-send-email-student/src/send_email_user/v1/telegram/services/send_message_telegram.js b/server-send-email-student/src/send_email_user/v1/telegram/services/send_message_telegram.js index c287de0..d35a027 100644 --- a/server-send-email-student/src/send_email_user/v1/telegram/services/send_message_telegram.js +++ b/server-send-email-student/src/send_email_user/v1/telegram/services/send_message_telegram.js @@ -8,6 +8,7 @@ const { sendTelegram } = require('../../../../share/utils/telegram'); * @return { Object } */ const sendMessageTelegram = { + // eslint-disable-next-line consistent-return handleException: (message) => { try { return sendTelegram(message); diff --git a/server-send-email-student/src/share/configs/constants.js b/server-send-email-student/src/share/configs/constants.js index 8bb3844..568e3b8 100644 --- a/server-send-email-student/src/share/configs/constants.js +++ b/server-send-email-student/src/share/configs/constants.js @@ -115,12 +115,16 @@ module.exports = { // CRON KEY_CRON_EXIT_C: 'c*', + // DB + KEY_CRON_EXIT_DB: 'db*', + // SERVER ALL KEY_SERVER: { REDIS_SERVER_ADMIN: 'admin_unErrorServer', REDIS_SERVER_STUDENT: 'user_unErrorServer', REDIS_SERVER_MEDIA: 'media_unErrorServer', REDIS_SERVER_CRON: 'cron_unErrorServer', + REDIS_DB: 'db_fail', }, /** * @author Nguyễn Tiến Tài