// This file is a part of Taskflow. // Copyright (C) 2025 Robby Zambito // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package api import ( "encoding/json" "fmt" "io" "math" "math/rand" "net/http" "time" ) var fs http.Handler func init() { fs = http.FileServer(http.Dir("static")) } const LogLength = 100 type accessLog struct { ClientAddr string `json:"clientAddr"` RequestedPath string `json:"requestedPath"` RequestTime time.Time `json:"requestTime"` HttpMethod string `json:"httpMethod"` } func CreateFilesHandler(logs *[LogLength]string, n *int, toLogParser chan string) http.HandlerFunc { return 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, toLogParser, string(jsonData)) // Serve the index.html file from the static directory http.StripPrefix("/", fs).ServeHTTP(w, r) } } func CreateGetLogs(logs *[LogLength]string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { for _, s := range logs { fmt.Fprintln(w, s) } } } func CreateLoginHandler(logs *[LogLength]string, n *int, toLogParser chan string) http.HandlerFunc { return 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, toLogParser, fmt.Sprintf(`{"authRequest": %s}`, string(body))) http.Error(w, `{"message": "Unauthorized"}`, http.StatusUnauthorized) return } if email, ok := data["email"].(string); ok { if rememberMe, ok := data["rememberMe"].(bool); ok { addRotLog(logs, n, toLogParser, fmt.Sprintf(`{"authRequest": {"email": "%s", "password": "XXXXXXXX", "loginTime": "%s", "success": false, "rememberMe": %t}}`, email, time.Now().UTC(), rememberMe)) } } http.Error(w, `{"message": "Unauthorized"}`, http.StatusUnauthorized) } } var status statusData func init() { // Backfill status // status.overallStatus = overallStatus{ // Status: StatusDown, // Description: "Everything is on fire", // Uptime: 0.0, // ResponseTime: 0.0, // ActiveIncidents: 9001, // ScheduledMaintenance: 0, // } status.overallStatus = overallStatus{ Status: StatusDegraded, Description: "Everything is not great", Uptime: 50.0, ResponseTime: 20000, ActiveIncidents: 9001, ScheduledMaintenance: 1, } status.services = []service{} status.services = append(status.services, service{ Id: ServiceAPIGateway, Name: "API Gateway", Description: "Core API services", Icon: IconAPIGateway, Status: StatusOperational, ResponseTime: 50, Uptime: 69.420, }) status.services = append(status.services, service{ Id: "logs", Name: "Log Warden", Description: "The master of the Logs", Icon: "📜", Status: StatusOperational, ResponseTime: 69, Uptime: 99.999, }) status.metrics = metrics{ Uptime: 90.5, ResponseTime: 169, RequestVolume: 5, ErrorRate: 106.0, } status.incidents = []incident{} status.maintenanceEvents = []maintenanceEvent{} status.uptimeEvents = []uptimeEvent{} // For runChance rand.Seed(time.Now().UnixNano()) incidentId := 42 go func() { tick20ms := time.NewTicker(20 * time.Millisecond) tick1s := time.NewTicker(1 * time.Second) tick10s := time.NewTicker(10 * time.Second) defer tick20ms.Stop() defer tick1s.Stop() defer tick10s.Stop() for { select { case <-tick20ms.C: // Update response times for i := range status.services { runChance(0.08, func() { status.services[i].ResponseTime = int(math.Max(7.0, float64(status.services[i].ResponseTime+rand.Intn(10)-5))) }) } status.overallStatus.ResponseTime = 0 for _, s := range status.services { status.overallStatus.ResponseTime = int(math.Max(float64(status.overallStatus.ResponseTime), float64(s.ResponseTime))) } status.metrics.ResponseTime = status.overallStatus.ResponseTime case <-tick1s.C: fmt.Println("1 s tick") case <-tick10s.C: for i := range status.incidents { runChance(0.05, func() { status.incidents[i].Status = StatusOperational }) } runChance(1.0, func() { severity := SeverityMinor runChance(0.3, func() { severity = SeverityMajor }) i := rand.Intn(int(math.Min(float64(len(allIncidentTitles)), float64(len(allIncidentDescriptions))))) serviceStatus := StatusDown runChance(0.5, func() { serviceStatus = StatusDegraded }) status.incidents = append(status.incidents, incident{ Id: fmt.Sprintf("%d", incidentId), Title: allIncidentTitles[i], Description: allIncidentDescriptions[i], Status: serviceStatus, Severity: severity, StartTime: time.Now().UTC(), AffectedServices: []string{}, }) incidentId++ }) status.overallStatus.ActiveIncidents = len(status.incidents) } } }() } func StatusHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.overallStatus) fmt.Fprint(w, string(jsonData)) } func StatusServicesHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.services) fmt.Fprint(w, string(jsonData)) } func StatusMetricsHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.metrics) fmt.Fprintf(w, string(jsonData)) } func StatusIncidentsHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.incidents) fmt.Fprintf(w, string(jsonData)) } func StatusMaintenanceHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.maintenanceEvents) fmt.Fprintf(w, string(jsonData)) } func StatusUptimeHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.uptimeEvents) fmt.Fprintf(w, string(jsonData)) } func CreateStatusSubscribeHandler(logs *[LogLength]string, n *int, toLogParser chan string) http.HandlerFunc { return 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() addRotLog(logs, n, toLogParser, fmt.Sprintf(`{"subscribeEmails": %s}`, string(body))) fmt.Fprint(w, "{}") } } func CreateContactHandler(logs *[LogLength]string, n *int, toLogParser chan string) http.HandlerFunc { return 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() addRotLog(logs, n, toLogParser, fmt.Sprintf(`{"contactMessage": %s}`, string(body))) fmt.Fprint(w, "{}") } }