mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 10:25:52 +02:00
feat: external notification
This commit is contained in:
parent
241fa4adfe
commit
04de1360c2
42 changed files with 3292 additions and 1393 deletions
269
cmd/external_notifier/generate.go
Normal file
269
cmd/external_notifier/generate.go
Normal file
|
@ -0,0 +1,269 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Structure to hold extracted notifier information
|
||||
type NotifierInfo struct {
|
||||
Name string
|
||||
Fields []FieldInfo
|
||||
FileName string
|
||||
ConfigKey string
|
||||
}
|
||||
|
||||
// Structure to hold field information for notifier
|
||||
type FieldInfo struct {
|
||||
Name string
|
||||
Key string
|
||||
Title string
|
||||
}
|
||||
|
||||
// Template for the TypeScript config file
|
||||
const tsConfigTemplate = `// This file is auto-generated by notification generator. DO NOT EDIT.
|
||||
import type { ExternalNotifyConfig } from './types'
|
||||
|
||||
const {{.Name}}Config: ExternalNotifyConfig = {
|
||||
name: () => $gettext('{{.Name}}'),
|
||||
config: [
|
||||
{{- range .Fields}}
|
||||
{
|
||||
key: '{{.Key}}',
|
||||
label: () => $gettext('{{.Title}}'),
|
||||
},
|
||||
{{- end}}
|
||||
],
|
||||
}
|
||||
|
||||
export default {{.Name}}Config
|
||||
`
|
||||
|
||||
// Regular expression to extract @external_notifier annotation
|
||||
var externalNotifierRegex = regexp.MustCompile(`@external_notifier\((\w+)\)`)
|
||||
|
||||
func main() {
|
||||
if err := GenerateExternalNotifiers(); err != nil {
|
||||
fmt.Printf("error generating external notifier configs: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateExternalNotifiers generates TypeScript config files for external notifiers
|
||||
func GenerateExternalNotifiers() error {
|
||||
fmt.Println("Generating external notifier configs...")
|
||||
|
||||
// Notification package path
|
||||
notificationPkgPath := "internal/notification"
|
||||
outputDir := "app/src/views/preference/components/ExternalNotify"
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
return fmt.Errorf("error creating output directory: %w", err)
|
||||
}
|
||||
|
||||
// Get all Go files in the notification package
|
||||
files, err := filepath.Glob(filepath.Join(notificationPkgPath, "*.go"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error scanning notification package: %w", err)
|
||||
}
|
||||
|
||||
// Collect all notifier info
|
||||
notifiers := []NotifierInfo{}
|
||||
|
||||
for _, file := range files {
|
||||
notifier, found := extractNotifierInfo(file)
|
||||
if found {
|
||||
notifiers = append(notifiers, notifier)
|
||||
fmt.Printf("Found notifier: %s in %s\n", notifier.Name, file)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate TypeScript config files
|
||||
for _, notifier := range notifiers {
|
||||
if err := generateTSConfig(notifier, outputDir); err != nil {
|
||||
return fmt.Errorf("error generating config for %s: %w", notifier.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update index.ts
|
||||
if err := updateIndexFile(notifiers, outputDir); err != nil {
|
||||
return fmt.Errorf("error updating index.ts: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Generation completed successfully!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract notifier information from a Go file
|
||||
func extractNotifierInfo(filePath string) (NotifierInfo, bool) {
|
||||
// Create the FileSet
|
||||
fset := token.NewFileSet()
|
||||
|
||||
// Parse the file
|
||||
file, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing file %s: %v\n", filePath, err)
|
||||
return NotifierInfo{}, false
|
||||
}
|
||||
|
||||
var notifierInfo NotifierInfo
|
||||
found := false
|
||||
|
||||
// Look for the type declaration with the @external_notifier annotation
|
||||
for _, decl := range file.Decls {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok || genDecl.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, spec := range genDecl.Specs {
|
||||
typeSpec, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
structType, ok := typeSpec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we have a comment with @external_notifier
|
||||
if genDecl.Doc != nil {
|
||||
for _, comment := range genDecl.Doc.List {
|
||||
matches := externalNotifierRegex.FindStringSubmatch(comment.Text)
|
||||
if len(matches) > 1 {
|
||||
notifierInfo.Name = matches[1]
|
||||
notifierInfo.ConfigKey = strings.ToLower(typeSpec.Name.Name)
|
||||
notifierInfo.FileName = strings.ToLower(matches[1])
|
||||
found = true
|
||||
|
||||
// Extract fields
|
||||
for _, field := range structType.Fields.List {
|
||||
if len(field.Names) > 0 {
|
||||
fieldName := field.Names[0].Name
|
||||
|
||||
// Get json tag and title from field tags
|
||||
var jsonKey, title string
|
||||
if field.Tag != nil {
|
||||
tagValue := strings.Trim(field.Tag.Value, "`")
|
||||
|
||||
// Extract json key
|
||||
jsonRegex := regexp.MustCompile(`json:"([^"]+)"`)
|
||||
jsonMatches := jsonRegex.FindStringSubmatch(tagValue)
|
||||
if len(jsonMatches) > 1 {
|
||||
jsonKey = jsonMatches[1]
|
||||
}
|
||||
|
||||
// Extract title
|
||||
titleRegex := regexp.MustCompile(`title:"([^"]+)"`)
|
||||
titleMatches := titleRegex.FindStringSubmatch(tagValue)
|
||||
if len(titleMatches) > 1 {
|
||||
title = titleMatches[1]
|
||||
}
|
||||
}
|
||||
|
||||
if jsonKey == "" {
|
||||
jsonKey = strings.ToLower(fieldName)
|
||||
}
|
||||
|
||||
if title == "" {
|
||||
title = fieldName
|
||||
}
|
||||
|
||||
notifierInfo.Fields = append(notifierInfo.Fields, FieldInfo{
|
||||
Name: fieldName,
|
||||
Key: jsonKey,
|
||||
Title: title,
|
||||
})
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return notifierInfo, found
|
||||
}
|
||||
|
||||
// Generate TypeScript config file for a notifier
|
||||
func generateTSConfig(notifier NotifierInfo, outputDir string) error {
|
||||
// Create template
|
||||
tmpl, err := template.New("tsConfig").Parse(tsConfigTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating template: %w", err)
|
||||
}
|
||||
|
||||
// Create output file
|
||||
outputFile := filepath.Join(outputDir, notifier.FileName+".ts")
|
||||
file, err := os.Create(outputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating output file %s: %w", outputFile, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Execute template
|
||||
err = tmpl.Execute(file, notifier)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error executing template: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Generated TypeScript config for %s at %s\n", notifier.Name, outputFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update index.ts file
|
||||
func updateIndexFile(notifiers []NotifierInfo, outputDir string) error {
|
||||
// Create content for index.ts
|
||||
var imports strings.Builder
|
||||
var configMap strings.Builder
|
||||
|
||||
// Sort notifiers alphabetically by name for stable output
|
||||
sort.Slice(notifiers, func(i, j int) bool {
|
||||
return notifiers[i].Name < notifiers[j].Name
|
||||
})
|
||||
|
||||
for _, notifier := range notifiers {
|
||||
fileName := notifier.FileName
|
||||
configName := notifier.Name + "Config"
|
||||
|
||||
imports.WriteString(fmt.Sprintf("import %s from './%s'\n", configName, fileName))
|
||||
}
|
||||
|
||||
// Generate the map
|
||||
configMap.WriteString("const configMap = {\n")
|
||||
for _, notifier := range notifiers {
|
||||
configMap.WriteString(fmt.Sprintf(" %s: %sConfig", strings.ToLower(notifier.Name), notifier.Name))
|
||||
configMap.WriteString(",\n")
|
||||
}
|
||||
configMap.WriteString("}\n")
|
||||
|
||||
content := fmt.Sprintf("// This file is auto-generated by notification generator. DO NOT EDIT.\n%s\n%s\nexport default configMap\n", imports.String(), configMap.String())
|
||||
|
||||
// Write to index.ts
|
||||
indexPath := filepath.Join(outputDir, "index.ts")
|
||||
err := os.WriteFile(indexPath, []byte(content), 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing index.ts: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Updated index.ts at %s\n", indexPath)
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue