From 583f7f4b4f6020d0b386d9461069b6316f68279d Mon Sep 17 00:00:00 2001 From: imsyy Date: Tue, 14 Mar 2023 16:04:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 5 + .gitignore | 30 ++ .hintrc | 13 + README.md | 154 ++++++ app.js | 89 ++++ package.json | 23 + pnpm-lock.yaml | 1131 ++++++++++++++++++++++++++++++++++++++++++++ public/404.html | 195 ++++++++ public/favicon.png | Bin 0 -> 6789 bytes public/favicon.svg | 11 + public/index.html | 201 ++++++++ routes/36kr.js | 129 +++++ routes/baidu.js | 132 ++++++ routes/bilibili.js | 117 +++++ routes/index.js | 37 ++ routes/ithome.js | 150 ++++++ routes/sspai.js | 117 +++++ routes/thepaper.js | 115 +++++ routes/tieba.js | 116 +++++ routes/toutiao.js | 115 +++++ routes/weibo.js | 128 +++++ routes/zhihu.js | 137 ++++++ utils/cacheData.js | 41 ++ 23 files changed, 3186 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 .hintrc create mode 100644 README.md create mode 100644 app.js create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 public/404.html create mode 100644 public/favicon.png create mode 100644 public/favicon.svg create mode 100644 public/index.html create mode 100644 routes/36kr.js create mode 100644 routes/baidu.js create mode 100644 routes/bilibili.js create mode 100644 routes/index.js create mode 100644 routes/ithome.js create mode 100644 routes/sspai.js create mode 100644 routes/thepaper.js create mode 100644 routes/tieba.js create mode 100644 routes/toutiao.js create mode 100644 routes/weibo.js create mode 100644 routes/zhihu.js create mode 100644 utils/cacheData.js diff --git a/.env b/.env new file mode 100644 index 0000000..0fa82a2 --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +# 服务端口 +PORT=6688 + +# 允许的域名 +ALLOWED_DOMAIN = '*' \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f636f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +test.md diff --git a/.hintrc b/.hintrc new file mode 100644 index 0000000..f301c61 --- /dev/null +++ b/.hintrc @@ -0,0 +1,13 @@ +{ + "extends": [ + "development" + ], + "hints": { + "meta-viewport": "off", + "disown-opener": "off", + "button-type": "off", + "axe/aria": "off", + "no-inline-styles": "off", + "axe/text-alternatives": "off" + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f7eab9 --- /dev/null +++ b/README.md @@ -0,0 +1,154 @@ +
+logo +

今日热榜

+

一个聚合热门数据的 API 接口

+
+ +## 示例 + +> 这里是使用该 API 的示例站点 + +- [今日热榜 - https://hot.imsyy.top/](https://hot.imsyy.top/) + +## 总览 + +> 🟢 状态正常 +> 🟠 可能失效 +> 🔴 无法使用 + +| **站点** | **类别** | **调用名称** | **状态** | +|--------|---------|-----------|--------| +| 哔哩哔哩 | 热门榜 | bilibili | 🟢 | +| 知乎 | 热榜 | zhihu | 🟢 | +| 百度 | 热搜榜 | baidu | 🟢 | +| 百度贴吧 | 热议榜 | tieba | 🟢 | +| 少数派 | 热榜 | sspai | 🟢 | +| IT之家 | 热榜 | ithome | 🟠 | +| 澎湃新闻 | 热榜 | thepaper | 🟢 | +| 今日头条 | 热榜 | toutiao | 🟢 | +| 微博热搜 | 热搜榜 | weibo | 🟢 | +| 36氪 | 热榜 | 36kr | 🟢 | +| 腾讯新闻 | 热点榜 | newsqq | 🔴 | + +## 调用 + +### 获取榜单数据 + +> 获取数据只需在域名后面加上上方列表中的调用名称即可 + +```http +GET https://api-hot.imsyy.top/bilibili/ +``` + +
+调用示例 + +```json +{ + "code": 200, + "message": "获取成功", + "title": "哔哩哔哩", // 榜单名称 + "subtitle": "热门榜", // 榜单类别 + "from": "server", // 此处返回是最新数据还是缓存 + "total": 100, // 数据总数 + "updateTime": "2023-03-14T07:40:51.846Z", // 数据获取时间 + "data": [ + { + "id": "BV1E84y1A7z2", + "title": "假如我的校园是一款RPG游戏!", + "desc": "所有取景都是在学校里面拍的,都是真实存在的场景哦!", + "pic": "http://i2.hdslb.com/bfs/archive/a24e442d0aae6d488db023c4ddcb450e9f2bf5f3.jpg", + "owner": { + "mid": 424658638, + "name": "四夕小田木_已黑化_", + "face": "https://i1.hdslb.com/bfs/face/afd9ba47933edc4842ccbeba2891a25465d1cf77.jpg" + }, + "data": { + "aid": 610872610, + "view": 4178745, + "danmaku": 4229, + "reply": 5317, + "favorite": 91020, + "coin": 133596, + "share": 46227, + "now_rank": 0, + "his_rank": 1, + "like": 616519, + "dislike": 0, + "vt": 0, + "vv": 0 + }, + "url": "https://b23.tv/BV1E84y1A7z2", + "mobileUrl": "https://m.bilibili.com/video/BV1E84y1A7z2" + }, + ... + ] +} +``` +
+ +### 获取榜单最新数据 + +> 获取最新数据只需在原链接后面加上 `/new`,这样就会直接从服务端拉取最新数据,不会从本地缓存中读取 + +```http +GET https://api-hot.imsyy.top/bilibili/new +``` + +
+调用示例 + +```json +{ + "code": 200, + "message": "获取成功", + "title": "哔哩哔哩", // 榜单名称 + "subtitle": "热门榜", // 榜单类别 + "total": 100, // 数据总数 + "updateTime": "2023-03-14T07:40:51.846Z", // 数据获取时间 + "data": [ + { + "id": "BV1E84y1A7z2", + "title": "假如我的校园是一款RPG游戏!", + "desc": "所有取景都是在学校里面拍的,都是真实存在的场景哦!", + "pic": "http://i2.hdslb.com/bfs/archive/a24e442d0aae6d488db023c4ddcb450e9f2bf5f3.jpg", + "owner": { + "mid": 424658638, + "name": "四夕小田木_已黑化_", + "face": "https://i1.hdslb.com/bfs/face/afd9ba47933edc4842ccbeba2891a25465d1cf77.jpg" + }, + "data": { + "aid": 610872610, + "view": 4178745, + "danmaku": 4229, + "reply": 5317, + "favorite": 91020, + "coin": 133596, + "share": 46227, + "now_rank": 0, + "his_rank": 1, + "like": 616519, + "dislike": 0, + "vt": 0, + "vv": 0 + }, + "url": "https://b23.tv/BV1E84y1A7z2", + "mobileUrl": "https://m.bilibili.com/video/BV1E84y1A7z2" + }, + ... + ] +} +``` +
+ +## 其他 + +- 本项目为了避免频繁请求官方数据,默认对数据做了缓存处理,默认为 `30` 分钟,如需更改,请自行前往 `utils\cacheData.js` 文件修改 +- 本项目部分接口使用了 **页面爬虫**,若违反对应页面的相关规则,请 **及时通知我去除该接口** + +## 免责声明 + +- 本项目提供的 `API` 仅供开发者进行技术研究和开发测试使用。使用该 `API` 获取的信息仅供参考,不代表本项目对信息的准确性、可靠性、合法性、完整性作出任何承诺或保证。本项目不对任何因使用该 `API` 获取信息而导致的任何直接或间接损失负责。本项目保留随时更改 `API` 接口地址、接口协议、接口参数及其他相关内容的权利。本项目对使用者使用 `API` 的行为不承担任何直接或间接的法律责任 +- 本项目并未与相关信息提供方建立任何关联或合作关系,获取的信息均来自公开渠道,如因使用该 `API` 获取信息而产生的任何法律责任,由使用者自行承担 +- 本项目对使用 `API` 获取的信息进行了最大限度的筛选和整理,但不保证信息的准确性和完整性。使用 `API` 获取信息时,请务必自行核实信息的真实性和可靠性,谨慎处理相关事项 +- 本项目保留对 `API` 的随时更改、停用、限制使用等措施的权利。任何因使用本 `API` 产生的损失,本项目不负担任何赔偿和责任 \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..cc58ce1 --- /dev/null +++ b/app.js @@ -0,0 +1,89 @@ +require("dotenv").config(); +const Koa = require("koa"); +const bodyParser = require("koa-bodyparser"); +const cors = require("koa2-cors"); +const serve = require("koa-static"); +const views = require("koa-views"); + +const app = new Koa(); +const net = require("net"); +const router = require("./routes"); + +// 配置信息 +let domain = process.env.ALLOWED_DOMAIN || "*"; +let port = process.env.PORT || 6688; + +// 解析请求体 +app.use(bodyParser()); + +// 静态文件目录 +app.use(serve(__dirname + "/public")); +app.use(views(__dirname + "/public")); + +// 跨域 +app.use( + cors({ + origin: domain, + }) +); + +app.use(async (ctx, next) => { + if (domain === "*") { + await next(); + } else { + if (ctx.headers.referer !== domain) { + ctx.status = 400; + ctx.body = { + code: 400, + message: "请通过正确的域名访问", + }; + } else { + await next(); + } + } +}); + +// 使用路由中间件 +app.use(router.routes()); +app.use(router.allowedMethods()); + +// 启动应用程序并监听端口 +const startApp = (port) => { + app.listen(port, () => { + console.log(`成功在 ${port} 端口上运行`); + }); +}; + +// 检测端口是否被占用 +const checkPort = (port) => { + return new Promise((resolve, reject) => { + const server = net + .createServer() + .once("error", (err) => { + if (err.code === "EADDRINUSE") { + console.log(`端口 ${port} 已被占用, 正在尝试其他端口...`); + server.close(); + resolve(false); + } else { + reject(err); + } + }) + .once("listening", () => { + server.close(); + resolve(true); + }) + .listen(port); + }); +}; + +// 尝试启动应用程序 +const tryStartApp = async (port) => { + let isPortAvailable = await checkPort(port); + while (!isPortAvailable) { + port++; + isPortAvailable = await checkPort(port); + } + startApp(port); +}; + +tryStartApp(port); diff --git a/package.json b/package.json new file mode 100644 index 0000000..003da87 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "dailyhot_api", + "version": "0.0.1", + "description": "一个今日热榜 .", + "main": "app.js", + "scripts": { + "start": "node app.js" + }, + "author": "imsyy", + "license": "ISC", + "dependencies": { + "axios": "^1.3.4", + "cheerio": "1.0.0-rc.12", + "dotenv": "^16.0.3", + "koa": "^2.14.1", + "koa-bodyparser": "^4.3.0", + "koa-router": "^12.0.0", + "koa-static": "^5.0.0", + "koa-views": "^8.0.0", + "koa2-cors": "^2.0.6", + "node-cache": "^5.1.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..eafd7e0 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1131 @@ +lockfileVersion: 5.4 + +specifiers: + axios: ^1.3.4 + cheerio: 1.0.0-rc.12 + dotenv: ^16.0.3 + koa: ^2.14.1 + koa-bodyparser: ^4.3.0 + koa-router: ^12.0.0 + koa-static: ^5.0.0 + koa-views: ^8.0.0 + koa2-cors: ^2.0.6 + node-cache: ^5.1.2 + +dependencies: + axios: 1.3.4 + cheerio: 1.0.0-rc.12 + dotenv: 16.0.3 + koa: 2.14.1 + koa-bodyparser: 4.3.0 + koa-router: 12.0.0 + koa-static: 5.0.0 + koa-views: 8.0.0 + koa2-cors: 2.0.6 + node-cache: 5.1.2 + +packages: + + /abbrev/1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: false + + /accepts/1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + /any-promise/1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: false + + /asynckit/0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + + /axios/1.3.4: + resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==} + dependencies: + follow-redirects: 1.15.2 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: false + + /bluebird/3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + dev: false + + /boolbase/1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: false + + /brace-expansion/2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: false + + /bytes/3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + dev: false + + /cache-content-type/1.0.1: + resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} + engines: {node: '>= 6.0.0'} + dependencies: + mime-types: 2.1.35 + ylru: 1.3.2 + dev: false + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + dev: false + + /cheerio-select/2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + dev: false + + /cheerio/1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.0.1 + htmlparser2: 8.0.1 + parse5: 7.1.2 + parse5-htmlparser2-tree-adapter: 7.0.0 + dev: false + + /clone/2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + dev: false + + /co-body/6.1.0: + resolution: {integrity: sha512-m7pOT6CdLN7FuXUcpuz/8lfQ/L77x8SchHCF4G0RBTJO20Wzmhn5Sp4/5WsKy8OSpifBSUrmg83qEqaDHdyFuQ==} + dependencies: + inflation: 2.0.0 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + dev: false + + /co/4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: false + + /combined-stream/1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: false + + /condense-newlines/0.2.1: + resolution: {integrity: sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==} + engines: {node: '>=0.10.0'} + dependencies: + extend-shallow: 2.0.1 + is-whitespace: 0.3.0 + kind-of: 3.2.2 + dev: false + + /config-chain/1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: false + + /consolidate/0.16.0: + resolution: {integrity: sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==} + engines: {node: '>= 0.10.0'} + peerDependencies: + arc-templates: ^0.5.3 + atpl: '>=0.7.6' + babel-core: ^6.26.3 + bracket-template: ^1.1.5 + coffee-script: ^1.12.7 + dot: ^1.1.3 + dust: ^0.3.0 + dustjs-helpers: ^1.7.4 + dustjs-linkedin: ^2.7.5 + eco: ^1.1.0-rc-3 + ect: ^0.5.9 + ejs: ^3.1.5 + haml-coffee: ^1.14.1 + hamlet: ^0.3.3 + hamljs: ^0.6.2 + handlebars: ^4.7.6 + hogan.js: ^3.0.2 + htmling: ^0.0.8 + jade: ^1.11.0 + jazz: ^0.0.18 + jqtpl: ~1.1.0 + just: ^0.1.8 + liquid-node: ^3.0.1 + liquor: ^0.0.5 + lodash: ^4.17.20 + marko: ^3.14.4 + mote: ^0.2.0 + mustache: ^4.0.1 + nunjucks: ^3.2.2 + plates: ~0.4.11 + pug: ^3.0.0 + qejs: ^3.0.5 + ractive: ^1.3.12 + razor-tmpl: ^1.3.1 + react: ^16.13.1 + react-dom: ^16.13.1 + slm: ^2.0.0 + squirrelly: ^5.1.0 + swig: ^1.4.2 + swig-templates: ^2.0.3 + teacup: ^2.0.0 + templayed: '>=0.2.3' + then-jade: '*' + then-pug: '*' + tinyliquid: ^0.2.34 + toffee: ^0.3.6 + twig: ^1.15.2 + twing: ^5.0.2 + underscore: ^1.11.0 + vash: ^0.13.0 + velocityjs: ^2.0.1 + walrus: ^0.10.1 + whiskers: ^0.4.0 + peerDependenciesMeta: + arc-templates: + optional: true + atpl: + optional: true + babel-core: + optional: true + bracket-template: + optional: true + coffee-script: + optional: true + dot: + optional: true + dust: + optional: true + dustjs-helpers: + optional: true + dustjs-linkedin: + optional: true + eco: + optional: true + ect: + optional: true + ejs: + optional: true + haml-coffee: + optional: true + hamlet: + optional: true + hamljs: + optional: true + handlebars: + optional: true + hogan.js: + optional: true + htmling: + optional: true + jade: + optional: true + jazz: + optional: true + jqtpl: + optional: true + just: + optional: true + liquid-node: + optional: true + liquor: + optional: true + lodash: + optional: true + marko: + optional: true + mote: + optional: true + mustache: + optional: true + nunjucks: + optional: true + plates: + optional: true + pug: + optional: true + qejs: + optional: true + ractive: + optional: true + razor-tmpl: + optional: true + react: + optional: true + react-dom: + optional: true + slm: + optional: true + squirrelly: + optional: true + swig: + optional: true + swig-templates: + optional: true + teacup: + optional: true + templayed: + optional: true + then-jade: + optional: true + then-pug: + optional: true + tinyliquid: + optional: true + toffee: + optional: true + twig: + optional: true + twing: + optional: true + underscore: + optional: true + vash: + optional: true + velocityjs: + optional: true + walrus: + optional: true + whiskers: + optional: true + dependencies: + bluebird: 3.7.2 + dev: false + + /content-disposition/0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type/1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /cookies/0.8.0: + resolution: {integrity: sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + keygrip: 1.1.0 + dev: false + + /copy-to/2.0.1: + resolution: {integrity: sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==} + dev: false + + /css-select/5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.0.1 + nth-check: 2.1.1 + dev: false + + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: false + + /debug/3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + dev: false + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: false + + /deep-equal/1.0.1: + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + dev: false + + /delayed-stream/1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + + /delegates/1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: false + + /depd/1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + dev: false + + /depd/2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dev: false + + /destroy/1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dev: false + + /dom-serializer/2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.4.0 + dev: false + + /domelementtype/2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: false + + /domhandler/5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: false + + /domutils/3.0.1: + resolution: {integrity: sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dev: false + + /dotenv/16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + dev: false + + /editorconfig/0.15.3: + resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} + hasBin: true + dependencies: + commander: 2.20.3 + lru-cache: 4.1.5 + semver: 5.7.1 + sigmund: 1.0.1 + dev: false + + /ee-first/1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /encodeurl/1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /entities/4.4.0: + resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} + engines: {node: '>=0.12'} + dev: false + + /escape-html/1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + /extend-shallow/2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + dependencies: + is-extendable: 0.1.1 + dev: false + + /follow-redirects/1.15.2: + resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + dev: false + + /form-data/4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /fresh/0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: false + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: false + + /get-intrinsic/1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: false + + /get-paths/0.0.7: + resolution: {integrity: sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA==} + engines: {node: '>=6.4'} + dependencies: + pify: 4.0.1 + dev: false + + /glob/8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: false + + /has-symbols/1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + + /has-tostringtag/1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: false + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: false + + /htmlparser2/8.0.1: + resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.0.1 + entities: 4.4.0 + dev: false + + /http-assert/1.5.0: + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + engines: {node: '>= 0.8'} + dependencies: + deep-equal: 1.0.1 + http-errors: 1.8.1 + dev: false + + /http-errors/1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + dev: false + + /http-errors/1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + dev: false + + /http-errors/2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + dev: false + + /iconv-lite/0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /inflation/2.0.0: + resolution: {integrity: sha512-m3xv4hJYR2oXw4o4Y5l6P5P16WYmazYof+el6Al3f+YlggGj6qT9kImBAnzDelRALnP5d3h4jGBPKzYCizjZZw==} + engines: {node: '>= 0.8.0'} + dev: false + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: false + + /inherits/2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + dev: false + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ini/1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: false + + /is-buffer/1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + dev: false + + /is-extendable/0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + dev: false + + /is-generator-function/1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: false + + /is-whitespace/0.3.0: + resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==} + engines: {node: '>=0.10.0'} + dev: false + + /js-beautify/1.14.7: + resolution: {integrity: sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 0.15.3 + glob: 8.1.0 + nopt: 6.0.0 + dev: false + + /keygrip/1.1.0: + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + engines: {node: '>= 0.6'} + dependencies: + tsscmp: 1.0.6 + dev: false + + /kind-of/3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + dependencies: + is-buffer: 1.1.6 + dev: false + + /koa-bodyparser/4.3.0: + resolution: {integrity: sha512-uyV8G29KAGwZc4q/0WUAjH+Tsmuv9ImfBUF2oZVyZtaeo0husInagyn/JH85xMSxM0hEk/mbCII5ubLDuqW/Rw==} + engines: {node: '>=8.0.0'} + dependencies: + co-body: 6.1.0 + copy-to: 2.0.1 + dev: false + + /koa-compose/4.1.0: + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + dev: false + + /koa-convert/2.0.0: + resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} + engines: {node: '>= 10'} + dependencies: + co: 4.6.0 + koa-compose: 4.1.0 + dev: false + + /koa-router/12.0.0: + resolution: {integrity: sha512-zGrdiXygGYW8WvrzeGsHZvKnHs4DzyGoqJ9a8iHlRkiwuEAOAPyI27//OlhoWdgFAEIM3qbUgr0KCuRaP/TCag==} + engines: {node: '>= 12'} + dependencies: + http-errors: 2.0.0 + koa-compose: 4.1.0 + methods: 1.1.2 + path-to-regexp: 6.2.1 + dev: false + + /koa-send/5.0.1: + resolution: {integrity: sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==} + engines: {node: '>= 8'} + dependencies: + debug: 4.3.4 + http-errors: 1.8.1 + resolve-path: 1.4.0 + transitivePeerDependencies: + - supports-color + dev: false + + /koa-static/5.0.0: + resolution: {integrity: sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==} + engines: {node: '>= 7.6.0'} + dependencies: + debug: 3.2.7 + koa-send: 5.0.1 + transitivePeerDependencies: + - supports-color + dev: false + + /koa-views/8.0.0: + resolution: {integrity: sha512-nvGKdG8yVyomhouwN060SWd7Q9kGYRr322dwN6jvI04hgiDEW9vsPXEje9OEtBgGXxHURe7xOzo3bxQ3DBc1Nw==} + peerDependencies: + '@types/koa': ^2.13.1 + peerDependenciesMeta: + '@types/koa': + optional: true + dependencies: + consolidate: 0.16.0 + debug: 4.3.4 + get-paths: 0.0.7 + koa-send: 5.0.1 + mz: 2.7.0 + pretty: 2.0.0 + resolve-path: 1.4.0 + transitivePeerDependencies: + - arc-templates + - atpl + - babel-core + - bracket-template + - coffee-script + - dot + - dust + - dustjs-helpers + - dustjs-linkedin + - eco + - ect + - ejs + - haml-coffee + - hamlet + - hamljs + - handlebars + - hogan.js + - htmling + - jade + - jazz + - jqtpl + - just + - liquid-node + - liquor + - lodash + - marko + - mote + - mustache + - nunjucks + - plates + - pug + - qejs + - ractive + - razor-tmpl + - react + - react-dom + - slm + - squirrelly + - supports-color + - swig + - swig-templates + - teacup + - templayed + - then-jade + - then-pug + - tinyliquid + - toffee + - twig + - twing + - underscore + - vash + - velocityjs + - walrus + - whiskers + dev: false + + /koa/2.14.1: + resolution: {integrity: sha512-USJFyZgi2l0wDgqkfD27gL4YGno7TfUkcmOe6UOLFOVuN+J7FwnNu4Dydl4CUQzraM1lBAiGed0M9OVJoT0Kqw==} + engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} + dependencies: + accepts: 1.3.8 + cache-content-type: 1.0.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookies: 0.8.0 + debug: 4.3.4 + delegates: 1.0.0 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + fresh: 0.5.2 + http-assert: 1.5.0 + http-errors: 1.8.1 + is-generator-function: 1.0.10 + koa-compose: 4.1.0 + koa-convert: 2.0.0 + on-finished: 2.4.1 + only: 0.0.2 + parseurl: 1.3.3 + statuses: 1.5.0 + type-is: 1.6.18 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /koa2-cors/2.0.6: + resolution: {integrity: sha512-JRCcSM4lamM+8kvKGDKlesYk2ASrmSTczDtGUnIadqMgnHU4Ct5Gw7Bxt3w3m6d6dy3WN0PU4oMP43HbddDEWg==} + engines: {node: '>= 7.6.0'} + dev: false + + /lru-cache/4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: false + + /media-typer/0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + /methods/1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /mime-db/1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types/2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /minimatch/5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: false + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + /mz/2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: false + + /negotiator/0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + /node-cache/5.1.2: + resolution: {integrity: sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==} + engines: {node: '>= 8.0.0'} + dependencies: + clone: 2.1.2 + dev: false + + /nopt/6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + + /nth-check/2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: false + + /object-assign/4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + + /object-inspect/1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: false + + /on-finished/2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + + /only/0.0.2: + resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} + dev: false + + /parse5-htmlparser2-tree-adapter/7.0.0: + resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} + dependencies: + domhandler: 5.0.3 + parse5: 7.1.2 + dev: false + + /parse5/7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.4.0 + dev: false + + /parseurl/1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: false + + /path-to-regexp/6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: false + + /pify/4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: false + + /pretty/2.0.0: + resolution: {integrity: sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==} + engines: {node: '>=0.10.0'} + dependencies: + condense-newlines: 0.2.1 + extend-shallow: 2.0.1 + js-beautify: 1.14.7 + dev: false + + /proto-list/1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: false + + /proxy-from-env/1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + + /pseudomap/1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: false + + /qs/6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + + /raw-body/2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /resolve-path/1.4.0: + resolution: {integrity: sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==} + engines: {node: '>= 0.8'} + dependencies: + http-errors: 1.6.3 + path-is-absolute: 1.0.1 + dev: false + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: false + + /setprototypeof/1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + dev: false + + /setprototypeof/1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + dev: false + + /sigmund/1.0.1: + resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} + dev: false + + /statuses/1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + dev: false + + /statuses/2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + dev: false + + /thenify-all/1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: false + + /thenify/3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: false + + /toidentifier/1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + /tsscmp/1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + dev: false + + /type-is/1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + /unpipe/1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + /vary/1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + + /yallist/2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: false + + /ylru/1.3.2: + resolution: {integrity: sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==} + engines: {node: '>= 4.0.0'} + dev: false diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..73f0bc8 --- /dev/null +++ b/public/404.html @@ -0,0 +1,195 @@ + + + + + 404 | Linkbook API + + + + + +
+
+
404 Not Found
+
请检查您的路径
+
+ +
+
+ + + + diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..e74ac2ffccd3cc5093329dc0a83afa2ee0012603 GIT binary patch literal 6789 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

