feat(analyticsApi): added /api/analytics/pageViewCount endpoint
Build and Push Docker image / build-and-push (push) Successful in 2m7s
Details
Build and Push Docker image / build-and-push (push) Successful in 2m7s
Details
This commit is contained in:
parent
87c191c11a
commit
92b03054d9
|
@ -11,18 +11,165 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// initSessionCount
|
||||
// initPageViewCount
|
||||
//
|
||||
// this endpoint is used to aggregate session metadata counts
|
||||
// it adds the following endpoint to the app:
|
||||
// GET /api/analytics/sessionCounts
|
||||
// This endpoint is used to retrieve page view counts for each unique path.
|
||||
// It adds the following endpoint to the app:
|
||||
// GET /api/analytics/pageViewCount
|
||||
//
|
||||
// the endpoint expects the following request data:
|
||||
// The endpoint expects the following query parameter:
|
||||
//
|
||||
// ?startDate=ISO 8601 formatted date (optional)
|
||||
//
|
||||
// - `startDate`: An optional start date in ISO 8601 format (e.g., "2024-11-12T15:04:05Z").
|
||||
// If provided, the global count reflects views from this date onward.
|
||||
// If not provided, the global count includes all views.
|
||||
//
|
||||
// Response format:
|
||||
// [
|
||||
//
|
||||
// {
|
||||
// "startDate": "start date in ISO 8601 format",
|
||||
// "id": <unique row id>,
|
||||
// "data": "<path>",
|
||||
// "count": <total view count since startDate>,
|
||||
// "last_30_days_data": [
|
||||
// {
|
||||
// "date": "YYYY-MM-DDTHH:MM:SSZ",
|
||||
// "count": <daily count>
|
||||
// },
|
||||
// ...
|
||||
// ]
|
||||
// },
|
||||
// ...
|
||||
//
|
||||
// ]
|
||||
//
|
||||
// `last_30_days_data` always provides daily counts for the most recent 30 days regardless of the `startDate` parameter.
|
||||
func initPageViewCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
|
||||
|
||||
e.Router.GET("/api/analytics/pageViewCount", func(c echo.Context) error {
|
||||
|
||||
// Check if user is in admin group
|
||||
if err := ldapApi.UserIsInAdminGroup(app, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get startDate from query parameter
|
||||
startDateParam := c.QueryParam("startDate")
|
||||
var startDate time.Time
|
||||
var err error
|
||||
if startDateParam != "" {
|
||||
// Parse the startDate, expecting an ISO 8601 format
|
||||
startDate, err = time.Parse(time.RFC3339, startDateParam)
|
||||
if err != nil {
|
||||
return apis.NewBadRequestError("Invalid start date format, expected ISO 8601", err)
|
||||
}
|
||||
}
|
||||
|
||||
type PageViewData struct {
|
||||
ID int `json:"id"`
|
||||
Data string `json:"data"`
|
||||
Count int `json:"count"`
|
||||
Last30DaysData interface{} `json:"last_30_days_data"`
|
||||
}
|
||||
|
||||
var result []PageViewData
|
||||
|
||||
// Execute the query with nested JSON aggregation
|
||||
err = app.Dao().DB().
|
||||
NewQuery(`
|
||||
SELECT
|
||||
(ROW_NUMBER() OVER()) AS id,
|
||||
view.path AS data,
|
||||
(
|
||||
SELECT COUNT(id)
|
||||
FROM analyticsPageViews
|
||||
WHERE path = view.path AND created >= {:startDate}
|
||||
) AS count,
|
||||
(
|
||||
SELECT json_group_array(
|
||||
json_object(
|
||||
'date', strftime('%Y-%m-%dT%H:%M:%SZ', created),
|
||||
'count', daily_count
|
||||
)
|
||||
)
|
||||
FROM (
|
||||
SELECT
|
||||
strftime('%Y-%m-%dT%H:%M:%SZ', created) AS date,
|
||||
COUNT(id) AS daily_count
|
||||
FROM analyticsPageViews
|
||||
WHERE
|
||||
path = view.path AND
|
||||
created >= datetime('now', '-30 days')
|
||||
GROUP BY date
|
||||
) AS daily_data
|
||||
) AS last_30_days_data
|
||||
FROM
|
||||
analyticsPageViews view
|
||||
GROUP BY
|
||||
view.path
|
||||
`).
|
||||
Bind(dbx.Params{
|
||||
"startDate": startDate,
|
||||
}).
|
||||
All(&result)
|
||||
|
||||
if err != nil {
|
||||
return apis.NewApiError(500, "Failed to query page view data", err)
|
||||
}
|
||||
|
||||
// Return the final JSON response
|
||||
return c.JSON(200, result)
|
||||
}, apis.ActivityLogger(app))
|
||||
}
|
||||
|
||||
// initAggregateCount
|
||||
//
|
||||
// This endpoint is used to aggregate session metadata counts by various categories
|
||||
// (device type, browser name, operating system, user agent, geo country code, and preferred language).
|
||||
// It adds the following endpoint to the app:
|
||||
// GET /api/analytics/sessionCounts
|
||||
//
|
||||
// The endpoint expects the following query parameter:
|
||||
//
|
||||
// ?startDate=ISO 8601 formatted date (optional)
|
||||
//
|
||||
// - `startDate`: An optional start date in ISO 8601 format (e.g., "2024-11-12T15:04:05Z").
|
||||
// If provided, counts reflect sessions from this date onward.
|
||||
// If not provided, counts include all sessions.
|
||||
//
|
||||
// Response format:
|
||||
//
|
||||
// {
|
||||
// "device_type": [
|
||||
// { "value": "<device type>", "count": <session count> },
|
||||
// ...
|
||||
// ],
|
||||
// "browser_name": [
|
||||
// { "value": "<browser name>", "count": <session count> },
|
||||
// ...
|
||||
// ],
|
||||
// "operating_system": [
|
||||
// { "value": "<operating system>", "count": <session count> },
|
||||
// ...
|
||||
// ],
|
||||
// "user_agent": [
|
||||
// { "value": "<user agent>", "count": <session count> },
|
||||
// ...
|
||||
// ],
|
||||
// "geo_country_code": [
|
||||
// { "value": "<country code>", "count": <session count> },
|
||||
// ...
|
||||
// ],
|
||||
// "preferred_language": [
|
||||
// { "value": "<preferred language>", "count": <session count> },
|
||||
// ...
|
||||
// ]
|
||||
// }
|
||||
func initSessionCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
|
||||
//
|
||||
// Each category includes an array of objects where `value` is the specific item (e.g., a device type or browser name)
|
||||
// and `count` is the number of sessions matching that item since `startDate`.
|
||||
func initAggregateCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
|
||||
e.Router.GET("/api/analytics/aggregateCount", func(c echo.Context) error {
|
||||
|
||||
// Check if user is in admin group
|
||||
|
@ -44,7 +191,7 @@ func initSessionCount(app *pocketbase.PocketBase, e *core.ServeEvent) {
|
|||
// 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"}
|
||||
fields := []string{"device_type", "browser_name", "operating_system", "geo_country_code", "preferred_language"}
|
||||
|
||||
for _, field := range fields {
|
||||
data := []Data{}
|
||||
|
@ -79,7 +226,8 @@ func InitAnalyticsApi(app *pocketbase.PocketBase, e *core.ServeEvent) error {
|
|||
|
||||
logger.LogInfoF("Initializing analytics api")
|
||||
|
||||
initSessionCount(app, e)
|
||||
initPageViewCount(app, e)
|
||||
initAggregateCount(app, e)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue