Browse Source

feat: add Lark and Lark Custom notification support

Jacky 2 months ago
parent
commit
7a0972495f

+ 6 - 6
app/src/components/Notification/notifications.ts

@@ -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

+ 0 - 1
app/src/views/preference/components/ExternalNotify/ExternalNotifyEditor.vue

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

+ 2 - 2
app/src/views/preference/components/ExternalNotify/bark.ts

@@ -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',
     },
   ],
 }

+ 2 - 2
app/src/views/preference/components/ExternalNotify/dingtalk.ts

@@ -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)',
     },
   ],
 }

+ 4 - 0
app/src/views/preference/components/ExternalNotify/index.ts

@@ -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,
 }
 

+ 14 - 0
app/src/views/preference/components/ExternalNotify/lark.ts

@@ -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

+ 38 - 0
app/src/views/preference/components/ExternalNotify/lark_custom.ts

@@ -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

+ 2 - 2
app/src/views/preference/components/ExternalNotify/telegram.ts

@@ -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',
     },
   ],
 }

+ 1 - 1
app/src/views/preference/components/ExternalNotify/types.d.ts

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

+ 6 - 0
cmd/external_notifier/app/src/views/preference/components/ExternalNotify/index.ts

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

+ 17 - 9
cmd/external_notifier/generate.go

@@ -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")

+ 1 - 4
internal/notification/bark.go

@@ -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))
 	})
 }

+ 1 - 5
internal/notification/dingding.go

@@ -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))
 	})
 }

+ 30 - 0
internal/notification/lark.go

@@ -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))
+	})
+}

+ 43 - 0
internal/notification/lark_custom.go

@@ -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))
+	})
+}

+ 2 - 5
internal/notification/telegram.go

@@ -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))
 	})
 }

+ 5 - 1
query/external_notifies.gen.go

@@ -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
 }