sZ0>j?@VXJbmaIyMWUR!|#wSIztII6Hda z*x-AR%fdP_ndOj6m|+OXmT0KIGHRN3H&rn$VmFU$!73OLyB5RG?b!xPqSemP(6pR@ z5cz`#rwBi(p^>smQbw}hBGsg(PCCK&()DMKILqsiJk+N}i3}^Qk&*XN(_h&9!{%i- zmgBa5eG@BU4Dn%4NWHSO>mn82@M$whtqMg>j|SgQ$7Ob47v%6j<_+!nn0(aXG~0X? z(QNQGOqpCKGkZ*d{Hr(luhod{$@^pr&=dC0fu9rkYDf&N? zjtQzbwVwB5od0s|B>;{Dj0COsY3SX-+Y8O)EiR@cft_xKmym%=lE85870*CL+Od=G z!>)ZOSxH~g$)njqqlMhrs`I&OC|>F%v1Ry@h3zgGV|Ifn=Xd6wgx3ZWLSx24E~5!P z2OZ4h>1urSYiBF+FD~pVAcYpr;fBS(#Z8{jhXgZBr_CR8%b$xiDrK-AL4gS z<9X0yy?ZPZin}qd2SE=oT!E@K|52e)e<|1b0Z&tQ@4BWcNd1qXB@Cu0mXDJKuBz1D z&G}0dK1TujZ~c36!L8wa1U2Fly2MVW2}Anh6iXgtnVAKTXoXPWmwW}RRyn)s;e9v?gHnRDG_Vm71GjXcIBA;RDMT9Ffeb{! z>LJQ|muErG24$rF%Fu}o?X|xLE8-HjyJGWT=k;w~d2f!3Qo>xkSSJKn->$&$xr7_|z0i3r@3-PBPc9ssRL#iyy{sFd13fzdt zlg<4~7nvv}PK}U?6D@$0FaKTRMmi~ZiY$a?;-P8FlHE5dFi&rT6&UImB%sZnSY-jL z%SyWyLdGbKsCcVUFA0fye+20GQRI~H3dvqalRm^cJ$ZOmcQ-Z?_9$iU6arVEZ+Wj) zfUx2IZ!_fyN8dAcc8z?F^H>qP3)cq_Xd?UP*kK*5Vlei=CHq;&pbyiPvqOQ>kHbGa zWQlpOlY9XZa2Az%tATjttEF4v*~QcTMQR*0sA=S(41inR74zyfDR?8*u)b|2IT`kS ztFNJ8NXKK`;&@6W!2(i}#I5+zAjsukQpvsenUq1p8cU+ftiHQr@|lu4{__;xnEL2o zRt3hU6mLjtl$u9#cs4gMpZv#{YO?)s>6t?%gSd_S?qcP-qZ!GFvSI@}i7lmk$Y*bI zQhTC74dGX*m*gLHdgRPb$&SalIjS?SC3mTsr^EQvVHEF*t(jq3iA{>Y^A%9@*;-&; z_SLBVkrHA<j=pFMGbZgp&B0%3j0#mrE<8yk34Cex#<}onBOs74e@h4A^vTTDg z-i%7FAh8nr?F=)YH2`qs;*vrsw5z^S0@flfh7!#$wG(00eYUs@iZJIjqT%UH(r`sq zNbn4uVc{@kafdj?nUao<1nRt!^_y*fWGrC9!e!noePvb5w&J^qaVbfItE=7O3aBxG z=kk*9eH!WAa0gh&sGfGOelrA-MFVzHO~du^Q`C0Ege6jZV+!|$bxb&yI~>gPn<(8! z8I@V$MGsL)4g9y}_g$5loGkZnhb3+2*513U}lBxT;NS}4z zje2kImFMphsbr3FVb}CN;x@oqfNc1~YNW;OK;o_dI9b&Dy?;&)$ux_3`T~_bgF*kR z7FTQV1}X41n#}ecpV^&%rnf&uot4nkLMxX9*vVCX@zleJ?-|Nbg_&NeE^)ZgfM=NF zb2imu<4@rA6xgC#`u!^8o34^ z8%_x)JmBQG`m*%@_3kGZoYtms&|;>h`IgH!nwU!FlI>rUFtTjlBkkAO2lHp7=dI!F;a zFqrL)W9>%;@DT{$j@lvv;KoaE3XWG7GggGze-|m>;Mh6|quO`>*MT84aEG6P>SV&M z0WJfgHKzhBNZl)T!RsF@_W{A+jTx93;c5u{hIaTS+_k5A@gxb7+uyi#LoASG8)k>D S%Eb4T07z9`l^P`*)PDiN)u&Vd literal 0 HcmV?d00001 diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..c0df2a1 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,11 @@ + + favicon-svg + + + + + + + \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..ac93312 --- /dev/null +++ b/public/index.html @@ -0,0 +1,201 @@ + + + + + DailyHot API + + + + + +

