fix(route): add input validations (#23304)

* fix(route): add input validation

* fix(route): more input validation

* fix(route): improve handling of publication date and category extraction
This commit is contained in:
Tony
2026-09-15 23:30:21 +08:00
committed by GitHub
parent b394f95403
commit 8cda8f80b6
82 changed files with 308 additions and 72 deletions
+21 -1
View File
@@ -41,6 +41,15 @@ const fakeDirectories = {
handler: (ctx: Context) => ({ title: String(ctx.get('fromOuter')), link: 'https://example.com', item: [], allowEmpty: true }),
},
},
'/redirect.ts': {
route: {
path: '/redirect',
name: 'Redirect',
handler: (ctx: Context) => {
ctx.set('redirect', '/flat/single');
},
},
},
'/boom.ts': {
route: {
path: '/boom',
@@ -90,7 +99,7 @@ afterAll(() => {
const buildApp = () => {
const namespaces: NamespacesType = {};
const dev = createDevRegistry({ routesDirectory, namespaces });
const app = new Hono<{ Variables: { fromOuter: string; data: Data; apiData: { ok: boolean } } }>();
const app = new Hono<{ Variables: { fromOuter: string; data: Data; apiData: { ok: boolean }; redirect: string } }>();
app.use(async (ctx, next) => {
ctx.set('fromOuter', 'bridged');
await next();
@@ -98,6 +107,10 @@ const buildApp = () => {
if (apiData) {
return ctx.json(apiData);
}
const redirect = ctx.get('redirect');
if (redirect) {
return ctx.redirect(redirect, 301);
}
const data = ctx.get('data');
if (data) {
return ctx.json(data);
@@ -143,6 +156,13 @@ describe('createDevRegistry', () => {
expect(body.title).toBe('bridged');
});
it('finalizes handlers that only set a redirect', async () => {
const { app } = buildApp();
const response = await app.request('/flat/redirect');
expect(response.status).toBe(301);
expect(response.headers.get('location')).toBe('/flat/single');
});
it('serves nested namespaces', async () => {
const { app } = buildApp();
const response = await app.request('/github/enterprise/news');
+2 -2
View File
@@ -46,7 +46,7 @@ export function createDevRegistry({ routesDirectory, namespaces }: { routesDirec
outer.set(key as never, value as never);
}
}
if (!ctx.finalized && (ctx.get('data') || ctx.get('apiData'))) {
if (!ctx.finalized && (ctx.get('data') || ctx.get('apiData') || ctx.get('redirect') || ctx.get('no-content'))) {
// Data-producing handlers return undefined (the outer template middleware renders the
// bridged vars); finalize so Hono does not raise "Context is not finalized".
ctx.res = new Response(null, { status: 204 });
@@ -104,7 +104,7 @@ export function createDevRegistry({ routesDirectory, namespaces }: { routesDirec
outerContexts.set(ctx.req.raw, ctx);
try {
const response = await subApp.fetch(ctx.req.raw);
if (ctx.get('data') || ctx.get('apiData')) {
if (ctx.get('data') || ctx.get('apiData') || ctx.get('redirect') || ctx.get('no-content')) {
// The upstream template middleware renders from the bridged vars
return;
}
+6
View File
@@ -3,16 +3,22 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate, parseRelativeDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx: Context): Promise<Data> => {
const { category = 'newslists', id } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://info.10000link.com';
+1 -1
View File
@@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://www.199it.com';
const targetUrl: string = new URL(category, baseUrl).href;
const targetUrl = `${baseUrl}/${category}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -18,7 +18,7 @@ export const handler = async (ctx: Context) => {
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
const currentUrl = new URL(`${params && params.endsWith('.htm') ? params : `${params}.htm`}${queryString ? `?${queryString}` : ''}`, rootUrl).href;
const currentUrl = `${rootUrl}/${params && params.endsWith('.htm') ? params : `${params}.htm`}${queryString ? `?${queryString}` : ''}`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -13,7 +13,7 @@ export const handler = async (ctx) => {
const domain = '423down.com';
const rootUrl = `https://www.${domain}`;
const currentUrl = new URL(category, rootUrl).href;
const currentUrl = `${rootUrl}/${category}`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -2,12 +2,14 @@ import { load } from 'cheerio';
import { raw } from 'hono/html';
import { renderToString } from 'hono/jsx/dom/server';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import md5 from '@/utils/md5';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
const renderDescription = ({ images, title, keys, details, description, info, links }) =>
renderToString(
@@ -79,6 +81,10 @@ function stringtoHex(acSTR) {
async function handler(ctx) {
const { id = '4k-uhd-1' } = ctx.req.param();
if (!isValidHost(id)) {
throw new InvalidParameterError('Invalid id');
}
const limit = ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 25;
const rootUrl = 'https://www.4ksj.com';
+1 -1
View File
@@ -17,7 +17,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const encoding = 'gb2312';
const baseUrl = 'https://www.hao6v.me';
const targetUrl: string = new URL(category.startsWith('gvod') ? `${category}.html` : category, baseUrl).href;
const targetUrl = `${baseUrl}/${category.startsWith('gvod') ? `${category}.html` : category}`;
const response = await ofetch(targetUrl, {
responseType: 'arrayBuffer',
+2 -2
View File
@@ -42,8 +42,8 @@ async function handler(ctx) {
let currentUrl: string;
let documentId;
if (Number.isNaN(category)) {
currentUrl = new URL(category, rootUrl).href;
if (Number.isNaN(Number(category))) {
currentUrl = `${rootUrl}/${category}`;
} else {
documentId = category;
const feedUrl = new URL(`news/feed/${documentId}/rss.xml`, rootUrl).href;
+1 -1
View File
@@ -12,7 +12,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://www.accessbriefing.com';
const currentUrl = new URL(category, rootUrl).href;
const currentUrl = `${rootUrl}/${category}`;
const apiUrl = new URL('Ajax/GetPagedArticles', rootUrl).href;
const { data: currentResponse } = await got(currentUrl);
+6
View File
@@ -3,16 +3,22 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx: Context): Promise<Data> => {
const { device, sort, searchParams } = ctx.req.param();
if (!isValidHost(device)) {
throw new InvalidParameterError('Invalid device');
}
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://amazfitwatchfaces.com';
+6
View File
@@ -2,14 +2,20 @@ import type { CheerioAPI } from 'cheerio';
import { load } from 'cheerio';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { isValidHost } from '@/utils/valid-host';
import { baseUrl, fetchItems } from './util';
export const handler = async (ctx: Context): Promise<Data> => {
const { category = 'highlights', country = 'us' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = Number(ctx.req.query('limit') ?? '100');
const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href;
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
const host = 'https://support.apple.com/';
@@ -33,6 +35,10 @@ export const route: Route = {
async function handler(ctx) {
const country = ctx.req.param('country') ?? '';
if (country && !isValidHost(country)) {
throw new InvalidParameterError('Invalid country');
}
const link = new URL(`${country}/service-programs`, host).href;
const response = await got(link);
+6
View File
@@ -3,16 +3,22 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/security-releases';
export const handler = async (ctx: Context): Promise<Data> => {
const { language: languageParam = 'en-us' } = ctx.req.param();
if (!isValidHost(languageParam)) {
throw new InvalidParameterError('Invalid language');
}
const language = languageParam as Language;
const limit = Number(ctx.req.query('limit') ?? '30');
+2 -2
View File
@@ -38,7 +38,7 @@ async function handler(ctx) {
const filterRegion = filterMatches[1];
const filterType = filterMatches[2] === 'tag' ? 'tags' : filterMatches[2] === 'category' ? 'categories' : filterMatches[2];
const filterKeyword = decodeURI(filterMatches[3].split('/').pop());
const filterApiUrl = new URL(`${filterRegion}/${apiSlug}/${filterType}?search=${filterKeyword}`, rootUrl).href;
const filterApiUrl = `${rootUrl}/${filterRegion}/${apiSlug}/${filterType}?search=${filterKeyword}`;
const { data: filterResponse } = await got(filterApiUrl);
@@ -47,7 +47,7 @@ async function handler(ctx) {
if (filter?.id ?? undefined) {
filterName = filter.name ?? filterKeyword;
currentUrl = filter.link ?? currentUrl;
apiUrl = new URL(`${filterRegion}/${apiSlug}/posts?_embed=true&per_page=${limit}&${filterType}=${filter.id}`, rootUrl).href;
apiUrl = `${rootUrl}/${filterRegion}/${apiSlug}/posts?_embed=true&per_page=${limit}&${filterType}=${filter.id}`;
}
}
+6
View File
@@ -1,11 +1,13 @@
import { load } from 'cheerio';
import iconv from 'iconv-lite';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
export const route: Route = {
path: '/:category?',
@@ -30,6 +32,10 @@ export const route: Route = {
async function handler(ctx) {
const { category = 'xxkd' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'http://www.auto-stats.org.cn';
+1 -1
View File
@@ -10,7 +10,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20;
const rootUrl = 'https://mba.bnu.edu.cn';
const currentUrl = new URL(`${category.replace(/\/$/, '')}/`, rootUrl).href;
const currentUrl = `${rootUrl}/${category.replace(/\/$/, '')}/`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -28,7 +28,7 @@ async function handler(ctx) {
let filterName;
const path = ctx.req.param('path') ?? '';
const currentUrl = new URL(`/${path}`, rootUrl).href;
const currentUrl = `${rootUrl}/${path}`;
let apiUrl = new URL(`${apiSlug}/posts?_embed=true&per_page=${limit}`, rootUrl).href;
const filterMatches = path.match(/^post-(tag|category)\/(.*)$/);
+1 -1
View File
@@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '10');
const baseUrl = 'http://www.ccagm.org.cn';
const targetUrl: string = new URL(category, baseUrl).href;
const targetUrl = `${baseUrl}/${category}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -14,7 +14,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '15');
const baseUrl = 'https://www.cccmc.org.cn';
const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href;
const targetUrl = `${baseUrl}/${category.endsWith('/') ? category : `${category}/`}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+6
View File
@@ -3,16 +3,22 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx: Context): Promise<Data> => {
const { category = 'news' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = Number(ctx.req.query('limit') ?? '7');
const baseUrl = 'http://www.ccg.org.cn';
+1 -1
View File
@@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '11');
const rootUrl = 'https://www.chinacdc.cn';
const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, rootUrl).href;
const targetUrl = `${rootUrl}/${category.endsWith('/') ? category : `${category}/`}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -17,7 +17,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://language.chinadaily.com.cn';
const targetUrl: string = new URL(category, baseUrl).href;
const targetUrl = `${baseUrl}/${category}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+6
View File
@@ -1,12 +1,18 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
export const handler = async (ctx) => {
const { category = 'news-325' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 12;
const rootUrl = 'https://www.cngold.org.cn';
+6
View File
@@ -3,14 +3,20 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
export const handler = async (ctx: Context): Promise<Data> => {
const { category = 'news', id = '10' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = Number(ctx.req.query('limit') ?? '20');
const baseUrl = 'https://www.cnljxh.org.cn';
+1 -1
View File
@@ -37,7 +37,7 @@ export const route: Route = {
async function handler(ctx) {
const { path = 'index/tzgg' } = ctx.req.param();
const baseUrl = 'http://jwc.cqu.edu.cn';
const url = new URL(`${path}.htm`, baseUrl).href;
const url = `${baseUrl}/${path}.htm`;
const response = await ofetch(url);
const $ = load(response);
+6
View File
@@ -1,13 +1,19 @@
import { load } from 'cheerio';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
export const handler = async (ctx: Context): Promise<Data> => {
const { topic, locale } = ctx.req.param();
if (locale && !isValidHost(locale)) {
throw new InvalidParameterError('Invalid locale');
}
const limit = Number(ctx.req.query('limit') ?? '10');
const baseUrl = 'https://cursor.com';
+6
View File
@@ -3,13 +3,19 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
export const handler = async (ctx: Context): Promise<Data> => {
const locale = ctx.req.param('locale');
if (locale && !isValidHost(locale)) {
throw new InvalidParameterError('Invalid locale');
}
const limit = Number(ctx.req.query('limit') ?? '100');
const baseUrl = 'https://cursor.com';
+6
View File
@@ -3,16 +3,22 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx: Context): Promise<Data> => {
const { lang = 'en' } = ctx.req.param();
if (!isValidHost(lang)) {
throw new InvalidParameterError('Invalid lang');
}
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://www.deepl.com';
+6
View File
@@ -1,14 +1,20 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx) => {
const { language = 'CN', category = 'paper' } = ctx.req.param();
if (!isValidHost(language)) {
throw new InvalidParameterError('Invalid language');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 6;
const rootUrl = 'https://www.dehenglaw.com';
+5
View File
@@ -1,8 +1,10 @@
import { load } from 'cheerio';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import ofetch from '@/utils/ofetch';
import { isValidHost } from '@/utils/valid-host';
export const route: Route = {
path: '/:type',
@@ -16,6 +18,9 @@ export const route: Route = {
async function handler(ctx: Context) {
const { type } = ctx.req.param();
if (!isValidHost(type)) {
throw new InvalidParameterError('Invalid type');
}
const host = 'https://www.dekudeals.com/';
const link = new URL(type, host).href;
+6
View File
@@ -1,10 +1,12 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -42,6 +44,10 @@ export const route: Route = {
async function handler(ctx) {
const { language, category = '' } = ctx.req.param();
if (!isValidHost(language)) {
throw new InvalidParameterError('Invalid language');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10;
const rootUrl = 'https://dn.com';
@@ -1,7 +1,9 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import got from '@/utils/got';
import { isValidHost } from '@/utils/valid-host';
const host = 'https://www.douban.com/explore/column/';
export const route: Route = {
@@ -16,6 +18,10 @@ export const route: Route = {
async function handler(ctx) {
const id = ctx.req.param('id');
if (!isValidHost(id)) {
throw new InvalidParameterError('Invalid id');
}
const link = new URL(id, host).href;
const response = await got.get(link);
const $ = load(response.data);
+6
View File
@@ -2,9 +2,11 @@ import { load } from 'cheerio';
import { raw } from 'hono/html';
import { renderToString } from 'hono/jsx/dom/server';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { isValidHost } from '@/utils/valid-host';
const baseURL = 'https://freecomputerbooks.com/';
@@ -43,6 +45,10 @@ export const route: Route = {
async function handler(ctx) {
const categoryId = ctx.req.param('category')?.trim();
if (categoryId && !isValidHost(categoryId)) {
throw new InvalidParameterError('Invalid category');
}
const requestURL = categoryId ? new URL(`${categoryId}.html`, baseURL).href : baseURL;
const $ = await cheerioLoad(requestURL);
+6
View File
@@ -4,9 +4,11 @@ import type { Element } from 'domhandler';
import type { Context } from 'hono';
import { renderToString } from 'hono/jsx/dom/server';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { isValidHost } from '@/utils/valid-host';
const renderDescription = ({ images, stargazersCount, language, description }) =>
renderToString(
@@ -49,6 +51,10 @@ const renderDescription = ({ images, stargazersCount, language, description }) =
export const handler = async (ctx: Context): Promise<Data> => {
const { category = 'repositories' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = Number(ctx.req.query('limit') ?? '100');
const baseUrl = 'https://gitstar-ranking.com';
+1 -1
View File
@@ -8,7 +8,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://www.mhlw.go.jp';
const currentUrl = new URL(category.endsWith('.html') ? category : `${category}.html`, rootUrl).href;
const currentUrl = `${rootUrl}/${category.endsWith('.html') ? category : `${category}.html`}`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -11,7 +11,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20;
const rootUrl = 'https://kjt.ah.gov.cn';
const currentUrl = new URL(`${category.replace(/\/$/, '').replace(/\/index\.html$/, '')}/`, rootUrl).href;
const currentUrl = `${rootUrl}/${category.replace(/\/$/, '').replace(/\/index\.html$/, '')}/`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -33,7 +33,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 15;
const rootUrl = 'https://gzw.cq.gov.cn';
const currentUrl = new URL(category, rootUrl).href;
const currentUrl = `${rootUrl}/${category}`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -35,6 +37,10 @@ export const route: Route = {
async function handler(ctx) {
const { category = 'gjlckjdjt' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'http://www.forestry.gov.cn';
+5
View File
@@ -1,5 +1,6 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import cache from '@/utils/cache';
import { getSubPath } from '@/utils/common-utils';
// 来人拯救一下啊( >﹏<。)
@@ -40,6 +41,7 @@ import { getSubPath } from '@/utils/common-utils';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
import { finishArticleItem } from '@/utils/wechat-mp';
import { renderZcjdpt } from './templates/zcjdpt';
@@ -49,6 +51,9 @@ const gdgov = async (info, ctx) => {
.split('/')
.filter((item) => item !== '');
const [site, branch] = path;
if (!isValidHost(site) || !isValidHost(branch)) {
throw new InvalidParameterError('Invalid site or branch');
}
// 网站
const pathstartat = info.pathstartat === undefined ? 0 : info.pathstartat;
+1 -1
View File
@@ -11,7 +11,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 25;
const rootUrl = 'https://www.lswz.gov.cn';
const currentUrl = new URL(`${category}.shtml`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}.shtml`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'http://www.gjs.moa.gov.cn';
const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href;
const targetUrl = `${baseUrl}/${category.endsWith('/') ? category : `${category}/`}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -67,7 +67,7 @@ async function handler(ctx) {
}
const rootUrl = 'https://www.nsfc.gov.cn';
const currentUrl = new URL((thePath.endsWith('/more') ? `${thePath}.htm` : thePath) || 'publish/portal0/tab442/', rootUrl).href;
const currentUrl = `${rootUrl}${(thePath.endsWith('/more') ? `${thePath}.htm` : thePath) || '/publish/portal0/tab442/'}`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import { renderToString } from 'hono/jsx/dom/server';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Language } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
const rootUrl = 'https://www.safe.gov.cn';
@@ -16,6 +18,10 @@ const zxfkCategoryApis = {
};
const processZxfkItems = async (site = 'beijing', category = 'ywzx', limit = 3) => {
if (!isValidHost(site)) {
throw new InvalidParameterError('Invalid site');
}
const apiUrl = new URL(`${zxfkCategoryApis[category]}${site}`, rootUrl).href;
const currentUrl = new URL(`${site}/${category}/index.html`, rootUrl).href;
+1 -1
View File
@@ -28,7 +28,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20;
const rootUrl = 'https://fgw.sh.gov.cn';
const currentUrl = new URL(`${category}/index.html`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}/index.html`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
const rootURL = 'http://www.zzb.sz.gov.cn/';
@@ -36,6 +38,10 @@ export const route: Route = {
async function handler(ctx) {
const categoryID = ctx.req.param('caty');
if (!isValidHost(categoryID)) {
throw new InvalidParameterError('Invalid caty');
}
const page = ctx.req.param('page') ?? '1';
const pageParam = Number.parseInt(page) > 1 ? `_${page}` : '';
+1 -1
View File
@@ -30,7 +30,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://fg.suzhou.gov.cn';
const currentUrl = new URL(`${categoryPath}.shtml`, rootUrl).href;
const currentUrl = `${rootUrl}/${categoryPath}.shtml`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -3,15 +3,21 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
export const handler = async (ctx: Context): Promise<Data> => {
const { category, id } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'http://load.grainoil.com.cn';
+6
View File
@@ -3,16 +3,22 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Language, Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx: Context): Promise<Data> => {
const { id = 'tzgg' } = ctx.req.param();
if (!isValidHost(id)) {
throw new InvalidParameterError('Invalid id');
}
const limit = Number(ctx.req.query('limit') ?? '10');
const baseUrl = 'https://hitgs.hit.edu.cn';
+1 -1
View File
@@ -10,7 +10,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20;
const rootUrl = 'https://www.hubu.edu.cn';
const currentUrl = new URL(`${category}.htm`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}.htm`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -10,7 +10,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20;
const rootUrl = 'https://zhxy.hubu.edu.cn';
const currentUrl = new URL(`${category}.htm`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}.htm`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -10,7 +10,7 @@ export const handler = async (ctx) => {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 16;
const rootUrl = 'https://gs.hust.edu.cn';
const currentUrl = new URL(`${category}.htm`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}.htm`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -11,7 +11,7 @@ export const handler = async (ctx) => {
const domain = 'mse.hust.edu.cn';
const rootUrl = `https://${domain}`;
const currentUrl = new URL(`${category}.htm`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}.htm`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -30,6 +32,10 @@ export const route: Route = {
async function handler(ctx) {
const { category = 'portraits' } = ctx.req.param();
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const author = 'Ian Spriggs';
+6
View File
@@ -1,14 +1,20 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx) => {
const { conference } = ctx.req.param();
if (conference && !isValidHost(conference)) {
throw new InvalidParameterError('Invalid conference');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 12;
const rootUrl = 'https://www.infoq.com';
+1 -1
View File
@@ -14,7 +14,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '20');
const baseUrl = 'https://www.investor.org.cn';
const targetUrl: string = new URL(id.endsWith('/') ? id : `${id}/`, baseUrl).href;
const targetUrl = `${baseUrl}/${id.endsWith('/') ? id : `${id}/`}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -44,7 +44,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'http://v.iqilu.com';
const currentUrl = new URL(category, rootUrl).href;
const currentUrl = `${rootUrl}/${category}`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -1,10 +1,12 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import { ViewType } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -40,6 +42,10 @@ export const route: Route = {
async function handler(ctx) {
const language = ctx.req.param('language');
if (!isValidHost(language)) {
throw new InvalidParameterError('Invalid language');
}
const baseUrl = 'https://www.jimmyspa.com';
const booksListUrl = new URL(`/${language}/Books/Ajax/changeList?year=&keyword=&categoryId=0&page=1`, baseUrl).href;
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import { ViewType } from '@/types';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -39,6 +41,10 @@ export const route: Route = {
async function handler(ctx) {
const language = ctx.req.param('language');
if (!isValidHost(language)) {
throw new InvalidParameterError('Invalid language');
}
const rootUrl = 'https://www.jimmyspa.com';
const currentUrl = new URL(`/${language}/News/Ajax/changeList?year=&keyword=&categoryId=0&page=1`, rootUrl).href;
+1 -1
View File
@@ -92,7 +92,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://www.kantarworldpanel.com/';
const currentUrl = new URL(`${region}/${category}`, rootUrl).href;
const currentUrl = `${rootUrl}${region}/${category}`;
const { data: response } = await got(currentUrl);
+2 -2
View File
@@ -39,7 +39,7 @@ async function handler(ctx) {
const category = ctx.req.param('category') ?? 'National';
const baseUrl = 'https://www.koreaherald.com/';
const response = await got(new URL(category, baseUrl).href);
const response = await got(`${baseUrl}${category}`);
const $ = load(response.data);
const title = $('ul.gnb').find('[class="on"]').length > 0 ? $('ul.gnb').find('[class="on"]').text() : $('div.nav_area > a.category').text();
const list = $('article.recent_news > ul.news_list > li')
@@ -63,7 +63,7 @@ async function handler(ctx) {
);
return {
title: `The Korea Herald - ${title}`,
link: new URL(category, baseUrl).href,
link: `${baseUrl}${category}`,
item: items,
};
}
+6
View File
@@ -1,11 +1,13 @@
import { load } from 'cheerio';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
export const route: Route = {
path: '/xwzx/:name?',
@@ -31,6 +33,10 @@ const nameProps = {
async function handler(ctx: Context): Promise<Data> {
const { name = 'all' } = ctx.req.param();
if (!isValidHost(name)) {
throw new InvalidParameterError('Invalid name');
}
const categories = name === 'all' ? Object.keys(nameProps) : [name];
const lists = await Promise.all(
+1 -1
View File
@@ -50,7 +50,7 @@ export async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 20;
const rootUrl = 'https://www.liulinblog.com';
const currentUrl = subPath === '/' ? rootUrl : new URL(subPath, rootUrl).href;
const currentUrl = subPath === '/' ? rootUrl : `${rootUrl}${subPath}`;
const { data: response } = await got(currentUrl);
+6
View File
@@ -1,14 +1,20 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
export const handler = async (ctx) => {
const { category = '' } = ctx.req.param();
if (category && !isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 12;
const rootUrl = 'https://lrepacks.net';
+6
View File
@@ -1,11 +1,13 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Route } from '@/types';
import cache from '@/utils/cache';
import { getSubPath } from '@/utils/common-utils';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -36,6 +38,10 @@ export const route: Route = {
export async function handler(ctx) {
const [id, category = 'china'] = getSubPath(ctx).split('/').filter(Boolean);
if (!isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = `http://${id}.m4.cn`;
+6
View File
@@ -3,12 +3,18 @@ import { load } from 'cheerio';
import type { Element } from 'domhandler';
import type { Context } from 'hono';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Data, DataItem, Route } from '@/types';
import { ViewType } from '@/types';
import ofetch from '@/utils/ofetch';
import { isValidHost } from '@/utils/valid-host';
export const handler = async (ctx: Context): Promise<Data> => {
const { language = '' } = ctx.req.param();
if (language && !isValidHost(language)) {
throw new InvalidParameterError('Invalid language');
}
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://musify.club';
+6
View File
@@ -1,8 +1,10 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import ofetch from '@/utils/ofetch';
import { isValidHost } from '@/utils/valid-host';
import { ProcessFeed, rootUrl } from './utils';
@@ -23,6 +25,10 @@ export const route: Route = {
async function handler(ctx) {
let sectionName: string = ctx.req.param('section');
if (!isValidHost(sectionName)) {
throw new InvalidParameterError('Invalid section');
}
sectionName = sectionName.replace('-', '/');
sectionName += '/';
const sectionUrl = new URL(sectionName, rootUrl).href;
+1 -1
View File
@@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '50');
const baseUrl = 'https://papers.cool';
const targetUrl: string = new URL(`${id}?show=${limit}`, baseUrl).href;
const targetUrl = `${baseUrl}/${id}?show=${limit}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '10');
const baseUrl = 'https://www.producereport.com';
const targetUrl: string = new URL(category, baseUrl).href;
const targetUrl = `${baseUrl}/${category}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+6
View File
@@ -1,10 +1,12 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
export const route: Route = {
path: '/:category?',
@@ -34,6 +36,10 @@ export const route: Route = {
async function handler(ctx) {
const { category = '' } = ctx.req.param();
if (category && !isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10;
const rootUrl = 'https://qqorw.cn';
+1 -1
View File
@@ -14,7 +14,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '17');
const baseUrl = 'https://www.samrdprc.org.cn';
const targetUrl: string = new URL(id.endsWith('/') ? id : `${id}/`, baseUrl).href;
const targetUrl = `${baseUrl}/${id.endsWith('/') ? id : `${id}/`}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -16,7 +16,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '12');
const baseUrl = 'https://www.semiconductors.org';
const targetUrl: string = new URL(category.endsWith('/') ? category : `${category}/`, baseUrl).href;
const targetUrl = `${baseUrl}/${category.endsWith('/') ? category : `${category}/`}`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
export const route: Route = {
path: '/pe/:id?',
@@ -67,6 +69,10 @@ export const route: Route = {
async function handler(ctx) {
const { id = '342' } = ctx.req.param();
if (!isValidHost(id)) {
throw new InvalidParameterError('Invalid id');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://pe2016.sspu.edu.cn';
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { DataItem, Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -323,6 +325,10 @@ export const route: Route = {
async function handler(ctx) {
const { category = '' } = ctx.req.param();
if (category && !isValidHost(category)) {
throw new InvalidParameterError('Invalid category');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 50;
const rootUrl = 'https://www.thoughtco.com';
+8 -10
View File
@@ -22,7 +22,8 @@ export const route: Route = {
},
{
name: 'WEIBO_REDIRECT_URL',
description: '',
optional: true,
description: "OAuth callback URL. Defaults to `<request origin>/weibo/timeline/0`. Set it when the auto-composed URL doesn't work",
},
],
requirePuppeteer: false,
@@ -93,7 +94,7 @@ async function handler(ctx) {
);
// 检查token失效
if (response.error !== undefined) {
const { app_key = '', redirect_url = ctx.req.origin + '/weibo/timeline/0' } = config.weibo;
const { app_key = '', redirect_url = `${new URL(ctx.req.url).origin}/weibo/timeline/0` } = config.weibo;
ctx.status = 302;
ctx.set({
@@ -164,8 +165,8 @@ async function handler(ctx) {
item: resultItem,
});
}
if (uid === '0' || ctx.req.query()) {
const { app_key = '', redirect_url = ctx.req.origin + '/weibo/timeline/0', app_secret = '' } = config.weibo;
if (uid === '0' || ctx.req.query('code')) {
const { app_key = '', redirect_url = `${new URL(ctx.req.url).origin}/weibo/timeline/0`, app_secret = '' } = config.weibo;
const code = ctx.req.query('code');
const routeParams = ctx.req.query('state');
@@ -176,14 +177,11 @@ async function handler(ctx) {
const expires_in = rep.data.expires_in;
await cache.set('weibotimelineuid' + uid, token, expires_in);
ctx.set({
'Content-Type': 'text/html; charset=UTF-8',
'Cache-Control': 'no-cache',
});
ctx.html(`<script>window.location = '/weibo/timeline/${uid}${routeParams ? `/${routeParams}` : ''}'</script>`);
ctx.header('Cache-Control', 'no-cache');
return ctx.redirect(`/weibo/timeline/${uid}${routeParams ? `/${routeParams}` : ''}`);
}
} else {
const { app_key = '', redirect_url = ctx.req.origin + '/weibo/timeline/0' } = config.weibo;
const { app_key = '', redirect_url = `${new URL(ctx.req.url).origin}/weibo/timeline/0` } = config.weibo;
ctx.status = 302;
ctx.set({
+1 -1
View File
@@ -61,7 +61,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = `https://hyxt.${domain}`;
const currentUrl = new URL(`${category}.htm`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}.htm`;
const { data: response } = await got(currentUrl);
+1 -1
View File
@@ -15,7 +15,7 @@ export const handler = async (ctx: Context): Promise<Data> => {
const limit = Number(ctx.req.query('limit') ?? '30');
const baseUrl = 'https://zs.xjtu.edu.cn';
const targetUrl: string = new URL(`${category}.htm`, baseUrl).href;
const targetUrl = `${baseUrl}/${category}.htm`;
const response = await ofetch(targetUrl);
const $: CheerioAPI = load(response);
+1 -1
View File
@@ -46,7 +46,7 @@ async function handler(ctx) {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://news.xmnn.cn';
const currentUrl = new URL(`${category}/`, rootUrl).href;
const currentUrl = `${rootUrl}/${category}/`;
const { data: response } = await got(currentUrl);
+6 -10
View File
@@ -25,7 +25,7 @@ async function handler(ctx): Promise<Data> {
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 10;
const rootUrl = 'https://dongke.yangtzeu.edu.cn';
const currentUrl = new URL(`/${ctx.req.param('path') ?? 'yqzl/xyxw'}.htm`, rootUrl).href;
const currentUrl = `${rootUrl}/${ctx.req.param('path') ?? 'yqzl/xyxw'}.htm`;
const { data: response } = await got(currentUrl);
@@ -52,15 +52,11 @@ async function handler(ctx): Promise<Data> {
item.title = content('title').text();
item.description = content('div.v_news_content').html();
item.category = content('meta[name="keywords"]').prop('content').split(',');
item.pubDate = timezone(
parseDate(
content('p.content-info')
.text()
.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)![0]
),
8
);
item.category = content('meta[name="keywords"]').prop('content')?.split(',');
const pubDate = content('p.content-info')
.text()
.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/)?.[0];
item.pubDate = pubDate ? timezone(parseDate(pubDate), 8) : undefined;
return item;
})
+6
View File
@@ -1,9 +1,11 @@
import { load } from 'cheerio';
import InvalidParameterError from '@/errors/types/invalid-parameter';
import type { Language, Route } from '@/types';
import cache from '@/utils/cache';
import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import { isValidHost } from '@/utils/valid-host';
import { renderDescription } from './templates/description';
@@ -74,6 +76,10 @@ export const route: Route = {
async function handler(ctx) {
const { column = 'article', category = '0' } = ctx.req.param();
if (!isValidHost(column)) {
throw new InvalidParameterError('Invalid column');
}
const limit = ctx.req.query('limit') ? Number(ctx.req.query('limit')) : 30;
const rootUrl = 'https://dt.yicai.com';
+3 -7
View File
@@ -7,6 +7,7 @@ import got from '@/utils/got';
import { parseDate } from '@/utils/parse-date';
import parser from '@/utils/rss-parser';
import timezone from '@/utils/timezone';
import { isValidHost } from '@/utils/valid-host';
export const route: Route = {
path: '/:lang?/:channel?',
@@ -48,17 +49,12 @@ For example, the path for the RSS feed url <https://www.yna.co.kr/rss/economy.xm
:::`,
};
// The language becomes the leading label of the requested host below, so it is
// checked against the codes the route documents. 'sp' is kept as well because
// the Spanish feed page linked in the description lives on sp.yna.co.kr.
const languages = new Set(['ko', 'en', 'cn', 'jp', 'ar', 'es', 'sp', 'fr']);
async function handler(ctx) {
const lang = ctx.req.param('lang') ?? 'ko';
const channel = ctx.req.param('channel') ?? 'news';
if (!languages.has(lang)) {
throw new InvalidParameterError('Unsupported language');
if (!isValidHost(lang)) {
throw new InvalidParameterError('Invalid lang');
}
let url;
+1 -1
View File
@@ -43,7 +43,7 @@ export const route: Route = {
async function handler(ctx) {
const uid = ctx.req.param('uid');
let pageUrl = `https://www.zcool.com.cn/u/${uid}`;
if (Number.isNaN(uid)) {
if (Number.isNaN(Number(uid))) {
if (!isValidHost(uid)) {
throw new InvalidParameterError('Invalid uid');
}