diff --git a/UPLOAD_GUIDE.md b/UPLOAD_GUIDE.md new file mode 100644 index 0000000..3f5fcda --- /dev/null +++ b/UPLOAD_GUIDE.md @@ -0,0 +1,126 @@ +# Upload API + +API endpoint để upload các loại file khác nhau. + +## Quy tắc phân loại file + +- **Images** (jpg, jpeg, png, gif, bmp, webp, svg, ico) → `public/images/` +- **Audio** (mp3, wav, ogg, m4a, aac, flac, wma) → `public/media/` +- **Video** (mp4, avi, mov, wmv, flv, mkv, webm) → `public/media/` +- **Các file khác** → `public/files/` + +## API Endpoints + +### 1. Upload single file +``` +POST /api/upload/single +Content-Type: multipart/form-data + +Body: +- file: (binary file) +``` + +**Response:** +```json +{ + "success": true, + "message": "File uploaded successfully", + "data": { + "filename": "example-1234567890.jpg", + "originalname": "example.jpg", + "mimetype": "image/jpeg", + "size": 12345, + "type": "image", + "path": "public/images/example-1234567890.jpg", + "url": "/public/images/example-1234567890.jpg" + } +} +``` + +### 2. Upload multiple files +``` +POST /api/upload/multiple +Content-Type: multipart/form-data + +Body: +- files: (array of binary files, max 10) +``` + +**Response:** +```json +{ + "success": true, + "message": "2 files uploaded successfully", + "data": [ + { + "filename": "photo1-1234567890.jpg", + "originalname": "photo1.jpg", + "mimetype": "image/jpeg", + "size": 12345, + "type": "image", + "path": "public/images/photo1-1234567890.jpg", + "url": "/public/images/photo1-1234567890.jpg" + }, + { + "filename": "audio-1234567891.mp3", + "originalname": "audio.mp3", + "mimetype": "audio/mpeg", + "size": 54321, + "type": "audio", + "path": "public/media/audio-1234567891.mp3", + "url": "/public/media/audio-1234567891.mp3" + } + ] +} +``` + +### 3. Delete file +``` +DELETE /api/upload/file +Content-Type: application/json + +Body: +{ + "filepath": "public/images/example-1234567890.jpg" +} +``` + +### 4. Get file info +``` +GET /api/upload/info?filepath=public/images/example-1234567890.jpg +``` + +## Giới hạn + +- **Max file size:** 50MB +- **Max files per upload (multiple):** 10 files + +## Sử dụng với Postman/cURL + +### Postman: +1. Chọn POST method +2. URL: `http://localhost:10001/api/upload/single` +3. Body → form-data +4. Key: `file`, Type: File +5. Chọn file cần upload + +### cURL: +```bash +# Upload single file +curl -X POST http://localhost:10001/api/upload/single \ + -F "file=@/path/to/your/file.jpg" + +# Upload multiple files +curl -X POST http://localhost:10001/api/upload/multiple \ + -F "files=@/path/to/file1.jpg" \ + -F "files=@/path/to/file2.mp3" +``` + +## Truy cập file đã upload + +Sau khi upload thành công, file có thể truy cập qua URL: +``` +http://localhost:10001/public/images/example-1234567890.jpg +http://localhost:10001/public/media/audio-1234567890.mp3 +http://localhost:10001/public/files/document-1234567890.pdf +``` diff --git a/app.js b/app.js index 858a2f0..21bd561 100644 --- a/app.js +++ b/app.js @@ -39,6 +39,7 @@ const vocabRoutes = require('./routes/vocabRoutes'); const grammarRoutes = require('./routes/grammarRoutes'); const storyRoutes = require('./routes/storyRoutes'); const learningContentRoutes = require('./routes/learningContentRoutes'); +const uploadRoutes = require('./routes/uploadRoutes'); /** * Initialize Express Application @@ -153,6 +154,7 @@ app.get('/api', (req, res) => { games: '/api/games', gameTypes: '/api/game-types', vocab: '/api/vocab', + upload: '/api/upload', }, documentation: '/api-docs', }); @@ -210,6 +212,7 @@ app.use('/api/vocab', vocabRoutes); app.use('/api/grammar', grammarRoutes); app.use('/api/stories', storyRoutes); app.use('/api/learning-content', learningContentRoutes); +app.use('/api/upload', uploadRoutes); /** * Queue Status Endpoint diff --git a/controllers/gameTypeController.js b/controllers/gameTypeController.js index fac98be..14aa43d 100644 --- a/controllers/gameTypeController.js +++ b/controllers/gameTypeController.js @@ -16,6 +16,7 @@ class GameTypeController { const cacheKey = `game_types:list:${page}:${limit}:${is_active || 'all'}:${is_premium || 'all'}:${difficulty_level || 'all'}`; const cached = await cacheUtils.get(cacheKey); + /* if (cached) { return res.json({ success: true, @@ -23,6 +24,7 @@ class GameTypeController { cached: true, }); } + */ const where = {}; if (is_active !== undefined) where.is_active = is_active === 'true'; diff --git a/controllers/teacherController.js b/controllers/teacherController.js index 997a31c..3e11c7b 100644 --- a/controllers/teacherController.js +++ b/controllers/teacherController.js @@ -263,12 +263,6 @@ class TeacherController { next(error); } } - jobId: job.id, - }); - } catch (error) { - next(error); - } - } /** * Get teacher datatypes diff --git a/controllers/uploadController.js b/controllers/uploadController.js new file mode 100644 index 0000000..3c99ad4 --- /dev/null +++ b/controllers/uploadController.js @@ -0,0 +1,276 @@ +const multer = require('multer'); +const path = require('path'); +const fs = require('fs'); + +/** + * Upload Controller - Quản lý upload file + */ +class UploadController { + constructor() { + // Danh sách các extension cho từng loại file + this.imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.ico']; + this.audioExtensions = ['.mp3', '.wav', '.ogg', '.m4a', '.aac', '.flac', '.wma']; + this.videoExtensions = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv', '.webm']; + + // Tạo các thư mục nếu chưa tồn tại + this.ensureDirectories(); + + // Cấu hình multer storage + this.storage = multer.diskStorage({ + destination: (req, file, cb) => { + const fileExt = path.extname(file.originalname).toLowerCase(); + let uploadPath = 'public/files'; + + if (this.imageExtensions.includes(fileExt)) { + uploadPath = 'public/images'; + } else if (this.audioExtensions.includes(fileExt) || this.videoExtensions.includes(fileExt)) { + uploadPath = 'public/media'; + } + + cb(null, uploadPath); + }, + filename: (req, file, cb) => { + // Tạo tên file unique: timestamp-random-originalname + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = path.extname(file.originalname); + const nameWithoutExt = path.basename(file.originalname, ext); + const sanitizedName = nameWithoutExt.replace(/[^a-zA-Z0-9]/g, '_'); + cb(null, `${sanitizedName}-${uniqueSuffix}${ext}`); + } + }); + + // Cấu hình multer với giới hạn kích thước + this.upload = multer({ + storage: this.storage, + limits: { + fileSize: 50 * 1024 * 1024, // 50MB max file size + }, + fileFilter: (req, file, cb) => { + // Cho phép tất cả các loại file + cb(null, true); + } + }); + } + + /** + * Đảm bảo các thư mục upload tồn tại + */ + ensureDirectories() { + const directories = [ + 'public/images', + 'public/media', + 'public/files' + ]; + + directories.forEach(dir => { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + }); + } + + /** + * Upload single file + */ + uploadSingle(req, res, next) { + const uploadHandler = this.upload.single('file'); + + uploadHandler(req, res, (err) => { + if (err instanceof multer.MulterError) { + return res.status(400).json({ + success: false, + message: 'Upload error', + error: err.message, + }); + } else if (err) { + return res.status(500).json({ + success: false, + message: 'Server error during upload', + error: err.message, + }); + } + + if (!req.file) { + return res.status(400).json({ + success: false, + message: 'No file uploaded', + }); + } + + // Xác định loại file + const fileExt = path.extname(req.file.originalname).toLowerCase(); + let fileType = 'file'; + + if (this.imageExtensions.includes(fileExt)) { + fileType = 'image'; + } else if (this.audioExtensions.includes(fileExt)) { + fileType = 'audio'; + } else if (this.videoExtensions.includes(fileExt)) { + fileType = 'video'; + } + + // Tạo URL truy cập file + const fileUrl = `/${req.file.path.replace(/\\/g, '/')}`; + + res.status(201).json({ + success: true, + message: 'File uploaded successfully', + data: { + filename: req.file.filename, + originalname: req.file.originalname, + mimetype: req.file.mimetype, + size: req.file.size, + type: fileType, + path: req.file.path, + url: fileUrl, + }, + }); + }); + } + + /** + * Upload multiple files + */ + uploadMultiple(req, res, next) { + const uploadHandler = this.upload.array('files', 10); // Max 10 files + + uploadHandler(req, res, (err) => { + if (err instanceof multer.MulterError) { + return res.status(400).json({ + success: false, + message: 'Upload error', + error: err.message, + }); + } else if (err) { + return res.status(500).json({ + success: false, + message: 'Server error during upload', + error: err.message, + }); + } + + if (!req.files || req.files.length === 0) { + return res.status(400).json({ + success: false, + message: 'No files uploaded', + }); + } + + const uploadedFiles = req.files.map(file => { + const fileExt = path.extname(file.originalname).toLowerCase(); + let fileType = 'file'; + + if (this.imageExtensions.includes(fileExt)) { + fileType = 'image'; + } else if (this.audioExtensions.includes(fileExt)) { + fileType = 'audio'; + } else if (this.videoExtensions.includes(fileExt)) { + fileType = 'video'; + } + + const fileUrl = `/${file.path.replace(/\\/g, '/')}`; + + return { + filename: file.filename, + originalname: file.originalname, + mimetype: file.mimetype, + size: file.size, + type: fileType, + path: file.path, + url: fileUrl, + }; + }); + + res.status(201).json({ + success: true, + message: `${uploadedFiles.length} files uploaded successfully`, + data: uploadedFiles, + }); + }); + } + + /** + * Delete file + */ + async deleteFile(req, res, next) { + try { + const { filepath } = req.body; + + if (!filepath) { + return res.status(400).json({ + success: false, + message: 'Filepath is required', + }); + } + + // Kiểm tra file có tồn tại không + if (!fs.existsSync(filepath)) { + return res.status(404).json({ + success: false, + message: 'File not found', + }); + } + + // Xóa file + fs.unlinkSync(filepath); + + res.json({ + success: true, + message: 'File deleted successfully', + }); + } catch (error) { + next(error); + } + } + + /** + * Get file info + */ + async getFileInfo(req, res, next) { + try { + const { filepath } = req.query; + + if (!filepath) { + return res.status(400).json({ + success: false, + message: 'Filepath is required', + }); + } + + // Kiểm tra file có tồn tại không + if (!fs.existsSync(filepath)) { + return res.status(404).json({ + success: false, + message: 'File not found', + }); + } + + const stats = fs.statSync(filepath); + const fileExt = path.extname(filepath).toLowerCase(); + let fileType = 'file'; + + if (this.imageExtensions.includes(fileExt)) { + fileType = 'image'; + } else if (this.audioExtensions.includes(fileExt)) { + fileType = 'audio'; + } else if (this.videoExtensions.includes(fileExt)) { + fileType = 'video'; + } + + res.json({ + success: true, + data: { + path: filepath, + size: stats.size, + type: fileType, + created: stats.birthtime, + modified: stats.mtime, + }, + }); + } catch (error) { + next(error); + } + } +} + +module.exports = new UploadController(); diff --git a/models/GameType.js b/models/GameType.js index 0db23e5..7710a90 100644 --- a/models/GameType.js +++ b/models/GameType.js @@ -31,7 +31,7 @@ const GameType = sequelize.define('game_types', { comment: 'Mô tả loại game (tiếng Việt)' }, type: { - type: DataTypes.TEXT, + type: DataTypes.STRING(100), allowNull: false, unique: true, comment: 'Mã định danh loại game: counting_quiz, math_game, word_puzzle, etc.' diff --git a/package-lock.json b/package-lock.json index 95f5a4c..e63906c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "joi": "^17.11.0", "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", + "multer": "^2.0.2", "mysql2": "^3.6.5", "sequelize": "^6.35.2", "swagger-jsdoc": "^6.2.8", @@ -1656,6 +1657,12 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/aproba": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", @@ -2019,7 +2026,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, "node_modules/bullmq": { @@ -2037,6 +2043,17 @@ "uuid": "11.1.0" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2422,6 +2439,21 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -5150,6 +5182,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", @@ -5288,6 +5329,36 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" } }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mysql2": { "version": "3.16.0", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.16.0.tgz", @@ -6494,6 +6565,14 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -6889,6 +6968,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -7157,6 +7242,15 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 1ae738e..e90eee7 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "joi": "^17.11.0", "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", + "multer": "^2.0.2", "mysql2": "^3.6.5", "sequelize": "^6.35.2", "swagger-jsdoc": "^6.2.8", diff --git a/public/files/.gitkeep b/public/files/.gitkeep new file mode 100644 index 0000000..74be194 --- /dev/null +++ b/public/files/.gitkeep @@ -0,0 +1,3 @@ +# Thư mục lưu trữ các file khác +* +!.gitkeep diff --git a/public/images/.gitkeep b/public/images/.gitkeep new file mode 100644 index 0000000..7596c15 --- /dev/null +++ b/public/images/.gitkeep @@ -0,0 +1,10 @@ +# Thư mục lưu trữ hình ảnh +*.jpg +*.jpeg +*.png +*.gif +*.bmp +*.webp +*.svg +*.ico +!.gitkeep diff --git a/public/images/quiz01.png b/public/images/quiz01.png new file mode 100644 index 0000000..8d80eff Binary files /dev/null and b/public/images/quiz01.png differ diff --git a/public/images/quiz02.png b/public/images/quiz02.png new file mode 100644 index 0000000..a00ec0e Binary files /dev/null and b/public/images/quiz02.png differ diff --git a/public/images/quiz03.png b/public/images/quiz03.png new file mode 100644 index 0000000..9023874 Binary files /dev/null and b/public/images/quiz03.png differ diff --git a/public/images/quiz04.png b/public/images/quiz04.png new file mode 100644 index 0000000..8adca5d Binary files /dev/null and b/public/images/quiz04.png differ diff --git a/public/images/quiz05.png b/public/images/quiz05.png new file mode 100644 index 0000000..27b8a63 Binary files /dev/null and b/public/images/quiz05.png differ diff --git a/public/media/.gitkeep b/public/media/.gitkeep new file mode 100644 index 0000000..55c0cc1 --- /dev/null +++ b/public/media/.gitkeep @@ -0,0 +1,16 @@ +# Thư mục lưu trữ media (audio/video) +*.mp3 +*.wav +*.ogg +*.m4a +*.aac +*.flac +*.wma +*.mp4 +*.avi +*.mov +*.wmv +*.flv +*.mkv +*.webm +!.gitkeep diff --git a/routes/uploadRoutes.js b/routes/uploadRoutes.js new file mode 100644 index 0000000..42723ef --- /dev/null +++ b/routes/uploadRoutes.js @@ -0,0 +1,119 @@ +const express = require('express'); +const router = express.Router(); +const uploadController = require('../controllers/uploadController'); + +/** + * @swagger + * tags: + * name: Upload + * description: File upload management + */ + +/** + * @swagger + * /api/upload/single: + * post: + * summary: Upload single file + * tags: [Upload] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * properties: + * file: + * type: string + * format: binary + * description: File to upload (images → public/images, audio/video → public/media, others → public/files) + * responses: + * 201: + * description: File uploaded successfully + * 400: + * description: No file uploaded or upload error + * 500: + * description: Server error + */ +router.post('/single', (req, res, next) => { + uploadController.uploadSingle(req, res, next); +}); + +/** + * @swagger + * /api/upload/multiple: + * post: + * summary: Upload multiple files (max 10) + * tags: [Upload] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * properties: + * files: + * type: array + * items: + * type: string + * format: binary + * description: Files to upload (max 10 files) + * responses: + * 201: + * description: Files uploaded successfully + * 400: + * description: No files uploaded or upload error + * 500: + * description: Server error + */ +router.post('/multiple', (req, res, next) => { + uploadController.uploadMultiple(req, res, next); +}); + +/** + * @swagger + * /api/upload/file: + * delete: + * summary: Delete uploaded file + * tags: [Upload] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - filepath + * properties: + * filepath: + * type: string + * example: "public/images/example-1234567890.jpg" + * responses: + * 200: + * description: File deleted successfully + * 404: + * description: File not found + */ +router.delete('/file', uploadController.deleteFile); + +/** + * @swagger + * /api/upload/info: + * get: + * summary: Get file information + * tags: [Upload] + * parameters: + * - in: query + * name: filepath + * required: true + * schema: + * type: string + * description: Path to the file + * responses: + * 200: + * description: File info retrieved successfully + * 404: + * description: File not found + */ +router.get('/info', uploadController.getFileInfo); + +module.exports = router; diff --git a/server.js b/server.js index 892adc8..8bbb35f 100644 --- a/server.js +++ b/server.js @@ -5,7 +5,7 @@ const { closeQueues } = require('./config/bullmq'); const config = require('./config/config.json'); const PORT = config.server.port || 3000; -const HOST = '0.0.0.0'; +const HOST = 'localhost'; let server; diff --git a/yarn.lock b/yarn.lock index 7eb9179..2181402 100644 --- a/yarn.lock +++ b/yarn.lock @@ -939,6 +939,11 @@ anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + "aproba@^1.0.3 || ^2.0.0": version "2.1.0" resolved "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz" @@ -1156,6 +1161,13 @@ bullmq@^5.1.0: tslib "2.8.1" uuid "11.1.0" +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + bytes@~3.1.2, bytes@3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" @@ -1360,6 +1372,16 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + console-control-strings@^1.0.0, console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" @@ -3029,6 +3051,11 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass@^3.0.0: version "3.3.6" resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" @@ -3049,6 +3076,13 @@ minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" +mkdirp@^0.5.6: + version "0.5.6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" @@ -3108,6 +3142,19 @@ msgpackr@1.11.5: optionalDependencies: msgpackr-extract "^3.0.2" +multer@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz" + integrity sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw== + dependencies: + append-field "^1.0.0" + busboy "^1.6.0" + concat-stream "^2.0.0" + mkdirp "^0.5.6" + object-assign "^4.1.1" + type-is "^1.6.18" + xtend "^4.0.2" + mysql2@^3.6.5: version "3.16.0" resolved "https://registry.npmjs.org/mysql2/-/mysql2-3.16.0.tgz" @@ -3470,7 +3517,7 @@ react-is@^18.0.0: resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -readable-stream@^3.4.0, readable-stream@^3.6.0, readable-stream@^3.6.2: +readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0, readable-stream@^3.6.2: version "3.6.2" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -3776,6 +3823,11 @@ statuses@~2.0.1, statuses@~2.0.2: resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" @@ -3989,7 +4041,7 @@ type-fest@^0.21.3: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-is@~1.6.18: +type-is@^1.6.18, type-is@~1.6.18: version "1.6.18" resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -3997,6 +4049,11 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" @@ -4160,6 +4217,11 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"