mirror of
https://github.com/certimate-go/certimate.git
synced 2026-07-20 21:01:41 +08:00
feat(provider): new notification provider: matrix
This commit is contained in:
parent
3970eef77e
commit
62ad00870b
@ -398,6 +398,13 @@ type AccessConfigForLiteSSL struct {
|
||||
AccessConfigForACMEExternalAccountBinding
|
||||
}
|
||||
|
||||
type AccessConfigForMatrix struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
UserId string `json:"userId"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
RoomId string `json:"roomId,omitempty"`
|
||||
}
|
||||
|
||||
type AccessConfigForMattermost struct {
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
Username string `json:"username"`
|
||||
|
||||
@ -89,6 +89,7 @@ const (
|
||||
AccessProviderTypeLinode = AccessProviderType("linode")
|
||||
AccessProviderTypeLiteSSL = AccessProviderType("litessl")
|
||||
AccessProviderTypeLocal = AccessProviderType("local")
|
||||
AccessProviderTypeMatrix = AccessProviderType("matrix")
|
||||
AccessProviderTypeMattermost = AccessProviderType("mattermost")
|
||||
AccessProviderTypeMohua = AccessProviderType("mohua")
|
||||
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
|
||||
@ -473,6 +474,7 @@ const (
|
||||
NotificationProviderTypeDiscordBot = NotificationProviderType(AccessProviderTypeDiscordBot)
|
||||
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
|
||||
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
|
||||
NotificationProviderTypeMatrix = NotificationProviderType(AccessProviderTypeMatrix)
|
||||
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
|
||||
NotificationProviderTypeSlackBot = NotificationProviderType(AccessProviderTypeSlackBot)
|
||||
NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot)
|
||||
|
||||
27
internal/notify/notifiers/sp_matrix.go
Normal file
27
internal/notify/notifiers/sp_matrix.go
Normal file
@ -0,0 +1,27 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
"github.com/certimate-go/certimate/pkg/core/notifier/providers/matrix"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.NotificationProviderTypeMatrix, func(options *ProviderFactoryOptions) (core.Notifier, error) {
|
||||
credentials := domain.AccessConfigForMatrix{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := matrix.NewNotifier(&matrix.NotifierConfig{
|
||||
ServerUrl: credentials.ServerUrl,
|
||||
UserId: credentials.UserId,
|
||||
AccessToken: credentials.AccessToken,
|
||||
RoomId: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "roomId", credentials.RoomId),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
73
pkg/core/notifier/providers/matrix/matrix.go
Normal file
73
pkg/core/notifier/providers/matrix/matrix.go
Normal file
@ -0,0 +1,73 @@
|
||||
package matrix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/notifier"
|
||||
matrixsdk "github.com/certimate-go/certimate/pkg/sdk3rd/matrix"
|
||||
)
|
||||
|
||||
type NotifierConfig struct {
|
||||
// Homeserver URL (Element web URL or Matrix homeserver base URL).
|
||||
// URL homeserver (адрес Element или базовый URL Matrix).
|
||||
ServerUrl string `json:"serverUrl"`
|
||||
// User ID (MXID), e.g. @bot:example.org.
|
||||
// Идентификатор пользователя (MXID), например @bot:example.org.
|
||||
UserId string `json:"userId"`
|
||||
// Access token from the homeserver (bot or user).
|
||||
// Access token с homeserver (бот или пользователь).
|
||||
AccessToken string `json:"accessToken"`
|
||||
// Room ID (!room:server) for notifications.
|
||||
// ID комнаты (!room:server) для уведомлений.
|
||||
RoomId string `json:"roomId"`
|
||||
}
|
||||
|
||||
type Notifier struct {
|
||||
config *NotifierConfig
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
var _ notifier.Provider = (*Notifier)(nil)
|
||||
|
||||
func NewNotifier(config *NotifierConfig) (*Notifier, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("the configuration of the notifier provider is nil")
|
||||
}
|
||||
|
||||
return &Notifier{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *Notifier) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
n.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
n.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) Notify(ctx context.Context, subject string, message string) (*notifier.NotifyResult, error) {
|
||||
if n.config.RoomId == "" {
|
||||
return nil, errors.New("matrix: config `roomId` is required")
|
||||
}
|
||||
|
||||
client, err := matrixsdk.NewClient(n.config.ServerUrl,
|
||||
matrixsdk.WithUserId(n.config.UserId),
|
||||
matrixsdk.WithAccessToken(n.config.AccessToken),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("matrix: %w", err)
|
||||
}
|
||||
|
||||
body := fmt.Sprintf("%s\n\n%s", subject, message)
|
||||
if err := client.SendTextMessageToRoom(ctx, n.config.RoomId, body); err != nil {
|
||||
return nil, fmt.Errorf("matrix: %w", err)
|
||||
}
|
||||
|
||||
return ¬ifier.NotifyResult{}, nil
|
||||
}
|
||||
51
pkg/core/notifier/providers/matrix/matrix_test.go
Normal file
51
pkg/core/notifier/providers/matrix/matrix_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package matrix_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core/notifier/internal/tester"
|
||||
impl "github.com/certimate-go/certimate/pkg/core/notifier/providers/matrix"
|
||||
)
|
||||
|
||||
var (
|
||||
fp = tester.Args("MATRIX_")
|
||||
fServerUrl string
|
||||
fUserId string
|
||||
fAccessToken string
|
||||
fRoomId string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fp.DefineString(&fServerUrl, "SERVERURL")
|
||||
fp.DefineString(&fUserId, "USERID")
|
||||
fp.DefineString(&fAccessToken, "ACCESSTOKEN")
|
||||
fp.DefineString(&fRoomId, "ROOMID")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -v ./matrix_test.go -args \
|
||||
--MATRIX_SERVERURL="https://example.com/your-matrix-server" \
|
||||
--MATRIX_USERID="@bot:example.org" \
|
||||
--MATRIX_ACCESSTOKEN="your-access-token" \
|
||||
--MATRIX_ROOMID="!room:example.org"
|
||||
*/
|
||||
func TestProvider(t *testing.T) {
|
||||
fp.Parse()
|
||||
|
||||
t.Run("Notify", func(t *testing.T) {
|
||||
provider, err := impl.NewNotifier(&impl.NotifierConfig{
|
||||
ServerUrl: fServerUrl,
|
||||
UserId: fUserId,
|
||||
AccessToken: fAccessToken,
|
||||
RoomId: fRoomId,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
return
|
||||
}
|
||||
|
||||
tester.TestNotify(t, provider, tester.TestNotifyArgs{})
|
||||
})
|
||||
}
|
||||
36
pkg/sdk3rd/matrix/api_send_message.go
Normal file
36
pkg/sdk3rd/matrix/api_send_message.go
Normal file
@ -0,0 +1,36 @@
|
||||
package matrix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SendText posts an m.room.message event (m.text).
|
||||
// Отправляет текстовое сообщение в комнату (m.room.message, msgtype m.text).
|
||||
// REF: https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
|
||||
func (c *Client) SendTextMessageToRoom(ctx context.Context, roomId, msgBody string) error {
|
||||
if strings.TrimSpace(roomId) == "" {
|
||||
return fmt.Errorf("sdkerr: unset roomId")
|
||||
}
|
||||
|
||||
txnId := newTransactionId()
|
||||
path := fmt.Sprintf("/_matrix/client/v3/rooms/%s/send/m.room.message/%s",
|
||||
url.PathEscape(roomId), url.PathEscape(txnId))
|
||||
|
||||
payload := map[string]any{
|
||||
"msgtype": "m.text",
|
||||
"body": msgBody,
|
||||
}
|
||||
|
||||
_, err := c.rc.R().
|
||||
SetContext(ctx).
|
||||
SetBody(payload).
|
||||
Put(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sdkerr: api error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
17
pkg/sdk3rd/matrix/api_versions.go
Normal file
17
pkg/sdk3rd/matrix/api_versions.go
Normal file
@ -0,0 +1,17 @@
|
||||
package matrix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// probeVersions checks that the homeserver exposes the Client-Server API.
|
||||
// Проверяет доступность homeserver (GET /_matrix/client/versions).
|
||||
// REF: https://spec.matrix.org/latest/client-server-api/#get_matrixclientversions
|
||||
func (c *Client) probeVersions() error {
|
||||
_, err := c.rc.R().Get("/_matrix/client/versions")
|
||||
if err != nil {
|
||||
return fmt.Errorf("sdkerr: failed to probe Matrix Client API versions: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
88
pkg/sdk3rd/matrix/client.go
Normal file
88
pkg/sdk3rd/matrix/client.go
Normal file
@ -0,0 +1,88 @@
|
||||
package matrix
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/app"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
rc *resty.Client
|
||||
}
|
||||
|
||||
func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
|
||||
opts := &Options{}
|
||||
for _, fn := range optFns {
|
||||
fn(opts)
|
||||
}
|
||||
|
||||
if serverUrl == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset serverUrl")
|
||||
}
|
||||
if _, err := url.Parse(serverUrl); err != nil {
|
||||
return nil, fmt.Errorf("sdkerr: invalid serverUrl: %w", err)
|
||||
}
|
||||
if opts.AccessToken == "" {
|
||||
return nil, fmt.Errorf("sdkerr: unset accessToken")
|
||||
}
|
||||
|
||||
baseUrl, _ := resolveBaseUrl(strings.TrimSuffix(serverUrl, "/"))
|
||||
if baseUrl == "" {
|
||||
baseUrl = serverUrl
|
||||
}
|
||||
|
||||
client := &Client{}
|
||||
client.rc = resty.New().
|
||||
SetBaseURL(strings.TrimSuffix(baseUrl, "/")).
|
||||
SetHeader("Authorization", "Bearer "+opts.AccessToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetHeader("User-Agent", app.AppUserAgent).
|
||||
if err := client.probeVersions(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||
c.rc.SetTimeout(timeout)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) SetTLSConfig(config *tls.Config) *Client {
|
||||
c.rc.SetTLSClientConfig(config)
|
||||
return c
|
||||
}
|
||||
|
||||
func resolveBaseUrl(serverUrl string) (string, error) {
|
||||
var wkJSON struct {
|
||||
Homeserver struct {
|
||||
BaseURL string `json:"base_url"`
|
||||
} `json:"m.homeserver"`
|
||||
}
|
||||
|
||||
_, err := resty.New().R().
|
||||
SetResult(&wkJSON).
|
||||
Get(serverUrl + "/.well-known/matrix/client")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to discovery Matrix Client API: %w", err)
|
||||
} else if strings.TrimSpace(wkJSON.Homeserver.BaseURL) != "" {
|
||||
return wkJSON.Homeserver.BaseURL, nil
|
||||
} else {
|
||||
return serverUrl, nil
|
||||
}
|
||||
}
|
||||
|
||||
func newTransactionId() string {
|
||||
b := make([]byte, 8)
|
||||
_, _ = rand.Read(b)
|
||||
return fmt.Sprintf("certimate_%d_%s", time.Now().UnixNano(), hex.EncodeToString(b))
|
||||
}
|
||||
20
pkg/sdk3rd/matrix/options.go
Normal file
20
pkg/sdk3rd/matrix/options.go
Normal file
@ -0,0 +1,20 @@
|
||||
package matrix
|
||||
|
||||
type Options struct {
|
||||
UserId string
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
type OptionsFunc func(*Options)
|
||||
|
||||
func WithUserId(userId string) OptionsFunc {
|
||||
return func(o *Options) {
|
||||
o.UserId = userId
|
||||
}
|
||||
}
|
||||
|
||||
func WithAccessToken(accessToken string) OptionsFunc {
|
||||
return func(o *Options) {
|
||||
o.AccessToken = accessToken
|
||||
}
|
||||
}
|
||||
1
ui/public/imgs/providers/matrix.svg
Normal file
1
ui/public/imgs/providers/matrix.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 64 64"><rect width="64" height="64" fill="#0dbd8b" rx="12"/><path fill="#fff" d="M32 12c-8 0-14 4-18 10l6 5q4.5-6 12-6c7.5 0 9 2 12 6l6-5c-4-6-10-10-18-10M18 30l-8 6v16l8-6zm28 0v16l8 6V30l-8-6zm-14 4c-5 0-9 4-9 9v7h6v-7q0-3 3-3c3 0 3 1 3 3v7h6v-7c0-5-4-9-9-9"/></svg>
|
||||
|
After Width: | Height: | Size: 347 B |
@ -73,6 +73,7 @@ import AccessConfigFieldsProviderLarkBot from "./AccessConfigFieldsProviderLarkB
|
||||
import AccessConfigFieldsProviderLeCDN from "./AccessConfigFieldsProviderLeCDN";
|
||||
import AccessConfigFieldsProviderLinode from "./AccessConfigFieldsProviderLinode";
|
||||
import AccessConfigFieldsProviderLiteSSL from "./AccessConfigFieldsProviderLiteSSL";
|
||||
import AccessConfigFieldsProviderMatrix from "./AccessConfigFieldsProviderMatrix";
|
||||
import AccessConfigFieldsProviderMattermost from "./AccessConfigFieldsProviderMattermost";
|
||||
import AccessConfigFieldsProviderMohua from "./AccessConfigFieldsProviderMohua";
|
||||
import AccessConfigFieldsProviderNamecheap from "./AccessConfigFieldsProviderNamecheap";
|
||||
@ -196,6 +197,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
|
||||
[ACCESS_PROVIDERS.INFOMANIAK]: AccessConfigFieldsProviderInfomaniak,
|
||||
[ACCESS_PROVIDERS.LINODE]: AccessConfigFieldsProviderLinode,
|
||||
[ACCESS_PROVIDERS.LITESSL]: AccessConfigFieldsProviderLiteSSL,
|
||||
[ACCESS_PROVIDERS.MATRIX]: AccessConfigFieldsProviderMatrix,
|
||||
[ACCESS_PROVIDERS.MATTERMOST]: AccessConfigFieldsProviderMattermost,
|
||||
[ACCESS_PROVIDERS.MOHUA]: AccessConfigFieldsProviderMohua,
|
||||
[ACCESS_PROVIDERS.NAMECHEAP]: AccessConfigFieldsProviderNamecheap,
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { core, z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const AccessConfigFormFieldsProviderMatrix = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "serverUrl"]}
|
||||
initialValue={initialValues.serverUrl}
|
||||
label={t("access.form.matrix_server_url.label")}
|
||||
rules={[formRule]}
|
||||
>
|
||||
<Input type="url" placeholder={t("access.form.matrix_server_url.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "userId"]}
|
||||
initialValue={initialValues.userId}
|
||||
label={t("access.form.matrix_user_id.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.matrix_user_id.tooltip") }} />}
|
||||
>
|
||||
<Input autoComplete="new-password" placeholder={t("access.form.matrix_user_id.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "accessToken"]}
|
||||
initialValue={initialValues.accessToken}
|
||||
label={t("access.form.matrix_access_token.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.matrix_access_token.tooltip") }} />}
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder={t("access.form.matrix_access_token.placeholder")} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={[parentNamePath, "roomId"]}
|
||||
initialValue={initialValues.roomId}
|
||||
label={t("access.form.matrix_room_id.label")}
|
||||
extra={t("access.form.matrix_room_id.help")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.matrix_room_id.tooltip") }} />}
|
||||
>
|
||||
<Input allowClear placeholder={t("access.form.matrix_room_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
serverUrl: "https://matrix-client.matrix.org",
|
||||
userId: "",
|
||||
accessToken: "",
|
||||
roomId: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
serverUrl: z.url({ protocol: core.regexes.httpProtocol }),
|
||||
userId: z.string().nonempty().startsWith("@"),
|
||||
accessToken: z.string().nonempty(),
|
||||
roomId: z.string().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(AccessConfigFormFieldsProviderMatrix, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -4,6 +4,7 @@ import { NOTIFICATION_PROVIDERS, type NotificationProviderType } from "@/domain/
|
||||
|
||||
import BizNotifyNodeConfigFieldsProviderDiscordBot from "./BizNotifyNodeConfigFieldsProviderDiscordBot";
|
||||
import BizNotifyNodeConfigFieldsProviderEmail from "./BizNotifyNodeConfigFieldsProviderEmail";
|
||||
import BizNotifyNodeConfigFieldsProviderMatrix from "./BizNotifyNodeConfigFieldsProviderMatrix";
|
||||
import BizNotifyNodeConfigFieldsProviderMattermost from "./BizNotifyNodeConfigFieldsProviderMattermost";
|
||||
import BizNotifyNodeConfigFieldsProviderSlackBot from "./BizNotifyNodeConfigFieldsProviderSlackBot";
|
||||
import BizNotifyNodeConfigFieldsProviderTelegramBot from "./BizNotifyNodeConfigFieldsProviderTelegramBot";
|
||||
@ -16,6 +17,7 @@ const providerComponentMap: Partial<Record<NotificationProviderType, React.Compo
|
||||
*/
|
||||
[NOTIFICATION_PROVIDERS.DISCORDBOT]: BizNotifyNodeConfigFieldsProviderDiscordBot,
|
||||
[NOTIFICATION_PROVIDERS.EMAIL]: BizNotifyNodeConfigFieldsProviderEmail,
|
||||
[NOTIFICATION_PROVIDERS.MATRIX]: BizNotifyNodeConfigFieldsProviderMatrix,
|
||||
[NOTIFICATION_PROVIDERS.MATTERMOST]: BizNotifyNodeConfigFieldsProviderMattermost,
|
||||
[NOTIFICATION_PROVIDERS.SLACKBOT]: BizNotifyNodeConfigFieldsProviderSlackBot,
|
||||
[NOTIFICATION_PROVIDERS.TELEGRAMBOT]: BizNotifyNodeConfigFieldsProviderTelegramBot,
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
import type { getI18n } from "react-i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const BizNotifyNodeConfigFieldsProviderMatrix = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "roomId"]}
|
||||
label={t("workflow_node.notify.form.matrix_room_id.label")}
|
||||
extra={t("workflow_node.notify.form.matrix_room_id.help")}
|
||||
rules={[formRule]}
|
||||
initialValue={initialValues?.roomId}
|
||||
>
|
||||
<Input allowClear placeholder={t("workflow_node.notify.form.matrix_room_id.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<Record<string, unknown>> => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const getSchema = (_opts?: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
return z.object({
|
||||
roomId: z.string().nullish(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizNotifyNodeConfigFieldsProviderMatrix, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@ -89,6 +89,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
|
||||
LINODE: "linode",
|
||||
LITESSL: "litessl",
|
||||
LOCAL: "local",
|
||||
MATRIX: "matrix",
|
||||
MATTERMOST: "mattermost",
|
||||
MOHUA: "mohua",
|
||||
NAMECHEAP: "namecheap",
|
||||
@ -283,6 +284,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
|
||||
[ACCESS_PROVIDERS.DISCORDBOT, "provider.discordbot", "/imgs/providers/discord.svg", [ACCESS_USAGES.NOTIFICATION]],
|
||||
[ACCESS_PROVIDERS.SLACKBOT, "provider.slackbot", "/imgs/providers/slack.svg", [ACCESS_USAGES.NOTIFICATION]],
|
||||
[ACCESS_PROVIDERS.TELEGRAMBOT, "provider.telegrambot", "/imgs/providers/telegram.svg", [ACCESS_USAGES.NOTIFICATION]],
|
||||
[ACCESS_PROVIDERS.MATRIX, "provider.matrix", "/imgs/providers/matrix.svg", [ACCESS_USAGES.NOTIFICATION]],
|
||||
[ACCESS_PROVIDERS.MATTERMOST, "provider.mattermost", "/imgs/providers/mattermost.svg", [ACCESS_USAGES.NOTIFICATION]],
|
||||
] satisfies Array<[AccessProviderType, string, string, AccessUsageType[], "builtin"] | [AccessProviderType, string, string, AccessUsageType[]]>
|
||||
).map(([type, name, icon, usages, builtin]) => [
|
||||
@ -943,6 +945,7 @@ export const NOTIFICATION_PROVIDERS = Object.freeze({
|
||||
DISCORDBOT: `${ACCESS_PROVIDERS.DISCORDBOT}`,
|
||||
EMAIL: `${ACCESS_PROVIDERS.EMAIL}`,
|
||||
LARKBOT: `${ACCESS_PROVIDERS.LARKBOT}`,
|
||||
MATRIX: `${ACCESS_PROVIDERS.MATRIX}`,
|
||||
MATTERMOST: `${ACCESS_PROVIDERS.MATTERMOST}`,
|
||||
SLACKBOT: `${ACCESS_PROVIDERS.SLACKBOT}`,
|
||||
TELEGRAMBOT: `${ACCESS_PROVIDERS.TELEGRAMBOT}`,
|
||||
@ -969,6 +972,7 @@ export const notificationProvidersMap: Map<NotificationProvider["type"] | string
|
||||
[NOTIFICATION_PROVIDERS.DISCORDBOT],
|
||||
[NOTIFICATION_PROVIDERS.SLACKBOT],
|
||||
[NOTIFICATION_PROVIDERS.TELEGRAMBOT],
|
||||
[NOTIFICATION_PROVIDERS.MATRIX],
|
||||
[NOTIFICATION_PROVIDERS.MATTERMOST],
|
||||
] satisfies Array<[NotificationProviderType]>
|
||||
).map(([type]) => [
|
||||
|
||||
@ -879,6 +879,26 @@
|
||||
"litessl_eab": {
|
||||
"guide": "Learn more about using EAB key in LiteSSL: <br><a href=\"https://freessl.cn/automation/eab-manager\" target=\"_blank\">https://freessl.cn/automation/eab-manager</a>"
|
||||
},
|
||||
"matrix_server_url": {
|
||||
"label": "Matrix server URL",
|
||||
"placeholder": "Please enter Matrix server URL"
|
||||
},
|
||||
"matrix_user_id": {
|
||||
"label": "Matrix user ID",
|
||||
"placeholder": "Please enter Matrix user ID",
|
||||
"tooltip": "For more information, see <a href=\"https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/</a>"
|
||||
},
|
||||
"matrix_access_token": {
|
||||
"label": "Matrix access token",
|
||||
"placeholder": "Please enter Matrix access token",
|
||||
"tooltip": "For more information, see <a href=\"https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/#your-access-token\" target=\"_blank\">https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/</a>"
|
||||
},
|
||||
"matrix_room_id": {
|
||||
"label": "Matrix room ID (Optional)",
|
||||
"placeholder": "Please enter the default Matrix room ID",
|
||||
"help": "Notes: It can be overrided in the workflows. Invite the bot to the room first.",
|
||||
"tooltip": "For more information, see <a href=\"https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/#advanced\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/</a>"
|
||||
},
|
||||
"mattermost_server_url": {
|
||||
"label": "Mattermost server URL",
|
||||
"placeholder": "Please enter Mattermost server URL"
|
||||
@ -895,7 +915,7 @@
|
||||
"label": "Mattermost channel ID (Optional)",
|
||||
"placeholder": "Please enter the default Mattermost channel ID",
|
||||
"help": "Notes: It can be overrided in the workflows.",
|
||||
"tooltip": "How to get it? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page."
|
||||
"tooltip": "How to get it? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details”. You can directly see the channel ID on the pop-up page."
|
||||
},
|
||||
"mohua_username": {
|
||||
"label": "Mohua Cloud username",
|
||||
|
||||
@ -191,6 +191,7 @@
|
||||
"linode_los": "Linode - Object Storage",
|
||||
"litessl": "LiteSSL",
|
||||
"local": "Local host",
|
||||
"matrix": "Matrix / ElementX",
|
||||
"mattermost": "Mattermost",
|
||||
"mohua": "Mohua",
|
||||
"mohua_mvh": "Mohua - MVH (Virtual Host)",
|
||||
|
||||
@ -2784,7 +2784,7 @@
|
||||
"webhook_data": {
|
||||
"label": "Webhook data (Optional)",
|
||||
"placeholder": "Please enter Webhook data",
|
||||
"help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
|
||||
"help": "Notes: Leave it blank to use the default Webhook data provided by the selected credential.",
|
||||
"vartips": "Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${CERTIMATE_DEPLOYER_COMMONNAME}</strong>: <br>The primary domain or IP address of the certificate.</li><li><strong>${CERTIMATE_DEPLOYER_SUBJECTALTNAMES}</strong>: <br>The domains or IP addresses of the certificate, separated by semicolons.</li><li><strong>${CERTIMATE_DEPLOYER_CERTIFICATE}</strong>: <br>The PEM format content of the certificate file.</li><li><strong>${CERTIMATE_DEPLOYER_CERTIFICATE_SERVER}</strong>: <br>The PEM format content of the server certificate file.</li><li><strong>${CERTIMATE_DEPLOYER_CERTIFICATE_INTERMEDIA}</strong>: <br>The PEM format content of the intermediate CA certificate file.</li><li><strong>${CERTIMATE_DEPLOYER_PRIVATEKEY}</strong>: <br>The PEM format content of the private key file.</li></ol>"
|
||||
},
|
||||
"webhook_timeout": {
|
||||
@ -2890,7 +2890,7 @@
|
||||
"discordbot_channel_id": {
|
||||
"label": "Discord channel ID (Optional)",
|
||||
"placeholder": "Please enter Discord channel ID",
|
||||
"help": "Notes: Leave it blank to use the default channel ID provided by the credential."
|
||||
"help": "Notes: Leave it blank to use the default channel ID provided by the selected credential."
|
||||
},
|
||||
"email_format": {
|
||||
"label": "Message format (Optional)",
|
||||
@ -2909,15 +2909,20 @@
|
||||
"placeholder": "Please enter receiver email address",
|
||||
"help": "Notes: Leave it blank to use the default receiver email address provided by the selected credential."
|
||||
},
|
||||
"matrix_room_id": {
|
||||
"label": "Matrix room ID (Optional)",
|
||||
"placeholder": "Please enter Matrix room ID",
|
||||
"help": "Notes: Leave it blank to use the default room ID provided by the selected credential."
|
||||
},
|
||||
"mattermost_channel_id": {
|
||||
"label": "Mattermost channel ID (Optional)",
|
||||
"placeholder": "Please enter Mattermost channel ID",
|
||||
"help": "Notes: Leave it blank to use the default channel ID provided by the credential."
|
||||
"help": "Notes: Leave it blank to use the default channel ID provided by the selected credential."
|
||||
},
|
||||
"slackbot_channel_id": {
|
||||
"label": "Slack channel ID (Optional)",
|
||||
"placeholder": "Please enter Slack channel ID",
|
||||
"help": "Notes: Leave it blank to use the default channel ID provided by the credential."
|
||||
"help": "Notes: Leave it blank to use the default channel ID provided by the selected credential."
|
||||
},
|
||||
"telegrambot_chat_id": {
|
||||
"label": "Telegram chat ID (Optional)",
|
||||
@ -2927,7 +2932,7 @@
|
||||
"webhook_data": {
|
||||
"label": "Webhook data (Optional)",
|
||||
"placeholder": "Please enter Webhook data",
|
||||
"help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
|
||||
"help": "Notes: Leave it blank to use the default Webhook data provided by the selected credential.",
|
||||
"vartips": "Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${CERTIMATE_NOTIFIER_SUBJECT}</strong>: <br>The subject of notification.</li><li><strong>${CERTIMATE_NOTIFIER_MESSAGE}</strong>: <br>The message of notification.</li></ol>"
|
||||
},
|
||||
"webhook_timeout": {
|
||||
|
||||
@ -78,6 +78,7 @@
|
||||
"placeholder": "搜索提供商……"
|
||||
}
|
||||
},
|
||||
|
||||
"shared_acme_eab_kid": {
|
||||
"label": "ACME EAB KID",
|
||||
"placeholder": "请输入 ACME EAB KID"
|
||||
@ -89,6 +90,7 @@
|
||||
"shared_allow_insecure_conns": {
|
||||
"label": "忽略 SSL/TLS 证书验证"
|
||||
},
|
||||
|
||||
"1panel_server_url": {
|
||||
"label": "1Panel 服务地址",
|
||||
"placeholder": "请输入 1Panel 服务地址",
|
||||
@ -877,6 +879,26 @@
|
||||
"litessl_eab": {
|
||||
"guide": "点击下方链接了解如何获取 LiteSSL EAB:<br><a href=\"https://freessl.cn/automation/eab-manager\" target=\"_blank\">https://freessl.cn/automation/eab-manager</a>"
|
||||
},
|
||||
"matrix_server_url": {
|
||||
"label": "Matrix 服务地址",
|
||||
"placeholder": "请输入 Matrix 服务地址"
|
||||
},
|
||||
"matrix_user_id": {
|
||||
"label": "Matrix 用户 ID",
|
||||
"placeholder": "请输入 Matrix 用户 ID",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/</a>"
|
||||
},
|
||||
"matrix_access_token": {
|
||||
"label": "Matrix Access token",
|
||||
"placeholder": "请输入 Matrix Access token",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/#your-access-token\" target=\"_blank\">https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/</a>"
|
||||
},
|
||||
"matrix_room_id": {
|
||||
"label": "Matrix 房间 ID(可选)",
|
||||
"placeholder": "请输入默认的 Matrix 房间 ID",
|
||||
"help": "提示:可在工作流中覆盖此设置。请先将机器人邀请进房间。",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/#advanced\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/</a>"
|
||||
},
|
||||
"mattermost_server_url": {
|
||||
"label": "Mattermost 服务地址",
|
||||
"placeholder": "请输入 Mattermost 服务地址"
|
||||
|
||||
@ -191,6 +191,7 @@
|
||||
"linode_los": "Linode - 对象存储",
|
||||
"litessl": "LiteSSL",
|
||||
"local": "本地主机",
|
||||
"matrix": "Matrix / ElementX",
|
||||
"mattermost": "Mattermost",
|
||||
"mohua": "嘿华云",
|
||||
"mohua_mvh": "嘿华云 - 虚拟主机 MVH ",
|
||||
|
||||
@ -2873,6 +2873,7 @@
|
||||
"params_config": {
|
||||
"label": "参数设置"
|
||||
},
|
||||
|
||||
"discordbot_channel_id": {
|
||||
"label": "Discord 频道 ID(可选)",
|
||||
"placeholder": "请输入 Discord 频道 ID",
|
||||
@ -2895,6 +2896,11 @@
|
||||
"placeholder": "请输入收件人邮箱以覆盖默认值",
|
||||
"help": "提示:不填写时,将使用所选通知渠道授权的默认收件人邮箱。"
|
||||
},
|
||||
"matrix_room_id": {
|
||||
"label": "Matrix 房间 ID(可选)",
|
||||
"placeholder": "请输入 Matrix 房间 ID",
|
||||
"help": "提示:不填写时,将使用所选通知渠道授权的默认房间 ID。"
|
||||
},
|
||||
"mattermost_channel_id": {
|
||||
"label": "Mattermost 频道 ID(可选)",
|
||||
"placeholder": "请输入 Mattermost 频道 ID",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user