feat: add Lark and Lark Custom notification support

This commit is contained in:
Jacky 2025-04-09 13:13:41 +00:00
parent e364353cd1
commit 7a0972495f
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
17 changed files with 174 additions and 38 deletions

View file

@ -4,6 +4,12 @@
const notifications: Record<string, { title: () => string, content: (args: any) => string }> = {
// user module notifications
'All Recovery Codes Have Been Used': {
title: () => $gettext('All Recovery Codes Have Been Used'),
content: (args: any) => $gettext('Please generate new recovery codes in the preferences immediately to prevent lockout.', args),
},
// cluster module notifications
'Reload Remote Nginx Error': {
title: () => $gettext('Reload Remote Nginx Error'),
@ -149,12 +155,6 @@ const notifications: Record<string, { title: () => string, content: (args: any)
title: () => $gettext('Save Remote Stream Success'),
content: (args: any) => $gettext('Save stream %{name} to %{node} successfully', args),
},
// user module notifications
'All Recovery Codes Have Been Used': {
title: () => $gettext('All Recovery Codes Have Been Used'),
content: (args: any) => $gettext('Please generate new recovery codes in the preferences immediately to prevent lockout.', args),
},
}
export default notifications

View file

@ -26,7 +26,6 @@ const columns = computed<Column[]>(() => {
type: input,
config: {
label: item.label,
required: true,
},
},
}))

View file

@ -6,11 +6,11 @@ const BarkConfig: ExternalNotifyConfig = {
config: [
{
key: 'device_key',
label: () => $gettext('Device Key'),
label: 'Device Key',
},
{
key: 'server_url',
label: () => $gettext('Server URL'),
label: 'Server URL',
},
],
}

View file

@ -6,11 +6,11 @@ const DingTalkConfig: ExternalNotifyConfig = {
config: [
{
key: 'access_token',
label: () => $gettext('Access Token'),
label: 'Access Token',
},
{
key: 'secret',
label: () => $gettext('Secret (Optional)'),
label: 'Secret (Optional)',
},
],
}

View file

@ -1,11 +1,15 @@
// This file is auto-generated by notification generator. DO NOT EDIT.
import BarkConfig from './bark'
import DingTalkConfig from './dingtalk'
import LarkConfig from './lark'
import LarkCustomConfig from './lark_custom'
import TelegramConfig from './telegram'
const configMap = {
bark: BarkConfig,
dingtalk: DingTalkConfig,
lark: LarkConfig,
lark_custom: LarkCustomConfig,
telegram: TelegramConfig,
}

View file

@ -0,0 +1,14 @@
// This file is auto-generated by notification generator. DO NOT EDIT.
import type { ExternalNotifyConfig } from './types'
const LarkConfig: ExternalNotifyConfig = {
name: () => $gettext('Lark'),
config: [
{
key: 'webhook_url',
label: 'Webhook URL',
},
],
}
export default LarkConfig

View file

@ -0,0 +1,38 @@
// This file is auto-generated by notification generator. DO NOT EDIT.
import type { ExternalNotifyConfig } from './types'
const LarkCustomConfig: ExternalNotifyConfig = {
name: () => $gettext('Lark Custom'),
config: [
{
key: 'app_id',
label: 'App ID',
},
{
key: 'app_secret',
label: 'App Secret',
},
{
key: 'open_id',
label: 'Open ID',
},
{
key: 'user_id',
label: 'User ID',
},
{
key: 'union_id',
label: 'Union ID',
},
{
key: 'email',
label: 'Email',
},
{
key: 'chat_id',
label: 'Chat ID',
},
],
}
export default LarkCustomConfig

View file

@ -6,11 +6,11 @@ const TelegramConfig: ExternalNotifyConfig = {
config: [
{
key: 'bot_token',
label: () => $gettext('Bot Token'),
label: 'Bot Token',
},
{
key: 'chat_id',
label: () => $gettext('Chat ID'),
label: 'Chat ID',
},
],
}

View file

