mirror of https://github.com/usual2970/certimate
				
				
				
			feat: add Bark notification channel and related settings
							parent
							
								
									a27a9f55a7
								
							
						
					
					
						commit
						657964cda4
					
				| 
						 | 
				
			
			@ -7,6 +7,7 @@ const (
 | 
			
		|||
	NotifyChannelLark       = "lark"
 | 
			
		||||
	NotifyChannelServerChan = "serverchan"
 | 
			
		||||
	NotifyChannelMail       = "mail"
 | 
			
		||||
	NotifyChannelBark       = "bark"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type NotifyTestPushReq struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ import (
 | 
			
		|||
	"github.com/usual2970/certimate/internal/utils/app"
 | 
			
		||||
 | 
			
		||||
	notifyPackage "github.com/nikoksr/notify"
 | 
			
		||||
	"github.com/nikoksr/notify/service/bark"
 | 
			
		||||
	"github.com/nikoksr/notify/service/dingding"
 | 
			
		||||
	"github.com/nikoksr/notify/service/http"
 | 
			
		||||
	"github.com/nikoksr/notify/service/lark"
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +109,8 @@ func getNotifier(channel string, conf map[string]any) (notifyPackage.Notifier, e
 | 
			
		|||
		return getServerChanNotifier(conf), nil
 | 
			
		||||
	case domain.NotifyChannelMail:
 | 
			
		||||
		return getMailNotifier(conf), nil
 | 
			
		||||
	case domain.NotifyChannelBark:
 | 
			
		||||
		return getBarkNotifier(conf), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, fmt.Errorf("notifier not found")
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +160,15 @@ func getServerChanNotifier(conf map[string]any) notifyPackage.Notifier {
 | 
			
		|||
	return rs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getBarkNotifier(conf map[string]any) notifyPackage.Notifier {
 | 
			
		||||
	deviceKey := getString(conf, "deviceKey")
 | 
			
		||||
	serverURL := getString(conf, "serverUrl")
 | 
			
		||||
	if serverURL == "" {
 | 
			
		||||
		return bark.New(deviceKey)
 | 
			
		||||
	}
 | 
			
		||||
	return bark.NewWithServers(deviceKey, serverURL)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getDingTalkNotifier(conf map[string]any) notifyPackage.Notifier {
 | 
			
		||||
	return dingding.New(&dingding.Config{
 | 
			
		||||
		Token:  getString(conf, "accessToken"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,246 @@
 | 
			
		|||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
 | 
			
		||||
import { Button } from "@/components/ui/button";
 | 
			
		||||
import { Input } from "@/components/ui/input";
 | 
			
		||||
import { Label } from "@/components/ui/label";
 | 
			
		||||
import { Switch } from "@/components/ui/switch";
 | 
			
		||||
import { useToast } from "@/components/ui/use-toast";
 | 
			
		||||
import { getErrMessage } from "@/lib/error";
 | 
			
		||||
import { NotifyChannels, NotifyChannelBark } from "@/domain/settings";
 | 
			
		||||
import { update } from "@/repository/settings";
 | 
			
		||||
import { useNotifyContext } from "@/providers/notify";
 | 
			
		||||
import { notifyTest } from "@/api/notify";
 | 
			
		||||
import Show from "@/components/Show";
 | 
			
		||||
 | 
			
		||||
type BarkSetting = {
 | 
			
		||||
  id: string;
 | 
			
		||||
  name: string;
 | 
			
		||||
  data: NotifyChannelBark;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Bark = () => {
 | 
			
		||||
  const { config, setChannels } = useNotifyContext();
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [changed, setChanged] = useState<boolean>(false);
 | 
			
		||||
 | 
			
		||||
  const [bark, setBark] = useState<BarkSetting>({
 | 
			
		||||
    id: config.id ?? "",
 | 
			
		||||
    name: "notifyChannels",
 | 
			
		||||
    data: {
 | 
			
		||||
      serverUrl: "",
 | 
			
		||||
      deviceKey: "",
 | 
			
		||||
      enabled: false,
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const [originBark, setOriginBark] = useState<BarkSetting>({
 | 
			
		||||
    id: config.id ?? "",
 | 
			
		||||
    name: "notifyChannels",
 | 
			
		||||
    data: {
 | 
			
		||||
      serverUrl: "",
 | 
			
		||||
      deviceKey: "",
 | 
			
		||||
      enabled: false,
 | 
			
		||||
    },
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setChanged(false);
 | 
			
		||||
  }, [config]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const data = getDetailBark();
 | 
			
		||||
    setOriginBark({
 | 
			
		||||
      id: config.id ?? "",
 | 
			
		||||
      name: "common.provider.bark",
 | 
			
		||||
      data,
 | 
			
		||||
    });
 | 
			
		||||
  }, [config]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const data = getDetailBark();
 | 
			
		||||
    setBark({
 | 
			
		||||
      id: config.id ?? "",
 | 
			
		||||
      name: "common.provider.bark",
 | 
			
		||||
      data,
 | 
			
		||||
    });
 | 
			
		||||
  }, [config]);
 | 
			
		||||
 | 
			
		||||
  const { toast } = useToast();
 | 
			
		||||
 | 
			
		||||
  const checkChanged = (data: NotifyChannelBark) => {
 | 
			
		||||
    if (data.serverUrl !== originBark.data.serverUrl || data.deviceKey !== originBark.data.deviceKey) {
 | 
			
		||||
      setChanged(true);
 | 
			
		||||
    } else {
 | 
			
		||||
      setChanged(false);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getDetailBark = () => {
 | 
			
		||||
    const df: NotifyChannelBark = {
 | 
			
		||||
      serverUrl: "",
 | 
			
		||||
      deviceKey: "",
 | 
			
		||||
      enabled: false,
 | 
			
		||||
    };
 | 
			
		||||
    if (!config.content) {
 | 
			
		||||
      return df;
 | 
			
		||||
    }
 | 
			
		||||
    const chanels = config.content as NotifyChannels;
 | 
			
		||||
    if (!chanels.bark) {
 | 
			
		||||
      return df;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return chanels.bark as NotifyChannelBark;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleSaveClick = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      const resp = await update({
 | 
			
		||||
        ...config,
 | 
			
		||||
        name: "notifyChannels",
 | 
			
		||||
        content: {
 | 
			
		||||
          ...config.content,
 | 
			
		||||
          bark: {
 | 
			
		||||
            ...bark.data,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      setChannels(resp);
 | 
			
		||||
      toast({
 | 
			
		||||
        title: t("common.save.succeeded.message"),
 | 
			
		||||
        description: t("settings.notification.config.saved.message"),
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      const msg = getErrMessage(e);
 | 
			
		||||
 | 
			
		||||
      toast({
 | 
			
		||||
        title: t("common.save.failed.message"),
 | 
			
		||||
        description: `${t("settings.notification.config.failed.message")}: ${msg}`,
 | 
			
		||||
        variant: "destructive",
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handlePushTestClick = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      await notifyTest("bark");
 | 
			
		||||
 | 
			
		||||
      toast({
 | 
			
		||||
        title: t("settings.notification.config.push.test.message.success.message"),
 | 
			
		||||
        description: t("settings.notification.config.push.test.message.success.message"),
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      const msg = getErrMessage(e);
 | 
			
		||||
 | 
			
		||||
      toast({
 | 
			
		||||
        title: t("settings.notification.config.push.test.message.failed.message"),
 | 
			
		||||
        description: `${t("settings.notification.config.push.test.message.failed.message")}: ${msg}`,
 | 
			
		||||
        variant: "destructive",
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleSwitchChange = async () => {
 | 
			
		||||
    const newData = {
 | 
			
		||||
      ...bark,
 | 
			
		||||
      data: {
 | 
			
		||||
        ...bark.data,
 | 
			
		||||
        enabled: !bark.data.enabled,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
    setBark(newData);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const resp = await update({
 | 
			
		||||
        ...config,
 | 
			
		||||
        name: "notifyChannels",
 | 
			
		||||
        content: {
 | 
			
		||||
          ...config.content,
 | 
			
		||||
          bark: {
 | 
			
		||||
            ...newData.data,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      setChannels(resp);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      const msg = getErrMessage(e);
 | 
			
		||||
 | 
			
		||||
      toast({
 | 
			
		||||
        title: t("common.save.failed.message"),
 | 
			
		||||
        description: `${t("settings.notification.config.failed.message")}: ${msg}`,
 | 
			
		||||
        variant: "destructive",
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div>
 | 
			
		||||
      <Input
 | 
			
		||||
        placeholder={t("settings.notification.bark.serverUrl.placeholder")}
 | 
			
		||||
        value={bark.data.serverUrl}
 | 
			
		||||
        onChange={(e) => {
 | 
			
		||||
          const newData = {
 | 
			
		||||
            ...bark,
 | 
			
		||||
            data: {
 | 
			
		||||
              ...bark.data,
 | 
			
		||||
              serverUrl: e.target.value,
 | 
			
		||||
            },
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          checkChanged(newData.data);
 | 
			
		||||
          setBark(newData);
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <Input
 | 
			
		||||
        placeholder={t("settings.notification.bark.deviceKey.placeholder")}
 | 
			
		||||
        value={bark.data.deviceKey}
 | 
			
		||||
        onChange={(e) => {
 | 
			
		||||
          const newData = {
 | 
			
		||||
            ...bark,
 | 
			
		||||
            data: {
 | 
			
		||||
              ...bark.data,
 | 
			
		||||
              deviceKey: e.target.value,
 | 
			
		||||
            },
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
          checkChanged(newData.data);
 | 
			
		||||
          setBark(newData);
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <div className="flex items-center space-x-1 mt-2">
 | 
			
		||||
        <Switch id="airplane-mode" checked={bark.data.enabled} onCheckedChange={handleSwitchChange} />
 | 
			
		||||
        <Label htmlFor="airplane-mode">{t("settings.notification.config.enable")}</Label>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div className="flex justify-end mt-2">
 | 
			
		||||
        <Show when={changed}>
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={() => {
 | 
			
		||||
              handleSaveClick();
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {t("common.save")}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Show>
 | 
			
		||||
 | 
			
		||||
        <Show when={!changed && bark.id != ""}>
 | 
			
		||||
          <Button
 | 
			
		||||
            variant="secondary"
 | 
			
		||||
            onClick={() => {
 | 
			
		||||
              handlePushTestClick();
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {t("settings.notification.config.push.test.message")}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Show>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Bark;
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ export type NotifyChannels = {
 | 
			
		|||
  webhook?: NotifyChannel;
 | 
			
		||||
  serverchan?: NotifyChannel;
 | 
			
		||||
  mail?: NotifyChannelMail;
 | 
			
		||||
  bark?: NotifyChannelBark;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type NotifyChannel =
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +33,8 @@ export type NotifyChannel =
 | 
			
		|||
  | NotifyChannelTelegram
 | 
			
		||||
  | NotifyChannelWebhook
 | 
			
		||||
  | NotifyChannelServerChan
 | 
			
		||||
  | NotifyChannelMail;
 | 
			
		||||
  | NotifyChannelMail
 | 
			
		||||
  | NotifyChannelBark;
 | 
			
		||||
 | 
			
		||||
export type NotifyChannelDingTalk = {
 | 
			
		||||
  accessToken: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +73,12 @@ export type NotifyChannelMail = {
 | 
			
		|||
  enabled: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type NotifyChannelBark = {
 | 
			
		||||
  deviceKey: string;
 | 
			
		||||
  serverUrl: string;
 | 
			
		||||
  enabled: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const defaultNotifyTemplate: NotifyTemplate = {
 | 
			
		||||
  title: "您有 {COUNT} 张证书即将过期",
 | 
			
		||||
  content: "有 {COUNT} 张证书即将过期,域名分别为 {DOMAINS},请保持关注!",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,5 +86,6 @@
 | 
			
		|||
  "common.provider.dingtalk": "DingTalk",
 | 
			
		||||
  "common.provider.telegram": "Telegram",
 | 
			
		||||
  "common.provider.lark": "Lark",
 | 
			
		||||
  "common.provider.mail": "Mail"
 | 
			
		||||
  "common.provider.mail": "Mail",
 | 
			
		||||
  "common.provider.bark": "Bark"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,8 @@
 | 
			
		|||
  "settings.notification.mail.smtp_port.placeholder": "SMTP server port, if not set, default is 25",
 | 
			
		||||
  "settings.notification.mail.username.placeholder": "username",
 | 
			
		||||
  "settings.notification.mail.password.placeholder": "password",
 | 
			
		||||
  "settings.notification.bark.serverUrl.placeholder": "Server URL, e.g. https://your-bark-server.com, leave it blank to use the bark default server",
 | 
			
		||||
  "settings.notification.bark.deviceKey.placeholder": "Device Key,e.g. XXXXXXXXXXXXXXXXXXXX",
 | 
			
		||||
 | 
			
		||||
  "settings.ca.tab": "Certificate Authority",
 | 
			
		||||
  "settings.ca.provider.errmsg.empty": "Please select a Certificate Authority",
 | 
			
		||||
| 
						 | 
				
			
			@ -49,4 +51,3 @@
 | 
			
		|||
  "settings.ca.eab_hmac_key.errmsg.empty": "Please enter EAB_HMAC_KEY.",
 | 
			
		||||
  "settings.ca.eab_kid_hmac_key.errmsg.empty": "Please enter EAB_KID and EAB_HMAC_KEY"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,6 +86,6 @@
 | 
			
		|||
  "common.provider.dingtalk": "钉钉",
 | 
			
		||||
  "common.provider.telegram": "Telegram",
 | 
			
		||||
  "common.provider.lark": "飞书",
 | 
			
		||||
  "common.provider.mail": "电子邮件"
 | 
			
		||||
  "common.provider.mail": "电子邮件",
 | 
			
		||||
  "common.provider.bark": "Bark"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,8 @@
 | 
			
		|||
  "settings.notification.mail.smtp_port.placeholder": "SMTP服务器端口, 如果未设置, 默认为25",
 | 
			
		||||
  "settings.notification.mail.username.placeholder": "用于登录到邮件服务器的用户名",
 | 
			
		||||
  "settings.notification.mail.password.placeholder": "用于登录到邮件服务器的密码",
 | 
			
		||||
  "settings.notification.bark.serverUrl.placeholder": "服务器URL,形如: https://your-bark-server.com, 留空则使用 Bark 默认服务器",
 | 
			
		||||
  "settings.notification.bark.deviceKey.placeholder": "设备密钥,形如: XXXXXXXXXXXXXXXXXXXX",
 | 
			
		||||
 | 
			
		||||
  "settings.ca.tab": "证书颁发机构(CA)",
 | 
			
		||||
  "settings.ca.provider.errmsg.empty": "请选择证书分发机构",
 | 
			
		||||
| 
						 | 
				
			
			@ -49,4 +51,3 @@
 | 
			
		|||
  "settings.ca.eab_hmac_key.errmsg.empty": "请输入EAB_HMAC_KEY",
 | 
			
		||||
  "settings.ca.eab_kid_hmac_key.errmsg.empty": "请输入EAB_KID和EAB_HMAC_KEY"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ import Telegram from "@/components/notify/Telegram";
 | 
			
		|||
import Webhook from "@/components/notify/Webhook";
 | 
			
		||||
import ServerChan from "@/components/notify/ServerChan";
 | 
			
		||||
import Mail from "@/components/notify/Mail";
 | 
			
		||||
import Bark from "@/components/notify/Bark";
 | 
			
		||||
import { NotifyProvider } from "@/providers/notify";
 | 
			
		||||
 | 
			
		||||
const Notify = () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,12 +63,20 @@ const Notify = () => {
 | 
			
		|||
                <ServerChan />
 | 
			
		||||
              </AccordionContent>
 | 
			
		||||
            </AccordionItem>
 | 
			
		||||
 | 
			
		||||
            <AccordionItem value="item-7" className="dark:border-stone-200">
 | 
			
		||||
              <AccordionTrigger>{t("common.provider.mail")}</AccordionTrigger>
 | 
			
		||||
              <AccordionContent>
 | 
			
		||||
                <Mail />
 | 
			
		||||
              </AccordionContent>
 | 
			
		||||
            </AccordionItem>
 | 
			
		||||
 | 
			
		||||
            <AccordionItem value="item-8" className="dark:border-stone-200">
 | 
			
		||||
              <AccordionTrigger>{t("common.provider.bark")}</AccordionTrigger>
 | 
			
		||||
              <AccordionContent>
 | 
			
		||||
                <Bark />
 | 
			
		||||
              </AccordionContent>
 | 
			
		||||
            </AccordionItem>
 | 
			
		||||
          </Accordion>
 | 
			
		||||
        </div>
 | 
			
		||||
      </NotifyProvider>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue