From 86c2a35a31052b5c4903f37d6613fa66bacb5826 Mon Sep 17 00:00:00 2001 From: Nikiroy78 <35032449+Nikiroy78@users.noreply.github.com> Date: Fri, 6 Oct 2023 04:41:18 +0300 Subject: [PATCH] Add backend fixes --- api/v1/index.js | 145 ++++++++++++++++--------- api/v1/methods/create-item.js | 142 +++++++++++------------- api/v1/methods/edit-item.js | 158 ++++++++++++++------------- api/v1/methods/get-authors.js | 168 +++++++++++++++++------------ api/v1/methods/get-music.js | 127 ++++++++++++++++++++++ api/v1/methods/get-muzic.js | 142 ------------------------ api/v1/methods/index.js | 29 ++++- api/v1/methods/remove-item.js | 122 ++++++++++----------- api/v1/response-wrapper.js | 61 ----------- api/v1/typeChecker.js | 180 +++++++++++++++++++++++++++++-- db-dump/install-it.sql | Bin 4270 -> 4270 bytes frontend/main.html | 4 +- frontend/public/js/api-module.js | 16 +-- frontend/public/js/main.js | 102 +++++++++--------- frontend/public/js/menu.js | 90 ++++++++-------- frontend/public/js/sound.js | 24 ++--- logger.js | 22 ++-- server.js | 1 + 18 files changed, 855 insertions(+), 678 deletions(-) create mode 100644 api/v1/methods/get-music.js delete mode 100644 api/v1/methods/get-muzic.js delete mode 100644 api/v1/response-wrapper.js diff --git a/api/v1/index.js b/api/v1/index.js index 42ee87a..bb59336 100644 --- a/api/v1/index.js +++ b/api/v1/index.js @@ -1,63 +1,110 @@ const router = require("express").Router(); -const response = require("./response-wrapper"); -// const config = require('../../config-handler'); +const fs = require("fs"); +const config = require('../../config-handler'); +const database = require('../../database'); +const bodyParser = require('body-parser'); // Парсинг куки //router.use(require('cookie-parser')()); -// Загрузка музыки при помощи спец. метода -function muzicLoad(req, res) { - res.setHeader("Content-Type", "audio/mpeg"); - global.database.muzic.get( - (err, data) => { - data = data[0]?.data; - if (err) { - res.send(Buffer.from([])); - } else { - res.send(!data ? Buffer.from([]) : data); - } - }, - { where: { id: !Number.isInteger(+req.query.id) ? 0 : +req.query.id } }, - ); -} +// Парсим JSON +router.use(bodyParser.json({ type: 'application/*+json' })) +router.use(bodyParser.json({ type: 'application/json' })) +const unknownError = (err) => { + const stackId = new Date().getTime(); + let errorLoggingFolder = config().error_logs_folder; + errorLoggingFolder = !["/", "\\"].includes(errorLoggingFolder.at(-1)) + ? errorLoggingFolder + "/" + : errorLoggingFolder; + fs.writeFileSync( + `${errorLoggingFolder}error_${stackId}.log`, + `ERROR: +Date: ${new Date()} +Name: ${err.name} +Message: ${err.message} +Stack: +${err.stack}`, + ); + + return { + error: "UNKNOWN_ERROR", + details: { + trace_id: stackId, + }, + }; +}; + +// Загрузка музыки при помощи спец. метода +router.get('/music', async (req, res) => { + res.setHeader("Content-Type", "audio/mpeg"); + database.music.findAll({ + where: { id: !Number.isInteger(+req.query.id) ? 0 : +req.query.id }, raw: true + }).then((data) => { + data = data[0]?.data; + res.send(!data ? Buffer.from([]) : data); + }).catch((err) => { + res.send(Buffer.from([])); + }); +}); + +// middleware (Обработка типа ответа) +router.use((req, res, next) => { + const unknownResponseFormat = "UNKNOWN_RESPONSE_FORMAT"; + + if (!req.query.response_format) { + res.send(unknownResponseFormat); + } + else if (req.query.response_format === 'json') { + res.result = function (data, isErr = false, code = 200) { + this.status(code).json(!isErr ? { + response : data + } : { + error : data + }); + }; + next(); + } + else { + res.send(unknownResponseFormat); + } +}); + +// Методы router.use( require('./methods') ); -// Подгрузка с файла -/*router.use("/:method_name", async (req, res, next, ...etc) => { - if (req.params.method_name === "muzic") { - muzicLoad(req, res); - return; - } - - try { - const methodFunct = require(`./methods/${req.params.method_name}`); - response(methodFunct, req, res); - } catch (e) { - if (e.message.includes("Cannot find module")) { - const ApiError = require("./errorClass"); - res.status(400).sendModed( - await response( - (req, res) => { - throw new ApiError("METHOD_NOT_FOUNDED"); - }, - req, - res, - ), - ); - } else { - response( - async () => { - throw e; - }, - req, - res, - ); +// Обработка ошибок +router.use( + async function (err, req, res, next) { + if (err) { + if (err.name === "ApiError") { + req.logger.log( + new Date(), + req.ip, + `Ошибка API.\n${err.stack}`, + true + ); + return res.result({ + error : err.message, + details : err.details + }, true, 400); + } + else { + req.logger.log( + new Date(), + req.ip, + `Критическая ошибка API\n${err.stack}`, + true + ); + return res.result({ + error : unknownError(err) + }, true, 500); + } } + else next(); } -}); -*/ +); module.exports = router; diff --git a/api/v1/methods/create-item.js b/api/v1/methods/create-item.js index 15e31cb..0e03dbf 100644 --- a/api/v1/methods/create-item.js +++ b/api/v1/methods/create-item.js @@ -1,87 +1,75 @@ const ApiError = require("../errorClass"); const config = require("../../../config-handler"); +const database = require("../../../database"); +const apiTypes = require("../typeChecker"); -async function isAuthorExists(authorId) { - if (!authorId) return false; - return ( - ( - await global.database.authors - .promiseMode() - .get({ where: { id: authorId } }) - ).length !== 0 - ); -} +const router = require('express').Router(); async function checkSyntaxArgs(req, res) { - return !!( - // Проверка поля type. - ["author", "muzic"].indexOf(req.json?.type) !== -1 && // Проверка поля name. - typeof req.json.name === "string" && - req.json.name.length >= 2 && - (req.json.type === "muzic" - ? true - : config().authors_blacklist.filter((blacklisted) => { - return req.json.name - .toLowerCase() - .includes(blacklisted.toLowerCase()); - }).length === 0) && // Дополнительные поля для muzic - (req.json.type === "muzic" - ? !!( - // Для `muzic` - ( - // Проверка поля author_id. - (await isAuthorExists(req.json.author_id)) && // Проверка поля data. (Передаётся либо ничего, либо строка с base64) - (req.json.data === undefined - ? true - : typeof req.json.data === "string") - ) - ) - : true) - ); + if (req.body === undefined || !req.headers['content-type'].includes('json')) { + throw new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'create-item', no_post : false }); + } + const checker = new apiTypes.TypeChecker(); + + await checker.checkRequired(req.body.type, apiTypes.ItemType, 'type'); + await checker.checkRequired(req.body.name, apiTypes.NameType, 'name'); + + if (req.body.type === 'music') { + await checker.checkRequired(req.body.author_id, apiTypes.AuthorType, 'author_id'); + await checker.checkAdditional(req.body.data, apiTypes.Base64FileType, 'data'); + } + + const result = await checker.calculate(); + if (!result.success) { + throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result); + } } -module.exports = async (req, res) => { - if (req.json === undefined) { - // console.log(req.headers); - throw new ApiError("METHOD_MUST_BE_POST_JSON", { - request_method: req.method, - "content-type": !req.headers["content-type"] - ? null - : req.headers["content-type"], - }); +router.post('/create-item', async (req, res, next) => { + try { + await checkSyntaxArgs(req, res); + + if (req.body.type === "author") { + if ( + config().authors_blacklist.filter((blacklisted) => { + return req.body.name + .toLowerCase() + .includes(blacklisted.toLowerCase()); + }).length !== 0 + ) { + throw new ApiError("AUTHOR_BLACKLISTED"); + } + + const result = await database.authors.create({ + name: req.body.name, + time: Math.round(new Date().getTime() / 1000), + }); + res.result(result.dataValues.id); + } else { + const result = await database.music.create({ + name: req.body.name, + author_id: req.body.author_id, + data: + (req.body.data === undefined || req.body.data === null) + ? undefined + : Buffer.from(req.body.data, "base64"), + time: Math.round(new Date().getTime() / 1000), + }); + res.result(result.dataValues.id); + } } - if (!(await checkSyntaxArgs(req, res))) { - throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { - request_method: req.method, - params: { - type: req.json?.type === undefined ? null : req.json.type, - name: req.json?.name === undefined ? null : req.json.name, - ...(req.json?.type === "muzic" - ? { - author_id: - req.json?.author_id === undefined ? null : req.json?.author_id, - data: req.json?.data === undefined ? null : req.json.data, - } - : {}), - }, - }); + catch (err) { + return next(err); } - if (req.json.type === "author") { - let result = await global.database.authors.promiseMode().add({ - name: req.json.name, - time: Math.round(new Date().getTime() / 1000), - }); - return result.dataValues.id; - } else { - let result = await global.database.muzic.promiseMode().add({ - name: req.json.name, - author_id: req.json.author_id, - data: - req.json.data === undefined - ? undefined - : Buffer.from(req.json.data, "base64"), - time: Math.round(new Date().getTime() / 1000), - }); - return result.dataValues.id; +}); + +router.use('/create-item', async (req, res, next) => { + try { + return next(new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'create-item', no_post : true })); } -}; \ No newline at end of file + catch (err) { + return next(err); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/api/v1/methods/edit-item.js b/api/v1/methods/edit-item.js index 7599fb0..9ee5b61 100644 --- a/api/v1/methods/edit-item.js +++ b/api/v1/methods/edit-item.js @@ -1,90 +1,88 @@ const ApiError = require("../errorClass"); +const database = require("../../../database"); +const config = require("../../../config-handler"); +const apiTypes = require("../typeChecker"); -async function isAuthorExists(id) { - if (!id || !Number.isInteger(id)) return false; - return ( - (await global.database.authors.promiseMode().get({ where: { id } })) - .length !== 0 - ); -} - -async function isMuzicExists(id) { - if (!id || !Number.isInteger(id)) return false; - let result = - (await global.database.muzic.promiseMode().get({ where: { id } })) - .length !== 0; - console.log("isMuzicExists", result); - return result; -} +const router = require('express').Router(); async function checkSyntaxArgs(req, res) { - return !!( - // Проверка поля type - ["author", "muzic"].includes(req.json.type) && // Проверка поля name - (req.json.name === undefined - ? true - : typeof req.json.name === "string" && req.json.name.length >= 2) && // Проверка id - (req.json.type === "author" - ? await isAuthorExists(req.json.id) - : await isMuzicExists(req.json.id)) && // Проверка при type=muzic - (req.json.type === "muzic" - ? // Проверка поля author_id - (req.json.author_id === undefined - ? true - : await isAuthorExists(req.json.author_id)) && // Проверка поля data. (Передаётся либо ничего, либо строка с base64) - ((req.json.data === undefined || req.json.data === null) ? true : typeof req.json.data === "string") - : true) - ); + if (req.body === undefined || !req.headers['content-type'].includes('json')) { + throw new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'edit-item', no_post : false }); + } + const checker = new apiTypes.TypeChecker(); + + await checker.checkRequired(req.body.type, apiTypes.ItemType, 'type'); + await checker.checkAdditional(req.body.name, apiTypes.NameType, 'name'); + + if (req.body.type === 'music') { + await checker.checkRequired(req.body.id, apiTypes.MusicType, 'id'); + + await checker.checkAdditional(req.body.author_id, apiTypes.AuthorType, 'author_id'); + await checker.checkAdditional(req.body.data, apiTypes.Base64FileType, 'data'); + } + else if (req.body.type === 'author') { + await checker.checkRequired(req.body.id, apiTypes.AuthorType, 'id'); + } + + const result = await checker.calculate(); + if (!result.success) { + throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result); + } } -module.exports = async (req, res) => { - if (req.method !== "POST") { - throw new ApiError("METHOD_MUST_BE_POST_JSON", { - request_method: req.method, - }); +router.post('/edit-item', async (req, res, next) => { + try { + await checkSyntaxArgs(req, res); + + if (req.body.type === "author") { + if ( + config().authors_blacklist.filter((blacklisted) => { + return req.body.name + .toLowerCase() + .includes(blacklisted.toLowerCase()); + }).length !== 0 + ) { + throw new ApiError("AUTHOR_BLACKLISTED"); + } + + await database.authors.update( + { + name: req.body.name, + }, + { + where: { + id: req.body.id, + }, + }, + ); + } else { + await database.music.update( + { + name: req.body.name, + author_id: req.body.author_id, + data: !req.body.data ? (req.body.data === null ? null : undefined) : Buffer.from(req.body.data, "base64"), + }, + { + where: { + id: req.body.id, + }, + }, + ); + } + res.result("ok"); } - console.log(await checkSyntaxArgs(req, res)); - if (!(await checkSyntaxArgs(req, res))) { - throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { - request_method: req.method, - params: { - id: req.json.id === undefined ? null : req.json.id, - type: req.json.type === undefined ? null : req.json.type, - name: req.json.name === undefined ? null : req.json.name, - ...(req.json.type === "muzic" - ? { - author_id: req.json.author_id, - data: req.json.data, - } - : {}), - }, - }); + catch (err) { + return next(err); } +}); - if (req.json.type === "author") { - await global.database.authors.promiseMode().edit( - { - name: req.json.name, - }, - { - where: { - id: req.json.id, - }, - }, - ); - } else { - await global.database.muzic.promiseMode().edit( - { - name: req.json.name, - author_id: req.json.author_id, - data: !req.json.data ? (req.json.data === null ? null : undefined) : Buffer.from(req.json.data, "base64"), - }, - { - where: { - id: req.json.id, - }, - }, - ); +router.use('/edit-item', async (req, res, next) => { + try { + return next(new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'edit-item', no_post : true })); } - return "ok"; -}; \ No newline at end of file + catch (err) { + return next(err); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/api/v1/methods/get-authors.js b/api/v1/methods/get-authors.js index 1f55801..9bcd0bd 100644 --- a/api/v1/methods/get-authors.js +++ b/api/v1/methods/get-authors.js @@ -1,81 +1,109 @@ +const { Op } = require("sequelize"); const ApiError = require("../errorClass"); +const database = require("../../../database"); +const apiTypes = require("../typeChecker"); + +const router = require('express').Router(); async function checkSyntaxArgs(req, res) { - return !!( - // Проверка поля offset - (req.query.id === undefined ? true : Number.isInteger(+req.query.offset)) && // Проверка поля offset - (req.query.offset === undefined - ? true - : Number.isInteger(+req.query.offset)) && // Проверка поля count - (req.query.count === undefined - ? true - : Number.isInteger(+req.query.count)) && // Проверка поля min_date - (req.query.min_date === undefined - ? true - : Number.isInteger(+req.query.min_date)) && // Проверка поля max_date - (req.query.max_date === undefined - ? true - : Number.isInteger(+req.query.max_date)) && // Проверка поля q. (Ключевые слова для поиска) - true // (Проверки нет, query всегда строка, необязательный параметр) - ); + const checker = new apiTypes.TypeChecker(false); + + await checker.checkAdditional(req.query.id, apiTypes.AuthorType, 'id'); + await checker.checkAdditional(req.query.offset, apiTypes.IntType, 'offset'); + await checker.checkAdditional(req.query.count, apiTypes.IntType, 'count'); + await checker.checkAdditional(req.query.min_date, apiTypes.IntType, 'min_date'); + await checker.checkAdditional(req.query.max_date, apiTypes.IntType, 'max_date'); + await checker.checkAdditional(req.query.q, apiTypes.StrType, 'q'); + + const result = await checker.calculate(); + if (!result.success) { + throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result); + } } -module.exports = async (req, res) => { - if (req.method !== "GET") { - throw new ApiError("METHOD_MUST_BE_GET", { - request_method: req.method, - }); - } - if (!(await checkSyntaxArgs(req, res))) { - throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { - request_method: req.method, - params: { - id: req.query?.id === undefined ? null : req.query.id, - q: req.query?.q === undefined ? null : req.query.q, - offset: req.query?.offset === undefined ? null : req.query.offset, - count: req.query?.count === undefined ? null : req.query.count, - min_date: req.query?.min_date === undefined ? null : req.query.min_date, - max_date: req.query?.max_date === undefined ? null : req.query.max_date, - }, - }); - } - - const offset = req.query.offset === undefined ? 0 : +req.query.offset; - const countFromNull = - req.query.count === undefined ? undefined : offset + +req.query.count; - - let result = ( - await global.database.authors.promiseMode().get( - req.query.id === undefined - ? {} - : { - id: +req.query.id, - }, - ) - ).map((i) => ({ - id: i.id, - name: i.name, - date: i.time, - })); - // Если просят выборку по времени - if (req.query.min_date || req.query.max_date) { +router.get('/get-authors', async (req, res, next) => { + try { + await checkSyntaxArgs(req, res); + + const offset = req.query.offset === undefined ? 0 : +req.query.offset; + const countFromNull = + req.query.count === undefined ? undefined : offset + +req.query.count; + + // Выборка по времени + const requiredTimeSearch = req.query.min_date || req.query.max_date; const minDate = req.query.min_date === undefined ? 0 : +req.query.min_date; - const maxDate = - req.query.max_date === undefined ? 1e32 : +req.query.max_date; + const maxDate = req.query.max_date === undefined ? 9999999999 : +req.query.max_date; + + let result = ( + await database.authors.findAll( + req.query.id === undefined + ? { + order: [ + ['id', 'ASC'], + ['name', 'ASC'], + ], + attributes: ['id', 'name', 'time'], + ...(requiredTimeSearch ? ({ + // Запрос на выборку по времени + where : { + time : { + [Op.gte] : minDate, + [Op.lte] : maxDate + } + } + }) : {}), + raw : true + } + : { + order: [ + ['id', 'ASC'], + ['name', 'ASC'], + ], + attributes: ['id', 'name', 'time'], + where : { + id: +req.query.id, + ...(requiredTimeSearch ? ({ + // Запрос на выборку по времени + time : { + [Op.gte] : minDate, + [Op.lte] : maxDate + } + }) : {}) + }, + raw : true + }, + ) + ).map((i) => ({ + id: i.id, + name: i.name, + date: i.time, + })); - result = result.filter((res) => minDate <= res.date && res.date <= maxDate); - } + if (req.query?.q !== undefined) { + result = result.filter((i) => { + const search = req.query.q.toLowerCase(); + return i.name.toLowerCase().includes(search); + }); + } - if (req.query?.q !== undefined) { - result = result.filter((i) => { - const search = req.query.q.toLowerCase(); - return i.name.toLowerCase().includes(search); + result = result.slice(offset, countFromNull); + res.result({ + count: result.length, + items: result, }); } + catch (err) { + return next(err); + } +}); - result = result.slice(offset, countFromNull); - return { - count: result.length, - items: result, - }; -}; \ No newline at end of file +router.use('/get-authors', async (req, res, next) => { + try { + return next(new ApiError("METHOD_MUST_BE_GET", { method : 'get-authors' })); + } + catch (err) { + return next(err); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/api/v1/methods/get-music.js b/api/v1/methods/get-music.js new file mode 100644 index 0000000..eb50ce6 --- /dev/null +++ b/api/v1/methods/get-music.js @@ -0,0 +1,127 @@ +const { Op } = require("sequelize"); +const ApiError = require("../errorClass"); +const database = require("../../../database"); +const apiTypes = require("../typeChecker"); + +const router = require('express').Router(); + +async function checkSyntaxArgs(req, res) { + const checker = new apiTypes.TypeChecker(false); + + await checker.checkAdditional(req.query.id, apiTypes.MusicType, 'id'); + await checker.checkAdditional(req.query.author, apiTypes.AuthorType, 'author'); + await checker.checkAdditional(req.query.authors, apiTypes.AuthorsType, 'authors'); + await checker.checkAdditional(req.query.offset, apiTypes.IntType, 'offset'); + await checker.checkAdditional(req.query.count, apiTypes.IntType, 'count'); + await checker.checkAdditional(req.query.min_date, apiTypes.IntType, 'min_date'); + await checker.checkAdditional(req.query.max_date, apiTypes.IntType, 'max_date'); + await checker.checkAdditional(req.query.q, apiTypes.StrType, 'q'); + await checker.checkAdditional(req.query.searchByAuthor, apiTypes.StrType, 'searchByAuthor'); + await checker.checkAdditional(req.query.searchByName, apiTypes.StrType, 'searchByName'); + + const result = await checker.calculate(); + if (!result.success) { + throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result); + } +} + +router.get('/get-music', async (req, res, next) => { + try { + await checkSyntaxArgs(req, res); + + // Оптимизация: флаг searchByAuthor выставится на 0, если была запрошена выборка по музыке определённого автора + // Тем самым, мы ускоряем время ответа, поскольку мы не перебираем массив searchedAuthors с целью поиска совпадений + let searchByAuthor = + req.query.author === undefined ? req.query.searchByAuthor : "0"; + + const offset = req.query.offset === undefined ? 0 : +req.query.offset; + const countFromNull = + req.query.count === undefined ? undefined : offset + +req.query.count; + + const searchAuthors = !req.query.authors ? [] : [...req.query.authors.split(',').map(i => +i)]; + if (req.query.author) searchAuthors.push(+req.query.author); + + const minDate = req.query.min_date === undefined ? 0 : +req.query.min_date; + const maxDate = req.query.max_date === undefined ? 9999999999 : +req.query.max_date; + + let searchedParams = []; + if (searchAuthors > 0) { + searchAuthors.forEach(author_id => { + console.log(author_id); + searchedParams.push({ [Op.and] : [ + req.query.id !== undefined ? {id : +req.query.id} : undefined, + { author_id }, + {time : { + [Op.gte]: minDate, + [Op.lte]: maxDate, + }}, + ] }); + }); + } + else { + searchedParams.push({ [Op.and] : [ + req.query.id !== undefined ? {id : +req.query.id} : undefined, + {time : { + [Op.gte]: minDate, + [Op.lte]: maxDate, + }}, + ] }); + } + + console.log('searchedParams', searchedParams); + + let result = ( + await database.music.findAll({ + where : { [Op.or]: searchedParams }, + raws : true + }) + ).map((i) => ({ + id: i.id, + name: i.name, + author_id: i.author_id, + is_data_exists: i.data !== null && i.data.length > 0, + date: i.time, + })); + + if (req.query?.q !== undefined) { + let authors = await database.authors.findAll({raws : true}); + let searchedAuthors = result.map((i) => { + let author_id = i.author_id; + return authors.filter((a) => a.id === author_id)[0]; + }); + result = result.filter((i) => { + const search = req.query.q.toLowerCase(); + return ( + (req.query.searchByName !== "0" && + i.name.toLowerCase().includes(search)) || + !!( + searchByAuthor !== "0" && + searchedAuthors + .filter((a) => a.id === i.author_id)[0] + ?.name.toLowerCase() + .includes(search) + ) + ); + }); + } + result = result.slice(offset, countFromNull); + res.result({ + count: result.length, + items: result, + }); + } + catch (err) { + return next(err); + } +}); + +router.use('/get-music', async (req, res, next) => { + try { + return next(new ApiError("METHOD_MUST_BE_GET", { method : 'get-music' })); + } + catch (err) { + return next(err); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/api/v1/methods/get-muzic.js b/api/v1/methods/get-muzic.js deleted file mode 100644 index e1ad971..0000000 --- a/api/v1/methods/get-muzic.js +++ /dev/null @@ -1,142 +0,0 @@ -const ApiError = require("../errorClass"); - -async function isAuthorExists(authorId) { - if (!authorId) return false; - return ( - ( - await global.database.authors - .promiseMode() - .get({ where: { id: authorId } }) - ).length !== 0 - ); -} - -async function isAuthorsExists(authorsId) { - if (!Array.isArray(authorsId)) return false; - const authors = (await global.database.authors.promiseMode().get()).map( - (i) => i.id, - ); - return authorsId.map((authorId) => authors.includes(authorId)); -} - -async function checkSyntaxArgs(req, res) { - return !!( - // Проверка поля author. - (req.query.author === undefined - ? true - : await isAuthorExists(+req.query.author)) && // Проверка поля authors. - (req.query.authors === undefined - ? true - : await isAuthorsExists(req.query.authors.split(",").map((i) => +i))) && // Проверка поля offset - (req.query.offset === undefined - ? true - : Number.isInteger(+req.query.offset)) && // Проверка поля count - (req.query.count === undefined - ? true - : Number.isInteger(+req.query.count)) && // Проверка поля min_date - (req.query.min_date === undefined - ? true - : Number.isInteger(+req.query.min_date)) && // Проверка поля max_date - (req.query.max_date === undefined - ? true - : Number.isInteger(+req.query.max_date)) && // Проверка поля q. (Ключевые слова для поиска) - true && // (Проверки нет, query всегда строка, необязательный параметр) // Проверка поля searchByAuthor (Флаг для 'q'. 0 - отключить) - true && // (Проверки нет, query всегда строка, необязательный параметр) // Проверка поля searchByName (Флаг для 'q'. 0 - отключить) - true // (Проверки нет, query всегда строка, необязательный параметр) - ); -} - -module.exports = async (req, res) => { - if (req.method !== "GET") { - throw new ApiError("METHOD_MUST_BE_GET", { - request_method: req.method, - }); - } - if (!(await checkSyntaxArgs(req, res))) { - throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { - request_method: req.method, - params: { - author: req.query?.author === undefined ? null : req.query.author, - authors: req.query?.authors === undefined ? null : req.query.authors, - q: req.query?.q === undefined ? null : req.query.q, - offset: req.query?.offset === undefined ? null : req.query.offset, - count: req.query?.count === undefined ? null : req.query.count, - searchByAuthor: - req.query?.searchByAuthor === undefined - ? null - : req.query.searchByAuthor, - searchByName: - req.query?.searchByName === undefined ? null : req.query.searchByName, - min_date: req.query?.min_date === undefined ? null : req.query.min_date, - max_date: req.query?.max_date === undefined ? null : req.query.max_date, - }, - }); - } - - // Оптимизация: флаг searchByAuthor выставится на 0, если была запрошена выборка по музыке определённого автора - // Тем самым, мы ускоряем время ответа, поскольку мы не перебираем массив searchedAuthors с целью поиска совпадений - let searchByAuthor = - req.query.author === undefined ? req.query.searchByAuthor : "0"; - - const offset = req.query.offset === undefined ? 0 : +req.query.offset; - const countFromNull = - req.query.count === undefined ? undefined : offset + +req.query.count; - - let result = ( - await global.database.muzic.promiseMode().get( - req.query?.author === undefined - ? {} - : { - where: { - author_id: +req.query.author, - }, - }, - ) - ).map((i) => ({ - id: i.id, - name: i.name, - author_id: i.author_id, - is_data_exists: i.data !== null && i.data.length > 0, - date: i.time, - })); - // Если просят выборку по authors - if (req.query.authors) { - let authors = req.query.authors.split(",").map((author) => +author); - result = result.filter((res) => authors.includes(res.author_id)); - } - // Если просят выборку по времени - if (req.query.min_date || req.query.max_date) { - const minDate = req.query.min_date === undefined ? 0 : +req.query.min_date; - const maxDate = - req.query.max_date === undefined ? 1e32 : +req.query.max_date; - - result = result.filter((res) => minDate <= res.date && res.date <= maxDate); - } - - if (req.query?.q !== undefined) { - let authors = await global.database.authors.promiseMode().get(); - let searchedAuthors = result.map((i) => { - let author_id = i.author_id; - return authors.filter((a) => a.id === author_id)[0]; - }); - result = result.filter((i) => { - const search = req.query.q.toLowerCase(); - return ( - (req.query.searchByName !== "0" && - i.name.toLowerCase().includes(search)) || - !!( - searchByAuthor !== "0" && - searchedAuthors - .filter((a) => a.id === i.author_id)[0] - ?.name.toLowerCase() - .includes(search) - ) - ); - }); - } - result = result.slice(offset, countFromNull); - return { - count: result.length, - items: result, - }; -}; \ No newline at end of file diff --git a/api/v1/methods/index.js b/api/v1/methods/index.js index 3418369..eeba932 100644 --- a/api/v1/methods/index.js +++ b/api/v1/methods/index.js @@ -1,3 +1,30 @@ const router = require('express').Router(); -router.use(); +router.use( + require('./create-item') +); + +router.use( + require('./edit-item') +); + +router.use( + require('./remove-item') +); + +router.use( + require('./get-authors') +); + +router.use( + require('./get-music') +); + +router.use('*', async (req, res) => { + res.result({ + error : "UNKNOWN_METHOD", + details : {} + }, true, 400); +}); + +module.exports = router; \ No newline at end of file diff --git a/api/v1/methods/remove-item.js b/api/v1/methods/remove-item.js index b0c288a..996b286 100644 --- a/api/v1/methods/remove-item.js +++ b/api/v1/methods/remove-item.js @@ -1,72 +1,68 @@ const ApiError = require("../errorClass"); -const config = require("../../../config-handler"); +const database = require("../../../database"); +const apiTypes = require("../typeChecker"); -async function isAuthorExists(authorId) { - if (!authorId) return false; - return ( - ( - await global.database.authors - .promiseMode() - .get({ where: { id: authorId } }) - ).length !== 0 - ); -} +const router = require('express').Router(); async function checkSyntaxArgs(req, res) { - return !!( - // Проверка поля type. - ["author", "muzic"].indexOf(req.json?.type) !== -1 && // Проверка поля id. - (Number.isInteger(req.json.id) && req.json.type === "author" - ? await isAuthorExists(req.json.id) - : true) - ); + if (req.body === undefined || !req.headers['content-type'].includes('json')) { + throw new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'remove-item', no_post : false }); + } + const checker = new apiTypes.TypeChecker(); + + await checker.checkRequired(req.body.type, apiTypes.ItemType, 'type'); + + if (req.body.type === 'music') { + await checker.checkRequired(req.body.id, apiTypes.MusicType, 'id'); + } + else if (req.body.type === 'author') { + await checker.checkRequired(req.body.id, apiTypes.AuthorType, 'id'); + } + + const result = await checker.calculate(); + if (!result.success) { + throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result); + } } -module.exports = async (req, res) => { - if (req.json === undefined) { - // console.log(req.headers); - throw new ApiError("METHOD_MUST_BE_POST_JSON", { - request_method: req.method, - "content-type": !req.headers["content-type"] - ? null - : req.headers["content-type"], - }); +router.post('/remove-item', async (req, res, next) => { + try { + await checkSyntaxArgs(req, res); + + if (req.body.type === "author") { + // Удаляем всю музыку этого исполнителя + await database.music.destroy({ + where: { + author_id: req.body.id, + }, + }); + // Удаляем исполнителя + await database.authors.destroy({ + where: { + id: req.body.id, + }, + }); + } else { + await database.music.destroy({ + where: { + id: req.body.id, + }, + }); + } + res.result("ok"); } - if (!(await checkSyntaxArgs(req, res))) { - throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { - request_method: req.method, - params: { - type: req.json?.type === undefined ? null : req.json.type, - name: req.json?.name === undefined ? null : req.json.name, - ...(req.json?.type === "muzic" - ? { - author_id: - req.json?.author_id === undefined ? null : req.json?.author_id, - data: req.json?.data === undefined ? null : req.json.data, - } - : {}), - }, - }); + catch (err) { + return next(err); } - if (req.json.type === "author") { - // Удаляем всю музыку этого исполнителя - await global.database.muzic.promiseMode().remove({ - where: { - author_id: req.json.id, - }, - }); - // Удаляем исполнителя - await global.database.authors.promiseMode().remove({ - where: { - id: req.json.id, - }, - }); - } else { - await global.database.muzic.promiseMode().remove({ - where: { - id: req.json.id, - }, - }); +}); + +router.use('/remove-item', async (req, res, next) => { + try { + return next(new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'remove-item', no_post : true })); } - return "ok"; -}; \ No newline at end of file + catch (err) { + return next(err); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/api/v1/response-wrapper.js b/api/v1/response-wrapper.js deleted file mode 100644 index c40a381..0000000 --- a/api/v1/response-wrapper.js +++ /dev/null @@ -1,61 +0,0 @@ -const fs = require("fs"); -const config = require("../../config-handler"); - -const unknownError = (err) => { - const stackId = new Date().getTime(); - let errorLoggingFolder = config().error_logs_folder; - errorLoggingFolder = !["/", "\\"].includes(errorLoggingFolder.at(-1)) - ? errorLoggingFolder + "/" - : errorLoggingFolder; - fs.writeFileSync( - `${errorLoggingFolder}error_${stackId}.log`, - `ERROR: -Date: ${new Date()} -Name: ${err.name} -Message: ${err.message} -Stack: -${err.stack}`, - ); - - return { - error: "UNKNOWN_ERROR", - details: { - trace_id: stackId, - }, - }; -}; - -const unknownResponseFormat = "UNKNOWN_RESPONSE_FORMAT"; - -async function handlingError(funct, success, error) { - try { - success(await funct()); - } catch (e) { - // console.log('error', e); - error( - e.name === "ApiError" - ? { - error: e.message, - details: e.details, - } - : unknownError(e), - ); - } -} - -module.exports = async (method, req, res) => { - if (req.query.response_format === "json") { - handlingError( - async () => ({ response: await method(req, res) }), - async (data) => { - res.sendModed(data); - }, - async (errBody) => { - res.errorModeOn(); - res.status(400).sendModed(errBody); - }, - ); - } else { - res.status(400).sendModed(unknownResponseFormat); - } -}; \ No newline at end of file diff --git a/api/v1/typeChecker.js b/api/v1/typeChecker.js index 6bdefba..b53949c 100644 --- a/api/v1/typeChecker.js +++ b/api/v1/typeChecker.js @@ -1,35 +1,203 @@ +const { Op } = require("sequelize"); +const database = require("../../database"); + +// TypeChecker class +class TypeChecker { + constructor (strictMode = true) { + this.strict = strictMode; + + this.requiredMissed = []; + this.additionalMissed = []; + + this.requiredUnsyntax = []; + this.additionalUnsyntax = []; + + this.unsyntax = false; + } + + async checkRequired (param, Type, paramName) { + if (param === undefined) { + this.requiredMissed.push(paramName); + this.unsyntax = true; + } + else { + const type = new Type(param); + if (this.strict && !(await type.checkStrict())) { + this.requiredUnsyntax.push(paramName); + this.unsyntax = true; + } + else if (!this.strict && !(await type.checkNoStrict())) { + this.requiredUnsyntax.push(paramName); + this.unsyntax = true; + } + } + } + + async checkAdditional (param, Type, paramName) { + if (param === undefined) { + this.additionalMissed.push(paramName); + } + else { + const type = new Type(param); + if (this.strict && !(await type.checkStrict())) { + this.additionalUnsyntax.push(paramName); + this.unsyntax = true; + } + else if (!this.strict && !(await type.checkNoStrict())) { + this.additionalUnsyntax.push(paramName); + this.unsyntax = true; + } + } + } + + async calculate () { + return { + success : !this.unsyntax, + missed : { + required : this.requiredMissed, + additional : this.additionalMissed + }, + unsyntax : { + required : this.requiredUnsyntax, + additional : this.additionalUnsyntax + } + } + } +} + + +// Types + class ParamType { constructor (value) { this.value = value; } - checkNoStrict () { + async checkNoStrict () { return true; } - checkStrict () { + async checkStrict () { return true; } } class StrType extends ParamType { - checkStrict () { + async checkStrict () { + return typeof this.value === 'string'; + } + + async checkNoStrict () { return typeof this.value === 'string'; } } class IntType extends ParamType { - checkNoStrict () { + async checkNoStrict () { return Number.isInteger(+this.value); } - checkStrict () { + async checkStrict () { return Number.isInteger(this.value); } } +// Custom types + +class ItemType extends StrType { + async checkStrict () { + if (await super.checkStrict()) return ['music', 'author'].includes(this.value); + return false; + } + + async checkNoStrict () { + if (await super.checkNoStrict()) return ['music', 'author'].includes(this.value); + return false; + } +} + +class NameType extends StrType { + async checkStrict () { + if (await super.checkStrict()) return this.value.length >= 2; + return false; + } + + async checkNoStrict () { + if (await super.checkNoStrict()) return this.value.length >= 2; + return false; + } +} + +class AuthorType extends IntType { + async checkStrict () { + if (await super.checkStrict()) { + const authorId = this.value; + return (await database.authors.findAll({ where: { id: authorId }, raw : true })).length !== 0; + } + return false; + } + + async checkNoStrict () { + if (await super.checkNoStrict()) { + const authorId = +this.value; + return (await database.authors.findAll({ where: { id: authorId }, raw : true })).length !== 0; + } + return false; + } +} + +class AuthorsType extends AuthorType { + async checkStrict () { + if (Array.isArray(this.value) && !this.value.map(i => Number.isInteger(i)).includes(false)) { + // return (await database.authors.findAll({ where: [Op.or]: [ + // { id: 12 }, + // { id: 13 } + // ], raw : true })).length === this.value; + return (await database.authors.findAll({ where: { [Op.or]: this.value.map(id => ({ id })) }, raw : true })).length === this.value; + } + return false; + } + + async checkNoStrict () { + if (await(new StrType(this.value)).checkStrict()) { + const values = this.value.split(',').map(i => +i); + if (!values.map(i => Number.isInteger(i)).includes(false)) { + return (await database.authors.findAll({ where: { [Op.or]: values.map(id => ({ id })) }, raw : true })).length === this.value.length; + } + return false; + } + return false; + } +} + +class MusicType extends IntType { + async checkStrict () { + if (await super.checkStrict()) { + const musicId = this.value; + return (await database.music.findAll({ where: { id: musicId }, raw : true })).length !== 0; + } + return false; + } + + async checkNoStrict () { + if (await super.checkNoStrict()) { + const musicId = +this.value; + return (await database.music.findAll({ where: { id: musicId }, raw : true })).length !== 0; + } + return false; + } +} + +class Base64FileType extends StrType { + async checkStrict () { + return this.value === null || (await super.checkStrict()); + } +} + module.exports = { ParamType, - StrType, IntType + StrType, IntType, + ItemType, NameType, AuthorType, AuthorsType, MusicType, Base64FileType, + TypeChecker }; diff --git a/db-dump/install-it.sql b/db-dump/install-it.sql index c033bb6589006de231d188009bdeb2573cbcea0b..30df35b7ad9cc163b6dd92953d92c070b41f6c5e 100644 GIT binary patch delta 269 zcmZ3dxK44yIabEv$>&*hfaD+6c|dYDTQ-nnXEz0sw(Nn7#hd%tI~bXZGm|IBamY`$ z&*hfaD+6c|dYDTQ-nnXEz0sw(Nn7Rh#?SI~bX(GLt9AamY`$ z - Kodex Muzic + Kodex Music @@ -10,7 +10,7 @@ diff --git a/frontend/public/js/api-module.js b/frontend/public/js/api-module.js index e45dab0..ac05a05 100644 --- a/frontend/public/js/api-module.js +++ b/frontend/public/js/api-module.js @@ -18,13 +18,13 @@ class Api { xhr.send(); } - getMuzic(params, cb) { + getMusic(params, cb) { const xhr = new XMLHttpRequest(); params = Object.entries(params) .map(([key, value]) => `${key}=${value}`) .join("&"); params = params !== "" ? `&${params}` : ""; - xhr.open("GET", `/api/v/1.0/get-muzic?response_format=json${params}`, true); + xhr.open("GET", `/api/v/1.0/get-music?response_format=json${params}`, true); xhr.onreadystatechange = function (event) { //console.log(event); if (this.readyState != 4) return; @@ -33,7 +33,7 @@ class Api { xhr.send(); } - deleteMuzic(id, cb) { + deleteMusic(id, cb) { const xhr = new XMLHttpRequest(); xhr.open("POST", `/api/v/1.0/remove-item?response_format=json`, true); xhr.setRequestHeader("Content-type", "application/json; charset=utf-8"); @@ -47,13 +47,13 @@ class Api { }; xhr.send( JSON.stringify({ - type: "muzic", + type: "music", id, }), ); } - editMuzic(id, name, data, author_id, cb) { + editMusic(id, name, data, author_id, cb) { const xhr = new XMLHttpRequest(); xhr.open("POST", `/api/v/1.0/edit-item?response_format=json`, true); xhr.setRequestHeader("Content-type", "application/json; charset=utf-8"); @@ -67,7 +67,7 @@ class Api { }; xhr.send( JSON.stringify({ - type: "muzic", + type: "music", id, name, author_id, @@ -76,7 +76,7 @@ class Api { ); } - createMuzic(author_id, name, data = null, cb) { + createMusic(author_id, name, data = null, cb) { data = data === null ? undefined : data; const xhr = new XMLHttpRequest(); xhr.open("POST", `/api/v/1.0/create-item?response_format=json`, true); @@ -88,7 +88,7 @@ class Api { }; xhr.send( JSON.stringify({ - type: "muzic", + type: "music", name, author_id, data, diff --git a/frontend/public/js/main.js b/frontend/public/js/main.js index 6a8312d..634551c 100644 --- a/frontend/public/js/main.js +++ b/frontend/public/js/main.js @@ -10,10 +10,10 @@ function showAudio(settingsAuthors, settingsAudio) { showAudio.settingsAuthors = settingsAuthors; showAudio.settingsAudio = settingsAudio; - const step2 = (muzic) => { - //console.log(muzic.items); + const step2 = (music) => { + //console.log(music.items); const showedAuthors = [ - ...new Set(muzic.items.map((i) => `author-${i.author_id}`)), + ...new Set(music.items.map((i) => `author-${i.author_id}`)), ]; [...document.getElementsByClassName("author-element")].forEach((el) => { if ( @@ -22,7 +22,7 @@ function showAudio(settingsAuthors, settingsAudio) { ) { if (!showedAuthors.includes(el.id)) { const id = el.id.slice(7); - document.getElementById(`loader-muzic-${id}`).innerHTML = + document.getElementById(`loader-music-${id}`).innerHTML = "

Пусто. Добавьте произведения

"; } return; @@ -30,48 +30,48 @@ function showAudio(settingsAuthors, settingsAudio) { el.hidden = !showedAuthors.includes(el.id); }); - muzic.items + music.items .sort((a, b) => a.date - b.date) - .forEach((muzic) => { + .forEach((music) => { document.getElementById( - `loader-muzic-${muzic.author_id}`, + `loader-music-${music.author_id}`, ).hidden = true; - if (muzic.is_data_exists) + if (music.is_data_exists) document.getElementById( - `playlist-author-${muzic.author_id}`, - ).innerHTML += `

-
${muzic.name}
- + music.is_data_exists + });'> ${music.name} + -

`; else document.getElementById( - `playlist-author-${muzic.author_id}`, - ).innerHTML += `

-
${muzic.name}
-

`; }); @@ -89,43 +89,43 @@ function showAudio(settingsAuthors, settingsAudio) {
Показать/Скрыть
-

Загрузка..

+

Загрузка..

-
+
`; - const addMuzicButtonFunct = () => + const addMusicButtonFunct = () => setTimeout( () => (document.getElementById( - `add-muzic-button-${author.id}`, + `add-music-button-${author.id}`, ).onclick = async () => { // console.log('>>', author.id); document.getElementById( - `add-muzic-${author.id}`, - ).innerHTML = `

+ `add-music-${author.id}`, + ).innerHTML = `

- -

`; + +

`; setTimeout(() => { document.getElementById( - `cancel-add-muzic-${author.id}`, + `cancel-add-music-${author.id}`, ).onclick = function () { document.getElementById( - `add-muzic-${author.id}`, - ).innerHTML = `
`; - addMuzicButtonFunct(); + `add-music-${author.id}`, + ).innerHTML = `
`; + addMusicButtonFunct(); }; - document.getElementById(`add-muzic-act-${author.id}`).onclick = + document.getElementById(`add-music-act-${author.id}`).onclick = function () { if ( - document.getElementById(`muzic-name-input-${author.id}`) + document.getElementById(`music-name-input-${author.id}`) .value.length >= 2 ) { - api.createMuzic( + api.createMusic( author.id, document.getElementById( - `muzic-name-input-${author.id}`, + `music-name-input-${author.id}`, ).value, document.getElementById(`attach-file-${author.id}`) .filedata, @@ -134,7 +134,7 @@ function showAudio(settingsAuthors, settingsAudio) { }, ); document.getElementById( - `add-muzic-${author.id}`, + `add-music-${author.id}`, ).innerHTML = "Добавление файла..."; } }; @@ -178,9 +178,9 @@ function showAudio(settingsAuthors, settingsAudio) { }), 0, ); - addMuzicButtonFunct(); + addMusicButtonFunct(); }); - api.getMuzic(settingsAudio, step2); + api.getMusic(settingsAudio, step2); document.getElementById("app-window").innerHTML += '
'; }); diff --git a/frontend/public/js/menu.js b/frontend/public/js/menu.js index dd8735b..212f841 100644 --- a/frontend/public/js/menu.js +++ b/frontend/public/js/menu.js @@ -83,7 +83,7 @@ function addAuthorMenu() { setTimeout(authorMenuButtonInit, 5); } -function selectFile(muzicId) { +function selectFile(musicId) { const input = document.createElement("input"); input.type = "file"; input.accept = "audio/mpeg"; @@ -106,22 +106,22 @@ function selectFile(muzicId) { reader.onload = function () { // console.log('reader.result', reader.result); if (file.type === "audio/mpeg") { - document.getElementById(`edit-attach-file-${muzicId}`).filedata = + document.getElementById(`edit-attach-file-${musicId}`).filedata = arrayBufferToBase64(reader.result); - document.getElementById(`edit-attach-file-${muzicId}`).innerText = + document.getElementById(`edit-attach-file-${musicId}`).innerText = "Загружен файл: " + file.name; - document.getElementById(`delete-attach-${muzicId}`).disabled = false; + document.getElementById(`delete-attach-${musicId}`).disabled = false; } // console.log(arrayBufferToBase64(reader.result)); }; }; } -function editMuzic(muzicId, muzicName) { - api.editMuzic( - muzicId, - muzicName, - document.getElementById(`edit-attach-file-${muzicId}`).filedata, +function editMusic(musicId, musicName) { + api.editMusic( + musicId, + musicName, + document.getElementById(`edit-attach-file-${musicId}`).filedata, undefined, (res) => { showAudio(showAudio.settingsAuthors, showAudio.settingsAudio); @@ -129,57 +129,57 @@ function editMuzic(muzicId, muzicName) { ); } -function removeMuzic(muzicId) { - api.deleteMuzic(muzicId, (res) => { +function removeMusic(musicId) { + api.deleteMusic(musicId, (res) => { showAudio(showAudio.settingsAuthors, showAudio.settingsAudio); }); } -function bindShowEditMuzicButtons(muzicId, muzicName, dataExists) { - document.getElementById(`cancel-edit-muzic-${muzicId}`).onclick = () => { - document.getElementById(`muzic-item-${muzicId}`).innerHTML = dataExists - ? `
${muzicName}
- - - ` - : `

-
${muzicName}
- +function bindShowEditMusicButtons(musicId, musicName, dataExists) { + document.getElementById(`cancel-edit-music-${musicId}`).onclick = () => { + document.getElementById(`music-item-${musicId}`).innerHTML = dataExists + ? `
${musicName}
+ + + ` + : `

+
${musicName}
+

`; }; - document.getElementById(`edit-attach-file-${muzicId}`).onclick = () => - selectFile(muzicId); - document.getElementById(`delete-attach-${muzicId}`).onclick = () => { - document.getElementById(`edit-attach-file-${muzicId}`).innerText = + document.getElementById(`edit-attach-file-${musicId}`).onclick = () => + selectFile(musicId); + document.getElementById(`delete-attach-${musicId}`).onclick = () => { + document.getElementById(`edit-attach-file-${musicId}`).innerText = "Добавить файл (.mp3)"; - document.getElementById(`edit-attach-file-${muzicId}`).filedata = null; - document.getElementById(`delete-attach-${muzicId}`).disabled = true; + document.getElementById(`edit-attach-file-${musicId}`).filedata = null; + document.getElementById(`delete-attach-${musicId}`).disabled = true; }; - document.getElementById(`edit-muzic-${muzicId}`).onclick = () => - editMuzic(muzicId, document.getElementById(`muzic-edit-name-input-${muzicId}`).value); - document.getElementById(`delete-muzic-${muzicId}`).onclick = () => - removeMuzic(muzicId); + document.getElementById(`edit-music-${musicId}`).onclick = () => + editMusic(musicId, document.getElementById(`music-edit-name-input-${musicId}`).value); + document.getElementById(`delete-music-${musicId}`).onclick = () => + removeMusic(musicId); } -function showEditMuzic(muzicId, muzicName, dataExists) { +function showEditMusic(musicId, musicName, dataExists) { document.getElementById( - `muzic-item-${muzicId}`, + `music-item-${musicId}`, ).innerHTML = `

-

- - - -

`; + + +

`; - setTimeout(() => bindShowEditMuzicButtons(muzicId, muzicName, dataExists), 5); + setTimeout(() => bindShowEditMusicButtons(musicId, musicName, dataExists), 5); } diff --git a/frontend/public/js/sound.js b/frontend/public/js/sound.js index b6b60be..c958c93 100644 --- a/frontend/public/js/sound.js +++ b/frontend/public/js/sound.js @@ -1,28 +1,28 @@ -let audio = new Audio("/api/v/1.0/muzic"); +let audio = new Audio("/api/v/1.0/music"); -function stopMuzic(id, interval) { +function stopMusic(id, interval) { audio.pause(); audio.currentTime = 0; document.getElementById(`play-${id}`).innerText = "Прослушать"; document.getElementById(`pause-${id}`).disabled = true; - document.getElementById(`play-${id}`).onclick = () => playMuzic(id); + document.getElementById(`play-${id}`).onclick = () => playMusic(id); clearInterval(interval); document.getElementById(`road-${id}`).value = 0; document.getElementById(`road-${id}`).max = 0; } -function pauseMuzic(id) { +function pauseMusic(id) { document.getElementById(`play-${id}`).disabled = true; document.getElementById(`pause-${id}`).innerText = "Продолжить"; - document.getElementById(`pause-${id}`).onclick = () => resumeMuzic(id); + document.getElementById(`pause-${id}`).onclick = () => resumeMusic(id); audio.pause(); } -function resumeMuzic(id) { +function resumeMusic(id) { document.getElementById(`play-${id}`).disabled = false; document.getElementById(`pause-${id}`).innerText = "Пауза"; - document.getElementById(`pause-${id}`).onclick = () => pauseMuzic(id); + document.getElementById(`pause-${id}`).onclick = () => pauseMusic(id); audio.play(); } @@ -30,12 +30,12 @@ function changeTime(value, id) { audio.currentTime = value; } -function playMuzic(id) { +function playMusic(id) { audio.pause(); audio.currentTime = 0; - if (audio.muzId !== undefined) stopMuzic(audio.muzId, audio.muzTimer); + if (audio.muzId !== undefined) stopMusic(audio.muzId, audio.muzTimer); - audio = new Audio(`/api/v/1.0/muzic?id=${id}`); + audio = new Audio(`/api/v/1.0/music?id=${id}`); audio.onloadedmetadata = function () { audio.muzId = id; document.getElementById(`play-${id}`).innerText = "Остановить"; @@ -52,8 +52,8 @@ function playMuzic(id) { }, 750); audio.muzTimer = interval; document.getElementById(`play-${id}`).onclick = () => - stopMuzic(id, interval); - document.getElementById(`pause-${id}`).onclick = () => pauseMuzic(id); + stopMusic(id, interval); + document.getElementById(`pause-${id}`).onclick = () => pauseMusic(id); document.getElementById(`road-${id}`).onchange = function () { // console.log('value', this.value); diff --git a/logger.js b/logger.js index a09cb3d..bb58b18 100644 --- a/logger.js +++ b/logger.js @@ -3,20 +3,20 @@ const fs = require("fs"); class Logger { constructor () { - this.isLoggerModeOn = config().logger_mode(); + this.isLoggerModeOn = config().logger_mode; if (this.isLoggerModeOn) { this.logId = `${(new Date()).getTime()}`; - let loggerFolder = config().logger_folder; - loggerFolder = !["/", "\\"].includes(loggerFolder.at(-1)) - ? loggerFolder + "/" - : loggerFolder; + this.loggerFolder = config().logger_folder; + this.loggerFolder = !["/", "\\"].includes(this.loggerFolder.at(-1)) + ? this.loggerFolder + "/" + : this.loggerFolder; fs.writeFile( - ` ${loggerFolder}${this.logId}.log`, '', + `${this.loggerFolder}${this.logId}.log`, '', { encoding: "utf8" - } + }, (err) => { - throw err; + if (err) throw err; } ); } @@ -24,13 +24,13 @@ class Logger { log (date, ip, action, isError = false) { if (this.isLoggerModeOn) fs.writeFile( - `${loggerFolder}${this.logId}.log`, !isError ? `IP: ${ip}\nДата запроса: ${date}` : `\nПроизошла ошибка: ${action}`, + `${this.loggerFolder}${this.logId}.log`, !isError ? `IP: ${ip}\nДата запроса: ${date}\nАктивное действие: ${action}` : `\nПроизошла ошибка: ${action}`, { encoding : "utf8", flag : "a" - } + }, (err) => { - throw err; + if (err) throw err; } ); } diff --git a/server.js b/server.js index 967e2e4..d02e48f 100644 --- a/server.js +++ b/server.js @@ -72,6 +72,7 @@ app.use(async (req, res, next) => { req.ip, `Пользователь обратился по пути: ${req.originalUrl} (HTTP ${req.httpVersion}; ${req.method})` ); + next(); }); app.use("/", require("./page-view"));