mirror of
https://github.com/imsyy/DailyHotApi.git
synced 2026-01-12 21:24:55 +08:00
Compare commits
5 Commits
v2.0.0-rc.
...
v2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14bc5a1dce | ||
|
|
11addd20ca | ||
|
|
23375519ba | ||
|
|
2ba46c5e0e | ||
|
|
d4a52a6b24 |
14
.github/workflows/docker.yml
vendored
14
.github/workflows/docker.yml
vendored
@@ -3,9 +3,10 @@ name: Publish Docker image
|
|||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
push_to_registry:
|
build-docker:
|
||||||
name: Push Docker image to multiple registries
|
name: Push Docker image to multiple registries
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
@@ -15,6 +16,12 @@ jobs:
|
|||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
@@ -36,11 +43,14 @@ jobs:
|
|||||||
imsyy/dailyhot-api
|
imsyy/dailyhot-api
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
|
platforms: |
|
||||||
|
linux/amd64
|
||||||
|
linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
17
Dockerfile
17
Dockerfile
@@ -1,8 +1,17 @@
|
|||||||
FROM node:20-alpine AS base
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
|
ENV NODE_ENV=docker
|
||||||
|
|
||||||
|
# 安装 Puppeteer 所需的依赖库
|
||||||
|
RUN apk add libc6-compat
|
||||||
|
# RUN apk add chromium nss freetype harfbuzz ca-certificates
|
||||||
|
|
||||||
|
# 配置 Chromium
|
||||||
|
# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
|
||||||
|
# ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
|
|
||||||
RUN apk add --no-cache libc6-compat
|
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g pnpm
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -10,9 +19,9 @@ COPY package*json tsconfig.json pnpm-lock.yaml .env ./
|
|||||||
COPY src ./src
|
COPY src ./src
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
|
|
||||||
RUN pnpm install && \
|
RUN pnpm install
|
||||||
pnpm build && \
|
RUN pnpm build
|
||||||
pnpm prune --production
|
RUN pnpm prune --production
|
||||||
|
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -2,6 +2,12 @@
|
|||||||
<img alt="logo" height="120" src="./public/favicon.png" width="120"/>
|
<img alt="logo" height="120" src="./public/favicon.png" width="120"/>
|
||||||
<h2>今日热榜</h2>
|
<h2>今日热榜</h2>
|
||||||
<p>一个聚合热门数据的 API 接口</p>
|
<p>一个聚合热门数据的 API 接口</p>
|
||||||
|
<br />
|
||||||
|
<img src="https://img.shields.io/github/last-commit/imsyy/DailyHotApi" alt="last commit"/>
|
||||||
|
<img src="https://img.shields.io/github/languages/code-size/imsyy/DailyHotApi" alt="code size"/>
|
||||||
|
<img src="https://img.shields.io/docker/image-size/imsyy/dailyhot-api" alt="docker-image-size"/>
|
||||||
|
<img src="https://github.com/imsyy/DailyHotApi/actions/workflows/docker.yml/badge.svg" alt="Publish Docker image"/>
|
||||||
|
<img src="https://github.com/imsyy/DailyHotApi/actions/workflows/npm.yml/badge.svg" alt="Publish npm package"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## 🚩 特性
|
## 🚩 特性
|
||||||
@@ -46,8 +52,8 @@
|
|||||||
| 稀土掘金 | 热榜 | juejin | 🟢 |
|
| 稀土掘金 | 热榜 | juejin | 🟢 |
|
||||||
| 腾讯新闻 | 热点榜 | qq-news | 🟢 |
|
| 腾讯新闻 | 热点榜 | qq-news | 🟢 |
|
||||||
| 网易新闻 | 热点榜 | netease-news | 🟢 |
|
| 网易新闻 | 热点榜 | netease-news | 🟢 |
|
||||||
| 吾爱破解 | 榜单 | 52pojie | 🟢 |
|
| 吾爱破解 | 榜单 | 52pojie | ❌ |
|
||||||
| 全球主机交流 | 榜单 | hostloc | 🟢 |
|
| 全球主机交流 | 榜单 | hostloc | ❌ |
|
||||||
| 虎嗅 | 24小时 | huxiu | 🟢 |
|
| 虎嗅 | 24小时 | huxiu | 🟢 |
|
||||||
| 爱范儿 | 快讯 | ifanr | 🟢 |
|
| 爱范儿 | 快讯 | ifanr | 🟢 |
|
||||||
| 英雄联盟 | 更新公告 | lol | 🟢 |
|
| 英雄联盟 | 更新公告 | lol | 🟢 |
|
||||||
@@ -142,6 +148,12 @@ npm run start
|
|||||||
|
|
||||||
成功启动后程序会在控制台输出可访问的地址
|
成功启动后程序会在控制台输出可访问的地址
|
||||||
|
|
||||||
|
### Vercel 部署
|
||||||
|
|
||||||
|
本项目支持通过 `Vercel` 进行一键部署,点击下方按钮或前往 [项目仓库](https://github.com/imsyy/DailyHotApi-Vercel) 进行手动部署
|
||||||
|
|
||||||
|
[](https://vercel.com/new/imsyys-projects/clone?repository-url=https%3A%2F%2Fgithub.com%2Fimsyy%2FDailyHotApi-Vercel)
|
||||||
|
|
||||||
### Railway 部署
|
### Railway 部署
|
||||||
|
|
||||||
本项目支持使用 [Railway](https://railway.app/) 一键部署,请先将本项目 fork 到您的仓库中,即可使用一键部署。
|
本项目支持使用 [Railway](https://railway.app/) 一键部署,请先将本项目 fork 到您的仓库中,即可使用一键部署。
|
||||||
@@ -150,12 +162,6 @@ npm run start
|
|||||||
|
|
||||||
本项目支持使用 [Zeabur](https://zeabur.com/) 一键部署,请先将本项目 fork 到您的仓库中,即可使用一键部署。
|
本项目支持使用 [Zeabur](https://zeabur.com/) 一键部署,请先将本项目 fork 到您的仓库中,即可使用一键部署。
|
||||||
|
|
||||||
### Vercel 部署
|
|
||||||
|
|
||||||
> 🚧 Vercel 部署支持正在修复中
|
|
||||||
|
|
||||||
若您目前仅能通过 `Vercel` 进行部署,那么请暂时不要使用最新版本
|
|
||||||
|
|
||||||
## ⚠️ 须知
|
## ⚠️ 须知
|
||||||
|
|
||||||
- 本项目为了避免频繁请求官方数据,默认对数据做了缓存处理,默认为 `60` 分钟,如需更改,请自行修改配置
|
- 本项目为了避免频繁请求官方数据,默认对数据做了缓存处理,默认为 `60` 分钟,如需更改,请自行修改配置
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dailyhot-api",
|
"name": "dailyhot-api",
|
||||||
"version": "2.0.0-rc.4",
|
"version": "2.0.0",
|
||||||
"description": "An Api on Today's Hot list",
|
"description": "An Api on Today's Hot list",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"API",
|
"API",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "imsyy",
|
"author": "imsyy",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/types.js",
|
"types": "src/types.d.ts",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
||||||
"dev": "tsx watch --no-cache src/index.ts",
|
"dev": "cross-env NODE_ENV=development tsx watch --no-cache src/index.ts",
|
||||||
"dev:cache": "tsx watch src/index.ts",
|
"dev:cache": "cross-env NODE_ENV=development tsx watch src/index.ts",
|
||||||
"build": "tsc --project tsconfig.json",
|
"build": "tsc --project tsconfig.json",
|
||||||
"start": "tsx src/index.ts"
|
"start": "tsx src/index.ts"
|
||||||
},
|
},
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
"@types/node": "^20.14.1",
|
"@types/node": "^20.14.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.12.0",
|
"@typescript-eslint/eslint-plugin": "^7.12.0",
|
||||||
"@typescript-eslint/parser": "^7.12.0",
|
"@typescript-eslint/parser": "^7.12.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"prettier": "^3.3.0",
|
"prettier": "^3.3.0",
|
||||||
"tsx": "^3.14.0",
|
"tsx": "^3.14.0",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -57,6 +57,9 @@ importers:
|
|||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^7.12.0
|
specifier: ^7.12.0
|
||||||
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
version: 7.12.0(eslint@8.57.0)(typescript@5.4.5)
|
||||||
|
cross-env:
|
||||||
|
specifier: ^7.0.3
|
||||||
|
version: 7.0.3
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.57.0
|
specifier: ^8.57.0
|
||||||
version: 8.57.0
|
version: 8.57.0
|
||||||
@@ -518,6 +521,11 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
cross-env@7.0.3:
|
||||||
|
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||||
|
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
cross-spawn@7.0.3:
|
cross-spawn@7.0.3:
|
||||||
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -1808,6 +1816,10 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.4.5
|
typescript: 5.4.5
|
||||||
|
|
||||||
|
cross-env@7.0.3:
|
||||||
|
dependencies:
|
||||||
|
cross-spawn: 7.0.3
|
||||||
|
|
||||||
cross-spawn@7.0.3:
|
cross-spawn@7.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-key: 3.1.1
|
path-key: 3.1.1
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -10,14 +10,16 @@ const serveHotApi = (port: number = config.PORT) => {
|
|||||||
fetch: app.fetch,
|
fetch: app.fetch,
|
||||||
port,
|
port,
|
||||||
});
|
});
|
||||||
logger.info(`🔥 DailyHot API 成功在端口 ${config.PORT} 上运行`);
|
logger.info(`🔥 DailyHot API 成功在端口 ${port} 上运行`);
|
||||||
logger.info(`🔗 Local: 👉 http://localhost:${config.PORT}`);
|
logger.info(`🔗 Local: 👉 http://localhost:${port}`);
|
||||||
return apiServer;
|
return apiServer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
serveHotApi();
|
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "docker") {
|
||||||
|
serveHotApi(config.PORT);
|
||||||
|
}
|
||||||
|
|
||||||
export default serveHotApi;
|
export default serveHotApi;
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|||||||
let allRoutePath: Array<string> = [];
|
let allRoutePath: Array<string> = [];
|
||||||
const routersDirName: string = "routes";
|
const routersDirName: string = "routes";
|
||||||
|
|
||||||
|
// 排除路由
|
||||||
|
const excludeRoutes: Array<string> = ["52pojie", "hostloc"];
|
||||||
|
|
||||||
// 建立完整目录路径
|
// 建立完整目录路径
|
||||||
const routersDirPath = path.join(__dirname, routersDirName);
|
const routersDirPath = path.join(__dirname, routersDirName);
|
||||||
|
|
||||||
@@ -47,6 +50,10 @@ if (fs.existsSync(routersDirPath) && fs.statSync(routersDirPath).isDirectory())
|
|||||||
// 注册全部路由
|
// 注册全部路由
|
||||||
for (let index = 0; index < allRoutePath.length; index++) {
|
for (let index = 0; index < allRoutePath.length; index++) {
|
||||||
const router = allRoutePath[index];
|
const router = allRoutePath[index];
|
||||||
|
// 是否处于排除名单
|
||||||
|
if (excludeRoutes.includes(router)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const listApp = app.basePath(`/${router}`);
|
const listApp = app.basePath(`/${router}`);
|
||||||
// 返回榜单
|
// 返回榜单
|
||||||
listApp.get("/", async (c) => {
|
listApp.get("/", async (c) => {
|
||||||
@@ -85,6 +92,16 @@ for (let index = 0; index < allRoutePath.length; index++) {
|
|||||||
...listData,
|
...listData,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// 请求方式错误
|
||||||
|
listApp.all("*", (c) =>
|
||||||
|
c.json(
|
||||||
|
{
|
||||||
|
code: 405,
|
||||||
|
message: "Method Not Allowed",
|
||||||
|
},
|
||||||
|
405,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取全部路由
|
// 获取全部路由
|
||||||
@@ -93,10 +110,20 @@ app.get("/all", (c) =>
|
|||||||
{
|
{
|
||||||
code: 200,
|
code: 200,
|
||||||
count: allRoutePath.length,
|
count: allRoutePath.length,
|
||||||
routes: allRoutePath.map((path) => ({
|
routes: allRoutePath.map((path) => {
|
||||||
name: path,
|
// 是否处于排除名单
|
||||||
path: `/${path}`,
|
if (excludeRoutes.includes(path)) {
|
||||||
})),
|
return {
|
||||||
|
name: path,
|
||||||
|
path: null,
|
||||||
|
message: "该接口暂时下线",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: path,
|
||||||
|
path: `/${path}`,
|
||||||
|
};
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
200,
|
200,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
const { type } = options;
|
const { type } = options;
|
||||||
const url = `https://www.v2ex.com/api/topics/${type}.json`;
|
const url = `https://www.v2ex.com/api/topics/${type}.json`;
|
||||||
const result = await get({ url, noCache });
|
const result = await get({ url, noCache });
|
||||||
const list = result.data.data.list;
|
const list = result.data;
|
||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ const getList = async (options: Options, noCache: boolean) => {
|
|||||||
const url = `http://www.nmc.cn/rest/findAlarm?pageNo=1&pageSize=20&signaltype=&signallevel=&province=${encodeURIComponent(province)}`;
|
const url = `http://www.nmc.cn/rest/findAlarm?pageNo=1&pageSize=20&signaltype=&signallevel=&province=${encodeURIComponent(province)}`;
|
||||||
const result = await get({ url, noCache });
|
const result = await get({ url, noCache });
|
||||||
const list = result.data.data.page.list;
|
const list = result.data.data.page.list;
|
||||||
console.log(list);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fromCache: result.fromCache,
|
fromCache: result.fromCache,
|
||||||
updateTime: result.updateTime,
|
updateTime: result.updateTime,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,7 +14,7 @@ const getWereadID = (bookId: string) => {
|
|||||||
// 取哈希结果的前三个字符作为初始值
|
// 取哈希结果的前三个字符作为初始值
|
||||||
let strSub = str.substring(0, 3);
|
let strSub = str.substring(0, 3);
|
||||||
// 判断书籍 ID 的类型并进行转换
|
// 判断书籍 ID 的类型并进行转换
|
||||||
let fa;
|
let fa: (string | any[])[];
|
||||||
if (/^\d*$/.test(bookId)) {
|
if (/^\d*$/.test(bookId)) {
|
||||||
// 如果书籍 ID 只包含数字,则将其拆分成长度为 9 的子字符串,并转换为十六进制表示
|
// 如果书籍 ID 只包含数字,则将其拆分成长度为 9 的子字符串,并转换为十六进制表示
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user