@tomisakae/syosetu-api
Version:
Enterprise-grade Fastify TypeScript API for Syosetu.com data extraction using official API and web scraping. Run instantly with 'npx @tomisakae/syosetu-api'
391 lines • 16.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.syosetuRoutes = syosetuRoutes;
const controllers_1 = require("@/controllers");
const middleware_1 = require("@/middleware");
const utils_1 = require("@/utils");
const config_1 = require("@/config");
async function syosetuRoutes(fastify) {
await fastify.register(Promise.resolve().then(() => __importStar(require('@fastify/rate-limit'))), config_1.rateLimitConfig.api);
const contentRateLimit = {
...config_1.rateLimitConfig.content,
keyGenerator: (request) => request.ip,
};
fastify.get('/api/syosetu/novel/:ncode', {
preHandler: [(0, middleware_1.validateParams)(utils_1.ncodeParamsSchema)],
schema: {
tags: ['Syosetu'],
summary: 'Get novel metadata',
description: 'Retrieve detailed information about a novel using its ncode',
params: {
type: 'object',
properties: {
ncode: {
type: 'string',
pattern: '^[a-z0-9]+$',
description: 'Novel code (e.g., n4754kf)',
},
},
required: ['ncode'],
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
ncode: { type: 'string' },
title: { type: 'string' },
author: { type: 'string' },
summary: { type: 'string' },
biggenre: { type: 'number' },
genre: { type: 'number' },
keywords: { type: 'string' },
firstPublished: { type: 'string' },
lastUpdated: { type: 'string' },
novelType: { type: 'number' },
isCompleted: { type: 'boolean' },
totalChapters: { type: 'number' },
wordCount: { type: 'number' },
readingTime: { type: 'number' },
bookmarks: { type: 'number' },
impressions: { type: 'number' },
reviews: { type: 'number' },
points: { type: 'number' },
raters: { type: 'number' },
},
},
message: { type: 'string' },
timestamp: { type: 'string' },
},
},
400: {
type: 'object',
properties: {
success: { type: 'boolean' },
error: { type: 'string' },
timestamp: { type: 'string' },
},
},
404: {
type: 'object',
properties: {
success: { type: 'boolean' },
error: { type: 'string' },
timestamp: { type: 'string' },
},
},
},
},
}, controllers_1.syosetuController.getNovelDetails.bind(controllers_1.syosetuController));
fastify.get('/api/syosetu/novel/:ncode/chapter/:chapter', {
preHandler: [
fastify.rateLimit(contentRateLimit),
(0, middleware_1.validateParams)(utils_1.chapterParamsSchema),
],
schema: {
tags: ['Syosetu'],
summary: 'Get chapter content',
description: 'Retrieve the content of a specific chapter using web scraping',
params: {
type: 'object',
properties: {
ncode: {
type: 'string',
pattern: '^[a-z0-9]+$',
description: 'Novel code',
},
chapter: {
type: 'string',
pattern: '^\\d+$',
description: 'Chapter number (starting from 1)',
},
},
required: ['ncode', 'chapter'],
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
ncode: { type: 'string' },
chapterNumber: { type: 'number' },
title: { type: 'string' },
htmlContent: { type: 'string' },
textContent: { type: 'string' },
date: { type: 'string' },
characterCount: { type: 'number' },
estimatedReadingTime: { type: 'number' },
url: { type: 'string' },
},
},
message: { type: 'string' },
timestamp: { type: 'string' },
},
},
},
},
}, controllers_1.syosetuController.getChapterContent.bind(controllers_1.syosetuController));
fastify.post('/api/syosetu/novel/:ncode/chapters', {
preHandler: [
fastify.rateLimit(contentRateLimit),
(0, middleware_1.validateParams)(utils_1.ncodeParamsSchema),
(0, middleware_1.validateBody)(utils_1.multipleChaptersBodySchema),
],
schema: {
tags: ['Syosetu'],
summary: 'Get multiple chapters',
description: 'Retrieve content of multiple chapters at once (max 10)',
params: {
type: 'object',
properties: {
ncode: {
type: 'string',
pattern: '^[a-z0-9]+$',
description: 'Novel code',
},
},
required: ['ncode'],
},
body: {
type: 'object',
properties: {
chapters: {
type: 'array',
items: { type: 'number' },
minItems: 1,
maxItems: 10,
description: 'Array of chapter numbers',
},
},
required: ['chapters'],
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
ncode: { type: 'string' },
totalRequested: { type: 'number' },
successful: { type: 'number' },
failed: { type: 'number' },
chapters: { type: 'array' },
errors: { type: 'array' },
},
},
message: { type: 'string' },
timestamp: { type: 'string' },
},
},
},
},
}, controllers_1.syosetuController.getMultipleChapters.bind(controllers_1.syosetuController));
fastify.get('/api/syosetu/search', {
preHandler: [(0, middleware_1.validateQuery)(utils_1.searchQuerySchema)],
schema: {
tags: ['Search'],
summary: 'Search novels',
description: 'Search for novels using various filters and parameters',
querystring: {
type: 'object',
properties: {
keyword: {
type: 'string',
description: 'Search keyword (required)',
},
order: {
type: 'string',
enum: [
'new',
'favnovelcnt',
'reviewcnt',
'hyoka',
'weeklypoint',
'monthlypoint',
'yearlypoint',
'weekly',
],
default: 'new',
description: 'Sort order',
},
limit: {
type: 'string',
pattern: '^\\d+$',
default: '20',
description: 'Number of results (1-500)',
},
start: {
type: 'string',
pattern: '^\\d+$',
description: 'Start position (1-2000)',
},
biggenre: {
type: 'string',
description: 'Big genre filter',
},
genre: {
type: 'string',
description: 'Genre filter',
},
},
required: ['keyword'],
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
keyword: { type: 'string' },
totalFound: { type: 'number' },
results: {
type: 'array',
items: {
type: 'object',
properties: {
ncode: { type: 'string' },
title: { type: 'string' },
author: { type: 'string' },
summary: { type: 'string' },
biggenre: { type: 'number' },
genre: { type: 'number' },
keywords: { type: 'string' },
firstPublished: { type: 'string' },
lastUpdated: { type: 'string' },
novelType: { type: 'number' },
isCompleted: { type: 'boolean' },
totalChapters: { type: 'number' },
wordCount: { type: 'number' },
bookmarks: { type: 'number' },
points: { type: 'number' },
},
},
},
},
},
message: { type: 'string' },
timestamp: { type: 'string' },
},
},
},
},
}, controllers_1.syosetuController.searchNovels.bind(controllers_1.syosetuController));
fastify.get('/api/syosetu/ranking', {
preHandler: [(0, middleware_1.validateQuery)(utils_1.rankingQuerySchema)],
schema: {
tags: ['Ranking'],
summary: 'Get novel ranking',
description: 'Retrieve novel ranking based on various criteria',
querystring: {
type: 'object',
properties: {
order: {
type: 'string',
enum: [
'hyoka',
'favnovelcnt',
'reviewcnt',
'dailypoint',
'weeklypoint',
'monthlypoint',
'quarterpoint',
'yearlypoint',
],
default: 'hyoka',
description: 'Ranking order',
},
limit: {
type: 'string',
pattern: '^\\d+$',
default: '50',
description: 'Number of results (1-100)',
},
biggenre: {
type: 'string',
description: 'Big genre filter',
},
genre: {
type: 'string',
description: 'Genre filter',
},
},
},
response: {
200: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {
type: 'object',
properties: {
rankings: {
type: 'array',
items: {
type: 'object',
properties: {
rank: { type: 'number' },
ncode: { type: 'string' },
title: { type: 'string' },
author: { type: 'string' },
summary: { type: 'string' },
genre: { type: 'number' },
bookmarks: { type: 'number' },
points: { type: 'number' },
globalPoints: { type: 'number' },
},
},
},
},
},
message: { type: 'string' },
timestamp: { type: 'string' },
},
},
},
},
}, controllers_1.syosetuController.getRanking.bind(controllers_1.syosetuController));
}
//# sourceMappingURL=syosetu.routes.js.map