mirror of
https://github.com/ddworken/hishtory.git
synced 2025-06-25 14:32:14 +02:00
Rename a bunch of DB functions + add error checking for DB table creation (follow up to #104)
This commit is contained in:
parent
ea10050872
commit
50c74e5881
@ -60,7 +60,7 @@ func updateUsageData(r *http.Request, userId, deviceId string, numEntriesHandled
|
|||||||
return fmt.Errorf("db.UsageDataFindByUserAndDevice: %w", err)
|
return fmt.Errorf("db.UsageDataFindByUserAndDevice: %w", err)
|
||||||
}
|
}
|
||||||
if len(usageData) == 0 {
|
if len(usageData) == 0 {
|
||||||
err := GLOBAL_DB.UsageDataCreate(
|
err := GLOBAL_DB.CreateUsageData(
|
||||||
r.Context(),
|
r.Context(),
|
||||||
&shared.UsageData{
|
&shared.UsageData{
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
@ -71,28 +71,28 @@ func updateUsageData(r *http.Request, userId, deviceId string, numEntriesHandled
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("db.UsageDataCreate: %w", err)
|
return fmt.Errorf("db.CreateUsageData: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usage := usageData[0]
|
usage := usageData[0]
|
||||||
|
|
||||||
if err := GLOBAL_DB.UsageDataUpdate(r.Context(), userId, deviceId, time.Now(), getRemoteAddr(r)); err != nil {
|
if err := GLOBAL_DB.UpdateUsageData(r.Context(), userId, deviceId, time.Now(), getRemoteAddr(r)); err != nil {
|
||||||
return fmt.Errorf("db.UsageDataUpdate: %w", err)
|
return fmt.Errorf("db.UpdateUsageData: %w", err)
|
||||||
}
|
}
|
||||||
if numEntriesHandled > 0 {
|
if numEntriesHandled > 0 {
|
||||||
if err := GLOBAL_DB.UsageDataUpdateNumEntriesHandled(r.Context(), userId, deviceId, numEntriesHandled); err != nil {
|
if err := GLOBAL_DB.UpdateUsageDataForNumEntriesHandled(r.Context(), userId, deviceId, numEntriesHandled); err != nil {
|
||||||
return fmt.Errorf("db.UsageDataUpdateNumEntriesHandled: %w", err)
|
return fmt.Errorf("db.UpdateUsageDataForNumEntriesHandled: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if usage.Version != getHishtoryVersion(r) {
|
if usage.Version != getHishtoryVersion(r) {
|
||||||
if err := GLOBAL_DB.UsageDataUpdateVersion(r.Context(), userId, deviceId, getHishtoryVersion(r)); err != nil {
|
if err := GLOBAL_DB.UpdateUsageDataClientVersion(r.Context(), userId, deviceId, getHishtoryVersion(r)); err != nil {
|
||||||
return fmt.Errorf("db.UsageDataUpdateVersion: %w", err)
|
return fmt.Errorf("db.UpdateUsageDataClientVersion: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isQuery {
|
if isQuery {
|
||||||
if err := GLOBAL_DB.UsageDataUpdateNumQueries(r.Context(), userId, deviceId); err != nil {
|
if err := GLOBAL_DB.UpdateUsageDataNumberQueries(r.Context(), userId, deviceId); err != nil {
|
||||||
return fmt.Errorf("db.UsageDataUpdateNumQueries: %w", err)
|
return fmt.Errorf("db.UpdateUsageDataNumberQueries: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,23 +125,23 @@ func usageStatsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func statsHandler(w http.ResponseWriter, r *http.Request) {
|
func statsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
numDevices, err := GLOBAL_DB.DevicesCount(r.Context())
|
numDevices, err := GLOBAL_DB.CountAllDevices(r.Context())
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
numEntriesProcessed, err := GLOBAL_DB.UsageDataTotal(r.Context())
|
numEntriesProcessed, err := GLOBAL_DB.UsageDataTotal(r.Context())
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
numDbEntries, err := GLOBAL_DB.EncHistoryEntryCount(r.Context())
|
numDbEntries, err := GLOBAL_DB.CountHistoryEntries(r.Context())
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
oneWeek := time.Hour * 24 * 7
|
oneWeek := time.Hour * 24 * 7
|
||||||
weeklyActiveInstalls, err := GLOBAL_DB.WeeklyActiveInstalls(r.Context(), oneWeek)
|
weeklyActiveInstalls, err := GLOBAL_DB.CountActiveInstalls(r.Context(), oneWeek)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
weeklyQueryUsers, err := GLOBAL_DB.WeeklyQueryUsers(r.Context(), oneWeek)
|
weeklyQueryUsers, err := GLOBAL_DB.CountQueryUsers(r.Context(), oneWeek)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
lastRegistration, err := GLOBAL_DB.LastRegistration(r.Context())
|
lastRegistration, err := GLOBAL_DB.DateOfLastRegistration(r.Context())
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(w, "Num devices: %d\n", numDevices)
|
_, _ = fmt.Fprintf(w, "Num devices: %d\n", numDevices)
|
||||||
@ -166,9 +166,7 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := updateUsageData(r, entries[0].UserId, entries[0].DeviceId, len(entries), false); err != nil {
|
_ = updateUsageData(r, entries[0].UserId, entries[0].DeviceId /* numEntriesHandled = */, len(entries) /* isQuery = */, false)
|
||||||
fmt.Printf("updateUsageData: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
devices, err := GLOBAL_DB.DevicesForUser(r.Context(), entries[0].UserId)
|
devices, err := GLOBAL_DB.DevicesForUser(r.Context(), entries[0].UserId)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
@ -178,7 +176,7 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
fmt.Printf("apiSubmitHandler: Found %d devices\n", len(devices))
|
fmt.Printf("apiSubmitHandler: Found %d devices\n", len(devices))
|
||||||
|
|
||||||
err = GLOBAL_DB.DeviceEntriesCreateChunk(r.Context(), devices, entries, 1000)
|
err = GLOBAL_DB.AddHistoryEntriesForAllDevices(r.Context(), devices, entries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to execute transaction to add entries to DB: %w", err))
|
panic(fmt.Errorf("failed to execute transaction to add entries to DB: %w", err))
|
||||||
}
|
}
|
||||||
@ -193,8 +191,8 @@ 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, false)
|
_ = updateUsageData(r, userId, deviceId /* numEntriesHandled = */, 0 /* isQuery = */, false)
|
||||||
historyEntries, err := GLOBAL_DB.EncHistoryEntriesForUser(r.Context(), userId)
|
historyEntries, err := GLOBAL_DB.AllHistoryEntriesForUser(r.Context(), userId)
|
||||||
checkGormError(err, 1)
|
checkGormError(err, 1)
|
||||||
fmt.Printf("apiBootstrapHandler: Found %d entries\n", len(historyEntries))
|
fmt.Printf("apiBootstrapHandler: Found %d entries\n", len(historyEntries))
|
||||||
if err := json.NewEncoder(w).Encode(historyEntries); err != nil {
|
if err := json.NewEncoder(w).Encode(historyEntries); err != nil {
|
||||||
@ -206,7 +204,7 @@ func apiQueryHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
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, true)
|
_ = updateUsageData(r, userId, deviceId /* numEntriesHandled = */, 0 /* isQuery = */, true)
|
||||||
|
|
||||||
// Delete any entries that match a pending deletion request
|
// Delete any entries that match a pending deletion request
|
||||||
deletionRequests, err := GLOBAL_DB.DeletionRequestsForUserAndDevice(r.Context(), userId, deviceId)
|
deletionRequests, err := GLOBAL_DB.DeletionRequestsForUserAndDevice(r.Context(), userId, deviceId)
|
||||||
@ -217,7 +215,7 @@ func apiQueryHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then retrieve
|
// Then retrieve
|
||||||
historyEntries, err := GLOBAL_DB.EncHistoryEntriesForDevice(r.Context(), deviceId, 5)
|
historyEntries, err := GLOBAL_DB.HistoryEntriesForDevice(r.Context(), deviceId, 5)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
fmt.Printf("apiQueryHandler: Found %d entries for %s\n", len(historyEntries), r.URL)
|
fmt.Printf("apiQueryHandler: Found %d entries for %s\n", len(historyEntries), r.URL)
|
||||||
if err := json.NewEncoder(w).Encode(historyEntries); err != nil {
|
if err := json.NewEncoder(w).Encode(historyEntries); err != nil {
|
||||||
@ -229,11 +227,11 @@ func apiQueryHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if isProductionEnvironment() {
|
if isProductionEnvironment() {
|
||||||
go func() {
|
go func() {
|
||||||
span, ctx := tracer.StartSpanFromContext(ctx, "apiQueryHandler.incrementReadCount")
|
span, ctx := tracer.StartSpanFromContext(ctx, "apiQueryHandler.incrementReadCount")
|
||||||
err := GLOBAL_DB.DeviceIncrementReadCounts(ctx, deviceId)
|
err := GLOBAL_DB.IncrementEntryReadCountsForDevice(ctx, deviceId)
|
||||||
span.Finish(tracer.WithError(err))
|
span.Finish(tracer.WithError(err))
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
err := GLOBAL_DB.DeviceIncrementReadCounts(ctx, deviceId)
|
err := GLOBAL_DB.IncrementEntryReadCountsForDevice(ctx, deviceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to increment read counts")
|
panic("failed to increment read counts")
|
||||||
}
|
}
|
||||||
@ -265,10 +263,10 @@ func apiRegisterHandler(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")
|
||||||
|
|
||||||
existingDevicesCount, err := GLOBAL_DB.DevicesCountForUser(r.Context(), userId)
|
existingDevicesCount, err := GLOBAL_DB.CountDevicesForUser(r.Context(), userId)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
fmt.Printf("apiRegisterHandler: existingDevicesCount=%d\n", existingDevicesCount)
|
fmt.Printf("apiRegisterHandler: existingDevicesCount=%d\n", existingDevicesCount)
|
||||||
if err := GLOBAL_DB.DeviceCreate(r.Context(), &shared.Device{UserId: userId, DeviceId: deviceId, RegistrationIp: getRemoteAddr(r), RegistrationDate: time.Now()}); err != nil {
|
if err := GLOBAL_DB.CreateDevice(r.Context(), &shared.Device{UserId: userId, DeviceId: deviceId, RegistrationIp: getRemoteAddr(r), RegistrationDate: time.Now()}); err != nil {
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,9 +274,7 @@ func apiRegisterHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
err := GLOBAL_DB.DumpRequestCreate(r.Context(), &shared.DumpRequest{UserId: userId, RequestingDeviceId: deviceId, RequestTime: time.Now()})
|
err := GLOBAL_DB.DumpRequestCreate(r.Context(), &shared.DumpRequest{UserId: userId, RequestingDeviceId: deviceId, RequestTime: time.Now()})
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
}
|
}
|
||||||
if err := updateUsageData(r, userId, deviceId, 0, false); err != nil {
|
_ = updateUsageData(r, userId, deviceId /* numEntriesHandled = */, 0 /* isQuery = */, false)
|
||||||
fmt.Printf("updateUsageData: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if GLOBAL_STATSD != nil {
|
if GLOBAL_STATSD != nil {
|
||||||
GLOBAL_STATSD.Incr("hishtory.register", []string{}, 1.0)
|
GLOBAL_STATSD.Incr("hishtory.register", []string{}, 1.0)
|
||||||
@ -324,11 +320,11 @@ func apiSubmitDumpHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = GLOBAL_DB.EncHistoryCreateMulti(r.Context(), entries...)
|
err = GLOBAL_DB.AddHistoryEntries(r.Context(), entries...)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
err = GLOBAL_DB.DumpRequestDeleteForUserAndDevice(r.Context(), userId, requestingDeviceId)
|
err = GLOBAL_DB.DumpRequestDeleteForUserAndDevice(r.Context(), userId, requestingDeviceId)
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
updateUsageData(r, userId, srcDeviceId, len(entries), false)
|
_ = updateUsageData(r, userId, srcDeviceId /* numEntriesHandled = */, len(entries) /* isQuery = */, false)
|
||||||
|
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
@ -384,28 +380,19 @@ func addDeletionRequestHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if isProductionEnvironment() {
|
if isProductionEnvironment() {
|
||||||
// Check that we have a reasonable looking set of devices/entries in the DB
|
encHistoryEntryCount, err := GLOBAL_DB.CountHistoryEntries(r.Context())
|
||||||
//rows, err := GLOBAL_DB.Raw("SELECT true FROM enc_history_entries LIMIT 1 OFFSET 1000").Rows()
|
|
||||||
//if err != nil {
|
|
||||||
// panic(fmt.Sprintf("failed to count entries in DB: %v", err))
|
|
||||||
//}
|
|
||||||
//defer rows.Close()
|
|
||||||
//if !rows.Next() {
|
|
||||||
// panic("Suspiciously few enc history entries!")
|
|
||||||
//}
|
|
||||||
encHistoryEntryCount, err := GLOBAL_DB.EncHistoryEntryCount(r.Context())
|
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
if encHistoryEntryCount < 1000 {
|
if encHistoryEntryCount < 1000 {
|
||||||
panic("Suspiciously few enc history entries!")
|
panic("Suspiciously few enc history entries!")
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceCount, err := GLOBAL_DB.DevicesCount(r.Context())
|
deviceCount, err := GLOBAL_DB.CountAllDevices(r.Context())
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
if deviceCount < 100 {
|
if deviceCount < 100 {
|
||||||
panic("Suspiciously few devices!")
|
panic("Suspiciously few devices!")
|
||||||
}
|
}
|
||||||
// Check that we can write to the DB. This entry will get written and then eventually cleaned by the cron.
|
// Check that we can write to the DB. This entry will get written and then eventually cleaned by the cron.
|
||||||
err = GLOBAL_DB.EncHistoryCreate(r.Context(), &shared.EncHistoryEntry{
|
err = GLOBAL_DB.AddHistoryEntries(r.Context(), &shared.EncHistoryEntry{
|
||||||
EncryptedData: []byte("data"),
|
EncryptedData: []byte("data"),
|
||||||
Nonce: []byte("nonce"),
|
Nonce: []byte("nonce"),
|
||||||
DeviceId: "healthcheck_device_id",
|
DeviceId: "healthcheck_device_id",
|
||||||
@ -432,7 +419,7 @@ func wipeDbEntriesHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
panic("refusing to wipe the DB non-test environment")
|
panic("refusing to wipe the DB non-test environment")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := GLOBAL_DB.EncHistoryClear(r.Context())
|
err := GLOBAL_DB.Unsafe_DeleteAllHistoryEntries(r.Context())
|
||||||
checkGormError(err, 0)
|
checkGormError(err, 0)
|
||||||
|
|
||||||
w.Header().Set("Content-Length", "0")
|
w.Header().Set("Content-Length", "0")
|
||||||
@ -468,7 +455,10 @@ func OpenDB() (*database.DB, error) {
|
|||||||
}
|
}
|
||||||
underlyingDb.SetMaxOpenConns(1)
|
underlyingDb.SetMaxOpenConns(1)
|
||||||
db.Exec("PRAGMA journal_mode = WAL")
|
db.Exec("PRAGMA journal_mode = WAL")
|
||||||
db.AddDatabaseTables()
|
err = db.AddDatabaseTables()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create underlying DB tables: %w", err)
|
||||||
|
}
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,7 +496,10 @@ func OpenDB() (*database.DB, error) {
|
|||||||
return nil, fmt.Errorf("failed to connect to the DB: %w", err)
|
return nil, fmt.Errorf("failed to connect to the DB: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
db.AddDatabaseTables()
|
err := db.AddDatabaseTables()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create underlying DB tables: %w", err)
|
||||||
|
}
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,3 +894,4 @@ func getMaximumNumberOfAllowedUsers() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(optimization): Maybe optimize the endpoints a bit to reduce the number of round trips required?
|
// TODO(optimization): Maybe optimize the endpoints a bit to reduce the number of round trips required?
|
||||||
|
// TODO: Add error checking for the calls to updateUsageData(...) that logs it/triggers an alert in prod, but is an error in test
|
||||||
|
@ -3,21 +3,11 @@ package database
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
"github.com/ddworken/hishtory/shared"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *DB) DevicesCountForUser(ctx context.Context, userID string) (int64, error) {
|
func (db *DB) CountAllDevices(ctx context.Context) (int64, error) {
|
||||||
var existingDevicesCount int64
|
|
||||||
tx := db.WithContext(ctx).Model(&shared.Device{}).Where("user_id = ?", userID).Count(&existingDevicesCount)
|
|
||||||
if tx.Error != nil {
|
|
||||||
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingDevicesCount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) DevicesCount(ctx context.Context) (int64, error) {
|
|
||||||
var numDevices int64 = 0
|
var numDevices int64 = 0
|
||||||
tx := db.WithContext(ctx).Model(&shared.Device{}).Count(&numDevices)
|
tx := db.WithContext(ctx).Model(&shared.Device{}).Count(&numDevices)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
@ -27,7 +17,17 @@ func (db *DB) DevicesCount(ctx context.Context) (int64, error) {
|
|||||||
return numDevices, nil
|
return numDevices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) DeviceCreate(ctx context.Context, device *shared.Device) error {
|
func (db *DB) CountDevicesForUser(ctx context.Context, userID string) (int64, error) {
|
||||||
|
var existingDevicesCount int64
|
||||||
|
tx := db.WithContext(ctx).Model(&shared.Device{}).Where("user_id = ?", userID).Count(&existingDevicesCount)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingDevicesCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) CreateDevice(ctx context.Context, device *shared.Device) error {
|
||||||
tx := db.WithContext(ctx).Create(device)
|
tx := db.WithContext(ctx).Create(device)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
return fmt.Errorf("tx.Error: %w", tx.Error)
|
return fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
@ -36,24 +36,6 @@ func (db *DB) DeviceCreate(ctx context.Context, device *shared.Device) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) DeviceEntriesCreateChunk(ctx context.Context, devices []*shared.Device, entries []*shared.EncHistoryEntry, chunkSize int) error {
|
|
||||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
||||||
for _, device := range devices {
|
|
||||||
for _, entry := range entries {
|
|
||||||
entry.DeviceId = device.DeviceId
|
|
||||||
}
|
|
||||||
// Chunk the inserts to prevent the `extended protocol limited to 65535 parameters` error
|
|
||||||
for _, entriesChunk := range shared.Chunks(entries, chunkSize) {
|
|
||||||
resp := tx.Create(&entriesChunk)
|
|
||||||
if resp.Error != nil {
|
|
||||||
return fmt.Errorf("resp.Error: %w", resp.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) DevicesForUser(ctx context.Context, userID string) ([]*shared.Device, error) {
|
func (db *DB) DevicesForUser(ctx context.Context, userID string) ([]*shared.Device, error) {
|
||||||
var devices []*shared.Device
|
var devices []*shared.Device
|
||||||
tx := db.WithContext(ctx).Where("user_id = ?", userID).Find(&devices)
|
tx := db.WithContext(ctx).Where("user_id = ?", userID).Find(&devices)
|
||||||
@ -63,7 +45,3 @@ func (db *DB) DevicesForUser(ctx context.Context, userID string) ([]*shared.Devi
|
|||||||
|
|
||||||
return devices, nil
|
return devices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) DeviceIncrementReadCounts(ctx context.Context, deviceID string) error {
|
|
||||||
return db.WithContext(ctx).Exec("UPDATE enc_history_entries SET read_count = read_count + 1 WHERE device_id = ?", deviceID).Error
|
|
||||||
}
|
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (db *DB) EncHistoryEntryCount(ctx context.Context) (int64, error) {
|
|
||||||
var numDbEntries int64
|
|
||||||
tx := db.WithContext(ctx).Model(&shared.EncHistoryEntry{}).Count(&numDbEntries)
|
|
||||||
if tx.Error != nil {
|
|
||||||
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return numDbEntries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) EncHistoryEntriesForUser(ctx context.Context, userID string) ([]*shared.EncHistoryEntry, error) {
|
|
||||||
var historyEntries []*shared.EncHistoryEntry
|
|
||||||
tx := db.WithContext(ctx).Where("user_id = ?", userID).Find(&historyEntries)
|
|
||||||
|
|
||||||
if tx.Error != nil {
|
|
||||||
return nil, fmt.Errorf("tx.Error: %w", tx.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return historyEntries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) EncHistoryEntriesForDevice(ctx context.Context, deviceID string, limit int) ([]*shared.EncHistoryEntry, error) {
|
|
||||||
var historyEntries []*shared.EncHistoryEntry
|
|
||||||
tx := db.WithContext(ctx).Where("device_id = ? AND read_count < ?", deviceID, limit).Find(&historyEntries)
|
|
||||||
|
|
||||||
if tx.Error != nil {
|
|
||||||
return nil, fmt.Errorf("tx.Error: %w", tx.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return historyEntries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) EncHistoryCreate(ctx context.Context, entry *shared.EncHistoryEntry) error {
|
|
||||||
tx := db.WithContext(ctx).Create(entry)
|
|
||||||
if tx.Error != nil {
|
|
||||||
return fmt.Errorf("tx.Error: %w", tx.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) EncHistoryCreateMulti(ctx context.Context, entries ...*shared.EncHistoryEntry) error {
|
|
||||||
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
||||||
for _, entry := range entries {
|
|
||||||
resp := tx.Create(&entry)
|
|
||||||
if resp.Error != nil {
|
|
||||||
return fmt.Errorf("resp.Error: %w", resp.Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) EncHistoryClear(ctx context.Context) error {
|
|
||||||
tx := db.WithContext(ctx).Exec("DELETE FROM enc_history_entries")
|
|
||||||
if tx.Error != nil {
|
|
||||||
return fmt.Errorf("tx.Error: %w", tx.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
85
internal/database/historyentries.go
Normal file
85
internal/database/historyentries.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ddworken/hishtory/shared"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (db *DB) CountHistoryEntries(ctx context.Context) (int64, error) {
|
||||||
|
var numDbEntries int64
|
||||||
|
tx := db.WithContext(ctx).Model(&shared.EncHistoryEntry{}).Count(&numDbEntries)
|
||||||
|
if tx.Error != nil {
|
||||||
|
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return numDbEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AllHistoryEntriesForUser(ctx context.Context, userID string) ([]*shared.EncHistoryEntry, error) {
|
||||||
|
var historyEntries []*shared.EncHistoryEntry
|
||||||
|
tx := db.WithContext(ctx).Where("user_id = ?", userID).Find(&historyEntries)
|
||||||
|
|
||||||
|
if tx.Error != nil {
|
||||||
|
return nil, fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return historyEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) HistoryEntriesForDevice(ctx context.Context, deviceID string, limit int) ([]*shared.EncHistoryEntry, error) {
|
||||||
|
var historyEntries []*shared.EncHistoryEntry
|
||||||
|
tx := db.WithContext(ctx).Where("device_id = ? AND read_count < ?", deviceID, limit).Find(&historyEntries)
|
||||||
|
|
||||||
|
if tx.Error != nil {
|
||||||
|
return nil, fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return historyEntries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AddHistoryEntries(ctx context.Context, entries ...*shared.EncHistoryEntry) error {
|
||||||
|
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
for _, entry := range entries {
|
||||||
|
resp := tx.Create(&entry)
|
||||||
|
if resp.Error != nil {
|
||||||
|
return fmt.Errorf("resp.Error: %w", resp.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AddHistoryEntriesForAllDevices(ctx context.Context, devices []*shared.Device, entries []*shared.EncHistoryEntry) error {
|
||||||
|
chunkSize := 1000
|
||||||
|
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||||
|
for _, device := range devices {
|
||||||
|
for _, entry := range entries {
|
||||||
|
entry.DeviceId = device.DeviceId
|
||||||
|
}
|
||||||
|
// Chunk the inserts to prevent the `extended protocol limited to 65535 parameters` error
|
||||||
|
for _, entriesChunk := range shared.Chunks(entries, chunkSize) {
|
||||||
|
resp := tx.Create(&entriesChunk)
|
||||||
|
if resp.Error != nil {
|
||||||
|
return fmt.Errorf("resp.Error: %w", resp.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Unsafe_DeleteAllHistoryEntries(ctx context.Context) error {
|
||||||
|
tx := db.WithContext(ctx).Exec("DELETE FROM enc_history_entries")
|
||||||
|
if tx.Error != nil {
|
||||||
|
return fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) IncrementEntryReadCountsForDevice(ctx context.Context, deviceID string) error {
|
||||||
|
return db.WithContext(ctx).Exec("UPDATE enc_history_entries SET read_count = read_count + 1 WHERE device_id = ?", deviceID).Error
|
||||||
|
}
|
@ -3,8 +3,9 @@ package database
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ddworken/hishtory/shared"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ddworken/hishtory/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *DB) UsageDataFindByUserAndDevice(ctx context.Context, userId, deviceId string) ([]shared.UsageData, error) {
|
func (db *DB) UsageDataFindByUserAndDevice(ctx context.Context, userId, deviceId string) ([]shared.UsageData, error) {
|
||||||
@ -22,7 +23,7 @@ func (db *DB) UsageDataFindByUserAndDevice(ctx context.Context, userId, deviceId
|
|||||||
return usageData, nil
|
return usageData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) UsageDataCreate(ctx context.Context, usageData *shared.UsageData) error {
|
func (db *DB) CreateUsageData(ctx context.Context, usageData *shared.UsageData) error {
|
||||||
tx := db.DB.WithContext(ctx).Create(usageData)
|
tx := db.DB.WithContext(ctx).Create(usageData)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
return fmt.Errorf("db.WithContext.Create: %w", tx.Error)
|
return fmt.Errorf("db.WithContext.Create: %w", tx.Error)
|
||||||
@ -31,8 +32,8 @@ func (db *DB) UsageDataCreate(ctx context.Context, usageData *shared.UsageData)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsageDataUpdate updates the entry for a given userID/deviceID pair with the lastUsed and lastIP values
|
// UpdateUsageData updates the entry for a given userID/deviceID pair with the lastUsed and lastIP values
|
||||||
func (db *DB) UsageDataUpdate(ctx context.Context, userId, deviceId string, lastUsed time.Time, lastIP string) error {
|
func (db *DB) UpdateUsageData(ctx context.Context, userId, deviceId string, lastUsed time.Time, lastIP string) error {
|
||||||
tx := db.DB.WithContext(ctx).Model(&shared.UsageData{}).
|
tx := db.DB.WithContext(ctx).Model(&shared.UsageData{}).
|
||||||
Where("user_id = ? AND device_id = ?", userId, deviceId).
|
Where("user_id = ? AND device_id = ?", userId, deviceId).
|
||||||
Update("last_used", lastUsed).
|
Update("last_used", lastUsed).
|
||||||
@ -45,7 +46,7 @@ func (db *DB) UsageDataUpdate(ctx context.Context, userId, deviceId string, last
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) UsageDataUpdateNumEntriesHandled(ctx context.Context, userId, deviceId string, numEntriesHandled int) error {
|
func (db *DB) UpdateUsageDataForNumEntriesHandled(ctx context.Context, userId, deviceId string, numEntriesHandled int) error {
|
||||||
tx := db.DB.WithContext(ctx).Exec("UPDATE usage_data SET num_entries_handled = COALESCE(num_entries_handled, 0) + ? WHERE user_id = ? AND device_id = ?", numEntriesHandled, userId, deviceId)
|
tx := db.DB.WithContext(ctx).Exec("UPDATE usage_data SET num_entries_handled = COALESCE(num_entries_handled, 0) + ? WHERE user_id = ? AND device_id = ?", numEntriesHandled, userId, deviceId)
|
||||||
|
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
@ -55,7 +56,7 @@ func (db *DB) UsageDataUpdateNumEntriesHandled(ctx context.Context, userId, devi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) UsageDataUpdateVersion(ctx context.Context, userID, deviceID string, version string) error {
|
func (db *DB) UpdateUsageDataClientVersion(ctx context.Context, userID, deviceID string, version string) error {
|
||||||
tx := db.DB.WithContext(ctx).Exec("UPDATE usage_data SET version = ? WHERE user_id = ? AND device_id = ?", version, userID, deviceID)
|
tx := db.DB.WithContext(ctx).Exec("UPDATE usage_data SET version = ? WHERE user_id = ? AND device_id = ?", version, userID, deviceID)
|
||||||
|
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
@ -65,7 +66,7 @@ func (db *DB) UsageDataUpdateVersion(ctx context.Context, userID, deviceID strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) UsageDataUpdateNumQueries(ctx context.Context, userID, deviceID string) error {
|
func (db *DB) UpdateUsageDataNumberQueries(ctx context.Context, userID, deviceID string) error {
|
||||||
tx := db.DB.WithContext(ctx).Exec("UPDATE usage_data SET num_queries = COALESCE(num_queries, 0) + 1, last_queried = ? WHERE user_id = ? AND device_id = ?", time.Now(), userID, deviceID)
|
tx := db.DB.WithContext(ctx).Exec("UPDATE usage_data SET num_queries = COALESCE(num_queries, 0) + 1, last_queried = ? WHERE user_id = ? AND device_id = ?", time.Now(), userID, deviceID)
|
||||||
|
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
@ -148,27 +149,27 @@ func (db *DB) UsageDataTotal(ctx context.Context) (int64, error) {
|
|||||||
return int64(nep.Total), nil
|
return int64(nep.Total), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) WeeklyActiveInstalls(ctx context.Context, since time.Duration) (int64, error) {
|
func (db *DB) CountActiveInstalls(ctx context.Context, since time.Duration) (int64, error) {
|
||||||
var weeklyActiveInstalls int64
|
var activeInstalls int64
|
||||||
tx := db.WithContext(ctx).Model(&shared.UsageData{}).Where("last_used > ?", time.Now().Add(-since)).Count(&weeklyActiveInstalls)
|
tx := db.WithContext(ctx).Model(&shared.UsageData{}).Where("last_used > ?", time.Now().Add(-since)).Count(&activeInstalls)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return weeklyActiveInstalls, nil
|
return activeInstalls, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) WeeklyQueryUsers(ctx context.Context, since time.Duration) (int64, error) {
|
func (db *DB) CountQueryUsers(ctx context.Context, since time.Duration) (int64, error) {
|
||||||
var weeklyQueryUsers int64
|
var activeQueryUsers int64
|
||||||
tx := db.WithContext(ctx).Model(&shared.UsageData{}).Where("last_queried > ?", time.Now().Add(-since)).Count(&weeklyQueryUsers)
|
tx := db.WithContext(ctx).Model(&shared.UsageData{}).Where("last_queried > ?", time.Now().Add(-since)).Count(&activeQueryUsers)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
return 0, fmt.Errorf("tx.Error: %w", tx.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return weeklyQueryUsers, nil
|
return activeQueryUsers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) LastRegistration(ctx context.Context) (string, error) {
|
func (db *DB) DateOfLastRegistration(ctx context.Context) (string, error) {
|
||||||
var lastRegistration string
|
var lastRegistration string
|
||||||
row := db.WithContext(ctx).Raw("SELECT to_char(max(registration_date), 'DD Month YYYY HH24:MI') FROM devices").Row()
|
row := db.WithContext(ctx).Raw("SELECT to_char(max(registration_date), 'DD Month YYYY HH24:MI') FROM devices").Row()
|
||||||
if err := row.Scan(&lastRegistration); err != nil {
|
if err := row.Scan(&lastRegistration); err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user