5 Commits

Author SHA1 Message Date
imsyy
bcff976a4d feat: 新增 CSDN 2024-06-17 14:06:22 +08:00
imsyy
62a8880ae4 feat: 输出美化 2024-06-13 17:51:44 +08:00
imsyy
e6f89c4868 🔧 build: 依赖更新 2024-06-13 16:53:59 +08:00
imsyy
fa80a29772 feat: 新增 历史上的今天 2024-06-13 14:09:16 +08:00
imsyy
b8f7c1ad23 🐞 fix: 修复错误包含 ts 文件 2024-06-13 10:12:06 +08:00
41 changed files with 357 additions and 961 deletions

View File

@@ -6,8 +6,7 @@ on:
jobs:
publish-npm:
runs-on:
ubuntu-latest
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20]

View File

@@ -49,6 +49,7 @@
| 今日头条 | 热榜 | toutiao | 🟢 |
| 36 氪 | 热榜 | 36kr | 🟢 |
| 51CTO | 推荐榜 | 51cto | 🟢 |
| CSDN | 排行榜 | csdn | 🟢 |
| 稀土掘金 | 热榜 | juejin | 🟢 |
| 腾讯新闻 | 热点榜 | qq-news | 🟢 |
| 网易新闻 | 热点榜 | netease-news | 🟢 |
@@ -66,6 +67,7 @@
| HelloGitHub | Trending | hellogithub | 🟢 |
| 中央气象台 | 全国气象预警 | weatheralarm | 🟢 |
| 中国地震台 | 地震速报 | earthquake | 🟢 |
| 历史上的今天 | 月-日 | history | 🟢 |
## ⚙️ 使用

View File

