Compare commits

...

6 Commits
v-0.9b ... main

Author SHA1 Message Date
Nikiroy78
36daa8be9c Fix bug with pause 2023-10-08 19:02:06 +03:00
Nikiroy78
29fec9506b Fix any components 2023-10-06 18:58:37 +03:00
Nikiroy78
9db7a4127f Fix bug with upload files 2023-10-06 05:05:19 +03:00
Nikiroy78
86c2a35a31 Add backend fixes 2023-10-06 04:41:18 +03:00
FullGreaM
023b5687f7 Add architecture fixes 2023-10-03 17:31:53 +03:00
Nikiroy78
f6275b0998 . 2023-10-03 02:50:16 +03:00
23 changed files with 1097 additions and 917 deletions

View File

@ -1,58 +1,114 @@
const router = require("express").Router(); const express = require("express");
const response = require("./response-wrapper"); const router = express.Router();
// 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')()); //router.use(require('cookie-parser')());
// Загрузка музыки при помощи спец. метода // Парсим JSON
function muzicLoad(req, res) { router.use(bodyParser.json({ type: 'application/*+json' }));
res.setHeader("Content-Type", "audio/mpeg"); router.use(bodyParser.json({ type: 'application/json' }));
global.database.muzic.get(
(err, data) => { const unknownError = (err) => {
data = data[0]?.data; const stackId = new Date().getTime();
if (err) { let errorLoggingFolder = config().error_logs_folder;
res.send(Buffer.from([])); errorLoggingFolder = !["/", "\\"].includes(errorLoggingFolder.at(-1))
} else { ? errorLoggingFolder + "/"
res.send(!data ? Buffer.from([]) : data); : errorLoggingFolder;
} fs.writeFileSync(
}, `${errorLoggingFolder}error_${stackId}.log`,
{ where: { id: !Number.isInteger(+req.query.id) ? 0 : +req.query.id } }, `ERROR:
Date: ${new Date()}
Name: ${err.name}
Message: ${err.message}
Stack:
${err.stack}`,
); );
}
// Подгрузка с файла return {
router.use("/:method_name", async (req, res, next, ...etc) => { error: "UNKNOWN_ERROR",
if (req.params.method_name === "muzic") { details: {
muzicLoad(req, res); trace_id: stackId,
return; },
} };
};
try { // Загрузка музыки при помощи спец. метода
const methodFunct = require(`./methods/${req.params.method_name}`); router.get('/music', async (req, res) => {
response(methodFunct, req, res); res.setHeader("Content-Type", "audio/mpeg");
} catch (e) { database.music.findAll({
if (e.message.includes("Cannot find module")) { where: { id: !Number.isInteger(+req.query.id) ? 0 : +req.query.id }, raw: true
const ApiError = require("./errorClass"); }).then((data) => {
res.status(400).sendModed( data = data[0]?.data;
await response( res.send(!data ? Buffer.from([]) : data);
(req, res) => { }).catch((err) => {
throw new ApiError("METHOD_NOT_FOUNDED"); res.send(Buffer.from([]));
}, });
req, });
res, // middleware (Обработка ошибок типа ответа)
), router.use((req, res, next) => {
); const unknownResponseFormat = "UNKNOWN_RESPONSE_FORMAT";
} else { if (!res.result) {
response( return res.status(400).send(unknownResponseFormat);
async () => {
throw e;
},
req,
res,
);
}
} }
next();
/*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(
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; module.exports = router;

View File

@ -1,87 +1,75 @@
const ApiError = require("../errorClass"); const ApiError = require("../errorClass");
const config = require("../../../config-handler"); const config = require("../../../config-handler");
const database = require("../../../database");
const apiTypes = require("../typeChecker");
async function isAuthorExists(authorId) { const router = require('express').Router();
if (!authorId) return false;
return (
(
await global.database.authors
.promiseMode()
.get({ where: { id: authorId } })
).length !== 0
);
}
async function checkSyntaxArgs(req, res) { async function checkSyntaxArgs(req, res) {
return !!( if (req.body === undefined || !req.headers['content-type'].includes('json')) {
// Проверка поля type. throw new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'create-item', no_post : false });
["author", "muzic"].indexOf(req.json?.type) !== -1 && // Проверка поля name. }
typeof req.json.name === "string" && const checker = new apiTypes.TypeChecker();
req.json.name.length >= 2 &&
(req.json.type === "muzic" await checker.checkRequired(req.body.type, apiTypes.ItemType, 'type');
? true await checker.checkRequired(req.body.name, apiTypes.NameType, 'name');
: config().authors_blacklist.filter((blacklisted) => {
return req.json.name if (req.body.type === 'music') {
.toLowerCase() await checker.checkRequired(req.body.author_id, apiTypes.AuthorType, 'author_id');
.includes(blacklisted.toLowerCase()); await checker.checkAdditional(req.body.data, apiTypes.Base64FileType, 'data');
}).length === 0) && // Дополнительные поля для muzic }
(req.json.type === "muzic"
? !!( const result = await checker.calculate();
// Для `muzic` if (!result.success) {
( throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result);
// Проверка поля author_id. }
(await isAuthorExists(req.json.author_id)) && // Проверка поля data. (Передаётся либо ничего, либо строка с base64)
(req.json.data === undefined
? true
: typeof req.json.data === "string")
)
)
: true)
);
} }
module.exports = async (req, res) => { router.post('/create-item', async (req, res, next) => {
if (req.json === undefined) { try {
// console.log(req.headers); await checkSyntaxArgs(req, res);
throw new ApiError("METHOD_MUST_BE_POST_JSON", {
request_method: req.method, if (req.body.type === "author") {
"content-type": !req.headers["content-type"] if (
? null config().authors_blacklist.filter((blacklisted) => {
: req.headers["content-type"], 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))) { catch (err) {
throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { return next(err);
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,
}
: {}),
},
});
} }
if (req.json.type === "author") { });
let result = await global.database.authors.promiseMode().add({
name: req.json.name, router.use('/create-item', async (req, res, next) => {
time: Math.round(new Date().getTime() / 1000), try {
}); return next(new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'create-item', no_post : true }));
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;
} }
}; catch (err) {
return next(err);
}
});
module.exports = router;

View File

@ -1,90 +1,88 @@
const ApiError = require("../errorClass"); const ApiError = require("../errorClass");
const database = require("../../../database");
const config = require("../../../config-handler");
const apiTypes = require("../typeChecker");
async function isAuthorExists(id) { const router = require('express').Router();
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;
}
async function checkSyntaxArgs(req, res) { async function checkSyntaxArgs(req, res) {
return !!( if (req.body === undefined || !req.headers['content-type'].includes('json')) {
// Проверка поля type throw new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'edit-item', no_post : false });
["author", "muzic"].includes(req.json.type) && // Проверка поля name }
(req.json.name === undefined const checker = new apiTypes.TypeChecker();
? true
: typeof req.json.name === "string" && req.json.name.length >= 2) && // Проверка id await checker.checkRequired(req.body.type, apiTypes.ItemType, 'type');
(req.json.type === "author" await checker.checkAdditional(req.body.name, apiTypes.NameType, 'name');
? await isAuthorExists(req.json.id)
: await isMuzicExists(req.json.id)) && // Проверка при type=muzic if (req.body.type === 'music') {
(req.json.type === "muzic" await checker.checkRequired(req.body.id, apiTypes.MusicType, 'id');
? // Проверка поля author_id
(req.json.author_id === undefined await checker.checkAdditional(req.body.author_id, apiTypes.AuthorType, 'author_id');
? true await checker.checkAdditional(req.body.data, apiTypes.Base64FileType, 'data');
: await isAuthorExists(req.json.author_id)) && // Проверка поля data. (Передаётся либо ничего, либо строка с base64) }
((req.json.data === undefined || req.json.data === null) ? true : typeof req.json.data === "string") else if (req.body.type === 'author') {
: true) 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) => { router.post('/edit-item', async (req, res, next) => {
if (req.method !== "POST") { try {
throw new ApiError("METHOD_MUST_BE_POST_JSON", { await checkSyntaxArgs(req, res);
request_method: req.method,
});
}
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,
}
: {}),
},
});
}
if (req.json.type === "author") { if (req.body.type === "author") {
await global.database.authors.promiseMode().edit( if (
{ config().authors_blacklist.filter((blacklisted) => {
name: req.json.name, return req.body.name
}, .toLowerCase()
{ .includes(blacklisted.toLowerCase());
where: { }).length !== 0
id: req.json.id, ) {
throw new ApiError("AUTHOR_BLACKLISTED");
}
await database.authors.update(
{
name: req.body.name,
}, },
}, {
); where: {
} else { id: req.body.id,
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,
}, },
}, );
); } 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");
} }
return "ok"; catch (err) {
}; return next(err);
}
});
router.use('/edit-item', async (req, res, next) => {
try {
return next(new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'edit-item', no_post : true }));
}
catch (err) {
return next(err);
}
});
module.exports = router;

View File

@ -1,81 +1,109 @@
const { Op } = require("sequelize");
const ApiError = require("../errorClass"); const ApiError = require("../errorClass");
const database = require("../../../database");
const apiTypes = require("../typeChecker");
const router = require('express').Router();
async function checkSyntaxArgs(req, res) { async function checkSyntaxArgs(req, res) {
return !!( const checker = new apiTypes.TypeChecker(false);
// Проверка поля offset
(req.query.id === undefined ? true : Number.isInteger(+req.query.offset)) && // Проверка поля offset await checker.checkAdditional(req.query.id, apiTypes.AuthorType, 'id');
(req.query.offset === undefined await checker.checkAdditional(req.query.offset, apiTypes.IntType, 'offset');
? true await checker.checkAdditional(req.query.count, apiTypes.IntType, 'count');
: Number.isInteger(+req.query.offset)) && // Проверка поля count await checker.checkAdditional(req.query.min_date, apiTypes.IntType, 'min_date');
(req.query.count === undefined await checker.checkAdditional(req.query.max_date, apiTypes.IntType, 'max_date');
? true await checker.checkAdditional(req.query.q, apiTypes.StrType, 'q');
: Number.isInteger(+req.query.count)) && // Проверка поля min_date
(req.query.min_date === undefined const result = await checker.calculate();
? true if (!result.success) {
: Number.isInteger(+req.query.min_date)) && // Проверка поля max_date throw new ApiError("UNSYNTAX_PARAMS_OR_MISSED_REQUIRED_PARAMS", result);
(req.query.max_date === undefined }
? true
: Number.isInteger(+req.query.max_date)) && // Проверка поля q. (Ключевые слова для поиска)
true // (Проверки нет, query всегда строка, необязательный параметр)
);
} }
module.exports = async (req, res) => { router.get('/get-authors', async (req, res, next) => {
if (req.method !== "GET") { try {
throw new ApiError("METHOD_MUST_BE_GET", { await checkSyntaxArgs(req, res);
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 offset = req.query.offset === undefined ? 0 : +req.query.offset;
const countFromNull = const countFromNull =
req.query.count === undefined ? undefined : offset + +req.query.count; req.query.count === undefined ? undefined : offset + +req.query.count;
let result = ( // Выборка по времени
await global.database.authors.promiseMode().get( const requiredTimeSearch = req.query.min_date || req.query.max_date;
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) {
const minDate = req.query.min_date === undefined ? 0 : +req.query.min_date; const minDate = req.query.min_date === undefined ? 0 : +req.query.min_date;
const maxDate = const maxDate = req.query.max_date === undefined ? 9999999999 : +req.query.max_date;
req.query.max_date === undefined ? 1e32 : +req.query.max_date;
result = result.filter((res) => minDate <= res.date && res.date <= maxDate); 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,
}));
if (req.query?.q !== undefined) { if (req.query?.q !== undefined) {
result = result.filter((i) => { result = result.filter((i) => {
const search = req.query.q.toLowerCase(); const search = req.query.q.toLowerCase();
return i.name.toLowerCase().includes(search); 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); router.use('/get-authors', async (req, res, next) => {
return { try {
count: result.length, return next(new ApiError("METHOD_MUST_BE_GET", { method : 'get-authors' }));
items: result, }
}; catch (err) {
}; return next(err);
}
});
module.exports = router;

127
api/v1/methods/get-music.js Normal file
View File

@ -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;

View File

@ -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,
};
};

30
api/v1/methods/index.js Normal file
View File

@ -0,0 +1,30 @@
const router = require('express').Router();
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;

View File

@ -1,72 +1,68 @@
const ApiError = require("../errorClass"); const ApiError = require("../errorClass");
const config = require("../../../config-handler"); const database = require("../../../database");
const apiTypes = require("../typeChecker");
async function isAuthorExists(authorId) { const router = require('express').Router();
if (!authorId) return false;
return (
(
await global.database.authors
.promiseMode()
.get({ where: { id: authorId } })
).length !== 0
);
}
async function checkSyntaxArgs(req, res) { async function checkSyntaxArgs(req, res) {
return !!( if (req.body === undefined || !req.headers['content-type'].includes('json')) {
// Проверка поля type. throw new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'remove-item', no_post : false });
["author", "muzic"].indexOf(req.json?.type) !== -1 && // Проверка поля id. }
(Number.isInteger(req.json.id) && req.json.type === "author" const checker = new apiTypes.TypeChecker();
? await isAuthorExists(req.json.id)
: true) 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) => { router.post('/remove-item', async (req, res, next) => {
if (req.json === undefined) { try {
// console.log(req.headers); await checkSyntaxArgs(req, res);
throw new ApiError("METHOD_MUST_BE_POST_JSON", {
request_method: req.method, if (req.body.type === "author") {
"content-type": !req.headers["content-type"] // Удаляем всю музыку этого исполнителя
? null await database.music.destroy({
: req.headers["content-type"], 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))) { catch (err) {
throw new ApiError("INVALID_OR_UNSYNTAX_PARAMS", { return next(err);
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,
}
: {}),
},
});
} }
if (req.json.type === "author") { });
// Удаляем всю музыку этого исполнителя
await global.database.muzic.promiseMode().remove({ router.use('/remove-item', async (req, res, next) => {
where: { try {
author_id: req.json.id, return next(new ApiError("METHOD_MUST_BE_POST_JSON", { method : 'remove-item', no_post : true }));
},
});
// Удаляем исполнителя
await global.database.authors.promiseMode().remove({
where: {
id: req.json.id,
},
});
} else {
await global.database.muzic.promiseMode().remove({
where: {
id: req.json.id,
},
});
} }
return "ok"; catch (err) {
}; return next(err);
}
});
module.exports = router;

View File

@ -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);
}
};

203
api/v1/typeChecker.js Normal file
View File

@ -0,0 +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;
}
async checkNoStrict () {
return true;
}
async checkStrict () {
return true;
}
}
class StrType extends ParamType {
async checkStrict () {
return typeof this.value === 'string';
}
async checkNoStrict () {
return typeof this.value === 'string';
}
}
class IntType extends ParamType {
async checkNoStrict () {
return Number.isInteger(+this.value);
}
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,
ItemType, NameType, AuthorType, AuthorsType, MusicType, Base64FileType,
TypeChecker
};

View File

@ -8,7 +8,7 @@
}, },
"database" : { "database" : {
"port" : 5433, "port" : 5433,
"address" : "localhost", "address" : "localhost",
"username" : "postgres", "username" : "postgres",
"password" : "root", "password" : "root",
"database" : "kodex-db" "database" : "kodex-db"
@ -20,5 +20,7 @@
"authors_blacklist" : [ "authors_blacklist" : [
"Монеточка", "Monetochka" "Монеточка", "Monetochka"
] ],
"request_size_limit" : "150mb"
} }

View File

@ -1,6 +1,7 @@
const { Sequelize, DataTypes } = require("sequelize"); const { Sequelize, DataTypes } = require("sequelize");
const config = require('./config-handler');
class Table { /*class Table {
constructor(model) { constructor(model) {
this.model = model; this.model = model;
} }
@ -58,7 +59,7 @@ class TablePromise extends Table {
async edit(data, condition) { async edit(data, condition) {
return await this.model.update(data, condition); return await this.model.update(data, condition);
} }
} }*/
class Database { class Database {
constructor(address, port, username, password, database) { constructor(address, port, username, password, database) {
@ -67,62 +68,83 @@ class Database {
); );
this.sequelize.authenticate().then( this.sequelize.authenticate().then(
() => { () => {
this.authors = new Table( // this.authors = new Table(
this.sequelize.define( // this.sequelize.define(
"authors", // "authors",
{ // {
id: { // id: {
type: DataTypes.INTEGER, // type: DataTypes.INTEGER,
primaryKey: true, // primaryKey: true,
autoIncrement: true, // autoIncrement: true,
allowNull: false, // allowNull: false,
}, // },
name: { // name: {
type: DataTypes.TEXT, // type: DataTypes.TEXT,
allowNull: false, // allowNull: false,
}, // },
time: { // time: {
type: DataTypes.INTEGER, // type: DataTypes.INTEGER,
allowNull: false, // allowNull: false,
}, // },
// },
// {
// freezeTableName: true,
// timestamps: false,
// },
// ),
// );
this.authors = this.sequelize.define(
"authors",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
}, },
{ name: {
freezeTableName: true, type: DataTypes.TEXT,
timestamps: false, allowNull: false,
}, },
), time: {
); type: DataTypes.INTEGER,
this.muzic = new Table( allowNull: false,
this.sequelize.define(
"muzic",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
name: {
type: DataTypes.TEXT,
allowNull: false,
},
author_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
data: {
type: DataTypes.BLOB("long"),
},
time: {
type: DataTypes.INTEGER,
allowNull: false,
},
}, },
{ },
freezeTableName: true, {
timestamps: false, freezeTableName: true,
timestamps: false,
},
)
this.music = this.sequelize.define(
"music",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
}, },
), name: {
type: DataTypes.TEXT,
allowNull: false,
},
author_id: {
type: DataTypes.INTEGER,
allowNull: false,
},
data: {
type: DataTypes.BLOB("long"),
},
time: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
freezeTableName: true,
timestamps: false,
},
); );
console.log("Database successful connected!"); console.log("Database successful connected!");
}, },
@ -133,4 +155,10 @@ class Database {
} }
} }
module.exports = { Database }; module.exports = new Database(
config().database.address,
config().database.port,
config().database.username,
config().database.password,
config().database.database
);