@ -1,6 +1,6 @@
export interface ExternalNotifyConfigItem {
key: string
label: () => string
label: string
}
export interface ExternalNotifyConfig {

View file

@ -0,0 +1,6 @@
// This file is auto-generated by notification generator. DO NOT EDIT.
const configMap = {
}
export default configMap

View file

@ -32,23 +32,23 @@ type FieldInfo struct {
const tsConfigTemplate = `// This file is auto-generated by notification generator. DO NOT EDIT.
import type { ExternalNotifyConfig } from './types'
const {{.Name}}Config: ExternalNotifyConfig = {
const {{.Name | replaceSpaces}}Config: ExternalNotifyConfig = {
name: () => $gettext('{{.Name}}'),
config: [
{{- range .Fields}}
{
key: '{{.Key}}',
label: () => $gettext('{{.Title}}'),
label: '{{.Title}}',
},
{{- end}}
],
}
export default {{.Name}}Config
export default {{.Name | replaceSpaces}}Config
`
// Regular expression to extract @external_notifier annotation
var externalNotifierRegex = regexp.MustCompile(`@external_notifier\((\w+)\)`)
var externalNotifierRegex = regexp.MustCompile(`@external_notifier\(([a-zA-Z0-9 _]+)\)`)
func main() {
if err := GenerateExternalNotifiers(); err != nil {
@ -142,7 +142,7 @@ func extractNotifierInfo(filePath string) (NotifierInfo, bool) {
if len(matches) > 1 {
notifierInfo.Name = matches[1]
notifierInfo.ConfigKey = strings.ToLower(typeSpec.Name.Name)
notifierInfo.FileName = strings.ToLower(matches[1])
notifierInfo.FileName = strings.ToLower(strings.ReplaceAll(matches[1], " ", "_"))
found = true
// Extract fields
@ -205,8 +205,15 @@ func extractNotifierInfo(filePath string) (NotifierInfo, bool) {
// Generate TypeScript config file for a notifier
func generateTSConfig(notifier NotifierInfo, outputDir string) error {
// Create template
tmpl, err := template.New("tsConfig").Parse(tsConfigTemplate)
// Create function map for template
funcMap := template.FuncMap{
"replaceSpaces": func(s string) string {
return strings.ReplaceAll(s, " ", "")
},
}
// Create template with function map
tmpl, err := template.New("tsConfig").Funcs(funcMap).Parse(tsConfigTemplate)
if err != nil {
return fmt.Errorf("error creating template: %w", err)
}
@ -242,7 +249,7 @@ func updateIndexFile(notifiers []NotifierInfo, outputDir string) error {
for _, notifier := range notifiers {
fileName := notifier.FileName
configName := notifier.Name + "Config"
configName := strings.ReplaceAll(notifier.Name, " ", "") + "Config"
imports.WriteString(fmt.Sprintf("import %s from './%s'\n", configName, fileName))
}
@ -250,7 +257,8 @@ func updateIndexFile(notifiers []NotifierInfo, outputDir string) error {
// Generate the map
configMap.WriteString("const configMap = {\n")
for _, notifier := range notifiers {
configMap.WriteString(fmt.Sprintf(" %s: %sConfig", strings.ToLower(notifier.Name), notifier.Name))
configKey := strings.ToLower(strings.ReplaceAll(notifier.Name, " ", "_"))
configMap.WriteString(fmt.Sprintf(" %s: %sConfig", configKey, strings.ReplaceAll(notifier.Name, " ", "")))
configMap.WriteString(",\n")
}
configMap.WriteString("}\n")

View file

@ -4,7 +4,6 @@ import (
"context"
"github.com/0xJacky/Nginx-UI/model"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/bark"
"github.com/uozi-tech/cosy/map2struct"
)
@ -26,8 +25,6 @@ func init() {
return ErrInvalidNotifierConfig
}
barkService := bark.NewWithServers(barkConfig.DeviceKey, barkConfig.ServerURL)
externalNotify := notify.New()
externalNotify.UseServices(barkService)
return externalNotify.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
return barkService.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
})
}

View file

@ -4,7 +4,6 @@ import (
"context"
"github.com/0xJacky/Nginx-UI/model"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/dingding"
"github.com/uozi-tech/cosy/map2struct"
)
@ -31,9 +30,6 @@ func init() {
Token: dingTalkConfig.AccessToken,
Secret: dingTalkConfig.Secret,
})
// Use the service
externalNotify := notify.New()
externalNotify.UseServices(dingTalkService)
return externalNotify.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
return dingTalkService.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
})
}

View file

@ -0,0 +1,30 @@
package notification
import (
"context"
"github.com/0xJacky/Nginx-UI/model"
"github.com/nikoksr/notify/service/lark"
"github.com/uozi-tech/cosy/map2struct"
)
// @external_notifier(Lark)
type Lark struct {
WebhookURL string `json:"webhook_url" title:"Webhook URL"`
}
func init() {
RegisterExternalNotifier("lark", func(ctx context.Context, n *model.ExternalNotify, msg *ExternalMessage) error {
larkConfig := &Lark{}
err := map2struct.WeakDecode(n.Config, larkConfig)
if err != nil {
return err
}
if larkConfig.WebhookURL == "" {
return ErrInvalidNotifierConfig
}
larkService := lark.NewWebhookService(larkConfig.WebhookURL)
return larkService.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
})
}

View file

@ -0,0 +1,43 @@
package notification
import (
"context"
"github.com/0xJacky/Nginx-UI/model"
"github.com/nikoksr/notify/service/lark"
"github.com/uozi-tech/cosy/map2struct"
)
// @external_notifier(Lark Custom)
type LarkCustom struct {
AppID string `json:"app_id" title:"App ID"`
AppSecret string `json:"app_secret" title:"App Secret"`
OpenID string `json:"open_id" title:"Open ID"`
UserID string `json:"user_id" title:"User ID"`
UnionID string `json:"union_id" title:"Union ID"`
Email string `json:"email" title:"Email"`
ChatID string `json:"chat_id" title:"Chat ID"`
}
func init() {
RegisterExternalNotifier("lark_custom", func(ctx context.Context, n *model.ExternalNotify, msg *ExternalMessage) error {
larkCustomConfig := &LarkCustom{}
err := map2struct.WeakDecode(n.Config, larkCustomConfig)
if err != nil {
return err
}
if larkCustomConfig.AppID == "" || larkCustomConfig.AppSecret == "" {
return ErrInvalidNotifierConfig
}
larkCustomAppService := lark.NewCustomAppService(larkCustomConfig.AppID, larkCustomConfig.AppSecret)
larkCustomAppService.AddReceivers(
lark.OpenID(larkCustomConfig.OpenID),
lark.UserID(larkCustomConfig.UserID),
lark.UnionID(larkCustomConfig.UnionID),
lark.Email(larkCustomConfig.Email),
lark.ChatID(larkCustomConfig.ChatID),
)
return larkCustomAppService.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
})
}

View file

@ -7,7 +7,6 @@ import (
"strconv"
"github.com/0xJacky/Nginx-UI/model"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/telegram"
"github.com/uozi-tech/cosy/map2struct"
)
@ -46,9 +45,7 @@ func init() {
}
telegramService.AddReceivers(chatIDInt)
externalNotify := notify.New()
externalNotify.UseServices(telegramService)
return externalNotify.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
return telegramService.Send(ctx, msg.GetTitle(n.Language), msg.GetContent(n.Language))
})
}