@@ -1,6 +1,6 @@
{
"name": "dailyhot-api",
"version": "2.0.1",
"version": "2.0.2",
"description": "An Api on Today's Hot list",
"keywords": [
"API",
@@ -17,7 +17,6 @@
"license": "MIT",
"author": "imsyy",
"main": "dist/index.js",
"types": "src/types.d.ts",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
@@ -25,7 +24,6 @@
"LICENSE",
"README.md",
"dist/**/*",
"src/types.d.ts",
"!dist/logs/**/*"
],
"scripts": {
@@ -40,25 +38,24 @@
"dependencies": {
"@hono/node-server": "^1.11.2",
"axios": "^1.7.2",
"chalk": "^5.3.0",
"cheerio": "1.0.0-rc.12",
"dayjs": "^1.11.11",
"dotenv": "^16.4.5",
"feed": "^4.2.2",
"hono": "^4.4.3",
"hono": "^4.4.5",
"md5": "^2.3.0",
"node-cache": "^5.1.2",
"puppeteer": "^22.10.0",
"puppeteer-cluster": "^0.24.0",
"rss-parser": "^3.13.0",
"winston": "^3.13.0"
},
"devDependencies": {
"@types/node": "^20.14.1",
"@typescript-eslint/eslint-plugin": "^7.12.0",
"@typescript-eslint/parser": "^7.12.0",
"@types/node": "^20.14.2",
"@typescript-eslint/eslint-plugin": "^7.13.0",
"@typescript-eslint/parser": "^7.13.0",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"prettier": "^3.3.0",
"prettier": "^3.3.2",
"tsx": "^3.14.0",
"typescript": "^5.4.5"
},

935
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,11 @@ const findTsFiles = (dirPath: string, allFiles: string[] = [], basePath: string
if (stat.isDirectory()) {
// 如果是文件夹,递归查找
findTsFiles(fullPath, allFiles, relativePath);
} else if (stat.isFile() && (item.endsWith(".ts") || item.endsWith(".js"))) {
} else if (
stat.isFile() &&
(item.endsWith(".ts") || item.endsWith(".js")) &&
!item.endsWith(".d.ts")
) {
// 符合条件
allFiles.push(relativePath.replace(/\.(ts|js)$/, ""));
}

View File

@@ -260,4 +260,13 @@ export type RouterType = {
comment_count: number;
created_at: number;
};
csdn: {
nickName: string;
articleTitle: string;
articleDetailUrl: string;
picList: [string];
hotRankScore: string;
period: string;
productId: string;
};
};

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { post } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "hot";
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "36kr",
title: "36氪",
type: "热榜",
parameData: {
parame: {
type: {
name: "热榜分类",
type: {

View File

@@ -2,7 +2,7 @@ import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { getToken, sign } from "../utils/getToken/51cto.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -2,7 +2,7 @@ import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { web } from "../utils/getData.js";
import { extractRss, parseRSS } from "../utils/parseRSS.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "hot";
@@ -11,7 +11,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "52pojie",
title: "吾爱破解",
type: "榜单",
parameData: {
parame: {
type: {
name: "榜单分类",
type: {

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "-1";
@@ -12,7 +12,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
title: "AcFun",
type: "排行榜",
description: "AcFun是一家弹幕视频网站致力于为每一个人带来欢乐。",
parameData: {
parame: {
type: {
name: "频道",
type: {

View File

@@ -9,7 +9,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "baidu",
title: "百度",
type: "热搜榜",
parameData: {
parame: {
type: {
name: "热搜类别",
type: {

View File

@@ -2,7 +2,7 @@ import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getBiliWbi from "../utils/getToken/bilibili.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "0";
@@ -12,7 +12,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
title: "哔哩哔哩",
type: "热门榜",
description: "你所热爱的,就是你的生活",
parameData: {
parame: {
type: {
name: "排行榜分区",
type: {

41
src/routes/csdn.ts Normal file
View File

@@ -0,0 +1,41 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);
const routeData: RouterData = {
name: "csdn",
title: "CSDN",
type: "排行榜",
description: "专业开发者社区",
link: "https://www.csdn.net/",
total: data?.length || 0,
updateTime,
fromCache,
data,
};
return routeData;
};
const getList = async (noCache: boolean) => {
const url = "https://blog.csdn.net/phoenix/web/blog/hot-rank?page=0&pageSize=30";
const result = await get({ url, noCache });
const list = result.data.data;
return {
fromCache: result.fromCache,
updateTime: result.updateTime,
data: list.map((v: RouterType["csdn"]) => ({
id: v.productId,
title: v.articleTitle,
cover: v.picList?.[0] || null,
desc: null,
author: v.nickName,
timestamp: getTime(v.period),
hot: Number(v.hotRankScore),
url: v.articleDetailUrl,
mobileUrl: v.articleDetailUrl,
})),
};
};

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
const mappings = {
O_TIME: "发震时刻(UTC+8)",
@@ -33,7 +33,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "earthquake",
title: "中国地震台",
type: "地震速报",
parameData: {
parame: {
type: {
name: "速报分类",
type: {

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "1";
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "genshin",
title: "原神",
type: "最新动态",
parameData: {
parame: {
type: {
name: "榜单分类",
type: {

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const sort = c.req.query("sort") || "featured";
@@ -11,7 +11,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
title: "HelloGitHub",
type: "热门仓库",
description: "分享 GitHub 上有趣、入门级的开源项目",
parameData: {
parame: {
sort: {
name: "排行榜分区",
type: {

51
src/routes/history.ts Normal file
View File

@@ -0,0 +1,51 @@
import type { RouterData, ListContext, Options } from "../types.js";
import { load } from "cheerio";
import { get } from "../utils/getData.js";
import { getCurrentDateTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
// 获取日期
const day = c.req.query("day") || getCurrentDateTime().day;
const month = c.req.query("month") || getCurrentDateTime().month;
const { fromCache, data, updateTime } = await getList({ month, day }, noCache);
const routeData: RouterData = {
name: "history",
title: "历史上的今天",
type: `${month}-${day}`,
parame: {
month: "月份",
day: "日期",
},
link: "https://www.lssjt.com/",
total: data?.length || 0,
updateTime,
fromCache,
data,
};
return routeData;
};
const getList = async (options: Options, noCache: boolean) => {
const { month, day } = options;
const url = `https://www.lssjt.com/${month}/${day}/`;
const result = await get({ url, noCache });
const $ = load(result.data);
const listDom = $("li.circler");
const listData = listDom.toArray().map((item, index) => {
const dom = $(item);
const href = dom.find("a").attr("href");
return {
id: index,
title: dom.find("a.txt").text().trim() || dom.find("a").attr("title"),
cover: dom.find("img").attr("data-original"),
timestamp: dom.find("div.text span").text().trim() || dom.find("div.t span").text().trim(),
hot: null,
url: href || undefined,
mobileUrl: href || undefined,
};
});
return {
fromCache: result.fromCache,
updateTime: result.updateTime,
data: listData,
};
};

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "1";
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "honkai",
title: "崩坏3",
type: "最新动态",
parameData: {
parame: {
type: {
name: "榜单分类",
type: {

View File

@@ -2,7 +2,7 @@ import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { web } from "../utils/getData.js";
import { extractRss, parseRSS } from "../utils/parseRSS.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "hot";
@@ -11,7 +11,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "hostloc",
title: "全球主机交流",
type: "榜单",
parameData: {
parame: {
type: {
name: "榜单分类",
type: {

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import { load } from "cheerio";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { post } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "热门文章";
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "sspai",
title: "少数派",
type: "热榜",
parameData: {
parame: {
type: {
name: "分类",
type: ["热门文章", "应用推荐", "生活方式", "效率技巧", "少数派播客"],

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const type = c.req.query("type") || "1";
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "starrail",
title: "崩坏:星穹铁道",
type: "最新动态",
parameData: {
parame: {
type: {
name: "榜单分类",
type: {

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -9,7 +9,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "v2ex",
title: "V2EX",
type: "主题榜",
parameData: {
parame: {
type: {
name: "榜单分类",
type: {

View File

@@ -1,7 +1,7 @@
import type { RouterData, ListContext, Options } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (c: ListContext, noCache: boolean) => {
const province = c.req.query("province") || "";
@@ -10,7 +10,7 @@ export const handleRoute = async (c: ListContext, noCache: boolean) => {
name: "weatheralarm",
title: "中央气象台",
type: type || "全国气象预警",
parameData: {
parame: {
province: {
name: "预警区域",
value: "省份名称( 例如:广东省 ",

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -2,7 +2,7 @@ import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getWereadID from "../utils/getToken/weread.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

View File

@@ -1,7 +1,7 @@
import type { RouterData } from "../types.js";
import type { RouterType } from "../router.types.js";
import { get } from "../utils/getData.js";
import getTime from "../utils/getTime.js";
import { getTime } from "../utils/getTime.js";
export const handleRoute = async (_: undefined, noCache: boolean) => {
const { fromCache, data, updateTime } = await getList(noCache);

6
src/types.d.ts vendored
View File

@@ -22,7 +22,7 @@ export type RouterData = {
title: string;
type: string;
description?: string;
parameData?: Record<string, string | object>;
parame?: Record<string, string | object>;
total: number;
link?: string;
updateTime: string;
@@ -61,5 +61,5 @@ export type Web = {
// 参数类型
export type Options = {
[key: string]: string | undefined;
};
[key: string]: string | number | undefined;
};

View File

@@ -1,7 +1,7 @@
import type { Get, Post, Web } from "../types.ts";
import { config } from "../config.js";
import { getCache, setCache, delCache } from "./cache.js";
import { Cluster } from "puppeteer-cluster";
// import { Cluster } from "puppeteer-cluster";
import logger from "./logger.js";
import axios from "axios";
@@ -13,25 +13,26 @@ const request = axios.create({
});
// puppeteer-cluster
export const createCluster = async () => {
return await Cluster.launch({
concurrency: Cluster.CONCURRENCY_BROWSER,
maxConcurrency: 5,
});
};
// export const createCluster = async () => {
// return await Cluster.launch({
// concurrency: Cluster.CONCURRENCY_BROWSER,
// maxConcurrency: 5,
// });
// };
// Cluster
const cluster = await createCluster();
// const cluster = await createCluster();
const cluster = null;
// Cluster configuration
cluster.task(async ({ page, data: { url, userAgent } }) => {
if (userAgent) {
await page.setUserAgent(userAgent);
}
await page.goto(url, { waitUntil: 'networkidle0' });
const pageContent = await page.content();
return pageContent;
});
// cluster.task(async ({ page, data: { url, userAgent } }) => {
// if (userAgent) {
// await page.setUserAgent(userAgent);
// }
// await page.goto(url, { waitUntil: "networkidle0" });
// const pageContent = await page.content();
// return pageContent;
// });
// 请求拦截
request.interceptors.request.use(

View File

@@ -1,21 +1,61 @@
import dayjs from "dayjs";
const getTime = (timeInput: string | number): number => {
interface CurrentDateTime {
year: number;
month: number;
day: number;
hour: number;
minute: number;
second: number;
}
export const getTime = (timeInput: string | number): number | null => {
try {
let num: number | string;
// 尝试将输入转换为数字
let num: number;
// 处理字符串的情况
if (typeof timeInput === "string") {
// 尝试将字符串直接转换为数字
num = Number(timeInput);
// 检查转换结果是否为有效数字
if (isNaN(num)) {
// 处理为字符串的日期时间
return dayjs(timeInput).valueOf();
// 将各种分隔符替换为标准格式
let standardizedInput = timeInput
.replace(/(\d{4})-(\d{2})-(\d{2})-(\d{2})/, "$1-$2-$3 $4") // "YYYY-MM-DD-HH" -> "YYYY-MM-DD HH"
.replace(/(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):?(\d{2})?:?(\d{2})?/, "$1-$2-$3 $4:$5:$6") // "YYYY-MM-DDTHH:mm:ss" -> "YYYY-MM-DD HH:mm:ss"
.replace(/(\d{4})[-/](\d{2})[-/](\d{2})/, "$1-$2-$3"); // "YYYY/MM/DD" or "YYYY-MM-DD" -> "YYYY-MM-DD"
// 减少解析过程中可能的多余空格
standardizedInput = standardizedInput.replace(/\s+/, " ").trim();
// 处理标准化后的日期时间字符串
const formatPatterns = [
"YYYY-MM-DD HH:mm:ss",
"YYYY-MM-DD HH:mm",
"YYYY-MM-DD HH",
"YYYY-MM-DD",
];
let parsedDate: dayjs.Dayjs | null = null;
for (const pattern of formatPatterns) {
parsedDate = dayjs(standardizedInput, pattern, true);
if (parsedDate.isValid()) {
break;
}
}
if (parsedDate && parsedDate.isValid()) {
return parsedDate.valueOf();
} else {
return null;
}
}
} else {
num = timeInput;
}
// 是否为毫秒级时间戳
if (num > 946684800000) {
// 以2000年作为毫秒时间戳参考点
return num;
} else {
return num * 1000;
@@ -25,4 +65,15 @@ const getTime = (timeInput: string | number): number => {
}
};
export default getTime;
export const getCurrentDateTime = (): CurrentDateTime => {
const now = dayjs();
return {
year: now.year(),
month: now.month() + 1,
day: now.date(),
hour: now.hour(),
minute: now.minute(),
second: now.second(),
};
};

View File

@@ -1,26 +1,54 @@
import { config } from "../config.js";
import { createLogger, format, transports } from "winston";
import path from "path";
import chalk from "chalk";
let pathOption: (typeof transports.File)[] = [];
// 日志输出目录
if (config.USE_LOG_FILE) {
pathOption = [
new transports.File({
filename: path.resolve("logs/error.log"),
level: "error",
maxsize: 1024 * 1024,
maxFiles: 1,
}),
new transports.File({
filename: path.resolve("logs/logger.log"),
maxsize: 1024 * 1024,
maxFiles: 1,
}),
];
try {
pathOption = [
new transports.File({
filename: path.resolve("logs/error.log"),
level: "error",
maxsize: 1024 * 1024,
maxFiles: 1,
}),
new transports.File({
filename: path.resolve("logs/logger.log"),
maxsize: 1024 * 1024,
maxFiles: 1,
}),
];
} catch (error) {
console.error("Failed to initialize log files. Logging to a file will be skipped.", error);
pathOption = [];
}
}
// 定义不同日志级别的彩色块
const levelColors: { [key: string]: string } = {
error: chalk.bgRed(" ERROR "),
warn: chalk.bgYellow(" WARN "),
info: chalk.bgBlue(" INFO "),
debug: chalk.bgGreen(" DEBUG "),
default: chalk.bgWhite(" LOG "),
};
// 自定义控制台日志输出格式
const consoleFormat = format.printf(({ level, message, timestamp, stack }) => {
// 获取原始日志级别
const originalLevel = Object.keys(levelColors).find((lvl) => level.includes(lvl)) || "default";
const colorLevel = levelColors[originalLevel] || levelColors.default;
let logMessage = `${colorLevel} [${timestamp}] ${message}`;
if (stack) {
logMessage += `\n${stack}`;
}
return logMessage;
});
// logger
const logger = createLogger({
// 最低的日志级别
@@ -39,11 +67,15 @@ const logger = createLogger({
// 控制台输出
if (process.env.NODE_ENV !== "production") {
logger.add(
new transports.Console({
format: format.combine(format.colorize(), format.simple()),
}),
);
try {
logger.add(
new transports.Console({
format: format.combine(format.colorize(), consoleFormat),
}),
);
} catch (error) {
console.error("Failed to add console transport. Console logging will be skipped.", error);
}
}
export default logger;