From a44c1bc964bcf2051d20d79322ee86c7cb4c88e1 Mon Sep 17 00:00:00 2001 From: carlos-burelo Date: Tue, 9 Nov 2021 00:30:44 -0600 Subject: [PATCH] Update to API V2, restoring to ESM and modularizing functions --- nodemon.json | 6 - package.json | 23 +- src/{app.ts => app.js} | 3 +- src/{config.ts => config.js} | 13 +- src/controllers/controller.js | 67 +++ src/controllers/controller.ts | 553 -------------------- src/controllers/getAnime.js | 53 ++ src/controllers/getAnimes.js | 27 + src/controllers/getBy.js | 35 ++ src/controllers/getCategories.js | 25 + src/controllers/getEmision.js | 26 + src/controllers/getEmision.ts | 34 -- src/controllers/getEpisode.js | 48 ++ src/controllers/getGenders.js | 26 + src/controllers/getLastest.js | 24 + src/controllers/getLastest.ts | 32 -- src/controllers/getYears.js | 26 + src/controllers/index.js | 10 + src/controllers/index.ts | 2 - src/controllers/searchAnimes.js | 27 + src/index.js | 6 + src/index.ts | 11 - src/models/interfaces.ts | 79 --- src/routes/{api.routes.ts => api.routes.js} | 50 +- tsconfig.json | 71 --- yarn.lock | 199 +------ 26 files changed, 469 insertions(+), 1007 deletions(-) delete mode 100644 nodemon.json rename src/{app.ts => app.js} (68%) rename src/{config.ts => config.js} (62%) create mode 100644 src/controllers/controller.js delete mode 100644 src/controllers/controller.ts create mode 100644 src/controllers/getAnime.js create mode 100644 src/controllers/getAnimes.js create mode 100644 src/controllers/getBy.js create mode 100644 src/controllers/getCategories.js create mode 100644 src/controllers/getEmision.js delete mode 100644 src/controllers/getEmision.ts create mode 100644 src/controllers/getEpisode.js create mode 100644 src/controllers/getGenders.js create mode 100644 src/controllers/getLastest.js delete mode 100644 src/controllers/getLastest.ts create mode 100644 src/controllers/getYears.js create mode 100644 src/controllers/index.js delete mode 100644 src/controllers/index.ts create mode 100644 src/controllers/searchAnimes.js create mode 100644 src/index.js delete mode 100644 src/index.ts delete mode 100644 src/models/interfaces.ts rename src/routes/{api.routes.ts => api.routes.js} (61%) delete mode 100644 tsconfig.json diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index 252ab75..0000000 --- a/nodemon.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "watch": ["src"], - "ext": "ts", - "ignore": ["src/**/.spect.ts"], - "exec": "ts-node src/index.ts" -} diff --git a/package.json b/package.json index 3138b4a..d031ef2 100644 --- a/package.json +++ b/package.json @@ -1,33 +1,26 @@ { "name": "monoschinos-api-ts", - "version": "1.2.0", + "version": "2.0.0", "description": "", "main": "index.js", "scripts": { - "start": "node dist/index.js", - "build": "tsc ", - "dev": "nodemon" + "start": "node src/index.js", + "dev": "nodemon src/index.js" }, + "type": "module", "keywords": [], "author": "", "license": "ISC", "devDependencies": { - "@types/axios": "^0.14.0", - "@types/cheerio": "^0.22.28", - "@types/cors": "^2.8.10", - "@types/dotenv": "^8.2.0", - "@types/express": "^4.17.11", - "@types/morgan": "^1.9.2", - "nodemon": "^2.0.7", - "ts-node": "^9.1.1", - "typescript": "^4.2.3" + "nodemon": "^2.0.14" }, "dependencies": { - "axios": "^0.21.1", + "axios": "^0.24.0", "cheerio": "^1.0.0-rc.5", "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "node-html-parser": "^5.1.0" } } diff --git a/src/app.ts b/src/app.js similarity index 68% rename from src/app.ts rename to src/app.js index 6e302ca..034dee6 100644 --- a/src/app.ts +++ b/src/app.js @@ -1,9 +1,8 @@ import express from 'express'; import cors from 'cors'; import morgan from 'morgan'; -import routes from './routes/api.routes'; +import routes from './routes/api.routes.js'; const app = express(); app.use(cors(), morgan('dev')); -// app.use('/', express.static('docs')); app.use('/', routes); export default app; diff --git a/src/config.ts b/src/config.js similarity index 62% rename from src/config.ts rename to src/config.js index 8862d00..16ce151 100644 --- a/src/config.ts +++ b/src/config.js @@ -1,10 +1,6 @@ -const appConfig = { - host: process.env.HOST || 'localhost', - port: process.env.PORT || 5000, -}; const page = 'https://monoschinos2.com'; -const urls = { +export const urls = { main: page, emision: page + '/emision?page=', search: page + '/search?q=', @@ -15,4 +11,9 @@ const urls = { ova: page + '/categoria/ova', }; -export { urls, appConfig }; +/** + * @returns {string} + */ +export function getAttr(html, selector, attr) { + return html.querySelector(selector).attributes[attr]; +} diff --git a/src/controllers/controller.js b/src/controllers/controller.js new file mode 100644 index 0000000..8fa9280 --- /dev/null +++ b/src/controllers/controller.js @@ -0,0 +1,67 @@ +import cheerio from 'cheerio'; +import axios from 'axios'; +import { urls } from '../config.js'; + +export async function getBy(req, res, multiple) { + try { + let { gender, letter, category } = req.params; + + let { page } = req.query; + + if (!page) { + page = '1'; + } + + let bodyResponse; + + if (multiple) { + bodyResponse = await axios.get( + `${urls.main}/categoria/${category}/genero/${gender}?page=${page}` + ); + } else if (gender && !multiple) { + bodyResponse = await axios.get(`${urls.main}/genero/${gender}?page=${page}`); + } else if (letter && !multiple) { + bodyResponse = await axios.get(`${urls.main}/letra/${letter}?page=${page}`); + } else if (category && !multiple) { + bodyResponse = await axios.get(`${urls.main}/categoria/${category}?page=${page}`); + } + const $ = cheerio.load(bodyResponse.data); + + const animes = []; + + $('.animes .container .row article').each((i, e) => { + let el = $(e); + let id = el.find('.link-anime').attr('href'); + id = id.split('/')[4]; + let img = el.find('.link-anime .Image img').attr('src'); + let title = el.find('.link-anime .Title').text(); + let type = el.find('.link-anime .info .category').text(); + let year = el.find('.link-anime .info .fecha').text(); + + let anime = { + id, + img, + title, + type, + year, + }; + + animes.push(anime); + }); + + let totalPages = $('.pagination').children().length; + totalPages = $('.pagination').find('.page-item')[totalPages - 2]; + let pages = parseInt($(totalPages).text()); + + res.status(200).json({ + animes, + pages, + success: true, + }); + } catch (err) { + res.status(500).json({ + message: err.message, + success: false, + }); + } +} diff --git a/src/controllers/controller.ts b/src/controllers/controller.ts deleted file mode 100644 index 96f1844..0000000 --- a/src/controllers/controller.ts +++ /dev/null @@ -1,553 +0,0 @@ -import cheerio from 'cheerio'; -import axios from 'axios'; -import { urls } from '../config'; -import { SuggestionI, GenderI, AnimeSearchI, EpI } from '../models/interfaces'; -import { Request, Response } from 'express'; - -export async function getAnime(req: Request, res: Response) { - let noImage: string = 'https://monoschinos2.com/assets/img/no_image.png'; - let defaulImage: string = - 'https://image.freepik.com/free-vector/404-error-page-found_41910-364.jpg'; - try { - let { id } = req.params; - const response = await axios.get(`${urls.anime}/${id}`); - const $ = cheerio.load(response.data); - let i = $('.TPost.Serie.Single'); - let banner: string = i.find('.Banner img').attr('src'); - if (banner == noImage) { - banner = defaulImage; - } else { - banner = banner; - } - let title = i - .find('h1.Title') - .text() - .replace(/Sub Español/gi, '') - .trim(); - let sinopsis = i.find('.row .col-sm-9 .Description p').text(); - let status = i.find('.row .col-sm-9 .Type').text().trim(); - let date = i - .find('.row .col-sm-9 .after-title:nth-child(n)') - .text() - .match(/\d{4}-\d{2}-\d{2}/)[0]; - let type = i - .find('.row .col-sm-9 .after-title:nth-child(n)') - .text() - .match(/\|\s\w+/gi)[0] - .replace(/\|?\s?/, ''); - let cover = i.find('.Image img').attr('src'); - let genders: GenderI[] = []; - i.find('.generos a').each((i, e) => { - let el = $(e); - let title = el.text(); - let id = el.attr('href').split('/')[4]; - genders.push({ title, id }); - }); - let episodes: EpI[] = []; - i.find('.container .SerieCaps .item').each((i, e) => { - let el = $(e); - let episodeId = el.attr('href'); - episodeId = episodeId.split('/')[4]; - episodes.push({ - number: parseInt(episodeId.split('-').pop()), - id: episodeId, - }); - }); - if (!episodes || episodes.length == 0) { - episodes = []; - } - let sugestions: SuggestionI[] = []; - i.find('.container .row .col-12 .recom article').each((i, e) => { - let el = $(e); - let id = el.find('a').attr('href'); - id = id.split('/')[4]; - let title = el - .find('a .Title') - .text() - .replace(/ Sub Español/gi, ''); - let cover = el.find('a .Image img').attr('src'); - let year = parseInt(el.find('.fecha').text()); - sugestions.push({ - id, - title, - cover, - year, - }); - }); - res.json({ - title, - banner, - cover, - sinopsis, - status, - date, - type, - genders, - episodes, - sugestions, - }); - } catch (err) { - res.json({ - message: err.message, - success: false, - }); - } -} -export async function getAnimes(req: Request, res: Response) { - try { - let { page } = req.params; - !page ? (page = '1') : (page = page); - const bodyResponse = await axios.get(`${urls.main}/animes?page=${page}`); - const $ = cheerio.load(bodyResponse.data); - const animes = []; - $('.animes .container .row article').each((i, e) => { - let el = $(e); - - let id = el.find('a').attr('href'); - id = id.split('/')[4]; - let title = el.find('.Title').text(); - let img = el.find('.Image img').attr('src'); - let category = el.find('.category').text(); - category = category.substring(1, category.length); - let year = parseInt(el.find('.fecha').text()); - - const anime = { - id, - title, - img, - category, - year, - }; - - animes.push(anime); - }); - - let totalPages: any = $('.pagination').children().length; - totalPages = $('.pagination').find('.page-item')[totalPages - 2]; - let pages = parseInt($(totalPages).text()); - let current = parseInt(page); - res.status(200).json({ - current, - pages, - animes, - }); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} -export async function searchAnime(req: Request, res: Response) { - try { - let { id } = req.params; - const response = await axios.get(`${urls.search}${id}`); - const $ = cheerio.load(response.data); - let animes = []; - $('.animes .row article').each((i, e) => { - let el = $(e); - let title = el.find('h3.Title').text(); - let cover = el.find('div.Image .cover .img-fluid').attr('src'); - let id1 = el.find('a.link-anime').attr('href'); - let id = id1.split('/')[4]; - let category = el.find('.category').text(); - category = category.substring(1, category.length); - let year = parseInt(el.find('.fecha').text()); - - let anime: AnimeSearchI = { - id, - title, - cover, - category, - year, - }; - animes.push(anime); - }); - - res.json(animes); - } catch (err) { - res.json({ - message: err.message, - success: false, - }); - } -} -export async function getEpisode(req: Request, res: Response) { - try { - let { id } = req.params; - const response = await axios.get(`${urls.episode}/${id}`); - const $ = cheerio.load(response.data); - let epnum = $('.Episode .Title-epi').text(); - let title = $('.Episode .Title-epi').text().replace('Sub Español', '').trim(); - let number: any = epnum.split(' '); - number = parseInt(number[number.length - 3]); - let ctrls = { - prev: false, - next: false, - }; - $('.d-flex.justify-content-center.mb-4').map((i, e) => { - let el = $(e); - if (el.text().includes('Anterior')) { - ctrls.prev = true; - } - if (el.text().includes('Siguiente')) { - ctrls.next = true; - } - }); - let animeId = $('a.btnWeb.green.Current') - .attr('href') - .replace('https://monoschinos2.com/anime/', ''); - let videos = []; - let videosContainer = $('.Episode .content .row .TPlayer').text(); - $(videosContainer).each((i, e) => { - let el = $(e); - let url = el.attr('src'); - if (url) { - url = url.split('url=')[1]; - url = decodeURIComponent(url); - url = url.split('&id')[0]; - let videolinks = new URL(url); - let { host } = videolinks; - let name = host.replace(/\.com|www\.|\.ru|repro\.|\.co|\.nz/, ''); - name = `${name.slice(0, 1).toUpperCase()}${name.slice(1)}`; - let servers = { - url, - name, - }; - videos.push(servers); - } - }); - let downloads = []; - let downloadsContainer = $('#downloads table tbody tr'); - - $(downloadsContainer).each((i, e) => { - let el = $(e); - - let link = el.find('a').attr('href'); - let sn = link.replace(/\.com|www\.|\.ru|repro\.|\.co|\.nz/, ''); - let servername = sn.slice(8); - let svn = servername.indexOf('/'); - let server = servername.slice(0, svn); - server = `${server.slice(0, 1).toUpperCase()}${server.slice(1)}`; - let down = { - server, - link, - }; - if (down) { - downloads.push(down); - } - }); - - res.json({ - title, - animeId, - ctrls, - number, - videos, - downloads, - }); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} -export async function getCategories(req: Request, res: Response) { - try { - const response = await axios.get(`${urls.main}/animes`); - const $ = cheerio.load(response.data); - let categories = []; - let categoriesContainer = $('.filter-container .clearfix .float-left')[0]; - $(categoriesContainer) - .find('.dropdown-menu .dropdown-item') - .each((i, e) => { - let el = $(e); - let title = el.text(); - let id = el.attr('href'); - id = id.split('/')[2]; - let category = { - title, - id, - }; - categories.push(category); - }); - - res.status(200).json(categories); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} -export async function getGenders(req: Request, res: Response) { - try { - const response = await axios.get(`${urls.main}/animes`); - const $ = cheerio.load(response.data); - - let genders = []; - - let gendersContainer = $('.filter-container .clearfix .float-left')[1]; - - $(gendersContainer) - .find('.dropdown-menu .dropdown-item') - .each((i, e) => { - let el = $(e); - - let title = el.text(); - if (title.charAt(0) == ' ') { - title = title.substring(1, title.length); - } - let id = el.attr('href'); - id = id.split('/')[2]; - let gender: GenderI = { - title, - id, - }; - genders.push(gender); - }); - res.json(genders); - } catch (err) { - res.status(500).json({ - message: err, - success: false, - }); - } -} -export async function getGender(req: any, res: any) { - try { - let { gender } = req.params; - let { page } = req.params; - !page ? (page = 1) : (page = page); - const bodyResponse = await axios.get(`${urls.main}/genero/${gender}?page=${page}`); - const $ = cheerio.load(bodyResponse.data); - const animes = []; - - $('.animes .container .row article').each((i, e) => { - let el = $(e); - - let id = el.find('a').attr('href'); - id = id.split('/')[4]; - let title = el.find('.Title').text(); - let img = el.find('.Image img').attr('src'); - let category = el.find('.category').text(); - category = category.substring(1, category.length); - let year = parseInt(el.find('.fecha').text()); - - const anime = { - id, - title, - img, - category, - year, - }; - - animes.push(anime); - }); - - let totalPages: any = $('.pagination').children().length; - totalPages = $('.pagination').find('.page-item')[totalPages - 2]; - let pages = parseInt($(totalPages).text()); - - let current = parseInt(page); - res.status(200).json({ - current, - pages, - animes, - }); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} -export async function getYears(req: Request, res: Response) { - try { - const response = await axios.get(`${urls.main}/animes`); - const $ = cheerio.load(response.data); - - let years = []; - - let YearsContainer = $('.filter-container .clearfix .float-left')[2]; - - $(YearsContainer) - .find('.dropdown-menu .dropdown-item') - .each((i, e) => { - let el = $(e); - - let title = el.text(); - if (title.charAt(0) == ' ') { - title = title.substring(1, title.length); - } - let id = el.attr('href'); - id = id.split('/')[2]; - let year: GenderI = { - title, - id, - }; - years.push(year); - }); - res.json(years); - } catch (err) { - res.status(500).json({ - message: err, - success: false, - }); - } -} -export async function getYear(req: Request, res: Response) { - try { - let { year } = req.params; - let { page } = req.params; - !page ? (page = '1') : (page = page); - const bodyResponse = await axios.get(`${urls.main}/year/${year}?page=${page}`); - const $ = cheerio.load(bodyResponse.data); - const animes = []; - - $('.animes .container .row article').each((i, e) => { - let el = $(e); - - let id = el.find('a').attr('href'); - id = id.split('/')[4]; - let title = el.find('.Title').text(); - let img = el.find('.Image img').attr('src'); - let category = el.find('.category').text(); - category = category.substring(1, category.length); - let year = parseInt(el.find('.fecha').text()); - - const anime = { - id, - title, - img, - category, - year, - }; - - animes.push(anime); - }); - - let totalPages: any = $('.pagination').children().length; - totalPages = $('.pagination').find('.page-item')[totalPages - 2]; - let pages = parseInt($(totalPages).text()); - - let current = parseInt(page); - res.status(200).json({ - current, - pages, - animes, - }); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} -export async function getLetters(req: Request, res: Response) { - try { - const bodyResponse = await axios.get(`${urls.main}/animes`); - const $ = cheerio.load(bodyResponse.data); - const letters = []; - let lettersContainer = $('.filter-container .clearfix .float-left')[3]; - $(lettersContainer) - .find('.dropdown-menu .dropdown-item') - .each((i, e) => { - let el = $(e); - let title = el.text(); - let id = el.attr('href'); - id = id.split('/')[2]; - let letter = { - title, - id, - }; - letters.push(letter); - }); - - res.status(200).json(letters); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} -export async function getBy(req: Request, res: Response, multiple?) { - try { - let { gender, letter, category } = req.params; - - let { page } = req.query; - - if (!page) { - page = '1'; - } - - let bodyResponse; - - if (multiple) { - bodyResponse = await axios.get( - `${urls.main}/categoria/${category}/genero/${gender}?page=${page}` - ); - } else if (gender && !multiple) { - bodyResponse = await axios.get(`${urls.main}/genero/${gender}?page=${page}`); - } else if (letter && !multiple) { - bodyResponse = await axios.get(`${urls.main}/letra/${letter}?page=${page}`); - } else if (category && !multiple) { - bodyResponse = await axios.get(`${urls.main}/categoria/${category}?page=${page}`); - } - const $ = cheerio.load(bodyResponse.data); - - const animes = []; - - $('.animes .container .row article').each((i, e) => { - let el = $(e); - let id = el.find('.link-anime').attr('href'); - id = id.split('/')[4]; - let img = el.find('.link-anime .Image img').attr('src'); - let title = el.find('.link-anime .Title').text(); - let type = el.find('.link-anime .info .category').text(); - let year = el.find('.link-anime .info .fecha').text(); - - let anime = { - id, - img, - title, - type, - year, - }; - - animes.push(anime); - }); - - let totalPages: any = $('.pagination').children().length; - totalPages = $('.pagination').find('.page-item')[totalPages - 2]; - let pages = parseInt($(totalPages).text()); - - res.status(200).json({ - animes, - pages, - success: true, - }); - } catch (err) { - res.status(500).json({ - message: err.message, - success: false, - }); - } -} - -// export { - -// getAnime, -// getAnimes, -// searchAnime, -// getEpisode, -// getCategories, -// getGenders, -// getGender, -// getYears, -// getYear, -// getLetters, -// getBy -// }; diff --git a/src/controllers/getAnime.js b/src/controllers/getAnime.js new file mode 100644 index 0000000..346dff3 --- /dev/null +++ b/src/controllers/getAnime.js @@ -0,0 +1,53 @@ +import axios from 'axios'; +import { getAttr, urls } from '../config.js'; +import { parse } from 'node-html-parser'; + +export async function getAnime(req, res) { + try { + const url = 'https://monoschinos2.com/'; + const noImage = 'https://monoschinos2.com/assets/img/no_image.png'; + const defaulImage = 'https://image.freepik.com/free-vector/404-error-page-found_41910-364.jpg'; + const { id } = req.params; + const { data } = await axios.get(`${urls.anime}/${id}`); + const html = parse(data); + const image = getAttr(html, '.Banner img', 'src'); + res.status(200).json({ + banner: image == noImage ? defaulImage : image || null, + image: getAttr(html, 'figure img.img-fluid', 'src') || null, + title: + html + .querySelector('h1.Title') + .text.replace(/Sub Español/gi, '') + .trim() || null, + sinopsis: html.querySelector('.row .col-sm-9 .Description p').text || null, + status: html.querySelector('.row .col-sm-9 .Type').text.trim() || null, + date: html.querySelector('.after-title.mb-2').text.match(/\d+-\d+-\d+/)[0] || null, + type: + html.querySelector('.after-title.mb-2').text.match(/(\d+-\d+-\d+)\s?\|\s([A-Za-z]+)/)[2] || + null, + genders: html.querySelectorAll('.generos a').map((g) => g.text) || null, + episodes: + html.querySelectorAll('.SerieCaps a').map((cap) => { + const epId = cap.attributes['href'].replace(`${url}ver/`, ''); + return { + no: parseInt(epId.split('-').pop()), + id: epId, + }; + }) || null, + sugestions: + html.querySelectorAll('.recom article').map((i) => { + return { + id: getAttr(i, 'a', 'href').replace(`${url}anime/`, ''), + image: getAttr(i, 'a .Image img', 'src'), + title: i.querySelector('a .Title').text, + year: parseInt(i.querySelector('.fecha').text), + }; + }) || null, + }); + } catch (error) { + res.status(500).json({ + id: 'intertal-server-error', + message: error.message, + }); + } +} diff --git a/src/controllers/getAnimes.js b/src/controllers/getAnimes.js new file mode 100644 index 0000000..97c2325 --- /dev/null +++ b/src/controllers/getAnimes.js @@ -0,0 +1,27 @@ +import { parse } from 'node-html-parser'; +import axios from 'axios'; +import { getAttr, urls } from '../config.js'; + +export async function getAnimes(req, res) { + try { + let { page = '1' } = req.params; + const { data } = await axios.get(`${urls.main}/animes?page=${page}`); + const html = parse(data); + res.json( + html.querySelectorAll('.animes .container .row article').map((i) => { + const id = getAttr(i, 'a', 'href'); + return { + id: id.split('/').pop() || null, + title: i.querySelector('.Title').text || null, + image: getAttr(i, '.Image img', 'src') || null, + type: i.querySelector('.category.text-uppercase').text || null, + year: parseInt(i.querySelector('.fecha').text) || null, + }; + }) + ); + } catch (err) { + res.status(500).json({ + message: err.message, + }); + } +} diff --git a/src/controllers/getBy.js b/src/controllers/getBy.js new file mode 100644 index 0000000..02b6ef5 --- /dev/null +++ b/src/controllers/getBy.js @@ -0,0 +1,35 @@ +import axios from 'axios'; +import { parse } from 'node-html-parser'; +import { urls, getAttr } from '../config.js'; + +export async function getBy(req, res) { + try { + const { gender, category, letter, year, page = '1', sort, order } = req.query; + let response; + if (gender) response = await axios.get(`${urls.main}/genero/${gender}?page=${page}`); + if (category) response = await axios.get(`${urls.main}/categoria/${category}?page=${page}`); + if (letter) response = await axios.get(`${urls.main}/letra/${letter}?page=${page}`); + if (year) response = await axios.get(`${urls.main}/year/${year}?page=${page}`); + const { data } = response; + const html = parse(data); + const animes = html.querySelectorAll('.animes .container .row article').map((i) => { + const id = getAttr(i, 'a', 'href'); + return { + id: id.split('/').pop() || null, + title: i.querySelector('.Title').text.trim() || null, + image: getAttr(i, '.Image img', 'src') || null, + type: i.querySelector('.category.text-uppercase').text.trim() || null, + year: parseInt(i.querySelector('.fecha').text) || null, + }; + }); + const isAsc = order == 'asc'; + const ordered = animes.sort((a, b) => { + return (a[sort] < b[sort] ? -1 : 1) * (isAsc ? 1 : -1); + }); + res.status(200).json(sort ? ordered : animes); + } catch (err) { + res.status(500).json({ + message: err.message, + }); + } +} diff --git a/src/controllers/getCategories.js b/src/controllers/getCategories.js new file mode 100644 index 0000000..b5186f6 --- /dev/null +++ b/src/controllers/getCategories.js @@ -0,0 +1,25 @@ +import axios from 'axios'; +import { parse } from 'node-html-parser'; +import { urls } from '../config.js'; + +export async function getCategories(req, res) { + try { + const { data } = await axios.get(`${urls.main}/animes`); + const html = parse(data); + res.status(200).json( + html + .querySelectorAll('div[aria-labelledby="categoryMenuButton"] a') + .map((i) => { + return { + name: i.text.trim(), + id: i?.attributes['href'].replace('/categoria/', ''), + }; + }) + .filter((i) => i.id !== '') + ); + } catch (err) { + res.status(500).json({ + message: err.message, + }); + } +} diff --git a/src/controllers/getEmision.js b/src/controllers/getEmision.js new file mode 100644 index 0000000..7874ae1 --- /dev/null +++ b/src/controllers/getEmision.js @@ -0,0 +1,26 @@ +import axios from 'axios'; +import { parse } from 'node-html-parser'; +import { getAttr, urls } from '../config.js'; + +export async function getEmision(req, res) { + try { + let { page = '1' } = req.query; + const { data } = await axios.get(`${urls.emision}${page}`); + const html = parse(data); + res.status(200).json( + html.querySelectorAll('.animes .container .row article').map((i) => { + return { + id: getAttr(i, 'a', 'href').split('/').pop(), + title: i.querySelector('.Title').text.trim(), + image: getAttr(i, '.Image img', 'src'), + type: i.querySelector('.category').text.trim(), + year: parseInt(i.querySelector('.fecha').text), + }; + }) + ); + } catch (err) { + res.status(500).json({ + message: err.message, + }); + } +} diff --git a/src/controllers/getEmision.ts b/src/controllers/getEmision.ts deleted file mode 100644 index 5d8b4b2..0000000 --- a/src/controllers/getEmision.ts +++ /dev/null @@ -1,34 +0,0 @@ -import axios from 'axios'; -import cheerio from 'cheerio'; -import { Request, Response } from 'express'; -import { urls } from '../config'; - -export async function getEmision(req: Request, res: Response) { - try { - let { page = '1' } = req.query; - const { data } = await axios.get(`${urls.emision}${page}`); - const $ = cheerio.load(data); - const animes = $('.animes .container .row article') - .map((i, e) => { - let el: cheerio.Cheerio = $(e); - let category: string = el.find('.category').text(); - return { - id: el.find('a').attr('href').split('/')[4], - title: el.find('.Title').text(), - img: el.find('.Image img').attr('src'), - category: el.find('.category').text().substring(1, category.length), - year: parseInt(el.find('.fecha').text()), - }; - }) - .toArray(); - let pagesBase: string = $('.pagination').text().match(/\d/g).pop(); - let pages = parseInt(pagesBase); - res.json({ animes, pages }).status(200); - } catch (err) { - res.json({ - message: err.message, - success: false, - }); - } -} -// 46 lines before refactoring diff --git a/src/controllers/getEpisode.js b/src/controllers/getEpisode.js new file mode 100644 index 0000000..c871f5d --- /dev/null +++ b/src/controllers/getEpisode.js @@ -0,0 +1,48 @@ +import axios from 'axios'; +import { getAttr, urls } from '../config.js'; +import { parse } from 'node-html-parser'; + +export async function getEpisode(req, res) { + try { + const { id } = req.params; + const { data } = await axios.get(`${urls.episode}/${id}`); + const html = parse(data); + const map = { + Anterior: true, + Siguiente: true, + }; + const videos = html.querySelectorAll('.TPlayer.mt-3.mb-3 .TPlayerTb').map((i) => { + const tag = parse(i.text); + return tag.querySelector('iframe')?.attributes['src']; + }); + const servers = html.querySelectorAll('#downloads table tbody tr').map((i) => { + return { + name: getAttr(i, 'a', 'href').match(/\/\/(.+)\//)[1], + url: getAttr(i, 'a', 'href'), + }; + }); + const ctrls = {}; + html.querySelectorAll('.d-flex.justify-content-center.mb-4 a').forEach((i) => { + const id = i.text.replace(/\s/g, ''); + let key; + id == 'Siguiente' ? (key = 'next') : id == 'Anterior' ? (key = 'prev') : 'menu'; + ctrls[key] = map[id]; + }); + delete ctrls['menu']; + res.json({ + title: html.querySelector('.Title-epi').text, + no: parseInt(id.split('-').pop()) || 0, + animeId: getAttr(html, 'a.btnWeb.green.Current', 'href').replace( + 'https://monoschinos2.com/anime/', + '' + ), + videos: videos.filter((i) => i !== undefined), + servers, + ctrls, + }); + } catch (err) { + res.status(500).json({ + message: err.message, + }); + } +} diff --git a/src/controllers/getGenders.js b/src/controllers/getGenders.js new file mode 100644 index 0000000..1a7c5a6 --- /dev/null +++ b/src/controllers/getGenders.js @@ -0,0 +1,26 @@ +import axios from 'axios'; +import { parse } from 'node-html-parser'; +import { urls } from '../config.js'; + +export async function getGenders(req, res) { + try { + const { data } = await axios.get(`${urls.main}/animes`); + const html = parse(data); + res.status(200).json( + html + .querySelectorAll('div[aria-labelledby="genreMenuButton"] a') + .map((i) => { + return { + name: i.text.trim(), + id: i?.attributes['href'].replace('/genero/', ''), + }; + }) + .filter((i) => i.id !== '') + ); + } catch (err) { + res.status(500).json({ + message: err, + success: false, + }); + } +} diff --git a/src/controllers/getLastest.js b/src/controllers/getLastest.js new file mode 100644 index 0000000..2456936 --- /dev/null +++ b/src/controllers/getLastest.js @@ -0,0 +1,24 @@ +import axios from 'axios'; +import { getAttr, urls } from '../config.js'; +import { parse } from 'node-html-parser'; + +export async function getLastest(req, res) { + try { + const { data } = await axios.get(urls.main); + const html = parse(data); + res.json( + html.querySelectorAll('.row article').map((i) => { + const id = getAttr(i, 'a', 'href').split('/').pop(); + return { + id: id || null, + title: i.querySelector('.Title').text || null, + image: getAttr(i, '.Image img', 'src') || null, + type: i.querySelector('.Image figure span').text.trim() || null, + episode: parseInt(id.split('-').pop()) || null, + }; + }) + ); + } catch (error) { + return res.json({ error: error.message }).status(500); + } +} diff --git a/src/controllers/getLastest.ts b/src/controllers/getLastest.ts deleted file mode 100644 index ae0c611..0000000 --- a/src/controllers/getLastest.ts +++ /dev/null @@ -1,32 +0,0 @@ -import cheerio from 'cheerio'; -import axios from 'axios'; -import { urls } from '../config'; -import { Response, Request } from 'express'; - -export async function getLastest(req: Request, res: Response) { - try { - const { data } = await axios.get(urls.main); - const $ = cheerio.load(data); - let getLastest = $('.container .caps .container')[0]; - const animes: cheerio.Element[] = $(getLastest) - .find('.row article') - .map((i, e) => { - let el: cheerio.Cheerio = $(e); - let type: string = el.find('.Image figure span').text(); - return { - id: el - .find('a') - .attr('href') - .replace(/https:\/\/monoschinos2\.com\/ver\//, ''), - title: el.find('.Title').html().split('\t')[0], - cover: el.find('.Image img').attr('src'), - type: type.substring(1, type.length), - episode: parseInt(el.find('.dataEpi .episode').text().split('\n')[1]), - }; - }) - .toArray(); - return res.json(animes).status(200); - } catch (error) { - return res.json({ error: error.message }).status(500); - } -} diff --git a/src/controllers/getYears.js b/src/controllers/getYears.js new file mode 100644 index 0000000..383ac4b --- /dev/null +++ b/src/controllers/getYears.js @@ -0,0 +1,26 @@ +import axios from 'axios'; +import { parse } from 'node-html-parser'; +import { urls } from '../config.js'; + +export async function getYears(req, res) { + try { + const { data } = await axios.get(`${urls.main}/animes`); + const html = parse(data); + res.status(200).json( + html + .querySelectorAll('div[aria-labelledby="yearMenuButton"] a') + .map((i) => { + return { + name: i.text.trim(), + id: i?.attributes['href'].replace('/year/', ''), + }; + }) + .filter((i) => i.id !== '') + ); + } catch (err) { + res.status(500).json({ + message: err, + success: false, + }); + } +} diff --git a/src/controllers/index.js b/src/controllers/index.js new file mode 100644 index 0000000..99f0ed0 --- /dev/null +++ b/src/controllers/index.js @@ -0,0 +1,10 @@ +export * from './getLastest.js'; +export * from './getEmision.js'; +export * from './getAnime.js'; +export * from './searchAnimes.js'; +export * from './getAnimes.js'; +export * from './getEpisode.js'; +export * from './getCategories.js'; +export * from './getGenders.js'; +export * from './getYears.js'; +export * from './getBy.js'; diff --git a/src/controllers/index.ts b/src/controllers/index.ts deleted file mode 100644 index 31c341c..0000000 --- a/src/controllers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './getLastest'; -export * from './getEmision'; diff --git a/src/controllers/searchAnimes.js b/src/controllers/searchAnimes.js new file mode 100644 index 0000000..1fad91c --- /dev/null +++ b/src/controllers/searchAnimes.js @@ -0,0 +1,27 @@ +import { parse } from 'node-html-parser'; +import axios from 'axios'; +import { getAttr, urls } from '../config.js'; + +export async function searchAnime(req, res) { + try { + let { id } = req.params; + const { data } = await axios.get(`${urls.search}${id}`); + const html = parse(data); + res.json( + html.querySelectorAll('.animes .row article').map((i) => { + return { + id: getAttr(i, 'a', 'href').split('/').pop() || null, + title: i.querySelector('h3.Title').text || null, + image: getAttr(i, '.cover .img-fluid', 'src') || null, + type: i.querySelector('.category.text-uppercase').text.trim() || null, + year: parseInt(i.querySelector('.fecha').text) || null, + }; + }) + ); + } catch (error) { + res.status(500).json({ + id: 'intertal-server-error', + message: error.message, + }); + } +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..692c9cc --- /dev/null +++ b/src/index.js @@ -0,0 +1,6 @@ +import app from './app.js'; + +app.listen(process.env.PORT || 5000, () => { + console.clear(); + console.info(`API Running on: 5000 ============================`); +}); diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 087cfde..0000000 --- a/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import app from './app'; -import { appConfig } from './config' - -function init(host:any, port:any) { - app.listen(port, host, ( ) => { - console.clear() - console.info(`API Running on: ${host}:${port} ============================`); - }) -} - -init(appConfig.host, appConfig.port) \ No newline at end of file diff --git a/src/models/interfaces.ts b/src/models/interfaces.ts deleted file mode 100644 index 66159e0..0000000 --- a/src/models/interfaces.ts +++ /dev/null @@ -1,79 +0,0 @@ -export interface EmisionResponse{ - animes: Array; - pages: number; -} - -export interface EmisionI{ - id: string; - title: string; - img: string; - category: string; - year: number; -} -// const anime: { - -// } -export interface LastestAnimeI { - title: string - cover: string - id?: string - episode: string - type: string -} -export interface AnimeI{ - id: string; - title: string; - banner: string; - type: string; - cover: string; - sinopsis: string; - status: string; - date: string; - genders: GenderI[]; - sugestions: SuggestionI[]; - episodes: EpI[]; -} - -export interface GenderI{ - id:string; - title:string; -} - -export interface SuggestionI{ - id:string; - title:string; - cover:string; - year:number; -} -export interface EpI{ - id:string; - number:number; -} - -export interface AnimeSearchI { - id?: string; - title: string; - cover: string - category: string; - year: number; -} - - -export interface EpisodeI { - id?: string; - title: string; - number: string - videos: Array; - downloads: Array; -} - -export interface VideosI { - url: string; - title: string; -} -export interface DownloadsI { - url: string; - server: string; -} - - diff --git a/src/routes/api.routes.ts b/src/routes/api.routes.js similarity index 61% rename from src/routes/api.routes.ts rename to src/routes/api.routes.js index 8c43dea..e2f0b50 100644 --- a/src/routes/api.routes.ts +++ b/src/routes/api.routes.js @@ -1,20 +1,24 @@ import { Router } from 'express'; const routes = Router(); + import { + getEmision, + getGenders, + getLastest, getAnime, - getAnimes, searchAnime, + getAnimes, getEpisode, - getGenders, - getGender, getCategories, getYears, - getYear, - getLetters, getBy, -} from '../controllers/controller'; +} from '../controllers/index.js'; -import { getEmision, getLastest } from '../controllers'; +routes.get('/', (_, res) => { + res.json({ + message: 'API Works', + }); +}); routes.get('/lastest', (req, res) => { getLastest(req, res); @@ -37,33 +41,33 @@ routes.get('/search/:id', (req, res) => { routes.get('/genders', (req, res) => { getGenders(req, res); }); -routes.get('/gender/:gender/:page', (req, res) => { - getGender(req, res); -}); + routes.get('/categories', (req, res) => { getCategories(req, res); }); routes.get('/years', (req, res) => { getYears(req, res); }); -routes.get('/year/:year/:page', (req, res) => { - getYear(req, res); -}); + routes.get('/letters', (req, res) => { getLetters(req, res); }); -routes.get('/category/:category/gender/:gender', (req, res) => { - getBy(req, res, true); -}); -routes.get('/gender/:gender', (req, res) => { - getBy(req, res); -}); -routes.get('/letter/:letter', (req, res) => { +routes.get('/by', (req, res) => { getBy(req, res); }); +// routes.get('/category/:category/gender/:gender', (req, res) => { +// getBy(req, res, true); +// }); +// routes.get('/gender/:gender', (req, res) => { +// getBy(req, res); +// }); -routes.get('/category/:category', (req, res) => { - getBy(req, res); -}); +// routes.get('/letter/:letter', (req, res) => { +// getBy(req, res); +// }); + +// routes.get('/category/:category', (req, res) => { +// getBy(req, res); +// }); export default routes; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 11a53a9..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist/", /* Redirect output structure to the directory. */ - "rootDir": "./src/", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": false, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": false, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ - - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} diff --git a/yarn.lock b/yarn.lock index 58d65a7..ea70a2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,101 +14,6 @@ dependencies: defer-to-connect "^1.0.1" -"@types/axios@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" - integrity sha1-7CMA++fX3d1+udOr+HmZlkyvzkY= - dependencies: - axios "*" - -"@types/body-parser@*": - version "1.19.1" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.1.tgz#0c0174c42a7d017b818303d4b5d969cb0b75929c" - integrity sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/cheerio@^0.22.28": - version "0.22.30" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.30.tgz#6c1ded70d20d890337f0f5144be2c5e9ce0936e6" - integrity sha512-t7ZVArWZlq3dFa9Yt33qFBQIK4CQd1Q3UJp0V+UhP6vgLWLM6Qug7vZuRSGXg45zXeB1Fm5X2vmBkEX58LV2Tw== - dependencies: - "@types/node" "*" - -"@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - -"@types/cors@^2.8.10": - version "2.8.12" - resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - -"@types/dotenv@^8.2.0": - version "8.2.0" - resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-8.2.0.tgz#5cd64710c3c98e82d9d15844375a33bf1b45d053" - integrity sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw== - dependencies: - dotenv "*" - -"@types/express-serve-static-core@^4.17.18": - version "4.17.24" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" - integrity sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/express@^4.17.11": - version "4.17.13" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" - integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.18" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/mime@^1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== - -"@types/morgan@^1.9.2": - version "1.9.3" - resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.3.tgz#ae04180dff02c437312bc0cfb1e2960086b2f540" - integrity sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "16.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.1.tgz#f3647623199ca920960006b3dccf633ea905f243" - integrity sha512-4/Z9DMPKFexZj/Gn3LylFgamNKHm4K3QDi0gz9B26Uk0c8izYf97B5fxfpspMNkWlFupblKM/nV8+NA9Ffvr+w== - -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/serve-static@*": - version "1.13.10" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" - integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -149,22 +54,17 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= -axios@*, axios@^0.21.1: - version "0.21.4" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" - integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== +axios@^0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== dependencies: - follow-redirects "^1.14.0" + follow-redirects "^1.14.4" balanced-match@^1.0.0: version "1.0.2" @@ -233,11 +133,6 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - bytes@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -384,11 +279,6 @@ cors@^2.8.5: object-assign "^4" vary "^1" -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -456,11 +346,6 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - dom-serializer@^1.0.1, dom-serializer@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" @@ -498,11 +383,6 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotenv@*: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - dotenv@^8.2.0: version "8.6.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" @@ -611,10 +491,10 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -follow-redirects@^1.14.0: - version "1.14.4" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" - integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== +follow-redirects@^1.14.4: + version "1.14.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" + integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== forwarded@0.2.0: version "0.2.0" @@ -696,6 +576,11 @@ has-yarn@^2.1.0: resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + htmlparser2@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -892,11 +777,6 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -977,10 +857,18 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -nodemon@^2.0.7: - version "2.0.13" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.13.tgz#67d40d3a4d5bd840aa785c56587269cfcf5d24aa" - integrity sha512-UMXMpsZsv1UXUttCn6gv8eQPhn6DR4BW+txnL3IN5IHqrCwcrT/yWHfL35UsClGXknTH79r5xbu+6J1zNHuSyA== +node-html-parser@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.1.0.tgz#753f5a60cdfe6d027c15857cb817df592c18c998" + integrity sha512-l6C1Gf1o7YuxeMGa17PypEez/rj+ii3q4/NZG37nRmWSLDjHyB0WNrlE4h2UW92D0JSfUSfu+lOvxThttVe7Jw== + dependencies: + css-select "^4.1.3" + he "1.2.0" + +nodemon@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.14.tgz#287c7a2f6cd8a18b07e94cd776ecb6a82e4ba439" + integrity sha512-frcpDx+PviKEQRSYzwhckuO2zoHcBYLHI754RE9z5h1RGtrngerc04mLpQQCPWBkH/2ObrX7We9YiwVSYZpFJQ== dependencies: chokidar "^3.2.2" debug "^3.2.6" @@ -1247,19 +1135,6 @@ signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.4.tgz#366a4684d175b9cab2081e3681fda3747b6c51d7" integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q== -source-map-support@^0.5.17: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -1324,18 +1199,6 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -ts-node@^9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== - dependencies: - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.17" - yn "3.1.1" - tslib@^2.2.0: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" @@ -1361,11 +1224,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.2.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" - integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA== - undefsafe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" @@ -1462,8 +1320,3 @@ yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==