commit c3b17e82eb4a515a2fc1a9bd71b14431417ed9a9
parent ff984cad70ab3182121107216a7e57a0c6d18b20
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date: Mon, 18 Sep 2023 22:19:57 +0200
feat(49/52): add cron and healthz
Diffstat:
M | go.mod | | | 1 | + |
M | go.sum | | | 2 | ++ |
M | main.go | | | 44 | ++++++++++++++++++++++++++------------------ |
M | server.go | | | 78 | +++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
4 files changed, 76 insertions(+), 49 deletions(-)
diff --git a/go.mod b/go.mod
@@ -29,5 +29,6 @@ require (
golang.org/x/text v0.9.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/robfig/cron.v2 v2.0.0-20150107220207-be2e0b0deed5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
@@ -488,6 +488,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/robfig/cron.v2 v2.0.0-20150107220207-be2e0b0deed5 h1:E846t8CnR+lv5nE+VuiKTDG/v1U2stad0QzddfJC7kY=
+gopkg.in/robfig/cron.v2 v2.0.0-20150107220207-be2e0b0deed5/go.mod h1:hiOFpYm0ZJbusNj2ywpbrXowU3G8U6GIQzqn2mw1UIE=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/main.go b/main.go
@@ -2,11 +2,12 @@ package main
import (
"fmt"
+ "github.com/julienschmidt/httprouter"
+ "gopkg.in/robfig/cron.v2"
+ "hivedav/caldav"
+ "hivedav/config"
"log"
"net/http"
- "hivedav/config"
- "hivedav/caldav"
- "github.com/julienschmidt/httprouter"
)
var hivedavVersion = "v0.2-nightly"
@@ -26,49 +27,56 @@ func main() {
}
log.Printf("Starting hivedav %s 🍯\n", hivedavVersion)
-
log.Println("Reading availability from:", conf.CaldavUri)
log.Println("Reading as user:", conf.CaldavUser)
+ c := cron.New()
+ c.AddFunc("@every 30m", func() { updateDatabase(server) })
+ c.Start()
+ go updateDatabase(server)
+
+ router := httprouter.New()
+ router.GET("/", server.Index)
+ router.GET("/week/:week", server.Week)
+ router.ServeFiles("/css/*filepath", http.Dir("./css"))
+ router.GET("/healthz", server.Healthz)
+ log.Printf("Listening on %s:%d\n", conf.ListenAddress, conf.ListenPort)
+ log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", conf.ListenAddress, conf.ListenPort), router))
+}
+
+func updateDatabase(s *Server) {
// Find current user principal
- userPrincipal, err := caldav.UserPrincipal(conf.CaldavUri, conf.CaldavUser, conf.CaldavPassword)
+ userPrincipal, err := caldav.UserPrincipal(s.config.CaldavUri, s.config.CaldavUser, s.config.CaldavPassword)
if err != nil {
log.Fatal("Error reading current user principal: ", err)
}
log.Println("User principal:", userPrincipal)
// Find home set of current user principal
- homeSetUri, err := caldav.CalendarHome(conf.CaldavUri, conf.CaldavUser, conf.CaldavPassword, userPrincipal, conf.Calendar)
+ homeSetUri, err := caldav.CalendarHome(s.config.CaldavUri, s.config.CaldavUser, s.config.CaldavPassword, userPrincipal, s.config.Calendar)
if err != nil {
log.Fatal("Error reading home set URI: ", err)
}
log.Println("Calendar homeset:", homeSetUri)
// Find calendar URI for the calendar specified in the config
- calendarUri, err := caldav.CalendarFromHomeSet(conf.CaldavUri, conf.CaldavUser, conf.CaldavPassword, homeSetUri, conf.Calendar)
+ calendarUri, err := caldav.CalendarFromHomeSet(s.config.CaldavUri, s.config.CaldavUser, s.config.CaldavPassword, homeSetUri, s.config.Calendar)
if err != nil {
log.Fatal("Error reading calendar URI: ", err)
}
- log.Printf("Using calendar %d in homeset with URI '%s'\n", conf.Calendar, calendarUri)
+ log.Printf("Using calendar %d in homeset with URI '%s'\n", s.config.Calendar, calendarUri)
log.Println("⏳ Fetching availability from CalDAV server, please wait..")
// Get availability data for the calendar specified in the config
- calData, err := caldav.GetAvailability(conf.CaldavUri, conf.CaldavUser, conf.CaldavPassword, calendarUri)
+ calData, err := caldav.GetAvailability(s.config.CaldavUri, s.config.CaldavUser, s.config.CaldavPassword, calendarUri)
if err != nil {
log.Fatal("Error reading calendar URI: ", err)
}
- err = server.updateAvailability(calData)
+ err = s.UpdateAvailability(calData)
if err != nil {
log.Fatalf("Error fetching availability: %v", err)
}
- log.Printf("Ready, listening on %s:%d\n", conf.ListenAddress, conf.ListenPort)
-
- router := httprouter.New()
- router.GET("/", server.Index)
- router.GET("/week/:week", server.Week)
- router.ServeFiles("/css/*filepath", http.Dir("./css"))
-
- log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", conf.ListenAddress, conf.ListenPort), router))
+ log.Println("Ready. Database was initialized with latest CalDAV availability data.")
}
diff --git a/server.go b/server.go
@@ -1,36 +1,36 @@
package main
import (
- "regexp"
- "time"
- "strconv"
- "github.com/teambition/rrule-go"
- "github.com/emersion/go-ical"
- "hivedav/config"
- "hivedav/caldav"
+ "database/sql"
"fmt"
+ "github.com/emersion/go-ical"
+ "github.com/julienschmidt/httprouter"
+ _ "github.com/mattn/go-sqlite3"
"github.com/pquerna/termchalk/prettytable"
+ "github.com/teambition/rrule-go"
+ "hivedav/caldav"
+ "hivedav/config"
"html/template"
- _ "github.com/mattn/go-sqlite3"
- "database/sql"
"io"
"log"
"net/http"
- "github.com/julienschmidt/httprouter"
+ "regexp"
+ "strconv"
"strings"
+ "time"
)
type Server struct {
config *config.Config
- db *sql.DB
+ db *sql.DB
}
type TableData struct {
TableHead []string
- Rows [][]string
- Week int
- Year int
- Version string
+ Rows [][]string
+ Week int
+ Year int
+ Version string
}
var drop = `DROP TABLE IF EXISTS availability;
@@ -169,7 +169,7 @@ func (s *Server) Week(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
rows := make([][]string, 9)
for i := range rows {
- rows[i] = make([]string, 6)
+ rows[i] = make([]string, 6)
}
// Define the table header
@@ -183,12 +183,12 @@ func (s *Server) Week(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
fmt.Sprintf("Fri %s", monday.Add(time.Hour*24*4).Format("02.01.")),
}
- tableData := TableData {
+ tableData := TableData{
TableHead: tableHead,
- Rows: rows,
- Week: week,
- Year: year,
- Version: hivedavVersion,
+ Rows: rows,
+ Week: week,
+ Year: year,
+ Version: hivedavVersion,
}
// TODO: use timeIt to go through the loops below, remove the static arrays
@@ -198,15 +198,15 @@ func (s *Server) Week(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
// Working hours - Eight to Five
//for h := 8; h < 17; h++ {
- for _, h := range []int{8,9,10,11,12,13,14,15,16} {
+ for _, h := range []int{8, 9, 10, 11, 12, 13, 14, 15, 16} {
var availability [5]string
// Working days - Monday through Friday
//for d := 0; d < 5; d++ {
- for _, d := range []int{1,2,3,4,5} {
+ for _, d := range []int{1, 2, 3, 4, 5} {
// add/remove 1 minute to make the sql time query in available() below behave correctly
// convert to read/compare in UTC from db
- start := timeIt.Add(time.Hour * time.Duration((d-1)*24 + h)).Add(1 * time.Minute).UTC().Format(sqlDateFmt)
- end := timeIt.Add(time.Hour * time.Duration((d-1)*24 + h+1)).Add(-1 * time.Minute).UTC().Format(sqlDateFmt)
+ start := timeIt.Add(time.Hour * time.Duration((d-1)*24+h)).Add(1 * time.Minute).UTC().Format(sqlDateFmt)
+ end := timeIt.Add(time.Hour * time.Duration((d-1)*24+h+1)).Add(-1 * time.Minute).UTC().Format(sqlDateFmt)
avi, err := s.available(start, end)
if err != nil {
log.Printf("Error getting availability on day '%d' hour '%d'\n", d, h)
@@ -270,14 +270,14 @@ func (s *Server) Week(w http.ResponseWriter, req *http.Request, ps httprouter.Pa
func (s *Server) Index(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
timeNow := time.Now()
- _, currentWeek:= timeNow.ISOWeek()
+ _, currentWeek := timeNow.ISOWeek()
ps := httprouter.Params{
httprouter.Param{"week", strconv.Itoa(currentWeek)},
}
s.Week(w, req, ps)
}
-func (s *Server) updateAvailability(calData []caldav.CalData) error {
+func (s *Server) UpdateAvailability(calData []caldav.CalData) error {
// TODO: Make max for recurring events without end time configurable
rrLimit := time.Date(2024, 12, 31, 0, 0, 0, 0, time.Local)
//localNow := time.Now()
@@ -309,14 +309,14 @@ func (s *Server) updateAvailability(calData []caldav.CalData) error {
if c.Name == ical.CompTimezone {
for _, child := range c.Children {
//tzOffsetFrom = child.Props.Get(ical.PropTimezoneOffsetFrom).Value;
- tzOffsetTo = child.Props.Get(ical.PropTimezoneOffsetTo).Value;
+ tzOffsetTo = child.Props.Get(ical.PropTimezoneOffsetTo).Value
}
}
}
var tzOffsetDuration time.Duration
re := regexp.MustCompile(`^([+-][0-9]{2}).*`)
- matches := re.FindStringSubmatch(tzOffsetTo)
+ matches := re.FindStringSubmatch(tzOffsetTo)
if len(matches) == 0 {
//_, offset := localNow.Zone()
//tzOffsetDuration = time.Second * time.Duration(offset)
@@ -327,7 +327,7 @@ func (s *Server) updateAvailability(calData []caldav.CalData) error {
tzOffsetDurationInt, _ := strconv.Atoi(matches[1])
tzOffsetDuration = time.Hour * time.Duration(tzOffsetDurationInt)
// reset to UTC
- tzOffsetDuration = time.Hour * time.Duration(-1 * tzOffsetDurationInt)
+ tzOffsetDuration = time.Hour * time.Duration(-1*tzOffsetDurationInt)
}
for _, e := range cal.Events() {
@@ -408,7 +408,7 @@ func (s *Server) updateAvailability(calData []caldav.CalData) error {
}
// parse time from local time zone and return UTC time for sqlite db
-func parseTime(timeStr string, offset time.Duration) (time.Time, error){
+func parseTime(timeStr string, offset time.Duration) (time.Time, error) {
t, err := time.Parse("20060102T150405Z", timeStr)
if err != nil {
t, err = time.Parse("20060102T150405", timeStr)
@@ -422,6 +422,22 @@ func parseTime(timeStr string, offset time.Duration) (time.Time, error){
return t.Add(offset), nil
}
+// https://github.com/kelseyhightower/app-healthz
+func (s *Server) Healthz(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
+ var count = 0
+ row := s.db.QueryRow("SELECT count(*) FROM availability")
+
+ if err := row.Scan(&count); err == sql.ErrNoRows {
+ log.Println("Error reading availabilty table")
+ }
+ if count > 0 {
+ io.WriteString(w, fmt.Sprintf("{\"status\":\"wagwan, hive all good\",\"rows\":\"%d\",\"version\":\"%s\"}", count, hivedavVersion))
+ } else {
+ w.WriteHeader(http.StatusInternalServerError)
+ io.WriteString(w, fmt.Sprintf("{\"status\":\"nah fam, db not initialized\",\"rows\":\"%d\",\"version\":\"%s\"}", count, hivedavVersion))
+ }
+}
+
func (s *Server) showCubicles() {
}