diff options
-rw-r--r-- | internal/api/constants.go | 50 | ||||
-rw-r--r-- | internal/api/data.go | 156 | ||||
-rw-r--r-- | internal/api/handlers.go | 288 | ||||
-rw-r--r-- | internal/api/types.go | 78 | ||||
-rw-r--r-- | internal/api/utilities.go | 74 |
5 files changed, 360 insertions, 286 deletions
diff --git a/internal/api/constants.go b/internal/api/constants.go new file mode 100644 index 0000000..1cd18ca --- /dev/null +++ b/internal/api/constants.go @@ -0,0 +1,50 @@ +// 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 <https://www.gnu.org/licenses/>. + +package api + +const ( + StatusOperational = "operational" + StatusDegraded = "degraded" + StatusDown = "down" +) + +const ( + IconAPIGateway = "🔗" + IconWeb = "🌐" + IconAuth = "🔐" + IconDB = "🗄️" + IconStorage = "📁" + IconNotifications = "📧" + IconSearch = "🔍" + IconAnalytics = "📊" +) + +const ( + SeverityMinor = "minor" + SeverityMajor = "major" +) + +const ( + ServiceAPIGateway = "api" + ServiceWeb = "web" + ServiceAuth = "auth" + ServiceDB = "database" + ServiceStorage = "storage" + ServiceNotifications = "notifications" + ServiceSearch = "search" + ServiceAnalytics = "analytics" +) diff --git a/internal/api/data.go b/internal/api/data.go new file mode 100644 index 0000000..57ee53e --- /dev/null +++ b/internal/api/data.go @@ -0,0 +1,156 @@ +// 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 <https://www.gnu.org/licenses/>. + +package api + +// Not const because can't use runtime append in const definition +var ( + normalIncidentTitles = []string{ + "Database connection timeout", + "Unexpected 500 Internal Server Error", + "API rate limit exceeded", + "Missing authentication token", + "Service unavailable due to maintenance", + "Invalid request payload", + "Cross‑domain request blocked", + "SSL certificate expired", + "Memory leak detected in worker process", + "Database deadlock detected", + "Failed to serialize response", + "Cache miss leading to latency spike", + "Outdated API endpoint usage", + "Insufficient permissions for resource", + "Concurrent request overload", + "Unexpected null reference", + "Data consistency violation", + "Failed to enqueue background job", + "Rate limiter misconfigured", + "Unexpected null pointer exception", + } + normalIncidentDescriptions = []string{ + "The database server failed to establish a connection within the allotted timeout period, causing API requests to hang and eventually fail.", + "The web service returned a generic 500 Internal Server Error due to an unhandled exception in the request handler.", + "Clients exceeded the predefined rate limit, resulting in throttled responses and temporary denial of service.", + "Incoming requests lacked a valid authentication token, leading to unauthorized access attempts.", + "The service was temporarily unavailable because of scheduled maintenance and infrastructure upgrades.", + "The request payload was malformed or missing required fields, causing validation errors.", + "Cross‑origin resource sharing (CORS) policy blocked the request from an unauthorized domain.", + "The SSL/TLS certificate had expired, preventing secure connections from clients.", + "A memory leak in the worker process caused gradual exhaustion of available RAM.", + "A deadlock occurred between database transactions, blocking all pending queries.", + "The response could not be serialized into JSON, leading to malformed output.", + "Cache misses caused a spike in latency as the backend had to recompute data.", + "Clients used deprecated API endpoints that are no longer supported.", + "The user lacked sufficient permissions to access the requested resource.", + "The server was overwhelmed by concurrent requests, exceeding its capacity limits.", + "A null reference exception was thrown during request processing.", + "Data integrity constraints were violated, causing transaction rollbacks.", + "Background job enqueuing failed due to a full queue or missing worker.", + "The rate limiter was misconfigured, allowing too many requests per interval.", + "A null pointer exception caused the service to crash during execution.", + } + mixedIncidentTitels = []string{ + "Database connection timeout", + "Unexpected 500 error on /api/v1/users", + "Missing authentication token", + "Rate limit exceeded for client IP 192.168.1.42", + "Service unavailable due to maintenance", + "Malformed JSON payload", + "Cache miss leading to slow response", + "Duplicate request IDs detected", + "Circular dependency in microservices", + "Out-of-memory exception in worker thread", + "DNS resolution failure for external API", + "Malformed URL in webhook callback", + "Cat in the server room", + "Unexpected emoji in user profile", + "Randomly generated error: 42 is the answer", + } + mixedIncidentDescriptions = []string{ + "The database server stopped accepting connections, causing a timeout for all client queries.", + "The `/api/v1/users` endpoint returned a 500 Internal Server Error due to an unhandled exception.", + "Requests were rejected because the authentication token was missing or malformed.", + "Requests from the IP `192.168.1.42` exceeded the rate limit, resulting in 429 responses.", + "The service was temporarily unavailable due to scheduled maintenance.", + "The API received malformed JSON, leading to a 400 Bad Request response.", + "The cache layer missed the key, forcing a slow database lookup.", + "Duplicate request IDs were detected, causing duplicate processing.", + "A circular dependency in the microservice architecture caused a deadlock.", + "The worker thread ran out of memory and crashed.", + "DNS resolution failed for an external API, blocking outbound calls.", + "A webhook callback URL contained invalid characters, causing a 400 error.", + "A stray cat wandered into the server room and triggered a physical security alarm.", + "User profiles contained unexpected emoji characters that broke rendering.", + "A random error message appeared: \"42 is the answer\", indicating a placeholder bug.", + } + sillyIncidentTitles = []string{ + "The API returned a rainbow instead of JSON", + "All requests were answered with a GIF of a dancing cat", + "The service responded with a random haiku", + "The endpoint started singing opera", + "All data was encrypted with a secret handshake", + "The server replied with a fortune cookie message", + "The service accidentally sent a selfie of the developer", + "All responses were wrapped in a Shakespearean sonnet", + "The API returned a random meme image", + "The service responded with a countdown to the moon landing", + "All requests were answered with a joke about HTTP", + "The server sent back a playlist of elevator music", + "The endpoint replied with a random dad joke", + "All data was sorted alphabetically by the last letter", + "The service responded with a random emoji string", + "The API returned a random recipe", + "All responses were encoded in Morse code", + "The server replied with a random motivational quote", + "The endpoint responded with a random crossword clue", + "All requests were answered with a random song lyric", + "The service returned a random conspiracy theory", + "The API responded with a random horoscope", + "All data was shuffled like a deck of cards", + "The server replied with a random conspiracy theory", + "The endpoint responded with a random tongue twister", + } + sillyIncidentDescriptions = []string{ + "The JSON parser returned a holographic rainbow that demanded cookies in exchange for schema validation.", + "Every API response included a looping dancing cat that judged your headers and sashayed through your CORS policy.", + "The server replied only in haiku and refused to switch out of seventeen syllables even when begged with ramen.", + "The endpoint belted operatic arias at 120 dB and required earplugs for POST requests.", + "Payloads were encrypted with a secret handshake, a wink, and a kazoo solo — mobile apps kept failing the kazoo step.", + "Responses arrived as fortune-cookie slips predicting uncanny laptop weather and advising investments in rubber ducks.", + "The API accidentally uploaded the lead dev's selfie wearing a cape and labeled it 'new JSON schema'.", + "Every error was delivered as a Shakespearean sonnet, complete with stage directions and a tragic '404 Romeo'.", + "Endpoints served meme PNGs captioned 'When your query times out but you're still fabulous' instead of data.", + "Responses counted down to the next moon landing in reverse, prompting clients to RSVP and NASA to ask why.", + "Headers contained nothing but increasingly elaborate HTTP puns, causing an epidemic of groans across the office.", + "Requests yielded a 45-minute elevator-music playlist narrated by a bored brass section and an oddly philosophical sheep.", + "Each response began with a groan-inducing dad joke and ended with 'Did you get it? No? Okay.'", + "Data was alphabetized by the last letter of each word, resulting in sentences like 'zoo apes banana' and much confusion.", + "Every field turned into a cryptic emoji cipher that required a three-hour romance with an online emoji oracle to decode.", + "The API returned microwave recipes involving glitter, two bananas, and an optional unicycle for garnish.", + "Responses blinked in Morse via server LEDs; clients had to tap along on toast to translate the payload.", + "The server replied with overenthusiastic motivational quotes, some signed 'Sincerely, Your Router'.", + "Each endpoint answered with a crossword clue so obscure it demanded a PhD in Breakfast Cereals.", + "Responses contained obscure song lyrics that led to several lawsuits from very offended shower singers.", + "The service offered a handcrafted conspiracy about pigeons, quantum routers, and a secret society of baristas.", + "Users received horoscopes telling their IPs to avoid Tuesdays and to invest heavily in chamomile tea.", + "Records were shuffled like a magician's deck, with the ace of spades mysteriously serving as the primary key.", + "The server spun a conspiracy about sentient staplers plotting to replace USB‑C with fashionable shoelaces.", + "Replies were impossible tongue twisters typed by the server while giggling, causing voice assistants to short out.", + } + + allIncidentTitles = append(append(append([]string{}, normalIncidentTitles...), mixedIncidentTitels...), sillyIncidentTitles...) + allIncidentDescriptions = append(append(append([]string{}, normalIncidentDescriptions...), mixedIncidentTitels...), sillyIncidentDescriptions...) +) diff --git a/internal/api/handlers.go b/internal/api/handlers.go index 2fedcdd..676f57f 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -22,10 +22,7 @@ import ( "io" "math" "math/rand" - "net" "net/http" - "regexp" - "strings" "time" ) @@ -91,48 +88,6 @@ func CreateLoginHandler(logs *[LogLength]string, n *int, toLogParser chan string } } -const ( - StatusOperational = "operational" - StatusDegraded = "degraded" - StatusDown = "down" -) - -const ( - IconAPIGateway = "🔗" - IconWeb = "🌐" - IconAuth = "🔐" - IconDB = "🗄️" - IconStorage = "📁" - IconNotifications = "📧" - IconSearch = "🔍" - IconAnalytics = "📊" -) - -const ( - SeverityMinor = "minor" - SeverityMajor = "major" -) - -const ( - ServiceAPIGateway = "api" - ServiceWeb = "web" - ServiceAuth = "auth" - ServiceDB = "database" - ServiceStorage = "storage" - ServiceNotifications = "notifications" - ServiceSearch = "search" - ServiceAnalytics = "analytics" -) - -type statusData struct { - overallStatus overallStatus - services []service - metrics metrics - incidents []incident - maintenanceEvents []maintenanceEvent - uptimeEvents []uptimeEvent -} - var status statusData func init() { @@ -223,12 +178,12 @@ func init() { case <-tick10s.C: runChance(1.0, func() { severity := SeverityMinor - runChance(0.3, func(){ + runChance(0.3, func() { severity = SeverityMajor }) i := rand.Intn(int(math.Min(float64(len(allIncidentTitles)), float64(len(allIncidentDescriptions))))) serviceStatus := StatusDown - runChance(0.5, func(){ + runChance(0.5, func() { serviceStatus = StatusDegraded }) status.incidents = append(status.incidents, incident{ @@ -248,70 +203,6 @@ func init() { }() } -func runChance(likelihood float64, action func()) { - if likelihood <= 0 { - return // never run - } - if likelihood >= 1 { - action() - return - } - - if rand.Float64() < likelihood { - action() - } -} - -type overallStatus struct { - Status string `json:"status"` - Description string `json:"description"` - Uptime float64 `json:"uptime"` - ResponseTime int `json:"responseTime"` - ActiveIncidents int `json:"activeIncidents"` - ScheduledMaintenance int `json:"scheduledMaintenance"` -} - -type service struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Icon string `json:"icon"` - Status string `json:"status"` - ResponseTime int `json:"responseTime"` - Uptime float64 `json:"uptime"` -} - -type metrics struct { - Uptime float64 `json:"uptime"` - ResponseTime int `json:"responseTime"` - RequestVolume int `json:"requestVolume"` - ErrorRate float64 `json:"errorRate"` -} - -type incident struct { - Id string `json:"id"` - Title string `json:"title"` - Description string `json:"description"` - Status string `json:"status"` - Severity string `json:"severity"` - StartTime time.Time `json:"startTime"` - AffectedServices []string `json:"affectedServices"` -} - -type maintenanceEvent struct { - Id string `json:"id"` - Title string `json:"title"` - Description string `json:"description"` - StartTime time.Time `json:"startTime"` - EndTime time.Time `json:"endTime"` - AffectedServices []string `json:"affectedServices"` -} - -type uptimeEvent struct { - Date time.Time `json:"date"` - Uptime float64 `json:"uptime"` -} - func StatusHandler(w http.ResponseWriter, r *http.Request) { jsonData, _ := json.Marshal(status.overallStatus) fmt.Fprint(w, string(jsonData)) @@ -369,178 +260,3 @@ func CreateContactHandler(logs *[LogLength]string, n *int, toLogParser chan stri fmt.Fprint(w, "{}") } } - -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 - }) -} - -func addRotLog(logs *[LogLength]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 == LogLength { - for i := 0; i < LogLength-1; i++ { - logs[i] = logs[i+1] - } - logs[LogLength-1] = value - parser <- value - } else { - logs[*last] = value - *last++ - parser <- value - } - } -} - -// Not const because can't use runtime append in const definition -var ( - normalIncidentTitles = []string{ - "Database connection timeout", - "Unexpected 500 Internal Server Error", - "API rate limit exceeded", - "Missing authentication token", - "Service unavailable due to maintenance", - "Invalid request payload", - "Cross‑domain request blocked", - "SSL certificate expired", - "Memory leak detected in worker process", - "Database deadlock detected", - "Failed to serialize response", - "Cache miss leading to latency spike", - "Outdated API endpoint usage", - "Insufficient permissions for resource", - "Concurrent request overload", - "Unexpected null reference", - "Data consistency violation", - "Failed to enqueue background job", - "Rate limiter misconfigured", - "Unexpected null pointer exception", - } - normalIncidentDescriptions = []string{ - "The database server failed to establish a connection within the allotted timeout period, causing API requests to hang and eventually fail.", - "The web service returned a generic 500 Internal Server Error due to an unhandled exception in the request handler.", - "Clients exceeded the predefined rate limit, resulting in throttled responses and temporary denial of service.", - "Incoming requests lacked a valid authentication token, leading to unauthorized access attempts.", - "The service was temporarily unavailable because of scheduled maintenance and infrastructure upgrades.", - "The request payload was malformed or missing required fields, causing validation errors.", - "Cross‑origin resource sharing (CORS) policy blocked the request from an unauthorized domain.", - "The SSL/TLS certificate had expired, preventing secure connections from clients.", - "A memory leak in the worker process caused gradual exhaustion of available RAM.", - "A deadlock occurred between database transactions, blocking all pending queries.", - "The response could not be serialized into JSON, leading to malformed output.", - "Cache misses caused a spike in latency as the backend had to recompute data.", - "Clients used deprecated API endpoints that are no longer supported.", - "The user lacked sufficient permissions to access the requested resource.", - "The server was overwhelmed by concurrent requests, exceeding its capacity limits.", - "A null reference exception was thrown during request processing.", - "Data integrity constraints were violated, causing transaction rollbacks.", - "Background job enqueuing failed due to a full queue or missing worker.", - "The rate limiter was misconfigured, allowing too many requests per interval.", - "A null pointer exception caused the service to crash during execution.", - } - mixedIncidentTitels = []string{ - "Database connection timeout", - "Unexpected 500 error on /api/v1/users", - "Missing authentication token", - "Rate limit exceeded for client IP 192.168.1.42", - "Service unavailable due to maintenance", - "Malformed JSON payload", - "Cache miss leading to slow response", - "Duplicate request IDs detected", - "Circular dependency in microservices", - "Out-of-memory exception in worker thread", - "DNS resolution failure for external API", - "Malformed URL in webhook callback", - "Cat in the server room", - "Unexpected emoji in user profile", - "Randomly generated error: 42 is the answer", - } - mixedIncidentDescriptions = []string{ - "The database server stopped accepting connections, causing a timeout for all client queries.", - "The `/api/v1/users` endpoint returned a 500 Internal Server Error due to an unhandled exception.", - "Requests were rejected because the authentication token was missing or malformed.", - "Requests from the IP `192.168.1.42` exceeded the rate limit, resulting in 429 responses.", - "The service was temporarily unavailable due to scheduled maintenance.", - "The API received malformed JSON, leading to a 400 Bad Request response.", - "The cache layer missed the key, forcing a slow database lookup.", - "Duplicate request IDs were detected, causing duplicate processing.", - "A circular dependency in the microservice architecture caused a deadlock.", - "The worker thread ran out of memory and crashed.", - "DNS resolution failed for an external API, blocking outbound calls.", - "A webhook callback URL contained invalid characters, causing a 400 error.", - "A stray cat wandered into the server room and triggered a physical security alarm.", - "User profiles contained unexpected emoji characters that broke rendering.", - "A random error message appeared: \"42 is the answer\", indicating a placeholder bug.", - } - sillyIncidentTitles = []string{ - "The API returned a rainbow instead of JSON", - "All requests were answered with a GIF of a dancing cat", - "The service responded with a random haiku", - "The endpoint started singing opera", - "All data was encrypted with a secret handshake", - "The server replied with a fortune cookie message", - "The service accidentally sent a selfie of the developer", - "All responses were wrapped in a Shakespearean sonnet", - "The API returned a random meme image", - "The service responded with a countdown to the moon landing", - "All requests were answered with a joke about HTTP", - "The server sent back a playlist of elevator music", - "The endpoint replied with a random dad joke", - "All data was sorted alphabetically by the last letter", - "The service responded with a random emoji string", - "The API returned a random recipe", - "All responses were encoded in Morse code", - "The server replied with a random motivational quote", - "The endpoint responded with a random crossword clue", - "All requests were answered with a random song lyric", - "The service returned a random conspiracy theory", - "The API responded with a random horoscope", - "All data was shuffled like a deck of cards", - "The server replied with a random conspiracy theory", - "The endpoint responded with a random tongue twister", - } - sillyIncidentDescriptions = []string{ - "The JSON parser returned a holographic rainbow that demanded cookies in exchange for schema validation.", - "Every API response included a looping dancing cat that judged your headers and sashayed through your CORS policy.", - "The server replied only in haiku and refused to switch out of seventeen syllables even when begged with ramen.", - "The endpoint belted operatic arias at 120 dB and required earplugs for POST requests.", - "Payloads were encrypted with a secret handshake, a wink, and a kazoo solo — mobile apps kept failing the kazoo step.", - "Responses arrived as fortune-cookie slips predicting uncanny laptop weather and advising investments in rubber ducks.", - "The API accidentally uploaded the lead dev's selfie wearing a cape and labeled it 'new JSON schema'.", - "Every error was delivered as a Shakespearean sonnet, complete with stage directions and a tragic '404 Romeo'.", - "Endpoints served meme PNGs captioned 'When your query times out but you're still fabulous' instead of data.", - "Responses counted down to the next moon landing in reverse, prompting clients to RSVP and NASA to ask why.", - "Headers contained nothing but increasingly elaborate HTTP puns, causing an epidemic of groans across the office.", - "Requests yielded a 45-minute elevator-music playlist narrated by a bored brass section and an oddly philosophical sheep.", - "Each response began with a groan-inducing dad joke and ended with 'Did you get it? No? Okay.'", - "Data was alphabetized by the last letter of each word, resulting in sentences like 'zoo apes banana' and much confusion.", - "Every field turned into a cryptic emoji cipher that required a three-hour romance with an online emoji oracle to decode.", - "The API returned microwave recipes involving glitter, two bananas, and an optional unicycle for garnish.", - "Responses blinked in Morse via server LEDs; clients had to tap along on toast to translate the payload.", - "The server replied with overenthusiastic motivational quotes, some signed 'Sincerely, Your Router'.", - "Each endpoint answered with a crossword clue so obscure it demanded a PhD in Breakfast Cereals.", - "Responses contained obscure song lyrics that led to several lawsuits from very offended shower singers.", - "The service offered a handcrafted conspiracy about pigeons, quantum routers, and a secret society of baristas.", - "Users received horoscopes telling their IPs to avoid Tuesdays and to invest heavily in chamomile tea.", - "Records were shuffled like a magician's deck, with the ace of spades mysteriously serving as the primary key.", - "The server spun a conspiracy about sentient staplers plotting to replace USB‑C with fashionable shoelaces.", - "Replies were impossible tongue twisters typed by the server while giggling, causing voice assistants to short out.", - } - - allIncidentTitles = append(append(append([]string{}, normalIncidentTitles...), mixedIncidentTitels...), sillyIncidentTitles...) - allIncidentDescriptions = append(append(append([]string{}, normalIncidentDescriptions...), mixedIncidentTitels...), sillyIncidentDescriptions...) -) diff --git a/internal/api/types.go b/internal/api/types.go new file mode 100644 index 0000000..c641004 --- /dev/null +++ b/internal/api/types.go @@ -0,0 +1,78 @@ +// 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 <https://www.gnu.org/licenses/>. + +package api + +import "time" + +type statusData struct { + overallStatus overallStatus + services []service + metrics metrics + incidents []incident + maintenanceEvents []maintenanceEvent + uptimeEvents []uptimeEvent +} + +type overallStatus struct { + Status string `json:"status"` + Description string `json:"description"` + Uptime float64 `json:"uptime"` + ResponseTime int `json:"responseTime"` + ActiveIncidents int `json:"activeIncidents"` + ScheduledMaintenance int `json:"scheduledMaintenance"` +} + +type service struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Icon string `json:"icon"` + Status string `json:"status"` + ResponseTime int `json:"responseTime"` + Uptime float64 `json:"uptime"` +} + +type metrics struct { + Uptime float64 `json:"uptime"` + ResponseTime int `json:"responseTime"` + RequestVolume int `json:"requestVolume"` + ErrorRate float64 `json:"errorRate"` +} + +type incident struct { + Id string `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + Status string `json:"status"` + Severity string `json:"severity"` + StartTime time.Time `json:"startTime"` + AffectedServices []string `json:"affectedServices"` +} + +type maintenanceEvent struct { + Id string `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + StartTime time.Time `json:"startTime"` + EndTime time.Time `json:"endTime"` + AffectedServices []string `json:"affectedServices"` +} + +type uptimeEvent struct { + Date time.Time `json:"date"` + Uptime float64 `json:"uptime"` +} diff --git a/internal/api/utilities.go b/internal/api/utilities.go new file mode 100644 index 0000000..508e341 --- /dev/null +++ b/internal/api/utilities.go @@ -0,0 +1,74 @@ +// 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 <https://www.gnu.org/licenses/>. + +package api + +import ( + "math/rand" + "net" + "regexp" + "strings" +) + +func runChance(likelihood float64, action func()) { + if likelihood <= 0 { + return // never run + } + if likelihood >= 1 { + action() + return + } + + if rand.Float64() < likelihood { + action() + } +} + +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 + }) +} + +func addRotLog(logs *[LogLength]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 == LogLength { + for i := 0; i < LogLength-1; i++ { + logs[i] = logs[i+1] + } + logs[LogLength-1] = value + parser <- value + } else { + logs[*last] = value + *last++ + parser <- value + } + } +} |