mirror of
https://github.com/trafficlunar/statsys.git
synced 2026-03-28 11:13:17 +00:00
feat: uptime percentages
This commit is contained in:
parent
9cc207cb66
commit
b36ac9523c
4 changed files with 70 additions and 7 deletions
3
TODO.md
3
TODO.md
|
|
@ -1,6 +1,5 @@
|
|||
- [ ] Accessibility
|
||||
- [ ] Better mobile support
|
||||
- [ ] Uptime percentage
|
||||
- [ ] View incidents
|
||||
- [ ] Latency graphs?
|
||||
- [ ] README thumbnail
|
||||
- [ ] README
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
|
|
@ -52,6 +53,7 @@ func InitializeDatabase() {
|
|||
service.MinuteTimeline = generateRecap(rawTimeline, "minutes")
|
||||
service.HourTimeline = generateRecap(rawTimeline, "hours")
|
||||
service.DayTimeline = generateRecap(rawTimeline, "days")
|
||||
calculateUptimePercentages(index)
|
||||
|
||||
// incidents
|
||||
incidentRows, err := db.Query(`SELECT status, startTime, endTime FROM incidents WHERE service = ?`, service.Name)
|
||||
|
|
@ -189,11 +191,11 @@ func generateRecap(rawTimeline []TimelineEntry, view string) []TimelineEntry {
|
|||
|
||||
switch view {
|
||||
case "minutes":
|
||||
timestamp = now.Add(-time.Duration(30-i) * time.Minute).Truncate(time.Minute)
|
||||
timestamp = now.Add(-time.Duration(limit-i) * time.Minute).Truncate(time.Minute)
|
||||
case "hours":
|
||||
timestamp = now.Add(-time.Duration(24-i) * time.Hour).Truncate(time.Hour)
|
||||
timestamp = now.Add(-time.Duration(limit-i) * time.Hour).Truncate(time.Hour)
|
||||
case "days":
|
||||
day := now.AddDate(0, 0, -(30 - i))
|
||||
day := now.AddDate(0, 0, -(limit - i))
|
||||
timestamp = time.Date(day.Year(), day.Month(), day.Day(), 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
|
||||
|
|
@ -212,6 +214,40 @@ func generateRecap(rawTimeline []TimelineEntry, view string) []TimelineEntry {
|
|||
return timeline
|
||||
}
|
||||
|
||||
func calculateUptimePercentages(serviceIndex int) {
|
||||
service := templateData.Services[serviceIndex]
|
||||
|
||||
calculateUptime := func(timeline []TimelineEntry) float64 {
|
||||
if len(timeline) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
online := 0
|
||||
total := 0
|
||||
|
||||
for _, entry := range timeline {
|
||||
if entry.Status != "Unknown" {
|
||||
total++
|
||||
if entry.Status == "Online" {
|
||||
online++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return math.Floor(float64(online)/float64(total)*100*100) / 100
|
||||
}
|
||||
|
||||
service.MinuteUptime = calculateUptime(service.MinuteTimeline)
|
||||
service.HourUptime = calculateUptime(service.HourTimeline)
|
||||
service.DayUptime = calculateUptime(service.DayTimeline)
|
||||
|
||||
templateData.Services[serviceIndex] = service
|
||||
}
|
||||
|
||||
// add entry to timeline and remove entries older than 30 days
|
||||
func AddToTimeline(serviceIndex int, status string) {
|
||||
service := templateData.Services[serviceIndex]
|
||||
|
|
@ -297,6 +333,7 @@ func AddToTimeline(serviceIndex int, status string) {
|
|||
}
|
||||
|
||||
templateData.Services[serviceIndex] = service
|
||||
calculateUptimePercentages(serviceIndex)
|
||||
}
|
||||
|
||||
func AddIncident(serviceIndex int, status string, startTime time.Time) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ type Service struct {
|
|||
MinuteTimeline []TimelineEntry
|
||||
HourTimeline []TimelineEntry
|
||||
DayTimeline []TimelineEntry
|
||||
MinuteUptime float64
|
||||
HourUptime float64
|
||||
DayUptime float64
|
||||
Incidents []Incident
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +77,7 @@ func renderError(w http.ResponseWriter, statusCode int, message string) {
|
|||
}
|
||||
|
||||
func index(w http.ResponseWriter, req *http.Request) {
|
||||
// Handle 404
|
||||
// handle 404
|
||||
if req.URL.Path != "/" {
|
||||
renderError(w, http.StatusNotFound, "Page not found")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -232,15 +232,33 @@
|
|||
}
|
||||
|
||||
.bars-footer {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.75rem;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
margin-top: 0.5rem;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bars-footer span {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.bars-footer hr {
|
||||
flex-grow: 1;
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.uptime-percentage {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: white;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
|
|
@ -487,6 +505,8 @@
|
|||
|
||||
<div class="bars-footer">
|
||||
<span>30 minutes ago</span>
|
||||
<hr />
|
||||
<span class="uptime-percentage">{{ $serviceStatus.MinuteUptime }}% uptime</span>
|
||||
<span>now</span>
|
||||
</div>
|
||||
{{ else if eq $.View "hours" }}
|
||||
|
|
@ -503,6 +523,8 @@
|
|||
|
||||
<div class="bars-footer">
|
||||
<span>24 hours ago</span>
|
||||
<hr />
|
||||
<span class="uptime-percentage">{{ $serviceStatus.HourUptime }}% uptime</span>
|
||||
<span>now</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -520,6 +542,8 @@
|
|||
|
||||
<div class="bars-footer">
|
||||
<span>30 days ago</span>
|
||||
<hr />
|
||||
<span class="uptime-percentage">{{ $serviceStatus.DayUptime }}% uptime</span>
|
||||
<span>today</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
|
|
|||
Loading…
Reference in a new issue