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 @@
-
+
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.
+
Playground •
Contributing •
-
Documentation •
Report 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();