From 8a6719fc19a744eb11bd3496f8161004323d1998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Thu, 27 Jul 2023 17:44:06 +0200 Subject: [PATCH] Remove message.owner_user_id field and implement db migrations --- scnserver/api/handler/common.go | 11 +- scnserver/api/handler/compat.go | 4 +- scnserver/cmd/dbhash/main.go | 7 + scnserver/db/{impl/primary => }/context.go | 5 +- scnserver/db/database.go | 20 +- scnserver/db/impl/logs/database.go | 85 ++-- scnserver/db/impl/logs/meta.go | 93 ++++- scnserver/db/impl/primary/channels.go | 23 +- scnserver/db/impl/primary/clients.go | 19 +- scnserver/db/impl/primary/compat.go | 15 +- scnserver/db/impl/primary/database.go | 133 ++++-- scnserver/db/impl/primary/deliveries.go | 15 +- scnserver/db/impl/primary/keytokens.go | 23 +- scnserver/db/impl/primary/messages.go | 14 +- scnserver/db/impl/primary/meta.go | 93 ++++- scnserver/db/impl/primary/subscriptions.go | 17 +- scnserver/db/impl/primary/users.go | 15 +- scnserver/db/impl/requests/database.go | 82 ++-- scnserver/db/impl/requests/meta.go | 93 ++++- scnserver/db/schema/assets.go | 51 ++- scnserver/db/schema/primary_4.ddl | 233 +++++++++++ scnserver/db/schema/primary_migration_3_4.ddl | 20 + .../{logic => db/simplectx}/simplecontext.go | 6 +- scnserver/go.mod | 23 +- scnserver/go.sum | 32 ++ scnserver/jobs/DeliveryRetryJob.go | 3 +- scnserver/logic/application.go | 5 +- scnserver/logic/permissions.go | 2 +- scnserver/models/enums_gen.go | 50 +-- scnserver/models/message.go | 8 +- scnserver/models/messagefilter.go | 10 - scnserver/swagger/swagger.json | 5 - scnserver/swagger/swagger.yaml | 4 - scnserver/test/database_test.go | 388 ++++++++++++++++++ scnserver/test/util/common.go | 4 + scnserver/test/util/webserver.go | 127 ++++-- 36 files changed, 1381 insertions(+), 357 deletions(-) rename scnserver/db/{impl/primary => }/context.go (61%) create mode 100644 scnserver/db/schema/primary_4.ddl create mode 100644 scnserver/db/schema/primary_migration_3_4.ddl rename scnserver/{logic => db/simplectx}/simplecontext.go (96%) create mode 100644 scnserver/test/database_test.go diff --git a/scnserver/api/handler/common.go b/scnserver/api/handler/common.go index cc6258f..a069d89 100644 --- a/scnserver/api/handler/common.go +++ b/scnserver/api/handler/common.go @@ -3,6 +3,7 @@ package handler import ( "blackforestbytes.com/simplecloudnotifier/api/apierr" "blackforestbytes.com/simplecloudnotifier/api/ginresp" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "blackforestbytes.com/simplecloudnotifier/logic" "bytes" "context" @@ -127,7 +128,9 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse { return ginresp.InternalError(errors.New("sqlite version too low")) } - err := h.app.Database.Ping(ctx) + tctx := simplectx.CreateSimpleContext(ctx, nil) + + err := h.app.Database.Ping(tctx) if err != nil { return ginresp.InternalError(err) } @@ -137,12 +140,12 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse { uuidKey, _ := langext.NewHexUUID() uuidWrite, _ := langext.NewHexUUID() - err = subdb.WriteMetaString(ctx, uuidKey, uuidWrite) + err = subdb.WriteMetaString(tctx, uuidKey, uuidWrite) if err != nil { return ginresp.InternalError(err) } - uuidRead, err := subdb.ReadMetaString(ctx, uuidKey) + uuidRead, err := subdb.ReadMetaString(tctx, uuidKey) if err != nil { return ginresp.InternalError(err) } @@ -151,7 +154,7 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse { return ginresp.InternalError(errors.New("writing into DB was not consistent")) } - err = subdb.DeleteMeta(ctx, uuidKey) + err = subdb.DeleteMeta(tctx, uuidKey) if err != nil { return ginresp.InternalError(err) } diff --git a/scnserver/api/handler/compat.go b/scnserver/api/handler/compat.go index 08dfd7e..d619030 100644 --- a/scnserver/api/handler/compat.go +++ b/scnserver/api/handler/compat.go @@ -311,7 +311,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse { } filter := models.MessageFilter{ - Owner: langext.Ptr([]models.UserID{user.UserID}), + Sender: langext.Ptr([]models.UserID{user.UserID}), CompatAcknowledged: langext.Ptr(false), } @@ -516,7 +516,7 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse { } filter := models.MessageFilter{ - Owner: langext.Ptr([]models.UserID{user.UserID}), + Sender: langext.Ptr([]models.UserID{user.UserID}), CompatAcknowledged: langext.Ptr(false), } diff --git a/scnserver/cmd/dbhash/main.go b/scnserver/cmd/dbhash/main.go index b460bca..a9afa1f 100644 --- a/scnserver/cmd/dbhash/main.go +++ b/scnserver/cmd/dbhash/main.go @@ -36,6 +36,13 @@ func main() { } fmt.Printf("PrimarySchema3 := %s\n", h0) } + { + h0, err := sq.HashSqliteSchema(ctx, schema.PrimarySchema4) + if err != nil { + h0 = "ERR" + } + fmt.Printf("PrimarySchema4 := %s\n", h0) + } { h0, err := sq.HashSqliteSchema(ctx, schema.RequestsSchema1) if err != nil { diff --git a/scnserver/db/impl/primary/context.go b/scnserver/db/context.go similarity index 61% rename from scnserver/db/impl/primary/context.go rename to scnserver/db/context.go index 8665415..a6e6e50 100644 --- a/scnserver/db/impl/primary/context.go +++ b/scnserver/db/context.go @@ -1,7 +1,6 @@ -package primary +package db import ( - "blackforestbytes.com/simplecloudnotifier/db" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) @@ -12,5 +11,5 @@ type TxContext interface { Err() error Value(key any) any - GetOrCreateTransaction(db db.DatabaseImpl) (sq.Tx, error) + GetOrCreateTransaction(db DatabaseImpl) (sq.Tx, error) } diff --git a/scnserver/db/database.go b/scnserver/db/database.go index 6053078..77cdc5a 100644 --- a/scnserver/db/database.go +++ b/scnserver/db/database.go @@ -13,17 +13,17 @@ type DatabaseImpl interface { BeginTx(ctx context.Context) (sq.Tx, error) Stop(ctx context.Context) error - ReadSchema(ctx context.Context) (int, error) + ReadSchema(ctx TxContext) (int, error) - WriteMetaString(ctx context.Context, key string, value string) error - WriteMetaInt(ctx context.Context, key string, value int64) error - WriteMetaReal(ctx context.Context, key string, value float64) error - WriteMetaBlob(ctx context.Context, key string, value []byte) error + WriteMetaString(ctx TxContext, key string, value string) error + WriteMetaInt(ctx TxContext, key string, value int64) error + WriteMetaReal(ctx TxContext, key string, value float64) error + WriteMetaBlob(ctx TxContext, key string, value []byte) error - ReadMetaString(ctx context.Context, key string) (*string, error) - ReadMetaInt(ctx context.Context, key string) (*int64, error) - ReadMetaReal(ctx context.Context, key string) (*float64, error) - ReadMetaBlob(ctx context.Context, key string) (*[]byte, error) + ReadMetaString(ctx TxContext, key string) (*string, error) + ReadMetaInt(ctx TxContext, key string) (*int64, error) + ReadMetaReal(ctx TxContext, key string) (*float64, error) + ReadMetaBlob(ctx TxContext, key string) (*[]byte, error) - DeleteMeta(ctx context.Context, key string) error + DeleteMeta(ctx TxContext, key string) error } diff --git a/scnserver/db/impl/logs/database.go b/scnserver/db/impl/logs/database.go index 700e214..f99e6b0 100644 --- a/scnserver/db/impl/logs/database.go +++ b/scnserver/db/impl/logs/database.go @@ -4,6 +4,7 @@ import ( server "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/db/dbtools" "blackforestbytes.com/simplecloudnotifier/db/schema" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "context" "database/sql" "errors" @@ -63,77 +64,93 @@ func (db *Database) DB() sq.DB { return db.db } -func (db *Database) Migrate(ctx context.Context) error { - ctx, cancel := context.WithTimeout(context.Background(), 24*time.Second) - defer cancel() +func (db *Database) Migrate(outerctx context.Context) error { + innerctx, cancel := context.WithTimeout(outerctx, 24*time.Second) + tctx := simplectx.CreateSimpleContext(innerctx, cancel) - currschema, err := db.ReadSchema(ctx) + tx, err := tctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + defer func() { + if tx.Status() == sq.TxStatusInitial || tx.Status() == sq.TxStatusActive { + err = tx.Rollback() + if err != nil { + log.Err(err).Msg("failed to rollback transaction") + } + } + }() + + ppReInit := false + + currschema, err := db.ReadSchema(tctx) if err != nil { return err } if currschema == 0 { + schemastr := schema.LogsSchema[schema.LogsSchemaVersion].SQL + schemahash := schema.LogsSchema[schema.LogsSchemaVersion].Hash - schemastr := schema.LogsSchema1 - - schemahash, err := sq.HashSqliteSchema(ctx, schemastr) + _, err = tx.Exec(tctx, schemastr, sq.PP{}) if err != nil { return err } - _, err = db.db.Exec(ctx, schemastr, sq.PP{}) + err = db.WriteMetaInt(tctx, "schema", int64(schema.LogsSchemaVersion)) if err != nil { return err } - err = db.WriteMetaInt(ctx, "schema", 1) + err = db.WriteMetaString(tctx, "schema_hash", schemahash) if err != nil { return err } - err = db.WriteMetaString(ctx, "schema_hash", schemahash) + ppReInit = true + + currschema = schema.LogsSchemaVersion + } + + if currschema == 1 { + schemHashDB, err := sq.HashSqliteDatabase(tctx, tx) if err != nil { return err } - err = db.pp.Init(ctx) // Re-Init + schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash") if err != nil { return err } - return nil - - } else if currschema == 1 { - - schemHashDB, err := sq.HashSqliteDatabase(ctx, db.db) - if err != nil { - return err - } - - schemaHashMeta, err := db.ReadMetaString(ctx, "schema_hash") - if err != nil { - return err - } - - schemHashAsset := schema.LogsHash1 - if err != nil { - return err - } - - if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schemHashAsset { + if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.LogsSchema[currschema].Hash { log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (logs db)") log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).Msg("Schema (logs db)") - log.Debug().Str("schemHashAsset", schemHashAsset).Msg("Schema (logs db)") + log.Debug().Str("schemaHashAsset", schema.LogsSchema[currschema].Hash).Msg("Schema (logs db)") return errors.New("database schema does not match (logs db)") } else { log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (logs db)") } + } - return nil // current - } else { + if currschema != schema.LogsSchemaVersion { return errors.New(fmt.Sprintf("Unknown DB schema: %d", currschema)) } + err = tx.Commit() + if err != nil { + return err + } + + if ppReInit { + log.Debug().Msg("Re-Init preprocessor") + err = db.pp.Init(outerctx) // Re-Init + if err != nil { + return err + } + } + + return nil } func (db *Database) Ping(ctx context.Context) error { diff --git a/scnserver/db/impl/logs/meta.go b/scnserver/db/impl/logs/meta.go index 958a7d7..2c0cccc 100644 --- a/scnserver/db/impl/logs/meta.go +++ b/scnserver/db/impl/logs/meta.go @@ -1,15 +1,19 @@ package logs import ( - "context" + "blackforestbytes.com/simplecloudnotifier/db" "errors" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" ) -func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { +func (db *Database) ReadSchema(ctx db.TxContext) (retval int, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return 0, err + } - r1, err := db.db.Query(ctx, "SELECT name FROM sqlite_master WHERE type = :typ AND name = :name", sq.PP{"typ": "table", "name": "meta"}) + r1, err := tx.Query(ctx, "SELECT name FROM sqlite_master WHERE type = :typ AND name = :name", sq.PP{"typ": "table", "name": "meta"}) if err != nil { return 0, err } @@ -31,7 +35,7 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { return 0, err } - r2, err := db.db.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": "schema"}) + r2, err := tx.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": "schema"}) if err != nil { return 0, err } @@ -62,8 +66,13 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { return dbschema, nil } -func (db *Database) WriteMetaString(ctx context.Context, key string, value string) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ +func (db *Database) WriteMetaString(ctx db.TxContext, key string, value string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ "key": key, "val": value, }) @@ -73,8 +82,13 @@ func (db *Database) WriteMetaString(ctx context.Context, key string, value strin return nil } -func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ +func (db *Database) WriteMetaInt(ctx db.TxContext, key string, value int64) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ "key": key, "val": value, }) @@ -84,8 +98,13 @@ func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) e return nil } -func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_real) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_real = :val", sq.PP{ +func (db *Database) WriteMetaReal(ctx db.TxContext, key string, value float64) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_real) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_real = :val", sq.PP{ "key": key, "val": value, }) @@ -95,8 +114,13 @@ func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64 return nil } -func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_blob) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_blob = :val", sq.PP{ +func (db *Database) WriteMetaBlob(ctx db.TxContext, key string, value []byte) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_blob) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_blob = :val", sq.PP{ "key": key, "val": value, }) @@ -106,8 +130,13 @@ func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) return nil } -func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *string, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_txt FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaString(ctx db.TxContext, key string) (retval *string, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_txt FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -137,8 +166,13 @@ func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *str return langext.Ptr(value), nil } -func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaInt(ctx db.TxContext, key string) (retval *int64, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -169,8 +203,13 @@ func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, return langext.Ptr(value), nil } -func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float64, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_real FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaReal(ctx db.TxContext, key string) (retval *float64, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_real FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -201,8 +240,13 @@ func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float return langext.Ptr(value), nil } -func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byte, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_blob FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaBlob(ctx db.TxContext, key string) (retval *[]byte, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_blob FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -233,8 +277,13 @@ func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byt return langext.Ptr(value), nil } -func (db *Database) DeleteMeta(ctx context.Context, key string) error { - _, err := db.db.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) DeleteMeta(ctx db.TxContext, key string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return err } diff --git a/scnserver/db/impl/primary/channels.go b/scnserver/db/impl/primary/channels.go index c8131ad..80c54c5 100644 --- a/scnserver/db/impl/primary/channels.go +++ b/scnserver/db/impl/primary/channels.go @@ -1,13 +1,14 @@ package primary import ( + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) -func (db *Database) GetChannelByName(ctx TxContext, userid models.UserID, chanName string) (*models.Channel, error) { +func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, chanName string) (*models.Channel, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -32,7 +33,7 @@ func (db *Database) GetChannelByName(ctx TxContext, userid models.UserID, chanNa return &channel, nil } -func (db *Database) GetChannelByID(ctx TxContext, chanid models.ChannelID) (*models.Channel, error) { +func (db *Database) GetChannelByID(ctx db.TxContext, chanid models.ChannelID) (*models.Channel, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -56,7 +57,7 @@ func (db *Database) GetChannelByID(ctx TxContext, chanid models.ChannelID) (*mod return &channel, nil } -func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName string, intName string, subscribeKey string) (models.Channel, error) { +func (db *Database) CreateChannel(ctx db.TxContext, userid models.UserID, dispName string, intName string, subscribeKey string) (models.Channel, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Channel{}, err @@ -81,7 +82,7 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName return entity.Model(), nil } -func (db *Database) ListChannelsByOwner(ctx TxContext, userid models.UserID, subUserID models.UserID) ([]models.ChannelWithSubscription, error) { +func (db *Database) ListChannelsByOwner(ctx db.TxContext, userid models.UserID, subUserID models.UserID) ([]models.ChannelWithSubscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -105,7 +106,7 @@ func (db *Database) ListChannelsByOwner(ctx TxContext, userid models.UserID, sub return data, nil } -func (db *Database) ListChannelsBySubscriber(ctx TxContext, userid models.UserID, confirmed *bool) ([]models.ChannelWithSubscription, error) { +func (db *Database) ListChannelsBySubscriber(ctx db.TxContext, userid models.UserID, confirmed *bool) ([]models.ChannelWithSubscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -135,7 +136,7 @@ func (db *Database) ListChannelsBySubscriber(ctx TxContext, userid models.UserID return data, nil } -func (db *Database) ListChannelsByAccess(ctx TxContext, userid models.UserID, confirmed *bool) ([]models.ChannelWithSubscription, error) { +func (db *Database) ListChannelsByAccess(ctx db.TxContext, userid models.UserID, confirmed *bool) ([]models.ChannelWithSubscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -166,7 +167,7 @@ func (db *Database) ListChannelsByAccess(ctx TxContext, userid models.UserID, co return data, nil } -func (db *Database) GetChannel(ctx TxContext, userid models.UserID, channelid models.ChannelID, enforceOwner bool) (models.ChannelWithSubscription, error) { +func (db *Database) GetChannel(ctx db.TxContext, userid models.UserID, channelid models.ChannelID, enforceOwner bool) (models.ChannelWithSubscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.ChannelWithSubscription{}, err @@ -200,7 +201,7 @@ func (db *Database) GetChannel(ctx TxContext, userid models.UserID, channelid mo return channel, nil } -func (db *Database) IncChannelMessageCounter(ctx TxContext, channel *models.Channel) error { +func (db *Database) IncChannelMessageCounter(ctx db.TxContext, channel *models.Channel) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -222,7 +223,7 @@ func (db *Database) IncChannelMessageCounter(ctx TxContext, channel *models.Chan return nil } -func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.ChannelID, newkey string) error { +func (db *Database) UpdateChannelSubscribeKey(ctx db.TxContext, channelid models.ChannelID, newkey string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -239,7 +240,7 @@ func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.Ch return nil } -func (db *Database) UpdateChannelDisplayName(ctx TxContext, channelid models.ChannelID, dispname string) error { +func (db *Database) UpdateChannelDisplayName(ctx db.TxContext, channelid models.ChannelID, dispname string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -256,7 +257,7 @@ func (db *Database) UpdateChannelDisplayName(ctx TxContext, channelid models.Cha return nil } -func (db *Database) UpdateChannelDescriptionName(ctx TxContext, channelid models.ChannelID, descname *string) error { +func (db *Database) UpdateChannelDescriptionName(ctx db.TxContext, channelid models.ChannelID, descname *string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err diff --git a/scnserver/db/impl/primary/clients.go b/scnserver/db/impl/primary/clients.go index 00f2246..c300cc4 100644 --- a/scnserver/db/impl/primary/clients.go +++ b/scnserver/db/impl/primary/clients.go @@ -1,12 +1,13 @@ package primary import ( + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) -func (db *Database) CreateClient(ctx TxContext, userid models.UserID, ctype models.ClientType, fcmToken string, agentModel string, agentVersion string) (models.Client, error) { +func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype models.ClientType, fcmToken string, agentModel string, agentVersion string) (models.Client, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Client{}, err @@ -30,7 +31,7 @@ func (db *Database) CreateClient(ctx TxContext, userid models.UserID, ctype mode return entity.Model(), nil } -func (db *Database) ClearFCMTokens(ctx TxContext, fcmtoken string) error { +func (db *Database) ClearFCMTokens(ctx db.TxContext, fcmtoken string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -44,7 +45,7 @@ func (db *Database) ClearFCMTokens(ctx TxContext, fcmtoken string) error { return nil } -func (db *Database) ListClients(ctx TxContext, userid models.UserID) ([]models.Client, error) { +func (db *Database) ListClients(ctx db.TxContext, userid models.UserID) ([]models.Client, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -63,7 +64,7 @@ func (db *Database) ListClients(ctx TxContext, userid models.UserID) ([]models.C return data, nil } -func (db *Database) GetClient(ctx TxContext, userid models.UserID, clientid models.ClientID) (models.Client, error) { +func (db *Database) GetClient(ctx db.TxContext, userid models.UserID, clientid models.ClientID) (models.Client, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Client{}, err @@ -85,7 +86,7 @@ func (db *Database) GetClient(ctx TxContext, userid models.UserID, clientid mode return client, nil } -func (db *Database) DeleteClient(ctx TxContext, clientid models.ClientID) error { +func (db *Database) DeleteClient(ctx db.TxContext, clientid models.ClientID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -99,7 +100,7 @@ func (db *Database) DeleteClient(ctx TxContext, clientid models.ClientID) error return nil } -func (db *Database) DeleteClientsByFCM(ctx TxContext, fcmtoken string) error { +func (db *Database) DeleteClientsByFCM(ctx db.TxContext, fcmtoken string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -113,7 +114,7 @@ func (db *Database) DeleteClientsByFCM(ctx TxContext, fcmtoken string) error { return nil } -func (db *Database) UpdateClientFCMToken(ctx TxContext, clientid models.ClientID, fcmtoken string) error { +func (db *Database) UpdateClientFCMToken(ctx db.TxContext, clientid models.ClientID, fcmtoken string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -130,7 +131,7 @@ func (db *Database) UpdateClientFCMToken(ctx TxContext, clientid models.ClientID return nil } -func (db *Database) UpdateClientAgentModel(ctx TxContext, clientid models.ClientID, agentModel string) error { +func (db *Database) UpdateClientAgentModel(ctx db.TxContext, clientid models.ClientID, agentModel string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -147,7 +148,7 @@ func (db *Database) UpdateClientAgentModel(ctx TxContext, clientid models.Client return nil } -func (db *Database) UpdateClientAgentVersion(ctx TxContext, clientid models.ClientID, agentVersion string) error { +func (db *Database) UpdateClientAgentVersion(ctx db.TxContext, clientid models.ClientID, agentVersion string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err diff --git a/scnserver/db/impl/primary/compat.go b/scnserver/db/impl/primary/compat.go index 9357c0f..519f591 100644 --- a/scnserver/db/impl/primary/compat.go +++ b/scnserver/db/impl/primary/compat.go @@ -1,13 +1,14 @@ package primary import ( + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "errors" "gogs.mikescher.com/BlackForestBytes/goext/sq" ) -func (db *Database) CreateCompatID(ctx TxContext, idtype string, newid string) (int64, error) { +func (db *Database) CreateCompatID(ctx db.TxContext, idtype string, newid string) (int64, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return 0, err @@ -42,7 +43,7 @@ func (db *Database) CreateCompatID(ctx TxContext, idtype string, newid string) ( return oldid, nil } -func (db *Database) ConvertCompatID(ctx TxContext, oldid int64, idtype string) (*string, error) { +func (db *Database) ConvertCompatID(ctx db.TxContext, oldid int64, idtype string) (*string, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -72,7 +73,7 @@ func (db *Database) ConvertCompatID(ctx TxContext, oldid int64, idtype string) ( return &newid, nil } -func (db *Database) ConvertToCompatID(ctx TxContext, newid string) (*int64, *string, error) { +func (db *Database) ConvertToCompatID(ctx db.TxContext, newid string) (*int64, *string, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, nil, err @@ -100,7 +101,7 @@ func (db *Database) ConvertToCompatID(ctx TxContext, newid string) (*int64, *str return &oldid, &idtype, nil } -func (db *Database) ConvertToCompatIDOrCreate(ctx TxContext, idtype string, newid string) (int64, error) { +func (db *Database) ConvertToCompatIDOrCreate(ctx db.TxContext, idtype string, newid string) (int64, error) { id1, _, err := db.ConvertToCompatID(ctx, newid) if err != nil { return 0, err @@ -116,7 +117,7 @@ func (db *Database) ConvertToCompatIDOrCreate(ctx TxContext, idtype string, newi return id2, nil } -func (db *Database) GetAck(ctx TxContext, msgid models.MessageID) (bool, error) { +func (db *Database) GetAck(ctx db.TxContext, msgid models.MessageID) (bool, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return false, err @@ -139,7 +140,7 @@ func (db *Database) GetAck(ctx TxContext, msgid models.MessageID) (bool, error) return res, nil } -func (db *Database) SetAck(ctx TxContext, userid models.UserID, msgid models.MessageID) error { +func (db *Database) SetAck(ctx db.TxContext, userid models.UserID, msgid models.MessageID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -156,7 +157,7 @@ func (db *Database) SetAck(ctx TxContext, userid models.UserID, msgid models.Mes return nil } -func (db *Database) IsCompatClient(ctx TxContext, clientid models.ClientID) (bool, error) { +func (db *Database) IsCompatClient(ctx db.TxContext, clientid models.ClientID) (bool, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return false, err diff --git a/scnserver/db/impl/primary/database.go b/scnserver/db/impl/primary/database.go index 1978d45..c7c0ffa 100644 --- a/scnserver/db/impl/primary/database.go +++ b/scnserver/db/impl/primary/database.go @@ -4,6 +4,7 @@ import ( server "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/db/dbtools" "blackforestbytes.com/simplecloudnotifier/db/schema" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "context" "database/sql" "errors" @@ -63,81 +64,147 @@ func (db *Database) DB() sq.DB { return db.db } -func (db *Database) Migrate(ctx context.Context) error { - ctx, cancel := context.WithTimeout(context.Background(), 24*time.Second) - defer cancel() +func (db *Database) Migrate(outerctx context.Context) error { + innerctx, cancel := context.WithTimeout(outerctx, 24*time.Second) + tctx := simplectx.CreateSimpleContext(innerctx, cancel) - currschema, err := db.ReadSchema(ctx) + tx, err := tctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + defer func() { + if tx.Status() == sq.TxStatusInitial || tx.Status() == sq.TxStatusActive { + err = tx.Rollback() + if err != nil { + log.Err(err).Msg("failed to rollback transaction") + } + } + }() + + ppReInit := false + + currschema, err := db.ReadSchema(tctx) if err != nil { return err } if currschema == 0 { + schemastr := schema.PrimarySchema[schema.PrimarySchemaVersion].SQL + schemahash := schema.PrimarySchema[schema.PrimarySchemaVersion].Hash - schemastr := schema.PrimarySchema3 - - schemahash, err := sq.HashSqliteSchema(ctx, schemastr) + _, err = tx.Exec(tctx, schemastr, sq.PP{}) if err != nil { return err } - _, err = db.db.Exec(ctx, schemastr, sq.PP{}) + err = db.WriteMetaInt(tctx, "schema", int64(schema.PrimarySchemaVersion)) if err != nil { return err } - err = db.WriteMetaInt(ctx, "schema", 3) + err = db.WriteMetaString(tctx, "schema_hash", schemahash) if err != nil { return err } - err = db.WriteMetaString(ctx, "schema_hash", schemahash) - if err != nil { - return err - } + ppReInit = true - err = db.pp.Init(ctx) // Re-Init - if err != nil { - return err - } + currschema = schema.PrimarySchemaVersion + } - return nil - - } else if currschema == 1 { + if currschema == 1 { return errors.New("cannot autom. upgrade schema 1") - } else if currschema == 2 { + } + + if currschema == 2 { return errors.New("cannot autom. upgrade schema 2") - } else if currschema == 3 { + } - schemHashDB, err := sq.HashSqliteDatabase(ctx, db.db) + if currschema == 3 { + + schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash") if err != nil { return err } - schemaHashMeta, err := db.ReadMetaString(ctx, "schema_hash") + schemHashDB, err := sq.HashSqliteDatabase(tctx, tx) if err != nil { return err } - schemHashAsset := schema.PrimaryHash3 - if err != nil { - return err - } - - if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schemHashAsset { + if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.PrimarySchema[currschema].Hash { log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (primary db)") log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).Msg("Schema (primary db)") - log.Debug().Str("schemHashAsset", schemHashAsset).Msg("Schema (primary db)") + log.Debug().Str("schemaHashAsset", schema.PrimarySchema[currschema].Hash).Msg("Schema (primary db)") return errors.New("database schema does not match (primary db)") } else { log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)") } - return nil // current - } else { + log.Info().Int("currschema", currschema).Msg("Upgrade schema from 3 -> 4") + + _, err = tx.Exec(tctx, schema.PrimaryMigration_3_4, sq.PP{}) + if err != nil { + return err + } + + currschema = 4 + + err = db.WriteMetaInt(tctx, "schema", int64(currschema)) + if err != nil { + return err + } + + err = db.WriteMetaString(tctx, "schema_hash", schema.PrimarySchema[currschema].Hash) + if err != nil { + return err + } + + log.Info().Int("currschema", currschema).Msg("Upgrade schema from 3 -> 4 succesfuly") + + ppReInit = true + } + + if currschema == 4 { + + schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash") + if err != nil { + return err + } + + schemHashDB, err := sq.HashSqliteDatabase(tctx, tx) + if err != nil { + return err + } + + if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.PrimarySchema[currschema].Hash { + log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (primary db)") + log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).Msg("Schema (primary db)") + log.Debug().Str("schemaHashAsset", schema.PrimarySchema[currschema].Hash).Msg("Schema (primary db)") + return errors.New("database schema does not match (primary db)") + } else { + log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)") + } + } + + if currschema != schema.PrimarySchemaVersion { return errors.New(fmt.Sprintf("Unknown DB schema: %d", currschema)) } + err = tx.Commit() + if err != nil { + return err + } + + if ppReInit { + log.Debug().Msg("Re-Init preprocessor") + err = db.pp.Init(outerctx) // Re-Init + if err != nil { + return err + } + } + + return nil } func (db *Database) Ping(ctx context.Context) error { diff --git a/scnserver/db/impl/primary/deliveries.go b/scnserver/db/impl/primary/deliveries.go index 66ff222..120f8f0 100644 --- a/scnserver/db/impl/primary/deliveries.go +++ b/scnserver/db/impl/primary/deliveries.go @@ -2,13 +2,14 @@ package primary import ( scn "blackforestbytes.com/simplecloudnotifier" + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) -func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg models.Message) (models.Delivery, error) { +func (db *Database) CreateRetryDelivery(ctx db.TxContext, client models.Client, msg models.Message) (models.Delivery, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Delivery{}, err @@ -38,7 +39,7 @@ func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg return entity.Model(), nil } -func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, msg models.Message, fcmDelivID string) (models.Delivery, error) { +func (db *Database) CreateSuccessDelivery(ctx db.TxContext, client models.Client, msg models.Message, fcmDelivID string) (models.Delivery, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Delivery{}, err @@ -67,7 +68,7 @@ func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, m return entity.Model(), nil } -func (db *Database) ListRetrieableDeliveries(ctx TxContext, pageSize int) ([]models.Delivery, error) { +func (db *Database) ListRetrieableDeliveries(ctx db.TxContext, pageSize int) ([]models.Delivery, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -89,7 +90,7 @@ func (db *Database) ListRetrieableDeliveries(ctx TxContext, pageSize int) ([]mod return data, nil } -func (db *Database) SetDeliverySuccess(ctx TxContext, delivery models.Delivery, fcmDelivID string) error { +func (db *Database) SetDeliverySuccess(ctx db.TxContext, delivery models.Delivery, fcmDelivID string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -108,7 +109,7 @@ func (db *Database) SetDeliverySuccess(ctx TxContext, delivery models.Delivery, return nil } -func (db *Database) SetDeliveryFailed(ctx TxContext, delivery models.Delivery) error { +func (db *Database) SetDeliveryFailed(ctx db.TxContext, delivery models.Delivery) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -127,7 +128,7 @@ func (db *Database) SetDeliveryFailed(ctx TxContext, delivery models.Delivery) e return nil } -func (db *Database) SetDeliveryRetry(ctx TxContext, delivery models.Delivery) error { +func (db *Database) SetDeliveryRetry(ctx db.TxContext, delivery models.Delivery) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -145,7 +146,7 @@ func (db *Database) SetDeliveryRetry(ctx TxContext, delivery models.Delivery) er return nil } -func (db *Database) CancelPendingDeliveries(ctx TxContext, messageID models.MessageID) error { +func (db *Database) CancelPendingDeliveries(ctx db.TxContext, messageID models.MessageID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err diff --git a/scnserver/db/impl/primary/keytokens.go b/scnserver/db/impl/primary/keytokens.go index 3676fac..beb5c47 100644 --- a/scnserver/db/impl/primary/keytokens.go +++ b/scnserver/db/impl/primary/keytokens.go @@ -1,6 +1,7 @@ package primary import ( + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "gogs.mikescher.com/BlackForestBytes/goext/langext" @@ -9,7 +10,7 @@ import ( "time" ) -func (db *Database) CreateKeyToken(ctx TxContext, name string, owner models.UserID, allChannels bool, channels []models.ChannelID, permissions models.TokenPermissionList, token string) (models.KeyToken, error) { +func (db *Database) CreateKeyToken(ctx db.TxContext, name string, owner models.UserID, allChannels bool, channels []models.ChannelID, permissions models.TokenPermissionList, token string) (models.KeyToken, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.KeyToken{}, err @@ -36,7 +37,7 @@ func (db *Database) CreateKeyToken(ctx TxContext, name string, owner models.User return entity.Model(), nil } -func (db *Database) ListKeyTokens(ctx TxContext, ownerID models.UserID) ([]models.KeyToken, error) { +func (db *Database) ListKeyTokens(ctx db.TxContext, ownerID models.UserID) ([]models.KeyToken, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -55,7 +56,7 @@ func (db *Database) ListKeyTokens(ctx TxContext, ownerID models.UserID) ([]model return data, nil } -func (db *Database) GetKeyToken(ctx TxContext, userid models.UserID, keyTokenid models.KeyTokenID) (models.KeyToken, error) { +func (db *Database) GetKeyToken(ctx db.TxContext, userid models.UserID, keyTokenid models.KeyTokenID) (models.KeyToken, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.KeyToken{}, err @@ -77,7 +78,7 @@ func (db *Database) GetKeyToken(ctx TxContext, userid models.UserID, keyTokenid return keyToken, nil } -func (db *Database) GetKeyTokenByToken(ctx TxContext, key string) (*models.KeyToken, error) { +func (db *Database) GetKeyTokenByToken(ctx db.TxContext, key string) (*models.KeyToken, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -99,7 +100,7 @@ func (db *Database) GetKeyTokenByToken(ctx TxContext, key string) (*models.KeyTo return &user, nil } -func (db *Database) DeleteKeyToken(ctx TxContext, keyTokenid models.KeyTokenID) error { +func (db *Database) DeleteKeyToken(ctx db.TxContext, keyTokenid models.KeyTokenID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -113,7 +114,7 @@ func (db *Database) DeleteKeyToken(ctx TxContext, keyTokenid models.KeyTokenID) return nil } -func (db *Database) UpdateKeyTokenName(ctx TxContext, keyTokenid models.KeyTokenID, name string) error { +func (db *Database) UpdateKeyTokenName(ctx db.TxContext, keyTokenid models.KeyTokenID, name string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -130,7 +131,7 @@ func (db *Database) UpdateKeyTokenName(ctx TxContext, keyTokenid models.KeyToken return nil } -func (db *Database) UpdateKeyTokenPermissions(ctx TxContext, keyTokenid models.KeyTokenID, perm models.TokenPermissionList) error { +func (db *Database) UpdateKeyTokenPermissions(ctx db.TxContext, keyTokenid models.KeyTokenID, perm models.TokenPermissionList) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -147,7 +148,7 @@ func (db *Database) UpdateKeyTokenPermissions(ctx TxContext, keyTokenid models.K return nil } -func (db *Database) UpdateKeyTokenAllChannels(ctx TxContext, keyTokenid models.KeyTokenID, allChannels bool) error { +func (db *Database) UpdateKeyTokenAllChannels(ctx db.TxContext, keyTokenid models.KeyTokenID, allChannels bool) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -164,7 +165,7 @@ func (db *Database) UpdateKeyTokenAllChannels(ctx TxContext, keyTokenid models.K return nil } -func (db *Database) UpdateKeyTokenChannels(ctx TxContext, keyTokenid models.KeyTokenID, channels []models.ChannelID) error { +func (db *Database) UpdateKeyTokenChannels(ctx db.TxContext, keyTokenid models.KeyTokenID, channels []models.ChannelID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -181,7 +182,7 @@ func (db *Database) UpdateKeyTokenChannels(ctx TxContext, keyTokenid models.KeyT return nil } -func (db *Database) IncKeyTokenMessageCounter(ctx TxContext, keyToken *models.KeyToken) error { +func (db *Database) IncKeyTokenMessageCounter(ctx db.TxContext, keyToken *models.KeyToken) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -203,7 +204,7 @@ func (db *Database) IncKeyTokenMessageCounter(ctx TxContext, keyToken *models.Ke return nil } -func (db *Database) UpdateKeyTokenLastUsed(ctx TxContext, keyTokenid models.KeyTokenID) error { +func (db *Database) UpdateKeyTokenLastUsed(ctx db.TxContext, keyTokenid models.KeyTokenID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err diff --git a/scnserver/db/impl/primary/messages.go b/scnserver/db/impl/primary/messages.go index c234828..494e007 100644 --- a/scnserver/db/impl/primary/messages.go +++ b/scnserver/db/impl/primary/messages.go @@ -1,6 +1,7 @@ package primary import ( + "blackforestbytes.com/simplecloudnotifier/db" ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken" "blackforestbytes.com/simplecloudnotifier/models" "database/sql" @@ -9,7 +10,7 @@ import ( "time" ) -func (db *Database) GetMessageByUserMessageID(ctx TxContext, usrMsgId string) (*models.Message, error) { +func (db *Database) GetMessageByUserMessageID(ctx db.TxContext, usrMsgId string) (*models.Message, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -31,7 +32,7 @@ func (db *Database) GetMessageByUserMessageID(ctx TxContext, usrMsgId string) (* return &msg, nil } -func (db *Database) GetMessage(ctx TxContext, scnMessageID models.MessageID, allowDeleted bool) (models.Message, error) { +func (db *Database) GetMessage(ctx db.TxContext, scnMessageID models.MessageID, allowDeleted bool) (models.Message, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Message{}, err @@ -57,7 +58,7 @@ func (db *Database) GetMessage(ctx TxContext, scnMessageID models.MessageID, all return msg, nil } -func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, channel models.Channel, timestampSend *time.Time, title string, content *string, priority int, userMsgId *string, senderIP string, senderName *string, usedKeyID models.KeyTokenID) (models.Message, error) { +func (db *Database) CreateMessage(ctx db.TxContext, senderUserID models.UserID, channel models.Channel, timestampSend *time.Time, title string, content *string, priority int, userMsgId *string, senderIP string, senderName *string, usedKeyID models.KeyTokenID) (models.Message, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Message{}, err @@ -66,7 +67,6 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha entity := models.MessageDB{ MessageID: models.NewMessageID(), SenderUserID: senderUserID, - OwnerUserID: channel.OwnerUserID, ChannelInternalName: channel.InternalName, ChannelID: channel.ChannelID, SenderIP: senderIP, @@ -89,7 +89,7 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha return entity.Model(), nil } -func (db *Database) DeleteMessage(ctx TxContext, messageID models.MessageID) error { +func (db *Database) DeleteMessage(ctx db.TxContext, messageID models.MessageID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -103,7 +103,7 @@ func (db *Database) DeleteMessage(ctx TxContext, messageID models.MessageID) err return nil } -func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pageSize *int, inTok ct.CursorToken) ([]models.Message, ct.CursorToken, error) { +func (db *Database) ListMessages(ctx db.TxContext, filter models.MessageFilter, pageSize *int, inTok ct.CursorToken) ([]models.Message, ct.CursorToken, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, ct.CursorToken{}, err @@ -151,7 +151,7 @@ func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pag } } -func (db *Database) CountMessages(ctx TxContext, filter models.MessageFilter) (int64, error) { +func (db *Database) CountMessages(ctx db.TxContext, filter models.MessageFilter) (int64, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return 0, err diff --git a/scnserver/db/impl/primary/meta.go b/scnserver/db/impl/primary/meta.go index e019d50..2af32bd 100644 --- a/scnserver/db/impl/primary/meta.go +++ b/scnserver/db/impl/primary/meta.go @@ -1,15 +1,19 @@ package primary import ( - "context" + "blackforestbytes.com/simplecloudnotifier/db" "errors" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" ) -func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { +func (db *Database) ReadSchema(ctx db.TxContext) (retval int, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return 0, err + } - r1, err := db.db.Query(ctx, "SELECT name FROM sqlite_master WHERE type = :typ AND name = :name", sq.PP{"typ": "table", "name": "meta"}) + r1, err := tx.Query(ctx, "SELECT name FROM sqlite_master WHERE type = :typ AND name = :name", sq.PP{"typ": "table", "name": "meta"}) if err != nil { return 0, err } @@ -31,7 +35,7 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { return 0, err } - r2, err := db.db.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": "schema"}) + r2, err := tx.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": "schema"}) if err != nil { return 0, err } @@ -62,8 +66,13 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { return dbschema, nil } -func (db *Database) WriteMetaString(ctx context.Context, key string, value string) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ +func (db *Database) WriteMetaString(ctx db.TxContext, key string, value string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ "key": key, "val": value, }) @@ -73,8 +82,13 @@ func (db *Database) WriteMetaString(ctx context.Context, key string, value strin return nil } -func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ +func (db *Database) WriteMetaInt(ctx db.TxContext, key string, value int64) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ "key": key, "val": value, }) @@ -84,8 +98,13 @@ func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) e return nil } -func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_real) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_real = :val", sq.PP{ +func (db *Database) WriteMetaReal(ctx db.TxContext, key string, value float64) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_real) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_real = :val", sq.PP{ "key": key, "val": value, }) @@ -95,8 +114,13 @@ func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64 return nil } -func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_blob) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_blob = :val", sq.PP{ +func (db *Database) WriteMetaBlob(ctx db.TxContext, key string, value []byte) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_blob) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_blob = :val", sq.PP{ "key": key, "val": value, }) @@ -106,8 +130,13 @@ func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) return nil } -func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *string, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_txt FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaString(ctx db.TxContext, key string) (retval *string, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_txt FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -137,8 +166,13 @@ func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *str return langext.Ptr(value), nil } -func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaInt(ctx db.TxContext, key string) (retval *int64, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -169,8 +203,13 @@ func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, return langext.Ptr(value), nil } -func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float64, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_real FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaReal(ctx db.TxContext, key string) (retval *float64, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_real FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -201,8 +240,13 @@ func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float return langext.Ptr(value), nil } -func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byte, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_blob FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaBlob(ctx db.TxContext, key string) (retval *[]byte, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_blob FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -233,8 +277,13 @@ func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byt return langext.Ptr(value), nil } -func (db *Database) DeleteMeta(ctx context.Context, key string) error { - _, err := db.db.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) DeleteMeta(ctx db.TxContext, key string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return err } diff --git a/scnserver/db/impl/primary/subscriptions.go b/scnserver/db/impl/primary/subscriptions.go index a43ab53..0b130d2 100644 --- a/scnserver/db/impl/primary/subscriptions.go +++ b/scnserver/db/impl/primary/subscriptions.go @@ -1,13 +1,14 @@ package primary import ( + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) -func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserID, channel models.Channel, confirmed bool) (models.Subscription, error) { +func (db *Database) CreateSubscription(ctx db.TxContext, subscriberUID models.UserID, channel models.Channel, confirmed bool) (models.Subscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Subscription{}, err @@ -31,7 +32,7 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI return entity.Model(), nil } -func (db *Database) ListSubscriptionsByChannel(ctx TxContext, channelID models.ChannelID) ([]models.Subscription, error) { +func (db *Database) ListSubscriptionsByChannel(ctx db.TxContext, channelID models.ChannelID) ([]models.Subscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -52,7 +53,7 @@ func (db *Database) ListSubscriptionsByChannel(ctx TxContext, channelID models.C return data, nil } -func (db *Database) ListSubscriptionsByChannelOwner(ctx TxContext, ownerUserID models.UserID, confirmed *bool) ([]models.Subscription, error) { +func (db *Database) ListSubscriptionsByChannelOwner(ctx db.TxContext, ownerUserID models.UserID, confirmed *bool) ([]models.Subscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -80,7 +81,7 @@ func (db *Database) ListSubscriptionsByChannelOwner(ctx TxContext, ownerUserID m return data, nil } -func (db *Database) ListSubscriptionsBySubscriber(ctx TxContext, subscriberUserID models.UserID, confirmed *bool) ([]models.Subscription, error) { +func (db *Database) ListSubscriptionsBySubscriber(ctx db.TxContext, subscriberUserID models.UserID, confirmed *bool) ([]models.Subscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -108,7 +109,7 @@ func (db *Database) ListSubscriptionsBySubscriber(ctx TxContext, subscriberUserI return data, nil } -func (db *Database) GetSubscription(ctx TxContext, subid models.SubscriptionID) (models.Subscription, error) { +func (db *Database) GetSubscription(ctx db.TxContext, subid models.SubscriptionID) (models.Subscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Subscription{}, err @@ -127,7 +128,7 @@ func (db *Database) GetSubscription(ctx TxContext, subid models.SubscriptionID) return sub, nil } -func (db *Database) GetSubscriptionBySubscriber(ctx TxContext, subscriberId models.UserID, channelId models.ChannelID) (*models.Subscription, error) { +func (db *Database) GetSubscriptionBySubscriber(ctx db.TxContext, subscriberId models.UserID, channelId models.ChannelID) (*models.Subscription, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err @@ -152,7 +153,7 @@ func (db *Database) GetSubscriptionBySubscriber(ctx TxContext, subscriberId mode return &user, nil } -func (db *Database) DeleteSubscription(ctx TxContext, subid models.SubscriptionID) error { +func (db *Database) DeleteSubscription(ctx db.TxContext, subid models.SubscriptionID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -166,7 +167,7 @@ func (db *Database) DeleteSubscription(ctx TxContext, subid models.SubscriptionI return nil } -func (db *Database) UpdateSubscriptionConfirmed(ctx TxContext, subscriptionID models.SubscriptionID, confirmed bool) error { +func (db *Database) UpdateSubscriptionConfirmed(ctx db.TxContext, subscriptionID models.SubscriptionID, confirmed bool) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err diff --git a/scnserver/db/impl/primary/users.go b/scnserver/db/impl/primary/users.go index 3ee2236..0f5eadd 100644 --- a/scnserver/db/impl/primary/users.go +++ b/scnserver/db/impl/primary/users.go @@ -2,13 +2,14 @@ package primary import ( scn "blackforestbytes.com/simplecloudnotifier" + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) -func (db *Database) CreateUser(ctx TxContext, protoken *string, username *string) (models.User, error) { +func (db *Database) CreateUser(ctx db.TxContext, protoken *string, username *string) (models.User, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.User{}, err @@ -35,7 +36,7 @@ func (db *Database) CreateUser(ctx TxContext, protoken *string, username *string return entity.Model(), nil } -func (db *Database) ClearProTokens(ctx TxContext, protoken string) error { +func (db *Database) ClearProTokens(ctx db.TxContext, protoken string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -49,7 +50,7 @@ func (db *Database) ClearProTokens(ctx TxContext, protoken string) error { return nil } -func (db *Database) GetUser(ctx TxContext, userid models.UserID) (models.User, error) { +func (db *Database) GetUser(ctx db.TxContext, userid models.UserID) (models.User, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.User{}, err @@ -68,7 +69,7 @@ func (db *Database) GetUser(ctx TxContext, userid models.UserID) (models.User, e return user, nil } -func (db *Database) UpdateUserUsername(ctx TxContext, userid models.UserID, username *string) error { +func (db *Database) UpdateUserUsername(ctx db.TxContext, userid models.UserID, username *string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -85,7 +86,7 @@ func (db *Database) UpdateUserUsername(ctx TxContext, userid models.UserID, user return nil } -func (db *Database) UpdateUserProToken(ctx TxContext, userid models.UserID, protoken *string) error { +func (db *Database) UpdateUserProToken(ctx db.TxContext, userid models.UserID, protoken *string) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -103,7 +104,7 @@ func (db *Database) UpdateUserProToken(ctx TxContext, userid models.UserID, prot return nil } -func (db *Database) IncUserMessageCounter(ctx TxContext, user *models.User) error { +func (db *Database) IncUserMessageCounter(ctx db.TxContext, user *models.User) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err @@ -132,7 +133,7 @@ func (db *Database) IncUserMessageCounter(ctx TxContext, user *models.User) erro return nil } -func (db *Database) UpdateUserLastRead(ctx TxContext, userid models.UserID) error { +func (db *Database) UpdateUserLastRead(ctx db.TxContext, userid models.UserID) error { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return err diff --git a/scnserver/db/impl/requests/database.go b/scnserver/db/impl/requests/database.go index c7f5c38..30070b4 100644 --- a/scnserver/db/impl/requests/database.go +++ b/scnserver/db/impl/requests/database.go @@ -4,6 +4,7 @@ import ( server "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/db/dbtools" "blackforestbytes.com/simplecloudnotifier/db/schema" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "context" "database/sql" "errors" @@ -63,77 +64,98 @@ func (db *Database) DB() sq.DB { return db.db } -func (db *Database) Migrate(ctx context.Context) error { - ctx, cancel := context.WithTimeout(context.Background(), 24*time.Second) - defer cancel() +func (db *Database) Migrate(outerctx context.Context) error { + innerctx, cancel := context.WithTimeout(outerctx, 24*time.Second) + tctx := simplectx.CreateSimpleContext(innerctx, cancel) - currschema, err := db.ReadSchema(ctx) + tx, err := tctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + defer func() { + if tx.Status() == sq.TxStatusInitial || tx.Status() == sq.TxStatusActive { + err = tx.Rollback() + if err != nil { + log.Err(err).Msg("failed to rollback transaction") + } + } + }() + + ppReInit := false + + currschema, err := db.ReadSchema(tctx) if err != nil { return err } if currschema == 0 { + schemastr := schema.RequestsSchema[schema.RequestsSchemaVersion].SQL + schemahash := schema.RequestsSchema[schema.RequestsSchemaVersion].Hash - schemastr := schema.RequestsSchema1 - - schemahash, err := sq.HashSqliteSchema(ctx, schemastr) + schemahash, err := sq.HashSqliteSchema(tctx, schemastr) if err != nil { return err } - _, err = db.db.Exec(ctx, schemastr, sq.PP{}) + _, err = tx.Exec(tctx, schemastr, sq.PP{}) if err != nil { return err } - err = db.WriteMetaInt(ctx, "schema", 1) + err = db.WriteMetaInt(tctx, "schema", int64(schema.RequestsSchemaVersion)) if err != nil { return err } - err = db.WriteMetaString(ctx, "schema_hash", schemahash) + err = db.WriteMetaString(tctx, "schema_hash", schemahash) if err != nil { return err } - err = db.pp.Init(ctx) // Re-Init + ppReInit = true + + currschema = schema.LogsSchemaVersion + } + + if currschema == 1 { + schemHashDB, err := sq.HashSqliteDatabase(tctx, tx) if err != nil { return err } - return nil - - } else if currschema == 1 { - - schemHashDB, err := sq.HashSqliteDatabase(ctx, db.db) + schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash") if err != nil { return err } - schemaHashMeta, err := db.ReadMetaString(ctx, "schema_hash") - if err != nil { - return err - } - - schemHashAsset := schema.RequestsHash1 - if err != nil { - return err - } - - if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schemHashAsset { + if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.RequestsSchema[currschema].Hash { log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (requests db)") log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).Msg("Schema (requests db)") - log.Debug().Str("schemHashAsset", schemHashAsset).Msg("Schema (requests db)") + log.Debug().Str("schemaHashAsset", schema.RequestsSchema[currschema].Hash).Msg("Schema (requests db)") return errors.New("database schema does not match (requests db)") } else { log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (requests db)") } + } - return nil // current - } else { + if currschema != schema.RequestsSchemaVersion { return errors.New(fmt.Sprintf("Unknown DB schema: %d", currschema)) } + err = tx.Commit() + if err != nil { + return err + } + + if ppReInit { + log.Debug().Msg("Re-Init preprocessor") + err = db.pp.Init(outerctx) // Re-Init + if err != nil { + return err + } + } + + return nil } func (db *Database) Ping(ctx context.Context) error { diff --git a/scnserver/db/impl/requests/meta.go b/scnserver/db/impl/requests/meta.go index 2713bfc..9d3551c 100644 --- a/scnserver/db/impl/requests/meta.go +++ b/scnserver/db/impl/requests/meta.go @@ -1,15 +1,19 @@ package requests import ( - "context" + "blackforestbytes.com/simplecloudnotifier/db" "errors" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" ) -func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { +func (db *Database) ReadSchema(ctx db.TxContext) (retval int, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return 0, err + } - r1, err := db.db.Query(ctx, "SELECT name FROM sqlite_master WHERE type = :typ AND name = :name", sq.PP{"typ": "table", "name": "meta"}) + r1, err := tx.Query(ctx, "SELECT name FROM sqlite_master WHERE type = :typ AND name = :name", sq.PP{"typ": "table", "name": "meta"}) if err != nil { return 0, err } @@ -31,7 +35,7 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { return 0, err } - r2, err := db.db.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": "schema"}) + r2, err := tx.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": "schema"}) if err != nil { return 0, err } @@ -62,8 +66,13 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) { return dbschema, nil } -func (db *Database) WriteMetaString(ctx context.Context, key string, value string) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ +func (db *Database) WriteMetaString(ctx db.TxContext, key string, value string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ "key": key, "val": value, }) @@ -73,8 +82,13 @@ func (db *Database) WriteMetaString(ctx context.Context, key string, value strin return nil } -func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ +func (db *Database) WriteMetaInt(ctx db.TxContext, key string, value int64) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ "key": key, "val": value, }) @@ -84,8 +98,13 @@ func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) e return nil } -func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_real) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_real = :val", sq.PP{ +func (db *Database) WriteMetaReal(ctx db.TxContext, key string, value float64) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_real) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_real = :val", sq.PP{ "key": key, "val": value, }) @@ -95,8 +114,13 @@ func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64 return nil } -func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) error { - _, err := db.db.Exec(ctx, "INSERT INTO meta (meta_key, value_blob) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_blob = :val", sq.PP{ +func (db *Database) WriteMetaBlob(ctx db.TxContext, key string, value []byte) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO meta (meta_key, value_blob) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_blob = :val", sq.PP{ "key": key, "val": value, }) @@ -106,8 +130,13 @@ func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) return nil } -func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *string, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_txt FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaString(ctx db.TxContext, key string) (retval *string, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_txt FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -137,8 +166,13 @@ func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *str return langext.Ptr(value), nil } -func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaInt(ctx db.TxContext, key string) (retval *int64, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_int FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -169,8 +203,13 @@ func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, return langext.Ptr(value), nil } -func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float64, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_real FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaReal(ctx db.TxContext, key string) (retval *float64, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_real FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -201,8 +240,13 @@ func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float return langext.Ptr(value), nil } -func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byte, reterr error) { - r2, err := db.db.Query(ctx, "SELECT value_blob FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) ReadMetaBlob(ctx db.TxContext, key string) (retval *[]byte, reterr error) { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return nil, err + } + + r2, err := tx.Query(ctx, "SELECT value_blob FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return nil, err } @@ -233,8 +277,13 @@ func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byt return langext.Ptr(value), nil } -func (db *Database) DeleteMeta(ctx context.Context, key string) error { - _, err := db.db.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) +func (db *Database) DeleteMeta(ctx db.TxContext, key string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) if err != nil { return err } diff --git a/scnserver/db/schema/assets.go b/scnserver/db/schema/assets.go index 5194b4d..5f16036 100644 --- a/scnserver/db/schema/assets.go +++ b/scnserver/db/schema/assets.go @@ -2,27 +2,52 @@ package schema import _ "embed" -//go:embed primary_1.ddl -var PrimarySchema1 string +type Def struct { + SQL string + Hash string +} -const PrimaryHash1 = "f2b2847f32681a7178e405553beea4a324034915a0c5a5dc70b3c6abbcc852f2" +//go:embed primary_1.ddl +var primarySchema1 string //go:embed primary_2.ddl -var PrimarySchema2 string - -const PrimaryHash2 = "07ed1449114416ed043084a30e0722a5f97bf172161338d2f7106a8dfd387d0a" +var primarySchema2 string //go:embed primary_3.ddl -var PrimarySchema3 string +var primarySchema3 string -const PrimaryHash3 = "65c2125ad0e12d02490cf2275f0067ef3c62a8522edf9a35ee8aa3f3c09b12e8" +//go:embed primary_4.ddl +var primarySchema4 string + +//go:embed primary_migration_3_4.ddl +var PrimaryMigration_3_4 string //go:embed requests_1.ddl -var RequestsSchema1 string - -const RequestsHash1 = "ebb0a5748b605e8215437413b738279670190ca8159b6227cfc2aa13418b41e9" +var requestsSchema1 string //go:embed logs_1.ddl -var LogsSchema1 string +var logsSchema1 string -const LogsHash1 = "65fba477c04095effc3a8e1bb79fe7547b8e52e983f776f156266eddc4f201d7" +var PrimarySchema = map[int]Def{ + 0: {"", ""}, + 1: {primarySchema1, "f2b2847f32681a7178e405553beea4a324034915a0c5a5dc70b3c6abbcc852f2"}, + 2: {primarySchema2, "07ed1449114416ed043084a30e0722a5f97bf172161338d2f7106a8dfd387d0a"}, + 3: {primarySchema3, "65c2125ad0e12d02490cf2275f0067ef3c62a8522edf9a35ee8aa3f3c09b12e8"}, + 4: {primarySchema4, "cb022156ab0e7aea39dd0c985428c43cae7d60e41ca8e9e5a84c774b3019d2ca"}, +} + +var PrimarySchemaVersion = 4 + +var RequestsSchema = map[int]Def{ + 0: {"", ""}, + 1: {requestsSchema1, "ebb0a5748b605e8215437413b738279670190ca8159b6227cfc2aa13418b41e9"}, +} + +var RequestsSchemaVersion = 1 + +var LogsSchema = map[int]Def{ + 0: {"", ""}, + 1: {logsSchema1, "65fba477c04095effc3a8e1bb79fe7547b8e52e983f776f156266eddc4f201d7"}, +} + +var LogsSchemaVersion = 1 diff --git a/scnserver/db/schema/primary_4.ddl b/scnserver/db/schema/primary_4.ddl new file mode 100644 index 0000000..fbf607b --- /dev/null +++ b/scnserver/db/schema/primary_4.ddl @@ -0,0 +1,233 @@ +CREATE TABLE users +( + user_id TEXT NOT NULL, + + username TEXT NULL DEFAULT NULL, + + timestamp_created INTEGER NOT NULL, + timestamp_lastread INTEGER NULL DEFAULT NULL, + timestamp_lastsent INTEGER NULL DEFAULT NULL, + + messages_sent INTEGER NOT NULL DEFAULT '0', + + quota_used INTEGER NOT NULL DEFAULT '0', + quota_used_day TEXT NULL DEFAULT NULL, + + is_pro INTEGER CHECK(is_pro IN (0, 1)) NOT NULL DEFAULT 0, + pro_token TEXT NULL DEFAULT NULL, + + PRIMARY KEY (user_id) +) STRICT; +CREATE UNIQUE INDEX "idx_users_protoken" ON users (pro_token) WHERE pro_token IS NOT NULL; + + +CREATE TABLE keytokens +( + keytoken_id TEXT NOT NULL, + + timestamp_created INTEGER NOT NULL, + timestamp_lastused INTEGER NULL DEFAULT NULL, + + name TEXT NOT NULL, + + owner_user_id TEXT NOT NULL, + + all_channels INTEGER CHECK(all_channels IN (0, 1)) NOT NULL, + channels TEXT NOT NULL, + token TEXT NOT NULL, + permissions TEXT NOT NULL, + + messages_sent INTEGER NOT NULL DEFAULT '0', + + PRIMARY KEY (keytoken_id) +) STRICT; +CREATE UNIQUE INDEX "idx_keytokens_token" ON keytokens (token); + + +CREATE TABLE clients +( + client_id TEXT NOT NULL, + + user_id TEXT NOT NULL, + type TEXT CHECK(type IN ('ANDROID', 'IOS')) NOT NULL, + fcm_token TEXT NOT NULL, + + timestamp_created INTEGER NOT NULL, + + agent_model TEXT NOT NULL, + agent_version TEXT NOT NULL, + + PRIMARY KEY (client_id) +) STRICT; +CREATE INDEX "idx_clients_userid" ON clients (user_id); +CREATE UNIQUE INDEX "idx_clients_fcmtoken" ON clients (fcm_token); + + +CREATE TABLE channels +( + channel_id TEXT NOT NULL, + + owner_user_id TEXT NOT NULL, + + internal_name TEXT NOT NULL, + display_name TEXT NOT NULL, + description_name TEXT NULL, + + subscribe_key TEXT NOT NULL, + + timestamp_created INTEGER NOT NULL, + timestamp_lastsent INTEGER NULL DEFAULT NULL, + + messages_sent INTEGER NOT NULL DEFAULT '0', + + PRIMARY KEY (channel_id) +) STRICT; +CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, internal_name); + +CREATE TABLE subscriptions +( + subscription_id TEXT NOT NULL, + + subscriber_user_id TEXT NOT NULL, + channel_owner_user_id TEXT NOT NULL, + channel_internal_name TEXT NOT NULL, + channel_id TEXT NOT NULL, + + timestamp_created INTEGER NOT NULL, + + confirmed INTEGER CHECK(confirmed IN (0, 1)) NOT NULL, + + PRIMARY KEY (subscription_id) +) STRICT; +CREATE UNIQUE INDEX "idx_subscriptions_ref" ON subscriptions (subscriber_user_id, channel_owner_user_id, channel_internal_name); +CREATE INDEX "idx_subscriptions_chan" ON subscriptions (channel_id); +CREATE INDEX "idx_subscriptions_subuser" ON subscriptions (subscriber_user_id); +CREATE INDEX "idx_subscriptions_ownuser" ON subscriptions (channel_owner_user_id); +CREATE INDEX "idx_subscriptions_tsc" ON subscriptions (timestamp_created); +CREATE INDEX "idx_subscriptions_conf" ON subscriptions (confirmed); + + +CREATE TABLE messages +( + message_id TEXT NOT NULL, + sender_user_id TEXT NOT NULL, + channel_internal_name TEXT NOT NULL, + channel_id TEXT NOT NULL, + sender_ip TEXT NOT NULL, + sender_name TEXT NULL, + + timestamp_real INTEGER NOT NULL, + timestamp_client INTEGER NULL, + + title TEXT NOT NULL, + content TEXT NULL, + priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL, + usr_message_id TEXT NULL, + + used_key_id TEXT NOT NULL, + + deleted INTEGER CHECK(deleted IN (0, 1)) NOT NULL DEFAULT '0', + + PRIMARY KEY (message_id) +) STRICT; +CREATE INDEX "idx_messages_channel" ON messages (channel_internal_name COLLATE BINARY); +CREATE INDEX "idx_messages_channel_nc" ON messages (channel_internal_name COLLATE NOCASE); +CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (sender_user_id, usr_message_id COLLATE BINARY); +CREATE INDEX "idx_messages_senderip" ON messages (sender_ip COLLATE BINARY); +CREATE INDEX "idx_messages_sendername" ON messages (sender_name COLLATE BINARY); +CREATE INDEX "idx_messages_sendername_nc" ON messages (sender_name COLLATE NOCASE); +CREATE INDEX "idx_messages_title" ON messages (title COLLATE BINARY); +CREATE INDEX "idx_messages_title_nc" ON messages (title COLLATE NOCASE); +CREATE INDEX "idx_messages_usedkey" ON messages (sender_user_id, used_key_id); +CREATE INDEX "idx_messages_deleted" ON messages (deleted); + + +CREATE VIRTUAL TABLE messages_fts USING fts5 +( + channel_internal_name, + sender_name, + title, + content, + + tokenize = unicode61, + content = 'messages', + content_rowid = 'rowid' +); + +CREATE TRIGGER fts_insert AFTER INSERT ON messages BEGIN + INSERT INTO messages_fts (rowid, channel_internal_name, sender_name, title, content) VALUES (new.rowid, new.channel_internal_name, new.sender_name, new.title, new.content); +END; + +CREATE TRIGGER fts_update AFTER UPDATE ON messages BEGIN + INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.rowid, old.channel_internal_name, old.sender_name, old.title, old.content); + INSERT INTO messages_fts ( rowid, channel_internal_name, sender_name, title, content) VALUES ( new.rowid, new.channel_internal_name, new.sender_name, new.title, new.content); +END; + +CREATE TRIGGER fts_delete AFTER DELETE ON messages BEGIN + INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.rowid, old.channel_internal_name, old.sender_name, old.title, old.content); +END; + + +CREATE TABLE deliveries +( + delivery_id TEXT NOT NULL, + + message_id TEXT NOT NULL, + receiver_user_id TEXT NOT NULL, + receiver_client_id TEXT NOT NULL, + + timestamp_created INTEGER NOT NULL, + timestamp_finalized INTEGER NULL, + + + status TEXT CHECK(status IN ('RETRY','SUCCESS','FAILED')) NOT NULL, + retry_count INTEGER NOT NULL DEFAULT 0, + next_delivery TEXT NULL DEFAULT NULL, + + fcm_message_id TEXT NULL, + + PRIMARY KEY (delivery_id) +) STRICT; +CREATE INDEX "idx_deliveries_receiver" ON deliveries (message_id, receiver_client_id); + + +CREATE TABLE compat_ids +( + old INTEGER NOT NULL, + new TEXT NOT NULL, + type TEXT NOT NULL +) STRICT; +CREATE UNIQUE INDEX "idx_compatids_new" ON compat_ids (new); +CREATE UNIQUE INDEX "idx_compatids_old" ON compat_ids (old, type); + + +CREATE TABLE compat_acks +( + user_id TEXT NOT NULL, + message_id TEXT NOT NULL +) STRICT; +CREATE INDEX "idx_compatacks_userid" ON compat_acks (user_id); +CREATE UNIQUE INDEX "idx_compatacks_messageid" ON compat_acks (message_id); +CREATE UNIQUE INDEX "idx_compatacks_userid_messageid" ON compat_acks (user_id, message_id); + + +CREATE TABLE compat_clients +( + client_id TEXT NOT NULL +) STRICT; +CREATE UNIQUE INDEX "idx_compatclient_clientid" ON compat_clients (client_id); + + +CREATE TABLE `meta` +( + meta_key TEXT NOT NULL, + value_int INTEGER NULL, + value_txt TEXT NULL, + value_real REAL NULL, + value_blob BLOB NULL, + + PRIMARY KEY (meta_key) +) STRICT; + + +INSERT INTO meta (meta_key, value_int) VALUES ('schema', 3) \ No newline at end of file diff --git a/scnserver/db/schema/primary_migration_3_4.ddl b/scnserver/db/schema/primary_migration_3_4.ddl new file mode 100644 index 0000000..7a6a25c --- /dev/null +++ b/scnserver/db/schema/primary_migration_3_4.ddl @@ -0,0 +1,20 @@ + +DROP INDEX idx_messages_owner_channel; + + +DROP INDEX idx_messages_owner_channel_nc; + + +DROP INDEX idx_messages_idempotency; +CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (sender_user_id, usr_message_id COLLATE BINARY); + + +DROP INDEX idx_messages_usedkey; +CREATE INDEX "idx_messages_usedkey" ON messages (sender_user_id, used_key_id); + + +ALTER TABLE messages DROP COLUMN owner_user_id; + + + + diff --git a/scnserver/logic/simplecontext.go b/scnserver/db/simplectx/simplecontext.go similarity index 96% rename from scnserver/logic/simplecontext.go rename to scnserver/db/simplectx/simplecontext.go index eda6350..05d2492 100644 --- a/scnserver/logic/simplecontext.go +++ b/scnserver/db/simplectx/simplecontext.go @@ -1,4 +1,4 @@ -package logic +package simplectx import ( "blackforestbytes.com/simplecloudnotifier/db" @@ -51,7 +51,9 @@ func (sc *SimpleContext) Cancel() { } sc.transaction = nil } - sc.cancelFunc() + if sc.cancelFunc != nil { + sc.cancelFunc() + } } func (sc *SimpleContext) GetOrCreateTransaction(db db.DatabaseImpl) (sq.Tx, error) { diff --git a/scnserver/go.mod b/scnserver/go.mod index 5faa60b..edef6d1 100644 --- a/scnserver/go.mod +++ b/scnserver/go.mod @@ -9,13 +9,14 @@ require ( github.com/jmoiron/sqlx v1.3.5 github.com/mattn/go-sqlite3 v1.14.17 github.com/rs/zerolog v1.29.1 - gogs.mikescher.com/BlackForestBytes/goext v0.0.163 + gogs.mikescher.com/BlackForestBytes/goext v0.0.218 gopkg.in/loremipsum.v1 v1.1.2 ) require ( - github.com/bytedance/sonic v1.9.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.10.0-rc3 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -29,15 +30,15 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.10.0 // indirect - golang.org/x/net v0.10.0 // indirect - golang.org/x/sys v0.9.0 // indirect - golang.org/x/term v0.9.0 // indirect - golang.org/x/text v0.10.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/arch v0.4.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/scnserver/go.sum b/scnserver/go.sum index e725880..f0d93d3 100644 --- a/scnserver/go.sum +++ b/scnserver/go.sum @@ -1,9 +1,16 @@ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -39,6 +46,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= @@ -60,6 +68,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -77,19 +87,32 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= gogs.mikescher.com/BlackForestBytes/goext v0.0.163 h1:GYC34wLOdBM/CgAov0AyznfHGd09Km106Ijmp8cZmp4= gogs.mikescher.com/BlackForestBytes/goext v0.0.163/go.mod h1:Tood+vqmPqS/meYRnUcGz837wqHkP8BykVpY1h8TWoI= +gogs.mikescher.com/BlackForestBytes/goext v0.0.216 h1:cr28oRYNBMNohRyjb0nHNZg0OLE/NVq82zbkVYuNS1M= +gogs.mikescher.com/BlackForestBytes/goext v0.0.216/go.mod h1:eBL+dE41RaoQw38oLVzxSxLPsFq6sGFug7o3i7Br+SY= +gogs.mikescher.com/BlackForestBytes/goext v0.0.217 h1:Z0vLwOgCzVi3nJLriPRBtFTyXhC9KjeAmk2DqvT7gPM= +gogs.mikescher.com/BlackForestBytes/goext v0.0.217/go.mod h1:eBL+dE41RaoQw38oLVzxSxLPsFq6sGFug7o3i7Br+SY= +gogs.mikescher.com/BlackForestBytes/goext v0.0.218 h1:ly69FPNGqcTc1o6Qf+P6HpHtKmOxG1/fWLj/Aqzi5Rg= +gogs.mikescher.com/BlackForestBytes/goext v0.0.218/go.mod h1:eBL+dE41RaoQw38oLVzxSxLPsFq6sGFug7o3i7Br+SY= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -97,14 +120,22 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/loremipsum.v1 v1.1.2 h1:12APklfJKuGszqZsrArW5QoQh03/W+qyCCjvnDuS6Tw= @@ -112,4 +143,5 @@ gopkg.in/loremipsum.v1 v1.1.2/go.mod h1:TuRvzFuzuejXj+odBU6Tubp/EPUyGb9wmSvHenyP 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= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/scnserver/jobs/DeliveryRetryJob.go b/scnserver/jobs/DeliveryRetryJob.go index 2fc949e..13a7c15 100644 --- a/scnserver/jobs/DeliveryRetryJob.go +++ b/scnserver/jobs/DeliveryRetryJob.go @@ -1,6 +1,7 @@ package jobs import ( + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" "errors" @@ -132,7 +133,7 @@ func (j *DeliveryRetryJob) execute() (fastrr bool, err error) { return len(deliveries) == 32, nil } -func (j *DeliveryRetryJob) redeliver(ctx *logic.SimpleContext, delivery models.Delivery) { +func (j *DeliveryRetryJob) redeliver(ctx *simplectx.SimpleContext, delivery models.Delivery) { client, err := j.app.Database.Primary.GetClient(ctx, delivery.ReceiverUserID, delivery.ReceiverClientID) if err != nil { diff --git a/scnserver/logic/application.go b/scnserver/logic/application.go index cacab72..68585cd 100644 --- a/scnserver/logic/application.go +++ b/scnserver/logic/application.go @@ -4,6 +4,7 @@ import ( scn "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/api/apierr" "blackforestbytes.com/simplecloudnotifier/api/ginresp" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "blackforestbytes.com/simplecloudnotifier/google" "blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/push" @@ -292,9 +293,9 @@ func (app *Application) StartRequest(g *gin.Context, uri any, query any, body an return actx, nil } -func (app *Application) NewSimpleTransactionContext(timeout time.Duration) *SimpleContext { +func (app *Application) NewSimpleTransactionContext(timeout time.Duration) *simplectx.SimpleContext { ictx, cancel := context.WithTimeout(context.Background(), timeout) - return CreateSimpleContext(ictx, cancel) + return simplectx.CreateSimpleContext(ictx, cancel) } func (app *Application) getPermissions(ctx *AppContext, hdr string) (models.PermissionSet, error) { diff --git a/scnserver/logic/permissions.go b/scnserver/logic/permissions.go index 09b12e3..b200b08 100644 --- a/scnserver/logic/permissions.go +++ b/scnserver/logic/permissions.go @@ -99,7 +99,7 @@ func (ac *AppContext) CheckPermissionMessageRead(msg models.Message) bool { func (ac *AppContext) CheckPermissionMessageDelete(msg models.Message) bool { p := ac.permissions - if p.Token != nil && p.Token.IsAdmin(msg.OwnerUserID) { + if p.Token != nil && p.Token.IsAdmin(msg.SenderUserID) { return true } diff --git a/scnserver/models/enums_gen.go b/scnserver/models/enums_gen.go index 21e0ad6..a336bff 100644 --- a/scnserver/models/enums_gen.go +++ b/scnserver/models/enums_gen.go @@ -4,7 +4,7 @@ package models import "gogs.mikescher.com/BlackForestBytes/goext/langext" -const ChecksumGenerator = "4bfd61daa179e1452035a34c25c6f8170a08500bc0a7aa0e3981f95ad4b0d7d2" +const ChecksumGenerator = "a1a684aa30d77d9a9936ccbb667b498c370a1f816273e9cd93948f4195155e90" type Enum interface { Valid() bool @@ -59,10 +59,7 @@ func (e ClientType) ValuesAny() []any { } func (e ClientType) ValuesMeta() []EnumMetaValue { - return []EnumMetaValue{ - EnumMetaValue{VarName: "ClientTypeAndroid", Value: ClientTypeAndroid, Description: nil}, - EnumMetaValue{VarName: "ClientTypeIOS", Value: ClientTypeIOS, Description: nil}, - } + return ClientTypeValuesMeta() } func (e ClientType) String() string { @@ -76,6 +73,10 @@ func (e ClientType) VarName() string { return "" } +func (e ClientType) Meta() EnumMetaValue { + return EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil} +} + func ParseClientType(vv string) (ClientType, bool) { for _, ev := range __ClientTypeValues { if string(ev) == vv { @@ -91,8 +92,8 @@ func ClientTypeValues() []ClientType { func ClientTypeValuesMeta() []EnumMetaValue { return []EnumMetaValue{ - EnumMetaValue{VarName: "ClientTypeAndroid", Value: ClientTypeAndroid, Description: nil}, - EnumMetaValue{VarName: "ClientTypeIOS", Value: ClientTypeIOS, Description: nil}, + ClientTypeAndroid.Meta(), + ClientTypeIOS.Meta(), } } @@ -128,11 +129,7 @@ func (e DeliveryStatus) ValuesAny() []any { } func (e DeliveryStatus) ValuesMeta() []EnumMetaValue { - return []EnumMetaValue{ - EnumMetaValue{VarName: "DeliveryStatusRetry", Value: DeliveryStatusRetry, Description: nil}, - EnumMetaValue{VarName: "DeliveryStatusSuccess", Value: DeliveryStatusSuccess, Description: nil}, - EnumMetaValue{VarName: "DeliveryStatusFailed", Value: DeliveryStatusFailed, Description: nil}, - } + return DeliveryStatusValuesMeta() } func (e DeliveryStatus) String() string { @@ -146,6 +143,10 @@ func (e DeliveryStatus) VarName() string { return "" } +func (e DeliveryStatus) Meta() EnumMetaValue { + return EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil} +} + func ParseDeliveryStatus(vv string) (DeliveryStatus, bool) { for _, ev := range __DeliveryStatusValues { if string(ev) == vv { @@ -161,9 +162,9 @@ func DeliveryStatusValues() []DeliveryStatus { func DeliveryStatusValuesMeta() []EnumMetaValue { return []EnumMetaValue{ - EnumMetaValue{VarName: "DeliveryStatusRetry", Value: DeliveryStatusRetry, Description: nil}, - EnumMetaValue{VarName: "DeliveryStatusSuccess", Value: DeliveryStatusSuccess, Description: nil}, - EnumMetaValue{VarName: "DeliveryStatusFailed", Value: DeliveryStatusFailed, Description: nil}, + DeliveryStatusRetry.Meta(), + DeliveryStatusSuccess.Meta(), + DeliveryStatusFailed.Meta(), } } @@ -208,12 +209,7 @@ func (e TokenPerm) ValuesAny() []any { } func (e TokenPerm) ValuesMeta() []EnumMetaValue { - return []EnumMetaValue{ - EnumMetaValue{VarName: "PermAdmin", Value: PermAdmin, Description: langext.Ptr("Edit userdata (+ includes all other permissions)")}, - EnumMetaValue{VarName: "PermChannelRead", Value: PermChannelRead, Description: langext.Ptr("Read messages")}, - EnumMetaValue{VarName: "PermChannelSend", Value: PermChannelSend, Description: langext.Ptr("Send messages")}, - EnumMetaValue{VarName: "PermUserRead", Value: PermUserRead, Description: langext.Ptr("Read userdata")}, - } + return TokenPermValuesMeta() } func (e TokenPerm) String() string { @@ -234,6 +230,10 @@ func (e TokenPerm) VarName() string { return "" } +func (e TokenPerm) Meta() EnumMetaValue { + return EnumMetaValue{VarName: e.VarName(), Value: e, Description: langext.Ptr(e.Description())} +} + func ParseTokenPerm(vv string) (TokenPerm, bool) { for _, ev := range __TokenPermValues { if string(ev) == vv { @@ -249,9 +249,9 @@ func TokenPermValues() []TokenPerm { func TokenPermValuesMeta() []EnumMetaValue { return []EnumMetaValue{ - EnumMetaValue{VarName: "PermAdmin", Value: PermAdmin, Description: langext.Ptr("Edit userdata (+ includes all other permissions)")}, - EnumMetaValue{VarName: "PermChannelRead", Value: PermChannelRead, Description: langext.Ptr("Read messages")}, - EnumMetaValue{VarName: "PermChannelSend", Value: PermChannelSend, Description: langext.Ptr("Send messages")}, - EnumMetaValue{VarName: "PermUserRead", Value: PermUserRead, Description: langext.Ptr("Read userdata")}, + PermAdmin.Meta(), + PermChannelRead.Meta(), + PermChannelSend.Meta(), + PermUserRead.Meta(), } } diff --git a/scnserver/models/message.go b/scnserver/models/message.go index a171209..cd36f68 100644 --- a/scnserver/models/message.go +++ b/scnserver/models/message.go @@ -14,8 +14,7 @@ const ( type Message struct { MessageID MessageID - SenderUserID UserID // user that sent the message - OwnerUserID UserID // oner of the message (= owner of the channel that contains it) + SenderUserID UserID // user that sent the message (this is also the owner of the channel that contains it) ChannelInternalName string ChannelID ChannelID SenderName *string @@ -34,7 +33,6 @@ func (m Message) FullJSON() MessageJSON { return MessageJSON{ MessageID: m.MessageID, SenderUserID: m.SenderUserID, - OwnerUserID: m.OwnerUserID, ChannelInternalName: m.ChannelInternalName, ChannelID: m.ChannelID, SenderName: m.SenderName, @@ -53,7 +51,6 @@ func (m Message) TrimmedJSON() MessageJSON { return MessageJSON{ MessageID: m.MessageID, SenderUserID: m.SenderUserID, - OwnerUserID: m.OwnerUserID, ChannelInternalName: m.ChannelInternalName, ChannelID: m.ChannelID, SenderName: m.SenderName, @@ -99,7 +96,6 @@ func (m Message) ShortContent() string { type MessageJSON struct { MessageID MessageID `json:"message_id"` SenderUserID UserID `json:"sender_user_id"` - OwnerUserID UserID `json:"owner_user_id"` ChannelInternalName string `json:"channel_internal_name"` ChannelID ChannelID `json:"channel_id"` SenderName *string `json:"sender_name"` @@ -116,7 +112,6 @@ type MessageJSON struct { type MessageDB struct { MessageID MessageID `db:"message_id"` SenderUserID UserID `db:"sender_user_id"` - OwnerUserID UserID `db:"owner_user_id"` ChannelInternalName string `db:"channel_internal_name"` ChannelID ChannelID `db:"channel_id"` SenderName *string `db:"sender_name"` @@ -135,7 +130,6 @@ func (m MessageDB) Model() Message { return Message{ MessageID: m.MessageID, SenderUserID: m.SenderUserID, - OwnerUserID: m.OwnerUserID, ChannelInternalName: m.ChannelInternalName, ChannelID: m.ChannelID, SenderName: m.SenderName, diff --git a/scnserver/models/messagefilter.go b/scnserver/models/messagefilter.go index 689eed8..dc138e5 100644 --- a/scnserver/models/messagefilter.go +++ b/scnserver/models/messagefilter.go @@ -17,7 +17,6 @@ type MessageFilter struct { ConfirmedSubscriptionBy *UserID SearchString *[]string Sender *[]UserID - Owner *[]UserID ChannelNameCS *[]string // case-sensitive ChannelNameCI *[]string // case-insensitive ChannelID *[]ChannelID @@ -79,15 +78,6 @@ func (f MessageFilter) SQL() (string, string, sq.PP, error) { sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")") } - if f.Owner != nil { - filter := make([]string, 0) - for i, v := range *f.Owner { - filter = append(filter, fmt.Sprintf("(owner_user_id = :owner_%d)", i)) - params[fmt.Sprintf("owner_%d", i)] = v - } - sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")") - } - if f.ChannelNameCI != nil { filter := make([]string, 0) for i, v := range *f.ChannelNameCI { diff --git a/scnserver/swagger/swagger.json b/scnserver/swagger/swagger.json index 987dd92..3752bc3 100644 --- a/scnserver/swagger/swagger.json +++ b/scnserver/swagger/swagger.json @@ -2952,8 +2952,6 @@ "handler.CreateUserKey.body": { "type": "object", "required": [ - "all_channels", - "channels", "name", "permissions" ], @@ -3574,9 +3572,6 @@ "message_id": { "type": "string" }, - "owner_user_id": { - "type": "string" - }, "priority": { "type": "integer" }, diff --git a/scnserver/swagger/swagger.yaml b/scnserver/swagger/swagger.yaml index ee8998c..a13f694 100644 --- a/scnserver/swagger/swagger.yaml +++ b/scnserver/swagger/swagger.yaml @@ -183,8 +183,6 @@ definitions: permissions: type: string required: - - all_channels - - channels - name - permissions type: object @@ -583,8 +581,6 @@ definitions: type: string message_id: type: string - owner_user_id: - type: string priority: type: integer sender_ip: diff --git a/scnserver/test/database_test.go b/scnserver/test/database_test.go new file mode 100644 index 0000000..1be1884 --- /dev/null +++ b/scnserver/test/database_test.go @@ -0,0 +1,388 @@ +package test + +import ( + "blackforestbytes.com/simplecloudnotifier/db/impl/logs" + "blackforestbytes.com/simplecloudnotifier/db/impl/primary" + "blackforestbytes.com/simplecloudnotifier/db/impl/requests" + "blackforestbytes.com/simplecloudnotifier/db/schema" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" + tt "blackforestbytes.com/simplecloudnotifier/test/util" + "context" + "fmt" + "github.com/jmoiron/sqlx" + "gogs.mikescher.com/BlackForestBytes/goext/sq" + "testing" +) + +func TestPrimaryDB_Current(t *testing.T) { + dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t) + defer stop() + + ctx := context.Background() + + tt.AssertAny(dbf1) + tt.AssertAny(dbf2) + tt.AssertAny(dbf3) + tt.AssertAny(conf) + + { + db1, err := primary.NewPrimaryDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema1, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema1", 0, schema1) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1.Stop(ctx) + tt.TestFailIfErr(t, err) + } + + { + db1New, err := primary.NewPrimaryDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema3, err := db1New.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema3", schema.PrimarySchemaVersion, schema3) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1New.Migrate(ctx) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema4, err := db1New.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema4", schema.PrimarySchemaVersion, schema4) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + } +} + +func TestLogsDB_Current(t *testing.T) { + dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t) + defer stop() + + ctx := context.Background() + + tt.AssertAny(dbf1) + tt.AssertAny(dbf2) + tt.AssertAny(dbf3) + tt.AssertAny(conf) + + { + db1, err := logs.NewLogsDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema1, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema1", 0, schema1) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.LogsSchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.LogsSchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1.Stop(ctx) + tt.TestFailIfErr(t, err) + } + + { + db1New, err := logs.NewLogsDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema3, err := db1New.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema3", schema.LogsSchemaVersion, schema3) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1New.Migrate(ctx) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema4, err := db1New.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema4", schema.LogsSchemaVersion, schema4) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + } +} + +func TestRequestsDB_Current(t *testing.T) { + dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t) + defer stop() + + ctx := context.Background() + + tt.AssertAny(dbf1) + tt.AssertAny(dbf2) + tt.AssertAny(dbf3) + tt.AssertAny(conf) + + { + db1, err := requests.NewRequestsDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema1, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema1", 0, schema1) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.RequestsSchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.RequestsSchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1.Stop(ctx) + tt.TestFailIfErr(t, err) + } + + { + db1New, err := requests.NewRequestsDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema3, err := db1New.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema3", schema.RequestsSchemaVersion, schema3) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1New.Migrate(ctx) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema4, err := db1New.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema4", schema.RequestsSchemaVersion, schema4) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + } +} + +func TestPrimaryDB_Migrate_from_3(t *testing.T) { + dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t) + defer stop() + + ctx := context.Background() + + tt.AssertAny(dbf1) + tt.AssertAny(dbf2) + tt.AssertAny(dbf3) + tt.AssertAny(conf) + + { + url := fmt.Sprintf("file:%s", dbf1) + + xdb, err := sqlx.Open("sqlite3", url) + tt.TestFailIfErr(t, err) + + qqdb := sq.NewDB(xdb) + + schemavers := 3 + + dbschema := schema.PrimarySchema[schemavers] + + _, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{}) + tt.TestFailIfErr(t, err) + + _, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{ + "key": "schema", + "val": schemavers, + }) + + _, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{ + "key": "schema_hash", + "val": dbschema.Hash, + }) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + schemHashDB, err := sq.HashSqliteDatabase(tctx, qqdb) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schemHashDB", dbschema.Hash, schemHashDB) + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = qqdb.Exit() + tt.TestFailIfErr(t, err) + } + + { + db1, err := primary.NewPrimaryDatabase(conf) + tt.TestFailIfErr(t, err) + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema1, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema1", 3, schema1) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + err = db1.Migrate(ctx) + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + + schema2, err := db1.ReadSchema(tctx) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2) + + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + { + tctx := simplectx.CreateSimpleContext(ctx, nil) + schemHashDB, err := sq.HashSqliteDatabase(tctx, db1.DB()) + tt.TestFailIfErr(t, err) + tt.AssertEqual(t, "schemHashDB", schema.PrimarySchema[schema.PrimarySchemaVersion].Hash, schemHashDB) + err = tctx.CommitTransaction() + tt.TestFailIfErr(t, err) + } + + err = db1.Stop(ctx) + tt.TestFailIfErr(t, err) + } +} diff --git a/scnserver/test/util/common.go b/scnserver/test/util/common.go index 0041b9f..6b86f7e 100644 --- a/scnserver/test/util/common.go +++ b/scnserver/test/util/common.go @@ -302,6 +302,10 @@ func AssertArrAny[T any](t *testing.T, key string, arr []T, fn func(T) bool) { } } +func AssertAny(v any) { + // used to prevent golang "unused variable error" +} + func unpointer(v any) any { if v == nil { return v diff --git a/scnserver/test/util/webserver.go b/scnserver/test/util/webserver.go index a4c5247..e15b5bd 100644 --- a/scnserver/test/util/webserver.go +++ b/scnserver/test/util/webserver.go @@ -75,32 +75,9 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) { TPrintln("DatabaseFile: " + dbfile2) TPrintln("DatabaseFile: " + dbfile3) - conf, ok := scn.GetConfig("local-host") - if !ok { - TestFail(t, "conf not found") - } + scn.Conf = CreateTestConfig(t, dbfile1, dbfile2, dbfile3) - conf.ServerPort = "0" // simply choose a free port - conf.DBMain.File = dbfile1 - conf.DBLogs.File = dbfile2 - conf.DBRequests.File = dbfile3 - conf.DBMain.Timeout = 500 * time.Millisecond - conf.DBLogs.Timeout = 500 * time.Millisecond - conf.DBRequests.Timeout = 500 * time.Millisecond - conf.DBMain.ConnMaxLifetime = 1 * time.Second - conf.DBLogs.ConnMaxLifetime = 1 * time.Second - conf.DBRequests.ConnMaxLifetime = 1 * time.Second - conf.DBMain.ConnMaxIdleTime = 1 * time.Second - conf.DBLogs.ConnMaxIdleTime = 1 * time.Second - conf.DBRequests.ConnMaxIdleTime = 1 * time.Second - conf.RequestMaxRetry = 32 - conf.RequestRetrySleep = 100 * time.Millisecond - conf.ReturnRawErrors = true - conf.DummyFirebase = true - - scn.Conf = conf - - sqlite, err := logic.NewDBPool(conf) + sqlite, err := logic.NewDBPool(scn.Conf) if err != nil { TestFailErr(t, err) } @@ -111,7 +88,7 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) { TestFailErr(t, err) } - ginengine := ginext.NewEngine(conf) + ginengine := ginext.NewEngine(scn.Conf) router := api.NewRouter(app) @@ -119,7 +96,7 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) { apc := google.NewDummy() - app.Init(conf, ginengine, nc, apc, []logic.Job{ + app.Init(scn.Conf, ginengine, nc, apc, []logic.Job{ jobs.NewDeliveryRetryJob(app), jobs.NewRequestLogCollectorJob(app), }) @@ -148,3 +125,99 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) { return app, "http://127.0.0.1:" + app.Port, stop } + +func StartSimpleTestspace(t *testing.T) (string, string, string, scn.Config, func()) { + InitTests() + + uuid1, _ := langext.NewHexUUID() + uuid2, _ := langext.NewHexUUID() + uuid3, _ := langext.NewHexUUID() + + dbdir := t.TempDir() + dbfile1 := filepath.Join(dbdir, uuid1+".sqlite3") + dbfile2 := filepath.Join(dbdir, uuid2+".sqlite3") + dbfile3 := filepath.Join(dbdir, uuid3+".sqlite3") + + err := os.MkdirAll(dbdir, os.ModePerm) + if err != nil { + TestFailErr(t, err) + } + + f1, err := os.Create(dbfile1) + if err != nil { + TestFailErr(t, err) + } + err = f1.Close() + if err != nil { + TestFailErr(t, err) + } + err = os.Chmod(dbfile1, 0777) + if err != nil { + TestFailErr(t, err) + } + f2, err := os.Create(dbfile2) + if err != nil { + TestFailErr(t, err) + } + err = f2.Close() + if err != nil { + TestFailErr(t, err) + } + err = os.Chmod(dbfile2, 0777) + if err != nil { + TestFailErr(t, err) + } + f3, err := os.Create(dbfile3) + if err != nil { + TestFailErr(t, err) + } + err = f3.Close() + if err != nil { + TestFailErr(t, err) + } + err = os.Chmod(dbfile3, 0777) + if err != nil { + TestFailErr(t, err) + } + + TPrintln("DatabaseFile
: " + dbfile1) + TPrintln("DatabaseFile: " + dbfile2) + TPrintln("DatabaseFile: " + dbfile3) + + scn.Conf = CreateTestConfig(t, dbfile1, dbfile2, dbfile3) + + stop := func() { + _ = os.Remove(dbfile1) + _ = os.Remove(dbfile2) + _ = os.Remove(dbfile3) + } + + return dbfile1, dbfile2, dbfile3, scn.Conf, stop +} + +func CreateTestConfig(t *testing.T, dbfile1 string, dbfile2 string, dbfile3 string) scn.Config { + conf, ok := scn.GetConfig("local-host") + if !ok { + TestFail(t, "conf not found") + } + + conf.ServerPort = "0" // simply choose a free port + conf.DBMain.File = dbfile1 + conf.DBLogs.File = dbfile2 + conf.DBRequests.File = dbfile3 + conf.DBMain.Timeout = 500 * time.Millisecond + conf.DBLogs.Timeout = 500 * time.Millisecond + conf.DBRequests.Timeout = 500 * time.Millisecond + conf.DBMain.ConnMaxLifetime = 1 * time.Second + conf.DBLogs.ConnMaxLifetime = 1 * time.Second + conf.DBRequests.ConnMaxLifetime = 1 * time.Second + conf.DBMain.ConnMaxIdleTime = 1 * time.Second + conf.DBLogs.ConnMaxIdleTime = 1 * time.Second + conf.DBRequests.ConnMaxIdleTime = 1 * time.Second + conf.RequestMaxRetry = 32 + conf.RequestRetrySleep = 100 * time.Millisecond + conf.ReturnRawErrors = true + conf.DummyFirebase = true + + return conf +}