feat: 抽象网络请求类 & 内容转为ctx向内传递
All checks were successful
Egg Server MIflow / build-image (push) Successful in 1m5s
All checks were successful
Egg Server MIflow / build-image (push) Successful in 1m5s
This commit is contained in:
parent
b9045bcfa1
commit
09e352a9c1
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -9,6 +9,8 @@
|
|||||||
"eamodio",
|
"eamodio",
|
||||||
"esbenp",
|
"esbenp",
|
||||||
"Gruntfuggly",
|
"Gruntfuggly",
|
||||||
|
"metas",
|
||||||
|
"mina",
|
||||||
"tseslint",
|
"tseslint",
|
||||||
"wlpbbgiky",
|
"wlpbbgiky",
|
||||||
"Yoav"
|
"Yoav"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import loggerIns from "../../log"
|
||||||
import appInfo from "../appInfo"
|
import appInfo from "../appInfo"
|
||||||
import pbClient from "../pbClient"
|
import pbClient from "../pbClient"
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ const update = async (id: string, appName: string, value: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenCache[appName] = value
|
tokenCache[appName] = value
|
||||||
console.log(`reset ${appName} access token success`, value)
|
loggerIns.info(`reset ${appName} access token success: ${value}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
28
index.ts
28
index.ts
@ -1,37 +1,41 @@
|
|||||||
|
import loggerIns from "./log"
|
||||||
import { manageBotReq } from "./routes/bot"
|
import { manageBotReq } from "./routes/bot"
|
||||||
import { manageMessageReq } from "./routes/message"
|
import { manageMessageReq } from "./routes/message"
|
||||||
import { manageMicroAppReq } from "./routes/microApp"
|
import { manageMicroAppReq } from "./routes/microApp"
|
||||||
import { manageSheetReq } from "./routes/sheet"
|
import { manageSheetReq } from "./routes/sheet"
|
||||||
import { initSchedule } from "./schedule"
|
import { initSchedule } from "./schedule"
|
||||||
import netTool from "./services/netTool"
|
import genContext from "./utils/genContext"
|
||||||
import { makeCheckPathTool } from "./utils/pathTools"
|
import { makeCheckPathTool } from "./utils/pathTools"
|
||||||
|
|
||||||
initSchedule()
|
initSchedule()
|
||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
async fetch(req) {
|
async fetch(req) {
|
||||||
|
// 生成上下文
|
||||||
|
const ctx = await genContext(req)
|
||||||
try {
|
try {
|
||||||
// 打印当前路由
|
|
||||||
console.log("🚀 ~ serve ~ req.url", req.url)
|
|
||||||
// 路由处理
|
// 路由处理
|
||||||
const { exactCheck, startsWithCheck } = makeCheckPathTool(req.url)
|
const { exactCheck, startsWithCheck, fullCheck } = makeCheckPathTool(
|
||||||
|
req.url
|
||||||
|
)
|
||||||
|
// 非根路由打印
|
||||||
|
if (!fullCheck("/")) ctx.logger.info(`${req.method} ${req.url}`)
|
||||||
// 机器人
|
// 机器人
|
||||||
if (exactCheck("/bot")) return await manageBotReq(req)
|
if (exactCheck("/bot")) return await manageBotReq(ctx)
|
||||||
// 消息代理发送
|
// 消息代理发送
|
||||||
if (exactCheck("/message")) return await manageMessageReq(req)
|
if (exactCheck("/message")) return await manageMessageReq(ctx)
|
||||||
// 表格代理操作
|
// 表格代理操作
|
||||||
if (exactCheck("/sheet")) return await manageSheetReq(req)
|
if (exactCheck("/sheet")) return await manageSheetReq(ctx)
|
||||||
// 小程序
|
// 小程序
|
||||||
if (startsWithCheck("/micro_app")) return await manageMicroAppReq(req)
|
if (startsWithCheck("/micro_app")) return await manageMicroAppReq(ctx)
|
||||||
// 其他
|
// 其他
|
||||||
return netTool.ok("hello, there is egg, glade to serve you!")
|
return ctx.genResp.ok("hello, there is egg, glade to serve you!")
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// 错误处理
|
// 错误处理
|
||||||
console.error("🚀 ~ serve ~ error", error)
|
return ctx.genResp.serverError(error.message || "server error")
|
||||||
return netTool.serverError(error.message || "server error")
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
port: 3000,
|
port: 3000,
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(`Listening on ${server.hostname}:${server.port}`)
|
loggerIns.info(`Listening on ${server.hostname}:${server.port}`)
|
||||||
|
55
log/index.ts
Normal file
55
log/index.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import "winston-daily-rotate-file"
|
||||||
|
|
||||||
|
import winston, { format } from "winston"
|
||||||
|
|
||||||
|
const isProd = process.env.NODE_ENV === "production"
|
||||||
|
|
||||||
|
const transports: any[] = [
|
||||||
|
new winston.transports.Console({
|
||||||
|
level: "info",
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (isProd) {
|
||||||
|
const config = {
|
||||||
|
datePattern: "YYYY-MM-DD",
|
||||||
|
zippedArchive: true,
|
||||||
|
maxSize: "20m",
|
||||||
|
maxFiles: "14d",
|
||||||
|
}
|
||||||
|
|
||||||
|
transports.push(
|
||||||
|
new winston.transports.DailyRotateFile({
|
||||||
|
level: "info",
|
||||||
|
filename: "/home/work/log/egg-info-%DATE%.log",
|
||||||
|
...config,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
transports.push(
|
||||||
|
new winston.transports.DailyRotateFile({
|
||||||
|
level: "debug",
|
||||||
|
filename: "/home/work/log/egg-debug-%DATE%.log",
|
||||||
|
...config,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const loggerIns = winston.createLogger({
|
||||||
|
level: "silly",
|
||||||
|
format: format.combine(
|
||||||
|
format.colorize({
|
||||||
|
level: !isProd,
|
||||||
|
}), // 开发环境下输出彩色日志
|
||||||
|
format.simple(), // 简单文本格式化
|
||||||
|
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
||||||
|
format.printf(({ level, message, timestamp, requestId }) => {
|
||||||
|
const singleLineMessage = isProd
|
||||||
|
? message.replace(/\n/g, " ") // 将换行符替换为空格
|
||||||
|
: message
|
||||||
|
return `${timestamp} [${level}]${requestId ? ` [RequestId: ${requestId}]` : ""}: ${singleLineMessage}`
|
||||||
|
})
|
||||||
|
),
|
||||||
|
transports,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default loggerIns
|
@ -20,6 +20,7 @@
|
|||||||
"@commitlint/config-conventional": "^19.2.2",
|
"@commitlint/config-conventional": "^19.2.2",
|
||||||
"@eslint/js": "^9.7.0",
|
"@eslint/js": "^9.7.0",
|
||||||
"@types/node-schedule": "^2.1.7",
|
"@types/node-schedule": "^2.1.7",
|
||||||
|
"@types/uuid": "^10.0.0",
|
||||||
"bun-types": "latest",
|
"bun-types": "latest",
|
||||||
"eslint": "^9.7.0",
|
"eslint": "^9.7.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
@ -33,6 +34,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-schedule": "^2.1.1",
|
"node-schedule": "^2.1.1",
|
||||||
"pocketbase": "^0.21.3"
|
"p-limit": "^6.1.0",
|
||||||
|
"pocketbase": "^0.21.3",
|
||||||
|
"uuid": "^10.0.0",
|
||||||
|
"winston": "^3.14.2",
|
||||||
|
"winston-daily-rotate-file": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,14 @@
|
|||||||
import { sleep } from "bun"
|
import { sleep } from "bun"
|
||||||
|
|
||||||
import service from "../../services"
|
import { Context, LarkAction } from "../../types"
|
||||||
import { LarkAction } from "../../types"
|
|
||||||
import { getActionType, getIsActionMsg } from "../../utils/msgTools"
|
import { getActionType, getIsActionMsg } from "../../utils/msgTools"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回ChatId卡片
|
* 返回ChatId卡片
|
||||||
* @param {LarkAction.Data} body
|
* @param {LarkAction.Data} body
|
||||||
|
* @returns {Promise<string>} 返回包含ChatId卡片的JSON字符串
|
||||||
*/
|
*/
|
||||||
const makeChatIdCard = async (body: LarkAction.Data) => {
|
const makeChatIdCard = async (body: LarkAction.Data): Promise<string> => {
|
||||||
await sleep(500)
|
await sleep(500)
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
type: "template",
|
type: "template",
|
||||||
@ -30,34 +30,38 @@ const ACTION_MAP = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理按钮点击事件
|
* 处理按钮点击事件
|
||||||
* @param {LarkAction.Data} body
|
* @param {Context.Data} ctx - 上下文数据,包含body, larkService和logger
|
||||||
|
* @returns {Promise<void>} 无返回值
|
||||||
*/
|
*/
|
||||||
const manageBtnClick = async (body: LarkAction.Data) => {
|
const manageBtnClick = async ({
|
||||||
|
body,
|
||||||
|
larkService,
|
||||||
|
logger,
|
||||||
|
}: Context.Data): Promise<void> => {
|
||||||
const { action } = body?.action?.value as {
|
const { action } = body?.action?.value as {
|
||||||
action: keyof typeof ACTION_MAP
|
action: keyof typeof ACTION_MAP
|
||||||
}
|
}
|
||||||
|
logger.info(`got button click action: ${action}`)
|
||||||
if (!action) return
|
if (!action) return
|
||||||
const func = ACTION_MAP[action]
|
const func = ACTION_MAP[action]
|
||||||
if (!func) return
|
if (!func) return
|
||||||
const card = await func(body)
|
const card = await func(body)
|
||||||
if (!card) return
|
if (!card) return
|
||||||
// 更新飞书的卡片
|
// 更新飞书的卡片
|
||||||
await service.lark.message.update()(body.open_message_id, card)
|
await larkService.message.update(body.open_message_id, card)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理Action消息
|
* 处理Action消息
|
||||||
* @param {LarkAction.Data} body
|
* @param {Context.Data} ctx - 上下文数据
|
||||||
* @returns {boolean} 是否在本函数中处理了消息
|
* @returns {boolean} 是否在本函数中处理了消息
|
||||||
*/
|
*/
|
||||||
export const manageActionMsg = (body: LarkAction.Data) => {
|
export const manageActionMsg = (ctx: Context.Data): boolean => {
|
||||||
// 过滤非Action消息
|
// 过滤非Action消息
|
||||||
if (!getIsActionMsg(body)) {
|
if (!getIsActionMsg(ctx.body)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const actionType = getActionType(body)
|
const actionType = getActionType(ctx.body)
|
||||||
if (actionType === "button") {
|
if (actionType === "button") manageBtnClick(ctx)
|
||||||
manageBtnClick(body)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import service from "../../services"
|
import { LarkService } from "../../services"
|
||||||
import { LarkEvent } from "../../types"
|
import { Context, LarkEvent } from "../../types"
|
||||||
import {
|
import {
|
||||||
getChatId,
|
getChatId,
|
||||||
getChatType,
|
getChatType,
|
||||||
@ -14,27 +14,32 @@ import {
|
|||||||
* @param {LarkEvent.Data} body
|
* @param {LarkEvent.Data} body
|
||||||
* @returns {boolean} 是否为P2P或者群聊并且艾特了小煎蛋
|
* @returns {boolean} 是否为P2P或者群聊并且艾特了小煎蛋
|
||||||
*/
|
*/
|
||||||
const getIsP2pOrGroupAtBot = (body: LarkEvent.Data) => {
|
const getIsP2pOrGroupAtBot = (body: LarkEvent.Data): boolean => {
|
||||||
const isP2p = getChatType(body) === "p2p"
|
const isP2p = getChatType(body) === "p2p"
|
||||||
const isAtBot = getMentions(body)?.some?.(
|
const isAtBot = getMentions(body)?.some?.(
|
||||||
(mention) => mention.name === "小煎蛋"
|
(mention) => mention.name === "小煎蛋"
|
||||||
)
|
)
|
||||||
return isP2p || isAtBot
|
return Boolean(isP2p || isAtBot)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 过滤出非法消息,如果发表情包就直接发回去
|
* 过滤出非法消息,如果发表情包就直接发回去
|
||||||
* @param {LarkEvent.Data} body
|
* @param {Context.Data} ctx - 上下文数据,包含body, logger和larkService
|
||||||
* @returns {boolean} 是否为非法消息
|
* @returns {boolean} 是否为非法消息
|
||||||
*/
|
*/
|
||||||
const filterIllegalMsg = (body: LarkEvent.Data) => {
|
const filterIllegalMsg = ({
|
||||||
|
body,
|
||||||
|
logger,
|
||||||
|
larkService,
|
||||||
|
}: Context.Data): boolean => {
|
||||||
// 没有chatId的消息不处理
|
// 没有chatId的消息不处理
|
||||||
const chatId = getChatId(body)
|
const chatId = getChatId(body)
|
||||||
|
logger.debug(`bot req chatId: ${chatId}`)
|
||||||
if (!chatId) return true
|
if (!chatId) return true
|
||||||
|
|
||||||
// 获取msgType
|
// 获取msgType
|
||||||
const msgType = getMsgType(body)
|
const msgType = getMsgType(body)
|
||||||
|
logger.debug(`bot req msgType: ${msgType}`)
|
||||||
// 放行纯文本消息
|
// 放行纯文本消息
|
||||||
if (msgType === "text") {
|
if (msgType === "text") {
|
||||||
// 过滤艾特全体成员的消息
|
// 过滤艾特全体成员的消息
|
||||||
@ -47,16 +52,18 @@ const filterIllegalMsg = (body: LarkEvent.Data) => {
|
|||||||
|
|
||||||
// 发表情包就直接发回去
|
// 发表情包就直接发回去
|
||||||
if (msgType === "sticker") {
|
if (msgType === "sticker") {
|
||||||
|
logger.info(`got a sticker message, chatId: ${chatId}`)
|
||||||
const content = body?.event?.message?.content
|
const content = body?.event?.message?.content
|
||||||
service.lark.message.send()("chat_id", chatId, "sticker", content)
|
larkService.message.send("chat_id", chatId, "sticker", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非表情包只在私聊或者群聊中艾特小煎蛋时才回复
|
// 非表情包只在私聊或者群聊中艾特小煎蛋时才回复
|
||||||
else if (getIsP2pOrGroupAtBot(body)) {
|
else if (getIsP2pOrGroupAtBot(body)) {
|
||||||
|
logger.info(`got a illegal message, chatId: ${chatId}`)
|
||||||
const content = JSON.stringify({
|
const content = JSON.stringify({
|
||||||
text: "哇!这是什么东东?我只懂普通文本啦![可爱]",
|
text: "哇!这是什么东东?我只懂普通文本啦![可爱]",
|
||||||
})
|
})
|
||||||
service.lark.message.send()("chat_id", chatId, "text", content)
|
larkService.message.send("chat_id", chatId, "text", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非纯文本,全不放行
|
// 非纯文本,全不放行
|
||||||
@ -65,9 +72,10 @@ const filterIllegalMsg = (body: LarkEvent.Data) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送ID消息
|
* 发送ID消息
|
||||||
* @param chatId - 发送消息的chatId
|
* @param {string} chatId - 发送消息的chatId
|
||||||
|
* @param {LarkService} service - Lark服务实例
|
||||||
*/
|
*/
|
||||||
const manageIdMsg = async (chatId: string) => {
|
const manageIdMsg = (chatId: string, service: LarkService): void => {
|
||||||
const content = JSON.stringify({
|
const content = JSON.stringify({
|
||||||
type: "template",
|
type: "template",
|
||||||
data: {
|
data: {
|
||||||
@ -80,33 +88,46 @@ const manageIdMsg = async (chatId: string) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
service.lark.message.send()("chat_id", chatId, "interactive", content)
|
service.message.send("chat_id", chatId, "interactive", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理命令消息
|
* 处理命令消息
|
||||||
* @param body - 消息体
|
* @param {Context.Data} ctx - 上下文数据,包含body, logger, larkService和attachService
|
||||||
* @returns
|
* @returns {boolean} 是否处理了命令消息
|
||||||
*/
|
*/
|
||||||
const manageCMDMsg = (body: LarkEvent.Data) => {
|
const manageCMDMsg = ({
|
||||||
|
body,
|
||||||
|
logger,
|
||||||
|
larkService,
|
||||||
|
attachService,
|
||||||
|
}: Context.Data): boolean => {
|
||||||
const text = getMsgText(body)
|
const text = getMsgText(body)
|
||||||
console.log("🚀 ~ manageCMDMsg ~ text:", text)
|
logger.debug(`bot req text: ${text}`)
|
||||||
const chatId = getChatId(body)
|
const chatId = getChatId(body)
|
||||||
|
// 处理命令消息
|
||||||
if (text.trim() === "/id") {
|
if (text.trim() === "/id") {
|
||||||
manageIdMsg(chatId)
|
logger.info(`bot command is /id, chatId: ${chatId}`)
|
||||||
|
manageIdMsg(chatId, larkService)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// CI监控
|
||||||
if (text.trim() === "/ci") {
|
if (text.trim() === "/ci") {
|
||||||
service.attach.ciMonitor(chatId)
|
logger.info(`bot command is /ci, chatId: ${chatId}`)
|
||||||
|
attachService.ciMonitor(chatId)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// 简报
|
||||||
if (text.includes("share") && text.includes("简报")) {
|
if (text.includes("share") && text.includes("简报")) {
|
||||||
service.attach.reportCollector(body)
|
logger.info(`bot command is share report, chatId: ${chatId}`)
|
||||||
// 这个用时比较久,先发一条提醒用户收到了请求
|
// 这个用时比较久,先发一条提醒用户收到了请求
|
||||||
const content = JSON.stringify({
|
larkService.message.send(
|
||||||
text: "正在为您收集简报,请稍等片刻~",
|
"chat_id",
|
||||||
})
|
chatId,
|
||||||
service.lark.message.send()("chat_id", chatId, "text", content)
|
"text",
|
||||||
|
"正在为您收集简报,请稍等片刻~"
|
||||||
|
)
|
||||||
|
attachService.reportCollector(body)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@ -114,10 +135,11 @@ const manageCMDMsg = (body: LarkEvent.Data) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 回复引导消息
|
* 回复引导消息
|
||||||
* @param {LarkEvent.Data} body
|
* @param {Context.Data} ctx - 上下文数据,包含body, larkService和logger
|
||||||
*/
|
*/
|
||||||
const replyGuideMsg = async (body: LarkEvent.Data) => {
|
const replyGuideMsg = ({ body, larkService, logger }: Context.Data): void => {
|
||||||
const chatId = getChatId(body)
|
const chatId = getChatId(body)
|
||||||
|
logger.info(`reply guide message, chatId: ${chatId}`)
|
||||||
const content = JSON.stringify({
|
const content = JSON.stringify({
|
||||||
type: "template",
|
type: "template",
|
||||||
data: {
|
data: {
|
||||||
@ -131,28 +153,28 @@ const replyGuideMsg = async (body: LarkEvent.Data) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await service.lark.message.send()("chat_id", chatId, "interactive", content)
|
larkService.message.send("chat_id", chatId, "interactive", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理Event消息
|
* 处理Event消息
|
||||||
* @param {LarkUserAction} body
|
* @param {Context.Data} ctx - 上下文数据
|
||||||
* @returns {boolean} 是否在本函数中处理了消息
|
* @returns {boolean} 是否在本函数中处理了消息
|
||||||
*/
|
*/
|
||||||
export const manageEventMsg = (body: LarkEvent.Data) => {
|
export const manageEventMsg = (ctx: Context.Data): boolean => {
|
||||||
// 过滤非Event消息
|
// 过滤非Event消息
|
||||||
if (!getIsEventMsg(body)) {
|
if (!getIsEventMsg(ctx.body)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// 过滤非法消息
|
// 过滤非法消息
|
||||||
if (filterIllegalMsg(body)) {
|
if (filterIllegalMsg(ctx)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 处理命令消息
|
// 处理命令消息
|
||||||
if (manageCMDMsg(body)) {
|
if (manageCMDMsg(ctx)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// 返回引导消息
|
// 返回引导消息
|
||||||
replyGuideMsg(body)
|
replyGuideMsg(ctx)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
import netTool from "../../services/netTool"
|
import { Context } from "../../types"
|
||||||
import { manageActionMsg } from "./actionMsg"
|
import { manageActionMsg } from "./actionMsg"
|
||||||
import { manageEventMsg } from "./eventMsg"
|
import { manageEventMsg } from "./eventMsg"
|
||||||
|
|
||||||
export const manageBotReq = async (req: Request) => {
|
/**
|
||||||
const body = (await req.json()) as any
|
* 处理机器人请求
|
||||||
console.log("🚀 ~ manageBotReq ~ body:", body)
|
* @param {Context.Data} ctx - 上下文数据,包含请求体、日志记录器和响应生成器
|
||||||
// 验证机器人
|
* @returns {Promise<Response>} 返回响应对象
|
||||||
if (body?.type === "url_verification") {
|
*/
|
||||||
return Response.json({ challenge: body?.challenge })
|
export const manageBotReq = async (ctx: Context.Data): Promise<Response> => {
|
||||||
|
const { body } = ctx
|
||||||
|
|
||||||
|
// 检查请求体是否为空
|
||||||
|
if (!body) {
|
||||||
|
return ctx.genResp.badRequest("bot req body is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证机器人
|
||||||
|
if (body.type === "url_verification") {
|
||||||
|
ctx.logger.info(`bot challenge: ${body.challenge}`)
|
||||||
|
return Response.json({ challenge: body.challenge })
|
||||||
|
}
|
||||||
|
|
||||||
// 处理Event消息
|
// 处理Event消息
|
||||||
if (manageEventMsg(body)) return netTool.ok()
|
if (manageEventMsg(ctx)) return ctx.genResp.ok()
|
||||||
|
|
||||||
// 处理Action消息
|
// 处理Action消息
|
||||||
if (manageActionMsg(body)) return netTool.ok()
|
if (manageActionMsg(ctx)) return ctx.genResp.ok()
|
||||||
// 其他
|
|
||||||
return netTool.ok()
|
// 其他情况,返回成功响应
|
||||||
|
return ctx.genResp.ok()
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,49 @@
|
|||||||
import db from "../../db"
|
import db from "../../db"
|
||||||
import service from "../../services"
|
import { Context, DB, LarkServer, MsgProxy } from "../../types"
|
||||||
import netTool from "../../services/netTool"
|
|
||||||
import { DB, LarkServer, MsgProxy } from "../../types"
|
|
||||||
import { safeJsonStringify } from "../../utils/pathTools"
|
import { safeJsonStringify } from "../../utils/pathTools"
|
||||||
|
|
||||||
const LOG_COLLECTION = "message_log"
|
const LOG_COLLECTION = "message_log"
|
||||||
|
|
||||||
const validateMessageReq = (body: MsgProxy.Body) => {
|
/**
|
||||||
|
* 校验消息请求的参数
|
||||||
|
* @param {Context.Data} ctx - 上下文数据,包含请求体和响应生成器
|
||||||
|
* @returns {false | Response} 如果校验失败,返回响应对象;否则返回 false
|
||||||
|
*/
|
||||||
|
const validateMessageReq = ({
|
||||||
|
body,
|
||||||
|
genResp,
|
||||||
|
}: Context.Data): false | Response => {
|
||||||
if (!body.api_key) {
|
if (!body.api_key) {
|
||||||
return netTool.badRequest("api_key is required")
|
return genResp.badRequest("api_key is required")
|
||||||
}
|
}
|
||||||
if (!body.group_id && !body.receive_id) {
|
if (!body.group_id && !body.receive_id) {
|
||||||
return netTool.badRequest("group_id or receive_id is required")
|
return genResp.badRequest("group_id or receive_id is required")
|
||||||
}
|
}
|
||||||
if (body.receive_id && !body.receive_id_type) {
|
if (body.receive_id && !body.receive_id_type) {
|
||||||
return netTool.badRequest("receive_id_type is required")
|
return genResp.badRequest("receive_id_type is required")
|
||||||
}
|
}
|
||||||
if (!body.msg_type) {
|
if (!body.msg_type) {
|
||||||
return netTool.badRequest("msg_type is required")
|
return genResp.badRequest("msg_type is required")
|
||||||
}
|
}
|
||||||
if (!body.content) {
|
if (!body.content) {
|
||||||
return netTool.badRequest("content is required")
|
return genResp.badRequest("content is required")
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export const manageMessageReq = async (req: Request) => {
|
/**
|
||||||
const body = (await req.json()) as MsgProxy.Body
|
* 处理消息请求
|
||||||
|
* @param {Context.Data} ctx - 上下文数据,包含请求体、日志记录器、响应生成器和 Lark 服务
|
||||||
|
* @returns {Promise<Response>} 返回响应对象
|
||||||
|
*/
|
||||||
|
export const manageMessageReq = async (
|
||||||
|
ctx: Context.Data
|
||||||
|
): Promise<Response> => {
|
||||||
|
const { body: rawBody, genResp, larkService } = ctx
|
||||||
|
const body = rawBody as MsgProxy.Body
|
||||||
|
|
||||||
// 校验参数
|
// 校验参数
|
||||||
const validateRes = validateMessageReq(body)
|
const validateRes = validateMessageReq(ctx)
|
||||||
if (validateRes) return validateRes
|
if (validateRes) return validateRes
|
||||||
|
|
||||||
// 处理消息内容
|
// 处理消息内容
|
||||||
@ -37,7 +52,7 @@ export const manageMessageReq = async (req: Request) => {
|
|||||||
? safeJsonStringify(body.content)
|
? safeJsonStringify(body.content)
|
||||||
: body.content
|
: body.content
|
||||||
|
|
||||||
// 遍历所有id发送消息,保存所有对应的messageId
|
// 初始化发送结果对象
|
||||||
const sendRes = {
|
const sendRes = {
|
||||||
chat_id: {} as Record<string, any>,
|
chat_id: {} as Record<string, any>,
|
||||||
open_id: {} as Record<string, any>,
|
open_id: {} as Record<string, any>,
|
||||||
@ -55,30 +70,30 @@ export const manageMessageReq = async (req: Request) => {
|
|||||||
final_content: finalContent,
|
final_content: finalContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验api_key
|
// 校验 api_key
|
||||||
const apiKeyInfo = await db.apiKey.getOne(body.api_key)
|
const apiKeyInfo = await db.apiKey.getOne(body.api_key)
|
||||||
if (!apiKeyInfo) {
|
if (!apiKeyInfo) {
|
||||||
const error = "api key not found"
|
const error = "api key not found"
|
||||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||||
return netTool.notFound(error)
|
return genResp.notFound(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取app name
|
// 获取 app name
|
||||||
const appName = apiKeyInfo.expand?.app?.name
|
const appName = apiKeyInfo.expand?.app?.name
|
||||||
if (!appName) {
|
if (!appName) {
|
||||||
const error = "app name not found"
|
const error = "app name not found"
|
||||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||||
return netTool.notFound(error)
|
return genResp.notFound(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有group_id,则发送给所有group_id中的人
|
// 如果有 group_id,则发送给所有 group_id 中的人
|
||||||
if (body.group_id) {
|
if (body.group_id) {
|
||||||
// 获取所有接收者
|
// 获取所有接收者
|
||||||
const group = await db.messageGroup.getOne(body.group_id!)
|
const group = await db.messageGroup.getOne(body.group_id!)
|
||||||
if (!group) {
|
if (!group) {
|
||||||
const error = "message group not found"
|
const error = "message group not found"
|
||||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||||
return netTool.notFound(error)
|
return genResp.notFound(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { chat_id, open_id, union_id, user_id, email } = group
|
const { chat_id, open_id, union_id, user_id, email } = group
|
||||||
@ -87,8 +102,9 @@ export const manageMessageReq = async (req: Request) => {
|
|||||||
const makeSendFunc = (receive_id_type: LarkServer.ReceiveIDType) => {
|
const makeSendFunc = (receive_id_type: LarkServer.ReceiveIDType) => {
|
||||||
return (receive_id: string) => {
|
return (receive_id: string) => {
|
||||||
sendList.push(
|
sendList.push(
|
||||||
service.lark.message
|
larkService
|
||||||
.send(appName)(
|
.child(appName)
|
||||||
|
.message.send(
|
||||||
receive_id_type,
|
receive_id_type,
|
||||||
receive_id,
|
receive_id,
|
||||||
body.msg_type,
|
body.msg_type,
|
||||||
@ -109,12 +125,13 @@ export const manageMessageReq = async (req: Request) => {
|
|||||||
if (email) email.map(makeSendFunc("email"))
|
if (email) email.map(makeSendFunc("email"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有receive_id,则发送给所有receive_id中的人
|
// 如果有 receive_id,则发送给所有 receive_id 中的人
|
||||||
if (body.receive_id && body.receive_id_type) {
|
if (body.receive_id && body.receive_id_type) {
|
||||||
body.receive_id.split(",").forEach((receive_id) => {
|
body.receive_id.split(",").forEach((receive_id) => {
|
||||||
sendList.push(
|
sendList.push(
|
||||||
service.lark.message
|
larkService
|
||||||
.send(appName)(
|
.child(appName)
|
||||||
|
.message.send(
|
||||||
body.receive_id_type,
|
body.receive_id_type,
|
||||||
receive_id,
|
receive_id,
|
||||||
body.msg_type,
|
body.msg_type,
|
||||||
@ -128,14 +145,14 @@ export const manageMessageReq = async (req: Request) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 里边有错误处理,这里不用担心执行不完
|
// 发送消息
|
||||||
await Promise.all(sendList)
|
await Promise.allSettled(sendList)
|
||||||
// 保存消息记录
|
// 保存消息记录
|
||||||
db.log.create(LOG_COLLECTION, { ...baseLog, send_result: sendRes })
|
db.log.create(LOG_COLLECTION, { ...baseLog, send_result: sendRes })
|
||||||
return netTool.ok(sendRes)
|
return genResp.ok(sendRes)
|
||||||
} catch {
|
} catch {
|
||||||
const error = "send msg failed"
|
const error = "send msg failed"
|
||||||
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
db.log.create(LOG_COLLECTION, { ...baseLog, error })
|
||||||
return netTool.serverError(error, sendRes)
|
return genResp.serverError(error, sendRes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import service from "../../services"
|
import { Context } from "../../types"
|
||||||
import netTool from "../../services/netTool"
|
|
||||||
import { makeCheckPathTool } from "../../utils/pathTools"
|
import { makeCheckPathTool } from "../../utils/pathTools"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,34 +6,28 @@ import { makeCheckPathTool } from "../../utils/pathTools"
|
|||||||
* @param req
|
* @param req
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const manageLogin = async (req: Request) => {
|
const manageLogin = async (ctx: Context.Data) => {
|
||||||
|
const { req, larkService, genResp, logger } = ctx
|
||||||
|
logger.info("micro app login")
|
||||||
const url = new URL(req.url)
|
const url = new URL(req.url)
|
||||||
const code = url.searchParams.get("code")
|
const code = url.searchParams.get("code")
|
||||||
const appName = url.searchParams.get("app_name") || undefined
|
const appName = url.searchParams.get("app_name") || undefined
|
||||||
if (!code) {
|
if (!code) {
|
||||||
return netTool.badRequest("code not found")
|
return genResp.badRequest("code not found")
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
code: resCode,
|
code: resCode,
|
||||||
data,
|
data,
|
||||||
msg,
|
message,
|
||||||
} = await service.lark.user.code2Login(appName)(code)
|
} = await larkService.child(appName).user.code2Login(code)
|
||||||
|
|
||||||
console.log("🚀 ~ manageLogin:", resCode, data, msg)
|
logger.debug(`get user session: ${JSON.stringify(data)}`)
|
||||||
|
|
||||||
if (resCode !== 0) {
|
if (resCode !== 0) {
|
||||||
return Response.json({
|
return genResp.serverError(message)
|
||||||
code: resCode,
|
|
||||||
message: msg,
|
|
||||||
data: null,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.json({
|
return genResp.ok(data)
|
||||||
code: 0,
|
|
||||||
message: "success",
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,35 +35,29 @@ const manageLogin = async (req: Request) => {
|
|||||||
* @param req
|
* @param req
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const manageBatchUser = async (req: Request) => {
|
const manageBatchUser = async (ctx: Context.Data) => {
|
||||||
const body = (await req.json()) as any
|
const { body, genResp, larkService, logger } = ctx
|
||||||
console.log("🚀 ~ manageBatchUser ~ body:", body)
|
logger.info("batch get user info")
|
||||||
|
if (!body) return genResp.badRequest("req body is empty")
|
||||||
const { user_ids, user_id_type, app_name } = body
|
const { user_ids, user_id_type, app_name } = body
|
||||||
if (!user_ids) {
|
if (!user_ids) {
|
||||||
return netTool.badRequest("user_ids not found")
|
return genResp.badRequest("user_ids not found")
|
||||||
}
|
}
|
||||||
if (!user_id_type) {
|
if (!user_id_type) {
|
||||||
return netTool.badRequest("user_id_type not found")
|
return genResp.badRequest("user_id_type not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
const { code, data, msg } = await service.lark.user.batchGet(app_name)(
|
const { code, data, message } = await larkService
|
||||||
user_ids,
|
.child(app_name)
|
||||||
user_id_type
|
.user.batchGet(user_ids, user_id_type)
|
||||||
)
|
|
||||||
|
logger.debug(`batch get user info: ${JSON.stringify(data)}`)
|
||||||
|
|
||||||
console.log("🚀 ~ manageBatchUser:", code, data, msg)
|
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
return Response.json({
|
return genResp.serverError(message)
|
||||||
code,
|
|
||||||
message: msg,
|
|
||||||
data: null,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return Response.json({
|
|
||||||
code,
|
return genResp.ok(data)
|
||||||
message: "success",
|
|
||||||
data: data.items,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,15 +65,15 @@ const manageBatchUser = async (req: Request) => {
|
|||||||
* @param req
|
* @param req
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const manageMicroAppReq = async (req: Request) => {
|
export const manageMicroAppReq = async (ctx: Context.Data) => {
|
||||||
const { exactCheck } = makeCheckPathTool(req.url, "/micro_app")
|
const { exactCheck } = makeCheckPathTool(ctx.req.url, "/micro_app")
|
||||||
// 处理登录请求
|
// 处理登录请求
|
||||||
if (exactCheck("/login")) {
|
if (exactCheck("/login")) {
|
||||||
return manageLogin(req)
|
return manageLogin(ctx)
|
||||||
}
|
}
|
||||||
// 处理批量获取用户信息请求
|
// 处理批量获取用户信息请求
|
||||||
if (exactCheck("/batch_user")) {
|
if (exactCheck("/batch_user")) {
|
||||||
return manageBatchUser(req)
|
return manageBatchUser(ctx)
|
||||||
}
|
}
|
||||||
return netTool.ok()
|
return ctx.genResp.ok()
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,72 @@
|
|||||||
import db from "../../db"
|
import db from "../../db"
|
||||||
import service from "../../services"
|
import { Context } from "../../types"
|
||||||
import netTool from "../../services/netTool"
|
|
||||||
import { SheetProxy } from "../../types/sheetProxy"
|
import { SheetProxy } from "../../types/sheetProxy"
|
||||||
|
|
||||||
const validateSheetReq = async (body: SheetProxy.Body) => {
|
/**
|
||||||
|
* 校验表格请求的参数
|
||||||
|
* @param {Context.Data} ctx - 上下文数据,包含请求体和响应生成器
|
||||||
|
* @returns {Promise<false | Response>} 如果校验失败,返回响应对象;否则返回 false
|
||||||
|
*/
|
||||||
|
const validateSheetReq = async (
|
||||||
|
ctx: Context.Data
|
||||||
|
): Promise<false | Response> => {
|
||||||
|
const { body, genResp } = ctx
|
||||||
if (!body.api_key) {
|
if (!body.api_key) {
|
||||||
return netTool.badRequest("api_key is required")
|
return genResp.badRequest("api_key is required")
|
||||||
}
|
}
|
||||||
if (!body.sheet_token) {
|
if (!body.sheet_token) {
|
||||||
return netTool.badRequest("sheet_token is required")
|
return genResp.badRequest("sheet_token is required")
|
||||||
}
|
}
|
||||||
if (!body.range) {
|
if (!body.range) {
|
||||||
return netTool.badRequest("range is required")
|
return genResp.badRequest("range is required")
|
||||||
}
|
}
|
||||||
if (!body.values) {
|
if (!body.values) {
|
||||||
return netTool.badRequest("values is required")
|
return genResp.badRequest("values is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SheetProxy.isType(body.type)) {
|
if (!SheetProxy.isType(body.type)) {
|
||||||
return netTool.badRequest("type is invalid")
|
return genResp.badRequest("type is invalid")
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export const manageSheetReq = async (req: Request) => {
|
/**
|
||||||
const body = (await req.json()) as SheetProxy.Body
|
* 处理表格请求
|
||||||
|
* @param {Context.Data} ctx - 上下文数据,包含请求体、响应生成器和 Lark 服务
|
||||||
|
* @returns {Promise<Response>} 返回响应对象
|
||||||
|
*/
|
||||||
|
export const manageSheetReq = async (ctx: Context.Data): Promise<Response> => {
|
||||||
|
const { body: rawBody, genResp, larkService } = ctx
|
||||||
|
const body = rawBody as SheetProxy.Body
|
||||||
|
|
||||||
// 校验参数
|
// 校验参数
|
||||||
const validateRes = await validateSheetReq(body)
|
const validateRes = await validateSheetReq(ctx)
|
||||||
if (validateRes) return validateRes
|
if (validateRes) return validateRes
|
||||||
|
|
||||||
// 校验api_key
|
// 校验 api_key
|
||||||
const apiKeyInfo = await db.apiKey.getOne(body.api_key)
|
const apiKeyInfo = await db.apiKey.getOne(body.api_key)
|
||||||
if (!apiKeyInfo) {
|
if (!apiKeyInfo) {
|
||||||
return netTool.notFound("api key not found")
|
return genResp.notFound("api key not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 app name
|
// 获取 app name
|
||||||
const appName = apiKeyInfo.expand?.app?.name
|
const appName = apiKeyInfo.expand?.app?.name
|
||||||
if (!appName) {
|
if (!appName) {
|
||||||
return netTool.notFound("app name not found")
|
return genResp.notFound("app name not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.type === "insert") {
|
if (body.type === "insert") {
|
||||||
// 插入行
|
// 插入行
|
||||||
const insertRes = await service.lark.sheet.insertRows(appName)(
|
const insertRes = await larkService
|
||||||
body.sheet_token,
|
.child(appName)
|
||||||
body.range,
|
.sheet.insertRows(body.sheet_token, body.range, body.values)
|
||||||
body.values
|
|
||||||
)
|
|
||||||
if (insertRes?.code !== 0) {
|
if (insertRes?.code !== 0) {
|
||||||
return netTool.serverError(insertRes?.msg, insertRes?.data)
|
return genResp.serverError(insertRes?.message)
|
||||||
}
|
}
|
||||||
// 返回
|
// 返回插入结果
|
||||||
return netTool.ok(insertRes?.data)
|
return genResp.ok(insertRes?.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return netTool.ok()
|
// 默认返回成功响应
|
||||||
|
return genResp.ok()
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,30 @@
|
|||||||
import db from "../db"
|
import pLimit from "p-limit"
|
||||||
import netTool from "../services/netTool"
|
|
||||||
|
|
||||||
const URL =
|
import db from "../db"
|
||||||
"https://open.f.mioffice.cn/open-apis/auth/v3/tenant_access_token/internal"
|
import loggerIns from "../log"
|
||||||
|
import { LarkService } from "../services"
|
||||||
|
|
||||||
export const resetAccessToken = async () => {
|
export const resetAccessToken = async () => {
|
||||||
try {
|
try {
|
||||||
const appList = await db.appInfo.getFullList()
|
const appList = await db.appInfo.getFullList()
|
||||||
for (const app of appList) {
|
const limit = pLimit(3)
|
||||||
const { tenant_access_token } = await netTool.post(URL, {
|
const service = new LarkService("", "schedule")
|
||||||
app_id: app.app_id,
|
const promiseList = appList.map((app) =>
|
||||||
app_secret: app.app_secret,
|
limit(() =>
|
||||||
})
|
service.auth.getAk(app.app_id, app.app_secret).then((res) => {
|
||||||
await db.tenantAccessToken.update(app.id, app.name, tenant_access_token)
|
if (res.code !== 0) return
|
||||||
}
|
return db.tenantAccessToken.update(
|
||||||
} catch (error) {
|
app.id,
|
||||||
console.error("🚀 ~ resetAccessToken ~ error", error)
|
app.name,
|
||||||
|
res.tenant_access_token
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await Promise.allSettled(promiseList)
|
||||||
|
} catch (error: any) {
|
||||||
|
loggerIns
|
||||||
|
.child({ requestId: "schedule" })
|
||||||
|
.error(`resetAccessToken error: ${error.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,26 @@
|
|||||||
import { LarkEvent } from "../../types"
|
import { LarkEvent } from "../../types"
|
||||||
import netTool from "../netTool"
|
import { NetToolBase } from "../../utils/netTool"
|
||||||
|
|
||||||
/**
|
class AttachService extends NetToolBase {
|
||||||
* 请求 CI 监控
|
/**
|
||||||
*/
|
* 监控CI状态
|
||||||
const ciMonitor = async (chat_id: string) => {
|
* @param {string} chat_id - 聊天ID。
|
||||||
const URL = `https://ci-monitor.xiaomiwh.cn/gitlab/ci?chat_id=${chat_id}`
|
* @returns {Promise<string>} 返回CI监控结果。
|
||||||
try {
|
*/
|
||||||
const res = await netTool.get(URL)
|
async ciMonitor(chat_id: string) {
|
||||||
return (res as string) || ""
|
const URL = `https://ci-monitor.xiaomiwh.cn/gitlab/ci?chat_id=${chat_id}`
|
||||||
} catch {
|
return this.get(URL).catch(() => "")
|
||||||
return ""
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集报告数据
|
||||||
|
* @param {LarkEvent.Data} body - 报告数据。
|
||||||
|
* @returns {Promise<string>} 返回报告收集结果。
|
||||||
|
*/
|
||||||
|
async reportCollector(body: LarkEvent.Data) {
|
||||||
|
const URL = "https://report.imoaix.cn/report"
|
||||||
|
return this.post(URL, body).catch(() => "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export default AttachService
|
||||||
* 请求简报收集器
|
|
||||||
* @param body
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const reportCollector = async (body: LarkEvent.Data) => {
|
|
||||||
const URL = "https://report.imoaix.cn/report"
|
|
||||||
try {
|
|
||||||
const res = await netTool.post(URL, body)
|
|
||||||
return (res as string) || ""
|
|
||||||
} catch {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const attach = {
|
|
||||||
ciMonitor,
|
|
||||||
reportCollector,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default attach
|
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import attach from "./attach"
|
import AttachService from "./attach"
|
||||||
import lark from "./lark"
|
import LarkService from "./lark"
|
||||||
|
|
||||||
const service = {
|
export { AttachService, LarkService }
|
||||||
attach,
|
|
||||||
lark,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default service
|
|
||||||
|
15
services/lark/auth.ts
Normal file
15
services/lark/auth.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import LarkBaseService from "./base"
|
||||||
|
|
||||||
|
class LarkAuthService extends LarkBaseService {
|
||||||
|
getAk(app_id: string, app_secret: string) {
|
||||||
|
return this.post<{ tenant_access_token: string; code: number }>(
|
||||||
|
"/auth/v3/tenant_access_token/internal",
|
||||||
|
{
|
||||||
|
app_id,
|
||||||
|
app_secret,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LarkAuthService
|
28
services/lark/base.ts
Normal file
28
services/lark/base.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import db from "../../db"
|
||||||
|
import { NetError, NetToolBase } from "../../utils/netTool"
|
||||||
|
|
||||||
|
class LarkBaseService extends NetToolBase {
|
||||||
|
constructor(appName: string, requestId: string) {
|
||||||
|
super({
|
||||||
|
prefix: "https://open.f.mioffice.cn/open-apis",
|
||||||
|
requestId,
|
||||||
|
getHeaders: async () => ({
|
||||||
|
Authorization: `Bearer ${await db.tenantAccessToken.get(appName)}`,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async request<T = any>(params: any): Promise<T> {
|
||||||
|
return super.request<T>(params).catch((error: NetError) => {
|
||||||
|
const res = {
|
||||||
|
code: error.code,
|
||||||
|
data: null,
|
||||||
|
message: error.message,
|
||||||
|
} as T
|
||||||
|
this.logger.error("larkNetTool catch error: ", JSON.stringify(res))
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LarkBaseService
|
@ -1,11 +1,21 @@
|
|||||||
import { LarkServer } from "../../types"
|
import { LarkServer } from "../../types"
|
||||||
import larkNetTool from "./larkNetTool"
|
import LarkBaseService from "./base"
|
||||||
|
|
||||||
const batchGetMeta =
|
class LarkDriveService extends LarkBaseService {
|
||||||
(appName?: string) =>
|
/**
|
||||||
async (docTokens: string[], doc_type = "doc", user_id_type = "user_id") => {
|
* 批量获取文档元数据。
|
||||||
const URL =
|
*
|
||||||
"https://open.f.mioffice.cn/open-apis/drive/v1/metas/batch_query"
|
* @param {string[]} docTokens - 文档令牌数组。
|
||||||
|
* @param {string} [doc_type="doc"] - 文档类型,默认为 "doc"。
|
||||||
|
* @param {string} [user_id_type="user_id"] - 用户ID类型,默认为 "user_id"。
|
||||||
|
* @returns {Promise<{ code: number, data: { metas: any[], failed_list: any[] }, message: string }>} 包含元数据和失败列表的响应对象。
|
||||||
|
*/
|
||||||
|
async batchGetMeta(
|
||||||
|
docTokens: string[],
|
||||||
|
doc_type = "doc",
|
||||||
|
user_id_type = "user_id"
|
||||||
|
) {
|
||||||
|
const path = "/drive/v1/metas/batch_query"
|
||||||
// 如果docTokens长度超出150,需要分批请求
|
// 如果docTokens长度超出150,需要分批请求
|
||||||
const docTokensLen = docTokens.length
|
const docTokensLen = docTokens.length
|
||||||
const maxLen = 150
|
const maxLen = 150
|
||||||
@ -20,13 +30,9 @@ const batchGetMeta =
|
|||||||
doc_type,
|
doc_type,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
return larkNetTool.post(appName)<LarkServer.BatchDocMetaRes>(
|
return this.post<LarkServer.BatchDocMetaRes>(path, data, {
|
||||||
URL,
|
user_id_type,
|
||||||
data,
|
})
|
||||||
{
|
|
||||||
user_id_type,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const responses = await Promise.all(requestMap)
|
const responses = await Promise.all(requestMap)
|
||||||
@ -44,12 +50,9 @@ const batchGetMeta =
|
|||||||
metas,
|
metas,
|
||||||
failed_list,
|
failed_list,
|
||||||
},
|
},
|
||||||
msg: "success",
|
message: "success",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const drive = {
|
|
||||||
batchGetMeta,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default drive
|
export default LarkDriveService
|
||||||
|
@ -1,13 +1,30 @@
|
|||||||
import drive from "./drive"
|
import LarkAuthService from "./auth"
|
||||||
import message from "./message"
|
import LarkDriveService from "./drive"
|
||||||
import sheet from "./sheet"
|
import LarkMessageService from "./message"
|
||||||
import user from "./user"
|
import LarkSheetService from "./sheet"
|
||||||
|
import LarkUserService from "./user"
|
||||||
|
|
||||||
const lark = {
|
class LarkService {
|
||||||
message,
|
drive: LarkDriveService
|
||||||
user,
|
message: LarkMessageService
|
||||||
drive,
|
user: LarkUserService
|
||||||
sheet,
|
sheet: LarkSheetService
|
||||||
|
auth: LarkAuthService
|
||||||
|
requestId: string
|
||||||
|
|
||||||
|
constructor(appName: string, requestId: string) {
|
||||||
|
this.drive = new LarkDriveService(appName, requestId)
|
||||||
|
this.message = new LarkMessageService(appName, requestId)
|
||||||
|
this.user = new LarkUserService(appName, requestId)
|
||||||
|
this.sheet = new LarkSheetService(appName, requestId)
|
||||||
|
this.auth = new LarkAuthService(appName, requestId)
|
||||||
|
this.requestId = requestId
|
||||||
|
}
|
||||||
|
|
||||||
|
child(appName?: string) {
|
||||||
|
if (!appName) return this
|
||||||
|
return new LarkService(appName, this.requestId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default lark
|
export default LarkService
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
import db from "../../db"
|
|
||||||
import { LarkServer } from "../../types"
|
|
||||||
import netTool from "../netTool"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送网络请求并返回一个解析为响应数据的Promise。
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param method - 请求使用的HTTP方法。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param payload - 请求的有效负载数据。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @param appName - 应用名称,用于获取授权令牌。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
* @throws 如果网络响应不成功或存在解析错误,则抛出错误。
|
|
||||||
*/
|
|
||||||
const larkNetTool = async <T = LarkServer.BaseRes>({
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
queryParams,
|
|
||||||
payload,
|
|
||||||
additionalHeaders,
|
|
||||||
appName = "egg",
|
|
||||||
}: {
|
|
||||||
url: string
|
|
||||||
method: string
|
|
||||||
queryParams?: any
|
|
||||||
payload?: any
|
|
||||||
additionalHeaders?: any
|
|
||||||
appName?: string
|
|
||||||
}): Promise<T> => {
|
|
||||||
const headersWithAuth = {
|
|
||||||
Authorization: `Bearer ${await db.tenantAccessToken.get(appName)}`,
|
|
||||||
...additionalHeaders,
|
|
||||||
}
|
|
||||||
return netTool<T>({
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
queryParams,
|
|
||||||
payload,
|
|
||||||
additionalHeaders: headersWithAuth,
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error("larkNetTool catch error: ", error)
|
|
||||||
return {
|
|
||||||
code: 1,
|
|
||||||
data: null,
|
|
||||||
msg: error.message || "网络请求异常",
|
|
||||||
} as T
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送GET请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param appName - 应用名称,用于获取授权令牌。
|
|
||||||
* @returns 一个函数,该函数接受URL、查询参数和附加头,并返回一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
larkNetTool.get =
|
|
||||||
(appName?: string) =>
|
|
||||||
<T = LarkServer.BaseRes>(
|
|
||||||
url: string,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
larkNetTool({
|
|
||||||
url,
|
|
||||||
method: "get",
|
|
||||||
queryParams,
|
|
||||||
additionalHeaders,
|
|
||||||
appName,
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送POST请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param appName - 应用名称,用于获取授权令牌。
|
|
||||||
* @returns 一个函数,该函数接受URL、有效负载、查询参数和附加头,并返回一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
larkNetTool.post =
|
|
||||||
(appName?: string) =>
|
|
||||||
<T = LarkServer.BaseRes>(
|
|
||||||
url: string,
|
|
||||||
payload?: any,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
larkNetTool({
|
|
||||||
url,
|
|
||||||
method: "post",
|
|
||||||
payload,
|
|
||||||
queryParams,
|
|
||||||
additionalHeaders,
|
|
||||||
appName,
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送DELETE请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param appName - 应用名称,用于获取授权令牌。
|
|
||||||
* @returns 一个函数,该函数接受URL、有效负载和附加头,并返回一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
larkNetTool.del =
|
|
||||||
(appName?: string) =>
|
|
||||||
<T = LarkServer.BaseRes>(
|
|
||||||
url: string,
|
|
||||||
payload: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
larkNetTool({ url, method: "delete", payload, additionalHeaders, appName })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送PATCH请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param appName - 应用名称,用于获取授权令牌。
|
|
||||||
* @returns 一个函数,该函数接受URL、有效负载和附加头,并返回一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
larkNetTool.patch =
|
|
||||||
(appName?: string) =>
|
|
||||||
<T = LarkServer.BaseRes>(
|
|
||||||
url: string,
|
|
||||||
payload: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
larkNetTool({ url, method: "patch", payload, additionalHeaders, appName })
|
|
||||||
|
|
||||||
export default larkNetTool
|
|
@ -1,46 +1,40 @@
|
|||||||
import { LarkServer } from "../../types/larkServer"
|
import { LarkServer } from "../../types/larkServer"
|
||||||
import larkNetTool from "./larkNetTool"
|
import LarkBaseService from "./base"
|
||||||
|
|
||||||
/**
|
class LarkMessageService extends LarkBaseService {
|
||||||
* 发送卡片
|
/**
|
||||||
* @param {LarkServer.ReceiveIDType} receive_id_type 消息接收者id类型 open_id/user_id/union_id/email/chat_id
|
* 发送卡片
|
||||||
* @param {string} receive_id 消息接收者的ID,ID类型应与查询参数receive_id_type 对应
|
* @param {LarkServer.ReceiveIDType} receive_id_type 消息接收者id类型 open_id/user_id/union_id/email/chat_id
|
||||||
* @param {MsgType} msg_type 消息类型 包括:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user
|
* @param {string} receive_id 消息接收者的ID,ID类型应与查询参数receive_id_type 对应
|
||||||
* @param {string} content 消息内容,JSON结构序列化后的字符串。不同msg_type对应不同内容
|
* @param {MsgType} msg_type 消息类型 包括:text、post、image、file、audio、media、sticker、interactive、share_chat、share_user
|
||||||
*/
|
* @param {string} content 消息内容,JSON结构序列化后的字符串。不同msg_type对应不同内容
|
||||||
const send =
|
*/
|
||||||
(appName?: string) =>
|
async send(
|
||||||
async (
|
|
||||||
receive_id_type: LarkServer.ReceiveIDType,
|
receive_id_type: LarkServer.ReceiveIDType,
|
||||||
receive_id: string,
|
receive_id: string,
|
||||||
msg_type: LarkServer.MsgType,
|
msg_type: LarkServer.MsgType,
|
||||||
content: string
|
content: string
|
||||||
) => {
|
) {
|
||||||
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages?receive_id_type=${receive_id_type}`
|
const path = `/im/v1/messages?receive_id_type=${receive_id_type}`
|
||||||
if (msg_type === "text" && !content.includes('"text"')) {
|
if (msg_type === "text" && !content.includes('"text"')) {
|
||||||
content = JSON.stringify({ text: content })
|
content = JSON.stringify({ text: content })
|
||||||
}
|
}
|
||||||
return larkNetTool.post(appName)<LarkServer.BaseRes>(URL, {
|
return this.post<LarkServer.BaseRes>(path, {
|
||||||
receive_id,
|
receive_id,
|
||||||
msg_type,
|
msg_type,
|
||||||
content,
|
content,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新卡片
|
* 更新卡片
|
||||||
* @param {string} message_id 消息id
|
* @param {string} message_id 消息id
|
||||||
* @param {string} content 消息内容,JSON结构序列化后的字符串。不同msg_type对应不同内容
|
* @param {string} content 消息内容,JSON结构序列化后的字符串。不同msg_type对应不同内容
|
||||||
*/
|
*/
|
||||||
const update =
|
async update(message_id: string, content: string) {
|
||||||
(appName?: string) => async (message_id: string, content: string) => {
|
const path = `/im/v1/messages/${message_id}`
|
||||||
const URL = `https://open.f.mioffice.cn/open-apis/im/v1/messages/${message_id}`
|
return this.patch<LarkServer.BaseRes>(path, { content })
|
||||||
return larkNetTool.patch(appName)<LarkServer.BaseRes>(URL, { content })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = {
|
|
||||||
send,
|
|
||||||
update,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default message
|
export default LarkMessageService
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { LarkServer } from "../../types/larkServer"
|
import { LarkServer } from "../../types/larkServer"
|
||||||
import larkNetTool from "./larkNetTool"
|
import LarkBaseService from "./base"
|
||||||
|
|
||||||
/**
|
class LarkSheetService extends LarkBaseService {
|
||||||
* 向电子表格中插入行。
|
/**
|
||||||
* @param appName - 应用程序的名称(可选)。
|
* 向电子表格中插入行。
|
||||||
* @returns 一个函数,该函数接受表格令牌、范围和要插入的值。
|
* @param {string} sheetToken - 表格令牌。
|
||||||
*/
|
* @param {string} range - 插入数据的范围。
|
||||||
const insertRows =
|
* @param {string[][]} values - 要插入的值。
|
||||||
(appName?: string) =>
|
* @returns {Promise<LarkServer.BaseRes>} 返回一个包含响应数据的Promise。
|
||||||
async (sheetToken: string, range: string, values: string[][]) => {
|
*/
|
||||||
const URL = `https://open.f.mioffice.cn/open-apis/sheets/v2/spreadsheets/${sheetToken}/values_append?insertDataOption=INSERT_ROWS`
|
async insertRows(sheetToken: string, range: string, values: string[][]) {
|
||||||
return larkNetTool.post(appName)<LarkServer.BaseRes>(URL, {
|
const path = `/sheets/v2/spreadsheets/${sheetToken}/values_append?insertDataOption=INSERT_ROWS`
|
||||||
|
return this.post<LarkServer.BaseRes>(path, {
|
||||||
valueRange: {
|
valueRange: {
|
||||||
range,
|
range,
|
||||||
values,
|
values,
|
||||||
@ -18,8 +19,16 @@ const insertRows =
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const sheet = {
|
/**
|
||||||
insertRows,
|
* 获取指定范围内的电子表格数据。
|
||||||
|
* @param {string} sheetToken - 表格令牌。
|
||||||
|
* @param {string} range - 要获取数据的范围。
|
||||||
|
* @returns {Promise<LarkServer.SpreadsheetRes>} 返回一个包含响应数据的Promise。
|
||||||
|
*/
|
||||||
|
async getRange(sheetToken: string, range: string) {
|
||||||
|
const path = `/sheets/v2/spreadsheets/${sheetToken}/values/${range}?valueRenderOption=ToString`
|
||||||
|
return this.get<LarkServer.SpreadsheetRes>(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default sheet
|
export default LarkSheetService
|
||||||
|
@ -1,39 +1,38 @@
|
|||||||
import { LarkServer } from "../../types/larkServer"
|
import { LarkServer } from "../../types/larkServer"
|
||||||
import larkNetTool from "./larkNetTool"
|
import LarkBaseService from "./base"
|
||||||
|
|
||||||
/**
|
class LarkUserService extends LarkBaseService {
|
||||||
* 登录凭证校验
|
/**
|
||||||
* @param code
|
* 登录凭证校验
|
||||||
* @returns
|
* @param {string} code 登录凭证
|
||||||
*/
|
* @returns
|
||||||
const code2Login = (appName?: string) => async (code: string) => {
|
*/
|
||||||
const URL = `https://open.f.mioffice.cn/open-apis/mina/v2/tokenLoginValidate`
|
async code2Login(code: string) {
|
||||||
return larkNetTool.post(appName)<LarkServer.UserSessionRes>(URL, { code })
|
const path = `/mina/v2/tokenLoginValidate`
|
||||||
}
|
return this.post<LarkServer.UserSessionRes>(path, { code })
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户信息
|
* 获取用户信息
|
||||||
* @param user_id
|
* @param {string} user_id 用户ID
|
||||||
* @returns
|
* @param {"open_id" | "user_id"} user_id_type 用户ID类型
|
||||||
*/
|
* @returns
|
||||||
const get =
|
*/
|
||||||
(appName?: string) =>
|
async getOne(user_id: string, user_id_type: "open_id" | "user_id") {
|
||||||
async (user_id: string, user_id_type: "open_id" | "user_id") => {
|
const path = `/contact/v3/users/${user_id}`
|
||||||
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/${user_id}`
|
return this.get<LarkServer.UserInfoRes>(path, {
|
||||||
return larkNetTool.get(appName)<LarkServer.UserInfoRes>(URL, {
|
|
||||||
user_id_type,
|
user_id_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量获取用户信息
|
* 批量获取用户信息
|
||||||
* @param user_ids
|
* @param {string[]} user_ids 用户ID数组
|
||||||
* @returns
|
* @param {"open_id" | "user_id"} user_id_type 用户ID类型
|
||||||
*/
|
* @returns
|
||||||
const batchGet =
|
*/
|
||||||
(appName?: string) =>
|
async batchGet(user_ids: string[], user_id_type: "open_id" | "user_id") {
|
||||||
async (user_ids: string[], user_id_type: "open_id" | "user_id") => {
|
const path = `/contact/v3/users/batch`
|
||||||
const URL = `https://open.f.mioffice.cn/open-apis/contact/v3/users/batch`
|
|
||||||
|
|
||||||
// 如果user_id长度超出50,需要分批请求,
|
// 如果user_id长度超出50,需要分批请求,
|
||||||
const userCount = user_ids.length
|
const userCount = user_ids.length
|
||||||
@ -47,10 +46,7 @@ const batchGet =
|
|||||||
const getParams = `${user_idsSlice
|
const getParams = `${user_idsSlice
|
||||||
.map((id) => `user_ids=${id}`)
|
.map((id) => `user_ids=${id}`)
|
||||||
.join("&")}&user_id_type=${user_id_type}`
|
.join("&")}&user_id_type=${user_id_type}`
|
||||||
return larkNetTool.get(appName)<LarkServer.BatchUserInfoRes>(
|
return this.get<LarkServer.BatchUserInfoRes>(path, getParams)
|
||||||
URL,
|
|
||||||
getParams
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,14 +61,9 @@ const batchGet =
|
|||||||
data: {
|
data: {
|
||||||
items,
|
items,
|
||||||
},
|
},
|
||||||
msg: "success",
|
message: "success",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = {
|
|
||||||
code2Login,
|
|
||||||
batchGet,
|
|
||||||
get,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default user
|
export default LarkUserService
|
||||||
|
@ -1,232 +0,0 @@
|
|||||||
interface NetRequestParams {
|
|
||||||
url: string
|
|
||||||
method: string
|
|
||||||
queryParams?: any
|
|
||||||
payload?: any
|
|
||||||
additionalHeaders?: any
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 记录响应详情并返回响应日志对象。
|
|
||||||
* @param response - 响应对象。
|
|
||||||
* @param method - 请求使用的HTTP方法。
|
|
||||||
* @param headers - 请求头。
|
|
||||||
* @param requestBody - 请求体。
|
|
||||||
* @param responseBody - 响应体。
|
|
||||||
* @returns 响应日志对象。
|
|
||||||
*/
|
|
||||||
const logResponse = (
|
|
||||||
response: Response,
|
|
||||||
method: string,
|
|
||||||
headers: any,
|
|
||||||
requestBody: any,
|
|
||||||
responseBody: any
|
|
||||||
) => {
|
|
||||||
const responseLog = {
|
|
||||||
ok: response.ok,
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
url: response.url,
|
|
||||||
method: method,
|
|
||||||
requestHeaders: headers,
|
|
||||||
responseHeaders: response.headers,
|
|
||||||
requestBody,
|
|
||||||
responseBody,
|
|
||||||
}
|
|
||||||
console.log("🚀 ~ responseLog:", JSON.stringify(responseLog, null, 2))
|
|
||||||
return responseLog
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送网络请求并返回一个解析为响应数据的Promise。
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param method - 请求使用的HTTP方法。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param payload - 请求的有效负载数据。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
* @throws 如果网络响应不成功或存在解析错误,则抛出错误。
|
|
||||||
*/
|
|
||||||
const netTool = async <T = any>({
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
queryParams,
|
|
||||||
payload,
|
|
||||||
additionalHeaders,
|
|
||||||
}: NetRequestParams): Promise<T> => {
|
|
||||||
// 拼接完整的URL
|
|
||||||
let fullUrl = url
|
|
||||||
if (queryParams) {
|
|
||||||
if (typeof queryParams === "string") {
|
|
||||||
fullUrl = `${url}?${queryParams}`
|
|
||||||
} else {
|
|
||||||
const queryString = new URLSearchParams(queryParams).toString()
|
|
||||||
if (queryString) fullUrl = `${url}?${queryString}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置请求头
|
|
||||||
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 = ""
|
|
||||||
|
|
||||||
try {
|
|
||||||
resText = await res.text()
|
|
||||||
resData = JSON.parse(resText)
|
|
||||||
} catch {
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录响应
|
|
||||||
logResponse(res, method, headers, payload, resData || resText)
|
|
||||||
if (!res.ok) {
|
|
||||||
if (resData?.msg) {
|
|
||||||
throw new Error(resData.msg)
|
|
||||||
}
|
|
||||||
if (resText) {
|
|
||||||
throw new Error(resText)
|
|
||||||
}
|
|
||||||
throw new Error("网络响应异常")
|
|
||||||
}
|
|
||||||
// http 错误码正常,但解析异常
|
|
||||||
if (!resData) {
|
|
||||||
throw new Error("解析响应数据异常")
|
|
||||||
}
|
|
||||||
return resData as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送GET请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
netTool.get = <T = any>(
|
|
||||||
url: string,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> => netTool({ url, method: "get", queryParams, additionalHeaders })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送POST请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param payload - 请求的有效负载数据。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
netTool.post = <T = any>(
|
|
||||||
url: string,
|
|
||||||
payload?: any,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
netTool({ url, method: "post", payload, queryParams, additionalHeaders })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送PUT请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param payload - 请求的有效负载数据。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
netTool.put = <T = any>(
|
|
||||||
url: string,
|
|
||||||
payload: any,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
netTool({ url, method: "put", payload, queryParams, additionalHeaders })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送DELETE请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param payload - 请求的有效负载数据。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
netTool.del = <T = any>(
|
|
||||||
url: string,
|
|
||||||
payload: any,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
netTool({ url, method: "delete", payload, queryParams, additionalHeaders })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送PATCH请求并返回一个解析为响应数据的Promise。
|
|
||||||
*
|
|
||||||
* @param url - 要发送请求的URL。
|
|
||||||
* @param payload - 请求的有效负载数据。
|
|
||||||
* @param queryParams - 要包含在URL中的查询参数。
|
|
||||||
* @param additionalHeaders - 要包含在请求中的附加头。
|
|
||||||
* @returns 一个解析为响应数据的Promise。
|
|
||||||
*/
|
|
||||||
netTool.patch = <T = any>(
|
|
||||||
url: string,
|
|
||||||
payload: any,
|
|
||||||
queryParams?: any,
|
|
||||||
additionalHeaders?: any
|
|
||||||
): Promise<T> =>
|
|
||||||
netTool({ url, method: "patch", payload, queryParams, additionalHeaders })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建一个表示400 Bad Request的响应对象。
|
|
||||||
*
|
|
||||||
* @param msg - 错误消息。
|
|
||||||
* @param requestId - 请求ID。
|
|
||||||
* @returns 一个表示400 Bad Request的响应对象。
|
|
||||||
*/
|
|
||||||
netTool.badRequest = (msg: string, requestId?: string) =>
|
|
||||||
Response.json({ code: 400, msg, requestId }, { status: 400 })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建一个表示404 Not Found的响应对象。
|
|
||||||
*
|
|
||||||
* @param msg - 错误消息。
|
|
||||||
* @param requestId - 请求ID。
|
|
||||||
* @returns 一个表示404 Not Found的响应对象。
|
|
||||||
*/
|
|
||||||
netTool.notFound = (msg: string, requestId?: string) =>
|
|
||||||
Response.json({ code: 404, msg, requestId }, { status: 404 })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建一个表示500 Internal Server Error的响应对象。
|
|
||||||
*
|
|
||||||
* @param msg - 错误消息。
|
|
||||||
* @param data - 错误数据。
|
|
||||||
* @param requestId - 请求ID。
|
|
||||||
* @returns 一个表示500 Internal Server Error的响应对象。
|
|
||||||
*/
|
|
||||||
netTool.serverError = (msg: string, data?: any, requestId?: string) =>
|
|
||||||
Response.json({ code: 500, msg, data, requestId }, { status: 500 })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建一个表示200 OK的响应对象。
|
|
||||||
*
|
|
||||||
* @param data - 响应数据。
|
|
||||||
* @param requestId - 请求ID。
|
|
||||||
* @returns 一个表示200 OK的响应对象。
|
|
||||||
*/
|
|
||||||
netTool.ok = (data?: any, requestId?: string) =>
|
|
||||||
Response.json({ code: 0, msg: "success", data, requestId })
|
|
||||||
|
|
||||||
export default netTool
|
|
17
types/context.ts
Normal file
17
types/context.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Logger } from "winston"
|
||||||
|
|
||||||
|
import { AttachService, LarkService } from "../services"
|
||||||
|
import NetTool from "../utils/netTool"
|
||||||
|
|
||||||
|
export namespace Context {
|
||||||
|
export interface Data {
|
||||||
|
req: Request
|
||||||
|
requestId: string
|
||||||
|
logger: Logger
|
||||||
|
genResp: NetTool
|
||||||
|
body: any
|
||||||
|
text: string
|
||||||
|
larkService: LarkService
|
||||||
|
attachService: AttachService
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
|
import type { Context } from "./context"
|
||||||
import type { DB } from "./db"
|
import type { DB } from "./db"
|
||||||
import type { LarkAction } from "./larkAction"
|
import type { LarkAction } from "./larkAction"
|
||||||
import type { LarkEvent } from "./larkEvent"
|
import type { LarkEvent } from "./larkEvent"
|
||||||
import type { LarkServer } from "./larkServer"
|
import type { LarkServer } from "./larkServer"
|
||||||
import type { MsgProxy } from "./msgProxy"
|
import type { MsgProxy } from "./msgProxy"
|
||||||
|
|
||||||
export { DB, LarkAction, LarkEvent, LarkServer, MsgProxy }
|
export { Context, DB, LarkAction, LarkEvent, LarkServer, MsgProxy }
|
||||||
|
@ -58,10 +58,28 @@ export namespace LarkServer {
|
|||||||
code: number
|
code: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ValueRange {
|
||||||
|
majorDimension: string // 插入维度
|
||||||
|
range: string // 返回数据的范围,为空时表示查询范围没有数据
|
||||||
|
revision: number // sheet 的版本号
|
||||||
|
values: Array<Array<any>> // 查询得到的值
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpreadsheetData {
|
||||||
|
revision: number // sheet 的版本号
|
||||||
|
spreadsheetToken: string // spreadsheet 的 token
|
||||||
|
valueRange: ValueRange // 值与范围
|
||||||
|
}
|
||||||
|
|
||||||
export interface BaseRes {
|
export interface BaseRes {
|
||||||
code: number
|
code: number
|
||||||
data: any
|
data: any
|
||||||
msg: string
|
// 在错误处理中msg会被赋值为message
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpreadsheetRes extends BaseRes {
|
||||||
|
data: SpreadsheetData
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserSessionRes extends BaseRes {
|
export interface UserSessionRes extends BaseRes {
|
||||||
|
42
utils/genContext.ts
Normal file
42
utils/genContext.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { v4 as uuid } from "uuid"
|
||||||
|
|
||||||
|
import loggerIns from "../log"
|
||||||
|
import { AttachService, LarkService } from "../services"
|
||||||
|
import { Context } from "../types"
|
||||||
|
import NetTool from "./netTool"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成请求上下文。
|
||||||
|
*
|
||||||
|
* @param {Request} req - 请求对象。
|
||||||
|
* @returns {Promise<Context.Data>} 返回包含请求上下文的对象。
|
||||||
|
*/
|
||||||
|
const genContext = async (req: Request) => {
|
||||||
|
const requestId = uuid()
|
||||||
|
const logger = loggerIns.child({ requestId })
|
||||||
|
const genResp = new NetTool({ requestId })
|
||||||
|
const larkService = new LarkService("egg", requestId)
|
||||||
|
const attachService = new AttachService({ requestId })
|
||||||
|
|
||||||
|
let body: any = null
|
||||||
|
let text: string = ""
|
||||||
|
try {
|
||||||
|
text = await req.text()
|
||||||
|
body = JSON.parse(text)
|
||||||
|
} catch {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
logger.debug(`req body: ${text}`)
|
||||||
|
return {
|
||||||
|
req,
|
||||||
|
requestId,
|
||||||
|
logger,
|
||||||
|
genResp,
|
||||||
|
body,
|
||||||
|
text,
|
||||||
|
larkService,
|
||||||
|
attachService,
|
||||||
|
} as Context.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
export default genContext
|
424
utils/netTool.ts
Normal file
424
utils/netTool.ts
Normal file
@ -0,0 +1,424 @@
|
|||||||
|
import { Logger } from "winston"
|
||||||
|
|
||||||
|
import loggerIns from "../log"
|
||||||
|
|
||||||
|
interface NetRequestParams {
|
||||||
|
url: string
|
||||||
|
method: string
|
||||||
|
queryParams?: any
|
||||||
|
payload?: any
|
||||||
|
additionalHeaders?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NetErrorDetail {
|
||||||
|
httpStatus: number
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NetError extends Error {
|
||||||
|
public code: number
|
||||||
|
public message: string
|
||||||
|
public httpStatus: number
|
||||||
|
|
||||||
|
constructor({ code, message, httpStatus }: NetErrorDetail) {
|
||||||
|
super(message)
|
||||||
|
this.code = code
|
||||||
|
this.message = message
|
||||||
|
this.httpStatus = httpStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络工具类,提供发送HTTP请求的方法。
|
||||||
|
*/
|
||||||
|
class NetToolBase {
|
||||||
|
protected prefix: string
|
||||||
|
protected headers: any
|
||||||
|
protected getHeaders: () => any
|
||||||
|
protected logger: Logger
|
||||||
|
protected requestId: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个网络工具类实例。
|
||||||
|
*
|
||||||
|
* @param {Object} params - 构造函数参数。
|
||||||
|
* @param {string} [params.prefix] - URL前缀。
|
||||||
|
* @param {any} [params.headers] - 默认请求头。
|
||||||
|
* @param {Function} [params.getHeaders] - 获取请求头的方法。
|
||||||
|
* @param {string} [params.requestId] - 请求ID。
|
||||||
|
*/
|
||||||
|
constructor({
|
||||||
|
prefix,
|
||||||
|
headers,
|
||||||
|
getHeaders,
|
||||||
|
requestId,
|
||||||
|
}: {
|
||||||
|
prefix?: string
|
||||||
|
headers?: any
|
||||||
|
getHeaders?: () => any
|
||||||
|
requestId?: string
|
||||||
|
} = {}) {
|
||||||
|
this.prefix = prefix || ""
|
||||||
|
this.headers = headers || {}
|
||||||
|
this.getHeaders = getHeaders || (() => ({}))
|
||||||
|
this.requestId = requestId || ""
|
||||||
|
this.logger = loggerIns.child({ requestId })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录响应详情并返回响应日志对象。
|
||||||
|
* @param response - 响应对象。
|
||||||
|
* @param method - 请求使用的HTTP方法。
|
||||||
|
* @param headers - 请求头。
|
||||||
|
* @param requestBody - 请求体。
|
||||||
|
* @param responseBody - 响应体。
|
||||||
|
* @returns 响应日志对象。
|
||||||
|
*/
|
||||||
|
private logResponse(
|
||||||
|
response: Response,
|
||||||
|
method: string,
|
||||||
|
headers: any,
|
||||||
|
requestBody: any,
|
||||||
|
responseBody: any
|
||||||
|
) {
|
||||||
|
const responseLog = {
|
||||||
|
ok: response.ok,
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
url: response.url,
|
||||||
|
method: method,
|
||||||
|
requestHeaders: headers,
|
||||||
|
responseHeaders: response.headers,
|
||||||
|
requestBody,
|
||||||
|
responseBody,
|
||||||
|
}
|
||||||
|
this.logger.http(JSON.stringify(responseLog, null, 2))
|
||||||
|
return responseLog
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送网络请求并返回一个解析为响应数据的Promise。
|
||||||
|
* @param url - 要发送请求的URL。
|
||||||
|
* @param method - 请求使用的HTTP方法。
|
||||||
|
* @param queryParams - 要包含在URL中的查询参数。
|
||||||
|
* @param payload - 请求的有效负载数据。
|
||||||
|
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||||
|
* @returns 一个解析为响应数据的Promise。
|
||||||
|
* @throws 如果网络响应不成功或存在解析错误,则抛出错误。
|
||||||
|
*/
|
||||||
|
protected async request<T = any>({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
queryParams,
|
||||||
|
payload,
|
||||||
|
additionalHeaders,
|
||||||
|
}: NetRequestParams): Promise<T> {
|
||||||
|
// 拼接完整的URL
|
||||||
|
let fullUrl = `${this.prefix}${url}`
|
||||||
|
if (queryParams) {
|
||||||
|
if (typeof queryParams === "string") {
|
||||||
|
fullUrl = `${fullUrl}?${queryParams}`
|
||||||
|
} else {
|
||||||
|
const queryString = new URLSearchParams(queryParams).toString()
|
||||||
|
if (queryString) fullUrl = `${fullUrl}?${queryString}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
const headers = {
|
||||||
|
...this.headers,
|
||||||
|
...(await this.getHeaders()),
|
||||||
|
...additionalHeaders,
|
||||||
|
}
|
||||||
|
// 设置请求Header
|
||||||
|
if (!(payload instanceof FormData)) {
|
||||||
|
headers["Content-Type"] = "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理请求数据
|
||||||
|
const body = payload instanceof FormData ? payload : JSON.stringify(payload)
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
const res = await fetch(fullUrl, {
|
||||||
|
method,
|
||||||
|
body,
|
||||||
|
headers,
|
||||||
|
})
|
||||||
|
// 获取响应数据
|
||||||
|
let resData: any = null
|
||||||
|
let resText: string = ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
resText = await res.text()
|
||||||
|
resData = JSON.parse(resText)
|
||||||
|
} catch {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录响应
|
||||||
|
this.logResponse(res, method, headers, payload, resData || resText)
|
||||||
|
if (!res.ok) {
|
||||||
|
if (resData?.message || resData?.msg) {
|
||||||
|
throw new NetError({
|
||||||
|
httpStatus: res.status,
|
||||||
|
code: resData?.code,
|
||||||
|
message: resData?.message || resData?.msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
throw new NetError({
|
||||||
|
httpStatus: res.status,
|
||||||
|
code: res.status,
|
||||||
|
message: resText || "网络响应异常",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// http 错误码正常,但解析异常
|
||||||
|
if (!resData) {
|
||||||
|
throw new NetError({
|
||||||
|
httpStatus: res.status,
|
||||||
|
code: 1,
|
||||||
|
message: "解析响应数据异常",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 响应数据异常
|
||||||
|
if ("code" in resData && resData.code !== 0) {
|
||||||
|
throw new NetError({
|
||||||
|
httpStatus: res.status,
|
||||||
|
code: resData.code,
|
||||||
|
message: resData.message || resData.msg || "网络请求失败",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return resData as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送GET请求并返回一个解析为响应数据的Promise。
|
||||||
|
*
|
||||||
|
* @param url - 要发送请求的URL。
|
||||||
|
* @param queryParams - 要包含在URL中的查询参数。
|
||||||
|
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||||
|
* @returns 一个解析为响应数据的Promise。
|
||||||
|
*/
|
||||||
|
protected get<T = any>(
|
||||||
|
url: string,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return this.request({ url, method: "get", queryParams, additionalHeaders })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送POST请求并返回一个解析为响应数据的Promise。
|
||||||
|
*
|
||||||
|
* @param url - 要发送请求的URL。
|
||||||
|
* @param payload - 请求的有效负载数据。
|
||||||
|
* @param queryParams - 要包含在URL中的查询参数。
|
||||||
|
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||||
|
* @returns 一个解析为响应数据的Promise。
|
||||||
|
*/
|
||||||
|
protected post<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload?: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return this.request({
|
||||||
|
url,
|
||||||
|
method: "post",
|
||||||
|
payload,
|
||||||
|
queryParams,
|
||||||
|
additionalHeaders,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送PUT请求并返回一个解析为响应数据的Promise。
|
||||||
|
*
|
||||||
|
* @param url - 要发送请求的URL。
|
||||||
|
* @param payload - 请求的有效负载数据。
|
||||||
|
* @param queryParams - 要包含在URL中的查询参数。
|
||||||
|
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||||
|
* @returns 一个解析为响应数据的Promise。
|
||||||
|
*/
|
||||||
|
protected put<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return this.request({
|
||||||
|
url,
|
||||||
|
method: "put",
|
||||||
|
payload,
|
||||||
|
queryParams,
|
||||||
|
additionalHeaders,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送DELETE请求并返回一个解析为响应数据的Promise。
|
||||||
|
*
|
||||||
|
* @param url - 要发送请求的URL。
|
||||||
|
* @param payload - 请求的有效负载数据。
|
||||||
|
* @param queryParams - 要包含在URL中的查询参数。
|
||||||
|
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||||
|
* @returns 一个解析为响应数据的Promise。
|
||||||
|
*/
|
||||||
|
protected del<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return this.request({
|
||||||
|
url,
|
||||||
|
method: "delete",
|
||||||
|
payload,
|
||||||
|
queryParams,
|
||||||
|
additionalHeaders,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送PATCH请求并返回一个解析为响应数据的Promise。
|
||||||
|
*
|
||||||
|
* @param url - 要发送请求的URL。
|
||||||
|
* @param payload - 请求的有效负载数据。
|
||||||
|
* @param queryParams - 要包含在URL中的查询参数。
|
||||||
|
* @param additionalHeaders - 要包含在请求中的附加头。
|
||||||
|
* @returns 一个解析为响应数据的Promise。
|
||||||
|
*/
|
||||||
|
protected patch<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return this.request({
|
||||||
|
url,
|
||||||
|
method: "patch",
|
||||||
|
payload,
|
||||||
|
queryParams,
|
||||||
|
additionalHeaders,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NetTool extends NetToolBase {
|
||||||
|
public request<T = any>({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
queryParams,
|
||||||
|
payload,
|
||||||
|
additionalHeaders,
|
||||||
|
}: NetRequestParams): Promise<T> {
|
||||||
|
return super.request<T>({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
queryParams,
|
||||||
|
payload,
|
||||||
|
additionalHeaders,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
public get<T = any>(
|
||||||
|
url: string,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return super.get<T>(url, queryParams, additionalHeaders)
|
||||||
|
}
|
||||||
|
public post<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload?: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return super.post<T>(url, payload, queryParams, additionalHeaders)
|
||||||
|
}
|
||||||
|
public put<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return super.put<T>(url, payload, queryParams, additionalHeaders)
|
||||||
|
}
|
||||||
|
public del<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return super.del<T>(url, payload, queryParams, additionalHeaders)
|
||||||
|
}
|
||||||
|
public patch<T = any>(
|
||||||
|
url: string,
|
||||||
|
payload: any,
|
||||||
|
queryParams?: any,
|
||||||
|
additionalHeaders?: any
|
||||||
|
): Promise<T> {
|
||||||
|
return super.patch<T>(url, payload, queryParams, additionalHeaders)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 创建一个表示400 Bad Request的响应对象。
|
||||||
|
*
|
||||||
|
* @param message - 错误消息。
|
||||||
|
* @returns 一个表示400 Bad Request的响应对象。
|
||||||
|
*/
|
||||||
|
badRequest(message: string) {
|
||||||
|
this.logger.error(`return a bad request response: ${message}`)
|
||||||
|
return Response.json(
|
||||||
|
{ code: 400, message, requestId: this.requestId },
|
||||||
|
{ status: 400 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个表示404 Not Found的响应对象。
|
||||||
|
*
|
||||||
|
* @param message - 错误消息。
|
||||||
|
* @returns 一个表示404 Not Found的响应对象。
|
||||||
|
*/
|
||||||
|
notFound(message: string) {
|
||||||
|
this.logger.error(`return a not found response: ${message}`)
|
||||||
|
return Response.json(
|
||||||
|
{ code: 404, message, requestId: this.requestId },
|
||||||
|
{ status: 404 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个表示500 Internal Server Error的响应对象。
|
||||||
|
*
|
||||||
|
* @param message - 错误消息。
|
||||||
|
* @param data - 错误数据。
|
||||||
|
* @returns 一个表示500 Internal Server Error的响应对象。
|
||||||
|
*/
|
||||||
|
serverError(message: string, data?: any) {
|
||||||
|
this.logger.error(`return a server error response: ${message}`)
|
||||||
|
return Response.json(
|
||||||
|
{ code: 500, message, data, requestId: this.requestId },
|
||||||
|
{ status: 500 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个表示200 OK的响应对象。
|
||||||
|
*
|
||||||
|
* @param data - 响应数据。
|
||||||
|
* @returns 一个表示200 OK的响应对象。
|
||||||
|
*/
|
||||||
|
ok(data?: any) {
|
||||||
|
this.logger.info(`return a ok response: ${JSON.stringify(data)}`)
|
||||||
|
return Response.json({
|
||||||
|
code: 0,
|
||||||
|
message: "success",
|
||||||
|
data,
|
||||||
|
requestId: this.requestId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NetToolBase }
|
||||||
|
|
||||||
|
export default NetTool
|
@ -1,3 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* 创建一个路径检查工具,用于精确匹配和前缀匹配路径。
|
||||||
|
* @param {string} url - 要检查的基础 URL。
|
||||||
|
* @param {string} [prefix] - 可选的路径前缀。
|
||||||
|
* @returns {object} 包含路径检查方法的对象。
|
||||||
|
*/
|
||||||
|
export const makeCheckPathTool = (url: string, prefix?: string) => {
|
||||||
|
const { pathname } = new URL(url)
|
||||||
|
const makePath = (path: string) => `${prefix || ""}${path}`
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* 检查路径是否与基础 URL 的路径精确匹配。
|
||||||
|
* @param {string} path - 要检查的路径。
|
||||||
|
* @returns {boolean} 如果路径精确匹配则返回 true,否则返回 false。
|
||||||
|
*/
|
||||||
|
exactCheck: (path: string) => {
|
||||||
|
return pathname === makePath(path)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 检查路径是否以基础 URL 的路径为前缀。
|
||||||
|
* @param {string} path - 要检查的路径。
|
||||||
|
* @returns {boolean} 如果路径以基础 URL 的路径为前缀则返回 true,否则返回 false。
|
||||||
|
*/
|
||||||
|
startsWithCheck: (path: string) => pathname.startsWith(makePath(path)),
|
||||||
|
/**
|
||||||
|
* 检查完整路径是否与基础 URL 的路径精确匹配。
|
||||||
|
* @param {string} path - 要检查的路径。
|
||||||
|
* @returns {boolean} 如果完整路径与基础 URL 的路径精确匹配则返回 true,否则返回 false。
|
||||||
|
*/
|
||||||
|
fullCheck: (path: string) => pathname === path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 裁剪路径字符串,如果路径长度超过20个字符,则只保留最后两级目录。
|
||||||
|
*
|
||||||
|
* @param {string} path - 要处理的路径字符串。
|
||||||
|
* @returns {string} - 裁剪后的路径字符串,如果长度不超过20个字符则返回原路径。
|
||||||
|
*/
|
||||||
|
export const shortenPath = (path: string): string => {
|
||||||
|
if (path.length <= 20) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = path.split("/")
|
||||||
|
if (parts.length <= 2) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
return `.../${parts[parts.length - 2]}/${parts[parts.length - 1]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全地将对象转换为 JSON 字符串。
|
||||||
|
* 如果转换失败,则返回对象的字符串表示。
|
||||||
|
* @param {any} obj - 要转换的对象。
|
||||||
|
* @returns {string} - JSON 字符串或对象的字符串表示。
|
||||||
|
*/
|
||||||
export const safeJsonStringify = (obj: any) => {
|
export const safeJsonStringify = (obj: any) => {
|
||||||
try {
|
try {
|
||||||
return JSON.stringify(obj)
|
return JSON.stringify(obj)
|
||||||
@ -5,16 +63,3 @@ export const safeJsonStringify = (obj: any) => {
|
|||||||
return String(obj)
|
return String(obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const makeCheckPathTool = (url: string, prefix?: string) => {
|
|
||||||
const { pathname } = new URL(url)
|
|
||||||
const makePath = (path: string) => `${prefix || ""}${path}`
|
|
||||||
return {
|
|
||||||
// 精确匹配
|
|
||||||
exactCheck: (path: string) => {
|
|
||||||
return pathname === makePath(path)
|
|
||||||
},
|
|
||||||
// 前缀匹配
|
|
||||||
startsWithCheck: (path: string) => pathname.startsWith(makePath(path)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -16,7 +16,6 @@ export const managePbError = async <T>(
|
|||||||
try {
|
try {
|
||||||
return await dbFunc()
|
return await dbFunc()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.log("🚀 ~ managePbError ~ err:", err)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user