From 87e94023055045d7c554ce84e415a879a4adab8e Mon Sep 17 00:00:00 2001 From: Indrawan I Date: Fri, 23 Dec 2022 10:04:22 +0700 Subject: [PATCH] ci: generate docs from apidoc definitions (#10) * add some complier files to gitignore * feat: add apidoc definitions in each controller * ci: generate docs from apidoc definitions * docs: update readme * ci: update playground workflows * bump marine template --- .github/workflows/playground.yml | 26 ++++++ .gitignore | 5 +- README.md | 81 ++++++++++++------- package.json | 22 +++-- src/controller/3hentai/3hentaiGet.ts | 29 +++++++ src/controller/3hentai/3hentaiRandom.ts | 28 +++++++ src/controller/3hentai/3hentaiSearch.ts | 31 +++++++ src/controller/asmhentai/asmhentaiGet.ts | 29 +++++++ src/controller/asmhentai/asmhentaiRandom.ts | 28 +++++++ src/controller/asmhentai/asmhentaiSearch.ts | 29 +++++++ src/controller/hentai2read/hentai2readGet.ts | 29 +++++++ .../hentai2read/hentai2readSearch.ts | 28 +++++++ src/controller/hentaifox/hentaifoxGet.ts | 29 +++++++ src/controller/hentaifox/hentaifoxRandom.ts | 27 +++++++ src/controller/hentaifox/hentaifoxSearch.ts | 31 +++++++ src/controller/nhentai/nhentaiGet.ts | 29 +++++++ src/controller/nhentai/nhentaiRandom.ts | 28 +++++++ src/controller/nhentai/nhentaiRelated.ts | 29 +++++++ src/controller/nhentai/nhentaiSearch.ts | 31 +++++++ src/controller/pururin/pururinGet.ts | 29 +++++++ src/controller/pururin/pururinRandom.ts | 29 +++++++ src/controller/pururin/pururinSearch.ts | 31 +++++++ .../simply-hentai/simply-hentaiGet.ts | 29 +++++++ src/index.ts | 1 - src/router/endpoint.ts | 3 +- 25 files changed, 652 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/playground.yml diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml new file mode 100644 index 0000000..2892a4e --- /dev/null +++ b/.github/workflows/playground.yml @@ -0,0 +1,26 @@ +name: Playground + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-and-deploy: + concurrency: ci-${{ github.ref }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Installing dependencies + run: npm install + - name: Generating docs + run: npm run build:apidoc + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4.4.1 + with: + branch: gh-pages + folder: docs \ No newline at end of file diff --git a/.gitignore b/.gitignore index 11a1ce5..9b1da1b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ yarn.lock /build /playground +/template +/docs p.ts .env .idea -CHANGELOG.md \ No newline at end of file +CHANGELOG.md +theme.zip \ No newline at end of file diff --git a/README.md b/README.md index 5455f33..e05f04e 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@
-jandapress +jandapress

RESTful and experimental API for the doujinboards

- +

Jandapress was named **JCE** (Janda Cheerio Express) and definitely depends on them. The motivation of this project is to bring you an actionable data related doujin with gather in mind. +PlaygroundContributing • -DocumentationReport Issues
@@ -26,19 +26,18 @@ The motivation of this project is to bring you an actionable data related doujin - [Jandapress vs. the doujinboards](#jandapress-vs-the-whole-doujin-sites) - [Prerequisites](#prerequisites) - [Installation](#installation) + - [Docker](#docker) + - [Manual](#manual) - [Running tests](#running-tests) - - [Routing](#routing) - - [nhentai-api](#routing) - - [pururin-api](#routing) - - [hentaifox-api](#routing) - - [asmhentai-api](#routing) - - [hentai2read-api](#routing) - - [simply-hentai-api](#routing) - - [3hentai-api](#routing) + - [Playground](https://sinkaroid.github.io/jandapress) + - [Routing](#playground) + - [Status response](#status-response) - [Limitations](#limitations) + - [CLosing remarks](https://github.com/sinkaroid/jandapress/blob/master/CLOSING_REMARKS.md) + - [Alternative links](https://github.com/sinkaroid/jandapress/blob/master/CLOSING_REMARKS.md#alternative-links) - [Pronunciation](#Pronunciation) - - [Legal](#legal) - [Client libraries / Wrappers](#client-libraries--wrappers) + - [Legal](#legal) ## The problem @@ -51,10 +50,10 @@ You enjoy consume doujin sites to build web applications. There are a lot sites - Gather the most doujin sites - Objects taken that are consistent structure, almost -- Objects taken is re-appended to make it more actionable +- Objects taken is re-appended to make extendable - All in one: get, search, and random methods - In the future we may implement JWT authentication -- Pure scraping +- Pure scraping, except nh sigh.. ## Jandapress vs. the whole doujin sites **Features availability** that Jandapress has @@ -68,15 +67,29 @@ You enjoy consume doujin sites to build web applications. There are a lot sites | `asmhentai` | [![Asmhentai](https://github.com/sinkaroid/jandapress/workflows/Asmhentai%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/asmhentai.yml) | ✅ | ✅ | ✅ | | `3hentai` | [![Asmhentai](https://github.com/sinkaroid/jandapress/workflows/3hentai%20test/badge.svg)](https://github.com/sinkaroid/jandapress/actions/workflows/3hentai.yml) | ✅ | ✅ | ✅ | +## Prerequisites + + +
NOTE: NodeJS 14.x or higher
+ +To handle several requests from each web, You will also need [Redis](https://redis.io/) for persistent caching, free tier is available on [Redis Labs](https://redislabs.com/), You can also choose another provider as we using [keyv](https://github.com/jaredwray/keyv) Key-value storage with support for multiple backends. All data must be stored in `` here. + ## Installation Rename `.env.schema` to `.env` and fill the value with your own. +```bash +PORT=3000 ## default port +REDIS_URL=redis://default:somenicepassword@someredishost:1337 ## the database url +EXPIRE_CACHE=1 ## should expired in a day +``` + ### Docker docker pull ghcr.io/sinkaroid/jandapress:latest docker run -p 3000:3000 -d ghcr.io/sinkaroid/jandapress:latest ### Manual + `git clone https://github.com/sinkaroid/jandapress.git` - Install dependencies - `npm install / yarn install` @@ -86,16 +99,11 @@ Rename `.env.schema` to `.env` and fill the value with your own. - Jandapress testings - `npm run start:dev` -## Prerequisites - - -
NOTE: NodeJS 14.x or higher
- -You will also need [Redis](https://redis.io/) for persistent caching, free tier is available on [Redis Labs](https://redislabs.com/) ## Running tests Jandapress depends on - [express](https://github.com/expressjs/express) web api framework +- [keyv](https://github.com/jaredwray/keyv) key-value storage with support for multiple backends - [cheerio](https://cheerio.js.org/) for parsing html - [cors](https://github.com/expressjs/cors) middleware for enabling CORS - [rate-limit](https://github.com/nfriedly/express-rate-limit) rate-limiting middleware for express @@ -112,15 +120,20 @@ Jandapress depends on ### Check nhentai It's under cloudflare protection or not `npm run test:cf` +### Generating playground like swagger from apidoc definition +`npm run build:apidoc` + > To running other tests, you can see object scripts in file `package.json` -## Routing -the `parameter?`: means is optional +## Playground +https://sinkaroid.github.io/jandapress + +- These `parameter?`: means is optional - `/` : index page ### Nhentai -The missing piece of nhentai.net +The missing piece of nhentai.net - https://sinkaroid.github.io/jandapress/#api-nhentai - `/nhentai` : nhentai api - **get**, takes parameters : `book` - **search**, takes parameters : `key`, `?page`, `?sort` @@ -133,7 +146,7 @@ The missing piece of nhentai.net - https://janda.mod.land/nhentai/search?key=futanari&page=2&sort=popular-today ### Pururin -The missing piece of pururin.to +The missing piece of pururin.to - https://sinkaroid.github.io/jandapress/#api-pururin - `/pururin` : pururin api - **get**, takes parameters : `book` - **search**, takes parameters : `key`, `?page`, `?sort` @@ -145,7 +158,7 @@ The missing piece of pururin.to - https://janda.mod.land/pururin/search?key=futanari&page=2&sort=most-viewed ### Hentaifox -The missing piece of hentaifox.com +The missing piece of hentaifox.com - https://sinkaroid.github.io/jandapress/#api-hentaifox - `/hentaifox`: hentaifox api - **get**, takes parameters : `book` - **search**, takes parameters : `key`, `?page`, `?sort` @@ -157,7 +170,7 @@ The missing piece of hentaifox.com - https://janda.mod.land/hentaifox/search?key=milf&page=2&sort=latest ### Asmhentai -The missing piece of asmhentai.com +The missing piece of asmhentai.com - https://sinkaroid.github.io/jandapress/#api-asmhentai - `/asmhentai`: asmhentai api - **get**, takes parameters : `book` - **search**, takes parameters : `key`, `?page` @@ -169,7 +182,7 @@ The missing piece of asmhentai.com - https://janda.mod.land/asmhentai/search?key=futanari&page=2 ### Hentai2read -The missing piece of hentai2read.com +The missing piece of hentai2read.com - https://sinkaroid.github.io/jandapress/#api-hentai2read - `/hentai2read`: hentai2read api - **get**, takes parameters : `book` - **search**, takes parameters : `key` @@ -180,7 +193,7 @@ The missing piece of hentai2read.com - https://janda.mod.land/hentai2read/search?key=futanari ### Simply-hentai -The missing piece of simply-hentai.com +The missing piece of simply-hentai.com - https://sinkaroid.github.io/jandapress/#api-simply-hentai - `/simply-hentai`: simply-hentai api - **get**, takes parameters : `book` - sort parameters on search @@ -189,7 +202,7 @@ The missing piece of simply-hentai.com - https://janda.mod.land/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages ### 3hentai -The missing piece of 3hentai.net +The missing piece of 3hentai.net - https://sinkaroid.github.io/jandapress/#api-3hentai - `/3hentai`: 3hentai api - **get**, takes parameters : `book` - **search**, takes parameters : `key`, `?page`, `?sort` @@ -200,6 +213,12 @@ The missing piece of 3hentai.net - https://janda.mod.land/3hentai/get?book=608979 - https://janda.mod.land/3hentai/search?key=futanari&page=2&sort=popular-7d +## Status response + + HTTP/1.1 200 OK + HTTP/1.1 200 (cached) + HTTP/1.1 500 (bad parameters) + ## Limitations Nhentai was cloudflare protection enabled, If IP and our thoughts against them? You should implement a proxy. Check [`cookie branch`](https://github.com/sinkaroid/jandapress/tree/cookie), take a look this workaround [Zekfad/nhentai-api/issues/25#issuecomment-1141360074](https://github.com/Zekfad/nhentai-api/issues/25#issuecomment-1141360074) @@ -211,10 +230,12 @@ Nhentai was cloudflare protection enabled, If IP and our thoughts against them? > That's unfortunate, Hit the "Sponsor this project" button, any kind of donations will helps me to funding the development. ## Pronunciation -[`id_ID`](https://www.localeplanet.com/java/id-ID/index.html) • **/jan·da/** — dewasa dan mengikat; _(?)_ +[`id_ID`](https://www.localeplanet.com/java/id-ID/index.html) • **/jan·da/** — Dewasa dan mengikat; _(?)_ ## Client libraries / Wrappers +Seamlessly integrate with the languages you love, simplified the usage, and intelisense definitions on your IDEs + - [janda](https://github.com/sinkaroid/janda) Python wrapper by [sinkaroid](https://github.com/sinkaroid) - Or [create your own](https://github.com/sinkaroid/jandapress/edit/master/README.md) diff --git a/package.json b/package.json index 97fcfb8..6cc94d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jandapress", - "version": "2.0.2-dev", - "description": "Experimental doujin API with gather in mind", + "version": "2.0.3-dev", + "description": "RESTful and experimental API for the Doujinshi, Pressing the whole nhentai, pururin, hentaifox, and more.. where the official one is lack.", "main": "build/src/index.js", "scripts": { "build": "rimraf build && tsc", @@ -12,6 +12,9 @@ "start:dev": "ts-node-dev src/index.ts", "lint": "npx eslint . --ext .ts", "lint:fix": "npx eslint . --fix", + "build:freshdoc": "rimraf docs && rimraf template && rimraf theme.zip", + "build:template": " npm run build:freshdoc && curl https://codeload.github.com/ScathachGrip/apidocjs-theme/zip/refs/tags/v9 -o theme.zip && unzip theme.zip && mv apidocjs-theme-9 template", + "build:apidoc": "npm run build:template && npx apidoc -i src -o ./docs -t ./template/template-marine", "test:nhentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/nhentai/get?book=177013 | jq '.'\"", "test:pururin": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/pururin/get?book=47226 | jq '.'\"", "test:hentaifox": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/hentaifox/get?book=59026 | jq '.'\"", @@ -20,6 +23,12 @@ "test:simply-hentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages | jq '.'\"", "test:3hentai": "npx start-server-and-test 3000 \"curl -v http://localhost:3000/3hentai/get?book=608979 | jq '.'\"" }, + "apidoc": { + "title": "Jandapress API Documentation", + "url" : "https://janda.mod.land", + "sampleUrl": "https://janda.mod.land", + "name": "Jandapress" + }, "keywords": [], "author": "sinkaroid", "repository": { @@ -31,12 +40,14 @@ "@keyv/redis": "^2.5.4", "cheerio": "^1.0.0-rc.11", "cors": "^2.8.5", + "dotenv": "^16.0.1", "express": "^4.18.1", "express-rate-limit": "^6.4.0", "express-slow-down": "^1.4.0", "keyv": "^4.5.2", "phin": "^3.6.1", - "pino": "^8.7.0" + "pino": "^8.7.0", + "pino-pretty": "^9.1.1" }, "devDependencies": { "@types/cors": "^2.8.12", @@ -46,10 +57,9 @@ "@types/tough-cookie": "^4.0.2", "@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/parser": "^5.18.0", - "dotenv": "^16.0.1", + "apidoc": "^0.29.0", "eslint": "^8.29.0", "npx": "^10.2.2", - "pino-pretty": "^9.1.1", "rimraf": "^3.0.2", "start-server-and-test": "^1.14.0", "ts-node": "^10.8.1", @@ -59,4 +69,4 @@ "engines": { "node": ">=14" } -} +} \ No newline at end of file diff --git a/src/controller/3hentai/3hentaiGet.ts b/src/controller/3hentai/3hentaiGet.ts index aa1cf6a..93e1ce9 100644 --- a/src/controller/3hentai/3hentaiGet.ts +++ b/src/controller/3hentai/3hentaiGet.ts @@ -10,6 +10,35 @@ export async function get3hentai(req: Request, res: Response, next: NextFunction if (!book) throw Error("Parameter book is required"); if (!isNumeric(book)) throw Error("Value must be number"); + /** + * @api {get} /3hentai/get?book=:book Get 3hentai + * @apiName Get 3hentai + * @apiGroup 3hentai + * @apiDescription Get a doujinshi on 3hentai based on id + * + * @apiParam {Number} book Book ID + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/3hentai/get?book=123 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/3hentai/get?book=123") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/3hentai/get?book=123") as resp: + * print(await resp.json()) + */ + const url = `${c.THREEHENTAI}/d/${book}`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/3hentai/3hentaiRandom.ts b/src/controller/3hentai/3hentaiRandom.ts index 8348aa0..618c508 100644 --- a/src/controller/3hentai/3hentaiRandom.ts +++ b/src/controller/3hentai/3hentaiRandom.ts @@ -5,6 +5,34 @@ import { Request, Response, NextFunction } from "express"; export async function random3hentai(req: Request, res: Response, next: NextFunction) { try { + /** + * @api {get} /3hentai/random Random 3hentai + * @apiName Random 3hentai + * @apiGroup 3hentai + * @apiDescription Gets random doujinshi on 3hentai + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/3hentai/random + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/3hentai/random") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/3hentai/random") as resp: + * print(await resp.json()) + * + */ + const url = `${c.THREEHENTAI}/random`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/3hentai/3hentaiSearch.ts b/src/controller/3hentai/3hentaiSearch.ts index e6ce1de..942a53f 100644 --- a/src/controller/3hentai/3hentaiSearch.ts +++ b/src/controller/3hentai/3hentaiSearch.ts @@ -12,6 +12,37 @@ export async function search3hentai(req: Request, res: Response, next: NextFunct if (!key) throw Error("Parameter key is required"); if (!sorting.includes(sort)) throw Error("Invalid sort: " + sorting.join(", ")); + /** + * @api {get} /3hentai/search Search 3hentai + * @apiName Search 3hentai + * @apiGroup 3hentai + * @apiDescription Search doujinshi on 3hentai + * @apiParam {String} key Keyword to search + * @apiParam {Number} [page=1] Page number + * @apiParam {String} [sort=recent] + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/3hentai/search?key=yuri + * curl -i https://janda.mod.land/3hentai/search?key=yuri&page=2&sort=recent + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/3hentai/search?key=yuri") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/3hentai/search?key=yuri") as resp: + * print(await resp.json()) + */ + const url = `${c.THREEHENTAI}/search?q=${key}&page=${page}&sort=${sort}`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/asmhentai/asmhentaiGet.ts b/src/controller/asmhentai/asmhentaiGet.ts index 5ef7283..d9a935c 100644 --- a/src/controller/asmhentai/asmhentaiGet.ts +++ b/src/controller/asmhentai/asmhentaiGet.ts @@ -10,6 +10,35 @@ export async function getAsmhentai(req: Request, res: Response, next: NextFuncti if (!book) throw Error("Parameter book is required"); if (!isNumeric(book)) throw Error("Value must be number"); + /** + * @api {get} /asmhentai/get?book=:book Get asmhentai + * @apiName Get asmhentai + * @apiGroup asmhentai + * @apiDescription Get a doujinshi on asmhentai based on id + * + * @apiParam {Number} book Book ID + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/asmhentai/get?book=123 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/asmhentai/get?book=123") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/asmhentai/get?book=123") as resp: + * print(await resp.json()) + */ + const url = `${c.ASMHENTAI}/g/${book}/`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/asmhentai/asmhentaiRandom.ts b/src/controller/asmhentai/asmhentaiRandom.ts index 87fe446..17dc6a0 100644 --- a/src/controller/asmhentai/asmhentaiRandom.ts +++ b/src/controller/asmhentai/asmhentaiRandom.ts @@ -5,6 +5,34 @@ import { Request, Response, NextFunction } from "express"; export async function randomAsmhentai(req: Request, res: Response, next: NextFunction) { try { + /** + * @api {get} /asmhentai/random Random asmhentai + * @apiName Random asmhentai + * @apiGroup asmhentai + * @apiDescription Gets random doujinshi on asmhentai + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/asmhentai/random + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/asmhentai/random") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/asmhentai/random") as resp: + * print(await resp.json()) + * + */ + const url = `${c.ASMHENTAI}/random/`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/asmhentai/asmhentaiSearch.ts b/src/controller/asmhentai/asmhentaiSearch.ts index 52324b4..9d289f0 100644 --- a/src/controller/asmhentai/asmhentaiSearch.ts +++ b/src/controller/asmhentai/asmhentaiSearch.ts @@ -10,6 +10,35 @@ export async function searchAsmhentai(req: Request, res: Response, next: NextFun if (!key) throw Error("Parameter key is required"); + /** + * @api {get} /asmhentai/search Search asmhentai + * @apiName Search asmhentai + * @apiGroup asmhentai + * @apiDescription Search doujinshi on asmhentai + * @apiParam {String} key Keyword to search + * @apiParam {Number} [page=1] Page number + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/asmhentai/search?key=yuri + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/asmhentai/search?key=yuri") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/asmhentai/search?key=yuri") as resp: + * print(await resp.json()) + */ + const url = `${c.ASMHENTAI}/search/?q=${key}&page=${page}`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/hentai2read/hentai2readGet.ts b/src/controller/hentai2read/hentai2readGet.ts index 63a2b41..bb2eb68 100644 --- a/src/controller/hentai2read/hentai2readGet.ts +++ b/src/controller/hentai2read/hentai2readGet.ts @@ -9,6 +9,35 @@ export async function getHentai2read(req: Request, res: Response) { if (!book) throw Error("Parameter book is required"); if (book.split("/").length !== 2) throw Error("Book must be in format 'book_example/chapter'. Example: 'fate_lewd_summoning/1'"); + /** + * @api {get} /hentai2read/get?book=:book Get hentai2read + * @apiName Get hentai2read + * @apiGroup hentai2read + * @apiDescription Get a doujinshi on hentai2read + * + * @apiParam {String} book Book path + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/hentai2read/get?book=butabako_shotaone_matome_fgo_hen/1") as resp: + * print(await resp.json()) + */ + const url = `${c.HENTAI2READ}/${book}/`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/hentai2read/hentai2readSearch.ts b/src/controller/hentai2read/hentai2readSearch.ts index e8d01b4..858cbee 100644 --- a/src/controller/hentai2read/hentai2readSearch.ts +++ b/src/controller/hentai2read/hentai2readSearch.ts @@ -7,6 +7,34 @@ export async function searchHentai2read(req: Request, res: Response, next: NextF try { const key = req.query.key || ""; if (!key) throw Error("Parameter book is required"); + + /** + * @api {get} /hentai2read/search Search hentai2read + * @apiName Search hentai2read + * @apiGroup hentai2read + * @apiDescription Search doujinshi on hentai2read + * @apiParam {String} key Keyword to search + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/hentai2read/search?key=yuri + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/hentai2read/search?key=yuri") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/hentai2read/search?key=yuri") as resp: + * print(await resp.json()) + */ const url = `${c.HENTAI2READ}/hentai-list/search/${key}`; const data = await scrapeContent(url); diff --git a/src/controller/hentaifox/hentaifoxGet.ts b/src/controller/hentaifox/hentaifoxGet.ts index 3dfb8f7..4481204 100644 --- a/src/controller/hentaifox/hentaifoxGet.ts +++ b/src/controller/hentaifox/hentaifoxGet.ts @@ -10,6 +10,35 @@ export async function getHentaifox(req: Request, res: Response, next: NextFuncti if (!book) throw Error("Parameter book is required"); if (!isNumeric(book)) throw Error("Parameter book must be number"); + /** + * @api {get} /hentaifox/get?book=:book Get hentaifox + * @apiName Get hentaifox + * @apiGroup hentaifox + * @apiDescription Get a doujinshi on hentaifox based on id + * + * @apiParam {Number} book Book ID + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/hentaifox/get?book=123 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/hentaifox/get?book=123") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/hentaifox/get?book=123") as resp: + * print(await resp.json()) + */ + const url = `${c.HENTAIFOX}/gallery/${book}/`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/hentaifox/hentaifoxRandom.ts b/src/controller/hentaifox/hentaifoxRandom.ts index 58cc151..5f19756 100644 --- a/src/controller/hentaifox/hentaifoxRandom.ts +++ b/src/controller/hentaifox/hentaifoxRandom.ts @@ -5,6 +5,33 @@ import { logger } from "../../utils/logger"; export async function randomHentaifox(req: Request, res: Response, next: NextFunction) { try { + /** + * @api {get} /hentaifox/random Random hentaifox + * @apiName Random hentaifox + * @apiGroup hentaifox + * @apiDescription Gets random doujinshi on hentaifox + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/hentaifox/random + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/hentaifox/random") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/hentaifox/random") as resp: + * print(await resp.json()) + * + */ const url = `${c.HENTAIFOX}/random`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/hentaifox/hentaifoxSearch.ts b/src/controller/hentaifox/hentaifoxSearch.ts index 6e85898..0101c12 100644 --- a/src/controller/hentaifox/hentaifoxSearch.ts +++ b/src/controller/hentaifox/hentaifoxSearch.ts @@ -6,6 +6,37 @@ import { Request, Response, NextFunction } from "express"; export async function searchHentaifox(req: Request, res: Response, next: NextFunction) { try { + /** + * @api {get} /hentaifox/search Search hentaifox + * @apiName Search hentaifox + * @apiGroup hentaifox + * @apiDescription Search doujinshi on hentaifox + * @apiParam {String} key Keyword to search + * @apiParam {Number} [page=1] Page number + * @apiParam {String} [sort=latest] + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/hentaifox/search?key=yuri + * curl -i https://janda.mod.land/hentaifox/search?key=yuri&page=2&sort=latest + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/hentaifox/search?key=yuri") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/hentaifox/search?key=yuri") as resp: + * print(await resp.json()) + */ + const key = req.query.key as string; const page = req.query.page || 1; const sort = req.query.sort as string || sorting[0] as string; diff --git a/src/controller/nhentai/nhentaiGet.ts b/src/controller/nhentai/nhentaiGet.ts index 299570f..bea21ae 100644 --- a/src/controller/nhentai/nhentaiGet.ts +++ b/src/controller/nhentai/nhentaiGet.ts @@ -13,6 +13,35 @@ export async function getNhentai(req: Request, res: Response) { let actualAPI; if (!await mock(c.NHENTAI)) actualAPI = c.NHENTAI_IP_3; else actualAPI = c.NHENTAI; + + /** + * @api {get} /nhentai/get?book=:book Get nhentai + * @apiName Get nhentai + * @apiGroup nhentai + * @apiDescription Get a doujinshi on nhentai based on id + * + * @apiParam {Number} book Book ID + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/nhentai/get?book=123 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/nhentai/get?book=123") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/nhentai/get?book=123") as resp: + * print(await resp.json()) + */ const url = `${actualAPI}/api/gallery/${book}`; const data = await scrapeContent(url); diff --git a/src/controller/nhentai/nhentaiRandom.ts b/src/controller/nhentai/nhentaiRandom.ts index 097c325..5d37453 100644 --- a/src/controller/nhentai/nhentaiRandom.ts +++ b/src/controller/nhentai/nhentaiRandom.ts @@ -13,6 +13,34 @@ export async function randomNhentai(req: Request, res: Response, next: NextFunct const id = await getIdRandomNhentai(); + /** + * @api {get} /nhentai/random Random nhentai + * @apiName Random nhentai + * @apiGroup nhentai + * @apiDescription Gets random doujinshi on nhentai + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/nhentai/random + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/nhentai/random") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/nhentai/random") as resp: + * print(await resp.json()) + * + */ + const url = `${actualAPI}/api/gallery/${id}`; const data = await scrapeContent(url, true); logger.info({ diff --git a/src/controller/nhentai/nhentaiRelated.ts b/src/controller/nhentai/nhentaiRelated.ts index 0812de8..e8721ac 100644 --- a/src/controller/nhentai/nhentaiRelated.ts +++ b/src/controller/nhentai/nhentaiRelated.ts @@ -13,6 +13,35 @@ export async function relatedNhentai(req: Request, res: Response) { let actualAPI; if (!await mock(c.NHENTAI)) actualAPI = c.NHENTAI_IP_3; else actualAPI = c.NHENTAI; + + /** + * @api {get} /nhentai/related?book=:book Get related nhentai + * @apiName Get related nhentai + * @apiGroup nhentai + * @apiDescription Get related or similar doujinshi on nhentai based on id + * + * @apiParam {Number} book Book ID + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/nhentai/related?book=123 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/nhentai/related?book=123") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/nhentai/related?book=123") as resp: + * print(await resp.json()) + */ const url = `${actualAPI}/api/gallery/${book}/related`; const data = await scrapeContent(url); diff --git a/src/controller/nhentai/nhentaiSearch.ts b/src/controller/nhentai/nhentaiSearch.ts index f9cf854..9c884be 100644 --- a/src/controller/nhentai/nhentaiSearch.ts +++ b/src/controller/nhentai/nhentaiSearch.ts @@ -17,6 +17,37 @@ export async function searchNhentai(req: Request, res: Response) { if (!await mock(c.NHENTAI)) actualAPI = c.NHENTAI_IP_3; else actualAPI = c.NHENTAI; + /** + * @api {get} /nhentai/search Search nhentai + * @apiName Search nhentai + * @apiGroup nhentai + * @apiDescription Search doujinshi on nhentai + * @apiParam {String} key Keyword to search + * @apiParam {Number} [page=1] Page number + * @apiParam {String} [sort=popular-today] + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/nhentai/search?key=yuri + * curl -i https://janda.mod.land/nhentai/search?key=yuri&page=2&sort=popular-today + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/nhentai/search?key=yuri") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/nhentai/search?key=yuri") as resp: + * print(await resp.json()) + */ + const url = `${actualAPI}/api/galleries/search?query=${key}&sort=${sort}&page=${page}`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/pururin/pururinGet.ts b/src/controller/pururin/pururinGet.ts index 1749709..405bb38 100644 --- a/src/controller/pururin/pururinGet.ts +++ b/src/controller/pururin/pururinGet.ts @@ -10,6 +10,35 @@ export async function getPururin(req: Request, res: Response, next: NextFunction if (!book) throw Error("Parameter book is required"); if (!isNumeric(book)) throw Error("Parameter book must be number"); + /** + * @api {get} /pururin/get?book=:book Get pururin + * @apiName Get pururin + * @apiGroup pururin + * @apiDescription Get a doujinshi on pururin based on id + * + * @apiParam {Number} book Book ID + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/pururin/get?book=123 + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/pururin/get?book=123") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/pururin/get?book=123") as resp: + * print(await resp.json()) + */ + const url = `${c.PURURIN}/gallery/${book}/janda`; const data = await scrapeContent(url); logger.info({ diff --git a/src/controller/pururin/pururinRandom.ts b/src/controller/pururin/pururinRandom.ts index e95808e..6b6c800 100644 --- a/src/controller/pururin/pururinRandom.ts +++ b/src/controller/pururin/pururinRandom.ts @@ -7,6 +7,35 @@ import { Request, Response, NextFunction } from "express"; export async function randomPururin(req: Request, res: Response, next: NextFunction) { try { const id = await getIdRandomPururin(); + + /** + * @api {get} /pururin/random Random pururin + * @apiName Random pururin + * @apiGroup pururin + * @apiDescription Gets random doujinshi on pururin + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/pururin/random + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/pururin/random") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/pururin/random") as resp: + * print(await resp.json()) + * + */ + const url = `${c.PURURIN}/gallery/${id}/janda`; const data = await scrapeContent(url, true); logger.info({ diff --git a/src/controller/pururin/pururinSearch.ts b/src/controller/pururin/pururinSearch.ts index e464a8e..b6a2b03 100644 --- a/src/controller/pururin/pururinSearch.ts +++ b/src/controller/pururin/pururinSearch.ts @@ -11,6 +11,37 @@ export async function searchPururin(req: Request, res: Response, next: NextFunct const sort = req.query.sort as string || sorting[0] as string; if (!key) throw Error("Parameter key is required"); if (!sorting.includes(sort)) throw Error("Invalid sort: " + sorting.join(", ")); + + /** + * @api {get} /pururin/search Search pururin + * @apiName Search pururin + * @apiGroup pururin + * @apiDescription Search doujinshi on pururin + * @apiParam {String} key Keyword to search + * @apiParam {Number} [page=1] Page number + * @apiParam {String} [sort=newest] + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/pururin/search?key=yuri + * curl -i https://janda.mod.land/pururin/search?key=yuri&page=2&sort=newest + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/pururin/search?key=yuri") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/pururin/search?key=yuri") as resp: + * print(await resp.json()) + */ const url = `${c.PURURIN}/search/${sort}?q=${key}&page=${page}`; const data = await scrapeContent(url); diff --git a/src/controller/simply-hentai/simply-hentaiGet.ts b/src/controller/simply-hentai/simply-hentaiGet.ts index 07d2a49..a7c977f 100644 --- a/src/controller/simply-hentai/simply-hentaiGet.ts +++ b/src/controller/simply-hentai/simply-hentaiGet.ts @@ -11,6 +11,35 @@ export async function getSimplyhentai(req: Request, res: Response) { let actualAPI; if (!await mock(c.SIMPLY_HENTAI)) actualAPI = c.SIMPLY_HENTAI_PROXIFIED; + + /** + * @api {get} /simply-hentai/get?book=:book Get simply-hentai + * @apiName Get simply-hentai + * @apiGroup simply-hentai + * @apiDescription Get a doujinshi on simply-hentai + * + * @apiParam {String} book Book path + * + * @apiSuccessExample {json} Success-Response: + * HTTP/1.1 200 OK + * HTTP/1.1 200 (cached) + * + * @apiExample {curl} curl + * curl -i https://janda.mod.land/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages + * + * @apiExample {js} JS/TS + * import axios from "axios" + * + * axios.get("https://janda.mod.land/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages") + * .then(res => console.log(res.data)) + * .catch(err => console.error(err)) + * + * @apiExample {python} Python + * import aiohttp + * async with aiohttp.ClientSession() as session: + * async with session.get("https://janda.mod.land/simply-hentai/get?book=fate-grand-order/fgo-sanbunkatsuhou/all-pages") as resp: + * print(await resp.json()) + */ const url = `${actualAPI}/${book}`; const data = await scrapeContent(url); diff --git a/src/index.ts b/src/index.ts index 4fc3fab..0d29f99 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,6 @@ const janda = new JandaPress(); const app = express(); dotenv.config(); - app.get("/", slow, limiter, (req, res) => { res.send({ success: true, diff --git a/src/router/endpoint.ts b/src/router/endpoint.ts index a34c1bf..dd4f145 100644 --- a/src/router/endpoint.ts +++ b/src/router/endpoint.ts @@ -1,5 +1,7 @@ import { Router } from "express"; import cors from "cors"; +import { slow, limiter } from "../utils/limit-options"; + import { searchHentaifox } from "../controller/hentaifox/hentaifoxSearch"; import { getHentaifox } from "../controller/hentaifox/hentaifoxGet"; import { getPururin } from "../controller/pururin/pururinGet"; @@ -20,7 +22,6 @@ import { get3hentai } from "../controller/3hentai/3hentaiGet"; import { search3hentai } from "../controller/3hentai/3hentaiSearch"; import { random3hentai } from "../controller/3hentai/3hentaiRandom"; -import { slow, limiter } from "../utils/limit-options"; function scrapeRoutes() { const router = Router();