From 18a95387ee35530388ed6f8d2cb80e558a36b103 Mon Sep 17 00:00:00 2001 From: zhaoyingbo Date: Thu, 25 Jul 2024 01:09:24 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0lint-staged=E5=92=8C?= =?UTF-8?q?commitlint=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/commit-msg | 1 + .husky/pre-commit | 1 + bun.lockb | Bin 51675 -> 101954 bytes commitlint.config.js | 1 + controllers/managePipeLine/index.ts | 58 ++++++++++---------- controllers/manageProject/index.ts | 40 +++++++------- controllers/manageRobot/index.ts | 81 +++++++++++++-------------- controllers/manageUser/index.ts | 30 +++++----- db/index.ts | 12 ++-- db/pbClient.ts | 8 +-- db/pipeline/index.ts | 18 +++--- db/project/index.ts | 16 +++--- db/user/index.ts | 30 +++++----- db/view/index.ts | 18 +++--- eslint.config.js | 10 ++-- index.ts | 24 ++++---- package.json | 16 +++++- prettier.config.js | 8 +++ routes/ci/index.ts | 18 +++--- schedule/index.ts | 14 ++--- schedule/syncPipLine.ts | 20 +++---- script/badge/cloudml.ts | 64 +++++++++++----------- script/badge/miai.ts | 52 +++++++++--------- script/pipline/pending.json | 6 +- script/pipline/running.json | 10 +--- script/pipline/success.json | 10 +--- service/gitlab/badge.ts | 32 +++++------ service/gitlab/commit.ts | 14 ++--- service/gitlab/index.ts | 13 ++--- service/gitlab/pipeline.ts | 28 +++++----- service/gitlab/project.ts | 18 +++--- service/gitlab/tools.ts | 18 +++--- service/index.ts | 8 +-- service/message/index.ts | 28 +++++----- service/netTool.ts | 73 ++++++++++++------------- types/db.ts | 68 +++++++++++------------ types/gitlab.ts | 82 ++++++++++++++-------------- utils/pbTools.ts | 8 +-- utils/robotTools.ts | 20 +++---- utils/timeTools.ts | 14 ++--- 40 files changed, 499 insertions(+), 491 deletions(-) create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 commitlint.config.js create mode 100644 prettier.config.js diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..0a4b97d --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..af5adff --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +lint-staged \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 1e242db13f1533fd96c7356c844f65cdef372bd4..16b112f0947e0bc35298fa281eab6bde77e99ab5 100755 GIT binary patch delta 37641 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 A+yDRo delta 7245 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 pffon8epzQjcxK_@UvAg { // 先获取最新的pipelineID - const latestOne = await db.pipeline.getLatestOne(project.id); + const latestOne = await db.pipeline.getLatestOne(project.id) // 获取本次数据获取的截止时间,如果没有,则获取从20240101到现在所有pipeline信息 const latestTime = moment( latestOne?.created_at || "2024-01-01T00:00:00.000+08:00" - ); + ) // 获取pipeline列表并保存 - const fullPipelineList: Gitlab.Pipeline[] = []; - let page = 1; - let hasBeforeLatestTime = false; + const fullPipelineList: Gitlab.Pipeline[] = [] + let page = 1 + let hasBeforeLatestTime = false while (!hasBeforeLatestTime) { const pipelines = await service.gitlab.pipeline.getList( project.project_id, page++ - ); + ) // 如果当前页没有数据,则直接跳出 - if (pipelines.length === 0) break; + if (pipelines.length === 0) break pipelines.forEach((pipeline) => { // 如果已经有了比最新的pipeline还要早的pipeline,则跳出 - if (hasBeforeLatestTime) return; + if (hasBeforeLatestTime) return if (moment(pipeline.created_at).isSameOrBefore(latestTime)) { - hasBeforeLatestTime = true; + hasBeforeLatestTime = true } else { - fullPipelineList.push(pipeline); + fullPipelineList.push(pipeline) } - }); + }) } const fullPipelineDetailList = await Promise.all( fullPipelineList.map(({ project_id, id, created_at }) => service.gitlab.pipeline.getDetail(project_id, id, created_at) ) - ); + ) return fullPipelineDetailList.filter((v) => v) as (Gitlab.PipelineDetail & { - created_at: string; - })[]; -}; + created_at: string + })[] +} const insertFullPipelineList = async ( fullPipelineList: (Gitlab.PipelineDetail & { created_at: string })[][], fullUserMap: Record, fullProjectMap: Record ) => { - const dbPipelineList: Partial = []; + const dbPipelineList: Partial = [] fullPipelineList.forEach((pipelineList) => { pipelineList.forEach((pipeline) => { // 如果没有时间信息,则跳过 if (!pipeline.created_at || !pipeline.started_at || !pipeline.finished_at) - return; + return dbPipelineList.push({ project_id: fullProjectMap[pipeline.project_id], user_id: fullUserMap[pipeline.user.id], @@ -70,17 +70,17 @@ const insertFullPipelineList = async ( finished_at: pipeline.finished_at, duration: pipeline.duration, queued_duration: pipeline.queued_duration, - }); - }); - }); + }) + }) + }) await Promise.all( dbPipelineList.map((v: Partial) => db.pipeline.create(v)) - ); -}; + ) +} const managePipeline = { getFullPipelineList, insertFullPipelineList, -}; +} -export default managePipeline; +export default managePipeline diff --git a/controllers/manageProject/index.ts b/controllers/manageProject/index.ts index ddfb713..987f11a 100644 --- a/controllers/manageProject/index.ts +++ b/controllers/manageProject/index.ts @@ -1,14 +1,14 @@ -import db from "../../db"; -import service from "../../service"; -import { DB } from "../../types/db"; +import db from "../../db" +import service from "../../service" +import { DB } from "../../types/db" /** * 填充项目信息 */ const fillProj = async (project: DB.Project) => { - const projDetail = await service.gitlab.project.getDetail(project.project_id); + const projDetail = await service.gitlab.project.getDetail(project.project_id) if (!projDetail) { - return project; + return project } const useFulParams: Partial = { ...project, @@ -17,38 +17,38 @@ const fillProj = async (project: DB.Project) => { name: projDetail.name, path_with_namespace: projDetail.path_with_namespace, web_url: projDetail.web_url, - }; - return await db.project.update(project.id, useFulParams); -}; + } + return await db.project.update(project.id, useFulParams) +} /** * 获取到当前所有的项目列表 * 并把信息不全的项目送给fillProj填充内容 */ const getFullProjList = async () => { - const fullList = await db.project.getFullList(); + const fullList = await db.project.getFullList() // 把信息不全的项目送过去填充 const filledProjList = await Promise.all( fullList.filter((v) => !v.name).map((item) => fillProj(item)) - ); + ) // 合并成完整数据 const filledFullProjList = fullList .filter((v) => v.name) - .concat(filledProjList); - return filledFullProjList; -}; + .concat(filledProjList) + return filledFullProjList +} const getFullProjectMap = (fullProjList: DB.Project[]) => { - const fullProjectMap: Record = {}; + const fullProjectMap: Record = {} fullProjList.forEach((item) => { - fullProjectMap[item.project_id] = item.id; - }); - return fullProjectMap; -}; + fullProjectMap[item.project_id] = item.id + }) + return fullProjectMap +} const manageProject = { getFullProjList, getFullProjectMap, -}; +} -export default manageProject; +export default manageProject diff --git a/controllers/manageRobot/index.ts b/controllers/manageRobot/index.ts index 62aa70b..88e55bc 100644 --- a/controllers/manageRobot/index.ts +++ b/controllers/manageRobot/index.ts @@ -1,37 +1,34 @@ -import db from "../../db"; -import service from "../../service"; -import { calculateWeeklyRate } from "../../utils/robotTools"; -import { - getPrevWeekWithYear, - getWeekTimeWithYear, -} from "../../utils/timeTools"; +import db from "../../db" +import service from "../../service" +import { calculateWeeklyRate } from "../../utils/robotTools" +import { getPrevWeekWithYear, getWeekTimeWithYear } from "../../utils/timeTools" const getNewCicdStatus = async () => { - const fullProjList = await db.project.getFullList(); + const fullProjList = await db.project.getFullList() const has_new_cicd_count = String( fullProjList.filter((item) => { - return item.has_new_cicd === true; + return item.has_new_cicd === true }).length - ); + ) const without_new_cicd_count = String( fullProjList.filter((item) => { - return item.has_new_cicd === false; + return item.has_new_cicd === false }).length - ); + ) return { has_new_cicd_count, without_new_cicd_count, - }; -}; + } +} const getStatisticsInfo = async () => { const curWeekInfo = await db.view.getFullStatisticsByWeek( getWeekTimeWithYear() - ); + ) const prevWeekInfo = await db.view.getFullStatisticsByWeek( getPrevWeekWithYear() - ); - if (!curWeekInfo || !prevWeekInfo) return {}; + ) + if (!curWeekInfo || !prevWeekInfo) return {} return { total_count: String(curWeekInfo?.total_count ?? 0), weekly_count_rate: calculateWeeklyRate( @@ -48,37 +45,37 @@ const getStatisticsInfo = async () => { curWeekInfo?.success_rate, prevWeekInfo?.success_rate ).text, - }; -}; + } +} const getProjDiffInfo = async () => { const curWeekInfo = - (await db.view.getProjStatisticsByWeek(getWeekTimeWithYear())) || []; + (await db.view.getProjStatisticsByWeek(getWeekTimeWithYear())) || [] const prevWeekInfo = - (await db.view.getProjStatisticsByWeek(getPrevWeekWithYear())) || []; + (await db.view.getProjStatisticsByWeek(getPrevWeekWithYear())) || [] const group: { - project_name: string; - project_ref: string; - project_duration: string; - project_duration_rate: string; - percentage: string; - }[] = []; + project_name: string + project_ref: string + project_duration: string + project_duration_rate: string + percentage: string + }[] = [] curWeekInfo.forEach((curWeekProjInfo) => { const prevWeekProjInfo = prevWeekInfo.find( (info) => info.ref === curWeekProjInfo.ref && info.name === curWeekProjInfo.name - ); - if (!prevWeekProjInfo) return; + ) + if (!prevWeekProjInfo) return const { text: project_duration_rate, percentage } = calculateWeeklyRate( curWeekProjInfo.duration, prevWeekProjInfo.duration, false - ); + ) - if (percentage === "0") return; + if (percentage === "0") return group.push({ project_name: curWeekProjInfo.name, @@ -86,15 +83,15 @@ const getProjDiffInfo = async () => { project_duration: String(curWeekProjInfo.duration), project_duration_rate, percentage, - }); - }); + }) + }) // 排序 - group.sort((a, b) => Number(b.percentage) - Number(a.percentage)); + group.sort((a, b) => Number(b.percentage) - Number(a.percentage)) // 取前五个 - return group.slice(0, 5); -}; + return group.slice(0, 5) +} /** * 获取机器人消息 @@ -114,26 +111,26 @@ const getRobotMsg = async () => group_table: await getProjDiffInfo(), }, }, - }); + }) /** * 通过ChatID发送CI报告 * @param chat_id */ const sendCIReportByChatId = async (chat_id: string) => { - await service.message.byChatId(chat_id, await getRobotMsg()); -}; + await service.message.byChatId(chat_id, await getRobotMsg()) +} /** * 通过定时任务发送CI报告 * @returns */ const sendCIReportByCron = async () => - await service.message.byGroupId("52usf3w8l6z4vs1", await getRobotMsg()); + await service.message.byGroupId("52usf3w8l6z4vs1", await getRobotMsg()) const manageRobot = { sendCIReportByChatId, sendCIReportByCron, -}; +} -export default manageRobot; +export default manageRobot diff --git a/controllers/manageUser/index.ts b/controllers/manageUser/index.ts index 5f24e2d..f027f98 100644 --- a/controllers/manageUser/index.ts +++ b/controllers/manageUser/index.ts @@ -1,15 +1,15 @@ -import db from "../../db"; -import { Gitlab } from "../../types/gitlab"; +import db from "../../db" +import { Gitlab } from "../../types/gitlab" const getFullUserMap = async (fullPipelineList: Gitlab.PipelineDetail[][]) => { - const userList: Gitlab.User[] = []; + const userList: Gitlab.User[] = [] fullPipelineList.forEach((fullPipeline) => { fullPipeline.forEach((item) => { if (item.user && !userList.find((v) => v.id === item.user?.id)) { - userList.push(item.user); + userList.push(item.user) } - }); - }); + }) + }) const dbUserInfo = await Promise.all( userList @@ -23,17 +23,17 @@ const getFullUserMap = async (fullPipelineList: Gitlab.PipelineDetail[][]) => { web_url: user.web_url, }) ) - ); - const userMap: Record = {}; + ) + const userMap: Record = {} dbUserInfo.forEach((item) => { - if (!item) return; - userMap[item.user_id] = item.id; - }); - return userMap; -}; + if (!item) return + userMap[item.user_id] = item.id + }) + return userMap +} const manageUser = { getFullUserMap, -}; +} -export default manageUser; +export default manageUser diff --git a/db/index.ts b/db/index.ts index c3d876b..7f1d34e 100644 --- a/db/index.ts +++ b/db/index.ts @@ -1,13 +1,13 @@ -import pipeline from "./pipeline"; -import project from "./project"; -import user from "./user"; -import view from "./view"; +import pipeline from "./pipeline" +import project from "./project" +import user from "./user" +import view from "./view" const db = { project, pipeline, user, view, -}; +} -export default db; +export default db diff --git a/db/pbClient.ts b/db/pbClient.ts index 7999649..7bd419b 100644 --- a/db/pbClient.ts +++ b/db/pbClient.ts @@ -1,7 +1,7 @@ -import PocketBase from "pocketbase"; +import PocketBase from "pocketbase" -const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn"); +const pbClient = new PocketBase("https://ci-pb.xiaomiwh.cn") -pbClient.autoCancellation(false); +pbClient.autoCancellation(false) -export default pbClient; +export default pbClient diff --git a/db/pipeline/index.ts b/db/pipeline/index.ts index 3f42a0f..2008b67 100644 --- a/db/pipeline/index.ts +++ b/db/pipeline/index.ts @@ -1,6 +1,6 @@ -import { DB } from "../../types/db"; -import { managePb404 } from "../../utils/pbTools"; -import pbClient from "../pbClient"; +import { DB } from "../../types/db" +import { managePb404 } from "../../utils/pbTools" +import pbClient from "../pbClient" /** * 通过其 ID 检索一个管道。 @@ -10,7 +10,7 @@ import pbClient from "../pbClient"; const getOne = (id: string) => managePb404( async () => await pbClient.collection("pipeline").getOne(id) - ); + ) /** * 检索项目的最新管道。 @@ -25,8 +25,8 @@ const getLatestOne = (project_id: string) => { .getFirstListItem(`project_id="${project_id}"`, { sort: "-created_at", }) - ); -}; + ) +} /** * 创建一个新的管道。 @@ -34,12 +34,12 @@ const getLatestOne = (project_id: string) => { * @returns 一个解析为创建的管道对象的 promise。 */ const create = async (data: Partial) => - await pbClient.collection("pipeline").create(data); + await pbClient.collection("pipeline").create(data) const pipeline = { create, getOne, getLatestOne, -}; +} -export default pipeline; +export default pipeline diff --git a/db/project/index.ts b/db/project/index.ts index 71d318a..f2c574b 100644 --- a/db/project/index.ts +++ b/db/project/index.ts @@ -1,6 +1,6 @@ -import { DB } from "../../types/db"; -import { managePb404 } from "../../utils/pbTools"; -import pbClient from "../pbClient"; +import { DB } from "../../types/db" +import { managePb404 } from "../../utils/pbTools" +import pbClient from "../pbClient" /** * 通过其 ID 检索单个项目。 @@ -10,14 +10,14 @@ import pbClient from "../pbClient"; const getOne = (id: string) => managePb404( async () => await pbClient.collection("project").getOne(id) - ); + ) /** * 检索项目的完整列表。 * @returns 一个解析为项目对象数组的 promise。 */ const getFullList = async () => - await pbClient.collection("project").getFullList(); + await pbClient.collection("project").getFullList() /** * 使用新数据更新项目。 @@ -26,7 +26,7 @@ const getFullList = async () => * @returns 一个解析为更新后的项目对象的 promise。 */ const update = async (id: string, data: Partial) => - await pbClient.collection("project").update(id, data); + await pbClient.collection("project").update(id, data) /** * 用于管理项目的函数集合。 @@ -35,6 +35,6 @@ const project = { getFullList, getOne, update, -}; +} -export default project; +export default project diff --git a/db/user/index.ts b/db/user/index.ts index 685b2c3..52e8b00 100644 --- a/db/user/index.ts +++ b/db/user/index.ts @@ -1,6 +1,6 @@ -import { DB } from "../../types/db"; -import { managePb404 } from "../../utils/pbTools"; -import pbClient from "../pbClient"; +import { DB } from "../../types/db" +import { managePb404 } from "../../utils/pbTools" +import pbClient from "../pbClient" /** * 根据提供的ID从数据库检索单个用户。 @@ -8,9 +8,7 @@ import pbClient from "../pbClient"; * @returns 如果找到,返回解析为用户对象的promise;如果未找到,抛出404错误。 */ const getOne = (id: string) => - managePb404( - async () => await pbClient.collection("user").getOne(id) - ); + managePb404(async () => await pbClient.collection("user").getOne(id)) /** * 根据提供的用户ID从数据库检索单个用户。 @@ -25,8 +23,8 @@ const getOneByUserId = (user_id: number) => { .getFirstListItem(`user_id="${user_id}"`, { sort: "-created", }) - ); -}; + ) +} /** * 在数据库中创建一个新用户。 @@ -34,7 +32,7 @@ const getOneByUserId = (user_id: number) => { * @returns 返回解析为已创建用户对象的promise。 */ const create = async (data: Partial) => - await pbClient.collection("user").create(data); + await pbClient.collection("user").create(data) /** * 在数据库中插入或更新一个用户。 @@ -45,11 +43,11 @@ const create = async (data: Partial) => * 如果数据不包含用户ID,则返回null。 */ const upsert = async (data: Partial) => { - if (!data.user_id) return null; - const userInfo = await getOneByUserId(data.user_id); - if (userInfo) return userInfo; - return await create(data); -}; + if (!data.user_id) return null + const userInfo = await getOneByUserId(data.user_id) + if (userInfo) return userInfo + return await create(data) +} /** * 用户模块提供了与数据库中的用户集合交互的函数。 @@ -59,6 +57,6 @@ const user = { upsert, getOne, getOneByUserId, -}; +} -export default user; +export default user diff --git a/db/view/index.ts b/db/view/index.ts index 4f74c04..fae65b6 100644 --- a/db/view/index.ts +++ b/db/view/index.ts @@ -1,6 +1,6 @@ -import { DB } from "../../types/db"; -import { managePb404 } from "../../utils/pbTools"; -import pbClient from "../pbClient"; +import { DB } from "../../types/db" +import { managePb404 } from "../../utils/pbTools" +import pbClient from "../pbClient" /** * 根据给定的周来检索完整的统计信息。 @@ -13,8 +13,8 @@ const getFullStatisticsByWeek = (week: string) => { await pbClient .collection("statisticsPerWeek") .getFirstListItem(`week="${week}"`) - ); -}; + ) +} /** * 根据给定的周来检索项目统计信息。 @@ -27,12 +27,12 @@ const getProjStatisticsByWeek = (week: string) => { await pbClient .collection("statisticsPerProj") .getFullList({ filter: `week="${week}"` }) - ); -}; + ) +} const view = { getFullStatisticsByWeek, getProjStatisticsByWeek, -}; +} -export default view; +export default view diff --git a/eslint.config.js b/eslint.config.js index 4343203..d871960 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,7 +1,7 @@ -import pluginJs from "@eslint/js"; -import simpleImportSort from "eslint-plugin-simple-import-sort"; -import globals from "globals"; -import tseslint from "typescript-eslint"; +import pluginJs from "@eslint/js" +import simpleImportSort from "eslint-plugin-simple-import-sort" +import globals from "globals" +import tseslint from "typescript-eslint" export default [ { files: ["**/*.{js,mjs,cjs,ts}"] }, @@ -19,4 +19,4 @@ export default [ "simple-import-sort/exports": "error", }, }, -]; +] diff --git a/index.ts b/index.ts index 4b7ba66..cfcf6d4 100644 --- a/index.ts +++ b/index.ts @@ -1,27 +1,27 @@ -import { manageCIMonitorReq } from "./routes/ci"; -import initSchedule from "./schedule"; -import netTool from "./service/netTool"; +import { manageCIMonitorReq } from "./routes/ci" +import initSchedule from "./schedule" +import netTool from "./service/netTool" // 启动定时任务 -initSchedule(); +initSchedule() const server = Bun.serve({ async fetch(req) { try { - const url = new URL(req.url); + const url = new URL(req.url) // 根路由 - if (url.pathname === "/") return netTool.ok("hello, glade to see you!"); + if (url.pathname === "/") return netTool.ok("hello, glade to see you!") // CI 监控 - if (url.pathname === "/ci") return manageCIMonitorReq(req); + if (url.pathname === "/ci") return manageCIMonitorReq(req) // 其他 - return netTool.ok("hello, glade to see you!"); + return netTool.ok("hello, glade to see you!") } catch (error: any) { // 错误处理 - console.error("🚀 ~ serve ~ error", error); - return netTool.serverError(error.message || "server error"); + console.error("🚀 ~ serve ~ error", error) + return netTool.serverError(error.message || "server error") } }, port: 3000, -}); +}) -console.log(`Listening on ${server.hostname}:${server.port}`); +console.log(`Listening on ${server.hostname}:${server.port}`) diff --git a/package.json b/package.json index dd3952c..bdf0a27 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,20 @@ "type": "module", "scripts": { "start": "bun run index.ts", - "lint": "eslint --fix ." + "lint": "eslint --fix .", + "prepare": "husky", + "prettier": "prettier --write ." + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": [ + "eslint --fix", + "prettier --write", + "git add" + ] }, "devDependencies": { + "@commitlint/cli": "^19.3.0", + "@commitlint/config-conventional": "^19.2.2", "@eslint/js": "^9.7.0", "@types/lodash": "^4.14.202", "@types/node-schedule": "^2.1.6", @@ -14,6 +25,9 @@ "eslint": "9.x", "eslint-plugin-simple-import-sort": "^12.1.1", "globals": "^15.8.0", + "husky": "^9.1.1", + "lint-staged": "^15.2.7", + "prettier": "^3.3.3", "typescript-eslint": "^7.17.0" }, "peerDependencies": { diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..02cb33a --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,8 @@ +const config = { + trailingComma: "es5", + tabWidth: 2, + semi: false, + singleQuote: false, +} + +export default config diff --git a/routes/ci/index.ts b/routes/ci/index.ts index 4225965..e4d0e77 100644 --- a/routes/ci/index.ts +++ b/routes/ci/index.ts @@ -1,5 +1,5 @@ -import manageRobot from "../../controllers/manageRobot"; -import netTool from "../../service/netTool"; +import manageRobot from "../../controllers/manageRobot" +import netTool from "../../service/netTool" /** * 处理管理CI监视器的请求。 @@ -7,16 +7,16 @@ import netTool from "../../service/netTool"; * @returns 响应对象。 */ export const manageCIMonitorReq = (req: Request) => { - const url = new URL(req.url); + const url = new URL(req.url) if (url.pathname === "/ci") { - const chat_id = url.searchParams.get("chat_id"); - if (!chat_id) return netTool.badRequest("chat_id is required!"); - manageRobot.sendCIReportByChatId(chat_id); + const chat_id = url.searchParams.get("chat_id") + if (!chat_id) return netTool.badRequest("chat_id is required!") + manageRobot.sendCIReportByChatId(chat_id) return Response.json({ code: 0, msg: "success", data: "reporting...", - }); + }) } - return netTool.ok(); -}; + return netTool.ok() +} diff --git a/schedule/index.ts b/schedule/index.ts index 9e83cd0..1af9c6d 100644 --- a/schedule/index.ts +++ b/schedule/index.ts @@ -1,15 +1,15 @@ -import { scheduleJob } from "node-schedule"; +import { scheduleJob } from "node-schedule" -import manageRobot from "../controllers/manageRobot"; -import syncPipLine from "./syncPipLine"; +import manageRobot from "../controllers/manageRobot" +import syncPipLine from "./syncPipLine" const initSchedule = async () => { // 每周五早上10点发送CI报告 - scheduleJob("0 10 * * 5", manageRobot.sendCIReportByCron); + scheduleJob("0 10 * * 5", manageRobot.sendCIReportByCron) // 每15分钟同步一次CI数据 - scheduleJob("*/15 * * * *", syncPipLine); + scheduleJob("*/15 * * * *", syncPipLine) // 立即同步一次 - syncPipLine(); + syncPipLine() } -export default initSchedule; \ No newline at end of file +export default initSchedule diff --git a/schedule/syncPipLine.ts b/schedule/syncPipLine.ts index bbfb67b..8b7bc57 100644 --- a/schedule/syncPipLine.ts +++ b/schedule/syncPipLine.ts @@ -1,19 +1,19 @@ -import managePipeline from "../controllers/managePipeLine"; -import manageProject from "../controllers/manageProject"; -import manageUser from "../controllers/manageUser"; +import managePipeline from "../controllers/managePipeLine" +import manageProject from "../controllers/manageProject" +import manageUser from "../controllers/manageUser" const syncPipLine = async () => { - const fullProjList = await manageProject.getFullProjList(); + const fullProjList = await manageProject.getFullProjList() const fullPipelineList = await Promise.all( fullProjList.map((v) => managePipeline.getFullPipelineList(v)) - ); - const fullUserMap = await manageUser.getFullUserMap(fullPipelineList); - const fullProjectMap = await manageProject.getFullProjectMap(fullProjList); + ) + const fullUserMap = await manageUser.getFullUserMap(fullPipelineList) + const fullProjectMap = await manageProject.getFullProjectMap(fullProjList) await managePipeline.insertFullPipelineList( fullPipelineList, fullUserMap, fullProjectMap - ); -}; + ) +} -export default syncPipLine; +export default syncPipLine diff --git a/script/badge/cloudml.ts b/script/badge/cloudml.ts index ff8e08e..78ea223 100644 --- a/script/badge/cloudml.ts +++ b/script/badge/cloudml.ts @@ -1,6 +1,6 @@ -import service from "../../service"; -import { BadgeSetParams } from "../../service/gitlab/badge"; -import { Gitlab } from "../../types/gitlab"; +import service from "../../service" +import { BadgeSetParams } from "../../service/gitlab/badge" +import { Gitlab } from "../../types/gitlab" const projectList = [ // "cloud-ml/cloudml-maas", @@ -22,39 +22,39 @@ const projectList = [ // "cloud-ml/knowledge-base", // "cloud-ml/ai-proxy", "cloud-ml/lark_auth", -]; +] const getNewProjectBadge = async ( projectDetail: Gitlab.ProjDetail ): Promise => { // 项目路径 cloud-ml/cloudml-dev - const projectPath = projectDetail.path_with_namespace; + const projectPath = projectDetail.path_with_namespace // 根据项目路径获取sonarqubeId 类似于 cloud-ml/cloudml-dev -> cloud-ml:cloudml-dev - const sonarqubeId = projectPath.replace("/", ":"); + const sonarqubeId = projectPath.replace("/", ":") // 获取项目的badges const badges: Gitlab.Badge[] = await service.gitlab.badge.get( projectDetail.id - ); + ) // 对badges进行补全,可能只有 name: "sonarqube_coverage" 的情况,把剩下的补全 - const badgeNames = badges.map((badge) => badge.name); - const badgeNameSet = new Set(badgeNames); + const badgeNames = badges.map((badge) => badge.name) + const badgeNameSet = new Set(badgeNames) const badgeNameList = [ "sonarqube_bugs", "sonarqube_vulnerabilities", "sonarqube_security_hotspots", "sonarqube_coverage", "sonarqube_quality_gate", - ]; - const diff = [...badgeNameList].filter((x) => !badgeNameSet.has(x)); + ] + const diff = [...badgeNameList].filter((x) => !badgeNameSet.has(x)) const newBadges: BadgeSetParams[] = diff.map((name) => { const link_url = encodeURI( `https://sonarqube.mioffice.cn/dashboard?id=${sonarqubeId}` - ); - const metric = name.replace("sonarqube_", ""); + ) + const metric = name.replace("sonarqube_", "") const image_url = name !== "sonarqube_quality_gate" ? `https://sonarqube.mioffice.cn/api/badges/measure?key=${sonarqubeId}&metric=${metric}` - : `https://sonarqube.mioffice.cn/api/badges/gate?key=${sonarqubeId}`; + : `https://sonarqube.mioffice.cn/api/badges/gate?key=${sonarqubeId}` return { id: projectDetail.id, link_url, @@ -62,39 +62,39 @@ const getNewProjectBadge = async ( rendered_image_url: image_url, rendered_link_url: link_url, name, - }; - }); - return newBadges; -}; + } + }) + return newBadges +} const addNewProjectBadge = async (badgeSetParamsList: BadgeSetParams[]) => { - const chunkSize = 5; // 每次并发请求的数量 + const chunkSize = 5 // 每次并发请求的数量 for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) { - const chunk = badgeSetParamsList.slice(i, i + chunkSize); + const chunk = badgeSetParamsList.slice(i, i + chunkSize) const res = await Promise.all( chunk.map((badgeSetParams) => service.gitlab.badge.add(badgeSetParams)) - ); - console.log(res); + ) + console.log(res) } -}; +} const main = async () => { - const errorList: string[] = []; + const errorList: string[] = [] const badgeAddParamsList = await Promise.all( projectList.map(async (projectName) => { - const projectDetail = await service.gitlab.project.getDetail(projectName); + const projectDetail = await service.gitlab.project.getDetail(projectName) if (!projectDetail) { - errorList.push(projectName); - return []; + errorList.push(projectName) + return [] } - return await getNewProjectBadge(projectDetail); + return await getNewProjectBadge(projectDetail) }) - ); + ) - await addNewProjectBadge(badgeAddParamsList.flat()); -}; + await addNewProjectBadge(badgeAddParamsList.flat()) +} -main(); +main() // [ // { diff --git a/script/badge/miai.ts b/script/badge/miai.ts index b5f9e66..1098bc8 100644 --- a/script/badge/miai.ts +++ b/script/badge/miai.ts @@ -1,6 +1,6 @@ -import service from "../../service"; -import { BadgeSetParams } from "../../service/gitlab/badge"; -import { Gitlab } from "../../types/gitlab"; +import service from "../../service" +import { BadgeSetParams } from "../../service/gitlab/badge" +import { Gitlab } from "../../types/gitlab" const projectList = [ "miai-fe/fe/ai-admin-fe", @@ -53,23 +53,23 @@ const projectList = [ "miai-fe/fe/ai-schedule-manager-fe", "miai-fe/fe/ai-voiceprint-fe", "miai-fe/fe/ai-shortcut-fe", -]; +] const getProjectId = async (projectName: string) => { - const res = await service.gitlab.project.getDetail(projectName); - return res?.id; -}; + const res = await service.gitlab.project.getDetail(projectName) + return res?.id +} const getNewProjectBadge = async ( projectId: number ): Promise => { - const badges: Gitlab.Badge[] = await service.gitlab.badge.get(projectId); + const badges: Gitlab.Badge[] = await service.gitlab.badge.get(projectId) const replacePath = (value: string) => value.replace( "https://sonarqube.mioffice.cn", "http://scan.sonarqube.xiaomi.srv" - ); + ) return badges.map((badge: Gitlab.Badge) => ({ id: projectId, @@ -78,36 +78,36 @@ const getNewProjectBadge = async ( image_url: badge.image_url, rendered_image_url: badge.rendered_image_url, rendered_link_url: replacePath(badge.rendered_link_url), - })); -}; + })) +} const setNewProjectBadge = async (badgeSetParamsList: BadgeSetParams[]) => { - const chunkSize = 5; // 每次并发请求的数量 + const chunkSize = 5 // 每次并发请求的数量 for (let i = 0; i < badgeSetParamsList.length; i += chunkSize) { - const chunk = badgeSetParamsList.slice(i, i + chunkSize); + const chunk = badgeSetParamsList.slice(i, i + chunkSize) const res = await Promise.all( chunk.map((badgeSetParams) => service.gitlab.badge.set(badgeSetParams)) - ); - console.log(res); + ) + console.log(res) } -}; +} const main = async () => { - const errorList: string[] = []; + const errorList: string[] = [] const badgeSetParamsList = await Promise.all( projectList.map(async (projectName) => { - const projectId = await getProjectId(projectName); + const projectId = await getProjectId(projectName) if (!projectId) { - errorList.push(projectName); - return []; + errorList.push(projectName) + return [] } - return await getNewProjectBadge(projectId); + return await getNewProjectBadge(projectId) }) - ); + ) - await setNewProjectBadge(badgeSetParamsList.flat()); + await setNewProjectBadge(badgeSetParamsList.flat()) - console.log("errorList", errorList); -}; + console.log("errorList", errorList) +} -main(); +main() diff --git a/script/pipline/pending.json b/script/pipline/pending.json index c299626..6262703 100644 --- a/script/pipline/pending.json +++ b/script/pipline/pending.json @@ -9,9 +9,7 @@ "source": "push", "status": "pending", "detailed_status": "pending", - "stages": [ - "print" - ], + "stages": ["print"], "created_at": "2024-07-23 09:28:44 +0800", "finished_at": null, "duration": null, @@ -80,4 +78,4 @@ "environment": null } ] -} \ No newline at end of file +} diff --git a/script/pipline/running.json b/script/pipline/running.json index 6ac9831..d80a3a9 100644 --- a/script/pipline/running.json +++ b/script/pipline/running.json @@ -9,9 +9,7 @@ "source": "push", "status": "running", "detailed_status": "running", - "stages": [ - "print" - ], + "stages": ["print"], "created_at": "2024-07-23 08:57:14 +0800", "finished_at": null, "duration": null, @@ -78,9 +76,7 @@ "runner_type": "group_type", "active": true, "is_shared": false, - "tags": [ - "cloudml-fe-bj" - ] + "tags": ["cloudml-fe-bj"] }, "artifacts_file": { "filename": null, @@ -89,4 +85,4 @@ "environment": null } ] -} \ No newline at end of file +} diff --git a/script/pipline/success.json b/script/pipline/success.json index c94e969..886e5c3 100644 --- a/script/pipline/success.json +++ b/script/pipline/success.json @@ -9,9 +9,7 @@ "source": "push", "status": "success", "detailed_status": "passed", - "stages": [ - "print" - ], + "stages": ["print"], "created_at": "2024-07-23 08:57:14 +0800", "finished_at": "2024-07-23 08:57:24 +0800", "duration": 5, @@ -78,9 +76,7 @@ "runner_type": "group_type", "active": true, "is_shared": false, - "tags": [ - "cloudml-fe-bj" - ] + "tags": ["cloudml-fe-bj"] }, "artifacts_file": { "filename": null, @@ -89,4 +85,4 @@ "environment": null } ] -} \ No newline at end of file +} diff --git a/service/gitlab/badge.ts b/service/gitlab/badge.ts index 619594b..64e93d8 100644 --- a/service/gitlab/badge.ts +++ b/service/gitlab/badge.ts @@ -1,13 +1,13 @@ -import { Gitlab } from "../../types/gitlab"; -import netTool from "../netTool"; -import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"; +import { Gitlab } from "../../types/gitlab" +import netTool from "../netTool" +import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools" /** * 代表设置 GitLab 徽章的参数。 */ export type BadgeSetParams = Omit & { - badge_id?: number; -}; + badge_id?: number +} /** * 为特定项目检索 GitLab 徽章。 @@ -15,12 +15,12 @@ export type BadgeSetParams = Omit & { * @returns 一个承诺,解析为 GitLab 徽章的数组。 */ const get = async (project_id: number) => { - const URL = `${GITLAB_BASE_URL}/projects/${project_id}/badges`; + const URL = `${GITLAB_BASE_URL}/projects/${project_id}/badges` return gitlabReqWarp( () => netTool.get(URL, {}, GITLAB_AUTH_HEADER), [] - ); -}; + ) +} /** * 设置 GitLab 徽章。 @@ -28,12 +28,12 @@ const get = async (project_id: number) => { * @returns 一个承诺,解析为更新后的徽章。 */ const set = async (badge: BadgeSetParams) => { - const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges/${badge.badge_id}`; + const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges/${badge.badge_id}` return gitlabReqWarp( () => netTool.put(URL, badge, {}, GITLAB_AUTH_HEADER), null - ); -}; + ) +} /** * 添加 GitLab 徽章。 @@ -41,12 +41,12 @@ const set = async (badge: BadgeSetParams) => { * @returns 一个承诺,解析为添加的徽章。 */ const add = async (badge: BadgeSetParams) => { - const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges`; + const URL = `${GITLAB_BASE_URL}/projects/${badge.id}/badges` return gitlabReqWarp( () => netTool.post(URL, badge, {}, GITLAB_AUTH_HEADER), null - ); -}; + ) +} /** * 代表一系列与徽章相关的函数。 @@ -55,6 +55,6 @@ const badge = { get, set, add, -}; +} -export default badge; +export default badge diff --git a/service/gitlab/commit.ts b/service/gitlab/commit.ts index ec567f7..a74d63d 100644 --- a/service/gitlab/commit.ts +++ b/service/gitlab/commit.ts @@ -1,5 +1,5 @@ -import netTool from "../netTool"; -import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"; +import netTool from "../netTool" +import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools" /** * 检索与特定提交关联的合并请求。 @@ -8,15 +8,15 @@ import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"; * @returns 一个解析为合并请求数组的promise。 */ const getMr = async (project_id: number, sha: string) => { - const URL = `${GITLAB_BASE_URL}/projects/${project_id}/repository/commits/${sha}/merge_requests`; + const URL = `${GITLAB_BASE_URL}/projects/${project_id}/repository/commits/${sha}/merge_requests` return gitlabReqWarp( () => netTool.get(URL, {}, GITLAB_AUTH_HEADER), [] - ); -}; + ) +} const commit = { getMr, -}; +} -export default commit; +export default commit diff --git a/service/gitlab/index.ts b/service/gitlab/index.ts index 2e1a61b..c247995 100644 --- a/service/gitlab/index.ts +++ b/service/gitlab/index.ts @@ -1,14 +1,13 @@ - -import badge from "./badge"; -import commit from "./commit"; -import pipeline from "./pipeline"; -import project from "./project"; +import badge from "./badge" +import commit from "./commit" +import pipeline from "./pipeline" +import project from "./project" const gitlab = { project, badge, commit, pipeline, -}; +} -export default gitlab; +export default gitlab diff --git a/service/gitlab/pipeline.ts b/service/gitlab/pipeline.ts index dc8d579..e83c4f5 100644 --- a/service/gitlab/pipeline.ts +++ b/service/gitlab/pipeline.ts @@ -1,6 +1,6 @@ -import { Gitlab } from "../../types/gitlab"; -import netTool from "../netTool"; -import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"; +import { Gitlab } from "../../types/gitlab" +import netTool from "../netTool" +import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools" /** * 获取特定GitLab流水线的详细信息。 @@ -14,14 +14,14 @@ const getDetail = async ( pipeline_id: number, created_at: string ) => { - const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines/${pipeline_id}`; + const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines/${pipeline_id}` const res = await gitlabReqWarp( () => netTool.get(URL, {}, GITLAB_AUTH_HEADER), null - ); - if (res === null) return null; - return { ...res, created_at }; -}; + ) + if (res === null) return null + return { ...res, created_at } +} /** * 获取特定项目的GitLab流水线列表。 @@ -30,17 +30,17 @@ const getDetail = async ( * @returns 一个解析为流水线数组的promise。 */ const getList = async (project_id: number, page = 1) => { - const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines`; - const params = { scope: "finished", per_page: 100, page }; + const URL = `${GITLAB_BASE_URL}/projects/${project_id}/pipelines` + const params = { scope: "finished", per_page: 100, page } return gitlabReqWarp( () => netTool.get(URL, params, GITLAB_AUTH_HEADER), [] - ); -}; + ) +} const pipeline = { getDetail, getList, -}; +} -export default pipeline; +export default pipeline diff --git a/service/gitlab/project.ts b/service/gitlab/project.ts index 842f65e..243709a 100644 --- a/service/gitlab/project.ts +++ b/service/gitlab/project.ts @@ -1,6 +1,6 @@ -import { Gitlab } from "../../types/gitlab"; -import netTool from "../netTool"; -import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"; +import { Gitlab } from "../../types/gitlab" +import netTool from "../netTool" +import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools" /** * 获取 GitLab 项目的详细信息。 @@ -9,16 +9,16 @@ import { GITLAB_AUTH_HEADER, GITLAB_BASE_URL, gitlabReqWarp } from "./tools"; */ const getDetail = async (project_id: number | string) => { if (typeof project_id === "string") - project_id = encodeURIComponent(project_id); - const URL = `${GITLAB_BASE_URL}/projects/${project_id}`; + project_id = encodeURIComponent(project_id) + const URL = `${GITLAB_BASE_URL}/projects/${project_id}` return gitlabReqWarp( () => netTool.get(URL, {}, GITLAB_AUTH_HEADER), null - ); -}; + ) +} const project = { getDetail, -}; +} -export default project; +export default project diff --git a/service/gitlab/tools.ts b/service/gitlab/tools.ts index 48ec540..470763c 100644 --- a/service/gitlab/tools.ts +++ b/service/gitlab/tools.ts @@ -1,19 +1,19 @@ -import { Gitlab } from "../../types/gitlab"; +import { Gitlab } from "../../types/gitlab" export const gitlabReqWarp = async ( func: () => Promise, default_value: any ): Promise => { try { - let response = {} as T & Gitlab.Error; - response = (await func()) as T & Gitlab.Error; - if (response.message === "404 Project Not Found") return default_value; - return response; + let response = {} as T & Gitlab.Error + response = (await func()) as T & Gitlab.Error + if (response.message === "404 Project Not Found") return default_value + return response } catch { - return default_value; + return default_value } -}; +} -export const GITLAB_BASE_URL = "https://git.n.xiaomi.com/api/v4"; +export const GITLAB_BASE_URL = "https://git.n.xiaomi.com/api/v4" -export const GITLAB_AUTH_HEADER = { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" }; +export const GITLAB_AUTH_HEADER = { "PRIVATE-TOKEN": "Zd1UASPcMwVox5tNS6ep" } diff --git a/service/index.ts b/service/index.ts index 3ef1ac5..41c86b2 100644 --- a/service/index.ts +++ b/service/index.ts @@ -1,9 +1,9 @@ -import gitlab from "./gitlab"; -import message from "./message"; +import gitlab from "./gitlab" +import message from "./message" const service = { gitlab, message, -}; +} -export default service; +export default service diff --git a/service/message/index.ts b/service/message/index.ts index 8bf8e5c..c0b39d6 100644 --- a/service/message/index.ts +++ b/service/message/index.ts @@ -1,27 +1,27 @@ -import netTool from "../netTool"; +import netTool from "../netTool" -const API_KEY = "1dfz4wlpbbgiky0"; -const URL = "https://egg.imoaix.cn/message"; +const API_KEY = "1dfz4wlpbbgiky0" +const URL = "https://egg.imoaix.cn/message" const message = async (body: any) => { try { const res = await netTool.post(URL, { api_key: API_KEY, ...body, - }); - return res; + }) + return res } catch { - return null; + return null } -}; +} message.byGroupId = async (group_id: string, content: string) => { return message({ group_id, msg_type: "interactive", content, - }); -}; + }) +} message.byChatId = async (chat_id: string, content: string) => { return message({ @@ -29,8 +29,8 @@ message.byChatId = async (chat_id: string, content: string) => { receive_id_type: "chat_id", msg_type: "interactive", content, - }); -}; + }) +} message.byUserId = async (user_id: string, content: string) => { return message({ @@ -38,7 +38,7 @@ message.byUserId = async (user_id: string, content: string) => { receive_id_type: "user_id", msg_type: "interactive", content, - }); -}; + }) +} -export default message; +export default message diff --git a/service/netTool.ts b/service/netTool.ts index 190dcce..082a37f 100644 --- a/service/netTool.ts +++ b/service/netTool.ts @@ -1,9 +1,9 @@ interface NetRequestParams { - url: string; - method: string; - queryParams?: any; - payload?: any; - additionalHeaders?: any; + url: string + method: string + queryParams?: any + payload?: any + additionalHeaders?: any } /** @@ -32,10 +32,10 @@ const logResponse = ( responseHeaders: response.headers, requestBody, responseBody, - }; - console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2)); - return responseLog; -}; + } + console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2)) + return responseLog +} /** * 发送网络请求并返回一个解析为响应数据的Promise。 @@ -55,13 +55,13 @@ const netTool = async ({ additionalHeaders, }: NetRequestParams): Promise => { // 拼接完整的URL - let fullUrl = url; + let fullUrl = url if (queryParams) { if (typeof queryParams === "string") { - fullUrl = `${url}?${queryParams}`; + fullUrl = `${url}?${queryParams}` } else { - const queryString = new URLSearchParams(queryParams).toString(); - if (queryString) fullUrl = `${url}?${queryString}`; + const queryString = new URLSearchParams(queryParams).toString() + if (queryString) fullUrl = `${url}?${queryString}` } } @@ -69,42 +69,42 @@ const netTool = async ({ const headers = { "Content-Type": "application/json", ...additionalHeaders, - }; + } // 发送请求 const res = await fetch(fullUrl, { method, body: JSON.stringify(payload), headers, - }); + }) // 获取响应数据 - let resData: any = null; - let resText: string = ""; + let resData: any = null + let resText: string = "" try { - resText = await res.text(); - resData = JSON.parse(resText); + resText = await res.text() + resData = JSON.parse(resText) } catch { /* empty */ } // 记录响应 - logResponse(res, method, headers, payload, resData || resText); + logResponse(res, method, headers, payload, resData || resText) if (!res.ok) { if (resData?.msg) { - throw new Error(resData.msg); + throw new Error(resData.msg) } if (resText) { - throw new Error(resText); + throw new Error(resText) } - throw new Error("网络响应异常"); + throw new Error("网络响应异常") } // http 错误码正常,但解析异常 if (!resData) { - throw new Error("解析响应数据异常"); + throw new Error("解析响应数据异常") } - return resData as T; -}; + return resData as T +} /** * 发送GET请求并返回一个解析为响应数据的Promise。 @@ -118,8 +118,7 @@ netTool.get = ( url: string, queryParams?: any, additionalHeaders?: any -): Promise => - netTool({ url, method: "get", queryParams, additionalHeaders }); +): Promise => netTool({ url, method: "get", queryParams, additionalHeaders }) /** * 发送POST请求并返回一个解析为响应数据的Promise。 @@ -136,7 +135,7 @@ netTool.post = ( queryParams?: any, additionalHeaders?: any ): Promise => - netTool({ url, method: "post", payload, queryParams, additionalHeaders }); + netTool({ url, method: "post", payload, queryParams, additionalHeaders }) /** * 发送PUT请求并返回一个解析为响应数据的Promise。 @@ -153,7 +152,7 @@ netTool.put = ( queryParams?: any, additionalHeaders?: any ): Promise => - netTool({ url, method: "put", payload, queryParams, additionalHeaders }); + netTool({ url, method: "put", payload, queryParams, additionalHeaders }) /** * 发送DELETE请求并返回一个解析为响应数据的Promise。 @@ -170,7 +169,7 @@ netTool.del = ( queryParams?: any, additionalHeaders?: any ): Promise => - netTool({ url, method: "delete", payload, queryParams, additionalHeaders }); + netTool({ url, method: "delete", payload, queryParams, additionalHeaders }) /** * 发送PATCH请求并返回一个解析为响应数据的Promise。 @@ -187,7 +186,7 @@ netTool.patch = ( queryParams?: any, additionalHeaders?: any ): Promise => - netTool({ url, method: "patch", payload, queryParams, additionalHeaders }); + netTool({ url, method: "patch", payload, queryParams, additionalHeaders }) /** * 创建一个表示400 Bad Request的响应对象。 @@ -197,7 +196,7 @@ netTool.patch = ( * @returns 一个表示400 Bad Request的响应对象。 */ netTool.badRequest = (msg: string, requestId?: string) => - Response.json({ code: 400, msg, requestId }, { status: 400 }); + Response.json({ code: 400, msg, requestId }, { status: 400 }) /** * 创建一个表示404 Not Found的响应对象。 @@ -207,7 +206,7 @@ netTool.badRequest = (msg: string, requestId?: string) => * @returns 一个表示404 Not Found的响应对象。 */ netTool.notFound = (msg: string, requestId?: string) => - Response.json({ code: 404, msg, requestId }, { status: 404 }); + Response.json({ code: 404, msg, requestId }, { status: 404 }) /** * 创建一个表示500 Internal Server Error的响应对象。 @@ -218,7 +217,7 @@ netTool.notFound = (msg: string, requestId?: string) => * @returns 一个表示500 Internal Server Error的响应对象。 */ netTool.serverError = (msg: string, data?: any, requestId?: string) => - Response.json({ code: 500, msg, data, requestId }, { status: 500 }); + Response.json({ code: 500, msg, data, requestId }, { status: 500 }) /** * 创建一个表示200 OK的响应对象。 @@ -228,6 +227,6 @@ netTool.serverError = (msg: string, data?: any, requestId?: string) => * @returns 一个表示200 OK的响应对象。 */ netTool.ok = (data?: any, requestId?: string) => - Response.json({ code: 0, msg: "success", data, requestId }); + Response.json({ code: 0, msg: "success", data, requestId }) -export default netTool; +export default netTool diff --git a/types/db.ts b/types/db.ts index a4df2a5..33aaa6f 100644 --- a/types/db.ts +++ b/types/db.ts @@ -1,52 +1,52 @@ -import { RecordModel } from "pocketbase"; +import { RecordModel } from "pocketbase" export namespace DB { export interface Pipeline extends RecordModel { - project_id: string; - user_id: string; - pipeline_id: number; - ref: string; - status: string; - web_url: string; + project_id: string + user_id: string + pipeline_id: number + ref: string + status: string + web_url: string // 2024-03-06 02:53:59.509Z - created_at: string; - started_at: string; - finished_at: string; - duration: number; - queued_duration: number; + created_at: string + started_at: string + finished_at: string + duration: number + queued_duration: number } export interface Project extends RecordModel { - project_id: number; - description: string; - name: string; - path_with_namespace: string; - web_url: string; - avatar_url: string; - has_new_cicd: boolean; + project_id: number + description: string + name: string + path_with_namespace: string + web_url: string + avatar_url: string + has_new_cicd: boolean } export interface User extends RecordModel { - user_id: number; - username: string; - name: string; - avatar_url: string; - web_url: string; + user_id: number + username: string + name: string + avatar_url: string + web_url: string } export interface StatisticsPerWeek extends RecordModel { - week: string; - total_count: number; - failed_count: number; - success_count: number; - success_rate: number; - duration: number; + week: string + total_count: number + failed_count: number + success_count: number + success_rate: number + duration: number } export interface StatisticsPerProj extends RecordModel { - week: string; - name: string; - duration: number; - ref: string; + week: string + name: string + duration: number + ref: string } } diff --git a/types/gitlab.ts b/types/gitlab.ts index 0b6bdc3..d93f761 100644 --- a/types/gitlab.ts +++ b/types/gitlab.ts @@ -1,59 +1,59 @@ export namespace Gitlab { export interface Error { - message: string; + message: string } export interface User { - id: number; - username: string; - name: string; - state: string; - avatar_url: string; - web_url: string; + id: number + username: string + name: string + state: string + avatar_url: string + web_url: string } export interface ProjDetail { - id: number; - description: string; - name: string; - path_with_namespace: string; - web_url: string; - avatar_url?: any; - message?: string; + id: number + description: string + name: string + path_with_namespace: string + web_url: string + avatar_url?: any + message?: string } export interface PipelineDetail { - id: number; - project_id: number; - ref: string; - status: string; - web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046"; - user: User; - started_at: string; - finished_at: string; - duration: number; - queued_duration: number; - message?: string; + id: number + project_id: number + ref: string + status: string + web_url: "https://git.n.xiaomi.com/miai-fe/fe/ai-scene-review-fe/-/pipelines/7646046" + user: User + started_at: string + finished_at: string + duration: number + queued_duration: number + message?: string } export interface Pipeline { - id: number; - project_id: number; - sha: string; - ref: string; - status: string; - source: string; - created_at: string; - updated_at: string; - web_url: string; + id: number + project_id: number + sha: string + ref: string + status: string + source: string + created_at: string + updated_at: string + web_url: string } export interface Badge { - id: number; - name: string; - link_url: string; - image_url: string; - rendered_link_url: string; - rendered_image_url: string; - kind: "project" | "group"; + id: number + name: string + link_url: string + image_url: string + rendered_link_url: string + rendered_image_url: string + kind: "project" | "group" } } diff --git a/utils/pbTools.ts b/utils/pbTools.ts index f7e8026..ce6f962 100644 --- a/utils/pbTools.ts +++ b/utils/pbTools.ts @@ -2,10 +2,10 @@ export const managePb404 = async ( dbFunc: () => Promise ): Promise => { try { - return await dbFunc(); + return await dbFunc() } catch (err: any) { if (err?.message === "The requested resource wasn't found.") { - return null; - } else throw err; + return null + } else throw err } -}; +} diff --git a/utils/robotTools.ts b/utils/robotTools.ts index 43df940..1d152d8 100644 --- a/utils/robotTools.ts +++ b/utils/robotTools.ts @@ -5,22 +5,22 @@ */ export const calculatePercentageChange = (cur: number, prev: number) => { // 计算差值 - const diff = cur - prev; + const diff = cur - prev if (diff === 0) return { diff, percentage: "0", - }; + } // 计算百分比 - const percentage = Math.abs((diff / prev) * 100).toFixed(1); + const percentage = Math.abs((diff / prev) * 100).toFixed(1) return { diff, percentage, - }; -}; + } +} /** * 计算周同比 @@ -36,7 +36,7 @@ export const calculateWeeklyRate = ( const { diff, percentage } = calculatePercentageChange( Number(cur), Number(prev) - ); + ) if (diff > 0) return { text: `${ @@ -44,7 +44,7 @@ export const calculateWeeklyRate = ( }↑${percentage}%`, diff, percentage, - }; + } if (diff < 0) return { text: `${ @@ -52,10 +52,10 @@ export const calculateWeeklyRate = ( }↓${percentage}%`, diff, percentage, - }; + } return { text: `${needCN ? "较上周 " : ""}0%`, diff, percentage, - }; -}; + } +} diff --git a/utils/timeTools.ts b/utils/timeTools.ts index cece517..dff9c61 100644 --- a/utils/timeTools.ts +++ b/utils/timeTools.ts @@ -1,22 +1,22 @@ -import moment from "moment"; +import moment from "moment" /** * 获取今天是今年的第几周,like 2024-05 */ export const getWeekTimeWithYear = () => { - return moment().format("YYYY-WW"); -}; + return moment().format("YYYY-WW") +} /** * 获取上周是今年的第几周,like 2024-04 */ export const getPrevWeekWithYear = () => { - return moment().subtract(1, "weeks").format("YYYY-WW"); -}; + return moment().subtract(1, "weeks").format("YYYY-WW") +} /** * 秒转分钟,保留一位小数 */ export const sec2min = (sec: number) => { - return (sec / 60).toFixed(1); -}; + return (sec / 60).toFixed(1) +}