package main import ( "fmt" "io" "log" "net" "net/http" "os" "strings" "time" "gopkg.in/yaml.v3" "git.gm6.ru/icewind/notify" ) // Config — структура для чтения YAML-файла type Config struct { ListenAddresses []string `yaml:"listen_addresses"` LogDir string `yaml:"log_dir"` Telegram *ConfigTelegraam `yaml:"telegram"` } type ConfigTelegraam struct { Token string `yaml:"token"` GroupID int64 `yaml:"group_id"` } func GetRemoteAddr(r *http.Request) string { // Сначала смотрим X-Forwarded-For if xff := r.Header.Get("X-Forwarded-For"); xff != "" { // X-Forwarded-For может содержать несколько IP через запятую ips := strings.Split(xff, ",") if len(ips) > 0 { return strings.TrimSpace(ips[0]) } } // Потом X-Real-IP if realIP := r.Header.Get("X-Real-IP"); realIP != "" { return realIP } // И в конце RemoteAddr host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return r.RemoteAddr } return host } func BuildMessage(r *http.Request) string { t := time.Now() var body []byte if r.ContentLength > 1024 { body = []byte("too long") } else { // Читаем тело запроса body, _ = io.ReadAll(r.Body) _ = r.Body.Close() } // Формируем содержимое entry := fmt.Sprintf("[%s] %s %s%s\n", t.Format(time.RFC3339), r.Method, r.Host, r.URL.String()) entry += "RemoteAddr: " + GetRemoteAddr(r) + "\n" entry += "Headers:\n" for name, values := range r.Header { for _, v := range values { entry += fmt.Sprintf(" %s: %s\n", name, v) } } entry += fmt.Sprintf("Body:\n%s\n", string(body)) entry += "----\n" entry = "```" + entry + "```" return entry } func main() { log.Println("Запуск приложения") // Загружаем конфиг cfg, err := loadConfig("config.yaml") if err != nil { log.Fatalf("Ошибка загрузки конфига: %v", err) } if cfg.Telegram == nil { log.Fatalf("не указана секция telegram") } tg, err := notify.NewTelegram(cfg.Telegram.Token, true) if err != nil { log.Fatal(err) } // Общий обработчик handler := func(w http.ResponseWriter, r *http.Request) { paths := strings.Split(r.URL.Path, "/") switch { case len(paths) < 2: w.WriteHeader(http.StatusNotFound) return case paths[1] == "telegram": default: w.WriteHeader(http.StatusNotFound) return } entry := BuildMessage(r) _, err = tg.SendTextMessage(cfg.Telegram.GroupID, entry) if err != nil { log.Println(err) w.WriteHeader(http.StatusBadGateway) w.Write([]byte(err.Error())) } else { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("OK")) } } // Регистрируем хендлер http.HandleFunc("/", handler) // Запускаем серверы для всех адресов for _, addr := range cfg.ListenAddresses { addr := addr // захватываем в область видимости go func() { log.Printf("Слушаю %s ...", addr) if err := http.ListenAndServe(addr, nil); err != nil { log.Printf("Ошибка на %s: %v", addr, err) } }() } // Блокируем main, чтобы программа не завершилась select {} } // loadConfig читает YAML-конфиг из файла func loadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil }