发布 0.1.0:通知渠道、告警规则与多项体验修复
CI / test (push) Successful in 27s
Release / release (push) Successful in 26s

This commit is contained in:
Wang Defa
2026-07-10 17:31:19 +08:00
parent 9d0c116ce3
commit 5464d2dfea
68 changed files with 1604 additions and 359 deletions
+46 -6
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NButton, NInput, NSpin, NSwitch, NTabPane, NTabs, useMessage } from 'naive-ui'
import { NButton, NInput, NSpin, NSwitch, NTabPane, NTabs } from 'naive-ui'
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
@@ -8,6 +8,7 @@ import {
getSecuritySetting,
getTaskSetting,
getTelegramSetting,
listNotifyChannels,
listNotifyTemplates,
testTelegram,
updateNotifyEvents,
@@ -20,27 +21,47 @@ import FootNote from '@/components/FootNote.vue'
import FormField from '@/components/FormField.vue'
import AboutTab from '@/components/settings/AboutTab.vue'
import AccountSecurityCard from '@/components/settings/AccountSecurityCard.vue'
import NotifyChannelCard from '@/components/settings/NotifyChannelCard.vue'
import NotifyTemplateModal from '@/components/settings/NotifyTemplateModal.vue'
import { useAsync } from '@/composables/useAsync'
import type { NotifyEventsSetting, NotifyTemplateItem, SecuritySetting } from '@/types/api'
import type {
NotifyChannelItem,
NotifyEventsSetting,
NotifyTemplateItem,
SecuritySetting,
} from '@/types/api'
import { useAuthStore } from '@/stores/auth'
import { useToast } from '@/composables/useToast'
const message = useMessage()
const message = useToast()
const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
// 设置页以 tab 组织:通知(管理 + 方式)/ 任务(抢机熔断阈值)/ 安全(WAF/网络/账号)
const tab = ref('notify')
// OAuth 绑定回跳:成功/失败提示后清理 query,并停在安全 tab
// OAuth 绑定回跳:成功/失败提示后清理 query,并停在安全 tab;
// 绑定使令牌版本递增,fragment 携带的新 token 须先落地,否则后续请求 401
onMounted(() => {
const { oauth, oauthError } = route.query
if (oauth !== 'bound' && !oauthError) return
consumeBindToken()
tab.value = 'security'
if (oauth === 'bound') message.success('外部身份绑定成功')
if (typeof oauthError === 'string' && oauthError) message.error(oauthError)
void router.replace({ query: {} })
})
/** 消费绑定回跳 fragment 中的新会话 token(无感换发,不进服务端日志) */
function consumeBindToken() {
const hash = window.location.hash
if (!hash.startsWith('#oauthToken=')) return
const token = decodeURIComponent(hash.slice('#oauthToken='.length))
history.replaceState(null, '', window.location.pathname + window.location.search)
if (token) auth.setSession(token, new Date(Date.now() + 24 * 3600e3).toISOString())
}
// ---- 通知管理:按事件类型的推送开关 ----
const events = useAsync(getNotifyEvents)
const eventForm = ref<NotifyEventsSetting>({
@@ -138,6 +159,14 @@ async function saveEvents() {
}
}
// ---- 通知方式:多渠道(webhook/ntfy/bark/smtp)----
const channels = useAsync(listNotifyChannels)
/** 渠道保存接口回全渠道最新视图,就地覆盖免二次拉取 */
function onChannelsSaved(items: NotifyChannelItem[]) {
channels.data.value = items
}
// ---- 通知方式:Telegram 渠道配置 ----
const setting = useAsync(getTelegramSetting)
const enabled = ref(false)
@@ -431,13 +460,24 @@ async function sendTest() {
拉进群 浏览器打开 api.telegram.org/bot&lt;token&gt;/getUpdates
message.chat.id 即为 chat_id
</FootNote>
<!-- 多渠道: Telegram 并存,可同时启用任意多个 -->
<div class="border-t border-line-soft">
<NotifyChannelCard
v-for="ch in channels.data.value ?? []"
:key="ch.type"
:item="ch"
@saved="onChannelsSaved"
/>
</div>
</div>
<div class="panel px-4.5 py-3.5">
<div class="text-[13px] font-semibold">送达规则</div>
<div class="mt-1.5 text-xs leading-relaxed text-ink-3">
发送为异步10 秒超时失败仅记后端日志不影响任务执行同一事件按状态变化去重
会刷屏云端事件另受日志回传链路约束未建链路的租户不产生该类通知
事件同时投递到全部已启用渠道发送为异步10 秒超时单渠道失败仅记后端日志
影响其他渠道与任务执行同一事件按状态变化去重不会刷屏
云端事件另受日志回传链路约束未建链路的租户不产生该类通知
</div>
</div>
</div>