+
+
HotDaily API
+
服务已正常运行
+
+ + +
+
+ + + + diff --git a/routes/36kr.js b/routes/36kr.js new file mode 100644 index 0000000..f6e879f --- /dev/null +++ b/routes/36kr.js @@ -0,0 +1,129 @@ +const Router = require("koa-router"); +const krRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "krData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://gateway.36kr.com/api/mis/nav/home/nav/rank/hot"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + return data.map((v) => { + return { + id: v.itemId, + title: v.templateMaterial.widgetTitle, + pic: v.templateMaterial.widgetImage, + owner: v.templateMaterial.authorName, + hot: v.templateMaterial.statRead, + data: v.templateMaterial, + url: `https://www.36kr.com/p/${v.itemId}`, + mobileUrl: `https://www.36kr.com/p/${v.itemId}`, + }; + }); +}; + +// 36氪热榜 +krRouter.get("/36kr", async (ctx) => { + console.log("获取36氪热榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取36氪热榜"); + // 从服务器拉取数据 + const response = await axios.post(url, { + partner_id: "wap", + param: { + siteId: 1, + platformId: 2, + }, + }); + data = getData(response.data.data.hotRankList); + updateTime = new Date().toISOString(); + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "36氪", + subtitle: "热榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 36氪热榜 - 获取最新数据 +krRouter.get("/36kr/new", async (ctx) => { + console.log("获取36氪热榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.post(url, { + partner_id: "wap", + param: { + siteId: 1, + platformId: 2, + }, + }); + const newData = getData(response.data.data.hotRankList); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取36氪热榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "36氪", + subtitle: "热榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "36氪", + subtitle: "热榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = krRouter; diff --git a/routes/baidu.js b/routes/baidu.js new file mode 100644 index 0000000..fe87f9f --- /dev/null +++ b/routes/baidu.js @@ -0,0 +1,132 @@ +const Router = require("koa-router"); +const baiduRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "baiduData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://top.baidu.com/board?tab=realtime"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + const dataList = []; + try { + const pattern = /<\!--s-data:(.*?)-->/s; + const matchResult = data.match(pattern); + const jsonObject = JSON.parse(matchResult[1]).cards[0].content; + jsonObject.forEach((v) => { + dataList.push({ + title: v.query, + desc: v.desc, + pic: v.img, + hot: v.hotScore, + url: `https://www.baidu.com/s?wd=${encodeURIComponent(v.query)}`, + mobileUrl: v.url, + }); + }); + return dataList; + } catch (error) { + console.error("数据处理出错" + error); + return false; + } +}; + +// 百度热搜 +baiduRouter.get("/baidu", async (ctx) => { + console.log("获取百度热搜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取百度热搜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data); + updateTime = new Date().toISOString(); + if (!data) { + ctx.body = { + code: 500, + message: "获取失败", + }; + return false; + } + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "百度", + subtitle: "热搜榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 百度热搜 - 获取最新数据 +baiduRouter.get("/baidu/new", async (ctx) => { + console.log("获取百度热搜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取百度热搜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "百度", + subtitle: "热搜榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "百度", + subtitle: "热搜榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = baiduRouter; diff --git a/routes/bilibili.js b/routes/bilibili.js new file mode 100644 index 0000000..85e2ff1 --- /dev/null +++ b/routes/bilibili.js @@ -0,0 +1,117 @@ +const Router = require("koa-router"); +const bilibiliRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "bilibiliData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://api.bilibili.com/x/web-interface/ranking/v2"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + return data.map((v) => { + return { + id: v.bvid, + title: v.title, + desc: v.desc, + pic: v.pic, + owner: v.owner, + data: v.stat, + url: v.short_link, + mobileUrl: `https://m.bilibili.com/video/${v.bvid}`, + }; + }); +}; + +// 哔哩哔哩热门榜 +bilibiliRouter.get("/bilibili", async (ctx) => { + console.log("获取哔哩哔哩热门榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取哔哩哔哩热门榜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data.data.list); + updateTime = new Date().toISOString(); + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "哔哩哔哩", + subtitle: "热门榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "哔哩哔哩热门榜获取失败", + }; + } +}); + +// 哔哩哔哩热门榜 - 获取最新数据 +bilibiliRouter.get("/bilibili/new", async (ctx) => { + console.log("获取哔哩哔哩热门榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data.data.list); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取哔哩哔哩热门榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "哔哩哔哩", + subtitle: "热门榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "哔哩哔哩", + subtitle: "热门榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "哔哩哔哩热门榜获取失败", + }; + } + } +}); + +module.exports = bilibiliRouter; diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..6949a46 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,37 @@ +const Router = require("koa-router"); + +const bilibiliRouter = require("./bilibili"); +const zhihuRouter = require("./zhihu"); +const baiduRouter = require("./baidu"); +const weiboRouter = require("./weibo"); +const itHomeRouter = require("./ithome"); +const krRouter = require("./36kr"); +const sspaiRouter = require("./sspai"); +const tiebaRouter = require("./tieba"); +const toutiaoRouter = require("./toutiao"); +const thepaperRouter = require("./thepaper"); + +const router = new Router(); + +// 根目录 +router.get("/", async (ctx) => { + await ctx.render("index"); +}); + +router.use(bilibiliRouter.routes()); +router.use(zhihuRouter.routes()); +router.use(baiduRouter.routes()); +router.use(weiboRouter.routes()); +router.use(itHomeRouter.routes()); +router.use(krRouter.routes()); +router.use(sspaiRouter.routes()); +router.use(tiebaRouter.routes()); +router.use(toutiaoRouter.routes()); +router.use(thepaperRouter.routes()); + +// 404 路由 +router.use(async (ctx) => { + await ctx.render("404"); +}); + +module.exports = router; diff --git a/routes/ithome.js b/routes/ithome.js new file mode 100644 index 0000000..3c1d7b1 --- /dev/null +++ b/routes/ithome.js @@ -0,0 +1,150 @@ +const Router = require("koa-router"); +const itHomeRouter = new Router(); +const axios = require("axios"); +const cheerio = require("cheerio"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "itHomeData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://m.ithome.com/rankm/"; +const headers = { + "User-Agent": + "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1", +}; + +// it之家特殊处理 - url +const replaceLink = (url) => { + const match = url.match(/html\/(\d+)\.htm/)[1]; + return `https://www.ithome.com/0/${match.slice(0, 3)}/${match.slice(3)}.htm`; +}; + +// 数据处理 +const getData = (data) => { + if (!data) return false; + const dataList = {}; + const $ = cheerio.load(data); + try { + $(".rank-name").each(function () { + const type = $(this).data("rank-type"); + const newListHtml = $(this).next(".rank-box").html(); + const newsList = cheerio + .load(newListHtml)(".placeholder") + .get() + .map((v) => { + return { + title: $(v).find(".plc-title").text(), + img: $(v).find("img").attr("data-original"), + time: $(v).find(".post-time").text(), + hot: $(v).find(".review-num").text(), + url: replaceLink($(v).find("a").attr("href")), + mobileUrl: $(v).find("a").attr("href"), + }; + }); + dataList[type] = { + name: $(this).text(), + total: newsList.length, + list: newsList, + }; + }); + return dataList; + } catch (error) { + console.error("数据处理出错" + error); + return false; + } +}; + +// IT之家热榜 +itHomeRouter.get("/ithome", async (ctx) => { + console.log("获取IT之家热榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取IT之家热榜"); + // 从服务器拉取数据 + const response = await axios.get(url, { headers }); + data = getData(response.data); + updateTime = new Date().toISOString(); + if (!data) { + ctx.body = { + code: 500, + message: "获取失败", + }; + return false; + } + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "IT之家", + subtitle: "热榜", + from, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// IT之家热榜 - 获取最新数据 +itHomeRouter.get("/ithome/new", async (ctx) => { + console.log("获取IT之家热榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url, { headers }); + const newData = getData(response.data); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取IT之家热榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "IT之家", + subtitle: "热榜", + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "IT之家", + subtitle: "热榜", + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = itHomeRouter; diff --git a/routes/sspai.js b/routes/sspai.js new file mode 100644 index 0000000..835453c --- /dev/null +++ b/routes/sspai.js @@ -0,0 +1,117 @@ +const Router = require("koa-router"); +const sspaiRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "krData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = `https://sspai.com/api/v1/article/tag/page/get?limit=40&tag=热门文章`; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + return data.map((v) => { + return { + id: v.id, + title: v.title, + desc: v.summary, + pic: `https://cdn.sspai.com/${v.banner}`, + owner: v.author, + hot: v.like_count, + url: `https://sspai.com/post/${v.id}`, + mobileUrl: `https://sspai.com/post/${v.itemId}`, + }; + }); +}; + +// 少数派热榜 +sspaiRouter.get("/sspai", async (ctx) => { + console.log("获取少数派热榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取少数派热榜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data.data); + updateTime = new Date().toISOString(); + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "少数派", + subtitle: "最热", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 少数派热榜 - 获取最新数据 +sspaiRouter.get("/sspai/new", async (ctx) => { + console.log("获取少数派热榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data.data); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取少数派热榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "少数派", + subtitle: "最热", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "少数派", + subtitle: "最热", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = sspaiRouter; diff --git a/routes/thepaper.js b/routes/thepaper.js new file mode 100644 index 0000000..2092aed --- /dev/null +++ b/routes/thepaper.js @@ -0,0 +1,115 @@ +const Router = require("koa-router"); +const thepaperRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "thepaperData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://cache.thepaper.cn/contentapi/wwwIndex/rightSidebar"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + return data.map((v) => { + return { + id: v.contId, + title: v.name, + pic: v.pic, + time: v.pubTime, + url: `https://www.thepaper.cn/newsDetail_forward_${v.contId}`, + mobileUrl: `https://m.thepaper.cn/newsDetail_forward_${v.contId}`, + }; + }); +}; + +// 澎湃热榜 +thepaperRouter.get("/thepaper", async (ctx) => { + console.log("获取澎湃热榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取澎湃热榜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data.data.hotNews); + updateTime = new Date().toISOString(); + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "澎湃新闻", + subtitle: "热榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 澎湃热榜 - 获取最新数据 +thepaperRouter.get("/thepaper/new", async (ctx) => { + console.log("获取澎湃热榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data.data.hotNews); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取澎湃热榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "澎湃新闻", + subtitle: "热榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "澎湃新闻", + subtitle: "热榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = thepaperRouter; diff --git a/routes/tieba.js b/routes/tieba.js new file mode 100644 index 0000000..52f2235 --- /dev/null +++ b/routes/tieba.js @@ -0,0 +1,116 @@ +const Router = require("koa-router"); +const tiebaRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "tiebaData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://tieba.baidu.com/hottopic/browse/topicList"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + return data.map((v) => { + return { + id: v.topic_id, + title: v.topic_name, + desc: v.topic_desc, + pic: v.topic_pic, + hot: v.discuss_num, + url: v.topic_url, + mobileUrl: v.topic_url, + }; + }); +}; + +// 贴吧热议榜 +tiebaRouter.get("/tieba", async (ctx) => { + console.log("获取贴吧热议榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取贴吧热议榜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data.data.bang_topic.topic_list); + updateTime = new Date().toISOString(); + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "百度贴吧", + subtitle: "热议榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 贴吧热议榜 - 获取最新数据 +tiebaRouter.get("/tieba/new", async (ctx) => { + console.log("获取贴吧热议榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data.data.bang_topic.topic_list); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取贴吧热议榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "百度贴吧", + subtitle: "热议榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "百度贴吧", + subtitle: "热议榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = tiebaRouter; diff --git a/routes/toutiao.js b/routes/toutiao.js new file mode 100644 index 0000000..657136a --- /dev/null +++ b/routes/toutiao.js @@ -0,0 +1,115 @@ +const Router = require("koa-router"); +const toutiaoRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "toutiaoData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + return data.map((v) => { + return { + id: v.ClusterId, + title: v.Title, + pic: v.Image.url, + hot: v.HotValue, + url: `https://www.toutiao.com/trending/${v.ClusterIdStr}/`, + mobileUrl: `https://api.toutiaoapi.com/feoffline/amos_land/new/html/main/index.html?topic_id=${v.ClusterIdStr}`, + }; + }); +}; + +// 头条热榜 +toutiaoRouter.get("/toutiao", async (ctx) => { + console.log("获取头条热榜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取头条热榜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data.data); + updateTime = new Date().toISOString(); + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "今日头条", + subtitle: "热榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 头条热榜 - 获取最新数据 +toutiaoRouter.get("/toutiao/new", async (ctx) => { + console.log("获取头条热榜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data.data); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取头条热榜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "今日头条", + subtitle: "热榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "今日头条", + subtitle: "热榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = toutiaoRouter; diff --git a/routes/weibo.js b/routes/weibo.js new file mode 100644 index 0000000..50b0ba3 --- /dev/null +++ b/routes/weibo.js @@ -0,0 +1,128 @@ +const Router = require("koa-router"); +const weiboRouter = new Router(); +const axios = require("axios"); +// const cheerio = require("cheerio"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "weiboData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://weibo.com/ajax/side/hotSearch"; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + // return data; + return data.map((v) => { + const key = v.word_scheme ? v.word_scheme : `#${v.word}`; + return { + title: v.word, + desc: key, + hot: v.raw_hot, + url: `https://s.weibo.com/weibo?q=${encodeURIComponent( + key + )}&t=31&band_rank=1&Refer=top`, + mobileUrl: `https://s.weibo.com/weibo?q=${encodeURIComponent( + key + )}&t=31&band_rank=1&Refer=top`, + }; + }); +}; + +// 微博热搜 +weiboRouter.get("/weibo", async (ctx) => { + console.log("获取微博热搜"); + try { + // 从缓存中获取数据 + let data = await get(cacheKey); + const from = data ? "cache" : "server"; + if (!data) { + // 如果缓存中不存在数据 + console.log("从服务端重新获取微博热搜"); + // 从服务器拉取数据 + const response = await axios.get(url); + data = getData(response.data.data.realtime); + updateTime = new Date().toISOString(); + if (!data) { + ctx.body = { + code: 500, + message: "获取失败", + }; + return false; + } + // 将数据写入缓存 + await set(cacheKey, data); + } + ctx.body = { + code: 200, + message: "获取成功", + title: "微博", + subtitle: "热搜榜", + from, + total: data.length, + updateTime, + data, + }; + } catch (error) { + console.error(error); + ctx.body = { + code: 500, + message: "获取失败", + }; + } +}); + +// 微博热搜 - 获取最新数据 +weiboRouter.get("/weibo/new", async (ctx) => { + console.log("获取微博热搜 - 最新数据"); + try { + // 从服务器拉取最新数据 + const response = await axios.get(url); + const newData = getData(response.data.data.realtime); + updateTime = new Date().toISOString(); + console.log("从服务端重新获取微博热搜"); + + // 返回最新数据 + ctx.body = { + code: 200, + message: "获取成功", + title: "微博", + subtitle: "热搜榜", + total: newData.length, + updateTime, + data: newData, + }; + + // 删除旧数据 + await del(cacheKey); + // 将最新数据写入缓存 + await set(cacheKey, newData); + } catch (error) { + // 如果拉取最新数据失败,尝试从缓存中获取数据 + console.error(error); + const cachedData = await get(cacheKey); + if (cachedData) { + ctx.body = { + code: 200, + message: "获取成功", + title: "微博", + subtitle: "热搜榜", + total: cachedData.length, + updateTime, + data: cachedData, + }; + } else { + // 如果缓存中也没有数据,则返回错误信息 + ctx.body = { + code: 500, + message: "获取失败", + }; + } + } +}); + +module.exports = weiboRouter; diff --git a/routes/zhihu.js b/routes/zhihu.js new file mode 100644 index 0000000..5f6398a --- /dev/null +++ b/routes/zhihu.js @@ -0,0 +1,137 @@ +const Router = require("koa-router"); +const zhihuRouter = new Router(); +const axios = require("axios"); +const { get, set, del } = require("../utils/cacheData"); + +// 缓存键名 +const cacheKey = "zhihuData"; + +// 调用时间 +let updateTime = new Date().toISOString(); + +// 调用路径 +const url = "https://www.zhihu.com/hot"; +const headers = { + "User-Agent": + "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1", +}; + +// 数据处理 +const getData = (data) => { + if (!data) return []; + const dataList = []; + try { + const pattern = + /