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": 5' 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 // 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, 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, 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, fmt.Sprintf(`{"authRequest": {"email": "%s", "password": "XXXXXXXX", "loginTime": "%s", "success": false, "rememberMe": %t}}`, email, time.Now().UTC(), rememberMe)) } } http.Error(w, "Forbidden", http.StatusForbidden) }) // 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, value string) { if strings.Contains(value, "\n") { for _, v := range strings.Split(value, "\n") { addRotLog(logs, last, v) } } else { if *last == log_length { for i := 0; i < log_length-1; i++ { logs[i] = logs[i+1] } logs[log_length-1] = value } else { logs[*last] = value *last++ } } } // 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 }) }