View file

@ -33,6 +33,7 @@ func newExternalNotify(db *gorm.DB, opts ...gen.DOOption) externalNotify {
_externalNotify.UpdatedAt = field.NewTime(tableName, "updated_at")
_externalNotify.DeletedAt = field.NewField(tableName, "deleted_at")
_externalNotify.Type = field.NewString(tableName, "type")
_externalNotify.Language = field.NewString(tableName, "language")
_externalNotify.Config = field.NewField(tableName, "config")
_externalNotify.fillFieldMap()
@ -49,6 +50,7 @@ type externalNotify struct {
UpdatedAt field.Time
DeletedAt field.Field
Type field.String
Language field.String
Config field.Field
fieldMap map[string]field.Expr
@ -71,6 +73,7 @@ func (e *externalNotify) updateTableName(table string) *externalNotify {
e.UpdatedAt = field.NewTime(table, "updated_at")
e.DeletedAt = field.NewField(table, "deleted_at")
e.Type = field.NewString(table, "type")
e.Language = field.NewString(table, "language")
e.Config = field.NewField(table, "config")
e.fillFieldMap()
@ -88,12 +91,13 @@ func (e *externalNotify) GetFieldByName(fieldName string) (field.OrderExpr, bool
}
func (e *externalNotify) fillFieldMap() {
e.fieldMap = make(map[string]field.Expr, 6)
e.fieldMap = make(map[string]field.Expr, 7)
e.fieldMap["id"] = e.ID
e.fieldMap["created_at"] = e.CreatedAt
e.fieldMap["updated_at"] = e.UpdatedAt
e.fieldMap["deleted_at"] = e.DeletedAt
e.fieldMap["type"] = e.Type
e.fieldMap["language"] = e.Language
e.fieldMap["config"] = e.Config
}