Files
http_logger/main.go
2025-10-26 13:14:31 +03:00

154 lines
3.5 KiB
Go

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
}