diff --git a/lib/app.tsx b/lib/app.tsx
index 53e85e2a56dee1..65feef3abfc31f 100644
--- a/lib/app.tsx
+++ b/lib/app.tsx
@@ -13,6 +13,7 @@ import header from '@/middleware/header';
import antiHotlink from '@/middleware/anti-hotlink';
import parameter from '@/middleware/parameter';
import { jsxRenderer } from 'hono/jsx-renderer';
+import { trimTrailingSlash } from 'hono/trailing-slash';
import logger from '@/utils/logger';
@@ -26,6 +27,7 @@ process.on('uncaughtException', (e) => {
const app = new Hono();
+app.use(trimTrailingSlash());
app.use(compress());
app.use(
diff --git a/lib/errors/index.test.ts b/lib/errors/index.test.ts
index f76af62198a959..e1af5105878618 100644
--- a/lib/errors/index.test.ts
+++ b/lib/errors/index.test.ts
@@ -22,7 +22,7 @@ describe('httperror', () => {
it(`httperror`, async () => {
const response = await request.get('/test/httperror');
expect(response.status).toBe(503);
- expect(response.text).toMatch('404 Not Found: target website might be blocking our access, you can host your own RSSHub instance for a better usability.');
+ expect(response.text).toMatch('FetchError: [GET] "https://httpbingo.org/status/404": 404 Not Found');
}, 20000);
});
@@ -31,10 +31,26 @@ describe('RequestInProgressError', () => {
const responses = await Promise.all([request.get('/test/slow'), request.get('/test/slow')]);
expect(new Set(responses.map((r) => r.status))).toEqual(new Set([200, 503]));
expect(new Set(responses.map((r) => r.headers['cache-control']))).toEqual(new Set([`public, max-age=${config.cache.routeExpire}`, `public, max-age=${config.requestTimeout / 1000}`]));
- expect(responses.filter((r) => r.text.includes('This path is currently fetching, please come back later!'))).toHaveLength(1);
+ expect(responses.filter((r) => r.text.includes('RequestInProgressError: This path is currently fetching, please come back later!'))).toHaveLength(1);
});
});
+describe('config-not-found-error', () => {
+ it(`config-not-found-error`, async () => {
+ const response = await request.get('/test/config-not-found-error');
+ expect(response.status).toBe(503);
+ expect(response.text).toMatch('ConfigNotFoundError: Test config not found error');
+ }, 20000);
+});
+
+describe('invalid-parameter-error', () => {
+ it(`invalid-parameter-error`, async () => {
+ const response = await request.get('/test/invalid-parameter-error');
+ expect(response.status).toBe(503);
+ expect(response.text).toMatch('InvalidParameterError: Test invalid parameter error');
+ }, 20000);
+});
+
describe('route throws an error', () => {
it('route path error should have path mounted', async () => {
await request.get('/test/error');
@@ -47,19 +63,19 @@ describe('route throws an error', () => {
const value = $(item).find('.debug-value').html()?.trim();
switch (key) {
case 'Request Amount:':
- expect(value).toBe('7');
+ expect(value).toBe('9');
break;
case 'Hot Routes:':
- expect(value).toBe('4 /test/:id
');
+ expect(value).toBe('6 /test/:id
');
break;
case 'Hot Paths:':
- expect(value).toBe('2 /test/error
2 /test/slow
1 /test/httperror
1 /thisDoesNotExist
1 /
');
+ expect(value).toBe('2 /test/error
2 /test/slow
1 /test/httperror
1 /test/config-not-found-error
1 /test/invalid-parameter-error
1 /thisDoesNotExist
1 /
');
break;
case 'Hot Error Routes:':
- expect(value).toBe('3 /test/:id
');
+ expect(value).toBe('5 /test/:id
');
break;
case 'Hot Error Paths:':
- expect(value).toBe('2 /test/error
1 /test/httperror
1 /test/slow
1 /thisDoesNotExist
');
+ expect(value).toBe('2 /test/error
1 /test/httperror
1 /test/slow
1 /test/config-not-found-error
1 /test/invalid-parameter-error
1 /thisDoesNotExist
');
break;
default:
}
diff --git a/lib/errors/index.tsx b/lib/errors/index.tsx
index 2f16d0512d4d79..56ba079e455fc3 100644
--- a/lib/errors/index.tsx
+++ b/lib/errors/index.tsx
@@ -5,9 +5,7 @@ import Sentry from '@sentry/node';
import logger from '@/utils/logger';
import Error from '@/views/error';
-import RequestInProgressError from './request-in-progress';
-import RejectError from './reject';
-import NotFoundError from './not-found';
+import NotFoundError from './types/not-found';
export const errorHandler: ErrorHandler = (error, ctx) => {
const requestPath = ctx.req.path;
@@ -38,27 +36,29 @@ export const errorHandler: ErrorHandler = (error, ctx) => {
});
}
- let message = '';
- if (error.name && (error.name === 'HTTPError' || error.name === 'RequestError' || error.name === 'FetchError')) {
- ctx.status(503);
- message = `${error.message}: target website might be blocking our access, you can host your own RSSHub instance for a better usability.`;
- } else if (error instanceof RequestInProgressError) {
- ctx.header('Cache-Control', `public, max-age=${config.requestTimeout / 1000}`);
- ctx.status(503);
- message = error.message;
- } else if (error instanceof RejectError) {
- ctx.status(403);
- message = error.message;
- } else if (error instanceof NotFoundError) {
- ctx.status(404);
- message = 'wrong path';
- if (ctx.req.path.endsWith('/')) {
- message += ', you can try removing the trailing slash in the path';
- }
- } else {
- ctx.status(503);
- message = process.env.NODE_ENV === 'production' ? error.message : error.stack || error.message;
+ let errorMessage = process.env.NODE_ENV === 'production' ? error.message : error.stack || error.message;
+ switch (error.constructor.name) {
+ case 'HTTPError':
+ case 'RequestError':
+ case 'FetchError':
+ ctx.status(503);
+ break;
+ case 'RequestInProgressError':
+ ctx.header('Cache-Control', `public, max-age=${config.requestTimeout / 1000}`);
+ ctx.status(503);
+ break;
+ case 'RejectError':
+ ctx.status(403);
+ break;
+ case 'NotFoundError':
+ ctx.status(404);
+ errorMessage += 'The route does not exist or has been deleted.';
+ break;
+ default:
+ ctx.status(503);
+ break;
}
+ const message = `${error.name}: ${errorMessage}`;
logger.error(`Error in ${requestPath}: ${message}`);
diff --git a/lib/errors/not-found.ts b/lib/errors/not-found.ts
deleted file mode 100644
index 2ba6e18b3e1945..00000000000000
--- a/lib/errors/not-found.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-class NotFoundError extends Error {}
-
-export default NotFoundError;
diff --git a/lib/errors/reject.ts b/lib/errors/reject.ts
deleted file mode 100644
index 6296482265cae3..00000000000000
--- a/lib/errors/reject.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-class RejectError extends Error {}
-
-export default RejectError;
diff --git a/lib/errors/request-in-progress.ts b/lib/errors/request-in-progress.ts
deleted file mode 100644
index 99118977e8e77f..00000000000000
--- a/lib/errors/request-in-progress.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-class RequestInProgressError extends Error {}
-
-export default RequestInProgressError;
diff --git a/lib/errors/types/config-not-found.ts b/lib/errors/types/config-not-found.ts
new file mode 100644
index 00000000000000..c96cea03c2b80e
--- /dev/null
+++ b/lib/errors/types/config-not-found.ts
@@ -0,0 +1,5 @@
+class ConfigNotFoundError extends Error {
+ name = 'ConfigNotFoundError';
+}
+
+export default ConfigNotFoundError;
diff --git a/lib/errors/types/invalid-parameter.ts b/lib/errors/types/invalid-parameter.ts
new file mode 100644
index 00000000000000..8599ec4d2af4f3
--- /dev/null
+++ b/lib/errors/types/invalid-parameter.ts
@@ -0,0 +1,5 @@
+class InvalidParameterError extends Error {
+ name = 'InvalidParameterError';
+}
+
+export default InvalidParameterError;
diff --git a/lib/errors/types/not-found.ts b/lib/errors/types/not-found.ts
new file mode 100644
index 00000000000000..9cba16b31e797a
--- /dev/null
+++ b/lib/errors/types/not-found.ts
@@ -0,0 +1,5 @@
+class NotFoundError extends Error {
+ name = 'NotFoundError';
+}
+
+export default NotFoundError;
diff --git a/lib/errors/types/reject.ts b/lib/errors/types/reject.ts
new file mode 100644
index 00000000000000..b6b91fe4967c4a
--- /dev/null
+++ b/lib/errors/types/reject.ts
@@ -0,0 +1,5 @@
+class RejectError extends Error {
+ name = 'RejectError';
+}
+
+export default RejectError;
diff --git a/lib/errors/types/request-in-progress.ts b/lib/errors/types/request-in-progress.ts
new file mode 100644
index 00000000000000..73ae4b5705d37c
--- /dev/null
+++ b/lib/errors/types/request-in-progress.ts
@@ -0,0 +1,5 @@
+class RequestInProgressError extends Error {
+ name = 'RequestInProgressError';
+}
+
+export default RequestInProgressError;
diff --git a/lib/middleware/access-control.ts b/lib/middleware/access-control.ts
index 24714869b8b65c..2ba1b237c764fd 100644
--- a/lib/middleware/access-control.ts
+++ b/lib/middleware/access-control.ts
@@ -1,7 +1,7 @@
import type { MiddlewareHandler } from 'hono';
import { config } from '@/config';
import md5 from '@/utils/md5';
-import RejectError from '@/errors/reject';
+import RejectError from '@/errors/types/reject';
const reject = () => {
throw new RejectError('Authentication failed. Access denied.');
diff --git a/lib/middleware/cache.ts b/lib/middleware/cache.ts
index a9e8c8a8f48368..5ceee99eadfd54 100644
--- a/lib/middleware/cache.ts
+++ b/lib/middleware/cache.ts
@@ -2,7 +2,7 @@ import xxhash from 'xxhash-wasm';
import type { MiddlewareHandler } from 'hono';
import { config } from '@/config';
-import RequestInProgressError from '@/errors/request-in-progress';
+import RequestInProgressError from '@/errors/types/request-in-progress';
import cacheModule from '@/utils/cache/index';
import { Data } from '@/types';
diff --git a/lib/middleware/parameter.test.ts b/lib/middleware/parameter.test.ts
index 6fb8788bbf35b2..4166157238ef63 100644
--- a/lib/middleware/parameter.test.ts
+++ b/lib/middleware/parameter.test.ts
@@ -324,7 +324,7 @@ describe('wrong_path', () => {
const response = await app.request('/wrong');
expect(response.status).toBe(404);
expect(response.headers.get('cache-control')).toBe(`public, max-age=${config.cache.routeExpire}`);
- expect(await response.text()).toMatch('wrong path');
+ expect(await response.text()).toMatch('The route does not exist or has been deleted.');
});
});
diff --git a/lib/routes/12306/index.ts b/lib/routes/12306/index.ts
index 392af5db14769a..769af199a1d113 100644
--- a/lib/routes/12306/index.ts
+++ b/lib/routes/12306/index.ts
@@ -7,6 +7,7 @@ import got from '@/utils/got';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const rootUrl = 'https://kyfw.12306.cn';
@@ -85,7 +86,7 @@ async function handler(ctx) {
},
});
if (response.data.data === undefined || response.data.data.length === 0) {
- throw new Error('没有找到相关车次,请检查参数是否正确');
+ throw new InvalidParameterError('没有找到相关车次,请检查参数是否正确');
}
const data = response.data.data.result;
const map = response.data.data.map;
diff --git a/lib/routes/163/news/rank.ts b/lib/routes/163/news/rank.ts
index be2be096827399..2c83f7064e4f45 100644
--- a/lib/routes/163/news/rank.ts
+++ b/lib/routes/163/news/rank.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import iconv from 'iconv-lite';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const rootUrl = 'https://news.163.com';
@@ -119,9 +120,9 @@ async function handler(ctx) {
const cfg = config[category];
if (!cfg) {
- throw new Error('Bad category. See docs');
+ throw new InvalidParameterError('Bad category. See docs');
} else if ((category !== 'whole' && type === 'click' && time === 'month') || (category === 'whole' && type === 'click' && time === 'hour') || (type === 'follow' && time === 'hour')) {
- throw new Error('Bad timeRange range. See docs');
+ throw new InvalidParameterError('Bad timeRange range. See docs');
}
const currentUrl = category === 'money' ? cfg.link : `${rootUrl}${cfg.link}`;
diff --git a/lib/routes/163/news/special.ts b/lib/routes/163/news/special.ts
index bbf0d051b4a8e7..c80244353cfa99 100644
--- a/lib/routes/163/news/special.ts
+++ b/lib/routes/163/news/special.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -43,7 +44,7 @@ export const route: Route = {
async function handler(ctx) {
if (!ctx.req.param('type')) {
- throw new Error('Bad parameter. See https://docs.rsshub.app/routes/game#wang-yi-da-shen');
+ throw new InvalidParameterError('Bad parameter. See https://docs.rsshub.app/routes/game#wang-yi-da-shen');
}
const selectedType = Number.parseInt(ctx.req.param('type'));
let type;
diff --git a/lib/routes/18comic/utils.ts b/lib/routes/18comic/utils.ts
index 9c8829e6a4e3b4..e4ea27d1b3746a 100644
--- a/lib/routes/18comic/utils.ts
+++ b/lib/routes/18comic/utils.ts
@@ -8,6 +8,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const defaultDomain = 'jmcomic1.me';
// list of address: https://jmcomic2.bet
@@ -15,7 +16,7 @@ const allowDomain = new Set(['18comic.vip', '18comic.org', 'jmcomic.me', 'jmcomi
const getRootUrl = (domain) => {
if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(domain)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
return `https://${domain}`;
diff --git a/lib/routes/19lou/index.ts b/lib/routes/19lou/index.ts
index ca36be62c2f7fd..25b092aca0f109 100644
--- a/lib/routes/19lou/index.ts
+++ b/lib/routes/19lou/index.ts
@@ -6,6 +6,7 @@ import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import iconv from 'iconv-lite';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const setCookie = function (cookieName, cookieValue, seconds, path, domain, secure) {
let expires = null;
@@ -52,7 +53,7 @@ export const route: Route = {
async function handler(ctx) {
const city = ctx.req.param('city') ?? 'www';
if (!isValidHost(city)) {
- throw new Error('Invalid city');
+ throw new InvalidParameterError('Invalid city');
}
const rootUrl = `https://${city}.19lou.com`;
diff --git a/lib/routes/4gamers/utils.ts b/lib/routes/4gamers/utils.ts
index b9a2f2f5d35336..b4a9d06194e597 100644
--- a/lib/routes/4gamers/utils.ts
+++ b/lib/routes/4gamers/utils.ts
@@ -5,6 +5,7 @@ import got from '@/utils/got';
import path from 'node:path';
import { art } from '@/utils/render';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const getCategories = (tryGet) =>
tryGet('4gamers:categories', async () => {
@@ -48,7 +49,7 @@ const parseItem = async (item) => {
case 'ImageGroupSection':
return renderImages(section.items);
default:
- throw new Error(`Unhandled section type: ${section['@type']} on ${item.link}`);
+ throw new InvalidParameterError(`Unhandled section type: ${section['@type']} on ${item.link}`);
}
})
.join('')
diff --git a/lib/routes/591/list.ts b/lib/routes/591/list.ts
index 4ba811bb8cb898..5b3502ba9e52ce 100644
--- a/lib/routes/591/list.ts
+++ b/lib/routes/591/list.ts
@@ -10,6 +10,7 @@ import { load } from 'cheerio';
import got from '@/utils/got';
import { art } from '@/utils/render';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const cookieJar = new CookieJar();
@@ -133,7 +134,7 @@ async function handler(ctx) {
const country = ctx.req.param('country') ?? 'tw';
if (!isValidHost(country) && country !== 'tw') {
- throw new Error('Invalid country codes. Only "tw" is supported now.');
+ throw new InvalidParameterError('Invalid country codes. Only "tw" is supported now.');
}
/** @type {House[]} */
diff --git a/lib/routes/91porn/utils.ts b/lib/routes/91porn/utils.ts
index 40ef32d86d060f..36c6a2e6157b01 100644
--- a/lib/routes/91porn/utils.ts
+++ b/lib/routes/91porn/utils.ts
@@ -1,9 +1,10 @@
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const allowDomain = new Set(['91porn.com', 'www.91porn.com', '0122.91p30.com', 'www.91zuixindizhi.com', 'w1218.91p46.com']);
const domainValidation = (domain) => {
if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(domain)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
};
diff --git a/lib/routes/acfun/article.ts b/lib/routes/acfun/article.ts
index 709ce97e755f3e..3c7b623e911944 100644
--- a/lib/routes/acfun/article.ts
+++ b/lib/routes/acfun/article.ts
@@ -3,6 +3,7 @@ import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const baseUrl = 'https://www.acfun.cn';
const categoryMap = {
@@ -66,13 +67,13 @@ export const route: Route = {
async function handler(ctx) {
const { categoryId, sortType = 'createTime', timeRange = 'all' } = ctx.req.param();
if (!categoryMap[categoryId]) {
- throw new Error(`Invalid category Id: ${categoryId}`);
+ throw new InvalidParameterError(`Invalid category Id: ${categoryId}`);
}
if (!sortTypeEnum.has(sortType)) {
- throw new Error(`Invalid sort type: ${sortType}`);
+ throw new InvalidParameterError(`Invalid sort type: ${sortType}`);
}
if (!timeRangeEnum.has(timeRange)) {
- throw new Error(`Invalid time range: ${timeRange}`);
+ throw new InvalidParameterError(`Invalid time range: ${timeRange}`);
}
const url = `${baseUrl}/v/list${categoryId}/index.htm`;
diff --git a/lib/routes/aip/journal-pupp.ts b/lib/routes/aip/journal-pupp.ts
index 9010c9182e3799..85779d01e874cb 100644
--- a/lib/routes/aip/journal-pupp.ts
+++ b/lib/routes/aip/journal-pupp.ts
@@ -4,6 +4,7 @@ import { puppeteerGet, renderDesc } from './utils';
import { config } from '@/config';
import { isValidHost } from '@/utils/valid-host';
import puppeteer from '@/utils/puppeteer';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const handler = async (ctx) => {
const pub = ctx.req.param('pub');
@@ -11,7 +12,7 @@ const handler = async (ctx) => {
const host = `https://pubs.aip.org`;
const jrnlUrl = `${host}/${pub}/${jrn}/issue`;
if (!isValidHost(pub)) {
- throw new Error('Invalid pub');
+ throw new InvalidParameterError('Invalid pub');
}
// use Puppeteer due to the obstacle by cloudflare challenge
diff --git a/lib/routes/aisixiang/thinktank.ts b/lib/routes/aisixiang/thinktank.ts
index 861d0a8d38438f..12239370e75d55 100644
--- a/lib/routes/aisixiang/thinktank.ts
+++ b/lib/routes/aisixiang/thinktank.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { rootUrl, ossUrl, ProcessFeed } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/thinktank/:id/:type?',
@@ -43,7 +44,7 @@ async function handler(ctx) {
.toArray()
.filter((h) => (type ? $(h).text() === type : true));
if (!targetList) {
- throw new Error(`Not found ${type} in ${id}: ${currentUrl}`);
+ throw new InvalidParameterError(`Not found ${type} in ${id}: ${currentUrl}`);
}
for (const l of targetList) {
diff --git a/lib/routes/bangumi/tv/subject/index.ts b/lib/routes/bangumi/tv/subject/index.ts
index 0e31441948735e..83416b2d83528e 100644
--- a/lib/routes/bangumi/tv/subject/index.ts
+++ b/lib/routes/bangumi/tv/subject/index.ts
@@ -3,6 +3,7 @@ import getComments from './comments';
import getFromAPI from './offcial-subject-api';
import getEps from './ep';
import { queryToBoolean } from '@/utils/readable-social';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/tv/subject/:id/:type?/:showOriginalName?',
@@ -50,7 +51,7 @@ async function handler(ctx) {
response = await getFromAPI('topic')(id, showOriginalName);
break;
default:
- throw new Error(`暂不支持对${type}的订阅`);
+ throw new InvalidParameterError(`暂不支持对${type}的订阅`);
}
return response;
}
diff --git a/lib/routes/bdys/index.ts b/lib/routes/bdys/index.ts
index 981dbe0de3d38a..5c541d877cc8ab 100644
--- a/lib/routes/bdys/index.ts
+++ b/lib/routes/bdys/index.ts
@@ -11,6 +11,7 @@ import { art } from '@/utils/render';
import path from 'node:path';
import asyncPool from 'tiny-async-pool';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
// Visit https://www.bdys.me for the list of domains
const allowDomains = new Set(['52bdys.com', 'bde4.icu', 'bdys01.com']);
@@ -107,7 +108,7 @@ async function handler(ctx) {
const site = ctx.req.query('domain') || 'bdys01.com';
if (!config.feature.allow_user_supply_unsafe_domain && !allowDomains.has(new URL(`https://${site}`).hostname)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const rootUrl = `https://www.${site}`;
diff --git a/lib/routes/bendibao/news.ts b/lib/routes/bendibao/news.ts
index 59969ba0903f39..bca4a37467f281 100644
--- a/lib/routes/bendibao/news.ts
+++ b/lib/routes/bendibao/news.ts
@@ -5,6 +5,7 @@ import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/news/:city',
@@ -43,7 +44,7 @@ export const route: Route = {
async function handler(ctx) {
const city = ctx.req.param('city');
if (!isValidHost(city)) {
- throw new Error('Invalid city');
+ throw new InvalidParameterError('Invalid city');
}
const rootUrl = `http://${city}.bendibao.com`;
diff --git a/lib/routes/bilibili/followers.ts b/lib/routes/bilibili/followers.ts
index 962d860d03df9e..3cb11f00439dce 100644
--- a/lib/routes/bilibili/followers.ts
+++ b/lib/routes/bilibili/followers.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import cache from './cache';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/user/followers/:uid/:loginUid',
@@ -45,7 +46,7 @@ async function handler(ctx) {
const cookie = config.bilibili.cookies[loginUid];
if (cookie === undefined) {
- throw new Error('缺少对应 loginUid 的 Bilibili 用户登录后的 Cookie 值 bilibili 用户关注动态系列路由');
+ throw new ConfigNotFoundError('缺少对应 loginUid 的 Bilibili 用户登录后的 Cookie 值 bilibili 用户关注动态系列路由');
}
const name = await cache.getUsernameFromUID(uid);
@@ -68,7 +69,7 @@ async function handler(ctx) {
},
});
if (response.data.code === -6 || response.data.code === -101) {
- throw new Error('对应 loginUid 的 Bilibili 用户的 Cookie 已过期');
+ throw new ConfigNotFoundError('对应 loginUid 的 Bilibili 用户的 Cookie 已过期');
}
const data = response.data.data.list;
diff --git a/lib/routes/bilibili/followings-article.ts b/lib/routes/bilibili/followings-article.ts
index 6eab9ceb6692d5..42ae47e72e0eeb 100644
--- a/lib/routes/bilibili/followings-article.ts
+++ b/lib/routes/bilibili/followings-article.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import cache from './cache';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/followings/article/:uid',
@@ -39,7 +40,7 @@ async function handler(ctx) {
const cookie = config.bilibili.cookies[uid];
if (cookie === undefined) {
- throw new Error('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
}
const response = await got({
@@ -51,7 +52,7 @@ async function handler(ctx) {
},
});
if (response.data.code === -6) {
- throw new Error('对应 uid 的 Bilibili 用户的 Cookie 已过期');
+ throw new ConfigNotFoundError('对应 uid 的 Bilibili 用户的 Cookie 已过期');
}
const cards = response.data.data.cards;
diff --git a/lib/routes/bilibili/followings-dynamic.ts b/lib/routes/bilibili/followings-dynamic.ts
index 5c637d98db3709..1e09fa53930c3c 100644
--- a/lib/routes/bilibili/followings-dynamic.ts
+++ b/lib/routes/bilibili/followings-dynamic.ts
@@ -6,6 +6,7 @@ import utils from './utils';
import JSONbig from 'json-bigint';
import { fallback, queryToBoolean } from '@/utils/readable-social';
import querystring from 'querystring';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/followings/dynamic/:uid/:routeParams?',
@@ -49,7 +50,7 @@ async function handler(ctx) {
const cookie = config.bilibili.cookies[uid];
if (cookie === undefined) {
- throw new Error('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
}
const response = await got({
@@ -61,7 +62,7 @@ async function handler(ctx) {
},
});
if (response.data.code === -6) {
- throw new Error('对应 uid 的 Bilibili 用户的 Cookie 已过期');
+ throw new ConfigNotFoundError('对应 uid 的 Bilibili 用户的 Cookie 已过期');
}
const data = JSONbig.parse(response.body).data.cards;
diff --git a/lib/routes/bilibili/followings-video.ts b/lib/routes/bilibili/followings-video.ts
index 50d50943a38c56..6a112ed90a8b45 100644
--- a/lib/routes/bilibili/followings-video.ts
+++ b/lib/routes/bilibili/followings-video.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import cache from './cache';
import { config } from '@/config';
import utils from './utils';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/followings/video/:uid/:disableEmbed?',
@@ -41,7 +42,7 @@ async function handler(ctx) {
const cookie = config.bilibili.cookies[uid];
if (cookie === undefined) {
- throw new Error('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
}
const response = await got({
@@ -53,7 +54,7 @@ async function handler(ctx) {
},
});
if (response.data.code === -6) {
- throw new Error('对应 uid 的 Bilibili 用户的 Cookie 已过期');
+ throw new ConfigNotFoundError('对应 uid 的 Bilibili 用户的 Cookie 已过期');
}
const cards = response.data.data.cards;
diff --git a/lib/routes/bilibili/followings.ts b/lib/routes/bilibili/followings.ts
index 82cce50d205e47..0304864a70f69c 100644
--- a/lib/routes/bilibili/followings.ts
+++ b/lib/routes/bilibili/followings.ts
@@ -2,6 +2,8 @@ import { Route } from '@/types';
import got from '@/utils/got';
import cache from './cache';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/user/followings/:uid/:loginUid',
@@ -43,7 +45,7 @@ async function handler(ctx) {
const loginUid = ctx.req.param('loginUid');
const cookie = config.bilibili.cookies[loginUid];
if (cookie === undefined) {
- throw new Error('缺少对应 loginUid 的 Bilibili 用户登录后的 Cookie 值 bilibili 用户关注动态系列路由');
+ throw new ConfigNotFoundError('缺少对应 loginUid 的 Bilibili 用户登录后的 Cookie 值 bilibili 用户关注动态系列路由');
}
const uid = ctx.req.param('uid');
@@ -67,11 +69,11 @@ async function handler(ctx) {
},
});
if (response.data.code === -6) {
- throw new Error('对应 loginUid 的 Bilibili 用户的 Cookie 已过期');
+ throw new ConfigNotFoundError('对应 loginUid 的 Bilibili 用户的 Cookie 已过期');
}
// 22115 : 用户已设置隐私,无法查看
if (response.data.code === 22115) {
- throw new Error(response.data.message);
+ throw new InvalidParameterError(response.data.message);
}
const data = response.data.data.list;
diff --git a/lib/routes/bilibili/manga-followings.ts b/lib/routes/bilibili/manga-followings.ts
index bd134bc784fa46..b992b3e4aa9988 100644
--- a/lib/routes/bilibili/manga-followings.ts
+++ b/lib/routes/bilibili/manga-followings.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import cache from './cache';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/manga/followings/:uid/:limits?',
@@ -39,7 +40,7 @@ async function handler(ctx) {
const cookie = config.bilibili.cookies[uid];
if (cookie === undefined) {
- throw new Error('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
}
const page_size = ctx.req.param('limits') || 10;
const link = 'https://manga.bilibili.com/account-center';
@@ -53,7 +54,7 @@ async function handler(ctx) {
},
});
if (response.data.code === -6) {
- throw new Error('对应 uid 的 Bilibili 用户的 Cookie 已过期');
+ throw new ConfigNotFoundError('对应 uid 的 Bilibili 用户的 Cookie 已过期');
}
const comics = response.data.data;
diff --git a/lib/routes/bilibili/watchlater.ts b/lib/routes/bilibili/watchlater.ts
index fe43348eefec06..cb0cf9e31c7a59 100644
--- a/lib/routes/bilibili/watchlater.ts
+++ b/lib/routes/bilibili/watchlater.ts
@@ -4,6 +4,7 @@ import cache from './cache';
import { config } from '@/config';
import utils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/watchlater/:uid/:disableEmbed?',
@@ -42,7 +43,7 @@ async function handler(ctx) {
const cookie = config.bilibili.cookies[uid];
if (cookie === undefined) {
- throw new Error('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少对应 uid 的 Bilibili 用户登录后的 Cookie 值');
}
const response = await got({
@@ -55,7 +56,7 @@ async function handler(ctx) {
});
if (response.data.code) {
const message = response.data.code === -6 ? '对应 uid 的 Bilibili 用户的 Cookie 已过期' : response.data.message;
- throw new Error(`Error code ${response.data.code}: ${message}`);
+ throw new ConfigNotFoundError(`Error code ${response.data.code}: ${message}`);
}
const list = response.data.data.list || [];
diff --git a/lib/routes/biquge/index.ts b/lib/routes/biquge/index.ts
index 2f71468e6ffffa..581d94db85a465 100644
--- a/lib/routes/biquge/index.ts
+++ b/lib/routes/biquge/index.ts
@@ -7,6 +7,7 @@ import iconv from 'iconv-lite';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const allowHost = new Set([
'www.xbiquwx.la',
'www.biqu5200.net',
@@ -36,7 +37,7 @@ async function handler(ctx) {
const rootUrl = getSubPath(ctx).split('/').slice(1, 4).join('/');
const currentUrl = getSubPath(ctx).slice(1);
if (!config.feature.allow_user_supply_unsafe_domain && !allowHost.has(new URL(rootUrl).hostname)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const response = await got({
diff --git a/lib/routes/btzj/index.ts b/lib/routes/btzj/index.ts
index 8d775f73cc430f..58687e1a240098 100644
--- a/lib/routes/btzj/index.ts
+++ b/lib/routes/btzj/index.ts
@@ -10,6 +10,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const allowDomain = new Set(['2btjia.com', '88btbtt.com', 'btbtt15.com', 'btbtt20.com']);
export const route: Route = {
@@ -71,7 +72,7 @@ async function handler(ctx) {
let category = ctx.req.param('category') ?? '';
let domain = ctx.req.query('domain') ?? 'btbtt15.com';
if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(new URL(`http://${domain}/`).hostname)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
if (category === 'base') {
diff --git a/lib/routes/caixin/blog.ts b/lib/routes/caixin/blog.ts
index dc648a63ae6d10..add66df27bb785 100644
--- a/lib/routes/caixin/blog.ts
+++ b/lib/routes/caixin/blog.ts
@@ -5,6 +5,7 @@ import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
import { parseDate } from '@/utils/parse-date';
import { parseBlogArticle } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/blog/:column?',
@@ -30,7 +31,7 @@ async function handler(ctx) {
const { limit = 20 } = ctx.req.query();
if (column) {
if (!isValidHost(column)) {
- throw new Error('Invalid column');
+ throw new InvalidParameterError('Invalid column');
}
const link = `https://${column}.blog.caixin.com`;
const { data: response } = await got(link);
diff --git a/lib/routes/caixin/category.ts b/lib/routes/caixin/category.ts
index 31732e09a35639..60f17b21aad69f 100644
--- a/lib/routes/caixin/category.ts
+++ b/lib/routes/caixin/category.ts
@@ -6,6 +6,7 @@ import { isValidHost } from '@/utils/valid-host';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { parseArticle } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:column/:category',
@@ -47,7 +48,7 @@ async function handler(ctx) {
const column = ctx.req.param('column');
const url = `https://${column}.caixin.com/${category}`;
if (!isValidHost(column)) {
- throw new Error('Invalid column');
+ throw new InvalidParameterError('Invalid column');
}
const response = await got(url);
diff --git a/lib/routes/cls/depth.ts b/lib/routes/cls/depth.ts
index c14f3d6aeda93d..3d81bb1dd2f19d 100644
--- a/lib/routes/cls/depth.ts
+++ b/lib/routes/cls/depth.ts
@@ -10,6 +10,7 @@ import { art } from '@/utils/render';
import path from 'node:path';
import { rootUrl, getSearchParams } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const categories = {
1000: '头条',
@@ -57,7 +58,7 @@ async function handler(ctx) {
const title = categories[category];
if (!title) {
- throw new Error('Bad category. See docs');
+ throw new InvalidParameterError('Bad category. See docs');
}
const apiUrl = `${rootUrl}/v3/depth/home/assembled/${category}`;
diff --git a/lib/routes/cnjxol/index.ts b/lib/routes/cnjxol/index.ts
index ef2fe0a714ad7e..c28f69a1030c3d 100644
--- a/lib/routes/cnjxol/index.ts
+++ b/lib/routes/cnjxol/index.ts
@@ -8,6 +8,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const categories = {
jxrb: '嘉兴日报',
@@ -25,7 +26,7 @@ async function handler(ctx) {
const category = ctx.req.param('category') ?? 'jxrb';
const id = ctx.req.param('id');
if (!Object.keys(categories).includes(category)) {
- throw new Error('Invalid category');
+ throw new InvalidParameterError('Invalid category');
}
const rootUrl = `https://${category}.cnjxol.com`;
diff --git a/lib/routes/comicskingdom/index.ts b/lib/routes/comicskingdom/index.ts
index 4f89fc61cd4e35..ee4026017f397c 100644
--- a/lib/routes/comicskingdom/index.ts
+++ b/lib/routes/comicskingdom/index.ts
@@ -8,6 +8,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:name',
@@ -50,7 +51,7 @@ async function handler(ctx) {
.map((el) => $(el).find('a').first().attr('href'));
if (links.length === 0) {
- throw new Error(`Comic Not Found - ${name}`);
+ throw new InvalidParameterError(`Comic Not Found - ${name}`);
}
const items = await Promise.all(
links.map((link) =>
diff --git a/lib/routes/coolapk/dyh.ts b/lib/routes/coolapk/dyh.ts
index 4844cfd5195f6b..8b386a72505c26 100644
--- a/lib/routes/coolapk/dyh.ts
+++ b/lib/routes/coolapk/dyh.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/dyh/:dyhId',
@@ -48,7 +49,7 @@ async function handler(ctx) {
out = out.filter(Boolean); // 去除空值
if (out.length === 0) {
- throw new Error('仅限于采集站内订阅的看看号的图文及动态内容。这个ID可能是站外订阅。');
+ throw new InvalidParameterError('仅限于采集站内订阅的看看号的图文及动态内容。这个ID可能是站外订阅。');
}
return {
title: `酷安看看号-${targetTitle}`,
diff --git a/lib/routes/coolapk/huati.ts b/lib/routes/coolapk/huati.ts
index 5d8de470a8678a..7fc9820e895ffe 100644
--- a/lib/routes/coolapk/huati.ts
+++ b/lib/routes/coolapk/huati.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/huati/:tag',
@@ -32,7 +33,7 @@ async function handler(ctx) {
out = out.filter(Boolean); // 去除空值
if (out.length === 0) {
- throw new Error('这个话题还没有被创建或现在没有图文及动态内容。');
+ throw new InvalidParameterError('这个话题还没有被创建或现在没有图文及动态内容。');
}
return {
title: `酷安话题-${tag}`,
diff --git a/lib/routes/coolapk/user-dynamic.ts b/lib/routes/coolapk/user-dynamic.ts
index e380fe1f155639..965df483525cc9 100644
--- a/lib/routes/coolapk/user-dynamic.ts
+++ b/lib/routes/coolapk/user-dynamic.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/user/:uid/dynamic',
@@ -29,7 +30,7 @@ async function handler(ctx) {
});
const data = response.data.data;
if (!data) {
- throw new Error('这个人没有任何动态。');
+ throw new InvalidParameterError('这个人没有任何动态。');
}
let out = await Promise.all(
data.map((item) => {
@@ -43,7 +44,7 @@ async function handler(ctx) {
out = out.filter(Boolean); // 去除空值
if (out.length === 0) {
- throw new Error('这个人还没有图文或动态。');
+ throw new InvalidParameterError('这个人还没有图文或动态。');
}
return {
title: `酷安个人动态-${username}`,
diff --git a/lib/routes/discord/channel.ts b/lib/routes/discord/channel.ts
index 8474b51a169146..74c6fa34c3e204 100644
--- a/lib/routes/discord/channel.ts
+++ b/lib/routes/discord/channel.ts
@@ -8,6 +8,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { baseUrl, getChannel, getChannelMessages, getGuild } from './discord-api';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/channel/:channelId',
@@ -39,7 +40,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.discord || !config.discord.authorization) {
- throw new Error('Discord RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Discord RSS is disabled due to the lack of relevant config');
}
const { authorization } = config.discord;
const channelId = ctx.req.param('channelId');
diff --git a/lib/routes/discourse/utils.ts b/lib/routes/discourse/utils.ts
index 897738ed72518a..8638a86ffe4fbf 100644
--- a/lib/routes/discourse/utils.ts
+++ b/lib/routes/discourse/utils.ts
@@ -1,8 +1,9 @@
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
function getConfig(ctx) {
if (!config.discourse.config[ctx.req.param('configId')]) {
- throw new Error('Discourse RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Discourse RSS is disabled due to the lack of relevant config');
}
return config.discourse.config[ctx.req.param('configId')];
}
diff --git a/lib/routes/discuz/discuz.ts b/lib/routes/discuz/discuz.ts
index 864f09a72e35f2..e68a74485613fe 100644
--- a/lib/routes/discuz/discuz.ts
+++ b/lib/routes/discuz/discuz.ts
@@ -5,6 +5,8 @@ import { load } from 'cheerio';
import iconv from 'iconv-lite';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
function fixUrl(itemLink, baseUrl) {
// 处理相对链接
@@ -68,7 +70,7 @@ async function handler(ctx) {
const cookie = cid === undefined ? '' : config.discuz.cookies[cid];
if (cookie === undefined) {
- throw new Error('缺少对应论坛的cookie.');
+ throw new ConfigNotFoundError('缺少对应论坛的cookie.');
}
const header = {
@@ -149,7 +151,7 @@ async function handler(ctx) {
)
);
} else {
- throw new Error('不支持当前Discuz版本.');
+ throw new InvalidParameterError('不支持当前Discuz版本.');
}
return {
diff --git a/lib/routes/dlsite/campaign.ts b/lib/routes/dlsite/campaign.ts
index be219f3518628a..bcf575ce24b5ef 100644
--- a/lib/routes/dlsite/campaign.ts
+++ b/lib/routes/dlsite/campaign.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -141,7 +142,7 @@ async function handler(ctx) {
const info = infos[ctx.req.param('type')];
// 判断参数是否合理
if (info === undefined) {
- throw new Error('不支持指定类型!');
+ throw new InvalidParameterError('不支持指定类型!');
}
if (ctx.req.param('free') !== undefined) {
info.params.is_free = 1;
diff --git a/lib/routes/dlsite/new.ts b/lib/routes/dlsite/new.ts
index 90c5a7d84963d3..f4b4d3bb468b32 100644
--- a/lib/routes/dlsite/new.ts
+++ b/lib/routes/dlsite/new.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const host = 'https://www.dlsite.com';
const infos = {
@@ -75,7 +76,7 @@ async function handler(ctx) {
const info = infos[ctx.req.param('type')];
// 判断参数是否合理
if (info === undefined) {
- throw new Error('不支持指定类型!');
+ throw new InvalidParameterError('不支持指定类型!');
}
const link = info.url.slice(1);
diff --git a/lib/routes/domp4/utils.ts b/lib/routes/domp4/utils.ts
index d6e0f6bf10f121..15d045292b0a6c 100644
--- a/lib/routes/domp4/utils.ts
+++ b/lib/routes/domp4/utils.ts
@@ -1,4 +1,5 @@
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const defaultDomain = 'mp4us.com';
@@ -87,7 +88,7 @@ function decodeCipherText(p, a, c, k, e, d) {
function ensureDomain(ctx, domain = defaultDomain) {
const origin = `https://${domain}`;
if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomains.has(new URL(origin).hostname)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
return origin;
}
diff --git a/lib/routes/douyin/hashtag.ts b/lib/routes/douyin/hashtag.ts
index 4cafd12d1dc7b8..d7ef5538c83115 100644
--- a/lib/routes/douyin/hashtag.ts
+++ b/lib/routes/douyin/hashtag.ts
@@ -6,6 +6,7 @@ import { config } from '@/config';
import { fallback, queryToBoolean } from '@/utils/readable-social';
import { templates, resolveUrl, proxyVideo, getOriginAvatar } from './utils';
import puppeteer from '@/utils/puppeteer';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/hashtag/:cid/:routeParams?',
@@ -34,7 +35,7 @@ export const route: Route = {
async function handler(ctx) {
const cid = ctx.req.param('cid');
if (isNaN(cid)) {
- throw new TypeError('Invalid tag ID. Tag ID should be a number.');
+ throw new InvalidParameterError('Invalid tag ID. Tag ID should be a number.');
}
const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams')));
const embed = fallback(undefined, queryToBoolean(routeParams.embed), false); // embed video
diff --git a/lib/routes/douyin/live.ts b/lib/routes/douyin/live.ts
index bf640a007bc6d3..f4eaf5fb491cdb 100644
--- a/lib/routes/douyin/live.ts
+++ b/lib/routes/douyin/live.ts
@@ -4,6 +4,7 @@ import { config } from '@/config';
import { getOriginAvatar } from './utils';
import logger from '@/utils/logger';
import puppeteer from '@/utils/puppeteer';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/live/:rid',
@@ -31,7 +32,7 @@ export const route: Route = {
async function handler(ctx) {
const rid = ctx.req.param('rid');
if (isNaN(rid)) {
- throw new TypeError('Invalid room ID. Room ID should be a number.');
+ throw new InvalidParameterError('Invalid room ID. Room ID should be a number.');
}
const pageUrl = `https://live.douyin.com/${rid}`;
diff --git a/lib/routes/douyin/user.ts b/lib/routes/douyin/user.ts
index 72053101dca970..f4bf243df3888c 100644
--- a/lib/routes/douyin/user.ts
+++ b/lib/routes/douyin/user.ts
@@ -5,6 +5,7 @@ import { art } from '@/utils/render';
import { config } from '@/config';
import { fallback, queryToBoolean } from '@/utils/readable-social';
import { templates, resolveUrl, proxyVideo, getOriginAvatar, universalGet } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/user/:uid/:routeParams?',
@@ -33,7 +34,7 @@ export const route: Route = {
async function handler(ctx) {
const uid = ctx.req.param('uid');
if (!uid.startsWith('MS4wLjABAAAA')) {
- throw new Error('Invalid UID. UID should start with MS4wLjABAAAA.');
+ throw new InvalidParameterError('Invalid UID. UID should start with MS4wLjABAAAA.');
}
const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams')));
const embed = fallback(undefined, queryToBoolean(routeParams.embed), false); // embed video
diff --git a/lib/routes/dut/index.ts b/lib/routes/dut/index.ts
index a671c82904076f..dbee2d986bb95a 100644
--- a/lib/routes/dut/index.ts
+++ b/lib/routes/dut/index.ts
@@ -6,6 +6,7 @@ import { parseDate } from '@/utils/parse-date';
import defaults from './defaults';
import shortcuts from './shortcuts';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: ['/*/*', '/:0?'],
@@ -17,7 +18,7 @@ export const route: Route = {
async function handler(ctx) {
const site = ctx.params[0] ?? 'news';
if (!isValidHost(site)) {
- throw new Error('Invalid site');
+ throw new InvalidParameterError('Invalid site');
}
let items;
diff --git a/lib/routes/eagle/blog.ts b/lib/routes/eagle/blog.ts
index 4bf28bb30d1dde..d14e6cf18dd56a 100644
--- a/lib/routes/eagle/blog.ts
+++ b/lib/routes/eagle/blog.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const cateList = new Set(['all', 'design-resources', 'learn-design', 'inside-eagle']);
export const route: Route = {
@@ -35,7 +36,7 @@ async function handler(ctx) {
let cate = ctx.req.param('cate') ?? 'all';
let language = ctx.req.param('language') ?? 'cn';
if (!isValidHost(cate) || !isValidHost(language)) {
- throw new Error('Invalid host');
+ throw new InvalidParameterError('Invalid host');
}
if (!cateList.has(cate)) {
language = cate;
diff --git a/lib/routes/ehentai/favorites.ts b/lib/routes/ehentai/favorites.ts
index 2c0dac5a7566f1..5ff6a8a38d0549 100644
--- a/lib/routes/ehentai/favorites.ts
+++ b/lib/routes/ehentai/favorites.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import EhAPI from './ehapi';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/favorites/:favcat?/:order?/:page?/:routeParams?',
@@ -22,7 +23,7 @@ export const route: Route = {
async function handler(ctx) {
if (!EhAPI.has_cookie) {
- throw new Error('Ehentai favorites RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Ehentai favorites RSS is disabled due to the lack of relevant config');
}
const favcat = ctx.req.param('favcat') ? Number.parseInt(ctx.req.param('favcat')) : 0;
const page = ctx.req.param('page');
diff --git a/lib/routes/eprice/rss.ts b/lib/routes/eprice/rss.ts
index 4facb99afad8fd..510eb66caf3714 100644
--- a/lib/routes/eprice/rss.ts
+++ b/lib/routes/eprice/rss.ts
@@ -9,6 +9,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const allowRegion = new Set(['tw', 'hk']);
export const route: Route = {
@@ -37,7 +38,7 @@ export const route: Route = {
async function handler(ctx) {
const region = ctx.req.param('region') ?? 'tw';
if (!allowRegion.has(region)) {
- throw new Error('Invalid region');
+ throw new InvalidParameterError('Invalid region');
}
const feed = await parser.parseURL(`https://www.eprice.com.${region}/news/rss.xml`);
diff --git a/lib/routes/fansly/utils.ts b/lib/routes/fansly/utils.ts
index b95c937671f362..91f47ca2243e8c 100644
--- a/lib/routes/fansly/utils.ts
+++ b/lib/routes/fansly/utils.ts
@@ -4,6 +4,7 @@ const __dirname = getCurrentPath(import.meta.url);
import got from '@/utils/got';
import path from 'node:path';
import { art } from '@/utils/render';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const apiBaseUrl = 'https://apiv3.fansly.com';
const baseUrl = 'https://fansly.com';
@@ -27,7 +28,7 @@ const getAccountByUsername = (username, tryGet) =>
});
if (!accountResponse.response.length) {
- throw new Error('This profile or page does not exist.');
+ throw new InvalidParameterError('This profile or page does not exist.');
}
return accountResponse.response[0];
diff --git a/lib/routes/ff14/ff14-global.ts b/lib/routes/ff14/ff14-global.ts
index 2d54646209a1ae..3ffb4561ab729e 100644
--- a/lib/routes/ff14/ff14-global.ts
+++ b/lib/routes/ff14/ff14-global.ts
@@ -7,6 +7,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: ['/global/:lang/:type?', '/ff14_global/:lang/:type?'],
@@ -41,7 +42,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'all';
if (!isValidHost(lang)) {
- throw new Error('Invalid lang');
+ throw new InvalidParameterError('Invalid lang');
}
const response = await got({
diff --git a/lib/routes/finviz/news.ts b/lib/routes/finviz/news.ts
index 93d541b285454f..8ef797301c5459 100644
--- a/lib/routes/finviz/news.ts
+++ b/lib/routes/finviz/news.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const categories = {
news: 0,
@@ -41,7 +42,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 200;
if (!Object.hasOwn(categories, category.toLowerCase())) {
- throw new Error(`No category '${category}'.`);
+ throw new InvalidParameterError(`No category '${category}'.`);
}
const rootUrl = 'https://finviz.com';
diff --git a/lib/routes/gamme/category.ts b/lib/routes/gamme/category.ts
index fcca76ae4d6284..e60ab5a84f4eba 100644
--- a/lib/routes/gamme/category.ts
+++ b/lib/routes/gamme/category.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import parser from '@/utils/rss-parser';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:domain/:category?',
@@ -15,7 +16,7 @@ export const route: Route = {
async function handler(ctx) {
const { domain = 'news', category } = ctx.req.param();
if (!isValidHost(domain)) {
- throw new Error('Invalid domain');
+ throw new InvalidParameterError('Invalid domain');
}
const baseUrl = `https://${domain}.gamme.com.tw`;
const feed = await parser.parseURL(`${baseUrl + (category ? `/category/${category}` : '')}/feed`);
diff --git a/lib/routes/gamme/tag.ts b/lib/routes/gamme/tag.ts
index 106faa8051afbd..b9dbb922e49648 100644
--- a/lib/routes/gamme/tag.ts
+++ b/lib/routes/gamme/tag.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:domain/tag/:tag',
@@ -15,7 +16,7 @@ export const route: Route = {
async function handler(ctx) {
const { domain = 'news', tag } = ctx.req.param();
if (!isValidHost(domain)) {
- throw new Error('Invalid domain');
+ throw new InvalidParameterError('Invalid domain');
}
const baseUrl = `https://${domain}.gamme.com.tw`;
const pageUrl = `${baseUrl}/tag/${tag}`;
diff --git a/lib/routes/gcores/category.ts b/lib/routes/gcores/category.ts
index b02c4eb0b09a57..269a2a8d1a439d 100644
--- a/lib/routes/gcores/category.ts
+++ b/lib/routes/gcores/category.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -60,7 +61,7 @@ async function handler(ctx) {
list = list.get();
if (list.length > 0 && list.every((item) => item.url === undefined)) {
- throw new Error('Article URL not found! Please submit an issue on GitHub.');
+ throw new InvalidParameterError('Article URL not found! Please submit an issue on GitHub.');
}
const out = await Promise.all(
diff --git a/lib/routes/gcores/tag.ts b/lib/routes/gcores/tag.ts
index c6490932efcaec..d32c63351e0a68 100644
--- a/lib/routes/gcores/tag.ts
+++ b/lib/routes/gcores/tag.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -52,7 +53,7 @@ async function handler(ctx) {
.get();
if (list.length > 0 && list.every((item) => item.url === undefined)) {
- throw new Error('Article URL not found! Please submit an issue on GitHub.');
+ throw new InvalidParameterError('Article URL not found! Please submit an issue on GitHub.');
}
const out = await Promise.all(
diff --git a/lib/routes/github/follower.ts b/lib/routes/github/follower.ts
index 6a6dabb9a8160b..30363b8b29ad7b 100644
--- a/lib/routes/github/follower.ts
+++ b/lib/routes/github/follower.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/user/followers/:user',
@@ -27,7 +28,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.github || !config.github.access_token) {
- throw new Error('GitHub follower RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('GitHub follower RSS is disabled due to the lack of relevant config');
}
const user = ctx.req.param('user');
diff --git a/lib/routes/github/notifications.ts b/lib/routes/github/notifications.ts
index e0594bd46ff566..9668c410e39dca 100644
--- a/lib/routes/github/notifications.ts
+++ b/lib/routes/github/notifications.ts
@@ -4,6 +4,7 @@ import { parseDate } from '@/utils/parse-date';
const apiUrl = 'https://api.github.com';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/notifications',
@@ -36,7 +37,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.github || !config.github.access_token) {
- throw new Error('GitHub trending RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('GitHub trending RSS is disabled due to the lack of relevant config');
}
const headers = {
Accept: 'application/vnd.github.v3+json',
diff --git a/lib/routes/github/star.ts b/lib/routes/github/star.ts
index 06e2b1b2bac557..d7597ee2336605 100644
--- a/lib/routes/github/star.ts
+++ b/lib/routes/github/star.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/stars/:user/:repo',
@@ -27,7 +28,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.github || !config.github.access_token) {
- throw new Error('GitHub star RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('GitHub star RSS is disabled due to the lack of relevant config');
}
const user = ctx.req.param('user');
const repo = ctx.req.param('repo');
diff --git a/lib/routes/github/trending.ts b/lib/routes/github/trending.ts
index 4ec4313cb998dc..a7c31e9e93668c 100644
--- a/lib/routes/github/trending.ts
+++ b/lib/routes/github/trending.ts
@@ -7,6 +7,7 @@ import got from '@/utils/got';
import { art } from '@/utils/render';
import { load } from 'cheerio';
import path from 'node:path';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/trending/:since/:language/:spoken_language?',
@@ -44,7 +45,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.github || !config.github.access_token) {
- throw new Error('GitHub trending RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('GitHub trending RSS is disabled due to the lack of relevant config');
}
const since = ctx.req.param('since');
const language = ctx.req.param('language') === 'any' ? '' : ctx.req.param('language');
diff --git a/lib/routes/google/fonts.ts b/lib/routes/google/fonts.ts
index 4b32499540c279..623b5271df85e7 100644
--- a/lib/routes/google/fonts.ts
+++ b/lib/routes/google/fonts.ts
@@ -7,6 +7,7 @@ import { config } from '@/config';
import { art } from '@/utils/render';
import path from 'node:path';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const titleMap = {
date: 'Newest',
@@ -52,7 +53,7 @@ async function handler(ctx) {
const API_KEY = config.google.fontsApiKey;
if (!API_KEY) {
- throw new Error('Google Fonts API key is required.');
+ throw new ConfigNotFoundError('Google Fonts API key is required.');
}
const googleFontsAPI = `https://www.googleapis.com/webfonts/v1/webfonts?sort=${sort}&key=${API_KEY}`;
diff --git a/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts b/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts
index ed648795221e50..0feb049a894efa 100644
--- a/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts
+++ b/lib/routes/gov/shenzhen/xxgk/zfxxgj.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const rootUrl = 'http://www.sz.gov.cn/cn/xxgk/zfxxgj/';
const config = {
@@ -49,7 +50,7 @@ export const route: Route = {
async function handler(ctx) {
const cfg = config[ctx.req.param('caty')];
if (!cfg) {
- throw new Error('Bad category. See docs');
+ throw new InvalidParameterError('Bad category. See docs');
}
const currentUrl = new URL(cfg.link, rootUrl).href;
diff --git a/lib/routes/gov/shenzhen/zjj/index.ts b/lib/routes/gov/shenzhen/zjj/index.ts
index 0674eef013580a..8893e007e4c7f8 100644
--- a/lib/routes/gov/shenzhen/zjj/index.ts
+++ b/lib/routes/gov/shenzhen/zjj/index.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const config = {
tzgg: {
@@ -41,7 +42,7 @@ async function handler(ctx) {
const baseUrl = 'http://zjj.sz.gov.cn/xxgk/';
const cfg = config[ctx.req.param('caty')];
if (!cfg) {
- throw new Error('Bad category. See docs');
+ throw new InvalidParameterError('Bad category. See docs');
}
const currentUrl = new URL(cfg.link, baseUrl).href;
diff --git a/lib/routes/gov/suzhou/news.ts b/lib/routes/gov/suzhou/news.ts
index b69de4777dc821..074e3bf25f2184 100644
--- a/lib/routes/gov/suzhou/news.ts
+++ b/lib/routes/gov/suzhou/news.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/suzhou/news/:uid',
@@ -119,7 +120,7 @@ async function handler(ctx) {
title = '苏州市政府 - 民生资讯';
break;
default:
- throw new Error('pattern not matched');
+ throw new InvalidParameterError('pattern not matched');
}
if (apiUrl) {
const response = await got(apiUrl);
diff --git a/lib/routes/gumroad/index.ts b/lib/routes/gumroad/index.ts
index 321cba95b3b1cf..052f15bb1eca19 100644
--- a/lib/routes/gumroad/index.ts
+++ b/lib/routes/gumroad/index.ts
@@ -7,6 +7,7 @@ import { load } from 'cheerio';
import { art } from '@/utils/render';
import path from 'node:path';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:username/:products',
@@ -31,7 +32,7 @@ async function handler(ctx) {
const username = ctx.req.param('username');
const products = ctx.req.param('products');
if (!isValidHost(username)) {
- throw new Error('Invalid username');
+ throw new InvalidParameterError('Invalid username');
}
const url = `https://${username}.gumroad.com/l/${products}`;
diff --git a/lib/routes/guokr/channel.ts b/lib/routes/guokr/channel.ts
index ee52d5de173170..1d7c962bbf2749 100644
--- a/lib/routes/guokr/channel.ts
+++ b/lib/routes/guokr/channel.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { parseList, parseItem } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const channelMap = {
calendar: 'pac',
@@ -42,7 +43,7 @@ async function handler(ctx) {
const result = parseList(response.result);
if (result.length === 0) {
- throw new Error('Unknown channel');
+ throw new InvalidParameterError('Unknown channel');
}
const channelName = result[0].channels[0].name;
diff --git a/lib/routes/huanqiu/index.ts b/lib/routes/huanqiu/index.ts
index a59014961b048b..2b70b76f7f436b 100644
--- a/lib/routes/huanqiu/index.ts
+++ b/lib/routes/huanqiu/index.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
function getKeysRecursive(dic, key, attr, array) {
for (const v of Object.values(dic)) {
@@ -46,7 +47,7 @@ export const route: Route = {
async function handler(ctx) {
const category = ctx.req.param('category') ?? 'china';
if (!isValidHost(category)) {
- throw new Error('Invalid category');
+ throw new InvalidParameterError('Invalid category');
}
const host = `https://${category}.huanqiu.com`;
diff --git a/lib/routes/instagram/private-api/index.ts b/lib/routes/instagram/private-api/index.ts
index 92f1aaedad0354..b42a8beda7752c 100644
--- a/lib/routes/instagram/private-api/index.ts
+++ b/lib/routes/instagram/private-api/index.ts
@@ -4,6 +4,7 @@ import { ig, login } from './utils';
import logger from '@/utils/logger';
import { config } from '@/config';
import { renderItems } from '../common-utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
// loadContent pulls the desired user/tag/etc
async function loadContent(category, nameOrId, tryGet) {
@@ -93,7 +94,7 @@ async function handler(ctx) {
// e.g. username for user feed
const { category, key } = ctx.req.param();
if (!availableCategories.includes(category)) {
- throw new Error('Such feed is not supported.');
+ throw new InvalidParameterError('Such feed is not supported.');
}
if (config.instagram && config.instagram.proxy) {
diff --git a/lib/routes/instagram/private-api/utils.ts b/lib/routes/instagram/private-api/utils.ts
index f6cdeac65810d6..db481f242174fe 100644
--- a/lib/routes/instagram/private-api/utils.ts
+++ b/lib/routes/instagram/private-api/utils.ts
@@ -1,12 +1,13 @@
import { IgApiClient } from 'instagram-private-api';
import logger from '@/utils/logger';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const ig = new IgApiClient();
async function login(ig, cache) {
if (!config.instagram || !config.instagram.username || !config.instagram.password) {
- throw new Error('Instagram RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Instagram RSS is disabled due to the lack of relevant config');
}
const LOGIN_CACHE_KEY = 'instagram:login';
const { username, password } = config.instagram;
diff --git a/lib/routes/instagram/web-api/index.ts b/lib/routes/instagram/web-api/index.ts
index f53da7ff0c2750..6acc1249b4cc38 100644
--- a/lib/routes/instagram/web-api/index.ts
+++ b/lib/routes/instagram/web-api/index.ts
@@ -4,6 +4,8 @@ import { CookieJar } from 'tough-cookie';
import { config } from '@/config';
import { renderItems } from '../common-utils';
import { baseUrl, COOKIE_URL, checkLogin, getUserInfo, getUserFeedItems, getTagsFeedItems, getLoggedOutTagsFeedItems, renderGuestItems } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/2/:category/:key',
@@ -39,7 +41,7 @@ async function handler(ctx) {
const { category, key } = ctx.req.param();
const { cookie } = config.instagram;
if (!availableCategories.includes(category)) {
- throw new Error('Such feed is not supported.');
+ throw new InvalidParameterError('Such feed is not supported.');
}
let cookieJar = await cache.get('instagram:cookieJar');
@@ -58,7 +60,7 @@ async function handler(ctx) {
}
if (!wwwClaimV2 && cookie && !(await checkLogin(cookieJar, cache))) {
- throw new Error('Invalid cookie');
+ throw new ConfigNotFoundError('Invalid cookie');
}
let feedTitle, feedLink, feedDescription, feedLogo;
diff --git a/lib/routes/instagram/web-api/utils.ts b/lib/routes/instagram/web-api/utils.ts
index f2ddaa003badff..24a3a084579de9 100644
--- a/lib/routes/instagram/web-api/utils.ts
+++ b/lib/routes/instagram/web-api/utils.ts
@@ -6,6 +6,7 @@ import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
import { art } from '@/utils/render';
import path from 'node:path';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const baseUrl = 'https://www.instagram.com';
const COOKIE_URL = 'https://instagram.com';
@@ -59,7 +60,7 @@ const getUserInfo = async (username, cookieJar, cache) => {
},
});
if (response.url.includes('/accounts/login/')) {
- throw new Error('Invalid cookie');
+ throw new ConfigNotFoundError('Invalid cookie');
}
webProfileInfo = response.data.data.user;
@@ -69,7 +70,7 @@ const getUserInfo = async (username, cookieJar, cache) => {
await cache.set(`instagram:userInfo:${id}`, webProfileInfo);
} catch (error) {
if (error.message.includes("Cookie not in this host's domain")) {
- throw new Error('Invalid cookie');
+ throw new ConfigNotFoundError('Invalid cookie');
}
throw error;
}
@@ -97,7 +98,7 @@ const getUserFeedItems = (id, username, cookieJar, cache) =>
},
});
if (response.url.includes('/accounts/login/')) {
- throw new Error(`Invalid cookie.
+ throw new ConfigNotFoundError(`Invalid cookie.
Please also check if your account is being blocked by Instagram.`);
}
diff --git a/lib/routes/iqiyi/album.ts b/lib/routes/iqiyi/album.ts
index 460e25a2b4eee0..d2aed2de5be927 100644
--- a/lib/routes/iqiyi/album.ts
+++ b/lib/routes/iqiyi/album.ts
@@ -7,6 +7,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/album/:id',
@@ -43,7 +44,7 @@ async function handler(ctx) {
} = await got(`https://pcw-api.iqiyi.com/album/album/baseinfo/${album.videoAlbumInfo.albumId}`);
if (Object.keys(album.cacheAlbumList).length === 0) {
- throw new Error(`${baseInfo.name} is not available in this server region.`);
+ throw new InvalidParameterError(`${baseInfo.name} is not available in this server region.`);
}
let pos = 1;
diff --git a/lib/routes/itch/devlog.ts b/lib/routes/itch/devlog.ts
index 62c2dcb3c5ed48..7f2a931bc16617 100644
--- a/lib/routes/itch/devlog.ts
+++ b/lib/routes/itch/devlog.ts
@@ -10,6 +10,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/devlog/:user/:id',
@@ -38,7 +39,7 @@ async function handler(ctx) {
const user = ctx.req.param('user') ?? '';
const id = ctx.req.param('id') ?? '';
if (!isValidHost(user)) {
- throw new Error('Invalid user');
+ throw new InvalidParameterError('Invalid user');
}
const rootUrl = `https://${user}.itch.io/${id}/devlog`;
diff --git a/lib/routes/ithome/index.ts b/lib/routes/ithome/index.ts
index e80ba7c3c96ecb..840e9ab1bcd440 100644
--- a/lib/routes/ithome/index.ts
+++ b/lib/routes/ithome/index.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -59,7 +60,7 @@ export const route: Route = {
async function handler(ctx) {
const cfg = config[ctx.req.param('caty')];
if (!cfg) {
- throw new Error('Bad category. See https://docs.rsshub.app/routes/new-media#it-zhi-jia');
+ throw new InvalidParameterError('Bad category. See https://docs.rsshub.app/routes/new-media#it-zhi-jia');
}
const current_url = get_url(ctx.req.param('caty'));
diff --git a/lib/routes/ithome/ranking.ts b/lib/routes/ithome/ranking.ts
index cfd4cbd77dc12c..b95630cdceea9c 100644
--- a/lib/routes/ithome/ranking.ts
+++ b/lib/routes/ithome/ranking.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -49,7 +50,7 @@ async function handler(ctx) {
const id = type2id[option];
if (!id) {
- throw new Error('Bad type. See https://docs.rsshub.app/routes/new-media#it-zhi-jia');
+ throw new InvalidParameterError('Bad type. See https://docs.rsshub.app/routes/new-media#it-zhi-jia');
}
const list = $(`#${id} > li`)
diff --git a/lib/routes/iwara/subscriptions.ts b/lib/routes/iwara/subscriptions.ts
index 629d6fec4ac048..c7a94b687b0b29 100644
--- a/lib/routes/iwara/subscriptions.ts
+++ b/lib/routes/iwara/subscriptions.ts
@@ -9,6 +9,7 @@ import { art } from '@/utils/render';
import { parseDate } from '@/utils/parse-date';
import path from 'node:path';
import MarkdownIt from 'markdown-it';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const md = MarkdownIt({
html: true,
});
@@ -51,7 +52,7 @@ export const route: Route = {
async function handler() {
if (!config.iwara || !config.iwara.username || !config.iwara.password) {
- throw new Error('Iwara subscription RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Iwara subscription RSS is disabled due to the lack of relevant config');
}
const rootUrl = `https://www.iwara.tv`;
diff --git a/lib/routes/javbus/index.ts b/lib/routes/javbus/index.ts
index 9567e6fe739dd1..37ed73efa21bd7 100644
--- a/lib/routes/javbus/index.ts
+++ b/lib/routes/javbus/index.ts
@@ -10,6 +10,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const toSize = (raw) => {
const matches = raw.match(/(\d+(\.\d+)?)(\w+)/);
@@ -41,7 +42,7 @@ async function handler(ctx) {
const westernUrl = `https://www.${westernDomain}`;
if (!config.feature.allow_user_supply_unsafe_domain && (!allowDomain.has(new URL(`https://${domain}/`).hostname) || !allowDomain.has(new URL(`https://${westernDomain}/`).hostname))) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const currentUrl = `${isWestern ? westernUrl : rootUrl}${getSubPath(ctx)
diff --git a/lib/routes/javdb/utils.ts b/lib/routes/javdb/utils.ts
index f13b4484557cca..0e9016f760c339 100644
--- a/lib/routes/javdb/utils.ts
+++ b/lib/routes/javdb/utils.ts
@@ -3,13 +3,14 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const allowDomain = new Set(['javdb.com', 'javdb36.com', 'javdb007.com', 'javdb521.com']);
const ProcessItems = async (ctx, currentUrl, title) => {
const domain = ctx.req.query('domain') ?? 'javdb.com';
const url = new URL(currentUrl, `https://${domain}`);
if (!config.feature.allow_user_supply_unsafe_domain && !allowDomain.has(url.hostname)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const rootUrl = `https://${domain}`;
diff --git a/lib/routes/kyodonews/index.ts b/lib/routes/kyodonews/index.ts
index 5e71ddee8b4b38..a3f7cd84cc9da8 100644
--- a/lib/routes/kyodonews/index.ts
+++ b/lib/routes/kyodonews/index.ts
@@ -9,6 +9,8 @@ import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const resolveRelativeLink = (link, baseUrl) => (link.startsWith('http') ? link : `${baseUrl}${link}`);
@@ -37,7 +39,7 @@ async function handler(ctx) {
// raise error for invalid languages
if (!['china', 'tchina'].includes(language)) {
- throw new Error('Invalid language');
+ throw new ConfigNotFoundError('Invalid language');
}
const rootUrl = `https://${language}.kyodonews.net`;
@@ -47,7 +49,7 @@ async function handler(ctx) {
try {
response = await got(currentUrl);
} catch (error) {
- throw error.response && error.response.statusCode === 404 ? new Error('Invalid keyword') : error;
+ throw error.response && error.response.statusCode === 404 ? new InvalidParameterError('Invalid keyword') : error;
}
const $ = load(response.data, { xmlMode: keyword === 'rss' });
diff --git a/lib/routes/lemmy/index.ts b/lib/routes/lemmy/index.ts
index 256991c92b8e7e..a8bc1fd4b642c9 100644
--- a/lib/routes/lemmy/index.ts
+++ b/lib/routes/lemmy/index.ts
@@ -5,6 +5,8 @@ import { parseDate } from '@/utils/parse-date';
import MarkdownIt from 'markdown-it';
const md = MarkdownIt({ html: true });
import { config } from '@/config';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/:community/:sort?',
@@ -34,12 +36,12 @@ async function handler(ctx) {
const community = ctx.req.param('community');
const communitySlices = community.split('@');
if (communitySlices.length !== 2) {
- throw new Error(`Invalid community: ${community}`);
+ throw new InvalidParameterError(`Invalid community: ${community}`);
}
const instance = community.split('@')[1];
const allowedDomain = ['lemmy.world', 'lemm.ee', 'lemmy.ml', 'sh.itjust.works', 'feddit.de', 'hexbear.net', 'beehaw.org', 'lemmynsfw.com', 'lemmy.ca', 'programming.dev'];
if (!config.feature.allow_user_supply_unsafe_domain && !allowedDomain.includes(new URL(`http://${instance}/`).hostname)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const communityUrl = `https://${instance}/api/v3/community?name=${community}`;
diff --git a/lib/routes/liveuamap/index.ts b/lib/routes/liveuamap/index.ts
index 030bb2ec24906d..cfa1ea48bdcd7b 100644
--- a/lib/routes/liveuamap/index.ts
+++ b/lib/routes/liveuamap/index.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:region?',
@@ -31,7 +32,7 @@ async function handler(ctx) {
const region = ctx.req.param('region') ?? 'ukraine';
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 50;
if (!isValidHost(region)) {
- throw new Error('Invalid region');
+ throw new InvalidParameterError('Invalid region');
}
const url = `https://${region}.liveuamap.com/`;
diff --git a/lib/routes/lofter/user.ts b/lib/routes/lofter/user.ts
index 7ccdfd3eece76b..f3531b7f582721 100644
--- a/lib/routes/lofter/user.ts
+++ b/lib/routes/lofter/user.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -25,7 +26,7 @@ async function handler(ctx) {
const name = ctx.req.param('name') ?? 'i';
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : '50';
if (!isValidHost(name)) {
- throw new Error('Invalid name');
+ throw new InvalidParameterError('Invalid name');
}
const rootUrl = `${name}.lofter.com`;
diff --git a/lib/routes/m4/index.ts b/lib/routes/m4/index.ts
index fe684e5fad1da7..32c3d62a7e3028 100644
--- a/lib/routes/m4/index.ts
+++ b/lib/routes/m4/index.ts
@@ -10,6 +10,7 @@ import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:id?/:category{.+}?',
@@ -21,7 +22,7 @@ export const route: Route = {
async function handler(ctx) {
const { id = 'news', category = 'china' } = ctx.req.param();
if (!isValidHost(id)) {
- throw new Error('Invalid id');
+ throw new InvalidParameterError('Invalid id');
}
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
diff --git a/lib/routes/mail/imap.ts b/lib/routes/mail/imap.ts
index fa9be620c1bc4f..4f476198bb8875 100644
--- a/lib/routes/mail/imap.ts
+++ b/lib/routes/mail/imap.ts
@@ -5,6 +5,7 @@ import { config } from '@/config';
import { simpleParser } from 'mailparser';
import logger from '@/utils/logger';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/imap/:email/:folder{.+}?',
@@ -23,7 +24,7 @@ async function handler(ctx) {
};
if (!mailConfig.username || !mailConfig.password || !mailConfig.host || !mailConfig.port) {
- throw new Error('Email Inbox RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Email Inbox RSS is disabled due to the lack of relevant config');
}
const client = new ImapFlow({
diff --git a/lib/routes/manhuagui/subscribe.ts b/lib/routes/manhuagui/subscribe.ts
index e0679b29dbef53..34455022d0d366 100644
--- a/lib/routes/manhuagui/subscribe.ts
+++ b/lib/routes/manhuagui/subscribe.ts
@@ -8,6 +8,7 @@ import { load } from 'cheerio';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const web_url = 'https://www.manhuagui.com/user/book/shelf/1';
export const route: Route = {
@@ -45,7 +46,7 @@ export const route: Route = {
async function handler() {
if (!config.manhuagui || !config.manhuagui.cookie) {
- throw new Error('manhuagui RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('manhuagui RSS is disabled due to the lack of relevant config');
}
const cookie = config.manhuagui.cookie;
const response = await got({
diff --git a/lib/routes/mastodon/account-id.ts b/lib/routes/mastodon/account-id.ts
index bd553918875290..65a54cc2aa6197 100644
--- a/lib/routes/mastodon/account-id.ts
+++ b/lib/routes/mastodon/account-id.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/account_id/:site/:account_id/statuses/:only_media?',
@@ -14,7 +15,7 @@ async function handler(ctx) {
const account_id = ctx.req.param('account_id');
const only_media = ctx.req.param('only_media') ? 'true' : 'false';
if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const { account_data, data } = await utils.getAccountStatuses(site, account_id, only_media);
diff --git a/lib/routes/mastodon/timeline-local.ts b/lib/routes/mastodon/timeline-local.ts
index 43773041e01f25..c88541cb58f0d8 100644
--- a/lib/routes/mastodon/timeline-local.ts
+++ b/lib/routes/mastodon/timeline-local.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/timeline/:site/:only_media?',
@@ -26,7 +27,7 @@ async function handler(ctx) {
const site = ctx.req.param('site');
const only_media = ctx.req.param('only_media') ? 'true' : 'false';
if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const url = `http://${site}/api/v1/timelines/public?local=true&only_media=${only_media}`;
diff --git a/lib/routes/mastodon/timeline-remote.ts b/lib/routes/mastodon/timeline-remote.ts
index 6b17f620aaf026..bb85812af7c693 100644
--- a/lib/routes/mastodon/timeline-remote.ts
+++ b/lib/routes/mastodon/timeline-remote.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/remote/:site/:only_media?',
@@ -26,7 +27,7 @@ async function handler(ctx) {
const site = ctx.req.param('site');
const only_media = ctx.req.param('only_media') ? 'true' : 'false';
if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const url = `http://${site}/api/v1/timelines/public?remote=true&only_media=${only_media}`;
diff --git a/lib/routes/mastodon/utils.ts b/lib/routes/mastodon/utils.ts
index f25cda95e517bb..40b4401ade10f5 100644
--- a/lib/routes/mastodon/utils.ts
+++ b/lib/routes/mastodon/utils.ts
@@ -2,6 +2,7 @@ import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const allowSiteList = ['mastodon.social', 'pawoo.net', config.mastodon.apiHost].filter(Boolean);
@@ -93,10 +94,10 @@ async function getAccountIdByAcct(acct) {
const site = mastodonConfig.apiHost || acctHost;
const acctDomain = mastodonConfig.acctDomain || acctHost;
if (!(site && acctDomain)) {
- throw new Error('Mastodon RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Mastodon RSS is disabled due to the lack of relevant config');
}
if (!config.feature.allow_user_supply_unsafe_domain && !allowSiteList.includes(site)) {
- throw new Error(`RSS for this domain is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true' or 'MASTODON_API_HOST' is set.`);
+ throw new ConfigNotFoundError(`RSS for this domain is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true' or 'MASTODON_API_HOST' is set.`);
}
const search_url = `https://${site}/api/v2/search`;
diff --git a/lib/routes/medium/following.ts b/lib/routes/medium/following.ts
index bcaded1aa1e673..1f34e24533fc0b 100644
--- a/lib/routes/medium/following.ts
+++ b/lib/routes/medium/following.ts
@@ -3,6 +3,7 @@ import { config } from '@/config';
import parseArticle from './parse-article.js';
import { getFollowingFeedQuery } from './graphql.js';
+import ConfigNotFoundError from '@/errors/types/config-not-found.js';
export const route: Route = {
path: '/following/:user',
@@ -35,7 +36,7 @@ async function handler(ctx) {
const cookie = config.medium.cookies[user];
if (cookie === undefined) {
- throw new Error(`缺少 Medium 用户 ${user} 登录后的 Cookie 值`);
+ throw new ConfigNotFoundError(`缺少 Medium 用户 ${user} 登录后的 Cookie 值`);
}
const posts = await getFollowingFeedQuery(user, cookie);
@@ -43,7 +44,7 @@ async function handler(ctx) {
if (!posts) {
// login failed
- throw new Error(`Medium 用户 ${user} 的 Cookie 无效或已过期`);
+ throw new ConfigNotFoundError(`Medium 用户 ${user} 的 Cookie 无效或已过期`);
}
const urls = posts.items.map((data) => data.post.mediumUrl);
diff --git a/lib/routes/medium/for-you.ts b/lib/routes/medium/for-you.ts
index 320100c14b417a..48b7a204f96754 100644
--- a/lib/routes/medium/for-you.ts
+++ b/lib/routes/medium/for-you.ts
@@ -3,6 +3,7 @@ import { config } from '@/config';
import parseArticle from './parse-article.js';
import { getWebInlineRecommendedFeedQuery } from './graphql.js';
+import ConfigNotFoundError from '@/errors/types/config-not-found.js';
export const route: Route = {
path: '/for-you/:user',
@@ -35,7 +36,7 @@ async function handler(ctx) {
const cookie = config.medium.cookies[user];
if (cookie === undefined) {
- throw new Error(`缺少 Medium 用户 ${user} 登录后的 Cookie 值`);
+ throw new ConfigNotFoundError(`缺少 Medium 用户 ${user} 登录后的 Cookie 值`);
}
const posts = await getWebInlineRecommendedFeedQuery(user, cookie);
@@ -43,7 +44,7 @@ async function handler(ctx) {
if (!posts) {
// login failed
- throw new Error(`Medium 用户 ${user} 的 Cookie 无效或已过期`);
+ throw new ConfigNotFoundError(`Medium 用户 ${user} 的 Cookie 无效或已过期`);
}
const urls = posts.items.map((data) => data.post.mediumUrl);
diff --git a/lib/routes/medium/list.ts b/lib/routes/medium/list.ts
index 4b447baafc8057..d628e232cf3f9f 100644
--- a/lib/routes/medium/list.ts
+++ b/lib/routes/medium/list.ts
@@ -3,6 +3,8 @@ import { config } from '@/config';
import parseArticle from './parse-article.js';
import { getUserCatalogMainContentQuery } from './graphql.js';
+import ConfigNotFoundError from '@/errors/types/config-not-found.js';
+import InvalidParameterError from '@/errors/types/invalid-parameter.js';
export const route: Route = {
path: '/list/:user/:catalogId',
@@ -37,10 +39,10 @@ async function handler(ctx) {
ctx.set('json', catalog);
if (catalog && catalog.__typename === 'Forbidden') {
- throw new Error(`无权访问 id 为 ${catalogId} 的 List(可能是未设置 Cookie 或 Cookie 已过期)`);
+ throw new ConfigNotFoundError(`无权访问 id 为 ${catalogId} 的 List(可能是未设置 Cookie 或 Cookie 已过期)`);
}
if (!catalog || !catalog.itemsConnection) {
- throw new Error(`id 为 ${catalogId} 的 List 不存在`);
+ throw new InvalidParameterError(`id 为 ${catalogId} 的 List 不存在`);
}
const name = catalog.name;
diff --git a/lib/routes/medium/tag.ts b/lib/routes/medium/tag.ts
index 07ac433b5cb891..15ef2e54d00ca6 100644
--- a/lib/routes/medium/tag.ts
+++ b/lib/routes/medium/tag.ts
@@ -3,6 +3,7 @@ import { config } from '@/config';
import parseArticle from './parse-article.js';
import { getWebInlineTopicFeedQuery } from './graphql.js';
+import ConfigNotFoundError from '@/errors/types/config-not-found.js';
export const route: Route = {
path: '/tag/:user/:tag',
@@ -38,7 +39,7 @@ async function handler(ctx) {
const cookie = config.medium.cookies[user];
if (cookie === undefined) {
- throw new Error(`缺少 Medium 用户 ${user} 登录后的 Cookie 值`);
+ throw new ConfigNotFoundError(`缺少 Medium 用户 ${user} 登录后的 Cookie 值`);
}
const posts = await getWebInlineTopicFeedQuery(user, tag, cookie);
@@ -46,7 +47,7 @@ async function handler(ctx) {
if (!posts) {
// login failed
- throw new Error(`Medium 用户 ${user} 的 Cookie 无效或已过期`);
+ throw new ConfigNotFoundError(`Medium 用户 ${user} 的 Cookie 无效或已过期`);
}
const urls = posts.items.map((data) => data.post.mediumUrl);
diff --git a/lib/routes/mihoyo/bbs/cache.ts b/lib/routes/mihoyo/bbs/cache.ts
index 9e56eafa7401ff..7c986a673c344c 100644
--- a/lib/routes/mihoyo/bbs/cache.ts
+++ b/lib/routes/mihoyo/bbs/cache.ts
@@ -1,10 +1,11 @@
import cache from '@/utils/cache';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const getUserFullInfo = (ctx, uid) => {
if (!uid && !config.mihoyo.cookie) {
- throw new Error('GetUserFullInfo is not available due to the absense of [Miyoushe Cookie]. Check relevant config tutorial');
+ throw new ConfigNotFoundError('GetUserFullInfo is not available due to the absense of [Miyoushe Cookie]. Check relevant config tutorial');
}
uid ||= '';
const key = 'mihoyo:user-full-info-uid-' + uid;
diff --git a/lib/routes/mihoyo/bbs/timeline.ts b/lib/routes/mihoyo/bbs/timeline.ts
index 04795c48070370..af60ba5f8a70ac 100644
--- a/lib/routes/mihoyo/bbs/timeline.ts
+++ b/lib/routes/mihoyo/bbs/timeline.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import cache from './cache';
import { config } from '@/config';
import { post2item } from './utils';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/bbs/timeline',
@@ -37,7 +38,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.mihoyo.cookie) {
- throw new Error('Miyoushe Timeline is not available due to the absense of [Miyoushe Cookie]. Check relevant config tutorial');
+ throw new ConfigNotFoundError('Miyoushe Timeline is not available due to the absense of [Miyoushe Cookie]. Check relevant config tutorial');
}
const page_size = ctx.req.query('limit') || '20';
diff --git a/lib/routes/miniflux/entry.ts b/lib/routes/miniflux/entry.ts
index ed88b660ef0397..3867c8136ae758 100644
--- a/lib/routes/miniflux/entry.ts
+++ b/lib/routes/miniflux/entry.ts
@@ -1,6 +1,7 @@
import { Route, Data } from '@/types';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/entry/:feeds/:parameters?',
@@ -63,7 +64,7 @@ async function handler(ctx) {
const token = config.miniflux.token;
if (!token) {
- throw new Error('This RSS feed is disabled due to its incorrect configuration: the token is missing.');
+ throw new ConfigNotFoundError('This RSS feed is disabled due to its incorrect configuration: the token is missing.');
}
// In this function, var`mark`, `link`, and `limit`, `addFeedName`
diff --git a/lib/routes/miniflux/subscription.ts b/lib/routes/miniflux/subscription.ts
index 0aa91c42a4faeb..fa89d50fb89866 100644
--- a/lib/routes/miniflux/subscription.ts
+++ b/lib/routes/miniflux/subscription.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/subscription/:parameters?',
@@ -43,7 +44,7 @@ async function handler(ctx) {
const token = config.miniflux.token;
if (!token) {
- throw new Error('This RSS feed is disabled due to its incorrect configuration: the token is missing.');
+ throw new ConfigNotFoundError('This RSS feed is disabled due to its incorrect configuration: the token is missing.');
}
function set(item) {
diff --git a/lib/routes/mirror/index.ts b/lib/routes/mirror/index.ts
index 8dc33bc21fa827..9025647795a577 100644
--- a/lib/routes/mirror/index.ts
+++ b/lib/routes/mirror/index.ts
@@ -7,6 +7,7 @@ const md = MarkdownIt({
linkify: true,
});
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:id',
@@ -29,7 +30,7 @@ export const route: Route = {
async function handler(ctx) {
const id = ctx.req.param('id');
if (!id.endsWith('.eth') && !isValidHost(id)) {
- throw new Error('Invalid id');
+ throw new InvalidParameterError('Invalid id');
}
const rootUrl = 'https://mirror.xyz';
const currentUrl = id.endsWith('.eth') ? `${rootUrl}/${id}` : `https://${id}.mirror.xyz`;
diff --git a/lib/routes/misskey/featured-notes.ts b/lib/routes/misskey/featured-notes.ts
index 5fa9d8c11fc3ca..2891809315cb68 100644
--- a/lib/routes/misskey/featured-notes.ts
+++ b/lib/routes/misskey/featured-notes.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/notes/featured/:site',
@@ -24,7 +25,7 @@ export const route: Route = {
async function handler(ctx) {
const site = ctx.req.param('site');
if (!config.feature.allow_user_supply_unsafe_domain && !utils.allowSiteList.includes(site)) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
// docs on: https://misskey-hub.net/docs/api/endpoints/notes/featured.html
diff --git a/lib/routes/mixcloud/index.ts b/lib/routes/mixcloud/index.ts
index 5c8ef282decbb5..d907ec4b98d186 100644
--- a/lib/routes/mixcloud/index.ts
+++ b/lib/routes/mixcloud/index.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import CryptoJS from 'crypto-js';
import { parseDate } from '@/utils/parse-date';
import { queries } from './queries';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:username/:type?',
@@ -37,7 +38,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'uploads';
if (!['stream', 'uploads', 'favorites', 'listens'].includes(type)) {
- throw new Error(`Invalid type: ${type}`);
+ throw new InvalidParameterError(`Invalid type: ${type}`);
}
const username = ctx.req.param('username');
diff --git a/lib/routes/myfigurecollection/activity.ts b/lib/routes/myfigurecollection/activity.ts
index 30d45321f7de5e..8bd33065f062df 100644
--- a/lib/routes/myfigurecollection/activity.ts
+++ b/lib/routes/myfigurecollection/activity.ts
@@ -9,6 +9,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/activity/:category?/:language?/:latestAdditions?/:latestEdits?/:latestAlerts?/:latestPictures?',
@@ -75,7 +76,7 @@ async function handler(ctx) {
const latestPictures = ctx.req.param('latestPictures') ?? '1';
if (language && !isValidHost(language)) {
- throw new Error('Invalid language');
+ throw new InvalidParameterError('Invalid language');
}
const rootUrl = `https://${language === 'en' || language === '' ? '' : `${language}.`}myfigurecollection.net`;
diff --git a/lib/routes/myfigurecollection/index.ts b/lib/routes/myfigurecollection/index.ts
index 034a2052834e37..d0201640397719 100644
--- a/lib/routes/myfigurecollection/index.ts
+++ b/lib/routes/myfigurecollection/index.ts
@@ -8,6 +8,7 @@ import { load } from 'cheerio';
import { art } from '@/utils/render';
import path from 'node:path';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const shortcuts = {
potd: 'picture/browse/potd/',
@@ -46,7 +47,7 @@ async function handler(ctx) {
const language = ctx.req.param('language') ?? '';
const category = ctx.req.param('category') ?? 'figure';
if (language && !isValidHost(language)) {
- throw new Error('Invalid language');
+ throw new InvalidParameterError('Invalid language');
}
const rootUrl = `https://${language === 'en' || language === '' ? '' : `${language}.`}myfigurecollection.net`;
diff --git a/lib/routes/newrank/douyin.ts b/lib/routes/newrank/douyin.ts
index 4a77715a6acf43..ed2f38a65af7b5 100644
--- a/lib/routes/newrank/douyin.ts
+++ b/lib/routes/newrank/douyin.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/douyin/:dyid',
@@ -31,7 +32,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.newrank || !config.newrank.cookie) {
- throw new Error('newrank RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('newrank RSS is disabled due to the lack of relevant config');
}
const uid = ctx.req.param('dyid');
const nonce = utils.random_nonce(9);
diff --git a/lib/routes/newrank/wechat.ts b/lib/routes/newrank/wechat.ts
index 592d83a06c327a..dd7db0c947f52d 100644
--- a/lib/routes/newrank/wechat.ts
+++ b/lib/routes/newrank/wechat.ts
@@ -4,6 +4,7 @@ import { finishArticleItem } from '@/utils/wechat-mp';
import { load } from 'cheerio';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/wechat/:wxid',
@@ -30,7 +31,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.newrank || !config.newrank.cookie) {
- throw new Error('newrank RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('newrank RSS is disabled due to the lack of relevant config');
}
const uid = ctx.req.param('wxid');
const nonce = utils.random_nonce(9);
diff --git a/lib/routes/nhentai/other.ts b/lib/routes/nhentai/other.ts
index 191a140c3cc0c8..d7fe5273b3fe75 100644
--- a/lib/routes/nhentai/other.ts
+++ b/lib/routes/nhentai/other.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import { getSimple, getDetails, getTorrents } from './util';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const supportedKeys = new Set(['parody', 'character', 'tag', 'artist', 'group', 'language', 'category']);
@@ -36,7 +37,7 @@ async function handler(ctx) {
const { key, keyword, mode } = ctx.req.param();
if (!supportedKeys.has(key)) {
- throw new Error('Unsupported key');
+ throw new InvalidParameterError('Unsupported key');
}
const url = `https://nhentai.net/${key}/${keyword.toLowerCase().replace(' ', '-')}/`;
diff --git a/lib/routes/nhentai/util.ts b/lib/routes/nhentai/util.ts
index bd1b3f0e7c5031..9783450b1010d7 100644
--- a/lib/routes/nhentai/util.ts
+++ b/lib/routes/nhentai/util.ts
@@ -7,6 +7,7 @@ import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const baseUrl = 'https://nhentai.net';
@@ -88,11 +89,11 @@ const getDetails = (cache, simples, limit) => Promise.all(simples.slice(0, limit
const getTorrents = async (cache, simples, limit) => {
if (!config.nhentai || !config.nhentai.username || !config.nhentai.password) {
- throw new Error('nhentai RSS with torrents is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('nhentai RSS with torrents is disabled due to the lack of relevant config');
}
const cookie = await getCookie(config.nhentai.username, config.nhentai.password, cache);
if (!cookie) {
- throw new Error('Invalid username (or email) or password for nhentai torrent download');
+ throw new ConfigNotFoundError('Invalid username (or email) or password for nhentai torrent download');
}
return getTorrentWithCookie(cache, simples, cookie, limit);
};
diff --git a/lib/routes/nintendo/eshop-cn.ts b/lib/routes/nintendo/eshop-cn.ts
index f8b3a8eb1f0229..1ad97ada38addd 100644
--- a/lib/routes/nintendo/eshop-cn.ts
+++ b/lib/routes/nintendo/eshop-cn.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import util from './utils';
const software_url = 'https://www.nintendoswitch.com.cn/software/';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/eshop/cn',
@@ -38,7 +39,7 @@ async function handler() {
title: "附带导航!一做就上手 第一次的游戏程序设计"
*/
if (!result.recentSoftwareList) {
- throw new Error('软件信息不存在,请报告这个问题');
+ throw new InvalidParameterError('软件信息不存在,请报告这个问题');
}
let data = result.recentSoftwareList.map((item) => ({
diff --git a/lib/routes/nintendo/news-china.ts b/lib/routes/nintendo/news-china.ts
index a6b3be7ac80680..ba30267ffe60c7 100644
--- a/lib/routes/nintendo/news-china.ts
+++ b/lib/routes/nintendo/news-china.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import util from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const news_url = 'https://www.nintendoswitch.com.cn';
export const route: Route = {
@@ -41,7 +42,7 @@ async function handler() {
title: "8款新品开启预约:超级马力欧系列官方周边"
*/
if (!result.newsList) {
- throw new Error('新闻信息不存在,请报告这个问题');
+ throw new InvalidParameterError('新闻信息不存在,请报告这个问题');
}
let data = result.newsList.map((item) => ({
diff --git a/lib/routes/njust/cwc.ts b/lib/routes/njust/cwc.ts
index 4f37a672a34f5a..6d211c8bbf52cb 100644
--- a/lib/routes/njust/cwc.ts
+++ b/lib/routes/njust/cwc.ts
@@ -3,6 +3,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { getContent } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['tzgg', { title: '南京理工大学财务处 -- 通知公告', id: '/12432' }],
@@ -36,7 +37,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
const siteUrl = host + id + '/list.htm';
diff --git a/lib/routes/njust/dgxg.ts b/lib/routes/njust/dgxg.ts
index 1be3db32b17a80..c568ba9877cf2e 100644
--- a/lib/routes/njust/dgxg.ts
+++ b/lib/routes/njust/dgxg.ts
@@ -3,6 +3,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { getContent } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['gstz', { title: '南京理工大学电光学院研学网 -- 公示通知', id: '/6509' }],
@@ -37,7 +38,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'gstz';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
const siteUrl = host + id + '/list.htm';
diff --git a/lib/routes/njust/eoe.ts b/lib/routes/njust/eoe.ts
index 96562cdc05cb3e..231f92263e84ac 100644
--- a/lib/routes/njust/eoe.ts
+++ b/lib/routes/njust/eoe.ts
@@ -3,6 +3,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { getContent } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['tzgg', { title: '南京理工大学电子工程与光电技术学院 -- 通知公告', id: '/1920' }],
@@ -36,7 +37,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
const siteUrl = host + id + '/list.htm';
diff --git a/lib/routes/njust/jwc.ts b/lib/routes/njust/jwc.ts
index 44b9b366301b72..bbe175fe0631b8 100644
--- a/lib/routes/njust/jwc.ts
+++ b/lib/routes/njust/jwc.ts
@@ -3,6 +3,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { getContent } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['jstz', { title: '南京理工大学教务处 -- 教师通知', id: '/1216' }],
@@ -38,7 +39,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'xstz';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
const siteUrl = host + id + '/list.htm';
diff --git a/lib/routes/notion/database.ts b/lib/routes/notion/database.ts
index 833f4b185514a0..cd83c0753aa3e2 100644
--- a/lib/routes/notion/database.ts
+++ b/lib/routes/notion/database.ts
@@ -8,6 +8,8 @@ import got from '@/utils/got';
import { NotionToMarkdown } from 'notion-to-md';
import { load } from 'cheerio';
import MarkdownIt from 'markdown-it';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const md = MarkdownIt({
html: true,
linkify: true,
@@ -55,7 +57,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.notion.key) {
- throw new Error('Notion RSS is disabled due to the lack of NOTION_TOKEN(relevant config)');
+ throw new ConfigNotFoundError('Notion RSS is disabled due to the lack of NOTION_TOKEN(relevant config)');
}
const databaseId = ctx.req.param('databaseId');
@@ -161,9 +163,9 @@ async function handler(ctx) {
if (isNotionClientError(error)) {
if (error.statusCode === APIErrorCode.ObjectNotFound) {
- throw new Error('The database is not exist');
+ throw new InvalidParameterError('The database is not exist');
} else if (error.statusCode === APIErrorCode.Unauthorized) {
- throw new Error('Please check the config of NOTION_TOKEN');
+ throw new ConfigNotFoundError('Please check the config of NOTION_TOKEN');
} else {
ctx.throw(error.statusCode, 'Notion API Error');
}
diff --git a/lib/routes/nua/dc.ts b/lib/routes/nua/dc.ts
index 381a4276c3144d..37614bbc9a5536 100644
--- a/lib/routes/nua/dc.ts
+++ b/lib/routes/nua/dc.ts
@@ -1,5 +1,6 @@
import { Route } from '@/types';
import util from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/dc/:type',
@@ -80,7 +81,7 @@ async function handler(ctx) {
webPageName = 'ul.screen_4 .big_title';
break;
default:
- throw new Error(`暂不支持对${type}的订阅`);
+ throw new InvalidParameterError(`暂不支持对${type}的订阅`);
}
const items = await util.ProcessList(baseUrl, baseUrl, listName, listDate, webPageName);
diff --git a/lib/routes/nua/lib.ts b/lib/routes/nua/lib.ts
index 2eb74a1335b793..8c6254ff9df9e4 100644
--- a/lib/routes/nua/lib.ts
+++ b/lib/routes/nua/lib.ts
@@ -1,5 +1,6 @@
import { Route } from '@/types';
import util from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const baseUrl = 'https://lib.nua.edu.cn';
export const route: Route = {
@@ -49,7 +50,7 @@ async function handler(ctx) {
webPageName = '.wp_column.column-4.selected';
break;
default:
- throw new Error(`暂不支持对${type}的订阅`);
+ throw new InvalidParameterError(`暂不支持对${type}的订阅`);
}
const newsUrl = `${baseUrl}/${type}/list.htm`;
diff --git a/lib/routes/oceanengine/arithmetic-index.ts b/lib/routes/oceanengine/arithmetic-index.ts
index 2f082f00af7197..b40e52ad7fecdd 100644
--- a/lib/routes/oceanengine/arithmetic-index.ts
+++ b/lib/routes/oceanengine/arithmetic-index.ts
@@ -11,6 +11,7 @@ import path from 'node:path';
import { config } from '@/config';
import puppeteer from '@/utils/puppeteer';
import { createDecipheriv } from 'node:crypto';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
// Parameters
const CACHE_MAX_AGE = config.cache.contentExpire;
@@ -95,10 +96,10 @@ async function handler(ctx) {
const end_date = now.format('YYYYMMDD');
const keyword = ctx.req.param('keyword');
if (!keyword) {
- throw new Error('Invalid keyword');
+ throw new InvalidParameterError('Invalid keyword');
}
if (ctx.req.param('channel') && !['douyin', 'toutiao'].includes(ctx.req.param('channel'))) {
- throw new Error('Invalid channel。 Only support `douyin` or `toutiao`');
+ throw new InvalidParameterError('Invalid channel。 Only support `douyin` or `toutiao`');
}
const channel = ctx.req.param('channel') === 'toutiao' ? 'toutiao' : 'aweme'; // default channel is `douyin`
diff --git a/lib/routes/people/index.ts b/lib/routes/people/index.ts
index 67bb30fa5bd394..0970a07c011a7c 100644
--- a/lib/routes/people/index.ts
+++ b/lib/routes/people/index.ts
@@ -6,6 +6,7 @@ import iconv from 'iconv-lite';
import timezone from '@/utils/timezone';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:site?/:category{.+}?',
@@ -22,7 +23,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 30;
if (!isValidHost(site)) {
- throw new Error('Invalid site');
+ throw new InvalidParameterError('Invalid site');
}
const rootUrl = `http://${site}.people.com.cn`;
const currentUrl = new URL(`GB/${category}`, rootUrl).href;
diff --git a/lib/routes/pianyuan/utils.ts b/lib/routes/pianyuan/utils.ts
index 7948eac5c33c65..de18c2fcd8f056 100644
--- a/lib/routes/pianyuan/utils.ts
+++ b/lib/routes/pianyuan/utils.ts
@@ -1,6 +1,7 @@
import { load } from 'cheerio';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const security_key = 'pianyuan-security_session_verify';
const PHPSESSID_key = 'pianyuan-PHPSESSID';
const loginauth_key = 'pianyuan-py_loginauth';
@@ -47,7 +48,7 @@ async function getCookie(cache) {
let py_loginauth = await cache.get(loginauth_key);
if (!py_loginauth) {
if (!config.pianyuan || !config.pianyuan.cookie) {
- throw new Error('pianyuan is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('pianyuan is disabled due to the lack of relevant config');
}
py_loginauth = config.pianyuan.cookie;
}
@@ -77,7 +78,7 @@ async function request(link, cache) {
}
}
if (response.data.includes('会员登录后才能访问')) {
- throw new Error('pianyuan Cookie已失效');
+ throw new ConfigNotFoundError('pianyuan Cookie已失效');
}
return response;
}
diff --git a/lib/routes/pixiv/bookmarks.ts b/lib/routes/pixiv/bookmarks.ts
index ea5fab4bae1618..10b009faab5730 100644
--- a/lib/routes/pixiv/bookmarks.ts
+++ b/lib/routes/pixiv/bookmarks.ts
@@ -6,6 +6,7 @@ import getUserDetail from './api/get-user-detail';
import { config } from '@/config';
import pixivUtils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/user/bookmarks/:id',
@@ -32,14 +33,14 @@ export const route: Route = {
async function handler(ctx) {
if (!config.pixiv || !config.pixiv.refreshToken) {
- throw new Error('pixiv RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('pixiv RSS is disabled due to the lack of relevant config');
}
const id = ctx.req.param('id');
const token = await getToken(cache.tryGet);
if (!token) {
- throw new Error('pixiv not login');
+ throw new ConfigNotFoundError('pixiv not login');
}
const [bookmarksResponse, userDetailResponse] = await Promise.all([getBookmarks(id, token), getUserDetail(id, token)]);
diff --git a/lib/routes/pixiv/illustfollow.ts b/lib/routes/pixiv/illustfollow.ts
index 569499898e086f..4fb4e6dc7a2855 100644
--- a/lib/routes/pixiv/illustfollow.ts
+++ b/lib/routes/pixiv/illustfollow.ts
@@ -5,6 +5,7 @@ import getIllustFollows from './api/get-illust-follows';
import { config } from '@/config';
import pixivUtils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/user/illustfollows',
@@ -40,12 +41,12 @@ export const route: Route = {
async function handler() {
if (!config.pixiv || !config.pixiv.refreshToken) {
- throw new Error('pixiv RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('pixiv RSS is disabled due to the lack of relevant config');
}
const token = await getToken(cache.tryGet);
if (!token) {
- throw new Error('pixiv not login');
+ throw new ConfigNotFoundError('pixiv not login');
}
const response = await getIllustFollows(token);
diff --git a/lib/routes/pixiv/ranking.ts b/lib/routes/pixiv/ranking.ts
index 3f3576941705e2..a1fd17eb8242ba 100644
--- a/lib/routes/pixiv/ranking.ts
+++ b/lib/routes/pixiv/ranking.ts
@@ -5,6 +5,7 @@ import getRanking from './api/get-ranking';
import { config } from '@/config';
import pixivUtils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const titles = {
day: 'pixiv 日排行',
@@ -84,7 +85,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.pixiv || !config.pixiv.refreshToken) {
- throw new Error('pixiv RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('pixiv RSS is disabled due to the lack of relevant config');
}
const mode = alias[ctx.req.param('mode')] ?? ctx.req.param('mode');
@@ -92,7 +93,7 @@ async function handler(ctx) {
const token = await getToken(cache.tryGet);
if (!token) {
- throw new Error('pixiv not login');
+ throw new ConfigNotFoundError('pixiv not login');
}
const response = await getRanking(mode, ctx.req.param('date') && date, token);
diff --git a/lib/routes/pixiv/search.ts b/lib/routes/pixiv/search.ts
index 30c2a04561a74c..e4eb0f76dff914 100644
--- a/lib/routes/pixiv/search.ts
+++ b/lib/routes/pixiv/search.ts
@@ -6,6 +6,7 @@ import searchIllust from './api/search-illust';
import { config } from '@/config';
import pixivUtils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/search/:keyword/:order?/:mode?',
@@ -30,7 +31,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.pixiv || !config.pixiv.refreshToken) {
- throw new Error('pixiv RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('pixiv RSS is disabled due to the lack of relevant config');
}
const keyword = ctx.req.param('keyword');
@@ -39,7 +40,7 @@ async function handler(ctx) {
const token = await getToken(cache.tryGet);
if (!token) {
- throw new Error('pixiv not login');
+ throw new ConfigNotFoundError('pixiv not login');
}
const response = await (order === 'popular' ? searchPopularIllust(keyword, token) : searchIllust(keyword, token));
diff --git a/lib/routes/pixiv/user.ts b/lib/routes/pixiv/user.ts
index 47a1bc3ccda8bb..08243ad2e38c33 100644
--- a/lib/routes/pixiv/user.ts
+++ b/lib/routes/pixiv/user.ts
@@ -5,6 +5,7 @@ import getIllusts from './api/get-illusts';
import { config } from '@/config';
import pixivUtils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/user/:id',
@@ -31,13 +32,13 @@ export const route: Route = {
async function handler(ctx) {
if (!config.pixiv || !config.pixiv.refreshToken) {
- throw new Error('pixiv RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('pixiv RSS is disabled due to the lack of relevant config');
}
const id = ctx.req.param('id');
const token = await getToken(cache.tryGet);
if (!token) {
- throw new Error('pixiv not login');
+ throw new ConfigNotFoundError('pixiv not login');
}
const response = await getIllusts(id, token);
diff --git a/lib/routes/plurk/top.ts b/lib/routes/plurk/top.ts
index 07a8939d984a3b..1ce1924a1774d1 100644
--- a/lib/routes/plurk/top.ts
+++ b/lib/routes/plurk/top.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { baseUrl, getPlurk } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const categoryList = new Set(['topReplurks', 'topFavorites', 'topResponded']);
@@ -33,7 +34,7 @@ export const route: Route = {
async function handler(ctx) {
const { category = 'topReplurks', lang = 'en' } = ctx.req.param();
if (!categoryList.has(category)) {
- throw new Error(`Invalid category: ${category}`);
+ throw new InvalidParameterError(`Invalid category: ${category}`);
}
const { data: apiResponse } = await got(`${baseUrl}/Stats/${category}`, {
diff --git a/lib/routes/pornhub/category-url.ts b/lib/routes/pornhub/category-url.ts
index cbb09cd1da19d8..5a2af089439e8a 100644
--- a/lib/routes/pornhub/category-url.ts
+++ b/lib/routes/pornhub/category-url.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
import { headers, parseItems } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:language?/category_url/:url?',
@@ -33,7 +34,7 @@ async function handler(ctx) {
const { language = 'www', url = 'video' } = ctx.req.param();
const link = `https://${language}.pornhub.com/${url}`;
if (!isValidHost(language)) {
- throw new Error('Invalid language');
+ throw new InvalidParameterError('Invalid language');
}
const { data: response } = await got(link, { headers });
diff --git a/lib/routes/pornhub/model.ts b/lib/routes/pornhub/model.ts
index e9f9ae788686a9..da5f44b01deda0 100644
--- a/lib/routes/pornhub/model.ts
+++ b/lib/routes/pornhub/model.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
import { headers, parseItems } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:language?/model/:username/:sort?',
@@ -32,7 +33,7 @@ async function handler(ctx) {
const { language = 'www', username, sort = '' } = ctx.req.param();
const link = `https://${language}.pornhub.com/model/${username}/videos${sort ? `?o=${sort}` : ''}`;
if (!isValidHost(language)) {
- throw new Error('Invalid language');
+ throw new InvalidParameterError('Invalid language');
}
const { data: response } = await got(link, { headers });
diff --git a/lib/routes/pornhub/pornstar.ts b/lib/routes/pornhub/pornstar.ts
index 58faaa440b4047..9815004bb2c3c1 100644
--- a/lib/routes/pornhub/pornstar.ts
+++ b/lib/routes/pornhub/pornstar.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
import { headers, parseItems } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:language?/pornstar/:username/:sort?',
@@ -37,7 +38,7 @@ async function handler(ctx) {
const { language = 'www', username, sort = 'mr' } = ctx.req.param();
const link = `https://${language}.pornhub.com/pornstar/${username}/videos?o=${sort}`;
if (!isValidHost(language)) {
- throw new Error('Invalid language');
+ throw new InvalidParameterError('Invalid language');
}
const { data: response } = await got(link, { headers });
diff --git a/lib/routes/pornhub/users.ts b/lib/routes/pornhub/users.ts
index a28314a092f158..e3137b60199309 100644
--- a/lib/routes/pornhub/users.ts
+++ b/lib/routes/pornhub/users.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
import { headers, parseItems } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:language?/users/:username',
@@ -32,7 +33,7 @@ async function handler(ctx) {
const { language = 'www', username } = ctx.req.param();
const link = `https://${language}.pornhub.com/users/${username}/videos`;
if (!isValidHost(language)) {
- throw new Error('Invalid language');
+ throw new InvalidParameterError('Invalid language');
}
const { data: response } = await got(link, { headers });
diff --git a/lib/routes/qweather/3days.ts b/lib/routes/qweather/3days.ts
index 31f674fabfeb37..d077dde84f7d76 100644
--- a/lib/routes/qweather/3days.ts
+++ b/lib/routes/qweather/3days.ts
@@ -7,6 +7,7 @@ import got from '@/utils/got';
import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const WEATHER_API = 'https://devapi.qweather.com/v7/weather/3d';
const AIR_QUALITY_API = 'https://devapi.qweather.com/v7/air/5d';
@@ -39,7 +40,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.hefeng.key) {
- throw new Error('QWeather RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('QWeather RSS is disabled due to the lack of relevant config');
}
const id = await cache.tryGet(ctx.req.param('location') + '_id', async () => {
const response = await got(`${CIRY_LOOKUP_API}?location=${ctx.req.param('location')}&key=${config.hefeng.key}`);
diff --git a/lib/routes/rsshub/transform/html.ts b/lib/routes/rsshub/transform/html.ts
index 91889d4d87f336..d63c73782c24b1 100644
--- a/lib/routes/rsshub/transform/html.ts
+++ b/lib/routes/rsshub/transform/html.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/transform/html/:url/:routeParams',
@@ -57,7 +58,7 @@ Specify options (in the format of query string) in parameter \`routeParams\` par
async function handler(ctx) {
if (!config.feature.allow_user_supply_unsafe_domain) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const url = ctx.req.param('url');
const response = await got({
diff --git a/lib/routes/rsshub/transform/json.ts b/lib/routes/rsshub/transform/json.ts
index c61bde64b0dbc3..ec4ba9601d8982 100644
--- a/lib/routes/rsshub/transform/json.ts
+++ b/lib/routes/rsshub/transform/json.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
function jsonGet(obj, attr) {
if (typeof attr !== 'string') {
@@ -70,7 +71,7 @@ JSON Path only supports format like \`a.b.c\`. if you need to access arrays, lik
async function handler(ctx) {
if (!config.feature.allow_user_supply_unsafe_domain) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const url = ctx.req.param('url');
const response = await got({
diff --git a/lib/routes/rsshub/transform/sitemap.ts b/lib/routes/rsshub/transform/sitemap.ts
index 19f5d8d321d2d1..f013e943af0241 100644
--- a/lib/routes/rsshub/transform/sitemap.ts
+++ b/lib/routes/rsshub/transform/sitemap.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/transform/sitemap/:url/:routeParams?',
@@ -12,7 +13,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.feature.allow_user_supply_unsafe_domain) {
- throw new Error(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
+ throw new ConfigNotFoundError(`This RSS is disabled unless 'ALLOW_USER_SUPPLY_UNSAFE_DOMAIN' is set to 'true'.`);
}
const url = ctx.req.param('url');
const response = await got({
diff --git a/lib/routes/sehuatang/user.ts b/lib/routes/sehuatang/user.ts
index 01fbbb4cac65ec..7046f2f933e64a 100644
--- a/lib/routes/sehuatang/user.ts
+++ b/lib/routes/sehuatang/user.ts
@@ -5,6 +5,7 @@ import got from '@/utils/got'; // 自订的 got
import { load } from 'cheerio'; // 可以使用类似 jQuery 的 API HTML 解析器
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const baseUrl = 'https://sehuatang.org/';
@@ -33,7 +34,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.sehuatang.cookie) {
- throw new Error('Sehuatang RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Sehuatang RSS is disabled due to the lack of relevant config');
}
// 从Url参数中获取uid
const uid = ctx.req.param('uid');
diff --git a/lib/routes/shiep/index.ts b/lib/routes/shiep/index.ts
index 4c783ee548d00a..ed49dc73e25fa2 100644
--- a/lib/routes/shiep/index.ts
+++ b/lib/routes/shiep/index.ts
@@ -11,6 +11,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import { config } from './config';
import { radar } from './radar';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:type/:id?',
@@ -58,13 +59,13 @@ async function handler(ctx) {
const type = ctx.req.param('type');
if (!Object.keys(config).includes(type)) {
- throw new Error(`Invalid type: ${type}`);
+ throw new InvalidParameterError(`Invalid type: ${type}`);
}
const { listSelector = '.list_item', pubDateSelector = '.Article_PublishDate', descriptionSelector = '.wp_articlecontent', title } = config[type];
if (!title) {
- throw new Error(`Invalid type: ${type}`);
+ throw new InvalidParameterError(`Invalid type: ${type}`);
}
const host = `https://${type}.shiep.edu.cn`;
diff --git a/lib/routes/solidot/main.ts b/lib/routes/solidot/main.ts
index 34a61afec433df..18f1d5bcba03bb 100644
--- a/lib/routes/solidot/main.ts
+++ b/lib/routes/solidot/main.ts
@@ -9,6 +9,7 @@ import got from '@/utils/got'; // get web content
import { load } from 'cheerio'; // html parser
import get_article from './_article';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/:type?',
@@ -40,7 +41,7 @@ export const route: Route = {
async function handler(ctx) {
const type = ctx.req.param('type') ?? 'www';
if (!isValidHost(type)) {
- throw new Error('Invalid type');
+ throw new InvalidParameterError('Invalid type');
}
const base_url = `https://${type}.solidot.org`;
diff --git a/lib/routes/spotify/utils.ts b/lib/routes/spotify/utils.ts
index 6103efa730e2dc..ea460fba9fce45 100644
--- a/lib/routes/spotify/utils.ts
+++ b/lib/routes/spotify/utils.ts
@@ -1,10 +1,11 @@
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
import got from '@/utils/got';
// Token used to retrieve public information.
async function getPublicToken() {
if (!config.spotify || !config.spotify.clientId || !config.spotify.clientSecret) {
- throw new Error('Spotify public RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Spotify public RSS is disabled due to the lack of relevant config');
}
const { clientId, clientSecret } = config.spotify;
@@ -26,7 +27,7 @@ async function getPublicToken() {
// Note that we don't use PKCE since the client secret shall be safe on the server.
async function getPrivateToken() {
if (!config.spotify || !config.spotify.clientId || !config.spotify.clientSecret || !config.spotify.refreshToken) {
- throw new Error('Spotify private RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Spotify private RSS is disabled due to the lack of relevant config');
}
const { clientId, clientSecret, refreshToken } = config.spotify;
diff --git a/lib/routes/sspai/author.ts b/lib/routes/sspai/author.ts
index 120785b32096d3..fced3b0cfee885 100644
--- a/lib/routes/sspai/author.ts
+++ b/lib/routes/sspai/author.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -13,7 +14,7 @@ async function getUserId(slug) {
});
if (response.data.error !== 0) {
- throw new Error('User Not Found');
+ throw new InvalidParameterError('User Not Found');
}
return response.data.data.id;
diff --git a/lib/routes/swjtu/xg.ts b/lib/routes/swjtu/xg.ts
index 8103faa59722bc..06e734110d67eb 100644
--- a/lib/routes/swjtu/xg.ts
+++ b/lib/routes/swjtu/xg.ts
@@ -3,6 +3,7 @@ import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const rootURL = 'http://xg.swjtu.edu.cn';
const listURL = {
@@ -84,7 +85,7 @@ async function handler(ctx) {
const pageURL = listURL[code];
if (!pageURL) {
- throw new Error('code not supported');
+ throw new InvalidParameterError('code not supported');
}
const resp = await got({
diff --git a/lib/routes/telegram/stickerpack.ts b/lib/routes/telegram/stickerpack.ts
index 66338509104b07..cd5822117638ac 100644
--- a/lib/routes/telegram/stickerpack.ts
+++ b/lib/routes/telegram/stickerpack.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/stickerpack/:name',
@@ -22,7 +23,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.telegram || !config.telegram.token) {
- throw new Error('Telegram Sticker Pack RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Telegram Sticker Pack RSS is disabled due to the lack of relevant config');
}
const name = ctx.req.param('name');
diff --git a/lib/routes/telegram/tglib/channel.ts b/lib/routes/telegram/tglib/channel.ts
index 1c4f0947332ef2..73864bf364b6f0 100644
--- a/lib/routes/telegram/tglib/channel.ts
+++ b/lib/routes/telegram/tglib/channel.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { client, decodeMedia, getClient, getFilename, getMediaLink, streamDocument, streamThumbnail } from './client';
import { returnBigInt as bigInt } from 'telegram/Helpers';
import { HTMLParser } from 'telegram/extensions/html';
@@ -8,7 +9,7 @@ function parseRange(range, length) {
}
const [typ, segstr] = range.split('=');
if (typ !== 'bytes') {
- throw `unsupported range: ${typ}`;
+ throw new InvalidParameterError(`unsupported range: ${typ}`);
}
const segs = segstr.split(',').map((s) => s.trim());
const parsedSegs = [];
diff --git a/lib/routes/telegram/tglib/client.ts b/lib/routes/telegram/tglib/client.ts
index 6d01710f96e3f9..855c5f887d01db 100644
--- a/lib/routes/telegram/tglib/client.ts
+++ b/lib/routes/telegram/tglib/client.ts
@@ -4,11 +4,12 @@ import { StringSession } from 'telegram/sessions';
import { getAppropriatedPartSize } from 'telegram/Utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
let client: TelegramClient | undefined;
export async function getClient(authParams?: UserAuthParams, session?: string) {
if (!config.telegram.session && session === undefined) {
- throw new Error('TELEGRAM_SESSION is not configured');
+ throw new ConfigNotFoundError('TELEGRAM_SESSION is not configured');
}
if (client) {
return client;
diff --git a/lib/routes/tencent/news/coronavirus/data.ts b/lib/routes/tencent/news/coronavirus/data.ts
index 8b08bb410f6497..bff3ed95f0e189 100644
--- a/lib/routes/tencent/news/coronavirus/data.ts
+++ b/lib/routes/tencent/news/coronavirus/data.ts
@@ -6,6 +6,7 @@ import { getData } from './utils';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/news/coronavirus/data/:province?/:city?',
@@ -50,7 +51,7 @@ async function handler(ctx) {
}
}
if (!coronavirusData) {
- throw new Error(`未找到 ${placeName} 的疫情数据,请检查输入的省市名称是否正确`);
+ throw new InvalidParameterError(`未找到 ${placeName} 的疫情数据,请检查输入的省市名称是否正确`);
}
todayConfirm = coronavirusData.today?.confirm;
totalNowConfirm = coronavirusData.total?.nowConfirm;
diff --git a/lib/routes/tencent/qq/sdk/changelog.ts b/lib/routes/tencent/qq/sdk/changelog.ts
index 3b7d92762c3bc7..6f58b7c8c9474c 100644
--- a/lib/routes/tencent/qq/sdk/changelog.ts
+++ b/lib/routes/tencent/qq/sdk/changelog.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
@@ -32,7 +33,7 @@ async function handler(ctx) {
title = 'Android SDK 历史变更';
link = 'https://wiki.connect.qq.com/android_sdk历史变更';
} else {
- throw new Error('not support platform');
+ throw new InvalidParameterError('not support platform');
}
const response = await got.get(link);
diff --git a/lib/routes/test/index.ts b/lib/routes/test/index.ts
index 752fad595758f0..138e3691b4896c 100644
--- a/lib/routes/test/index.ts
+++ b/lib/routes/test/index.ts
@@ -3,6 +3,8 @@ import { config } from '@/config';
import got from '@/utils/got';
import wait from '@/utils/wait';
import cache from '@/utils/cache';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
let cacheIndex = 0;
@@ -23,6 +25,12 @@ async function handler(ctx) {
url: 'https://httpbingo.org/status/404',
});
}
+ if (ctx.req.param('id') === 'config-not-found-error') {
+ throw new ConfigNotFoundError('Test config not found error');
+ }
+ if (ctx.req.param('id') === 'invalid-parameter-error') {
+ throw new InvalidParameterError('Test invalid parameter error');
+ }
let item: DataItem[] = [];
switch (ctx.req.param('id')) {
case 'filter':
diff --git a/lib/routes/trending/all-trending.ts b/lib/routes/trending/all-trending.ts
index 31d15f3cfd8480..1233cdeaf281df 100644
--- a/lib/routes/trending/all-trending.ts
+++ b/lib/routes/trending/all-trending.ts
@@ -13,6 +13,7 @@ import { art } from '@/utils/render';
import path from 'node:path';
import { config } from '@/config';
import md5 from '@/utils/md5';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
// Constants
const CACHE_KEY = 'trending-all-in-one';
@@ -187,7 +188,7 @@ export const route: Route = {
async function handler(ctx) {
// Prevent making over 100 requests per invocation
if (ctx.req.param('numberOfDays') > 14) {
- throw new Error('days must be less than 14');
+ throw new InvalidParameterError('days must be less than 14');
}
const numberOfDays = ctx.req.param('numberOfDays') || 3;
const currentShanghaiDateTime = dayjs(toShanghaiTimezone(new Date()));
diff --git a/lib/routes/twitch/schedule.ts b/lib/routes/twitch/schedule.ts
index ce72407b3b917f..1f5af2c5e9191e 100644
--- a/lib/routes/twitch/schedule.ts
+++ b/lib/routes/twitch/schedule.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import got from '@/utils/got';
@@ -76,7 +77,7 @@ async function handler(ctx) {
const streamScheduleData = response.data[1].data;
if (!streamScheduleData.user.id) {
- throw new Error(`Username does not exist`);
+ throw new InvalidParameterError(`Username does not exist`);
}
const displayName = channelShellData.userOrError.displayName;
diff --git a/lib/routes/twitch/video.ts b/lib/routes/twitch/video.ts
index dd85d9e097b2a2..3e3ed59a8493f3 100644
--- a/lib/routes/twitch/video.ts
+++ b/lib/routes/twitch/video.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -42,7 +43,7 @@ async function handler(ctx) {
const login = ctx.req.param('login');
const filter = ctx.req.param('filter')?.toLowerCase() || 'all';
if (!FILTER_NODE_TYPE_MAP[filter]) {
- throw new Error(`Unsupported filter type "${filter}", please choose from { ${Object.keys(FILTER_NODE_TYPE_MAP).join(', ')} }`);
+ throw new InvalidParameterError(`Unsupported filter type "${filter}", please choose from { ${Object.keys(FILTER_NODE_TYPE_MAP).join(', ')} }`);
}
const response = await got({
@@ -72,14 +73,14 @@ async function handler(ctx) {
const channelVideoShelvesQueryData = response.data[0].data;
if (!channelVideoShelvesQueryData.user.id) {
- throw new Error(`Username does not exist`);
+ throw new InvalidParameterError(`Username does not exist`);
}
const displayName = channelVideoShelvesQueryData.user.displayName;
const videoShelvesEdge = channelVideoShelvesQueryData.user.videoShelves.edges.find((edge) => edge.node.type === FILTER_NODE_TYPE_MAP[filter]);
if (!videoShelvesEdge) {
- throw new Error(`No video under filter type "${filter}"`);
+ throw new InvalidParameterError(`No video under filter type "${filter}"`);
}
const out = videoShelvesEdge.node.items.map((item) => ({
diff --git a/lib/routes/twitter/api/index.ts b/lib/routes/twitter/api/index.ts
index 10d721d092197d..64e861cae56987 100644
--- a/lib/routes/twitter/api/index.ts
+++ b/lib/routes/twitter/api/index.ts
@@ -1,3 +1,4 @@
+import ConfigNotFoundError from '@/errors/types/config-not-found';
import mobileApi from './mobile-api/api';
import webApi from './web-api/api';
import { config } from '@/config';
@@ -19,7 +20,7 @@ let api: {
getHomeTimeline: ApiItem;
} = {
init: () => {
- throw new Error('Twitter API is not configured');
+ throw new ConfigNotFoundError('Twitter API is not configured');
},
getUser: () => null,
getUserTweets: () => null,
diff --git a/lib/routes/twitter/api/mobile-api/api.ts b/lib/routes/twitter/api/mobile-api/api.ts
index aa8bc978326087..199f21c6e217a8 100644
--- a/lib/routes/twitter/api/mobile-api/api.ts
+++ b/lib/routes/twitter/api/mobile-api/api.ts
@@ -7,6 +7,7 @@ import CryptoJS from 'crypto-js';
import queryString from 'query-string';
import { initToken, getToken } from './token';
import cache from '@/utils/cache';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const twitterGot = async (url, params) => {
const token = await getToken();
@@ -209,7 +210,7 @@ const getUser = async (id) => {
const cacheTryGet = async (_id, params, func) => {
const id = await getUserID(_id);
if (id === undefined) {
- throw new Error('User not found');
+ throw new InvalidParameterError('User not found');
}
const funcName = func.name;
const paramsString = JSON.stringify(params);
diff --git a/lib/routes/twitter/api/mobile-api/token.ts b/lib/routes/twitter/api/mobile-api/token.ts
index 2d49d7303fe9b5..cba2174cfe7f5a 100644
--- a/lib/routes/twitter/api/mobile-api/token.ts
+++ b/lib/routes/twitter/api/mobile-api/token.ts
@@ -1,5 +1,6 @@
import { config } from '@/config';
import login from './login';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
let tokenIndex = 0;
let authentication = null;
@@ -32,7 +33,7 @@ function getToken() {
tokenIndex = 0;
}
} else {
- throw new Error('Invalid twitter configs');
+ throw new ConfigNotFoundError('Invalid twitter configs');
}
return token;
diff --git a/lib/routes/twitter/api/web-api/api.ts b/lib/routes/twitter/api/web-api/api.ts
index 2b684806195ab7..0431b632a8b46b 100644
--- a/lib/routes/twitter/api/web-api/api.ts
+++ b/lib/routes/twitter/api/web-api/api.ts
@@ -2,6 +2,7 @@ import { baseUrl, gqlMap, gqlFeatures } from './constants';
import { config } from '@/config';
import cache from '@/utils/cache';
import { twitterGot, paginationTweets, gatherLegacyFromData } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const getUserData = (id) =>
cache.tryGet(`twitter-userdata-${id}`, () => {
@@ -33,7 +34,7 @@ const cacheTryGet = async (_id, params, func) => {
const userData: any = await getUserData(_id);
const id = (userData.data?.user || userData.data?.user_result)?.result?.rest_id;
if (id === undefined) {
- throw new Error('User not found');
+ throw new InvalidParameterError('User not found');
}
const funcName = func.name;
const paramsString = JSON.stringify(params);
diff --git a/lib/routes/twitter/api/web-api/utils.ts b/lib/routes/twitter/api/web-api/utils.ts
index dce2192370ed38..b76647de1bab5f 100644
--- a/lib/routes/twitter/api/web-api/utils.ts
+++ b/lib/routes/twitter/api/web-api/utils.ts
@@ -1,3 +1,4 @@
+import ConfigNotFoundError from '@/errors/types/config-not-found';
import { baseUrl, gqlFeatures, bearerToken, gqlMap } from './constants';
import { config } from '@/config';
import got from '@/utils/got';
@@ -6,7 +7,7 @@ import { Cookie } from 'tough-cookie';
export const twitterGot = async (url, params) => {
if (!config.twitter.cookie) {
- throw new Error('Twitter cookie is not configured');
+ throw new ConfigNotFoundError('Twitter cookie is not configured');
}
const jsonCookie = Object.fromEntries(
config.twitter.cookie
@@ -15,7 +16,7 @@ export const twitterGot = async (url, params) => {
.map((c) => [c?.key, c?.value])
);
if (!jsonCookie || !jsonCookie.auth_token || !jsonCookie.ct0) {
- throw new Error('Twitter cookie is not valid');
+ throw new ConfigNotFoundError('Twitter cookie is not valid');
}
const requestData = {
diff --git a/lib/routes/twitter/likes.ts b/lib/routes/twitter/likes.ts
index 1c62479b965026..4f69bbe21a0f2d 100644
--- a/lib/routes/twitter/likes.ts
+++ b/lib/routes/twitter/likes.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/likes/:id/:routeParams?',
@@ -22,7 +23,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) {
- throw new Error('Twitter RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Twitter RSS is disabled due to the lack of relevant config');
}
const id = ctx.req.param('id');
const client = await utils.getAppClient();
diff --git a/lib/routes/twitter/trends.ts b/lib/routes/twitter/trends.ts
index 19ea43b55ac4f2..37d28812d45731 100644
--- a/lib/routes/twitter/trends.ts
+++ b/lib/routes/twitter/trends.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import utils from './utils';
import { config } from '@/config';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/trends/:woeid?',
@@ -22,7 +23,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.twitter || !config.twitter.consumer_key || !config.twitter.consumer_secret) {
- throw new Error('Twitter RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Twitter RSS is disabled due to the lack of relevant config');
}
const woeid = ctx.req.param('woeid') ?? 1; // Global information is available by using 1 as the WOEID
const client = await utils.getAppClient();
diff --git a/lib/routes/uestc/cqe.ts b/lib/routes/uestc/cqe.ts
index a06de72b0f2480..9d3db276117b4f 100644
--- a/lib/routes/uestc/cqe.ts
+++ b/lib/routes/uestc/cqe.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import puppeteer from '@/utils/puppeteer';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const baseUrl = 'https://cqe.uestc.edu.cn/';
@@ -47,7 +48,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') || 'tzgg';
const pageUrl = mapUrl[type];
if (!pageUrl) {
- throw new Error('type not supported');
+ throw new InvalidParameterError('type not supported');
}
const browser = await puppeteer({ stealth: true });
diff --git a/lib/routes/uestc/jwc.ts b/lib/routes/uestc/jwc.ts
index 02fe7b33ddfc44..324e0f34c8c505 100644
--- a/lib/routes/uestc/jwc.ts
+++ b/lib/routes/uestc/jwc.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const dateRegex = /(20\d{2})\/(\d{2})\/(\d{2})/;
@@ -48,7 +49,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') || 'important';
const pageUrl = map[type];
if (!pageUrl) {
- throw new Error('type not supported');
+ throw new InvalidParameterError('type not supported');
}
const response = await got.get(baseUrl + pageUrl);
diff --git a/lib/routes/uestc/news.ts b/lib/routes/uestc/news.ts
index 6eb4cd98fcf14e..a602a1b1409fec 100644
--- a/lib/routes/uestc/news.ts
+++ b/lib/routes/uestc/news.ts
@@ -2,6 +2,7 @@ import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const dateRegex = /(20\d{2}).(\d{2})-(\d{2})/;
@@ -45,7 +46,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') || 'announcement';
const pageUrl = map[type];
if (!pageUrl) {
- throw new Error('type not supported');
+ throw new InvalidParameterError('type not supported');
}
const response = await got.get(baseUrl + pageUrl);
diff --git a/lib/routes/uestc/sise.ts b/lib/routes/uestc/sise.ts
index 522114338e6478..de987645646bc8 100644
--- a/lib/routes/uestc/sise.ts
+++ b/lib/routes/uestc/sise.ts
@@ -3,6 +3,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import dayjs from 'dayjs';
import puppeteer from '@/utils/puppeteer';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const baseUrl = 'https://sise.uestc.edu.cn/';
@@ -62,7 +63,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') || 1;
const divId = mapId[type];
if (!divId) {
- throw new Error('type not supported');
+ throw new InvalidParameterError('type not supported');
}
const browser = await puppeteer({ stealth: true });
diff --git a/lib/routes/uptimerobot/rss.ts b/lib/routes/uptimerobot/rss.ts
index 5bb7bc23b09bfa..46f174f5d74b49 100644
--- a/lib/routes/uptimerobot/rss.ts
+++ b/lib/routes/uptimerobot/rss.ts
@@ -7,6 +7,7 @@ import { art } from '@/utils/render';
import path from 'node:path';
import dayjs from 'dayjs';
import { fallback, queryToBoolean } from '@/utils/readable-social';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const titleRegex = /(.+)\s+is\s+([A-Z]+)\s+\((.+)\)/;
@@ -101,12 +102,12 @@ async function handler(ctx) {
const items = rss.items.reverse().map((item) => {
const titleMatch = item.title.match(titleRegex);
if (!titleMatch) {
- throw new Error('Unexpected title, please open an issue.');
+ throw new InvalidParameterError('Unexpected title, please open an issue.');
}
const [monitorName, status, id] = titleMatch.slice(1);
if (id !== item.link) {
- throw new Error('Monitor ID mismatch, please open an issue.');
+ throw new InvalidParameterError('Monitor ID mismatch, please open an issue.');
}
// id could be a URL, a domain, an IP address, or a hex string. fix it
@@ -125,7 +126,7 @@ async function handler(ctx) {
} else if (status === 'DOWN') {
monitor.down(duration);
} else {
- throw new Error('Unexpected status, please open an issue.');
+ throw new InvalidParameterError('Unexpected status, please open an issue.');
}
const desc = art(path.join(__dirname, 'templates/rss.art'), {
diff --git a/lib/routes/ustc/eeis.ts b/lib/routes/ustc/eeis.ts
index e32d2aed94e108..85d532d5ba7077 100644
--- a/lib/routes/ustc/eeis.ts
+++ b/lib/routes/ustc/eeis.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['tzgg', { title: '中国科学技术大学电子工程与信息科学系 - 通知公告', id: '2702' }],
@@ -44,7 +45,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
diff --git a/lib/routes/ustc/gs.ts b/lib/routes/ustc/gs.ts
index ff5b61fa121576..b5cfb55b0200d1 100644
--- a/lib/routes/ustc/gs.ts
+++ b/lib/routes/ustc/gs.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['tzgg', { title: '中国科学技术大学研究生院 - 通知公告', id: '9' }],
@@ -44,7 +45,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
diff --git a/lib/routes/ustc/math.ts b/lib/routes/ustc/math.ts
index d19fb67980dc72..cccdd7e245053b 100644
--- a/lib/routes/ustc/math.ts
+++ b/lib/routes/ustc/math.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['xyxw', { title: '中国科学技术大学数学科学学院 - 学院新闻', id: 'xyxw' }],
@@ -46,7 +47,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
diff --git a/lib/routes/ustc/sist.ts b/lib/routes/ustc/sist.ts
index dd49cc0cd36dfd..3b0a2953929af5 100644
--- a/lib/routes/ustc/sist.ts
+++ b/lib/routes/ustc/sist.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const map = new Map([
['tzgg', { title: '中国科学技术大学信息科学技术学院 - 通知公告', id: '5142' }],
@@ -44,7 +45,7 @@ async function handler(ctx) {
const type = ctx.req.param('type') ?? 'tzgg';
const info = map.get(type);
if (!info) {
- throw new Error('invalid type');
+ throw new InvalidParameterError('invalid type');
}
const id = info.id;
diff --git a/lib/routes/utgd/topic.ts b/lib/routes/utgd/topic.ts
index 17ce8d3ebbfdcd..e01fa5147c272d 100644
--- a/lib/routes/utgd/topic.ts
+++ b/lib/routes/utgd/topic.ts
@@ -9,6 +9,7 @@ import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
import MarkdownIt from 'markdown-it';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const md = MarkdownIt({
html: true,
});
@@ -58,7 +59,7 @@ async function handler(ctx) {
const topicItems = response.data.filter((i) => i.title === topic);
if (!topicItems) {
- throw new Error(`No topic named ${topic}`);
+ throw new InvalidParameterError(`No topic named ${topic}`);
}
const topicItem = topicItems[0];
diff --git a/lib/routes/wechat/data258.ts b/lib/routes/wechat/data258.ts
index a4babfa3a5b567..51142a9a7ae4cf 100644
--- a/lib/routes/wechat/data258.ts
+++ b/lib/routes/wechat/data258.ts
@@ -6,6 +6,7 @@ import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { finishArticleItem } from '@/utils/wechat-mp';
import wait from '@/utils/wait';
+import RequestInProgressError from '@/errors/types/request-in-progress';
const parsePage = ($item, hyperlinkSelector, timeSelector) => {
const hyperlink = $item.find(hyperlinkSelector);
@@ -35,7 +36,7 @@ export const route: Route = {
async function handler(ctx) {
// !!! here we must use a lock to prevent other requests to break the anti-anti-crawler workarounds !!!
if ((await cache.get('data258:lock', false)) === '1') {
- throw new Error('Another request is in progress, please try again later.');
+ throw new RequestInProgressError('Another request is in progress, please try again later.');
}
// !!! here no need to acquire the lock, because the MP/category page has no crawler detection !!!
@@ -69,7 +70,7 @@ async function handler(ctx) {
// !!! double-check !!!
if ((await cache.get('data258:lock', false)) === '1') {
- throw new Error('Another request is in progress, please try again later.');
+ throw new RequestInProgressError('Another request is in progress, please try again later.');
} else {
// !!! here we acquire the lock because the jump page has crawler detection !!!
await cache.set('data258:lock', '1', 60);
diff --git a/lib/routes/wechat/feeddd.ts b/lib/routes/wechat/feeddd.ts
index f9dede4d29cd34..21b1b6e16e0adb 100644
--- a/lib/routes/wechat/feeddd.ts
+++ b/lib/routes/wechat/feeddd.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { finishArticleItem } from '@/utils/wechat-mp';
@@ -14,7 +15,7 @@ const handler = async (ctx) => {
response = await got(apiUrl);
} catch (error) {
if ((error.name === 'HTTPError' || error.name === 'FetchError') && error.response.statusCode === 404) {
- throw new Error('该公众号不存在,有关如何获取公众号 id,详见 https://docs.rsshub.app/routes/new-media#wei-xin-gong-zhong-hao-feeddd-lai-yuan');
+ throw new InvalidParameterError('该公众号不存在,有关如何获取公众号 id,详见 https://docs.rsshub.app/routes/new-media#wei-xin-gong-zhong-hao-feeddd-lai-yuan');
}
throw error;
}
diff --git a/lib/routes/weibo/friends.ts b/lib/routes/weibo/friends.ts
index 4aeb9d1980022d..3cb5cbe34752ba 100644
--- a/lib/routes/weibo/friends.ts
+++ b/lib/routes/weibo/friends.ts
@@ -5,6 +5,7 @@ import got from '@/utils/got';
import { config } from '@/config';
import weiboUtils from './utils';
import { fallback, queryToBoolean } from '@/utils/readable-social';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/friends/:routeParams?',
@@ -45,7 +46,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.weibo.cookies) {
- throw new Error('Weibo Friends Timeline is not available due to the absense of [Weibo Cookies]. Check relevant config tutorial');
+ throw new ConfigNotFoundError('Weibo Friends Timeline is not available due to the absense of [Weibo Cookies]. Check relevant config tutorial');
}
let displayVideo = '1';
diff --git a/lib/routes/weibo/group.ts b/lib/routes/weibo/group.ts
index c3850822c5d262..ad00e817d07b88 100644
--- a/lib/routes/weibo/group.ts
+++ b/lib/routes/weibo/group.ts
@@ -5,6 +5,7 @@ import got from '@/utils/got';
import { config } from '@/config';
import weiboUtils from './utils';
import { fallback, queryToBoolean } from '@/utils/readable-social';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/group/:gid/:gname?/:routeParams?',
@@ -38,7 +39,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.weibo.cookies) {
- throw new Error('Weibo Group Timeline is not available due to the absense of [Weibo Cookies]. Check relevant config tutorial');
+ throw new ConfigNotFoundError('Weibo Group Timeline is not available due to the absense of [Weibo Cookies]. Check relevant config tutorial');
}
const gid = ctx.req.param('gid');
diff --git a/lib/routes/wnacg/index.ts b/lib/routes/wnacg/index.ts
index 6ab4a78c8d5c78..bb877eabcfc9b7 100644
--- a/lib/routes/wnacg/index.ts
+++ b/lib/routes/wnacg/index.ts
@@ -8,6 +8,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
const categories = {
1: '同人誌 漢化',
@@ -49,7 +50,7 @@ export const route: Route = {
async function handler(ctx) {
const { cid, tag } = ctx.req.param();
if (cid && !Object.keys(categories).includes(cid)) {
- throw new Error('此分类不存在');
+ throw new InvalidParameterError('此分类不存在');
}
const url = `${baseUrl}/albums${cid ? `-index-cate-${cid}` : ''}${tag ? `-index-tag-${tag}` : ''}.html`;
diff --git a/lib/routes/xiaohongshu/user.ts b/lib/routes/xiaohongshu/user.ts
index dc60ae9168a305..96b03405fb6bb4 100644
--- a/lib/routes/xiaohongshu/user.ts
+++ b/lib/routes/xiaohongshu/user.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import { getUser } from './util';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/user/:user_id/:category',
@@ -36,13 +37,13 @@ async function handler(ctx) {
);
const renderCollect = (collect) => {
if (!collect) {
- throw new Error('该用户已设置收藏内容不可见');
+ throw new InvalidParameterError('该用户已设置收藏内容不可见');
}
if (collect.code !== 0) {
throw new Error(JSON.stringify(collect));
}
if (!collect.data.notes.length) {
- throw new Error('该用户已设置收藏内容不可见');
+ throw new InvalidParameterError('该用户已设置收藏内容不可见');
}
return collect.data.notes.map((item) => ({
title: item.display_title,
diff --git a/lib/routes/xsijishe/rank.ts b/lib/routes/xsijishe/rank.ts
index fa3304e0bc9ec0..fd95af2be76980 100644
--- a/lib/routes/xsijishe/rank.ts
+++ b/lib/routes/xsijishe/rank.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
@@ -33,7 +34,7 @@ async function handler(ctx) {
title = '司机社综合月排行榜';
rankId = 'nex_recons_demens1';
} else {
- throw new Error('Invalid rank type');
+ throw new InvalidParameterError('Invalid rank type');
}
const url = `${baseUrl}/portal.php`;
const resp = await got(url);
diff --git a/lib/routes/xueqiu/timeline.ts b/lib/routes/xueqiu/timeline.ts
index 46d649ba930aec..1b47e38dfa1261 100644
--- a/lib/routes/xueqiu/timeline.ts
+++ b/lib/routes/xueqiu/timeline.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
import cache from '@/utils/cache';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const rootUrl = 'https://xueqiu.com';
export const route: Route = {
path: '/timeline/:usergroup_id?',
@@ -39,7 +40,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') || 15;
const usergroup_id = ctx.req.param('usergroup_id') ?? -1;
if (cookie === undefined) {
- throw new Error('缺少雪球用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少雪球用户登录后的 Cookie 值');
}
let out: DataItem[] = [];
let max_id = -1;
diff --git a/lib/routes/yahoo/news/tw/index.ts b/lib/routes/yahoo/news/tw/index.ts
index ec681e779d1fc9..30fb4a70f4f9c4 100644
--- a/lib/routes/yahoo/news/tw/index.ts
+++ b/lib/routes/yahoo/news/tw/index.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import { getArchive, getCategories, parseList, parseItem } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/news/:region/:category?',
@@ -58,7 +59,7 @@ export const route: Route = {
async function handler(ctx) {
const { region, category } = ctx.req.param();
if (!['hk', 'tw'].includes(region)) {
- throw new Error(`Unknown region: ${region}`);
+ throw new InvalidParameterError(`Unknown region: ${region}`);
}
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20;
diff --git a/lib/routes/yahoo/news/tw/provider-helper.ts b/lib/routes/yahoo/news/tw/provider-helper.ts
index d2e9a4d325358a..e7c9345f342b20 100644
--- a/lib/routes/yahoo/news/tw/provider-helper.ts
+++ b/lib/routes/yahoo/news/tw/provider-helper.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import { getProviderList } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/news/providers/:region',
@@ -23,7 +24,7 @@ export const route: Route = {
async function handler(ctx) {
const region = ctx.req.param('region');
if (!['hk', 'tw'].includes(region)) {
- throw new Error(`Unknown region: ${region}`);
+ throw new InvalidParameterError(`Unknown region: ${region}`);
}
const providerList = await getProviderList(region, cache.tryGet);
diff --git a/lib/routes/yahoo/news/tw/provider.ts b/lib/routes/yahoo/news/tw/provider.ts
index a5fffc0bebdf34..934ef11501dc9a 100644
--- a/lib/routes/yahoo/news/tw/provider.ts
+++ b/lib/routes/yahoo/news/tw/provider.ts
@@ -1,6 +1,7 @@
import { Route } from '@/types';
import cache from '@/utils/cache';
import { getArchive, getProviderList, parseList, parseItem } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/news/provider/:region/:providerId',
@@ -26,7 +27,7 @@ export const route: Route = {
async function handler(ctx) {
const { region, providerId } = ctx.req.param();
if (!['hk', 'tw'].includes(region)) {
- throw new Error(`Unknown region: ${region}`);
+ throw new InvalidParameterError(`Unknown region: ${region}`);
}
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit'), 10) : 20;
diff --git a/lib/routes/yahoo/news/us/index.ts b/lib/routes/yahoo/news/us/index.ts
index 9d8e568470ef75..3daf5d415cf662 100644
--- a/lib/routes/yahoo/news/us/index.ts
+++ b/lib/routes/yahoo/news/us/index.ts
@@ -4,6 +4,7 @@ import got from '@/utils/got';
import parser from '@/utils/rss-parser';
import { load } from 'cheerio';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/news/en/:category?',
@@ -15,7 +16,7 @@ export const route: Route = {
async function handler(ctx) {
const region = ctx.req.param('region') === 'en' ? '' : ctx.req.param('region').toLowerCase();
if (!isValidHost(region) && region !== '') {
- throw new Error('Invalid region');
+ throw new InvalidParameterError('Invalid region');
}
const category = ctx.req.param('category') ? ctx.req.param('category').toLowerCase() : '';
const rssUrl = `https://${region ? `${region}.` : ''}news.yahoo.com/rss/${category}`;
diff --git a/lib/routes/youtube/channel.ts b/lib/routes/youtube/channel.ts
index d5c557ffcfcedf..abd64ec0433435 100644
--- a/lib/routes/youtube/channel.ts
+++ b/lib/routes/youtube/channel.ts
@@ -3,6 +3,8 @@ import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/channel/:id/:embed?',
@@ -38,13 +40,13 @@ YouTube provides official RSS feeds for channels, for instance [https://www.yout
async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
- throw new Error('YouTube RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of relevant config');
}
const id = ctx.req.param('id');
const embed = !ctx.req.param('embed');
if (!utils.isYouTubeChannelId(id)) {
- throw new Error(`Invalid YouTube channel ID. \nYou may want to use /youtube/user/:id
instead.`);
+ throw new InvalidParameterError(`Invalid YouTube channel ID. \nYou may want to use /youtube/user/:id
instead.`);
}
const playlistId = (await utils.getChannelWithId(id, 'contentDetails', cache)).data.items[0].contentDetails.relatedPlaylists.uploads;
diff --git a/lib/routes/youtube/custom.ts b/lib/routes/youtube/custom.ts
index 7267f635f5be15..2f7f0453d2e403 100644
--- a/lib/routes/youtube/custom.ts
+++ b/lib/routes/youtube/custom.ts
@@ -5,6 +5,7 @@ import { config } from '@/config';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/c/:username/:embed?',
@@ -24,7 +25,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
- throw new Error('YouTube RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of relevant config');
}
const username = ctx.req.param('username');
const embed = !ctx.req.param('embed');
diff --git a/lib/routes/youtube/live.ts b/lib/routes/youtube/live.ts
index 680c66582ec0ec..6e2f10cced8a68 100644
--- a/lib/routes/youtube/live.ts
+++ b/lib/routes/youtube/live.ts
@@ -5,6 +5,7 @@ import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
import got from '@/utils/got';
import { load } from 'cheerio';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/live/:username/:embed?',
@@ -26,7 +27,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
- throw new Error('YouTube RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of relevant config');
}
const username = ctx.req.param('username');
const embed = !ctx.req.param('embed');
diff --git a/lib/routes/youtube/playlist.ts b/lib/routes/youtube/playlist.ts
index 70602d066fb89a..e62717e991d7bd 100644
--- a/lib/routes/youtube/playlist.ts
+++ b/lib/routes/youtube/playlist.ts
@@ -3,6 +3,7 @@ import cache from '@/utils/cache';
import utils from './utils';
import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/playlist/:id/:embed?',
@@ -24,7 +25,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
- throw new Error('YouTube RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of relevant config');
}
const id = ctx.req.param('id');
const embed = !ctx.req.param('embed');
diff --git a/lib/routes/youtube/subscriptions.ts b/lib/routes/youtube/subscriptions.ts
index f19492ae1d85fb..0fec4e22342bae 100644
--- a/lib/routes/youtube/subscriptions.ts
+++ b/lib/routes/youtube/subscriptions.ts
@@ -4,6 +4,7 @@ import { config } from '@/config';
import utils from './utils';
import { parseDate } from '@/utils/parse-date';
import asyncPool from 'tiny-async-pool';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/subscriptions/:embed?',
@@ -44,7 +45,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.youtube || !config.youtube.key || !config.youtube.clientId || !config.youtube.clientSecret || !config.youtube.refreshToken) {
- throw new Error('YouTube RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of relevant config');
}
const embed = !ctx.req.param('embed');
diff --git a/lib/routes/youtube/user.ts b/lib/routes/youtube/user.ts
index bc910c34418f7f..bd1d1e6ecd52ae 100644
--- a/lib/routes/youtube/user.ts
+++ b/lib/routes/youtube/user.ts
@@ -5,6 +5,7 @@ import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
import got from '@/utils/got';
import { load } from 'cheerio';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/user/:username/:embed?',
@@ -32,7 +33,7 @@ export const route: Route = {
async function handler(ctx) {
if (!config.youtube || !config.youtube.key) {
- throw new Error('YouTube RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('YouTube RSS is disabled due to the lack of relevant config');
}
const username = ctx.req.param('username');
const embed = !ctx.req.param('embed');
diff --git a/lib/routes/zcool/user.ts b/lib/routes/zcool/user.ts
index abdc135b3f6c8a..beac412846ad91 100644
--- a/lib/routes/zcool/user.ts
+++ b/lib/routes/zcool/user.ts
@@ -5,6 +5,7 @@ import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import { extractArticle, extractWork } from './utils';
import { isValidHost } from '@/utils/valid-host';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/user/:uid',
@@ -40,7 +41,7 @@ async function handler(ctx) {
let pageUrl = `https://www.zcool.com.cn/u/${uid}`;
if (isNaN(uid)) {
if (!isValidHost(uid)) {
- throw new Error('Invalid uid');
+ throw new InvalidParameterError('Invalid uid');
}
pageUrl = `https://${uid}.zcool.com.cn`;
}
diff --git a/lib/routes/zhihu/timeline.ts b/lib/routes/zhihu/timeline.ts
index 1c3aaab4e6ccdf..cdd7f28a40f742 100644
--- a/lib/routes/zhihu/timeline.ts
+++ b/lib/routes/zhihu/timeline.ts
@@ -3,6 +3,7 @@ import got from '@/utils/got';
import { config } from '@/config';
import utils from './utils';
import { parseDate } from '@/utils/parse-date';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
export const route: Route = {
path: '/timeline',
@@ -33,7 +34,7 @@ export const route: Route = {
async function handler(ctx) {
const cookie = config.zhihu.cookies;
if (cookie === undefined) {
- throw new Error('缺少知乎用户登录后的 Cookie 值');
+ throw new ConfigNotFoundError('缺少知乎用户登录后的 Cookie 值');
}
const response = await got({
method: 'get',
diff --git a/lib/routes/zhubai/index.ts b/lib/routes/zhubai/index.ts
index 64df930d372317..13db57d4377a1f 100644
--- a/lib/routes/zhubai/index.ts
+++ b/lib/routes/zhubai/index.ts
@@ -1,3 +1,4 @@
+import InvalidParameterError from '@/errors/types/invalid-parameter';
import { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
@@ -28,7 +29,7 @@ async function handler(ctx) {
const id = ctx.req.param('id');
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 20;
if (!isValidHost(id)) {
- throw new Error('Invalid id');
+ throw new InvalidParameterError('Invalid id');
}
const response = await got({
diff --git a/lib/routes/zjol/paper.ts b/lib/routes/zjol/paper.ts
index 11b3b0eacc3ee3..d1d988312eb83d 100644
--- a/lib/routes/zjol/paper.ts
+++ b/lib/routes/zjol/paper.ts
@@ -3,6 +3,7 @@ import cache from '@/utils/cache';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
export const route: Route = {
path: '/paper/:id?',
@@ -31,7 +32,7 @@ async function handler(ctx) {
const allowedId = ['zjrb', 'qjwb', 'msb', 'zjlnb', 'zjfzb', 'jnyb'];
if (!allowedId.includes(id)) {
- throw new Error('id not allowed');
+ throw new InvalidParameterError('id not allowed');
}
const query = id === 'jnyb' ? 'map[name="PagePicMap"] area' : 'ul.main-ed-articlenav-list li a';
diff --git a/lib/routes/zodgame/forum.ts b/lib/routes/zodgame/forum.ts
index 164945729371bd..4a49c33700dda1 100644
--- a/lib/routes/zodgame/forum.ts
+++ b/lib/routes/zodgame/forum.ts
@@ -8,6 +8,7 @@ import { config } from '@/config';
import { parseDate } from '@/utils/parse-date';
import { art } from '@/utils/render';
import path from 'node:path';
+import ConfigNotFoundError from '@/errors/types/config-not-found';
const rootUrl = 'https://zodgame.xyz';
@@ -40,7 +41,7 @@ async function handler(ctx) {
const cookie = config.zodgame.cookie;
if (cookie === undefined) {
- throw new Error('Zodgame RSS is disabled due to the lack of relevant config');
+ throw new ConfigNotFoundError('Zodgame RSS is disabled due to the lack of relevant config');
}
const response = await got({