package main import ( "encoding/json" "fmt" "io" "net" "net/http" "regexp" // "strconv" "strings" "time" ) // Example: // curl 'http://localhost:8080/v1/auth/login' -X POST -H 'Content-Type: application/json' --data-raw $'{}}\n{"king": "key"' const log_length = 100 type accessLog struct { ClientAddr string `json:"clientAddr"` RequestedPath string `json:"requestedPath"` RequestTime time.Time `json:"requestTime"` HttpMethod string `json:"httpMethod"` } type loginAttemptLog struct { Email string `json:"email"` Password string `json:"password"` LoginTime time.Time `json:"loginTime"` Success bool `json:"success"` RememberMe bool `json:"rememberMe"` } func main() { fs := http.FileServer(http.Dir("static")) var logs [log_length]string n := 0 king := "NOKING" logChan := make(chan string) go parser(logChan, &king) // Define a handler function for the root path http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(accessLog{ ClientAddr: RedactIP(r.RemoteAddr), RequestedPath: r.URL.Path, RequestTime: time.Now().UTC(), HttpMethod: r.Method, }) addRotLog(&logs, &n, logChan, string(jsonData)) // Serve the index.html file from the static directory http.StripPrefix("/", fs).ServeHTTP(w, r) }) http.HandleFunc("/logs", createGetLogs(&logs)) http.HandleFunc("/v1/auth/login", func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "Bad Request", http.StatusBadRequest) return } defer r.Body.Close() var data map[string]any if json.Unmarshal(body, &data) != nil { addRotLog(&logs, &n, logChan, fmt.Sprintf(`{"authRequest": %s}`, string(body))) http.Error(w, "Forbidden", http.StatusForbidden) return } if email, ok := data["email"].(string); ok { if rememberMe, ok := data["rememberMe"].(bool); ok { addRotLog(&logs, &n, logChan, fmt.Sprintf(`{"authRequest": {"email": "%s", "password": "XXXXXXXX", "loginTime": "%s", "success": false, "rememberMe": %t}}`, email, time.Now().UTC(), rememberMe)) } } http.Error(w, "Forbidden", http.StatusForbidden) }) http.HandleFunc("/v1/king", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, king) }) // Start the server on port 8080 fmt.Println("Server is listening on port 8080...") err := http.ListenAndServe(":8080", nil) if err != nil { fmt.Printf("Error starting server: %s\n", err) } } func createGetLogs(logs *[log_length]string) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { for _, s := range logs { fmt.Fprintln(w, s) } } } func addRotLog(logs *[log_length]string, last *int, parser chan string, value string) { if strings.Contains(value, "\n") { for _, v := range strings.Split(value, "\n") { addRotLog(logs, last, parser, v) } } else { if *last == log_length { for i := 0; i < log_length-1; i++ { logs[i] = logs[i+1] } logs[log_length-1] = value parser <- value } else { logs[*last] = value *last++ parser <- value } } } func parser(input chan string, king *string) { for value := range input { var data map[string]any if json.Unmarshal([]byte(value), &data) == nil { if k, ok := data["king"].(string); ok { *king = k } } } } // RedactIP partially redacts an IP address by replacing the last octet with 'xxx' func RedactIP(input string) string { ipRegex := `\b(?:\d{1,3}\.){3}\d{1,3}\b` re := regexp.MustCompile(ipRegex) return re.ReplaceAllStringFunc(input, func(match string) string { if ip := net.ParseIP(match); ip != nil { parts := strings.Split(match, ".") if len(parts) == 4 { parts[3] = "XXX" return strings.Join(parts, ".") } } return match }) }