mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00
Feat/ntfy (#57)
* implement ntfy notification * fix notification fields order * fix schema for ntfy --------- Co-authored-by: yusing <yusing@6uo.me>
This commit is contained in:
parent
c16a0444ca
commit
78900772bb
17 changed files with 171 additions and 44 deletions
|
@ -1,6 +1,7 @@
|
||||||
package notif
|
package notif
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -54,3 +55,15 @@ func (base *ProviderBase) GetMethod() string {
|
||||||
func (base *ProviderBase) GetMIMEType() string {
|
func (base *ProviderBase) GetMIMEType() string {
|
||||||
return "application/json"
|
return "application/json"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (base *ProviderBase) SetHeaders(logMsg *LogMessage, headers http.Header) {
|
||||||
|
// no-op by default
|
||||||
|
}
|
||||||
|
|
||||||
|
func (base *ProviderBase) makeRespError(resp *http.Response) error {
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err == nil {
|
||||||
|
return E.Errorf("%s status %d: %s", base.Name, resp.StatusCode, body)
|
||||||
|
}
|
||||||
|
return E.Errorf("%s status %d", base.Name, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import "fmt"
|
||||||
type Color uint
|
type Color uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Red Color = 0xff0000
|
ColorError Color = 0xff0000
|
||||||
Green Color = 0x00ff00
|
ColorSuccess Color = 0x00ff00
|
||||||
Blue Color = 0x0000ff
|
ColorInfo Color = 0x0000ff
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c Color) HexString() string {
|
func (c Color) HexString() string {
|
||||||
|
|
|
@ -38,6 +38,8 @@ func (cfg *NotificationConfig) UnmarshalMap(m map[string]any) (err E.Error) {
|
||||||
cfg.Provider = &Webhook{}
|
cfg.Provider = &Webhook{}
|
||||||
case ProviderGotify:
|
case ProviderGotify:
|
||||||
cfg.Provider = &GotifyClient{}
|
cfg.Provider = &GotifyClient{}
|
||||||
|
case ProviderNtfy:
|
||||||
|
cfg.Provider = &Ntfy{}
|
||||||
default:
|
default:
|
||||||
return ErrUnknownNotifProvider.
|
return ErrUnknownNotifProvider.
|
||||||
Subject(cfg.ProviderName).
|
Subject(cfg.ProviderName).
|
||||||
|
|
|
@ -14,10 +14,15 @@ type (
|
||||||
logCh chan *LogMessage
|
logCh chan *LogMessage
|
||||||
providers F.Set[Provider]
|
providers F.Set[Provider]
|
||||||
}
|
}
|
||||||
|
LogField struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
LogFields []LogField
|
||||||
LogMessage struct {
|
LogMessage struct {
|
||||||
Level zerolog.Level
|
Level zerolog.Level
|
||||||
Title string
|
Title string
|
||||||
Extras map[string]any
|
Extras LogFields
|
||||||
Color Color
|
Color Color
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -48,6 +53,10 @@ func Notify(msg *LogMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *LogFields) Add(name, value string) {
|
||||||
|
*f = append(*f, LogField{Name: name, Value: value})
|
||||||
|
}
|
||||||
|
|
||||||
func (disp *Dispatcher) RegisterProvider(cfg *NotificationConfig) {
|
func (disp *Dispatcher) RegisterProvider(cfg *NotificationConfig) {
|
||||||
disp.providers.Add(cfg.Provider)
|
disp.providers.Add(cfg.Provider)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,32 +3,22 @@ package notif
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func formatMarkdown(extras map[string]interface{}) string {
|
func formatMarkdown(extras LogFields) string {
|
||||||
msg := bytes.NewBufferString("")
|
msg := bytes.NewBufferString("")
|
||||||
for k, v := range extras {
|
for _, field := range extras {
|
||||||
msg.WriteString("#### ")
|
msg.WriteString("#### ")
|
||||||
msg.WriteString(k)
|
msg.WriteString(field.Name)
|
||||||
msg.WriteRune('\n')
|
msg.WriteRune('\n')
|
||||||
msg.WriteString(fmt.Sprintf("%v", v))
|
msg.WriteString(field.Value)
|
||||||
msg.WriteRune('\n')
|
msg.WriteRune('\n')
|
||||||
}
|
}
|
||||||
return msg.String()
|
return msg.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatDiscord(extras map[string]interface{}) (string, error) {
|
func formatDiscord(extras LogFields) (string, error) {
|
||||||
fieldsMap := make([]map[string]any, len(extras))
|
fields, err := json.Marshal(extras)
|
||||||
i := 0
|
|
||||||
for k, extra := range extras {
|
|
||||||
fieldsMap[i] = map[string]any{
|
|
||||||
"name": k,
|
|
||||||
"value": extra,
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
fields, err := json.Marshal(fieldsMap)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (client *GotifyClient) makeRespError(resp *http.Response) error {
|
||||||
var errm model.Error
|
var errm model.Error
|
||||||
err := json.NewDecoder(resp.Body).Decode(&errm)
|
err := json.NewDecoder(resp.Body).Decode(&errm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(ProviderGotify+" status %d, but failed to decode err response: %w", resp.StatusCode, err)
|
return fmt.Errorf("%s status %d, but failed to decode err response: %w", client.Name, resp.StatusCode, err)
|
||||||
}
|
}
|
||||||
return fmt.Errorf(ProviderGotify+" status %d %s: %s", resp.StatusCode, errm.Error, errm.ErrorDescription)
|
return fmt.Errorf("%s status %d %s: %s", client.Name, resp.StatusCode, errm.Error, errm.ErrorDescription)
|
||||||
}
|
}
|
||||||
|
|
89
internal/notif/ntfy.go
Normal file
89
internal/notif/ntfy.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package notif
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
E "github.com/yusing/go-proxy/internal/error"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://docs.ntfy.sh/publish
|
||||||
|
type Ntfy struct {
|
||||||
|
ProviderBase
|
||||||
|
Topic string `json:"topic"`
|
||||||
|
Style NtfyStyle `json:"style"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NtfyStyle string
|
||||||
|
|
||||||
|
const (
|
||||||
|
NtfyStyleMarkdown NtfyStyle = "markdown"
|
||||||
|
NtfyStylePlain NtfyStyle = "plain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (n *Ntfy) Validate() E.Error {
|
||||||
|
if n.URL == "" {
|
||||||
|
return E.New("url is required")
|
||||||
|
}
|
||||||
|
if n.Topic == "" {
|
||||||
|
return E.New("topic is required")
|
||||||
|
}
|
||||||
|
if n.Topic[0] == '/' {
|
||||||
|
return E.New("topic should not start with a slash")
|
||||||
|
}
|
||||||
|
switch n.Style {
|
||||||
|
case "":
|
||||||
|
n.Style = NtfyStyleMarkdown
|
||||||
|
case NtfyStyleMarkdown, NtfyStylePlain:
|
||||||
|
default:
|
||||||
|
return E.Errorf("invalid style, expecting %q or %q, got %q", NtfyStyleMarkdown, NtfyStylePlain, n.Style)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Ntfy) GetURL() string {
|
||||||
|
if n.URL[len(n.URL)-1] == '/' {
|
||||||
|
return n.URL + n.Topic
|
||||||
|
}
|
||||||
|
return n.URL + "/" + n.Topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Ntfy) GetMIMEType() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Ntfy) GetToken() string {
|
||||||
|
return n.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Ntfy) MakeBody(logMsg *LogMessage) (io.Reader, error) {
|
||||||
|
switch n.Style {
|
||||||
|
case NtfyStyleMarkdown:
|
||||||
|
return strings.NewReader(formatMarkdown(logMsg.Extras)), nil
|
||||||
|
default:
|
||||||
|
return &bytes.Buffer{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Ntfy) SetHeaders(logMsg *LogMessage, headers http.Header) {
|
||||||
|
headers.Set("Title", logMsg.Title)
|
||||||
|
|
||||||
|
switch logMsg.Level {
|
||||||
|
// warning (or other unspecified) uses default priority
|
||||||
|
case zerolog.FatalLevel:
|
||||||
|
headers.Set("Priority", "urgent")
|
||||||
|
case zerolog.ErrorLevel:
|
||||||
|
headers.Set("Priority", "high")
|
||||||
|
case zerolog.InfoLevel:
|
||||||
|
headers.Set("Priority", "low")
|
||||||
|
case zerolog.DebugLevel:
|
||||||
|
headers.Set("Priority", "min")
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.Style == NtfyStyleMarkdown {
|
||||||
|
headers.Set("Markdown", "yes")
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ type (
|
||||||
GetMIMEType() string
|
GetMIMEType() string
|
||||||
|
|
||||||
MakeBody(logMsg *LogMessage) (io.Reader, error)
|
MakeBody(logMsg *LogMessage) (io.Reader, error)
|
||||||
|
SetHeaders(logMsg *LogMessage, headers http.Header)
|
||||||
|
|
||||||
makeRespError(resp *http.Response) error
|
makeRespError(resp *http.Response) error
|
||||||
}
|
}
|
||||||
|
@ -30,6 +31,7 @@ type (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProviderGotify = "gotify"
|
ProviderGotify = "gotify"
|
||||||
|
ProviderNtfy = "ntfy"
|
||||||
ProviderWebhook = "webhook"
|
ProviderWebhook = "webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,6 +54,7 @@ func notifyProvider(ctx context.Context, provider Provider, msg *LogMessage) err
|
||||||
if provider.GetToken() != "" {
|
if provider.GetToken() != "" {
|
||||||
req.Header.Set("Authorization", "Bearer "+provider.GetToken())
|
req.Header.Set("Authorization", "Bearer "+provider.GetToken())
|
||||||
}
|
}
|
||||||
|
provider.SetHeaders(msg, req.Header)
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -92,12 +92,12 @@ func (webhook *Webhook) GetMIMEType() string {
|
||||||
func (webhook *Webhook) makeRespError(resp *http.Response) error {
|
func (webhook *Webhook) makeRespError(resp *http.Response) error {
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("webhook status %d, failed to read body: %w", resp.StatusCode, err)
|
return fmt.Errorf("%s status %d, failed to read body: %w", webhook.Name, resp.StatusCode, err)
|
||||||
}
|
}
|
||||||
if len(body) > 0 {
|
if len(body) > 0 {
|
||||||
return fmt.Errorf("webhook status %d: %s", resp.StatusCode, body)
|
return fmt.Errorf("%s status %d: %s", webhook.Name, resp.StatusCode, body)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("webhook status %d", resp.StatusCode)
|
return fmt.Errorf("%s status %d", webhook.Name, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (webhook *Webhook) MakeBody(logMsg *LogMessage) (io.Reader, error) {
|
func (webhook *Webhook) MakeBody(logMsg *LogMessage) (io.Reader, error) {
|
||||||
|
|
|
@ -198,33 +198,33 @@ func (mon *monitor) checkUpdateHealth() error {
|
||||||
status = health.StatusUnhealthy
|
status = health.StatusUnhealthy
|
||||||
}
|
}
|
||||||
if result.Healthy != (mon.status.Swap(status) == health.StatusHealthy) {
|
if result.Healthy != (mon.status.Swap(status) == health.StatusHealthy) {
|
||||||
extras := map[string]any{
|
extras := notif.LogFields{
|
||||||
"Service Name": mon.service,
|
{Name: "Service Name", Value: mon.service},
|
||||||
"Time": strutils.FormatTime(time.Now()),
|
{Name: "Time", Value: strutils.FormatTime(time.Now())},
|
||||||
}
|
}
|
||||||
if !result.Healthy {
|
if !result.Healthy {
|
||||||
extras["Last Seen"] = strutils.FormatLastSeen(GetLastSeen(mon.service))
|
extras.Add("Last Seen", strutils.FormatLastSeen(GetLastSeen(mon.service)))
|
||||||
}
|
}
|
||||||
if !mon.url.Load().Nil() {
|
if !mon.url.Load().Nil() {
|
||||||
extras["Service URL"] = mon.url.Load().String()
|
extras.Add("Service URL", mon.url.Load().String())
|
||||||
}
|
}
|
||||||
if result.Detail != "" {
|
if result.Detail != "" {
|
||||||
extras["Detail"] = result.Detail
|
extras.Add("Detail", result.Detail)
|
||||||
}
|
}
|
||||||
if result.Healthy {
|
if result.Healthy {
|
||||||
logger.Info().Msg("service is up")
|
logger.Info().Msg("service is up")
|
||||||
extras["Ping"] = fmt.Sprintf("%d ms", result.Latency.Milliseconds())
|
extras.Add("Ping", fmt.Sprintf("%d ms", result.Latency.Milliseconds()))
|
||||||
notif.Notify(¬if.LogMessage{
|
notif.Notify(¬if.LogMessage{
|
||||||
Title: "✅ Service is up ✅",
|
Title: "✅ Service is up ✅",
|
||||||
Extras: extras,
|
Extras: extras,
|
||||||
Color: notif.Green,
|
Color: notif.ColorSuccess,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logger.Warn().Msg("service went down")
|
logger.Warn().Msg("service went down")
|
||||||
notif.Notify(¬if.LogMessage{
|
notif.Notify(¬if.LogMessage{
|
||||||
Title: "❌ Service went down ❌",
|
Title: "❌ Service went down ❌",
|
||||||
Extras: extras,
|
Extras: extras,
|
||||||
Color: notif.Red,
|
Color: notif.ColorError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "godoxy-schemas",
|
"name": "godoxy-schemas",
|
||||||
"version": "0.9.0-22",
|
"version": "0.9.1-1",
|
||||||
"description": "JSON Schema and typescript types for GoDoxy configuration",
|
"description": "JSON Schema and typescript types for GoDoxy configuration",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
File diff suppressed because one or more lines are too long
12
schemas/config/notification.d.ts
vendored
12
schemas/config/notification.d.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
import { URL } from "../types";
|
import { URL } from "../types";
|
||||||
export declare const NOTIFICATION_PROVIDERS: readonly ["webhook", "gotify"];
|
export declare const NOTIFICATION_PROVIDERS: readonly ["webhook", "gotify", "ntfy"];
|
||||||
export type NotificationProvider = (typeof NOTIFICATION_PROVIDERS)[number];
|
export type NotificationProvider = (typeof NOTIFICATION_PROVIDERS)[number];
|
||||||
export type NotificationConfig = {
|
export type NotificationConfig = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -9,9 +9,17 @@ export interface GotifyConfig extends NotificationConfig {
|
||||||
provider: "gotify";
|
provider: "gotify";
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
export declare const NTFY_MSG_STYLES: string[];
|
||||||
|
export type NtfyStyle = (typeof NTFY_MSG_STYLES)[number];
|
||||||
|
export interface NtfyConfig extends NotificationConfig {
|
||||||
|
provider: "ntfy";
|
||||||
|
topic: string;
|
||||||
|
token?: string;
|
||||||
|
style?: NtfyStyle;
|
||||||
|
}
|
||||||
export declare const WEBHOOK_TEMPLATES: readonly ["", "discord"];
|
export declare const WEBHOOK_TEMPLATES: readonly ["", "discord"];
|
||||||
export declare const WEBHOOK_METHODS: readonly ["POST", "GET", "PUT"];
|
export declare const WEBHOOK_METHODS: readonly ["POST", "GET", "PUT"];
|
||||||
export declare const WEBHOOK_MIME_TYPES: readonly ["application/json", "application/x-www-form-urlencoded", "text/plain"];
|
export declare const WEBHOOK_MIME_TYPES: readonly ["application/json", "application/x-www-form-urlencoded", "text/plain", "text/markdown"];
|
||||||
export declare const WEBHOOK_COLOR_MODES: readonly ["hex", "dec"];
|
export declare const WEBHOOK_COLOR_MODES: readonly ["hex", "dec"];
|
||||||
export type WebhookTemplate = (typeof WEBHOOK_TEMPLATES)[number];
|
export type WebhookTemplate = (typeof WEBHOOK_TEMPLATES)[number];
|
||||||
export type WebhookMethod = (typeof WEBHOOK_METHODS)[number];
|
export type WebhookMethod = (typeof WEBHOOK_METHODS)[number];
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
export const NOTIFICATION_PROVIDERS = ["webhook", "gotify"];
|
export const NOTIFICATION_PROVIDERS = ["webhook", "gotify", "ntfy"];
|
||||||
|
export const NTFY_MSG_STYLES = ["markdown", "plain"];
|
||||||
export const WEBHOOK_TEMPLATES = ["", "discord"];
|
export const WEBHOOK_TEMPLATES = ["", "discord"];
|
||||||
export const WEBHOOK_METHODS = ["POST", "GET", "PUT"];
|
export const WEBHOOK_METHODS = ["POST", "GET", "PUT"];
|
||||||
export const WEBHOOK_MIME_TYPES = [
|
export const WEBHOOK_MIME_TYPES = [
|
||||||
"application/json",
|
"application/json",
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
"text/plain",
|
"text/plain",
|
||||||
|
"text/markdown",
|
||||||
];
|
];
|
||||||
export const WEBHOOK_COLOR_MODES = ["hex", "dec"];
|
export const WEBHOOK_COLOR_MODES = ["hex", "dec"];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { URL } from "../types";
|
import { URL } from "../types";
|
||||||
|
|
||||||
export const NOTIFICATION_PROVIDERS = ["webhook", "gotify"] as const;
|
export const NOTIFICATION_PROVIDERS = ["webhook", "gotify", "ntfy"] as const;
|
||||||
|
|
||||||
export type NotificationProvider = (typeof NOTIFICATION_PROVIDERS)[number];
|
export type NotificationProvider = (typeof NOTIFICATION_PROVIDERS)[number];
|
||||||
|
|
||||||
|
@ -17,12 +17,23 @@ export interface GotifyConfig extends NotificationConfig {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const NTFY_MSG_STYLES = ["markdown", "plain"];
|
||||||
|
export type NtfyStyle = (typeof NTFY_MSG_STYLES)[number];
|
||||||
|
|
||||||
|
export interface NtfyConfig extends NotificationConfig {
|
||||||
|
provider: "ntfy";
|
||||||
|
topic: string;
|
||||||
|
token?: string;
|
||||||
|
style?: NtfyStyle;
|
||||||
|
}
|
||||||
|
|
||||||
export const WEBHOOK_TEMPLATES = ["", "discord"] as const;
|
export const WEBHOOK_TEMPLATES = ["", "discord"] as const;
|
||||||
export const WEBHOOK_METHODS = ["POST", "GET", "PUT"] as const;
|
export const WEBHOOK_METHODS = ["POST", "GET", "PUT"] as const;
|
||||||
export const WEBHOOK_MIME_TYPES = [
|
export const WEBHOOK_MIME_TYPES = [
|
||||||
"application/json",
|
"application/json",
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
"text/plain",
|
"text/plain",
|
||||||
|
"text/markdown",
|
||||||
] as const;
|
] as const;
|
||||||
export const WEBHOOK_COLOR_MODES = ["hex", "dec"] as const;
|
export const WEBHOOK_COLOR_MODES = ["hex", "dec"] as const;
|
||||||
|
|
||||||
|
|
4
schemas/config/providers.d.ts
vendored
4
schemas/config/providers.d.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
import { URI, URL } from "../types";
|
import { URI, URL } from "../types";
|
||||||
import { GotifyConfig, WebhookConfig } from "./notification";
|
import { GotifyConfig, NtfyConfig, WebhookConfig } from "./notification";
|
||||||
export type Providers = {
|
export type Providers = {
|
||||||
/** List of route definition files to include
|
/** List of route definition files to include
|
||||||
*
|
*
|
||||||
|
@ -21,7 +21,7 @@ export type Providers = {
|
||||||
* @minItems 1
|
* @minItems 1
|
||||||
* @examples require(".").notificationExamples
|
* @examples require(".").notificationExamples
|
||||||
*/
|
*/
|
||||||
notification?: (WebhookConfig | GotifyConfig)[];
|
notification?: (WebhookConfig | GotifyConfig | NtfyConfig)[];
|
||||||
};
|
};
|
||||||
export declare const includeExamples: readonly ["file1.yml", "file2.yml"];
|
export declare const includeExamples: readonly ["file1.yml", "file2.yml"];
|
||||||
export declare const dockerExamples: readonly [{
|
export declare const dockerExamples: readonly [{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { URI, URL } from "../types";
|
import { URI, URL } from "../types";
|
||||||
import { GotifyConfig, WebhookConfig } from "./notification";
|
import { GotifyConfig, NtfyConfig, WebhookConfig } from "./notification";
|
||||||
|
|
||||||
export type Providers = {
|
export type Providers = {
|
||||||
/** List of route definition files to include
|
/** List of route definition files to include
|
||||||
|
@ -20,7 +20,7 @@ export type Providers = {
|
||||||
* @minItems 1
|
* @minItems 1
|
||||||
* @examples require(".").notificationExamples
|
* @examples require(".").notificationExamples
|
||||||
*/
|
*/
|
||||||
notification?: (WebhookConfig | GotifyConfig)[];
|
notification?: (WebhookConfig | GotifyConfig | NtfyConfig)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const includeExamples = ["file1.yml", "file2.yml"] as const;
|
export const includeExamples = ["file1.yml", "file2.yml"] as const;
|
||||||
|
|
Loading…
Add table
Reference in a new issue