feat: support pushover as notification

This commit is contained in:
imlonghao 2025-04-12 11:07:32 +08:00
parent 4e3f499d76
commit 6b8dbf5235
No known key found for this signature in database
GPG Key ID: BB80A757B3E37324
11 changed files with 239 additions and 0 deletions

View File

@ -14,6 +14,7 @@ const (
NotifyChannelTypeEmail = NotifyChannelType("email") NotifyChannelTypeEmail = NotifyChannelType("email")
NotifyChannelTypeGotify = NotifyChannelType("gotify") NotifyChannelTypeGotify = NotifyChannelType("gotify")
NotifyChannelTypeLark = NotifyChannelType("lark") NotifyChannelTypeLark = NotifyChannelType("lark")
NotifyChannelTypePushover = NotifyChannelType("pushover")
NotifyChannelTypePushPlus = NotifyChannelType("pushplus") NotifyChannelTypePushPlus = NotifyChannelType("pushplus")
NotifyChannelTypeServerChan = NotifyChannelType("serverchan") NotifyChannelTypeServerChan = NotifyChannelType("serverchan")
NotifyChannelTypeTelegram = NotifyChannelType("telegram") NotifyChannelTypeTelegram = NotifyChannelType("telegram")

View File

@ -10,6 +10,7 @@ import (
pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email" pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify" pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark" pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
pPushover "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus" pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan" pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram" pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
@ -59,6 +60,12 @@ func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]a
WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"), WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
}) })
case domain.NotifyChannelTypePushover:
return pPushover.NewNotifier(&pPushover.NotifierConfig{
Token: maputil.GetString(channelConfig, "token"),
User: maputil.GetString(channelConfig, "user"),
})
case domain.NotifyChannelTypePushPlus: case domain.NotifyChannelTypePushPlus:
return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{ return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
Token: maputil.GetString(channelConfig, "token"), Token: maputil.GetString(channelConfig, "token"),

View File

@ -0,0 +1,102 @@
package pushover
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"github.com/pkg/errors"
"github.com/usual2970/certimate/internal/pkg/core/notifier"
)
type NotifierConfig struct {
Token string `json:"token"` // 应用 API Token
User string `json:"user"` // 用户/分组 Key
}
type NotifierProvider struct {
config *NotifierConfig
logger *slog.Logger
// 未来将移除
httpClient *http.Client
}
var _ notifier.Notifier = (*NotifierProvider)(nil)
func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
if config == nil {
panic("config is nil")
}
return &NotifierProvider{
config: config,
httpClient: http.DefaultClient,
}, nil
}
func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
if logger == nil {
n.logger = slog.Default()
} else {
n.logger = logger
}
return n
}
// Notify 发送通知
// 参考文档https://pushover.net/api
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
// 请求体
reqBody := &struct {
Token string `json:"token"`
User string `json:"user"`
Title string `json:"title"`
Message string `json:"message"`
}{
Token: n.config.Token,
User: n.config.User,
Title: subject,
Message: message,
}
// Make request
body, err := json.Marshal(reqBody)
if err != nil {
return nil, errors.Wrap(err, "encode message body")
}
req, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
"https://api.pushover.net/1/messages.json",
bytes.NewReader(body),
)
if err != nil {
return nil, errors.Wrap(err, "create new request")
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
// Send request to pushover service
resp, err := n.httpClient.Do(req)
if err != nil {
return nil, errors.Wrapf(err, "send request to pushover server")
}
defer resp.Body.Close()
result, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "read response")
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("pushover returned status code %d: %s", resp.StatusCode, string(result))
}
return &notifier.NotifyResult{}, nil
}

View File

