mirror of
https://github.com/opsre/LiteOps.git
synced 2026-06-09 04:37:27 +08:00
🐞 fix: 修复通知配置测试webhook功能自定义关键词发送错误
This commit is contained in:
@@ -320,6 +320,12 @@ class NotificationTestView(View):
|
|||||||
# 准备测试消息
|
# 准备测试消息
|
||||||
timestamp = str(int(time.time() * 1000))
|
timestamp = str(int(time.time() * 1000))
|
||||||
test_message = "这是一条测试消息,如果你收到了这条消息,说明机器人配置正确。"
|
test_message = "这是一条测试消息,如果你收到了这条消息,说明机器人配置正确。"
|
||||||
|
|
||||||
|
# 使用自定义关键词安全设置,需要在消息中包含关键词
|
||||||
|
if robot.security_type == 'keyword' and robot.keywords:
|
||||||
|
# 测试消息中关键词
|
||||||
|
keyword = robot.keywords[0]
|
||||||
|
test_message = f"【{keyword}】{test_message}"
|
||||||
|
|
||||||
# 根据不同类型的机器人发送测试消息
|
# 根据不同类型的机器人发送测试消息
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,476 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="notification">
|
|
||||||
<div class="page-header">
|
|
||||||
<a-row justify="space-between" align="middle">
|
|
||||||
<a-col>
|
|
||||||
<h2>通知配置</h2>
|
|
||||||
</a-col>
|
|
||||||
<a-col>
|
|
||||||
<a-button type="primary" @click="showAddRobot">
|
|
||||||
<template #icon><PlusOutlined /></template>
|
|
||||||
添加机器人
|
|
||||||
</a-button>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a-card>
|
|
||||||
<a-table
|
|
||||||
:columns="robotColumns"
|
|
||||||
:data-source="robotList"
|
|
||||||
:loading="loading"
|
|
||||||
:pagination="false"
|
|
||||||
row-key="robot_id"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.key === 'action'">
|
|
||||||
<a-space>
|
|
||||||
<a-button type="link" @click="handleEdit(record)">
|
|
||||||
编辑
|
|
||||||
</a-button>
|
|
||||||
<a-button type="link" @click="handleTestRobot(record)">
|
|
||||||
测试
|
|
||||||
</a-button>
|
|
||||||
<a-popconfirm
|
|
||||||
title="确定要删除这个机器人吗?"
|
|
||||||
@confirm="handleDeleteRobot(record)"
|
|
||||||
>
|
|
||||||
<a-button type="link" danger>删除</a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-card>
|
|
||||||
|
|
||||||
<!-- 添加/编辑机器人抽屉 -->
|
|
||||||
<a-drawer
|
|
||||||
v-model:open="drawerVisible"
|
|
||||||
:title="isEdit ? '编辑机器人' : '添加机器人'"
|
|
||||||
placement="right"
|
|
||||||
width="500px"
|
|
||||||
:closable="false"
|
|
||||||
:footer="null"
|
|
||||||
@close="handleDrawerClose"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="robotForm"
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<a-form-item
|
|
||||||
label="机器人类型"
|
|
||||||
name="type"
|
|
||||||
:rules="[{ required: true, message: '请选择机器人类型' }]"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="robotForm.type"
|
|
||||||
placeholder="请选择机器人类型"
|
|
||||||
:disabled="isEdit"
|
|
||||||
>
|
|
||||||
<a-select-option value="dingtalk">钉钉机器人</a-select-option>
|
|
||||||
<a-select-option value="wecom">企业微信机器人</a-select-option>
|
|
||||||
<a-select-option value="feishu">飞书机器人</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
label="机器人名称"
|
|
||||||
name="name"
|
|
||||||
:rules="[{ required: true, message: '请输入机器人名称' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="robotForm.name"
|
|
||||||
placeholder="请输入机器人名称"
|
|
||||||
:maxLength="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
label="Webhook地址"
|
|
||||||
name="webhook"
|
|
||||||
:rules="[{ required: true, message: '请输入Webhook地址' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="robotForm.webhook"
|
|
||||||
placeholder="请输入Webhook地址"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
label="安全设置"
|
|
||||||
name="security_type"
|
|
||||||
:rules="[{ required: true, message: '请选择安全设置类型' }]"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="robotForm.security_type"
|
|
||||||
placeholder="请选择安全设置类型"
|
|
||||||
>
|
|
||||||
<a-select-option value="none">无</a-select-option>
|
|
||||||
<a-select-option value="secret">加签密钥</a-select-option>
|
|
||||||
<a-select-option value="keyword">自定义关键词</a-select-option>
|
|
||||||
<a-select-option value="ip">IP地址(段)</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<template v-if="robotForm.security_type === 'secret'">
|
|
||||||
<a-form-item
|
|
||||||
label="加签密钥"
|
|
||||||
name="secret"
|
|
||||||
:rules="[{ required: true, message: '请输入加签密钥' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="robotForm.secret"
|
|
||||||
placeholder="请输入加签密钥"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="robotForm.security_type === 'keyword'">
|
|
||||||
<a-form-item
|
|
||||||
label="自定义关键词"
|
|
||||||
name="keywords"
|
|
||||||
:rules="[{ required: true, message: '请添加关键词' }]"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="robotForm.keywords"
|
|
||||||
mode="tags"
|
|
||||||
placeholder="请输入关键词后按回车添加"
|
|
||||||
:token-separators="[',']"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="robotForm.security_type === 'ip'">
|
|
||||||
<a-form-item
|
|
||||||
label="IP白名单"
|
|
||||||
name="ip_list"
|
|
||||||
:rules="[{ required: true, message: '请添加IP地址' }]"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="robotForm.ip_list"
|
|
||||||
mode="tags"
|
|
||||||
placeholder="请输入IP地址后按回车添加"
|
|
||||||
:token-separators="[',']"
|
|
||||||
/>
|
|
||||||
<div class="form-item-help">
|
|
||||||
支持IP地址或IP地址段,例如: 192.168.1.1 或 192.168.1.1/24
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
label="备注"
|
|
||||||
name="remark"
|
|
||||||
>
|
|
||||||
<a-textarea
|
|
||||||
v-model:value="robotForm.remark"
|
|
||||||
placeholder="请输入备注信息"
|
|
||||||
:rows="4"
|
|
||||||
:maxLength="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<div style="text-align: left">
|
|
||||||
<a-space>
|
|
||||||
<a-button @click="handleDrawerClose">取消</a-button>
|
|
||||||
<a-button type="primary" :loading="submitLoading" @click="handleSubmit">
|
|
||||||
确定
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-drawer>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, reactive, onMounted } from 'vue';
|
|
||||||
import { message } from 'ant-design-vue';
|
|
||||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
|
||||||
import axios from 'axios';
|
|
||||||
import { checkPermission, hasFunctionPermission } from '../../utils/permission';
|
|
||||||
|
|
||||||
const drawerVisible = ref(false);
|
|
||||||
const submitLoading = ref(false);
|
|
||||||
const loading = ref(false);
|
|
||||||
const isEdit = ref(false);
|
|
||||||
|
|
||||||
const robotList = ref([]);
|
|
||||||
|
|
||||||
// 表格列定义
|
|
||||||
const robotColumns = [
|
|
||||||
{
|
|
||||||
title: '类型',
|
|
||||||
dataIndex: 'type',
|
|
||||||
key: 'type',
|
|
||||||
customRender: ({ text }) => getRobotTypeText(text)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '机器人名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Webhook地址',
|
|
||||||
dataIndex: 'webhook',
|
|
||||||
key: 'webhook',
|
|
||||||
ellipsis: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '安全设置',
|
|
||||||
dataIndex: 'security_type',
|
|
||||||
key: 'security_type',
|
|
||||||
customRender: ({ text }) => getSecurityTypeText(text)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '备注',
|
|
||||||
dataIndex: 'remark',
|
|
||||||
key: 'remark',
|
|
||||||
ellipsis: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'create_time',
|
|
||||||
key: 'create_time',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'action',
|
|
||||||
fixed: 'right',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 添加/编辑机器人表单
|
|
||||||
const formRef = ref();
|
|
||||||
const robotForm = reactive({
|
|
||||||
robot_id: '',
|
|
||||||
type: undefined,
|
|
||||||
name: '',
|
|
||||||
webhook: '',
|
|
||||||
security_type: 'none',
|
|
||||||
secret: '',
|
|
||||||
keywords: [],
|
|
||||||
ip_list: [],
|
|
||||||
remark: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取机器人列表
|
|
||||||
const loadRobotList = async () => {
|
|
||||||
if (!checkPermission('notification', 'view')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
loading.value = true;
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
const response = await axios.get('/api/notification/robots/', {
|
|
||||||
headers: { 'Authorization': token }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
robotList.value = response.data.data;
|
|
||||||
} else {
|
|
||||||
message.error(response.data.message || '获取机器人列表失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Load robot list error:', error);
|
|
||||||
message.error('获取机器人列表失败');
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取机器人类型文本
|
|
||||||
const getRobotTypeText = (type) => {
|
|
||||||
const types = {
|
|
||||||
dingtalk: '钉钉',
|
|
||||||
wecom: '企业微信',
|
|
||||||
feishu: '飞书',
|
|
||||||
};
|
|
||||||
return types[type] || type;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取安全设置类型文本
|
|
||||||
const getSecurityTypeText = (type) => {
|
|
||||||
const types = {
|
|
||||||
none: '无',
|
|
||||||
secret: '加签密钥',
|
|
||||||
keyword: '关键词',
|
|
||||||
ip: 'IP白名单',
|
|
||||||
};
|
|
||||||
return types[type] || type;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 显示添加机器人抽屉
|
|
||||||
const showAddRobot = () => {
|
|
||||||
if (!checkPermission('notification', 'create')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isEdit.value = false;
|
|
||||||
drawerVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 显示编辑机器人抽屉
|
|
||||||
const handleEdit = (record) => {
|
|
||||||
if (!checkPermission('notification', 'edit')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isEdit.value = true;
|
|
||||||
Object.assign(robotForm, {
|
|
||||||
robot_id: record.robot_id,
|
|
||||||
type: record.type,
|
|
||||||
name: record.name,
|
|
||||||
webhook: record.webhook,
|
|
||||||
security_type: record.security_type,
|
|
||||||
secret: record.secret,
|
|
||||||
keywords: record.keywords || [],
|
|
||||||
ip_list: record.ip_list || [],
|
|
||||||
remark: record.remark,
|
|
||||||
});
|
|
||||||
drawerVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDrawerClose = () => {
|
|
||||||
drawerVisible.value = false;
|
|
||||||
formRef.value?.resetFields();
|
|
||||||
// 重置表单
|
|
||||||
Object.assign(robotForm, {
|
|
||||||
robot_id: '',
|
|
||||||
type: undefined,
|
|
||||||
name: '',
|
|
||||||
webhook: '',
|
|
||||||
security_type: 'none',
|
|
||||||
secret: '',
|
|
||||||
keywords: [],
|
|
||||||
ip_list: [],
|
|
||||||
remark: '',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
try {
|
|
||||||
await formRef.value.validate();
|
|
||||||
submitLoading.value = true;
|
|
||||||
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
const method = isEdit.value ? 'put' : 'post';
|
|
||||||
const response = await axios[method]('/api/notification/robots/', robotForm, {
|
|
||||||
headers: { 'Authorization': token }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
message.success(isEdit.value ? '更新机器人成功' : '添加机器人成功');
|
|
||||||
handleDrawerClose();
|
|
||||||
loadRobotList(); // 重新加载列表
|
|
||||||
} else {
|
|
||||||
message.error(response.data.message || (isEdit.value ? '更新机器人失败' : '添加机器人失败'));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(isEdit.value ? 'Update robot error:' : 'Add robot error:', error);
|
|
||||||
message.error(isEdit.value ? '更新机器人失败' : '添加机器人失败');
|
|
||||||
} finally {
|
|
||||||
submitLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 测试机器人
|
|
||||||
const handleTestRobot = async (robot) => {
|
|
||||||
// 测试权限
|
|
||||||
if (!checkPermission('notification', 'test')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
const hide = message.loading('正在发送测试消息...', 0);
|
|
||||||
|
|
||||||
const response = await axios.post('/api/notification/robots/test/', {
|
|
||||||
robot_id: robot.robot_id
|
|
||||||
}, {
|
|
||||||
headers: { 'Authorization': token }
|
|
||||||
});
|
|
||||||
|
|
||||||
hide();
|
|
||||||
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
message.success('测试消息发送成功');
|
|
||||||
} else {
|
|
||||||
message.error(response.data.message || '发送测试消息失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Test robot error:', error);
|
|
||||||
message.error('发送测试消息失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除机器人
|
|
||||||
const handleDeleteRobot = async (robot) => {
|
|
||||||
// 删除权限
|
|
||||||
if (!checkPermission('notification', 'delete')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const token = localStorage.getItem('token');
|
|
||||||
const response = await axios.delete('/api/notification/robots/', {
|
|
||||||
headers: { 'Authorization': token },
|
|
||||||
data: { robot_id: robot.robot_id }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.code === 200) {
|
|
||||||
message.success('删除成功');
|
|
||||||
loadRobotList(); // 重新加载列表
|
|
||||||
} else {
|
|
||||||
message.error(response.data.message || '删除失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Delete robot error:', error);
|
|
||||||
message.error('删除失败');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 页面加载时获取机器人列表
|
|
||||||
onMounted(() => {
|
|
||||||
loadRobotList();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.page-header {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-header h2 {
|
|
||||||
margin: 0;
|
|
||||||
color: rgba(0, 0, 0, 0.85);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-card) {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-drawer-header) {
|
|
||||||
padding: 16px 24px;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-drawer-body) {
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-drawer-footer) {
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
padding: 10px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.ant-table-thead > tr > th) {
|
|
||||||
background: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-item-help {
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgba(0, 0, 0, 0.45);
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Reference in New Issue
Block a user