package app import ( "io" "net/http" "strings" ) func handleTraceTailPlain(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } lines := tailFile(traceLogPath, defaultTraceTailMax) w.Header().Set("Content-Type", "text/plain; charset=utf-8") _, _ = io.WriteString(w, strings.Join(lines, "\n")) } // --------------------------------------------------------------------- // trace-json // --------------------------------------------------------------------- // GET /api/v1/trace-json?mode=full|gui|events|smartdns func handleTraceJSON(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } mode := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("mode"))) if mode == "" { mode = "full" } if mode == "events" { mode = "gui" } var lines []string switch mode { case "smartdns": // чисто SmartDNS-лог lines = tailFile(smartdnsLogPath, defaultTraceTailMax) case "gui": // Events: только человеко-читабельные события/ошибки/команды. full := tailFile(traceLogPath, defaultTraceTailMax) allow := []string{ "[gui]", "[info]", "[login]", "[vpn]", "[event]", "[error]", } for _, l := range full { ll := strings.ToLower(l) // берём только наши "человеческие" префиксы ok := false for _, a := range allow { if strings.Contains(ll, strings.ToLower(a)) { ok = true break } } if !ok { // если префикса нет, но это похоже на ошибку — тоже включаем if strings.Contains(ll, "error") || strings.Contains(ll, "failed") || strings.Contains(ll, "timeout") { ok = true } } if !ok { continue } // режем шум от резолвера/маршрутов/массовых вставок if strings.Contains(ll, "smartdns") || strings.Contains(ll, "resolver") || strings.Contains(ll, "dnstt") || strings.Contains(ll, "routes") || strings.Contains(ll, "nft add element") || strings.Contains(ll, "cache hit:") { continue } lines = append(lines, l) } default: // full // полный хвост trace.log без фильтрации lines = tailFile(traceLogPath, defaultTraceTailMax) } writeJSON(w, http.StatusOK, map[string]any{ "lines": lines, }) }