mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
FEATURE (notifiers): Add Slack notifier
This commit is contained in:
@@ -6,4 +6,5 @@ const (
|
||||
NotifierTypeEmail NotifierType = "EMAIL"
|
||||
NotifierTypeTelegram NotifierType = "TELEGRAM"
|
||||
NotifierTypeWebhook NotifierType = "WEBHOOK"
|
||||
NotifierTypeSlack NotifierType = "SLACK"
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"postgresus-backend/internal/features/notifiers/models/email_notifier"
|
||||
slack_notifier "postgresus-backend/internal/features/notifiers/models/slack"
|
||||
telegram_notifier "postgresus-backend/internal/features/notifiers/models/telegram"
|
||||
webhook_notifier "postgresus-backend/internal/features/notifiers/models/webhook"
|
||||
|
||||
@@ -21,6 +22,7 @@ type Notifier struct {
|
||||
TelegramNotifier *telegram_notifier.TelegramNotifier `json:"telegramNotifier" gorm:"foreignKey:NotifierID"`
|
||||
EmailNotifier *email_notifier.EmailNotifier `json:"emailNotifier" gorm:"foreignKey:NotifierID"`
|
||||
WebhookNotifier *webhook_notifier.WebhookNotifier `json:"webhookNotifier" gorm:"foreignKey:NotifierID"`
|
||||
SlackNotifier *slack_notifier.SlackNotifier `json:"slackNotifier" gorm:"foreignKey:NotifierID"`
|
||||
}
|
||||
|
||||
func (n *Notifier) TableName() string {
|
||||
@@ -56,6 +58,8 @@ func (n *Notifier) getSpecificNotifier() NotificationSender {
|
||||
return n.EmailNotifier
|
||||
case NotifierTypeWebhook:
|
||||
return n.WebhookNotifier
|
||||
case NotifierTypeSlack:
|
||||
return n.SlackNotifier
|
||||
default:
|
||||
panic("unknown notifier type: " + string(n.NotifierType))
|
||||
}
|
||||
|
||||
134
backend/internal/features/notifiers/models/slack/model.go
Normal file
134
backend/internal/features/notifiers/models/slack/model.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package slack_notifier
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type SlackNotifier struct {
|
||||
NotifierID uuid.UUID `json:"notifierId" gorm:"primaryKey;column:notifier_id"`
|
||||
BotToken string `json:"botToken" gorm:"not null;column:bot_token"`
|
||||
TargetChatID string `json:"targetChatId" gorm:"not null;column:target_chat_id"`
|
||||
}
|
||||
|
||||
func (s *SlackNotifier) TableName() string { return "slack_notifiers" }
|
||||
|
||||
func (s *SlackNotifier) Validate() error {
|
||||
if s.BotToken == "" {
|
||||
return errors.New("bot token is required")
|
||||
}
|
||||
|
||||
if s.TargetChatID == "" {
|
||||
return errors.New("target channel ID is required")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(s.TargetChatID, "C") && !strings.HasPrefix(s.TargetChatID, "G") &&
|
||||
!strings.HasPrefix(s.TargetChatID, "D") &&
|
||||
!strings.HasPrefix(s.TargetChatID, "U") {
|
||||
return errors.New(
|
||||
"target channel ID must be a valid Slack channel ID (starts with C, G, D) or User ID (starts with U)",
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SlackNotifier) Send(logger *slog.Logger, heading, message string) error {
|
||||
full := fmt.Sprintf("*%s*", heading)
|
||||
|
||||
if message != "" {
|
||||
full = fmt.Sprintf("%s\n\n%s", full, message)
|
||||
}
|
||||
|
||||
payload, _ := json.Marshal(map[string]any{
|
||||
"channel": s.TargetChatID,
|
||||
"text": full,
|
||||
"mrkdwn": true,
|
||||
})
|
||||
|
||||
const (
|
||||
maxAttempts = 5
|
||||
defaultBackoff = 2 * time.Second // when Retry-After header missing
|
||||
backoffMultiplier = 1.5 // use exponential growth
|
||||
)
|
||||
|
||||
var (
|
||||
backoff = defaultBackoff
|
||||
attempts = 0
|
||||
)
|
||||
|
||||
for {
|
||||
attempts++
|
||||
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
"https://slack.com/api/chat.postMessage",
|
||||
bytes.NewReader(payload),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||
req.Header.Set("Authorization", "Bearer "+s.BotToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("send slack message: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
logger.Warn("Failed to close response body", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if resp.StatusCode == http.StatusTooManyRequests { // 429
|
||||
retryAfter := backoff
|
||||
if h := resp.Header.Get("Retry-After"); h != "" {
|
||||
if seconds, _ := strconv.Atoi(h); seconds > 0 {
|
||||
retryAfter = time.Duration(seconds) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
if attempts >= maxAttempts {
|
||||
return fmt.Errorf("rate-limited after %d attempts, giving up", attempts)
|
||||
}
|
||||
|
||||
logger.Warn("Slack rate-limited, retrying", "after", retryAfter, "attempt", attempts)
|
||||
time.Sleep(retryAfter)
|
||||
backoff = time.Duration(float64(backoff) * backoffMultiplier)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Slack always returns 200 for logical errors, so decode body
|
||||
var respBody struct {
|
||||
OK bool `json:"ok"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("decode response: %v – raw: %s", err, raw)
|
||||
}
|
||||
|
||||
if !respBody.OK {
|
||||
return fmt.Errorf("slack API error: %s", respBody.Error)
|
||||
}
|
||||
|
||||
logger.Info("Slack message sent", "channel", s.TargetChatID, "attempts", attempts)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -26,17 +26,21 @@ func (r *NotifierRepository) Save(notifier *Notifier) error {
|
||||
if notifier.WebhookNotifier != nil {
|
||||
notifier.WebhookNotifier.NotifierID = notifier.ID
|
||||
}
|
||||
case NotifierTypeSlack:
|
||||
if notifier.SlackNotifier != nil {
|
||||
notifier.SlackNotifier.NotifierID = notifier.ID
|
||||
}
|
||||
}
|
||||
|
||||
if notifier.ID == uuid.Nil {
|
||||
if err := tx.Create(notifier).
|
||||
Omit("TelegramNotifier", "EmailNotifier", "WebhookNotifier").
|
||||
Omit("TelegramNotifier", "EmailNotifier", "WebhookNotifier", "SlackNotifier").
|
||||
Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := tx.Save(notifier).
|
||||
Omit("TelegramNotifier", "EmailNotifier", "WebhookNotifier").
|
||||
Omit("TelegramNotifier", "EmailNotifier", "WebhookNotifier", "SlackNotifier").
|
||||
Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -64,6 +68,13 @@ func (r *NotifierRepository) Save(notifier *Notifier) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case NotifierTypeSlack:
|
||||
if notifier.SlackNotifier != nil {
|
||||
notifier.SlackNotifier.NotifierID = notifier.ID // Ensure ID is set
|
||||
if err := tx.Save(notifier.SlackNotifier).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -78,6 +89,7 @@ func (r *NotifierRepository) FindByID(id uuid.UUID) (*Notifier, error) {
|
||||
Preload("TelegramNotifier").
|
||||
Preload("EmailNotifier").
|
||||
Preload("WebhookNotifier").
|
||||
Preload("SlackNotifier").
|
||||
Where("id = ?", id).
|
||||
First(¬ifier).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -94,6 +106,7 @@ func (r *NotifierRepository) FindByUserID(userID uuid.UUID) ([]*Notifier, error)
|
||||
Preload("TelegramNotifier").
|
||||
Preload("EmailNotifier").
|
||||
Preload("WebhookNotifier").
|
||||
Preload("SlackNotifier").
|
||||
Where("user_id = ?", userID).
|
||||
Find(¬ifiers).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -124,6 +137,12 @@ func (r *NotifierRepository) Delete(notifier *Notifier) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case NotifierTypeSlack:
|
||||
if notifier.SlackNotifier != nil {
|
||||
if err := tx.Delete(notifier.SlackNotifier).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the main notifier
|
||||
|
||||
24
backend/migrations/20250624065518_add_slack_notifier.sql
Normal file
24
backend/migrations/20250624065518_add_slack_notifier.sql
Normal file
@@ -0,0 +1,24 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
|
||||
-- Create slack notifiers table
|
||||
CREATE TABLE slack_notifiers (
|
||||
notifier_id UUID PRIMARY KEY,
|
||||
bot_token TEXT NOT NULL,
|
||||
target_chat_id TEXT NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE slack_notifiers
|
||||
ADD CONSTRAINT fk_slack_notifiers_notifier
|
||||
FOREIGN KEY (notifier_id)
|
||||
REFERENCES notifiers (id)
|
||||
ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
|
||||
DROP TABLE IF EXISTS slack_notifiers;
|
||||
|
||||
-- +goose StatementEnd
|
||||
7
frontend/public/icons/notifiers/slack.svg
Normal file
7
frontend/public/icons/notifiers/slack.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M26.5002 14.9996C27.8808 14.9996 29 13.8804 29 12.4998C29 11.1192 27.8807 10 26.5001 10C25.1194 10 24 11.1193 24 12.5V14.9996H26.5002ZM19.5 14.9996C20.8807 14.9996 22 13.8803 22 12.4996V5.5C22 4.11929 20.8807 3 19.5 3C18.1193 3 17 4.11929 17 5.5V12.4996C17 13.8803 18.1193 14.9996 19.5 14.9996Z" fill="#2EB67D"/>
|
||||
<path d="M5.49979 17.0004C4.11919 17.0004 3 18.1196 3 19.5002C3 20.8808 4.1193 22 5.49989 22C6.8806 22 8 20.8807 8 19.5V17.0004H5.49979ZM12.5 17.0004C11.1193 17.0004 10 18.1197 10 19.5004V26.5C10 27.8807 11.1193 29 12.5 29C13.8807 29 15 27.8807 15 26.5V19.5004C15 18.1197 13.8807 17.0004 12.5 17.0004Z" fill="#E01E5A"/>
|
||||
<path d="M17.0004 26.5002C17.0004 27.8808 18.1196 29 19.5002 29C20.8808 29 22 27.8807 22 26.5001C22 25.1194 20.8807 24 19.5 24L17.0004 24L17.0004 26.5002ZM17.0004 19.5C17.0004 20.8807 18.1197 22 19.5004 22L26.5 22C27.8807 22 29 20.8807 29 19.5C29 18.1193 27.8807 17 26.5 17L19.5004 17C18.1197 17 17.0004 18.1193 17.0004 19.5Z" fill="#ECB22E"/>
|
||||
<path d="M14.9996 5.49979C14.9996 4.11919 13.8804 3 12.4998 3C11.1192 3 10 4.1193 10 5.49989C10 6.88061 11.1193 8 12.5 8L14.9996 8L14.9996 5.49979ZM14.9996 12.5C14.9996 11.1193 13.8803 10 12.4996 10L5.5 10C4.11929 10 3 11.1193 3 12.5C3 13.8807 4.11929 15 5.5 15L12.4996 15C13.8803 15 14.9996 13.8807 14.9996 12.5Z" fill="#36C5F0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -1,7 +1,16 @@
|
||||
export { notifierApi } from './api/notifierApi';
|
||||
export type { Notifier } from './models/Notifier';
|
||||
export type { EmailNotifier } from './models/EmailNotifier';
|
||||
export type { TelegramNotifier } from './models/TelegramNotifier';
|
||||
export type { WebhookNotifier } from './models/WebhookNotifier';
|
||||
export { WebhookMethod } from './models/WebhookMethod';
|
||||
export { NotifierType } from './models/NotifierType';
|
||||
|
||||
export type { EmailNotifier } from './models/email/EmailNotifier';
|
||||
export { validateEmailNotifier } from './models/email/validateEmailNotifier';
|
||||
|
||||
export type { TelegramNotifier } from './models/telegram/TelegramNotifier';
|
||||
export { validateTelegramNotifier } from './models/telegram/validateTelegramNotifier';
|
||||
|
||||
export type { WebhookNotifier } from './models/webhook/WebhookNotifier';
|
||||
export { validateWebhookNotifier } from './models/webhook/validateWebhookNotifier';
|
||||
export { WebhookMethod } from './models/webhook/WebhookMethod';
|
||||
|
||||
export type { SlackNotifier } from './models/slack/SlackNotifier';
|
||||
export { validateSlackNotifier } from './models/slack/validateSlackNotifier';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { EmailNotifier } from './EmailNotifier';
|
||||
import type { NotifierType } from './NotifierType';
|
||||
import type { TelegramNotifier } from './TelegramNotifier';
|
||||
import type { WebhookNotifier } from './WebhookNotifier';
|
||||
import type { SlackNotifier } from './slack/SlackNotifier';
|
||||
import type { EmailNotifier } from './email/EmailNotifier';
|
||||
import type { TelegramNotifier } from './telegram/TelegramNotifier';
|
||||
import type { WebhookNotifier } from './webhook/WebhookNotifier';
|
||||
|
||||
export interface Notifier {
|
||||
id: string;
|
||||
@@ -13,4 +14,5 @@ export interface Notifier {
|
||||
telegramNotifier?: TelegramNotifier;
|
||||
emailNotifier?: EmailNotifier;
|
||||
webhookNotifier?: WebhookNotifier;
|
||||
slackNotifier?: SlackNotifier;
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ export enum NotifierType {
|
||||
EMAIL = 'EMAIL',
|
||||
TELEGRAM = 'TELEGRAM',
|
||||
WEBHOOK = 'WEBHOOK',
|
||||
SLACK = 'SLACK',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { EmailNotifier } from './EmailNotifier';
|
||||
|
||||
export const validateEmailNotifier = (notifier: EmailNotifier): boolean => {
|
||||
if (!notifier.targetEmail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notifier.smtpHost) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notifier.smtpPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notifier.smtpUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notifier.smtpPassword) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -8,6 +8,8 @@ export const getNotifierLogoFromType = (type: NotifierType) => {
|
||||
return '/icons/notifiers/telegram.svg';
|
||||
case NotifierType.WEBHOOK:
|
||||
return '/icons/notifiers/webhook.svg';
|
||||
case NotifierType.SLACK:
|
||||
return '/icons/notifiers/slack.svg';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ export const getNotifierNameFromType = (type: NotifierType) => {
|
||||
return 'Telegram';
|
||||
case NotifierType.WEBHOOK:
|
||||
return 'Webhook';
|
||||
case NotifierType.SLACK:
|
||||
return 'Slack';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SlackNotifier {
|
||||
botToken: string;
|
||||
targetChatId: string;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { SlackNotifier } from './SlackNotifier';
|
||||
|
||||
export const validateSlackNotifier = (notifier: SlackNotifier): boolean => {
|
||||
if (!notifier.botToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notifier.targetChatId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { TelegramNotifier } from './TelegramNotifier';
|
||||
|
||||
export const validateTelegramNotifier = (notifier: TelegramNotifier): boolean => {
|
||||
if (!notifier.botToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!notifier.targetChatId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { WebhookNotifier } from './WebhookNotifier';
|
||||
|
||||
export const validateWebhookNotifier = (notifier: WebhookNotifier): boolean => {
|
||||
if (!notifier.webhookUrl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -6,10 +6,15 @@ import {
|
||||
NotifierType,
|
||||
WebhookMethod,
|
||||
notifierApi,
|
||||
validateEmailNotifier,
|
||||
validateSlackNotifier,
|
||||
validateTelegramNotifier,
|
||||
validateWebhookNotifier,
|
||||
} from '../../../../entity/notifiers';
|
||||
import { getNotifierLogoFromType } from '../../../../entity/notifiers/models/getNotifierLogoFromType';
|
||||
import { ToastHelper } from '../../../../shared/toast';
|
||||
import { EditEmailNotifierComponent } from './notifiers/EditEmailNotifierComponent';
|
||||
import { EditSlackNotifierComponent } from './notifiers/EditSlackNotifierComponent';
|
||||
import { EditTelegramNotifierComponent } from './notifiers/EditTelegramNotifierComponent';
|
||||
import { EditWebhookNotifierComponent } from './notifiers/EditWebhookNotifierComponent';
|
||||
|
||||
@@ -67,6 +72,9 @@ export function EditNotifierComponent({
|
||||
});
|
||||
} catch (e) {
|
||||
alert((e as Error).message);
|
||||
alert(
|
||||
'Make sure channel is public or bot is added to the private channel (via @invite) or group. For direct messages use User ID from Slack profile.',
|
||||
);
|
||||
}
|
||||
|
||||
setIsSendingTestNotification(false);
|
||||
@@ -102,6 +110,13 @@ export function EditNotifierComponent({
|
||||
};
|
||||
}
|
||||
|
||||
if (type === NotifierType.SLACK) {
|
||||
notifier.slackNotifier = {
|
||||
botToken: '',
|
||||
targetChatId: '',
|
||||
};
|
||||
}
|
||||
|
||||
setNotifier(
|
||||
JSON.parse(
|
||||
JSON.stringify({
|
||||
@@ -129,27 +144,28 @@ export function EditNotifierComponent({
|
||||
);
|
||||
}, [editingNotifier]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsTestNotificationSuccess(false);
|
||||
}, [notifier]);
|
||||
|
||||
const isAllDataFilled = () => {
|
||||
if (!notifier) return false;
|
||||
|
||||
if (!notifier.name) return false;
|
||||
|
||||
if (notifier.notifierType === NotifierType.TELEGRAM) {
|
||||
return notifier.telegramNotifier?.botToken && notifier.telegramNotifier?.targetChatId;
|
||||
if (notifier.notifierType === NotifierType.TELEGRAM && notifier.telegramNotifier) {
|
||||
return validateTelegramNotifier(notifier.telegramNotifier);
|
||||
}
|
||||
|
||||
if (notifier.notifierType === NotifierType.EMAIL) {
|
||||
return (
|
||||
notifier.emailNotifier?.targetEmail &&
|
||||
notifier.emailNotifier?.smtpHost &&
|
||||
notifier.emailNotifier?.smtpPort &&
|
||||
notifier.emailNotifier?.smtpUser &&
|
||||
notifier.emailNotifier?.smtpPassword
|
||||
);
|
||||
if (notifier.notifierType === NotifierType.EMAIL && notifier.emailNotifier) {
|
||||
return validateEmailNotifier(notifier.emailNotifier);
|
||||
}
|
||||
|
||||
if (notifier.notifierType === NotifierType.WEBHOOK) {
|
||||
return notifier.webhookNotifier?.webhookUrl;
|
||||
if (notifier.notifierType === NotifierType.WEBHOOK && notifier.webhookNotifier) {
|
||||
return validateWebhookNotifier(notifier.webhookNotifier);
|
||||
}
|
||||
|
||||
if (notifier.notifierType === NotifierType.SLACK && notifier.slackNotifier) {
|
||||
return validateSlackNotifier(notifier.slackNotifier);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -185,6 +201,7 @@ export function EditNotifierComponent({
|
||||
{ label: 'Telegram', value: NotifierType.TELEGRAM },
|
||||
{ label: 'Email', value: NotifierType.EMAIL },
|
||||
{ label: 'Webhook', value: NotifierType.WEBHOOK },
|
||||
{ label: 'Slack', value: NotifierType.SLACK },
|
||||
]}
|
||||
onChange={(value) => {
|
||||
setNotifierType(value);
|
||||
@@ -223,6 +240,14 @@ export function EditNotifierComponent({
|
||||
setIsUnsaved={setIsUnsaved}
|
||||
/>
|
||||
)}
|
||||
|
||||
{notifier?.notifierType === NotifierType.SLACK && (
|
||||
<EditSlackNotifierComponent
|
||||
notifier={notifier}
|
||||
setNotifier={setNotifier}
|
||||
setIsUnsaved={setIsUnsaved}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex">
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Input } from 'antd';
|
||||
|
||||
import type { Notifier } from '../../../../../entity/notifiers';
|
||||
|
||||
interface Props {
|
||||
notifier: Notifier;
|
||||
setNotifier: (notifier: Notifier) => void;
|
||||
setIsUnsaved: (isUnsaved: boolean) => void;
|
||||
}
|
||||
|
||||
export function EditSlackNotifierComponent({ notifier, setNotifier, setIsUnsaved }: Props) {
|
||||
return (
|
||||
<>
|
||||
<div className="mb-1 ml-[110px] max-w-[200px]" style={{ lineHeight: 1 }}>
|
||||
<a
|
||||
className="text-xs !text-blue-600"
|
||||
href="https://postgresus.com/notifier-slack"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
How to connect Slack (how to get bot token and chat ID)?
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mb-1 flex items-center">
|
||||
<div className="min-w-[110px]">Bot token</div>
|
||||
|
||||
<div className="w-[250px]">
|
||||
<Input
|
||||
value={notifier?.slackNotifier?.botToken || ''}
|
||||
onChange={(e) => {
|
||||
if (!notifier?.slackNotifier) return;
|
||||
|
||||
setNotifier({
|
||||
...notifier,
|
||||
slackNotifier: {
|
||||
...notifier.slackNotifier,
|
||||
botToken: e.target.value.trim(),
|
||||
},
|
||||
});
|
||||
setIsUnsaved(true);
|
||||
}}
|
||||
size="small"
|
||||
className="w-full"
|
||||
placeholder="xoxb-..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-1 flex items-center">
|
||||
<div className="min-w-[110px]">Target chat ID</div>
|
||||
|
||||
<div className="w-[250px]">
|
||||
<Input
|
||||
value={notifier?.slackNotifier?.targetChatId || ''}
|
||||
onChange={(e) => {
|
||||
if (!notifier?.slackNotifier) return;
|
||||
|
||||
setNotifier({
|
||||
...notifier,
|
||||
slackNotifier: {
|
||||
...notifier.slackNotifier,
|
||||
targetChatId: e.target.value.trim(),
|
||||
},
|
||||
});
|
||||
setIsUnsaved(true);
|
||||
}}
|
||||
size="small"
|
||||
className="w-full"
|
||||
placeholder="C1234567890"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { Input, Select, Tooltip } from 'antd';
|
||||
|
||||
import type { Notifier } from '../../../../../entity/notifiers';
|
||||
import { WebhookMethod } from '../../../../../entity/notifiers/models/WebhookMethod';
|
||||
import { WebhookMethod } from '../../../../../entity/notifiers/models/webhook/WebhookMethod';
|
||||
|
||||
interface Props {
|
||||
notifier: Notifier;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type Notifier, NotifierType } from '../../../../entity/notifiers';
|
||||
import { getNotifierLogoFromType } from '../../../../entity/notifiers/models/getNotifierLogoFromType';
|
||||
import { getNotifierNameFromType } from '../../../../entity/notifiers/models/getNotifierNameFromType';
|
||||
import { ShowEmailNotifierComponent } from './notifier/ShowEmailNotifierComponent';
|
||||
import { ShowSlackNotifierComponent } from './notifier/ShowSlackNotifierComponent';
|
||||
import { ShowTelegramNotifierComponent } from './notifier/ShowTelegramNotifierComponent';
|
||||
import { ShowWebhookNotifierComponent } from './notifier/ShowWebhookNotifierComponent';
|
||||
|
||||
@@ -31,6 +32,10 @@ export function ShowNotifierComponent({ notifier }: Props) {
|
||||
{notifier?.notifierType === NotifierType.WEBHOOK && (
|
||||
<ShowWebhookNotifierComponent notifier={notifier} />
|
||||
)}
|
||||
|
||||
{notifier?.notifierType === NotifierType.SLACK && (
|
||||
<ShowSlackNotifierComponent notifier={notifier} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { Notifier } from '../../../../../entity/notifiers';
|
||||
|
||||
interface Props {
|
||||
notifier: Notifier;
|
||||
}
|
||||
|
||||
export function ShowSlackNotifierComponent({ notifier }: Props) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<div className="min-w-[110px]">Bot token</div>
|
||||
|
||||
<div className="w-[250px]">*********</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-1 flex items-center">
|
||||
<div className="min-w-[110px]">Target chat ID</div>
|
||||
{notifier?.slackNotifier?.targetChatId}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user