Binary file not shown.

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>Kodex Muzic</title> <title>Kodex Music</title>
<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/main.css" rel="stylesheet"> <link href="/css/main.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css">
@ -10,7 +10,7 @@
<!-- <h3><center>Kodex Muzic</center></h3> --> <!-- <h3><center>Kodex Muzic</center></h3> -->
<nav class="navbar navbar-light" style="background-color: #e3f2fd;"> <nav class="navbar navbar-light" style="background-color: #e3f2fd;">
<div class="container-fluid"> <div class="container-fluid">
<span class="navbar-brand mb-0 h1">Kodex Muzic</span> <span class="navbar-brand mb-0 h1">Kodex Music</span>
</div> </div>
<p style="margin-left: 10px;"><b>Поиск:</b> <input type="text" placeholder="Поиск по ключевым словам" class="form-control" id="search-input" style="width: 250%"></p> <p style="margin-left: 10px;"><b>Поиск:</b> <input type="text" placeholder="Поиск по ключевым словам" class="form-control" id="search-input" style="width: 250%"></p>
</nav> </nav>

View File

@ -18,13 +18,13 @@ class Api {
xhr.send(); xhr.send();
} }
getMuzic(params, cb) { getMusic(params, cb) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
params = Object.entries(params) params = Object.entries(params)
.map(([key, value]) => `${key}=${value}`) .map(([key, value]) => `${key}=${value}`)
.join("&"); .join("&");
params = params !== "" ? `&${params}` : ""; 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) { xhr.onreadystatechange = function (event) {
//console.log(event); //console.log(event);
if (this.readyState != 4) return; if (this.readyState != 4) return;
@ -33,7 +33,7 @@ class Api {
xhr.send(); xhr.send();
} }
deleteMuzic(id, cb) { deleteMusic(id, cb) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", `/api/v/1.0/remove-item?response_format=json`, true); xhr.open("POST", `/api/v/1.0/remove-item?response_format=json`, true);
xhr.setRequestHeader("Content-type", "application/json; charset=utf-8"); xhr.setRequestHeader("Content-type", "application/json; charset=utf-8");
@ -47,13 +47,13 @@ class Api {
}; };
xhr.send( xhr.send(
JSON.stringify({ JSON.stringify({
type: "muzic", type: "music",
id, id,
}), }),
); );
} }
editMuzic(id, name, data, author_id, cb) { editMusic(id, name, data, author_id, cb) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", `/api/v/1.0/edit-item?response_format=json`, true); xhr.open("POST", `/api/v/1.0/edit-item?response_format=json`, true);
xhr.setRequestHeader("Content-type", "application/json; charset=utf-8"); xhr.setRequestHeader("Content-type", "application/json; charset=utf-8");
@ -67,7 +67,7 @@ class Api {
}; };
xhr.send( xhr.send(
JSON.stringify({ JSON.stringify({
type: "muzic", type: "music",
id, id,
name, name,
author_id, 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; data = data === null ? undefined : data;
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("POST", `/api/v/1.0/create-item?response_format=json`, true); xhr.open("POST", `/api/v/1.0/create-item?response_format=json`, true);
@ -84,11 +84,12 @@ class Api {
xhr.onreadystatechange = function (event) { xhr.onreadystatechange = function (event) {
//console.log(event); //console.log(event);
if (this.readyState != 4) return; if (this.readyState != 4) return;
console.log(this.responseText);
cb(JSON.parse(this.responseText).response); cb(JSON.parse(this.responseText).response);
}; };
xhr.send( xhr.send(
JSON.stringify({ JSON.stringify({
type: "muzic", type: "music",
name, name,
author_id, author_id,
data, data,

View File

@ -10,10 +10,10 @@ function showAudio(settingsAuthors, settingsAudio) {
showAudio.settingsAuthors = settingsAuthors; showAudio.settingsAuthors = settingsAuthors;
showAudio.settingsAudio = settingsAudio; showAudio.settingsAudio = settingsAudio;
const step2 = (muzic) => { const step2 = (music) => {
//console.log(muzic.items); //console.log(music.items);
const showedAuthors = [ 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) => { [...document.getElementsByClassName("author-element")].forEach((el) => {
if ( if (
@ -22,7 +22,7 @@ function showAudio(settingsAuthors, settingsAudio) {
) { ) {
if (!showedAuthors.includes(el.id)) { if (!showedAuthors.includes(el.id)) {
const id = el.id.slice(7); const id = el.id.slice(7);
document.getElementById(`loader-muzic-${id}`).innerHTML = document.getElementById(`loader-music-${id}`).innerHTML =
"<p><b>Пусто. Добавьте произведения</b></p>"; "<p><b>Пусто. Добавьте произведения</b></p>";
} }
return; return;
@ -30,48 +30,48 @@ function showAudio(settingsAuthors, settingsAudio) {
el.hidden = !showedAuthors.includes(el.id); el.hidden = !showedAuthors.includes(el.id);
}); });
muzic.items music.items
.sort((a, b) => a.date - b.date) .sort((a, b) => a.date - b.date)
.forEach((muzic) => { .forEach((music) => {
document.getElementById( document.getElementById(
`loader-muzic-${muzic.author_id}`, `loader-music-${music.author_id}`,
).hidden = true; ).hidden = true;
if (muzic.is_data_exists) if (music.is_data_exists)
document.getElementById( document.getElementById(
`playlist-author-${muzic.author_id}`, `playlist-author-${music.author_id}`,
).innerHTML += `<p><div id="muzic-item-${muzic.id}"> ).innerHTML += `<p><div id="music-item-${music.id}">
<h6><button class="btn btn-secondary" id="edit-muzic-item-${ <h6><button class="btn btn-secondary" id="edit-music-item-${
muzic.id music.id
}" onclick='showEditMuzic(${muzic.id}, ${JSON.stringify( }" onclick='showEditMusic(${music.id}, ${JSON.stringify(
muzic.name, music.name,
)}, ${ )}, ${
muzic.is_data_exists music.is_data_exists
});'><i class="bi bi-pencil-square"></i></button> ${muzic.name}</h6> });'><i class="bi bi-pencil-square"></i></button> ${music.name}</h6>
<button class="btn btn-primary play-button" onclick="playMuzic(${ <button class="btn btn-primary play-button" onclick="playMusic(${
muzic.id music.id
})" id="play-${muzic.id}">Прослушать</button> })" id="play-${music.id}">Прослушать</button>
<button class="btn btn-primary pause-button" onclick="" id="pause-${ <button class="btn btn-primary pause-button" onclick="" id="pause-${
muzic.id music.id
}" disabled>Пауза</button> }" disabled>Пауза</button>
<input class="road-muzic" type="range" id="road-${ <input class="road-music" type="range" id="road-${
muzic.id music.id
}" name="stop" min="0" max="0" value="0" step="0.01" style="width: 70%;"></label> }" name="stop" min="0" max="0" value="0" step="0.01" style="width: 70%;"></label>
<p style="margin-top: 7px;"><input type="checkbox" class="form-check-input" id="repeat-mode-${ <p style="margin-top: 7px;"><input type="checkbox" class="form-check-input" id="repeat-mode-${
muzic.id music.id
}"> }">
<label class="form-check-label" for="exampleCheck1">Повторять</label></p> <label class="form-check-label" for="exampleCheck1">Повторять</label></p>
</div></p>`; </div></p>`;
else else
document.getElementById( document.getElementById(
`playlist-author-${muzic.author_id}`, `playlist-author-${music.author_id}`,
).innerHTML += `<p><div id="muzic-item-${muzic.id}"> ).innerHTML += `<p><div id="music-item-${music.id}">
<h6>${muzic.name}</h6> <h6>${music.name}</h6>
<button class="btn btn-secondary" onclick='showEditMuzic(${ <button class="btn btn-secondary" onclick='showEditMusic(${
muzic.id music.id
}, ${JSON.stringify(muzic.name)}, ${ }, ${JSON.stringify(music.name)}, ${
muzic.is_data_exists music.is_data_exists
});' id="add-file-${muzic.id}" id="edit-muzic-item-${ });' id="add-file-${music.id}" id="edit-music-item-${
muzic.id music.id
}">Добавить звуковой файл</button> }">Добавить звуковой файл</button>
</div></p>`; </div></p>`;
}); });
@ -89,43 +89,43 @@ function showAudio(settingsAuthors, settingsAudio) {
<hr/> <hr/>
<a href='#' onclick="toggleShow(${author.id})">Показать/Скрыть</a> <a href='#' onclick="toggleShow(${author.id})">Показать/Скрыть</a>
<div id="playlist-author-${author.id}"> <div id="playlist-author-${author.id}">
<p id="loader-muzic-${author.id}">Загрузка..</p> <p id="loader-music-${author.id}">Загрузка..</p>
</div> </div>
<div id="add-muzic-${author.id}" style="margin-bottom: 15px;"><center><button class="btn btn-primary" id="add-muzic-button-${author.id}">Добавить произведение</button></center></div> <div id="add-music-${author.id}" style="margin-bottom: 15px;"><center><button class="btn btn-primary" id="add-music-button-${author.id}">Добавить произведение</button></center></div>
</div>`; </div>`;
const addMuzicButtonFunct = () => const addMusicButtonFunct = () =>
setTimeout( setTimeout(
() => () =>
(document.getElementById( (document.getElementById(
`add-muzic-button-${author.id}`, `add-music-button-${author.id}`,
).onclick = async () => { ).onclick = async () => {
// console.log('>>', author.id); // console.log('>>', author.id);
document.getElementById( document.getElementById(
`add-muzic-${author.id}`, `add-music-${author.id}`,
).innerHTML = `<p><input class="form-control" type="text" placeholder="Введите название произведения" id="muzic-name-input-${author.id}" style="width: 90%"></p> ).innerHTML = `<p><input class="form-control" type="text" placeholder="Введите название произведения" id="music-name-input-${author.id}" style="width: 90%"></p>
<p><button class="btn btn-primary" id="attach-file-${author.id}">Добавить файл (.mp3)</button> <p><button class="btn btn-primary" id="attach-file-${author.id}">Добавить файл (.mp3)</button>
<button class="btn btn-primary" id="add-muzic-act-${author.id}">Добавить произведение</button> <button class="btn btn-primary" id="add-music-act-${author.id}">Добавить произведение</button>
<button class="btn btn-danger" id="cancel-add-muzic-${author.id}">Отменить</button></p>`; <button class="btn btn-danger" id="cancel-add-music-${author.id}">Отменить</button></p>`;
setTimeout(() => { setTimeout(() => {
document.getElementById( document.getElementById(
`cancel-add-muzic-${author.id}`, `cancel-add-music-${author.id}`,
).onclick = function () { ).onclick = function () {
document.getElementById( document.getElementById(
`add-muzic-${author.id}`, `add-music-${author.id}`,
).innerHTML = `<center><button class="btn btn-primary" id="add-muzic-button-${author.id}">Добавить произведение</button></center>`; ).innerHTML = `<center><button class="btn btn-primary" id="add-music-button-${author.id}">Добавить произведение</button></center>`;
addMuzicButtonFunct(); addMusicButtonFunct();
}; };
document.getElementById(`add-muzic-act-${author.id}`).onclick = document.getElementById(`add-music-act-${author.id}`).onclick =
function () { function () {
if ( if (
document.getElementById(`muzic-name-input-${author.id}`) document.getElementById(`music-name-input-${author.id}`)
.value.length >= 2 .value.length >= 2
) { ) {
api.createMuzic( api.createMusic(
author.id, author.id,
document.getElementById( document.getElementById(
`muzic-name-input-${author.id}`, `music-name-input-${author.id}`,
).value, ).value,
document.getElementById(`attach-file-${author.id}`) document.getElementById(`attach-file-${author.id}`)
.filedata, .filedata,
@ -134,7 +134,7 @@ function showAudio(settingsAuthors, settingsAudio) {
}, },
); );
document.getElementById( document.getElementById(
`add-muzic-${author.id}`, `add-music-${author.id}`,
).innerHTML = "Добавление файла..."; ).innerHTML = "Добавление файла...";
} }
}; };
@ -178,9 +178,9 @@ function showAudio(settingsAuthors, settingsAudio) {
}), }),
0, 0,
); );
addMuzicButtonFunct(); addMusicButtonFunct();
}); });
api.getMuzic(settingsAudio, step2); api.getMusic(settingsAudio, step2);
document.getElementById("app-window").innerHTML += document.getElementById("app-window").innerHTML +=
'<div id="add-author" style="margin-bottom: 15px;"><center><button class="btn btn-primary" id="add-author-button" onclick="addAuthorMenu();">Добавить исполнителя</button></center></div>'; '<div id="add-author" style="margin-bottom: 15px;"><center><button class="btn btn-primary" id="add-author-button" onclick="addAuthorMenu();">Добавить исполнителя</button></center></div>';
}); });

View File

@ -83,7 +83,7 @@ function addAuthorMenu() {
setTimeout(authorMenuButtonInit, 5); setTimeout(authorMenuButtonInit, 5);
} }
function selectFile(muzicId) { function selectFile(musicId) {
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
input.accept = "audio/mpeg"; input.accept = "audio/mpeg";
@ -106,22 +106,22 @@ function selectFile(muzicId) {
reader.onload = function () { reader.onload = function () {
// console.log('reader.result', reader.result); // console.log('reader.result', reader.result);
if (file.type === "audio/mpeg") { if (file.type === "audio/mpeg") {
document.getElementById(`edit-attach-file-${muzicId}`).filedata = document.getElementById(`edit-attach-file-${musicId}`).filedata =
arrayBufferToBase64(reader.result); arrayBufferToBase64(reader.result);
document.getElementById(`edit-attach-file-${muzicId}`).innerText = document.getElementById(`edit-attach-file-${musicId}`).innerText =
"Загружен файл: " + file.name; "Загружен файл: " + file.name;
document.getElementById(`delete-attach-${muzicId}`).disabled = false; document.getElementById(`delete-attach-${musicId}`).disabled = false;
} }
// console.log(arrayBufferToBase64(reader.result)); // console.log(arrayBufferToBase64(reader.result));
}; };
}; };
} }
function editMuzic(muzicId, muzicName) { function editMusic(musicId, musicName) {
api.editMuzic( api.editMusic(
muzicId, musicId,
muzicName, musicName,
document.getElementById(`edit-attach-file-${muzicId}`).filedata, document.getElementById(`edit-attach-file-${musicId}`).filedata,
undefined, undefined,
(res) => { (res) => {
showAudio(showAudio.settingsAuthors, showAudio.settingsAudio); showAudio(showAudio.settingsAuthors, showAudio.settingsAudio);
@ -129,57 +129,57 @@ function editMuzic(muzicId, muzicName) {
); );
} }
function removeMuzic(muzicId) { function removeMusic(musicId) {
api.deleteMuzic(muzicId, (res) => { api.deleteMusic(musicId, (res) => {
showAudio(showAudio.settingsAuthors, showAudio.settingsAudio); showAudio(showAudio.settingsAuthors, showAudio.settingsAudio);
}); });
} }
function bindShowEditMuzicButtons(muzicId, muzicName, dataExists) { function bindShowEditMusicButtons(musicId, musicName, dataExists) {
document.getElementById(`cancel-edit-muzic-${muzicId}`).onclick = () => { document.getElementById(`cancel-edit-music-${musicId}`).onclick = () => {
document.getElementById(`muzic-item-${muzicId}`).innerHTML = dataExists document.getElementById(`music-item-${musicId}`).innerHTML = dataExists
? `<h6><button class="btn btn-secondary" id="edit-muzic-item-${muzicId}" onclick='showEditMuzic(${muzicId}, ${JSON.stringify( ? `<h6><button class="btn btn-secondary" id="edit-music-item-${musicId}" onclick='showEditMusic(${musicId}, ${JSON.stringify(
muzicName, musicName,
)}, ${dataExists});'><i class="bi bi-pencil-square"></i></button> ${muzicName}</h6> )}, ${dataExists});'><i class="bi bi-pencil-square"></i></button> ${musicName}</h6>
<button class="btn btn-primary play-button" onclick="playMuzic(${muzicId})" id="play-${muzicId}">Прослушать</button> <button class="btn btn-primary play-button" onclick="playMusic(${musicId})" id="play-${musicId}">Прослушать</button>
<button class="btn btn-primary pause-button" onclick="" id="pause-${muzicId}" disabled>Пауза</button> <button class="btn btn-primary pause-button" onclick="" id="pause-${musicId}" disabled>Пауза</button>
<input class="road-muzic" type="range" id="road-${muzicId}" name="stop" min="0" max="0" value="0" step="0.01" style="width: 70%;"/>` <input class="road-music" type="range" id="road-${musicId}" name="stop" min="0" max="0" value="0" step="0.01" style="width: 70%;"/>`
: `<p><div id="muzic-item-${muzicId}"> : `<p><div id="music-item-${musicId}">
<h6>${muzicName}</h6> <h6>${musicName}</h6>
<button class="btn btn-secondary" onclick='showEditMuzic(${muzicId}, ${JSON.stringify( <button class="btn btn-secondary" onclick='showEditMusic(${musicId}, ${JSON.stringify(
muzicName, musicName,
)}, ${dataExists});' id="add-file-${muzicId}" id="edit-muzic-item-${muzicId}">Добавить звуковой файл</button> )}, ${dataExists});' id="add-file-${musicId}" id="edit-music-item-${musicId}">Добавить звуковой файл</button>
</div></p>`; </div></p>`;
}; };
document.getElementById(`edit-attach-file-${muzicId}`).onclick = () => document.getElementById(`edit-attach-file-${musicId}`).onclick = () =>
selectFile(muzicId); selectFile(musicId);
document.getElementById(`delete-attach-${muzicId}`).onclick = () => { document.getElementById(`delete-attach-${musicId}`).onclick = () => {
document.getElementById(`edit-attach-file-${muzicId}`).innerText = document.getElementById(`edit-attach-file-${musicId}`).innerText =
"Добавить файл (.mp3)"; "Добавить файл (.mp3)";
document.getElementById(`edit-attach-file-${muzicId}`).filedata = null; document.getElementById(`edit-attach-file-${musicId}`).filedata = null;
document.getElementById(`delete-attach-${muzicId}`).disabled = true; document.getElementById(`delete-attach-${musicId}`).disabled = true;
}; };
document.getElementById(`edit-muzic-${muzicId}`).onclick = () => document.getElementById(`edit-music-${musicId}`).onclick = () =>
editMuzic(muzicId, document.getElementById(`muzic-edit-name-input-${muzicId}`).value); editMusic(musicId, document.getElementById(`music-edit-name-input-${musicId}`).value);
document.getElementById(`delete-muzic-${muzicId}`).onclick = () => document.getElementById(`delete-music-${musicId}`).onclick = () =>
removeMuzic(muzicId); removeMusic(musicId);
} }
function showEditMuzic(muzicId, muzicName, dataExists) { function showEditMusic(musicId, musicName, dataExists) {
document.getElementById( document.getElementById(
`muzic-item-${muzicId}`, `music-item-${musicId}`,
).innerHTML = `<p><input class="form-control" type="text" placeholder="Введите название произведения" value=${JSON.stringify( ).innerHTML = `<p><input class="form-control" type="text" placeholder="Введите название произведения" value=${JSON.stringify(
muzicName, musicName,
)} id="muzic-edit-name-input-${muzicId}" style="width: 90%"></p> )} id="music-edit-name-input-${musicId}" style="width: 90%"></p>
<p><button class="btn btn-primary" id="edit-attach-file-${muzicId}">${ <p><button class="btn btn-primary" id="edit-attach-file-${musicId}">${
dataExists ? "Сменить файл (.mp3)" : "Добавить файл (.mp3)" dataExists ? "Сменить файл (.mp3)" : "Добавить файл (.mp3)"
}</button> }</button>
<button class="btn btn-danger" id="delete-attach-${muzicId}"${ <button class="btn btn-danger" id="delete-attach-${musicId}"${
dataExists ? "" : " disabled" dataExists ? "" : " disabled"
}>Удалить файл</button> }>Удалить файл</button>
<button class="btn btn-primary" id="edit-muzic-${muzicId}">Редактировать произведение</button> <button class="btn btn-primary" id="edit-music-${musicId}">Редактировать произведение</button>
<button class="btn btn-secondary" id="cancel-edit-muzic-${muzicId}">Отменить</button> <button class="btn btn-secondary" id="cancel-edit-music-${musicId}">Отменить</button>
<button class="btn btn-danger" id="delete-muzic-${muzicId}">Удалить произведение</button></p>`; <button class="btn btn-danger" id="delete-music-${musicId}">Удалить произведение</button></p>`;
setTimeout(() => bindShowEditMuzicButtons(muzicId, muzicName, dataExists), 5); setTimeout(() => bindShowEditMusicButtons(musicId, musicName, dataExists), 5);
} }

View File

@ -1,28 +1,31 @@
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.pause();
// pauseMusic(id);
audio.currentTime = 0; audio.currentTime = 0;
document.getElementById(`play-${id}`).innerText = "Прослушать"; document.getElementById(`play-${id}`).innerText = "Прослушать";
document.getElementById(`play-${id}`).disabled = false;
document.getElementById(`pause-${id}`).innerText = "Пауза";
document.getElementById(`pause-${id}`).disabled = true; document.getElementById(`pause-${id}`).disabled = true;
document.getElementById(`play-${id}`).onclick = () => playMuzic(id); document.getElementById(`play-${id}`).onclick = () => playMusic(id);
clearInterval(interval); clearInterval(interval);
document.getElementById(`road-${id}`).value = 0; document.getElementById(`road-${id}`).value = 0;
document.getElementById(`road-${id}`).max = 0; document.getElementById(`road-${id}`).max = 0;
} }
function pauseMuzic(id) { function pauseMusic(id) {
document.getElementById(`play-${id}`).disabled = true; document.getElementById(`play-${id}`).disabled = true;
document.getElementById(`pause-${id}`).innerText = "Продолжить"; document.getElementById(`pause-${id}`).innerText = "Продолжить";
document.getElementById(`pause-${id}`).onclick = () => resumeMuzic(id); document.getElementById(`pause-${id}`).onclick = () => resumeMusic(id);
audio.pause(); audio.pause();
} }
function resumeMuzic(id) { function resumeMusic(id) {
document.getElementById(`play-${id}`).disabled = false; document.getElementById(`play-${id}`).disabled = false;
document.getElementById(`pause-${id}`).innerText = "Пауза"; document.getElementById(`pause-${id}`).innerText = "Пауза";
document.getElementById(`pause-${id}`).onclick = () => pauseMuzic(id); document.getElementById(`pause-${id}`).onclick = () => pauseMusic(id);
audio.play(); audio.play();
} }
@ -30,12 +33,12 @@ function changeTime(value, id) {
audio.currentTime = value; audio.currentTime = value;
} }
function playMuzic(id) { function playMusic(id) {
audio.pause(); audio.pause();
audio.currentTime = 0; 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.onloadedmetadata = function () {
audio.muzId = id; audio.muzId = id;
document.getElementById(`play-${id}`).innerText = "Остановить"; document.getElementById(`play-${id}`).innerText = "Остановить";
@ -52,8 +55,8 @@ function playMuzic(id) {
}, 750); }, 750);
audio.muzTimer = interval; audio.muzTimer = interval;
document.getElementById(`play-${id}`).onclick = () => document.getElementById(`play-${id}`).onclick = () =>
stopMuzic(id, interval); stopMusic(id, interval);
document.getElementById(`pause-${id}`).onclick = () => pauseMuzic(id); document.getElementById(`pause-${id}`).onclick = () => pauseMusic(id);
document.getElementById(`road-${id}`).onchange = function () { document.getElementById(`road-${id}`).onchange = function () {
// console.log('value', this.value); // console.log('value', this.value);

View File

@ -1,40 +1,39 @@
const config = require("./config-handler"); const config = require("./config-handler");
const fs = require("fs"); const fs = require("fs");
function log(date, req, ip, res) { class Logger {
const requestBody = !req.byteBody.toString("utf-8") constructor () {
? "" this.isLoggerModeOn = config().logger_mode;
: ` if (this.isLoggerModeOn) {
~~~~~~~~~~~~~~ this.logId = `${(new Date()).getTime()}`;
[REQUEST BODY] this.loggerFolder = config().logger_folder;
~~~~~~~~~~~~~~ this.loggerFolder = !["/", "\\"].includes(this.loggerFolder.at(-1))
${req.byteBody.toString("utf-8")}`; ? this.loggerFolder + "/"
let action = `HTTP ${req.httpVersion} ${req.method} ${req.originalUrl} : this.loggerFolder;
~~~~~~~~~ fs.writeFile(
[HEADERS] `${this.loggerFolder}${this.logId}.log`, '',
~~~~~~~~~ {
${Object.entries(req.headers) encoding: "utf8"
.map(([header, value]) => header + ": " + value) },
.join("\n")}${requestBody}`; (err) => {
let response = !res.isError if (err) throw err;
? "" }
: `\n----------------\nError raised:\n----------------\n${JSON.stringify( );
res.responseData, }
)}`; }
//console.log(`================================\nREPORT\n================================\n\nIP: ${ip}\n----------------\nACTION:\n----------------\n${action}${response}`);
let loggerFolder = config().logger_folder;
loggerFolder = !["/", "\\"].includes(loggerFolder.at(-1))
? loggerFolder + "/"
: loggerFolder;
fs.writeFileSync( log (date, ip, action, isError = false) {
`${loggerFolder}${date.getTime()}.log`, if (this.isLoggerModeOn) fs.writeFile(
`================================\nREPORT\n================================\n\nIP: ${ip}\n----------------\nACTION:\n----------------\n${action}${response}`, `${this.loggerFolder}${this.logId}.log`, !isError ? `IP: ${ip}\nДата запроса: ${date}\nАктивное действие: ${action}` : `\nПроизошла ошибка: ${action}`,
); {
encoding : "utf8",
flag : "a"
},
(err) => {
if (err) throw err;
}
);
}
} }
module.exports = async (req, res, next) => { module.exports = { Logger };
// console.log('ip', req.ip);
log(new Date(), req, req.ip, res);
next();
};

112
package-lock.json generated
View File

@ -5,7 +5,6 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"body": "^5.1.0",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.18.2", "express": "^4.18.2",
@ -53,17 +52,6 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
}, },
"node_modules/body": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz",
"integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==",
"dependencies": {
"continuable-cache": "^0.3.1",
"error": "^7.0.0",
"raw-body": "~1.1.0",
"safe-json-parse": "~1.0.1"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.2", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
@ -87,23 +75,6 @@
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
} }
}, },
"node_modules/body/node_modules/bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
"integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ=="
},
"node_modules/body/node_modules/raw-body": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz",
"integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==",
"dependencies": {
"bytes": "1",
"string_decoder": "0.10"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/buffer-writer": { "node_modules/buffer-writer": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
@ -151,11 +122,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/continuable-cache": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz",
"integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA=="
},
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@ -232,14 +198,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/error": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz",
"integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==",
"dependencies": {
"string-template": "~0.2.1"
}
},
"node_modules/escape-html": { "node_modules/escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -789,11 +747,6 @@
} }
] ]
}, },
"node_modules/safe-json-parse": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz",
"integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A=="
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@ -979,16 +932,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
},
"node_modules/string-template": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
"integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw=="
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -1114,33 +1057,6 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
}, },
"body": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz",
"integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==",
"requires": {
"continuable-cache": "^0.3.1",
"error": "^7.0.0",
"raw-body": "~1.1.0",
"safe-json-parse": "~1.0.1"
},
"dependencies": {
"bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
"integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ=="
},
"raw-body": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz",
"integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==",
"requires": {
"bytes": "1",
"string_decoder": "0.10"
}
}
}
},
"body-parser": { "body-parser": {
"version": "1.20.2", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
@ -1192,11 +1108,6 @@
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
}, },
"continuable-cache": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz",
"integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA=="
},
"cookie": { "cookie": {
"version": "0.5.0", "version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@ -1256,14 +1167,6 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
}, },
"error": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz",
"integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==",
"requires": {
"string-template": "~0.2.1"
}
},
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -1662,11 +1565,6 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}, },
"safe-json-parse": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz",
"integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A=="
},
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@ -1786,16 +1684,6 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
}, },
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
},
"string-template": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
"integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw=="
},
"toidentifier": { "toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",

View File

@ -1,6 +1,5 @@
{ {
"dependencies": { "dependencies": {
"body": "^5.1.0",
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"express": "^4.18.2", "express": "^4.18.2",

64
packages_pgadmin_org.pub Normal file
View File

@ -0,0 +1,64 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFtyz58BEACgKbtY59R0mxs8rWJNAn1BWNXwhuTvELNCV6gZkMRGFP14tMop
d9VcUx5UWiulT5wysji63xhkNljmE90jJdlxZwZ+XtnmLzIqp6i29EkAIUt1AoxM
w2ipMhfuwE6WA6VYxQihu5z2IDOR1PdDUHF5cX/GZgBon/2A33rG5IKTcaNZzL0O
c3rS5VzOzwnp1FHPlR7PY7BRDNe8q1MrQq14tlgMTaYziNg2t2YwjuhNV6G33qGE
h390aUnO/eMWIPJzKoi4mE5mhEbh4L/7sFlcRUC6Vs1xa5Ab+L5y2xoDe2grraKD
u+XpGJaDPLunlhDSTUsp0HsoLVU4ne/HNbCAm2b25tKFcFTUwDH4Ekge1/bQLCvx
kB63MMLa/FgsJ0XAr8zKEQFrc89qJU4JuvadL4hAIqZ1ywFlwTOBaNfZHbW2Pt5f
prktIL5d5jIHAdQrFPvLqhhjhM03de6O6dS5lDeP8dTdqzMcqBkwFMmjZMeRAcoJ
vs0jJNc0fYwL3h2JSWQnIhsvcSe6gk8GFVRbCCy9UplK1K/5TWw+y3mtfWwUCUSW
nBIuUTV+5iG21o3rdZgfEjXJtBAWW/hKoVwBTe5Ir9yIqaomG5ul0Sn2EgOravns
AWe2nk4l9cno5CPhGunEtiOD8YQJHskk7/NMtnPegB5j4tprXGS/cK/5hQARAQAB
tDxQYWNrYWdlIE1hbmFnZXIgKFBhY2thZ2UgU2lnbmluZyBLZXkpIDxwYWNrYWdl
c0BwZ2FkbWluLm9yZz6JAk4EEwEKADgWIQToaX4u73bALTpjMneIgbKoIQl28gUC
W3LPnwIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCIgbKoIQl28ukfD/4y
3gGysVSJU1964mpi/4NtSTruQ+fx8rN1vY/cctdQVr1ltuJsDRyPgGpXIh9zeK0/
bkCreCcuGezm2WOUFR6Kf54zMWWbIrAPpbib7rYi8n50jz7SkCfSyJZgqO2bAPBU
MP6Y09mdLaB4jib9Y6nDhFgm2V0rO64yX/bznVjBzNXFjCTgbPoABU0Guy4yHGUF
HkQ7Hdg1QLhupMWlphlMJbeSxZJx0T6ApNvr2Qg+uFykSXbXjP2e/tXGb9NeHveT
tw/hD2yMPzXJZ4uQbk8mJWDfD87fHY4ZUVqLJtKiS3omePJ5FWMPnLl0PLICkvmh
mhoxyKFGjB+62/PpYwclZLR8iB7wn9tIA/q6BqP/BBhgmzpuh7ZOAU/zUZ5D+tHu
Q9X0e29iFs4nxewmSM1uCq++l+gGMFRMn0FPH8nyQS/EcB/qXXRc0J3Ja7VVfH5B
djmVvqTeJmwY+xGdLZAh/WZ9raWd/qbRGNIcYOyhHnvp719EQxYSiiJbIQcRLDbc
IBiUG322ubSiR4+saYfx4ixrHvx8QYbtagien0kkXtoouhhIuLxq/EADRb979ZvQ
1hnlUkSGHRN6mDNyLztRqy/iibZrSgX+iKR9lQYI5MnhchihgoN7jyFUiGTV/VSG
6oH2KHiTgUQawli9OirnBB1oekf2+QZfZUvnM+b+SokCMwQQAQoAHRYhBODEzuuC
ax/aT7Ro4CSt+q9pjxUZBQJbcs/+AAoJECSt+q9pjxUZ4fAP/iQpwcrUZrPp3WI3
hi3wHAe+L6E6LiWlhMEMlqfy/2/xOpDEniwy6IEbMV7+H8WSbFYnTBM6EJAWPCMK
ZAfkduuB6xqHllEuPuFY9O13+fV5bJMrW/ej3MbX2yz+wfa6LLORRBB/e4R34suz
mlSzQzRttPHejmpNicn0S2kA07kqdl/2I3KcsWM1a5GeRZAukDSMLI6orZAGR+r4
xKpdEiEMHfoxYYxujmQR9+jqYYPsuViHc3LtIwaKMjTiWBx7wUDF+qIl7bNkT7P9
4VudNU9hhzCcYSAt4qiDykTojbSlXSx/ltqoTRhVQlk1kFk2g1O1zHyVpuAkolje
5ZmRGa/ZFQWuSOd01n1QqiRLrQDXHKDBh+sUOqaF4Hby/pwZwcsXGLEzSI9uC5He
WAn/yomDJInpyYwGmT9FyD9YSd9QjpM1y2/w2+KMj1KRq7GvmZUONYaWp1+A0eCL
yhdsZ+bqdS6DVnh0qKT/8ulWUKe+sxiRyAaKBF0QacuuRiRFAfgWbQbsrEQGcDqL
q7lmtmOKa0GrBQJAvTMUkxrpMG6SIe+HsJJ3/u+pmTWg77oiQqTByQPPJF0/EgMV
g/JzstHdI+FA7Q/4DKJZ5NrPBpUUQC0h+Iex426C7gBtnGXQHYB2Nx6xCtwmpPZv
w5THrRb07Y59nZEZLEdcL8bbH/CZuQINBFtyz58BEACt0Hcb8t24ZXsGcOlVnElo
cMMo17IdyDvs1j2jJxrNTT6jkxlgwG+ojStsRvllRrG85Wq/FNI6LuBY3Ux+Ymda
Nm+p8CJiEDE/Gql4GPSNZ7fCiiopRyyFXg61VM72lWokAT9o9GaSU0/sM5WDeXvM
A5QIlAg6+jQ7+R0MMLHeH0GTMnF58KAFmE7T72+H1zPtvH3qeQlOt+PBMJVNhjiO
2MwU7NlUIKVz4Vn1JmxA1kCWEIxZyFS82XXKc9BXgPqwnk27lqBxdZzDWFki8SBn
DdvwTT/s0chtwekWN4t2RofK0w33TF7+MSQLxpWLr8igrQQvBq6LBfMqm8tQWHL2
VDORDg5kKIpZv4pNxxIFmu1VX+W01Oj2GV6AOJgX6jadMiRlHptkz7D/dmnqsCyf
DRCmLcwB3y2/behbV1+iW2bViUaFoQIt/XXm2Jo1YtskxZ7LDngDin88pU6jId1N
dxjP2rKUjm/dyH5jMn1engv71w16TH+GVr42ho+yOwOTYo8qKDAvQgI8I8e+MlkM
LRLpgmFiECmWCovJOQ2JHizqFOmr+eSbeg7o5VpWA+cb0sCdbGUX8Kv6i8zP/ayh
VnWg1oU5Q0HTgH9gQ0rzkR+Re2O5xSKuNYnqVOJv4eRzt1NPFgZZOw+PFMJlvEz4
ujGwA/OsdJNLQK/HEK75GwARAQABiQI2BBgBCgAgFiEE6Gl+Lu92wC06YzJ3iIGy
qCEJdvIFAltyz58CGwwACgkQiIGyqCEJdvKPUg//f2YJGHX9FaNkCpoEk51QW5sv
pqITO24Ig65mEVVyx1GPOR9BQnCJoXZrnhEv2d/BpijFE/cR/fHv9bmqc434waeZ
PyDyflWTn6+MQYMJJfszKdJFaaY4qPeaCcoh7GC2qw4I5MINfNVTcinOU52XZzt6
F+ENm4h8u6vbS+55sKXjRRxNMHbBlNMr0yylukdGrs3PTGEYtXEPBhms4Plz5uHj
wkvf+rti84z2qqdX6y0YWxtRBy0cGeo15NYA8kHJLIQeUYbkV20PC7Uooj29DpIs
RxDv7F2qZ3KIse8oiJTIubdM+O7zNhzMo+XSUY2HM6aWDLCjV5SuJVJUsPxA3aEK
ijn/PjmGkr4DKhiant0nIB/pzyKelNQJHO5fgCFuV72R9GIR7yBRG2AU5OwgHQdy
5F0/4/6LtNVWZMKy2lEYuyW8fm0rbC7G5Qbz0KhYZWxp3F20rO6679ViMuNQTwQf
HI9akdtFqFEFPuoHyT3VAMxzeUAcMXwBaPcHw1EOlX1kibaM5dbDVOfKEr6JNj4V
N00CeuM++rHJSTeM/gcxO+BWpzaNFF9MMrCBL74wiY+WJ7rogRf5Du7H2e0+w/XO
puIx3rGSO9VhVrVcoTHimJPuWH7j56wybLS/TCh6HI8soMjYLzxWbqvSyV0b4xfb
czb/7fY4Fah80eE59/M=
=E6/L
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -1,67 +1,40 @@
"use strict"; "use strict";
const express = require("express"); const express = require("express");
const bodyHand = require("body"); //const bodyHand = require("body");
const config = require("./config-handler"); const config = require("./config-handler");
const http = require("http"); // const http = require("http");
const { Database } = require("./database"); const { Database } = require("./database");
const { Logger } = require('./logger');
const bodyParser = require('body-parser');
const app = express(); const app = express();
// Выставляем адекватное ограничение на тело запроса
app.use(bodyParser.json({limit: config().request_size_limit}));
global.database = new Database( app.use(
config().database.address, (req, res, next) => {
config().database.port, if (req.query.response_format === 'json') {
config().database.username, res.result = function (data, isErr = false, code = 200) {
config().database.password, this.status(code).json(!isErr ? {
config().database.database, response : data
} : {
error : data
});
};
}
next();
}
); );
http.ServerResponse.prototype.errorModeOn = function () { app.use(async (req, res, next) => {
this.isError = true; req.logger = new Logger();
}; req.logger.log(
new Date(),
http.ServerResponse.prototype.bindNext = function (next) { req.ip,
this.next = next; `Пользователь обратился по пути: ${req.originalUrl} (HTTP ${req.httpVersion}; ${req.method})`
};
http.ServerResponse.prototype.sendModed = function (sendData) {
// Модифицируем res.send
if (sendData !== undefined && config().logger_mode) {
this.responseData = sendData;
require("./logger")(this.req, this, this.next);
}
this.send(sendData);
};
app.use((req, res, next) => {
res.bindNext(next);
next();
});
app.use((req, res, next) => {
// Для добавления оригинального тела запроса
const body = bodyHand(
req,
res,
{
limit: 9999999999,
cache: false,
encoding: "base64",
},
(err, body) => {
if (!err) {
req.byteBody = Buffer.from(body, "base64");
// Запись в req.json при json
if (!!req.headers && req.headers["content-type"]?.includes("json")) {
try {
req.json = JSON.parse(req.byteBody.toString("utf8"));
} catch (_e) {}
}
}
next();
},
); );
next();
}); });
app.use("/", require("./page-view")); app.use("/", require("./page-view"));