feat: Add Bark as a new alert notification channel. (#12338)

This commit is contained in:
Alan
2026-03-30 10:58:52 +08:00
committed by GitHub
parent 62c50aafe1
commit 05ea384a00
19 changed files with 281 additions and 0 deletions

View File

@@ -650,6 +650,28 @@ func sendAlerts(alert dto.AlertDTO, alertType, quota, quotaType string, params [
continue
}
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)
case constant.Bark:
todayCount, isValid := canSendAlertToday(alertType, quotaType, alert.SendCount, m)
if !isValid {
continue
}
var create = dto.AlertLogCreate{
Type: alertUtil.GetCronJobType(alert.Type),
AlertId: alert.ID,
Count: todayCount + 1,
}
alertInfo := alert
alertInfo.Type = alertType
create.AlertRule = alertUtil.ProcessAlertRule(alert)
create.AlertDetail = alertUtil.ProcessAlertDetail(alertInfo, quotaType, params, m)
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
alertErr := alertUtil.CreateBarkAlertLog(create, alertInfo, params, transport, agentInfo)
if alertErr != nil {
global.LOG.Infof("%s alert %s push failed, err: %v", alertType, m, alertErr.Error())
continue
}
alertUtil.CreateNewAlertTask(quota, alertType, quotaType, m)
default:
}
}

View File

