mirror of
https://github.com/d0zingcat/alert-message-center.git
synced 2026-05-13 15:09:19 +00:00
refactor: Improve webhook type safety with unknown types and refined message content handling, and update Biome schema.
This commit is contained in:
@@ -31,9 +31,9 @@ interface User {
|
||||
|
||||
interface WebhookBody {
|
||||
msg_type?: string;
|
||||
content?: any;
|
||||
card?: any;
|
||||
post?: any;
|
||||
content?: unknown;
|
||||
card?: unknown;
|
||||
post?: unknown;
|
||||
image_key?: string;
|
||||
file_key?: string;
|
||||
audio_key?: string;
|
||||
@@ -44,7 +44,7 @@ interface WebhookBody {
|
||||
token?: string;
|
||||
file_type?: string;
|
||||
file_name?: string;
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const webhook = new Hono();
|
||||
@@ -63,13 +63,14 @@ const getRequestBody = async (c: Context): Promise<WebhookBody> => {
|
||||
contentType.includes("multipart/form-data") ||
|
||||
contentType.includes("application/x-www-form-urlencoded")
|
||||
) {
|
||||
body = await c.req.parseBody();
|
||||
body = (await c.req.parseBody()) as unknown as WebhookBody;
|
||||
// Handle stringified JSON fields in multipart
|
||||
const complexFields = ["content", "card", "post"];
|
||||
const complexFields: (keyof WebhookBody)[] = ["content", "card", "post"];
|
||||
for (const field of complexFields) {
|
||||
if (typeof body[field] === "string") {
|
||||
const val = body[field];
|
||||
if (typeof val === "string") {
|
||||
try {
|
||||
body[field] = JSON.parse(body[field]);
|
||||
body[field] = JSON.parse(val);
|
||||
} catch {
|
||||
// Not JSON, leave as is
|
||||
}
|
||||
@@ -107,7 +108,7 @@ const getRequestBody = async (c: Context): Promise<WebhookBody> => {
|
||||
delete body.file;
|
||||
}
|
||||
|
||||
const image = Array.isArray(body.image) ? body.image[0] : body.image;
|
||||
const image = Array.isArray(body.image) ? (body.image[0] as unknown) : body.image;
|
||||
if (image instanceof File) {
|
||||
const buffer = Buffer.from(await image.arrayBuffer());
|
||||
const imageKey = await feishuClient.uploadImage(buffer);
|
||||
@@ -154,7 +155,10 @@ const dispatchAlert = async (
|
||||
idType: "chat_id" as FeishuReceiveIdType,
|
||||
}));
|
||||
|
||||
const allRecipients: Recipient[] = [...validUserRecipients, ...groupRecipients];
|
||||
const allRecipients: Recipient[] = [
|
||||
...validUserRecipients,
|
||||
...groupRecipients,
|
||||
];
|
||||
|
||||
const [task] = await db
|
||||
.insert(alertTasks)
|
||||
@@ -164,7 +168,8 @@ const dispatchAlert = async (
|
||||
status: "processing",
|
||||
recipientCount: allRecipients.length,
|
||||
successCount: 0,
|
||||
payload: body as Record<string, unknown>, // Cast to satisfy Drizzle jsonb type
|
||||
// biome-ignore lint/suspicious/noExplicitAny: Drizzle expects specific jsonb type
|
||||
payload: body as any,
|
||||
})
|
||||
.returning();
|
||||
|
||||
@@ -184,7 +189,8 @@ const dispatchAlert = async (
|
||||
logger.info(
|
||||
{
|
||||
taskId: task.id,
|
||||
userCount: userRecipients.length,
|
||||
slug: topic.slug,
|
||||
userCount: validUserRecipients.length,
|
||||
groupCount: groupRecipients.length,
|
||||
},
|
||||
"[Webhook] Dispatching alerts",
|
||||
@@ -205,14 +211,32 @@ const dispatchAlert = async (
|
||||
const content = JSON.parse(JSON.stringify(body.content));
|
||||
const msgType = body.msg_type || "text";
|
||||
// Add prefix for text
|
||||
if (msgType === "text" && content.text) {
|
||||
content.text = `[${topic.name}]\n${content.text}`;
|
||||
if (
|
||||
msgType === "text" &&
|
||||
content &&
|
||||
typeof content === "object" &&
|
||||
"text" in content
|
||||
) {
|
||||
(content as Record<string, unknown>).text = `[Direct Message]\n${(content as Record<string, unknown>).text
|
||||
}`;
|
||||
}
|
||||
// Add prefix for interactive
|
||||
if (msgType === "interactive" && content.header) {
|
||||
content.header.title.content = `[${topic.name}] ${content.header.title.content}`;
|
||||
if (
|
||||
msgType === "interactive" &&
|
||||
content &&
|
||||
typeof content === "object" &&
|
||||
"header" in content
|
||||
) {
|
||||
const c = content as Record<string, Record<string, Record<string, unknown>>>;
|
||||
if (c.header?.title?.content) {
|
||||
c.header.title.content = `[${topic.slug || topic.name}] ${c.header.title.content
|
||||
}`;
|
||||
}
|
||||
}
|
||||
messagesToSend.push({ type: msgType, content });
|
||||
messagesToSend.push({
|
||||
type: msgType,
|
||||
content: content as Record<string, unknown> | string,
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Image
|
||||
@@ -234,7 +258,7 @@ const dispatchAlert = async (
|
||||
// 4. Fallback for no explicit content/attachment keys
|
||||
if (messagesToSend.length === 0) {
|
||||
let msgType = body.msg_type || "text";
|
||||
let content = body.content;
|
||||
let content: unknown = body.content;
|
||||
|
||||
if (body.card) {
|
||||
content = body.card;
|
||||
@@ -259,10 +283,19 @@ const dispatchAlert = async (
|
||||
}
|
||||
}
|
||||
// Add prefix for inferred types
|
||||
if (msgType === "text" && content.text) {
|
||||
content.text = `[${topic.name}]\n${content.text}`;
|
||||
if (
|
||||
msgType === "text" &&
|
||||
content &&
|
||||
typeof content === "object" &&
|
||||
"text" in content
|
||||
) {
|
||||
(content as Record<string, unknown>).text = `[${topic.name}]\n${(content as Record<string, unknown>).text
|
||||
}`;
|
||||
}
|
||||
messagesToSend.push({ type: msgType, content });
|
||||
messagesToSend.push({
|
||||
type: msgType,
|
||||
content: content as Record<string, unknown> | string,
|
||||
});
|
||||
}
|
||||
|
||||
let successCount = 0;
|
||||
@@ -444,9 +477,10 @@ webhook.post("/:token/topic/:slug", async (c) => {
|
||||
let user: User | null = null;
|
||||
if (!topic.isGlobal) {
|
||||
// 0. Find the User by Token
|
||||
user = (await db.query.users.findFirst({
|
||||
where: eq(users.personalToken, token),
|
||||
})) || null;
|
||||
user =
|
||||
(await db.query.users.findFirst({
|
||||
where: eq(users.personalToken, token),
|
||||
})) || null;
|
||||
|
||||
if (!user) {
|
||||
logger.warn({ token }, "[Webhook] Invalid personal token");
|
||||
|
||||
@@ -175,10 +175,11 @@ export default function SendAlertForm({
|
||||
|
||||
{status && (
|
||||
<div
|
||||
className={`mt-4 p-3 rounded-lg flex items-start animate-in fade-in zoom-in-95 ${status.type === "success"
|
||||
? "bg-green-50 text-green-700 border border-green-100"
|
||||
: "bg-red-50 text-red-700 border border-red-100"
|
||||
}`}
|
||||
className={`mt-4 p-3 rounded-lg flex items-start animate-in fade-in zoom-in-95 ${
|
||||
status.type === "success"
|
||||
? "bg-green-50 text-green-700 border border-green-100"
|
||||
: "bg-red-50 text-red-700 border border-red-100"
|
||||
}`}
|
||||
>
|
||||
{status.type === "success" ? (
|
||||
<CheckCircle2 className="w-4 h-4 mr-2 mt-0.5 flex-shrink-0" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
|
||||
Reference in New Issue
Block a user