Track queries in usage data + better formatting in the internal usage page

This commit is contained in:
David Dworken 2022-09-30 23:38:35 -07:00
parent 98a4f002fa
commit 18ddbf2ca9

View File

@ -17,6 +17,7 @@ import (
"github.com/ddworken/hishtory/shared" "github.com/ddworken/hishtory/shared"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/rodaine/table"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
@ -40,8 +41,12 @@ type UsageData struct {
LastUsed time.Time `json:"last_used"` LastUsed time.Time `json:"last_used"`
LastIp string `json:"last_ip"` LastIp string `json:"last_ip"`
NumEntriesHandled int `json:"num_entries_handled"` NumEntriesHandled int `json:"num_entries_handled"`
LastQueried time.Time `json:"last_queried"`
NumQueries int `json:"num_queries"`
} }
// TODO: Audit this file for queries that don't check result.Error
func getRequiredQueryParam(r *http.Request, queryParam string) string { func getRequiredQueryParam(r *http.Request, queryParam string) string {
val := r.URL.Query().Get(queryParam) val := r.URL.Query().Get(queryParam)
if val == "" { if val == "" {
@ -50,18 +55,20 @@ func getRequiredQueryParam(r *http.Request, queryParam string) string {
return val return val
} }
func updateUsageData(r *http.Request, userId, deviceId string, numEntries int) { func updateUsageData(r *http.Request, userId, deviceId string, numEntriesHandled int, isQuery bool) {
var usageData []UsageData var usageData []UsageData
GLOBAL_DB.Where("user_id = ? AND device_id = ?", userId, deviceId).Find(&usageData) GLOBAL_DB.Where("user_id = ? AND device_id = ?", userId, deviceId).Find(&usageData)
if len(usageData) == 0 { if len(usageData) == 0 {
GLOBAL_DB.Create(&UsageData{UserId: userId, DeviceId: deviceId, LastUsed: time.Now(), NumEntriesHandled: numEntries}) GLOBAL_DB.Create(&UsageData{UserId: userId, DeviceId: deviceId, LastUsed: time.Now(), NumEntriesHandled: numEntriesHandled})
} else { } else {
GLOBAL_DB.Model(&UsageData{}).Where("user_id = ? AND device_id = ?", userId, deviceId).Update("last_used", time.Now()).Update("last_ip", getRemoteAddr(r)) GLOBAL_DB.Model(&UsageData{}).Where("user_id = ? AND device_id = ?", userId, deviceId).Update("last_used", time.Now()).Update("last_ip", getRemoteAddr(r))
if numEntries > 0 { if numEntriesHandled > 0 {
GLOBAL_DB.Exec("UPDATE usage_data SET num_entries_handled = COALESCE(num_entries_handled, 0) + ? WHERE user_id = ? AND device_id = ?", numEntries, userId, deviceId) GLOBAL_DB.Exec("UPDATE usage_data SET num_entries_handled = COALESCE(num_entries_handled, 0) + ? WHERE user_id = ? AND device_id = ?", numEntriesHandled, userId, deviceId)
} }
} }
if isQuery {
GLOBAL_DB.Exec("UPDATE usage_data SET num_queries = COALESCE(num_queries, 0) + 1, last_queried = ? WHERE user_id = ? AND device_id = ?", time.Now(), userId, deviceId)
}
} }
func usageStatsHandler(w http.ResponseWriter, r *http.Request) { func usageStatsHandler(w http.ResponseWriter, r *http.Request) {
@ -71,7 +78,9 @@ func usageStatsHandler(w http.ResponseWriter, r *http.Request) {
COUNT(DISTINCT devices.device_id) as num_devices, COUNT(DISTINCT devices.device_id) as num_devices,
SUM(usage_data.num_entries_handled) as num_history_entries, SUM(usage_data.num_entries_handled) as num_history_entries,
MAX(usage_data.last_used) as last_active, MAX(usage_data.last_used) as last_active,
COALESCE(STRING_AGG(DISTINCT usage_data.last_ip, ' ') FILTER (WHERE usage_data.last_ip != 'Unknown'), 'Unknown') as ip_addresses COALESCE(STRING_AGG(DISTINCT usage_data.last_ip, ' ') FILTER (WHERE usage_data.last_ip != 'Unknown'), 'Unknown') as ip_addresses,
COALESCE(SUM(usage_data.num_queries), 0) as num_queries,
COALESCE(MAX(usage_data.last_queried), 'January 1, 1970') as last_queried
FROM devices FROM devices
INNER JOIN usage_data ON devices.device_id = usage_data.device_id INNER JOIN usage_data ON devices.device_id = usage_data.device_id
GROUP BY devices.user_id GROUP BY devices.user_id
@ -81,18 +90,23 @@ func usageStatsHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
tbl := table.New("Registration Date", "Num Devices", "Num Entries", "Num Queries", "Last Active", "Last Query", "IPs")
tbl.WithWriter(w)
for rows.Next() { for rows.Next() {
var registrationDate time.Time var registrationDate time.Time
var numDevices int var numDevices int
var numEntries int var numEntries int
var lastUsedDate time.Time var lastUsedDate time.Time
var ipAddresses string var ipAddresses string
err = rows.Scan(&registrationDate, &numDevices, &numEntries, &lastUsedDate, &ipAddresses) var numQueries int
var lastQueried time.Time
err = rows.Scan(&registrationDate, &numDevices, &numEntries, &lastUsedDate, &ipAddresses, &numQueries, &lastQueried)
if err != nil { if err != nil {
panic(err) panic(err)
} }
w.Write([]byte(fmt.Sprintf("Registered: %s\tNumDevices: %d\tNumEntries: %d\tLastUsed: %s\tIP: %s\n", registrationDate.Format("2006-01-02"), numDevices, numEntries, lastUsedDate.Format("2006-01-02"), ipAddresses))) tbl.AddRow(registrationDate.Format("2006-01-02"), numDevices, numEntries, numQueries, lastUsedDate.Format("2006-01-02"), lastQueried.Format("2006-01-02"), ipAddresses)
} }
tbl.Print()
} }
func apiSubmitHandler(w http.ResponseWriter, r *http.Request) { func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
@ -107,7 +121,7 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
} }
fmt.Printf("apiSubmitHandler: received request containg %d EncHistoryEntry\n", len(entries)) fmt.Printf("apiSubmitHandler: received request containg %d EncHistoryEntry\n", len(entries))
for _, entry := range entries { for _, entry := range entries {
updateUsageData(r, entry.UserId, entry.DeviceId, 1) updateUsageData(r, entry.UserId, entry.DeviceId, 1, false)
tx := GLOBAL_DB.Where("user_id = ?", entry.UserId) tx := GLOBAL_DB.Where("user_id = ?", entry.UserId)
var devices []*shared.Device var devices []*shared.Device
result := tx.Find(&devices) result := tx.Find(&devices)
@ -131,7 +145,7 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
func apiBootstrapHandler(w http.ResponseWriter, r *http.Request) { func apiBootstrapHandler(w http.ResponseWriter, r *http.Request) {
userId := getRequiredQueryParam(r, "user_id") userId := getRequiredQueryParam(r, "user_id")
deviceId := getRequiredQueryParam(r, "device_id") deviceId := getRequiredQueryParam(r, "device_id")
updateUsageData(r, userId, deviceId, 0) updateUsageData(r, userId, deviceId, 0, false)
tx := GLOBAL_DB.Where("user_id = ?", userId) tx := GLOBAL_DB.Where("user_id = ?", userId)
var historyEntries []*shared.EncHistoryEntry var historyEntries []*shared.EncHistoryEntry
result := tx.Find(&historyEntries) result := tx.Find(&historyEntries)
@ -148,7 +162,7 @@ func apiBootstrapHandler(w http.ResponseWriter, r *http.Request) {
func apiQueryHandler(w http.ResponseWriter, r *http.Request) { func apiQueryHandler(w http.ResponseWriter, r *http.Request) {
userId := getRequiredQueryParam(r, "user_id") userId := getRequiredQueryParam(r, "user_id")
deviceId := getRequiredQueryParam(r, "device_id") deviceId := getRequiredQueryParam(r, "device_id")
updateUsageData(r, userId, deviceId, 0) updateUsageData(r, userId, deviceId, 0, true)
// Increment the count // Increment the count
result := GLOBAL_DB.Exec("UPDATE enc_history_entries SET read_count = read_count + 1 WHERE device_id = ?", deviceId) result := GLOBAL_DB.Exec("UPDATE enc_history_entries SET read_count = read_count + 1 WHERE device_id = ?", deviceId)
if result.Error != nil { if result.Error != nil {
@ -201,7 +215,7 @@ func apiRegisterHandler(w http.ResponseWriter, r *http.Request) {
if existingDevicesCount > 0 { if existingDevicesCount > 0 {
GLOBAL_DB.Create(&shared.DumpRequest{UserId: userId, RequestingDeviceId: deviceId, RequestTime: time.Now()}) GLOBAL_DB.Create(&shared.DumpRequest{UserId: userId, RequestingDeviceId: deviceId, RequestTime: time.Now()})
} }
updateUsageData(r, userId, deviceId, 0) updateUsageData(r, userId, deviceId, 0, false)
} }
func apiGetPendingDumpRequestsHandler(w http.ResponseWriter, r *http.Request) { func apiGetPendingDumpRequestsHandler(w http.ResponseWriter, r *http.Request) {
@ -254,7 +268,7 @@ func apiSubmitDumpHandler(w http.ResponseWriter, r *http.Request) {
if result.Error != nil { if result.Error != nil {
panic(fmt.Errorf("failed to clear the dump request: %v", err)) panic(fmt.Errorf("failed to clear the dump request: %v", err))
} }
updateUsageData(r, userId, srcDeviceId, len(entries)) updateUsageData(r, userId, srcDeviceId, len(entries), false)
} }
func apiBannerHandler(w http.ResponseWriter, r *http.Request) { func apiBannerHandler(w http.ResponseWriter, r *http.Request) {