@@ -30,6 +30,8 @@ func (s *AlertSender) Send(quota string, params []dto.Param) {
s.sendSMS(quota, params)
case constant.Email:
s.sendEmail(quota, params)
case constant.Bark:
s.sendBark(quota, params)
case constant.WeCom, constant.DingTalk, constant.FeiShu:
s.sendWebhook(quota, params, method)
}
@@ -45,6 +47,8 @@ func (s *AlertSender) ResourceSend(quota string, params []dto.Param) {
s.sendResourceSMS(quota, params)
case constant.Email:
s.sendResourceEmail(quota, params)
case constant.Bark:
s.sendResourceBark(quota, params)
case constant.WeCom, constant.DingTalk, constant.FeiShu:
s.sendResourceWebhook(quota, params, method)
}
@@ -101,6 +105,31 @@ func (s *AlertSender) sendEmail(quota string, params []dto.Param) {
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
}
func (s *AlertSender) sendBark(quota string, params []dto.Param) {
totalCount, isValid := s.canSendAlert(constant.Bark)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: totalCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
AlertRule: alertUtil.ProcessAlertRule(s.alert),
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
}
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo)
if err != nil {
global.LOG.Errorf("%s alert bark push failed: %v", s.alert.Type, err)
return
}
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Bark)
}
func (s *AlertSender) sendWebhook(quota string, params []dto.Param, method string) {
totalCount, isValid := s.canSendAlert(method)
if !isValid {
@@ -171,6 +200,30 @@ func (s *AlertSender) sendResourceEmail(quota string, params []dto.Param) {
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Email)
}
func (s *AlertSender) sendResourceBark(quota string, params []dto.Param) {
todayCount, isValid := s.canResourceSendAlert(constant.Bark)
if !isValid {
return
}
create := dto.AlertLogCreate{
Status: constant.AlertSuccess,
Count: todayCount + 1,
AlertId: s.alert.ID,
Type: s.alert.Type,
AlertRule: alertUtil.ProcessAlertRule(s.alert),
AlertDetail: alertUtil.ProcessAlertDetail(s.alert, quota, params, constant.Bark),
}
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
if err := alertUtil.CreateBarkAlertLog(create, s.alert, params, transport, agentInfo); err != nil {
global.LOG.Errorf("failed to send Bark alert: %v", err)
return
}
alertUtil.CreateNewAlertTask(quota, s.alert.Type, s.quotaType, constant.Bark)
}
func (s *AlertSender) sendResourceWebhook(quota string, params []dto.Param, method string) {
todayCount, isValid := s.canResourceSendAlert(method)
if !isValid {

View File

@@ -25,4 +25,5 @@ const (
DingTalk = "dingTalk"
FeiShu = "feiShu"
Custom = "custom"
Bark = "bark"
)

View File

@@ -11,6 +11,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/i18n"
"github.com/1Panel-dev/1Panel/agent/utils/bark"
"github.com/1Panel-dev/1Panel/agent/utils/email"
"github.com/1Panel-dev/1Panel/agent/utils/psutil"
"github.com/1Panel-dev/1Panel/agent/utils/re"
@@ -103,6 +104,40 @@ func CreateEmailAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params [
}
}
func CreateBarkAlertLog(create dto.AlertLogCreate, alert dto.AlertDTO, params []dto.Param, transport *http.Transport, agentInfo *dto.AgentInfo) error {
var alertLog model.AlertLog
alertRepo := repo.NewIAlertRepo()
create.Method = constant.Bark
barkConfig, err := alertRepo.GetConfig(alertRepo.WithByType(constant.Bark))
if err != nil {
return err
}
var barkInfo dto.AlertWebhookConfig
err = json.Unmarshal([]byte(barkConfig.Config), &barkInfo)
if err != nil {
return err
}
if barkInfo.Url == "" {
create.Message = "bark config url is required"
create.Status = constant.AlertError
return SaveAlertLog(create, &alertLog)
}
content := GetSendContent(alert.Type, params, agentInfo)
if content == "" {
content = i18n.GetMsgWithMap("CommonAlert", map[string]interface{}{"msg": alert.Title})
}
if err = bark.SendMessage(barkInfo.Url, i18n.GetMsgByKey("PanelAlertTitle"), content, transport); err != nil {
create.Message = err.Error()
create.Status = constant.AlertError
return SaveAlertLog(create, &alertLog)
}
create.Status = constant.AlertSuccess
return SaveAlertLog(create, &alertLog)
}
func SaveAlertLog(create dto.AlertLogCreate, alertLog *model.AlertLog) error {
alertRepo := repo.NewIAlertRepo()
if err := copier.Copy(&alertLog, &create); err != nil {

View File

@@ -66,6 +66,29 @@ func PushAlert(pushAlert dto.PushAlert) error {
continue
}
alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Email)
case constant.Bark:
todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Bark)
if err != nil || alert.SendCount <= todayCount {
continue
}
var create = dto.AlertLogCreate{
Type: alertUtil.GetCronJobType(alert.Type),
AlertId: alert.ID,
Count: todayCount + 1,
}
transport := xpack.LoadRequestTransport()
agentInfo, _ := xpack.GetAgentInfo()
params := alertUtil.CreateAlertParams(alertUtil.GetCronJobTypeName(pushAlert.Param))
alertDetail := alertUtil.ProcessAlertDetail(alert, pushAlert.TaskName, params, constant.Bark)
alertRule := alertUtil.ProcessAlertRule(alert)
create.AlertRule = alertRule
create.AlertDetail = alertDetail
err = alertUtil.CreateBarkAlertLog(create, alert, params, transport, agentInfo)
if err != nil {
global.LOG.Errorf("%s alert bark push failed: %v", alert.Type, err)
continue
}
alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), constant.Bark)
case constant.WeCom, constant.DingTalk, constant.FeiShu:
todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), m)
if err != nil || alert.SendCount <= todayCount {

46
agent/utils/bark/bark.go Normal file
View File

@@ -0,0 +1,46 @@
package bark
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type BarkMessage struct {
Title string `json:"title"`
Body string `json:"body"`
}
func SendMessage(url string, title string, body string, transport *http.Transport) error {
msg := BarkMessage{
Title: title,
Body: body,
}
data, err := json.Marshal(msg)
if err != nil {
return err
}
client := &http.Client{
Transport: transport,
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bark push failed with status code %d", resp.StatusCode)
}
return nil
}

View File

@@ -4085,6 +4085,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'Email',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: 'Total alerts sent before expiry (once daily)',
panelUpdateRulesHelper: 'Total alerts sent for new panel version (once daily)',
oneDaySendCountRulesHelper: 'Maximum alerts sent per day',
@@ -4254,6 +4255,7 @@ const message = {
userNameHelper: 'Username is empty, the sender address will be used by default',
alertConfigHelper: 'Configure alert notification channels to receive panel message push',
weComConfigHelper: 'WeCom alert notification configuration',
barkConfigHelper: 'Bark alert notification configuration',
wechatConfigHelper: 'WeChat Official Account alert notification configuration',
dingTalkConfigHelper: 'DingTalk alert notification configuration',
feiShuConfigHelper: 'Feishu alert notification configuration',

View File

@@ -4097,6 +4097,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'Correo',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: 'Número total de alertas enviadas antes de expirar (una vez al día)',
panelUpdateRulesHelper: 'Alertas totales enviadas por nueva versión del panel (una vez al día)',
oneDaySendCountRulesHelper: 'Número máximo de alertas diarias',
@@ -4265,6 +4266,7 @@ const message = {
wechatConfigHelper: 'Configuración de notificación de alerta de Cuenta Oficial WeChat',
dingTalkConfigHelper: 'Configuración de notificación de alerta DingTalk',
feiShuConfigHelper: 'Configuración de notificación de alerta Feishu',
barkConfigHelper: 'Configuración de notificación de alerta Bark',
webhookName: 'Nombre del bot',
webhookUrl: 'URL de Webhook',
alertConfigProHelper:

View File

@@ -4090,6 +4090,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'メール',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: '期限前に送信されるアラートの合計1日1回',
panelUpdateRulesHelper: '新しいパネルバージョンに関するアラートの合計1日1回',
oneDaySendCountRulesHelper: '1日に送信できる最大アラート回数',
@@ -4263,6 +4264,7 @@ const message = {
wechatConfigHelper: 'WeChat公式アカウントアラート通知設定',
dingTalkConfigHelper: 'DingTalkアラート通知設定',
feiShuConfigHelper: 'Feishuアラート通知設定',
barkConfigHelper: 'Barkアラート通知設定',
webhookName: 'ボット名',
webhookUrl: 'Webhook URL',
alertConfigProHelper:

View File

@@ -3998,6 +3998,7 @@ const message = {
feiShu: '페이슈',
mail: '이메일',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: '만료 발송된 알림 (하루 1)',
panelUpdateRulesHelper: ' 패널 버전에 대한 알림 (하루 1)',
oneDaySendCountRulesHelper: '하루 최대 발송 가능한 알림 ',
@@ -4166,6 +4167,7 @@ const message = {
wechatConfigHelper: 'WeChat 공식 계정 알림 구성',
dingTalkConfigHelper: 'DingTalk 알림 구성',
feiShuConfigHelper: 'Feishu 알림 구성',
barkConfigHelper: 'Bark 알림 구성',
webhookName: ' 이름',
webhookUrl: 'Webhook URL',
alertConfigProHelper:

View File

@@ -4144,6 +4144,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'E-mel',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: 'Jumlah amaran dihantar sebelum tamat tempoh (sekali sehari)',
panelUpdateRulesHelper: 'Jumlah amaran dihantar untuk versi panel baharu (sekali sehari)',
oneDaySendCountRulesHelper: 'Maksimum amaran dihantar setiap hari',
@@ -4321,6 +4322,7 @@ const message = {
wechatConfigHelper: 'Konfigurasi pemberitahuan amaran Akaun Rasmi WeChat',
dingTalkConfigHelper: 'Konfigurasi pemberitahuan amaran DingTalk',
feiShuConfigHelper: 'Konfigurasi pemberitahuan amaran Feishu',
barkConfigHelper: 'Konfigurasi pemberitahuan amaran Bark',
webhookName: 'Nama bot',
webhookUrl: 'URL Webhook',
alertConfigProHelper: 'Edisi Profesional turut menyokong amaran WeCom, DingTalk, Feishu dan SMS.',

View File

@@ -4294,6 +4294,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'E-mail',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: 'Alertas totais enviados antes da expiração (uma vez por dia)',
panelUpdateRulesHelper: 'Alertas totais enviados para nova versão do painel (uma vez por dia)',
oneDaySendCountRulesHelper: 'Número máximo de alertas enviados por dia',
@@ -4470,6 +4471,7 @@ const message = {
wechatConfigHelper: 'Configuração de notificação de alerta da Conta Oficial WeChat',
dingTalkConfigHelper: 'Configuração de notificação de alerta DingTalk',
feiShuConfigHelper: 'Configuração de notificação de alerta Feishu',
barkConfigHelper: 'Configuração de notificação de alerta Bark',
webhookName: 'Nome do bot',
webhookUrl: 'URL do Webhook',
alertConfigProHelper:

View File

@@ -4146,6 +4146,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'Электронная Почта',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: 'Общее количество уведомлений до истечения срока действия (раз в день)',
panelUpdateRulesHelper: 'Общее количество уведомлений для новой версии панели (раз в день)',
oneDaySendCountRulesHelper: 'Максимальное количество уведомлений в день',
@@ -4326,6 +4327,7 @@ const message = {
wechatConfigHelper: 'Конфигурация уведомлений официального аккаунта WeChat',
dingTalkConfigHelper: 'Конфигурация уведомлений DingTalk',
feiShuConfigHelper: 'Конфигурация уведомлений Feishu',
barkConfigHelper: 'Конфигурация уведомлений Bark',
webhookName: 'Имя бота',
webhookUrl: 'URL Webhook',
alertConfigProHelper:

View File

@@ -4136,6 +4136,7 @@ const message = {
feiShu: 'FeiShu',
mail: 'E-posta',
weCom: 'WeCom',
bark: 'Bark',
sendCountRulesHelper: 'Sona ermeden önce gönderilen toplam uyarılar (günde bir kez)',
panelUpdateRulesHelper:
'Yeni panel sürümü algılandığında bir kez uyarı gönder (işlenmezse ertesi gün tekrar gönderilir)',
@@ -4314,6 +4315,7 @@ const message = {
wechatConfigHelper: 'WeChat Resmi Hesap uyarı bildirim yapılandırması',
dingTalkConfigHelper: 'DingTalk uyarı bildirim yapılandırması',
feiShuConfigHelper: 'Feishu uyarı bildirim yapılandırması',
barkConfigHelper: 'Bark uyarı bildirim yapılandırması',
webhookName: 'Bot adı',
webhookUrl: 'Webhook URL',
alertConfigProHelper:

View File

@@ -3759,6 +3759,7 @@ const message = {
feiShu: '飛書通知',
mail: '信箱通知',
weCom: '企業微信',
bark: 'Bark',
sendCountRulesHelper: '到期前發送告警的總數每日僅發送一次',
panelUpdateRulesHelper: '新版本發送告警總數每日僅發送一次',
oneDaySendCountRulesHelper: '每日發送告警的總數',
@@ -3924,6 +3925,7 @@ const message = {
wechatConfigHelper: '微信公眾號告警通知設定',
dingTalkConfigHelper: '釘釘告警通知設定',
feiShuConfigHelper: '飛書告警通知設定',
barkConfigHelper: 'Bark 告警通知設定',
webhookName: '機器人名稱',
webhookUrl: 'Webhook 位址',
alertConfigProHelper: '專業版額外支援企業微信釘釘飛書及簡訊告警',

View File

@@ -3756,6 +3756,7 @@ const message = {
feiShu: '飞书通知',
mail: '邮箱通知',
weCom: '企业微信',
bark: 'Bark',
sendCountRulesHelper: '到期前发送告警的总数每日仅发送一次',
panelUpdateRulesHelper: '新版本发送告警总数每日仅发送一次',
oneDaySendCountRulesHelper: '每日发送告警的总数',
@@ -3922,6 +3923,7 @@ const message = {
wechatConfigHelper: '微信公众号告警通知配置',
dingTalkConfigHelper: '钉钉告警通知配置',
feiShuConfigHelper: '飞书告警通知配置',
barkConfigHelper: 'Bark 告警通知配置',
webhookName: '机器人名称',
webhookUrl: 'Webhook 地址',
alertConfigProHelper: '专业版额外支持企业微信钉钉飞书及短信告警',

View File

@@ -322,6 +322,7 @@
<el-form-item :label="$t('xpack.alert.alertMethod')" prop="sendMethod">
<el-select class="selectClass" v-model="dialogData.rowData!.sendMethod" multiple cleanable>
<el-option value="mail" :label="$t('xpack.alert.mail')" />
<el-option value="bark" :label="$t('xpack.alert.bark')" />
<el-option
value="weCom"
v-if="!globalStore.isIntl"

View File

@@ -229,6 +229,8 @@ const formatMethod = (row: Alert.AlertLog) => {
return t('xpack.alert.sms');
case 'webhook':
return t('xpack.alert.webhook');
case 'bark':
return t('xpack.alert.bark');
default:
return t('xpack.alert.unknown');
}

View File

@@ -121,6 +121,59 @@
</el-button>
</div>
</el-card>
<el-card class="rounded-2xl shadow hover:shadow-md transition-all">
<div class="flex items-center justify-between mb-2">
<div class="text-lg font-semibold">{{ $t('xpack.alert.bark') }}</div>
<div>
<el-button
plain
round
size="default"
:disabled="!barkConfig.id"
@click="onChangeBark(barkConfig.id)"
>
{{ $t('commons.button.edit') }}
</el-button>
<el-button
size="default"
plain
round
:disabled="!barkConfig.id"
@click="onDelete(barkConfig.id)"
>
{{ $t('commons.button.delete') }}
</el-button>
</div>
</div>
<div class="text-sm mb-2">{{ $t('xpack.alert.barkConfigHelper') }}</div>
<el-divider class="!mb-2 !mt-3" />
<div class="text-sm config-form" v-if="barkConfig.id">
<el-form
@submit.prevent
ref="alertFormRef"
:label-position="mobile ? 'top' : 'left'"
label-width="110px"
>
<el-form-item :label="$t('xpack.alert.webhookName')" prop="displayName">
{{ barkConfig.config.displayName }}
</el-form-item>
<el-form-item :label="$t('xpack.alert.webhookUrl')" prop="url">
<div class="webhook-field">
<el-tooltip :content="barkConfig.config.url" placement="top" effect="dark">
<span class="webhook-text">
{{ barkConfig.config.url }}
</span>
</el-tooltip>
</div>
</el-form-item>
</el-form>
</div>
<div v-else class="flex items-center justify-center" style="height: 257px">
<el-button size="large" round plain type="primary" @click="onChangeBark(0)">
{{ $t('commons.button.create') }}
</el-button>
</div>
</el-card>
<el-card
class="rounded-2xl shadow hover:shadow-md transition-all"
v-if="globalStore.isProductPro && !globalStore.isIntl"
@@ -453,6 +506,18 @@ const defaultSmsConfig: Alert.SmsConfig = {
};
const smsConfig = ref<Alert.SmsConfig>({ ...defaultSmsConfig });
const defaultBarkConfig: Alert.WebhookConfig = {
id: undefined,
type: 'bark',
title: 'xpack.alert.bark',
status: 'Enable',
config: {
displayName: '',
url: 'https://api.day.app/YOUR_KEY',
},
};
const barkConfig = ref<Alert.WebhookConfig>({ ...defaultBarkConfig });
const defaultWeComConfig: Alert.WebhookConfig = {
id: undefined,
type: 'weCom',
@@ -566,6 +631,10 @@ const search = async () => {
const feiShuFound = res.data.find((s: any) => s.type === 'feiShu');
assignConfig(feiShuFound, feiShuConfig, defaultFeiShuConfig);
const barkFound = res.data.find((s: any) => s.type === 'bark');
assignConfig(barkFound, barkConfig, defaultBarkConfig);
isInitialized.value = true;
} finally {
loading.value = false;
@@ -680,6 +749,15 @@ const onChangeFeiShu = (id: number) => {
});
};
const onChangeBark = (id: number) => {
webHookRef.value.acceptParams({
id: id,
config: barkConfig.value.config,
type: 'bark',
title: barkConfig.value.title,
});
};
onMounted(async () => {
await search();
if (globalStore.isProductPro && !globalStore.isIntl) {