@ -0,0 +1,62 @@
package pushover_test
import (
"context"
"flag"
"fmt"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushover"
)
const (
mockSubject = "test_subject"
mockMessage = "test_message"
)
var (
fToken string
fUser string
)
func init() {
argsPrefix := "CERTIMATE_NOTIFIER_PUSHOVER_"
flag.StringVar(&fToken, argsPrefix+"TOKEN", "", "")
flag.StringVar(&fUser, argsPrefix+"USER", "", "")
}
/*
Shell command to run this test:
go test -v ./pushover_test.go -args \
--CERTIMATE_NOTIFIER_PUSHOVER_TOKEN="your-pushover-token" \
--CERTIMATE_NOTIFIER_PUSHOVER_USER="your-pushover-user" \
*/
func TestNotify(t *testing.T) {
flag.Parse()
t.Run("Notify", func(t *testing.T) {
t.Log(strings.Join([]string{
"args:",
fmt.Sprintf("TOKEN: %v", fToken),
}, "\n"))
notifier, err := provider.NewNotifier(&provider.NotifierConfig{
Token: fToken,
User: fUser,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
if err != nil {
t.Errorf("err: %+v", err)
return
}
t.Logf("ok: %v", res)
})
}

View File

@ -9,6 +9,7 @@ import NotifyChannelEditFormDingTalkFields from "./NotifyChannelEditFormDingTalk
import NotifyChannelEditFormEmailFields from "./NotifyChannelEditFormEmailFields"; import NotifyChannelEditFormEmailFields from "./NotifyChannelEditFormEmailFields";
import NotifyChannelEditFormGotifyFields from "./NotifyChannelEditFormGotifyFields.tsx"; import NotifyChannelEditFormGotifyFields from "./NotifyChannelEditFormGotifyFields.tsx";
import NotifyChannelEditFormLarkFields from "./NotifyChannelEditFormLarkFields"; import NotifyChannelEditFormLarkFields from "./NotifyChannelEditFormLarkFields";
import NotifyChannelEditFormPushoverFields from "./NotifyChannelEditFormPushoverFields";
import NotifyChannelEditFormPushPlusFields from "./NotifyChannelEditFormPushPlusFields"; import NotifyChannelEditFormPushPlusFields from "./NotifyChannelEditFormPushPlusFields";
import NotifyChannelEditFormServerChanFields from "./NotifyChannelEditFormServerChanFields"; import NotifyChannelEditFormServerChanFields from "./NotifyChannelEditFormServerChanFields";
import NotifyChannelEditFormTelegramFields from "./NotifyChannelEditFormTelegramFields"; import NotifyChannelEditFormTelegramFields from "./NotifyChannelEditFormTelegramFields";
@ -54,6 +55,8 @@ const NotifyChannelEditForm = forwardRef<NotifyChannelEditFormInstance, NotifyCh
return <NotifyChannelEditFormGotifyFields />; return <NotifyChannelEditFormGotifyFields />;
case NOTIFY_CHANNELS.LARK: case NOTIFY_CHANNELS.LARK:
return <NotifyChannelEditFormLarkFields />; return <NotifyChannelEditFormLarkFields />;
case NOTIFY_CHANNELS.PUSHOVER:
return <NotifyChannelEditFormPushoverFields />;
case NOTIFY_CHANNELS.PUSHPLUS: case NOTIFY_CHANNELS.PUSHPLUS:
return <NotifyChannelEditFormPushPlusFields />; return <NotifyChannelEditFormPushPlusFields />;
case NOTIFY_CHANNELS.SERVERCHAN: case NOTIFY_CHANNELS.SERVERCHAN:

View File

@ -0,0 +1,41 @@
import { useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
const NotifyChannelEditFormPushoverFields = () => {
const { t } = useTranslation();
const formSchema = z.object({
token: z
.string({ message: t("settings.notification.channel.form.pushover_token.placeholder") })
.nonempty(t("settings.notification.channel.form.pushover_token.placeholder")),
user: z
.string({ message: t("settings.notification.channel.form.pushover_user.placeholder") })
.nonempty(t("settings.notification.channel.form.pushover_user.placeholder")),
});
const formRule = createSchemaFieldRule(formSchema);
return (
<>
<Form.Item
name="token"
label={t("settings.notification.channel.form.pushover_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("settings.notification.channel.form.pushover_token.tooltip") }}></span>}
>
<Input placeholder={t("settings.notification.channel.form.pushover_token.placeholder")} />
</Form.Item>
<Form.Item
name="user"
label={t("settings.notification.channel.form.pushover_user.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("settings.notification.channel.form.pushover_user.tooltip") }}></span>}
>
<Input placeholder={t("settings.notification.channel.form.pushover_user.placeholder")} />
</Form.Item>
</>
);
};
export default NotifyChannelEditFormPushoverFields;

View File

@ -44,6 +44,7 @@ export const NOTIFY_CHANNELS = Object.freeze({
EMAIL: "email", EMAIL: "email",
GOTIFY: "gotify", GOTIFY: "gotify",
LARK: "lark", LARK: "lark",
PUSHOVER: "pushover",
PUSHPLUS: "pushplus", PUSHPLUS: "pushplus",
SERVERCHAN: "serverchan", SERVERCHAN: "serverchan",
TELEGRAM: "telegram", TELEGRAM: "telegram",
@ -64,6 +65,7 @@ export type NotifyChannelsSettingsContent = {
[NOTIFY_CHANNELS.EMAIL]?: EmailNotifyChannelConfig; [NOTIFY_CHANNELS.EMAIL]?: EmailNotifyChannelConfig;
[NOTIFY_CHANNELS.GOTIFY]?: GotifyNotifyChannelConfig; [NOTIFY_CHANNELS.GOTIFY]?: GotifyNotifyChannelConfig;
[NOTIFY_CHANNELS.LARK]?: LarkNotifyChannelConfig; [NOTIFY_CHANNELS.LARK]?: LarkNotifyChannelConfig;
[NOTIFY_CHANNELS.PUSHOVER]?: PushoverNotifyChannelConfig;
[NOTIFY_CHANNELS.PUSHPLUS]?: PushPlusNotifyChannelConfig; [NOTIFY_CHANNELS.PUSHPLUS]?: PushPlusNotifyChannelConfig;
[NOTIFY_CHANNELS.SERVERCHAN]?: ServerChanNotifyChannelConfig; [NOTIFY_CHANNELS.SERVERCHAN]?: ServerChanNotifyChannelConfig;
[NOTIFY_CHANNELS.TELEGRAM]?: TelegramNotifyChannelConfig; [NOTIFY_CHANNELS.TELEGRAM]?: TelegramNotifyChannelConfig;
@ -106,6 +108,12 @@ export type LarkNotifyChannelConfig = {
enabled?: boolean; enabled?: boolean;
}; };
export type PushoverNotifyChannelConfig = {
token: string;
user: string;
enabled?: boolean;
};
export type PushPlusNotifyChannelConfig = { export type PushPlusNotifyChannelConfig = {
token: string; token: string;
enabled?: boolean; enabled?: boolean;
@ -143,6 +151,7 @@ export const notifyChannelsMap: Map<NotifyChannel["type"], NotifyChannel> = new
[NOTIFY_CHANNELS.DINGTALK, "common.notifier.dingtalk"], [NOTIFY_CHANNELS.DINGTALK, "common.notifier.dingtalk"],
[NOTIFY_CHANNELS.GOTIFY, "common.notifier.gotify"], [NOTIFY_CHANNELS.GOTIFY, "common.notifier.gotify"],
[NOTIFY_CHANNELS.LARK, "common.notifier.lark"], [NOTIFY_CHANNELS.LARK, "common.notifier.lark"],
[NOTIFY_CHANNELS.PUSHOVER, "common.notifier.pushover"],
[NOTIFY_CHANNELS.PUSHPLUS, "common.notifier.pushplus"], [NOTIFY_CHANNELS.PUSHPLUS, "common.notifier.pushplus"],
[NOTIFY_CHANNELS.WECOM, "common.notifier.wecom"], [NOTIFY_CHANNELS.WECOM, "common.notifier.wecom"],
[NOTIFY_CHANNELS.TELEGRAM, "common.notifier.telegram"], [NOTIFY_CHANNELS.TELEGRAM, "common.notifier.telegram"],

View File

@ -41,6 +41,7 @@
"common.notifier.email": "Email", "common.notifier.email": "Email",
"common.notifier.gotify": "Gotify", "common.notifier.gotify": "Gotify",
"common.notifier.lark": "Lark", "common.notifier.lark": "Lark",
"common.notifier.pushover": "Pushover",
"common.notifier.pushplus": "PushPlus", "common.notifier.pushplus": "PushPlus",
"common.notifier.serverchan": "ServerChan", "common.notifier.serverchan": "ServerChan",
"common.notifier.telegram": "Telegram", "common.notifier.telegram": "Telegram",

View File

@ -66,6 +66,12 @@
"settings.notification.channel.form.lark_webhook_url.label": "Webhook URL", "settings.notification.channel.form.lark_webhook_url.label": "Webhook URL",
"settings.notification.channel.form.lark_webhook_url.placeholder": "Please enter Webhook URL", "settings.notification.channel.form.lark_webhook_url.placeholder": "Please enter Webhook URL",
"settings.notification.channel.form.lark_webhook_url.tooltip": "For more information, see <a href=\"https://www.feishu.cn/hc/en-US/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/en-US/articles/807992406756</a>", "settings.notification.channel.form.lark_webhook_url.tooltip": "For more information, see <a href=\"https://www.feishu.cn/hc/en-US/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/en-US/articles/807992406756</a>",
"settings.notification.channel.form.pushover_token.placeholder": "Please enter Application API Token",
"settings.notification.channel.form.pushover_token.label": "Application API Token",
"settings.notification.channel.form.pushover_token.tooltip": "For more information, see <a href=\"https://pushover.net/api#registration\" target=\"_blank\">https://pushover.net/api#registration</a>",
"settings.notification.channel.form.pushover_user.placeholder": "Please enter User/Group Key",
"settings.notification.channel.form.pushover_user.label": "User/Group Key",
"settings.notification.channel.form.pushover_user.tooltip": "For more information, see <a href=\"https://pushover.net/api#identifiers\" target=\"_blank\">https://pushover.net/api#identifiers</a>",
"settings.notification.channel.form.pushplus_token.placeholder": "Please enter Token", "settings.notification.channel.form.pushplus_token.placeholder": "Please enter Token",
"settings.notification.channel.form.pushplus_token.label": "Token", "settings.notification.channel.form.pushplus_token.label": "Token",
"settings.notification.channel.form.pushplus_token.tooltip": "For more information, see <a href=\"https://www.pushplus.plus/push1.html\" target=\"_blank\">https://www.pushplus.plus/push1.html</a>", "settings.notification.channel.form.pushplus_token.tooltip": "For more information, see <a href=\"https://www.pushplus.plus/push1.html\" target=\"_blank\">https://www.pushplus.plus/push1.html</a>",

View File

@ -41,6 +41,7 @@
"common.notifier.email": "邮件", "common.notifier.email": "邮件",
"common.notifier.gotify": "Gotify", "common.notifier.gotify": "Gotify",
"common.notifier.lark": "飞书", "common.notifier.lark": "飞书",
"common.notifier.pushover": "Pushover",
"common.notifier.pushplus": "PushPlus推送加", "common.notifier.pushplus": "PushPlus推送加",
"common.notifier.serverchan": "Server 酱", "common.notifier.serverchan": "Server 酱",
"common.notifier.telegram": "Telegram", "common.notifier.telegram": "Telegram",

View File

@ -66,6 +66,12 @@
"settings.notification.channel.form.lark_webhook_url.label": "机器人 Webhook 地址", "settings.notification.channel.form.lark_webhook_url.label": "机器人 Webhook 地址",
"settings.notification.channel.form.lark_webhook_url.placeholder": "请输入机器人 Webhook 地址", "settings.notification.channel.form.lark_webhook_url.placeholder": "请输入机器人 Webhook 地址",
"settings.notification.channel.form.lark_webhook_url.tooltip": "这是什么?请参阅 <a href=\"https://www.feishu.cn/hc/zh-CN/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/zh-CN/articles/807992406756</a>", "settings.notification.channel.form.lark_webhook_url.tooltip": "这是什么?请参阅 <a href=\"https://www.feishu.cn/hc/zh-CN/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/zh-CN/articles/807992406756</a>",
"settings.notification.channel.form.pushover_token.placeholder": "请输入应用 API Token",
"settings.notification.channel.form.pushover_token.label": "应用 API Token",
"settings.notification.channel.form.pushover_token.tooltip": "这是什么?请参阅 <a href=\"https://pushover.net/api#registration\" target=\"_blank\">https://pushover.net/api#registration</a>",
"settings.notification.channel.form.pushover_user.placeholder": "请输入用户/分组 Key",
"settings.notification.channel.form.pushover_user.label": "用户/分组 Key",
"settings.notification.channel.form.pushover_user.tooltip": "这是什么?请参阅 <a href=\"https://pushover.net/api#identifiers\" target=\"_blank\">https://pushover.net/api#identifiers</a>",
"settings.notification.channel.form.pushplus_token.placeholder": "请输入Token", "settings.notification.channel.form.pushplus_token.placeholder": "请输入Token",
"settings.notification.channel.form.pushplus_token.label": "Token", "settings.notification.channel.form.pushplus_token.label": "Token",
"settings.notification.channel.form.pushplus_token.tooltip": "请参阅 <a href=\"https://www.pushplus.plus/push1.html\" target=\"_blank\">https://www.pushplus.plus/push1.html</a>", "settings.notification.channel.form.pushplus_token.tooltip": "请参阅 <a href=\"https://www.pushplus.plus/push1.html\" target=\"_blank\">https://www.pushplus.plus/push1.html</a>",