UNPKG

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