diff --git a/analyticsApi/main.go b/analyticsApi/main.go new file mode 100644 index 0000000..012bb62 --- /dev/null +++ b/analyticsApi/main.go @@ -0,0 +1,93 @@ +package analyticsApi + +import ( + "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/ldapApi" + "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/logger" + "github.com/labstack/echo/v5" + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase" + "github.com/pocketbase/pocketbase/apis" + "github.com/pocketbase/pocketbase/core" + "time" +) + +// initSessionCount +// +// this endpoint is used to aggregate session metadata counts +// it adds the following endpoint to the app: +// GET /api/analytics/sessionCounts +// +// the endpoint expects the following request data: +// +// { +// "startDate": "start date in ISO 8601 format", +// } +func initSessionCount(app *pocketbase.PocketBase, e *core.ServeEvent) { + e.Router.GET("/api/analytics/sessionCounts", func(c echo.Context) error { + + // Check if user is in admin group + if err := ldapApi.UserIsInAdminGroup(app, c); err != nil { + return err + } + + // Get data from request + data := struct { + StartDate string `json:"startDate"` + }{} + if err := c.Bind(&data); err != nil { + return apis.NewBadRequestError("Failed to read request data", err) + } + + // Parse the start date + startDate, err := time.Parse(time.RFC3339, data.StartDate) + if err != nil { + return apis.NewBadRequestError("Invalid start date format, expected ISO 8601", err) + } + + type Data struct { + Value string `db:"value" json:"value"` + Count int `db:"count" json:"count"` + } + + // Use a map to store the response data + response := make(map[string][]Data) + + fields := []string{"device_type", "browser_name", "operating_system", "user_agent", "geo_country_code", "preferred_language"} + + for _, field := range fields { + data := []Data{} + + err := app.Dao().DB(). + NewQuery(` + SELECT ` + field + ` as value, COUNT(id) AS count + FROM analyticsPageViews + WHERE created >= {:startDate} + GROUP BY ` + field). + Bind(dbx.Params{ + "startDate": startDate, + }). + All(&data) + + if err != nil { + return apis.NewApiError(500, "Failed to aggregate data for "+field, err) + } + + // Assign the data to the corresponding field in the map + response[field] = data + } + + // Return the final JSON response + return c.JSON(200, response) + }, apis.ActivityLogger(app)) + +} + +// InitAnalyticsApi initializes analytics api endpoints +func InitAnalyticsApi(app *pocketbase.PocketBase, e *core.ServeEvent) error { + + logger.LogInfoF("Initializing analytics api") + + initSessionCount(app, e) + + return nil +} diff --git a/main/main.go b/main/main.go index 12a488c..4322ec4 100644 --- a/main/main.go +++ b/main/main.go @@ -1,6 +1,7 @@ package main import ( + "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/analyticsApi" "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/emailApi" "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/eventEntriesNotifier" "git.stuve.uni-ulm.de/stuve-it/stuve-it-backend/ldapApi" @@ -30,6 +31,9 @@ func main() { // setup ldap login app.OnBeforeServe().Add(func(e *core.ServeEvent) error { return ldapApi.InitLdapApi(app, e) }) + // setup analytics api + app.OnBeforeServe().Add(func(e *core.ServeEvent) error { return analyticsApi.InitAnalyticsApi(app, e) }) + // setup qr api app.OnBeforeServe().Add(func(e *core.ServeEvent) error { return qrApi.InitQRApi(app, e) })