Remove message.owner_user_id field and implement db migrations

This commit is contained in:
Mike Schwörer 2023-07-27 17:44:06 +02:00
parent 308361a834
commit 8a6719fc19
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
36 changed files with 1381 additions and 357 deletions

View File

@ -3,6 +3,7 @@ package handler
import ( import (
"blackforestbytes.com/simplecloudnotifier/api/apierr" "blackforestbytes.com/simplecloudnotifier/api/apierr"
"blackforestbytes.com/simplecloudnotifier/api/ginresp" "blackforestbytes.com/simplecloudnotifier/api/ginresp"
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
"blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/logic"
"bytes" "bytes"
"context" "context"
@ -127,7 +128,9 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse {
return ginresp.InternalError(errors.New("sqlite version too low")) 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 { if err != nil {
return ginresp.InternalError(err) return ginresp.InternalError(err)
} }
@ -137,12 +140,12 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse {
uuidKey, _ := langext.NewHexUUID() uuidKey, _ := langext.NewHexUUID()
uuidWrite, _ := langext.NewHexUUID() uuidWrite, _ := langext.NewHexUUID()
err = subdb.WriteMetaString(ctx, uuidKey, uuidWrite) err = subdb.WriteMetaString(tctx, uuidKey, uuidWrite)
if err != nil { if err != nil {
return ginresp.InternalError(err) return ginresp.InternalError(err)
} }
uuidRead, err := subdb.ReadMetaString(ctx, uuidKey) uuidRead, err := subdb.ReadMetaString(tctx, uuidKey)
if err != nil { if err != nil {
return ginresp.InternalError(err) 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")) return ginresp.InternalError(errors.New("writing into DB was not consistent"))
} }
err = subdb.DeleteMeta(ctx, uuidKey) err = subdb.DeleteMeta(tctx, uuidKey)
if err != nil { if err != nil {
return ginresp.InternalError(err) return ginresp.InternalError(err)
} }

View File

@ -311,7 +311,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
} }
filter := models.MessageFilter{ filter := models.MessageFilter{
Owner: langext.Ptr([]models.UserID{user.UserID}), Sender: langext.Ptr([]models.UserID{user.UserID}),
CompatAcknowledged: langext.Ptr(false), CompatAcknowledged: langext.Ptr(false),
} }
@ -516,7 +516,7 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse {
} }
filter := models.MessageFilter{ filter := models.MessageFilter{
Owner: langext.Ptr([]models.UserID{user.UserID}), Sender: langext.Ptr([]models.UserID{user.UserID}),
CompatAcknowledged: langext.Ptr(false), CompatAcknowledged: langext.Ptr(false),
} }

View File

@ -36,6 +36,13 @@ func main() {
} }
fmt.Printf("PrimarySchema3 := %s\n", h0) 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) h0, err := sq.HashSqliteSchema(ctx, schema.RequestsSchema1)
if err != nil { if err != nil {

View File

@ -1,7 +1,6 @@
package primary package db
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "time"
) )
@ -12,5 +11,5 @@ type TxContext interface {
Err() error Err() error
Value(key any) any Value(key any) any
GetOrCreateTransaction(db db.DatabaseImpl) (sq.Tx, error) GetOrCreateTransaction(db DatabaseImpl) (sq.Tx, error)
} }

View File

@ -13,17 +13,17 @@ type DatabaseImpl interface {
BeginTx(ctx context.Context) (sq.Tx, error) BeginTx(ctx context.Context) (sq.Tx, error)
Stop(ctx context.Context) 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 WriteMetaString(ctx TxContext, key string, value string) error
WriteMetaInt(ctx context.Context, key string, value int64) error WriteMetaInt(ctx TxContext, key string, value int64) error
WriteMetaReal(ctx context.Context, key string, value float64) error WriteMetaReal(ctx TxContext, key string, value float64) error
WriteMetaBlob(ctx context.Context, key string, value []byte) error WriteMetaBlob(ctx TxContext, key string, value []byte) error
ReadMetaString(ctx context.Context, key string) (*string, error) ReadMetaString(ctx TxContext, key string) (*string, error)
ReadMetaInt(ctx context.Context, key string) (*int64, error) ReadMetaInt(ctx TxContext, key string) (*int64, error)
ReadMetaReal(ctx context.Context, key string) (*float64, error) ReadMetaReal(ctx TxContext, key string) (*float64, error)
ReadMetaBlob(ctx context.Context, key string) (*[]byte, error) ReadMetaBlob(ctx TxContext, key string) (*[]byte, error)
DeleteMeta(ctx context.Context, key string) error DeleteMeta(ctx TxContext, key string) error
} }

View File

@ -4,6 +4,7 @@ import (
server "blackforestbytes.com/simplecloudnotifier" server "blackforestbytes.com/simplecloudnotifier"
"blackforestbytes.com/simplecloudnotifier/db/dbtools" "blackforestbytes.com/simplecloudnotifier/db/dbtools"
"blackforestbytes.com/simplecloudnotifier/db/schema" "blackforestbytes.com/simplecloudnotifier/db/schema"
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
@ -63,77 +64,93 @@ func (db *Database) DB() sq.DB {
return db.db return db.db
} }
func (db *Database) Migrate(ctx context.Context) error { func (db *Database) Migrate(outerctx context.Context) error {
ctx, cancel := context.WithTimeout(context.Background(), 24*time.Second) innerctx, cancel := context.WithTimeout(outerctx, 24*time.Second)
defer cancel() 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 { if err != nil {
return err return err
} }
if currschema == 0 { if currschema == 0 {
schemastr := schema.LogsSchema[schema.LogsSchemaVersion].SQL
schemahash := schema.LogsSchema[schema.LogsSchemaVersion].Hash
schemastr := schema.LogsSchema1 _, err = tx.Exec(tctx, schemastr, sq.PP{})
schemahash, err := sq.HashSqliteSchema(ctx, schemastr)
if err != nil { if err != nil {
return err return err
} }
_, err = db.db.Exec(ctx, schemastr, sq.PP{}) err = db.WriteMetaInt(tctx, "schema", int64(schema.LogsSchemaVersion))
if err != nil { if err != nil {
return err return err
} }
err = db.WriteMetaInt(ctx, "schema", 1) err = db.WriteMetaString(tctx, "schema_hash", schemahash)
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
err = db.pp.Init(ctx) // Re-Init schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash")
if err != nil { if err != nil {
return err return err
} }
return nil if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.LogsSchema[currschema].Hash {
} 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 {
log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (logs db)") log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (logs db)")
log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).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)") return errors.New("database schema does not match (logs db)")
} else { } else {
log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (logs db)") log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (logs db)")
} }
}
return nil // current if currschema != schema.LogsSchemaVersion {
} else {
return errors.New(fmt.Sprintf("Unknown DB schema: %d", currschema)) 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 { func (db *Database) Ping(ctx context.Context) error {

View File

@ -1,15 +1,19 @@
package logs package logs
import ( import (
"context" "blackforestbytes.com/simplecloudnotifier/db"
"errors" "errors"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "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 { if err != nil {
return 0, err return 0, err
} }
@ -31,7 +35,7 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) {
return 0, err 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 { if err != nil {
return 0, err return 0, err
} }
@ -62,8 +66,13 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) {
return dbschema, nil return dbschema, nil
} }
func (db *Database) WriteMetaString(ctx context.Context, key string, value string) error { func (db *Database) WriteMetaString(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -73,8 +82,13 @@ func (db *Database) WriteMetaString(ctx context.Context, key string, value strin
return nil return nil
} }
func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) error { func (db *Database) WriteMetaInt(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -84,8 +98,13 @@ func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) e
return nil return nil
} }
func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64) error { func (db *Database) WriteMetaReal(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -95,8 +114,13 @@ func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64
return nil return nil
} }
func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) error { func (db *Database) WriteMetaBlob(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -106,8 +130,13 @@ func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte)
return nil return nil
} }
func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *string, reterr error) { func (db *Database) ReadMetaString(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -137,8 +166,13 @@ func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *str
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, reterr error) { func (db *Database) ReadMetaInt(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -169,8 +203,13 @@ func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64,
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float64, reterr error) { func (db *Database) ReadMetaReal(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -201,8 +240,13 @@ func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byte, reterr error) { func (db *Database) ReadMetaBlob(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -233,8 +277,13 @@ func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byt
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) DeleteMeta(ctx context.Context, key string) error { func (db *Database) DeleteMeta(ctx db.TxContext, key string) error {
_, err := db.db.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) 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 { if err != nil {
return err return err
} }

View File

@ -1,13 +1,14 @@
package primary package primary
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"database/sql" "database/sql"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -32,7 +33,7 @@ func (db *Database) GetChannelByName(ctx TxContext, userid models.UserID, chanNa
return &channel, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -56,7 +57,7 @@ func (db *Database) GetChannelByID(ctx TxContext, chanid models.ChannelID) (*mod
return &channel, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Channel{}, err return models.Channel{}, err
@ -81,7 +82,7 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -105,7 +106,7 @@ func (db *Database) ListChannelsByOwner(ctx TxContext, userid models.UserID, sub
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -135,7 +136,7 @@ func (db *Database) ListChannelsBySubscriber(ctx TxContext, userid models.UserID
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -166,7 +167,7 @@ func (db *Database) ListChannelsByAccess(ctx TxContext, userid models.UserID, co
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.ChannelWithSubscription{}, err return models.ChannelWithSubscription{}, err
@ -200,7 +201,7 @@ func (db *Database) GetChannel(ctx TxContext, userid models.UserID, channelid mo
return channel, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -222,7 +223,7 @@ func (db *Database) IncChannelMessageCounter(ctx TxContext, channel *models.Chan
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -239,7 +240,7 @@ func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.Ch
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -256,7 +257,7 @@ func (db *Database) UpdateChannelDisplayName(ctx TxContext, channelid models.Cha
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err

View File

@ -1,12 +1,13 @@
package primary package primary
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Client{}, err return models.Client{}, err
@ -30,7 +31,7 @@ func (db *Database) CreateClient(ctx TxContext, userid models.UserID, ctype mode
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -44,7 +45,7 @@ func (db *Database) ClearFCMTokens(ctx TxContext, fcmtoken string) error {
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -63,7 +64,7 @@ func (db *Database) ListClients(ctx TxContext, userid models.UserID) ([]models.C
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Client{}, err return models.Client{}, err
@ -85,7 +86,7 @@ func (db *Database) GetClient(ctx TxContext, userid models.UserID, clientid mode
return client, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -99,7 +100,7 @@ func (db *Database) DeleteClient(ctx TxContext, clientid models.ClientID) error
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -113,7 +114,7 @@ func (db *Database) DeleteClientsByFCM(ctx TxContext, fcmtoken string) error {
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -130,7 +131,7 @@ func (db *Database) UpdateClientFCMToken(ctx TxContext, clientid models.ClientID
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -147,7 +148,7 @@ func (db *Database) UpdateClientAgentModel(ctx TxContext, clientid models.Client
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err

View File

@ -1,13 +1,14 @@
package primary package primary
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"database/sql" "database/sql"
"errors" "errors"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return 0, err return 0, err
@ -42,7 +43,7 @@ func (db *Database) CreateCompatID(ctx TxContext, idtype string, newid string) (
return oldid, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -72,7 +73,7 @@ func (db *Database) ConvertCompatID(ctx TxContext, oldid int64, idtype string) (
return &newid, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -100,7 +101,7 @@ func (db *Database) ConvertToCompatID(ctx TxContext, newid string) (*int64, *str
return &oldid, &idtype, nil 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) id1, _, err := db.ConvertToCompatID(ctx, newid)
if err != nil { if err != nil {
return 0, err return 0, err
@ -116,7 +117,7 @@ func (db *Database) ConvertToCompatIDOrCreate(ctx TxContext, idtype string, newi
return id2, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return false, err return false, err
@ -139,7 +140,7 @@ func (db *Database) GetAck(ctx TxContext, msgid models.MessageID) (bool, error)
return res, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -156,7 +157,7 @@ func (db *Database) SetAck(ctx TxContext, userid models.UserID, msgid models.Mes
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return false, err return false, err

View File

@ -4,6 +4,7 @@ import (
server "blackforestbytes.com/simplecloudnotifier" server "blackforestbytes.com/simplecloudnotifier"
"blackforestbytes.com/simplecloudnotifier/db/dbtools" "blackforestbytes.com/simplecloudnotifier/db/dbtools"
"blackforestbytes.com/simplecloudnotifier/db/schema" "blackforestbytes.com/simplecloudnotifier/db/schema"
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
@ -63,81 +64,147 @@ func (db *Database) DB() sq.DB {
return db.db return db.db
} }
func (db *Database) Migrate(ctx context.Context) error { func (db *Database) Migrate(outerctx context.Context) error {
ctx, cancel := context.WithTimeout(context.Background(), 24*time.Second) innerctx, cancel := context.WithTimeout(outerctx, 24*time.Second)
defer cancel() 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 { if err != nil {
return err return err
} }
if currschema == 0 { if currschema == 0 {
schemastr := schema.PrimarySchema[schema.PrimarySchemaVersion].SQL
schemahash := schema.PrimarySchema[schema.PrimarySchemaVersion].Hash
schemastr := schema.PrimarySchema3 _, err = tx.Exec(tctx, schemastr, sq.PP{})
schemahash, err := sq.HashSqliteSchema(ctx, schemastr)
if err != nil { if err != nil {
return err return err
} }
_, err = db.db.Exec(ctx, schemastr, sq.PP{}) err = db.WriteMetaInt(tctx, "schema", int64(schema.PrimarySchemaVersion))
if err != nil { if err != nil {
return err return err
} }
err = db.WriteMetaInt(ctx, "schema", 3) err = db.WriteMetaString(tctx, "schema_hash", schemahash)
if err != nil { if err != nil {
return err return err
} }
err = db.WriteMetaString(ctx, "schema_hash", schemahash) ppReInit = true
if err != nil {
return err currschema = schema.PrimarySchemaVersion
} }
err = db.pp.Init(ctx) // Re-Init if currschema == 1 {
if err != nil {
return err
}
return nil
} else if currschema == 1 {
return errors.New("cannot autom. upgrade schema 1") return errors.New("cannot autom. upgrade schema 1")
} else if currschema == 2 { }
if currschema == 2 {
return errors.New("cannot autom. upgrade schema 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 { if err != nil {
return err return err
} }
schemaHashMeta, err := db.ReadMetaString(ctx, "schema_hash") schemHashDB, err := sq.HashSqliteDatabase(tctx, tx)
if err != nil { if err != nil {
return err return err
} }
schemHashAsset := schema.PrimaryHash3 if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.PrimarySchema[currschema].Hash {
if err != nil {
return err
}
if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schemHashAsset {
log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (primary db)") log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (primary db)")
log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).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)") return errors.New("database schema does not match (primary db)")
} else { } else {
log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)") log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)")
} }
return nil // current 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 { } 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)) 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 { func (db *Database) Ping(ctx context.Context) error {

View File

@ -2,13 +2,14 @@ package primary
import ( import (
scn "blackforestbytes.com/simplecloudnotifier" scn "blackforestbytes.com/simplecloudnotifier"
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Delivery{}, err return models.Delivery{}, err
@ -38,7 +39,7 @@ func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Delivery{}, err return models.Delivery{}, err
@ -67,7 +68,7 @@ func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, m
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -89,7 +90,7 @@ func (db *Database) ListRetrieableDeliveries(ctx TxContext, pageSize int) ([]mod
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -108,7 +109,7 @@ func (db *Database) SetDeliverySuccess(ctx TxContext, delivery models.Delivery,
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -127,7 +128,7 @@ func (db *Database) SetDeliveryFailed(ctx TxContext, delivery models.Delivery) e
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -145,7 +146,7 @@ func (db *Database) SetDeliveryRetry(ctx TxContext, delivery models.Delivery) er
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err

View File

@ -1,6 +1,7 @@
package primary package primary
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"database/sql" "database/sql"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
@ -9,7 +10,7 @@ import (
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.KeyToken{}, err return models.KeyToken{}, err
@ -36,7 +37,7 @@ func (db *Database) CreateKeyToken(ctx TxContext, name string, owner models.User
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -55,7 +56,7 @@ func (db *Database) ListKeyTokens(ctx TxContext, ownerID models.UserID) ([]model
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.KeyToken{}, err return models.KeyToken{}, err
@ -77,7 +78,7 @@ func (db *Database) GetKeyToken(ctx TxContext, userid models.UserID, keyTokenid
return keyToken, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -99,7 +100,7 @@ func (db *Database) GetKeyTokenByToken(ctx TxContext, key string) (*models.KeyTo
return &user, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -113,7 +114,7 @@ func (db *Database) DeleteKeyToken(ctx TxContext, keyTokenid models.KeyTokenID)
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -130,7 +131,7 @@ func (db *Database) UpdateKeyTokenName(ctx TxContext, keyTokenid models.KeyToken
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -147,7 +148,7 @@ func (db *Database) UpdateKeyTokenPermissions(ctx TxContext, keyTokenid models.K
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -164,7 +165,7 @@ func (db *Database) UpdateKeyTokenAllChannels(ctx TxContext, keyTokenid models.K
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -181,7 +182,7 @@ func (db *Database) UpdateKeyTokenChannels(ctx TxContext, keyTokenid models.KeyT
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -203,7 +204,7 @@ func (db *Database) IncKeyTokenMessageCounter(ctx TxContext, keyToken *models.Ke
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err

View File

@ -1,6 +1,7 @@
package primary package primary
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken" ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"database/sql" "database/sql"
@ -9,7 +10,7 @@ import (
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -31,7 +32,7 @@ func (db *Database) GetMessageByUserMessageID(ctx TxContext, usrMsgId string) (*
return &msg, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Message{}, err return models.Message{}, err
@ -57,7 +58,7 @@ func (db *Database) GetMessage(ctx TxContext, scnMessageID models.MessageID, all
return msg, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Message{}, err return models.Message{}, err
@ -66,7 +67,6 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha
entity := models.MessageDB{ entity := models.MessageDB{
MessageID: models.NewMessageID(), MessageID: models.NewMessageID(),
SenderUserID: senderUserID, SenderUserID: senderUserID,
OwnerUserID: channel.OwnerUserID,
ChannelInternalName: channel.InternalName, ChannelInternalName: channel.InternalName,
ChannelID: channel.ChannelID, ChannelID: channel.ChannelID,
SenderIP: senderIP, SenderIP: senderIP,
@ -89,7 +89,7 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID models.UserID, cha
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -103,7 +103,7 @@ func (db *Database) DeleteMessage(ctx TxContext, messageID models.MessageID) err
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, ct.CursorToken{}, err 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -1,15 +1,19 @@
package primary package primary
import ( import (
"context" "blackforestbytes.com/simplecloudnotifier/db"
"errors" "errors"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "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 { if err != nil {
return 0, err return 0, err
} }
@ -31,7 +35,7 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) {
return 0, err 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 { if err != nil {
return 0, err return 0, err
} }
@ -62,8 +66,13 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) {
return dbschema, nil return dbschema, nil
} }
func (db *Database) WriteMetaString(ctx context.Context, key string, value string) error { func (db *Database) WriteMetaString(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -73,8 +82,13 @@ func (db *Database) WriteMetaString(ctx context.Context, key string, value strin
return nil return nil
} }
func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) error { func (db *Database) WriteMetaInt(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -84,8 +98,13 @@ func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) e
return nil return nil
} }
func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64) error { func (db *Database) WriteMetaReal(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -95,8 +114,13 @@ func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64
return nil return nil
} }
func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) error { func (db *Database) WriteMetaBlob(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -106,8 +130,13 @@ func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte)
return nil return nil
} }
func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *string, reterr error) { func (db *Database) ReadMetaString(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -137,8 +166,13 @@ func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *str
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, reterr error) { func (db *Database) ReadMetaInt(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -169,8 +203,13 @@ func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64,
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float64, reterr error) { func (db *Database) ReadMetaReal(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -201,8 +240,13 @@ func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byte, reterr error) { func (db *Database) ReadMetaBlob(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -233,8 +277,13 @@ func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byt
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) DeleteMeta(ctx context.Context, key string) error { func (db *Database) DeleteMeta(ctx db.TxContext, key string) error {
_, err := db.db.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) 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 { if err != nil {
return err return err
} }

View File

@ -1,13 +1,14 @@
package primary package primary
import ( import (
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"database/sql" "database/sql"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Subscription{}, err return models.Subscription{}, err
@ -31,7 +32,7 @@ func (db *Database) CreateSubscription(ctx TxContext, subscriberUID models.UserI
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -52,7 +53,7 @@ func (db *Database) ListSubscriptionsByChannel(ctx TxContext, channelID models.C
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -80,7 +81,7 @@ func (db *Database) ListSubscriptionsByChannelOwner(ctx TxContext, ownerUserID m
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -108,7 +109,7 @@ func (db *Database) ListSubscriptionsBySubscriber(ctx TxContext, subscriberUserI
return data, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Subscription{}, err return models.Subscription{}, err
@ -127,7 +128,7 @@ func (db *Database) GetSubscription(ctx TxContext, subid models.SubscriptionID)
return sub, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -152,7 +153,7 @@ func (db *Database) GetSubscriptionBySubscriber(ctx TxContext, subscriberId mode
return &user, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -166,7 +167,7 @@ func (db *Database) DeleteSubscription(ctx TxContext, subid models.SubscriptionI
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err

View File

@ -2,13 +2,14 @@ package primary
import ( import (
scn "blackforestbytes.com/simplecloudnotifier" scn "blackforestbytes.com/simplecloudnotifier"
"blackforestbytes.com/simplecloudnotifier/db"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.User{}, err return models.User{}, err
@ -35,7 +36,7 @@ func (db *Database) CreateUser(ctx TxContext, protoken *string, username *string
return entity.Model(), nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -49,7 +50,7 @@ func (db *Database) ClearProTokens(ctx TxContext, protoken string) error {
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.User{}, err return models.User{}, err
@ -68,7 +69,7 @@ func (db *Database) GetUser(ctx TxContext, userid models.UserID) (models.User, e
return user, nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -85,7 +86,7 @@ func (db *Database) UpdateUserUsername(ctx TxContext, userid models.UserID, user
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -103,7 +104,7 @@ func (db *Database) UpdateUserProToken(ctx TxContext, userid models.UserID, prot
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err
@ -132,7 +133,7 @@ func (db *Database) IncUserMessageCounter(ctx TxContext, user *models.User) erro
return nil 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) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return err return err

View File

@ -4,6 +4,7 @@ import (
server "blackforestbytes.com/simplecloudnotifier" server "blackforestbytes.com/simplecloudnotifier"
"blackforestbytes.com/simplecloudnotifier/db/dbtools" "blackforestbytes.com/simplecloudnotifier/db/dbtools"
"blackforestbytes.com/simplecloudnotifier/db/schema" "blackforestbytes.com/simplecloudnotifier/db/schema"
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
@ -63,77 +64,98 @@ func (db *Database) DB() sq.DB {
return db.db return db.db
} }
func (db *Database) Migrate(ctx context.Context) error { func (db *Database) Migrate(outerctx context.Context) error {
ctx, cancel := context.WithTimeout(context.Background(), 24*time.Second) innerctx, cancel := context.WithTimeout(outerctx, 24*time.Second)
defer cancel() 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 { if err != nil {
return err return err
} }
if currschema == 0 { if currschema == 0 {
schemastr := schema.RequestsSchema[schema.RequestsSchemaVersion].SQL
schemahash := schema.RequestsSchema[schema.RequestsSchemaVersion].Hash
schemastr := schema.RequestsSchema1 schemahash, err := sq.HashSqliteSchema(tctx, schemastr)
schemahash, err := sq.HashSqliteSchema(ctx, schemastr)
if err != nil { if err != nil {
return err return err
} }
_, err = db.db.Exec(ctx, schemastr, sq.PP{}) _, err = tx.Exec(tctx, schemastr, sq.PP{})
if err != nil { if err != nil {
return err return err
} }
err = db.WriteMetaInt(ctx, "schema", 1) err = db.WriteMetaInt(tctx, "schema", int64(schema.RequestsSchemaVersion))
if err != nil { if err != nil {
return err return err
} }
err = db.WriteMetaString(ctx, "schema_hash", schemahash) err = db.WriteMetaString(tctx, "schema_hash", schemahash)
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
return nil schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash")
} else if currschema == 1 {
schemHashDB, err := sq.HashSqliteDatabase(ctx, db.db)
if err != nil { if err != nil {
return err return err
} }
schemaHashMeta, err := db.ReadMetaString(ctx, "schema_hash") if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.RequestsSchema[currschema].Hash {
if err != nil {
return err
}
schemHashAsset := schema.RequestsHash1
if err != nil {
return err
}
if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schemHashAsset {
log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (requests db)") log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (requests db)")
log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).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)") return errors.New("database schema does not match (requests db)")
} else { } else {
log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (requests db)") log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (requests db)")
} }
}
return nil // current if currschema != schema.RequestsSchemaVersion {
} else {
return errors.New(fmt.Sprintf("Unknown DB schema: %d", currschema)) 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 { func (db *Database) Ping(ctx context.Context) error {

View File

@ -1,15 +1,19 @@
package requests package requests
import ( import (
"context" "blackforestbytes.com/simplecloudnotifier/db"
"errors" "errors"
"gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "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 { if err != nil {
return 0, err return 0, err
} }
@ -31,7 +35,7 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) {
return 0, err 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 { if err != nil {
return 0, err return 0, err
} }
@ -62,8 +66,13 @@ func (db *Database) ReadSchema(ctx context.Context) (retval int, reterr error) {
return dbschema, nil return dbschema, nil
} }
func (db *Database) WriteMetaString(ctx context.Context, key string, value string) error { func (db *Database) WriteMetaString(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -73,8 +82,13 @@ func (db *Database) WriteMetaString(ctx context.Context, key string, value strin
return nil return nil
} }
func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) error { func (db *Database) WriteMetaInt(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -84,8 +98,13 @@ func (db *Database) WriteMetaInt(ctx context.Context, key string, value int64) e
return nil return nil
} }
func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64) error { func (db *Database) WriteMetaReal(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -95,8 +114,13 @@ func (db *Database) WriteMetaReal(ctx context.Context, key string, value float64
return nil return nil
} }
func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte) error { func (db *Database) WriteMetaBlob(ctx db.TxContext, 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{ 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, "key": key,
"val": value, "val": value,
}) })
@ -106,8 +130,13 @@ func (db *Database) WriteMetaBlob(ctx context.Context, key string, value []byte)
return nil return nil
} }
func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *string, reterr error) { func (db *Database) ReadMetaString(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -137,8 +166,13 @@ func (db *Database) ReadMetaString(ctx context.Context, key string) (retval *str
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64, reterr error) { func (db *Database) ReadMetaInt(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -169,8 +203,13 @@ func (db *Database) ReadMetaInt(ctx context.Context, key string) (retval *int64,
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float64, reterr error) { func (db *Database) ReadMetaReal(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -201,8 +240,13 @@ func (db *Database) ReadMetaReal(ctx context.Context, key string) (retval *float
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byte, reterr error) { func (db *Database) ReadMetaBlob(ctx db.TxContext, 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}) 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 { if err != nil {
return nil, err return nil, err
} }
@ -233,8 +277,13 @@ func (db *Database) ReadMetaBlob(ctx context.Context, key string) (retval *[]byt
return langext.Ptr(value), nil return langext.Ptr(value), nil
} }
func (db *Database) DeleteMeta(ctx context.Context, key string) error { func (db *Database) DeleteMeta(ctx db.TxContext, key string) error {
_, err := db.db.Exec(ctx, "DELETE FROM meta WHERE meta_key = :key", sq.PP{"key": key}) 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 { if err != nil {
return err return err
} }

View File

@ -2,27 +2,52 @@ package schema
import _ "embed" import _ "embed"
//go:embed primary_1.ddl type Def struct {
var PrimarySchema1 string SQL string
Hash string
}
const PrimaryHash1 = "f2b2847f32681a7178e405553beea4a324034915a0c5a5dc70b3c6abbcc852f2" //go:embed primary_1.ddl
var primarySchema1 string
//go:embed primary_2.ddl //go:embed primary_2.ddl
var PrimarySchema2 string var primarySchema2 string
const PrimaryHash2 = "07ed1449114416ed043084a30e0722a5f97bf172161338d2f7106a8dfd387d0a"
//go:embed primary_3.ddl //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 //go:embed requests_1.ddl
var RequestsSchema1 string var requestsSchema1 string
const RequestsHash1 = "ebb0a5748b605e8215437413b738279670190ca8159b6227cfc2aa13418b41e9"
//go:embed logs_1.ddl //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

View File

@ -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)

View File

@ -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;

View File

@ -1,4 +1,4 @@
package logic package simplectx
import ( import (
"blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/db"
@ -51,8 +51,10 @@ func (sc *SimpleContext) Cancel() {
} }
sc.transaction = nil sc.transaction = nil
} }
if sc.cancelFunc != nil {
sc.cancelFunc() sc.cancelFunc()
} }
}
func (sc *SimpleContext) GetOrCreateTransaction(db db.DatabaseImpl) (sq.Tx, error) { func (sc *SimpleContext) GetOrCreateTransaction(db db.DatabaseImpl) (sq.Tx, error) {
if sc.cancelled { if sc.cancelled {

View File

@ -9,13 +9,14 @@ require (
github.com/jmoiron/sqlx v1.3.5 github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-sqlite3 v1.14.17 github.com/mattn/go-sqlite3 v1.14.17
github.com/rs/zerolog v1.29.1 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 gopkg.in/loremipsum.v1 v1.1.2
) )
require ( require (
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.10.0-rc3 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // 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/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // 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/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // 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/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.4.0 // indirect
golang.org/x/crypto v0.10.0 // indirect golang.org/x/crypto v0.11.0 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.12.0 // indirect
golang.org/x/sys v0.9.0 // indirect golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.9.0 // indirect golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@ -1,9 +1,16 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 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 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 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-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 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-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/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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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.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 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 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 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= 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/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 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.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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 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.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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 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 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 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 h1:GYC34wLOdBM/CgAov0AyznfHGd09Km106Ijmp8cZmp4=
gogs.mikescher.com/BlackForestBytes/goext v0.0.163/go.mod h1:Tood+vqmPqS/meYRnUcGz837wqHkP8BykVpY1h8TWoI= 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.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 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 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 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= 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 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 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-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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= 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.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 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= 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 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= 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.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 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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= 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -1,6 +1,7 @@
package jobs package jobs
import ( import (
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
"blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/logic"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"errors" "errors"
@ -132,7 +133,7 @@ func (j *DeliveryRetryJob) execute() (fastrr bool, err error) {
return len(deliveries) == 32, nil 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) client, err := j.app.Database.Primary.GetClient(ctx, delivery.ReceiverUserID, delivery.ReceiverClientID)
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@ import (
scn "blackforestbytes.com/simplecloudnotifier" scn "blackforestbytes.com/simplecloudnotifier"
"blackforestbytes.com/simplecloudnotifier/api/apierr" "blackforestbytes.com/simplecloudnotifier/api/apierr"
"blackforestbytes.com/simplecloudnotifier/api/ginresp" "blackforestbytes.com/simplecloudnotifier/api/ginresp"
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
"blackforestbytes.com/simplecloudnotifier/google" "blackforestbytes.com/simplecloudnotifier/google"
"blackforestbytes.com/simplecloudnotifier/models" "blackforestbytes.com/simplecloudnotifier/models"
"blackforestbytes.com/simplecloudnotifier/push" "blackforestbytes.com/simplecloudnotifier/push"
@ -292,9 +293,9 @@ func (app *Application) StartRequest(g *gin.Context, uri any, query any, body an
return actx, nil 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) 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) { func (app *Application) getPermissions(ctx *AppContext, hdr string) (models.PermissionSet, error) {

View File

@ -99,7 +99,7 @@ func (ac *AppContext) CheckPermissionMessageRead(msg models.Message) bool {
func (ac *AppContext) CheckPermissionMessageDelete(msg models.Message) bool { func (ac *AppContext) CheckPermissionMessageDelete(msg models.Message) bool {
p := ac.permissions p := ac.permissions
if p.Token != nil && p.Token.IsAdmin(msg.OwnerUserID) { if p.Token != nil && p.Token.IsAdmin(msg.SenderUserID) {
return true return true
} }

View File

@ -4,7 +4,7 @@ package models
import "gogs.mikescher.com/BlackForestBytes/goext/langext" import "gogs.mikescher.com/BlackForestBytes/goext/langext"
const ChecksumGenerator = "4bfd61daa179e1452035a34c25c6f8170a08500bc0a7aa0e3981f95ad4b0d7d2" const ChecksumGenerator = "a1a684aa30d77d9a9936ccbb667b498c370a1f816273e9cd93948f4195155e90"
type Enum interface { type Enum interface {
Valid() bool Valid() bool
@ -59,10 +59,7 @@ func (e ClientType) ValuesAny() []any {
} }
func (e ClientType) ValuesMeta() []EnumMetaValue { func (e ClientType) ValuesMeta() []EnumMetaValue {
return []EnumMetaValue{ return ClientTypeValuesMeta()
EnumMetaValue{VarName: "ClientTypeAndroid", Value: ClientTypeAndroid, Description: nil},
EnumMetaValue{VarName: "ClientTypeIOS", Value: ClientTypeIOS, Description: nil},
}
} }
func (e ClientType) String() string { func (e ClientType) String() string {
@ -76,6 +73,10 @@ func (e ClientType) VarName() string {
return "" return ""
} }
func (e ClientType) Meta() EnumMetaValue {
return EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}
}
func ParseClientType(vv string) (ClientType, bool) { func ParseClientType(vv string) (ClientType, bool) {
for _, ev := range __ClientTypeValues { for _, ev := range __ClientTypeValues {
if string(ev) == vv { if string(ev) == vv {
@ -91,8 +92,8 @@ func ClientTypeValues() []ClientType {
func ClientTypeValuesMeta() []EnumMetaValue { func ClientTypeValuesMeta() []EnumMetaValue {
return []EnumMetaValue{ return []EnumMetaValue{
EnumMetaValue{VarName: "ClientTypeAndroid", Value: ClientTypeAndroid, Description: nil}, ClientTypeAndroid.Meta(),
EnumMetaValue{VarName: "ClientTypeIOS", Value: ClientTypeIOS, Description: nil}, ClientTypeIOS.Meta(),
} }
} }
@ -128,11 +129,7 @@ func (e DeliveryStatus) ValuesAny() []any {
} }
func (e DeliveryStatus) ValuesMeta() []EnumMetaValue { func (e DeliveryStatus) ValuesMeta() []EnumMetaValue {
return []EnumMetaValue{ return DeliveryStatusValuesMeta()
EnumMetaValue{VarName: "DeliveryStatusRetry", Value: DeliveryStatusRetry, Description: nil},
EnumMetaValue{VarName: "DeliveryStatusSuccess", Value: DeliveryStatusSuccess, Description: nil},
EnumMetaValue{VarName: "DeliveryStatusFailed", Value: DeliveryStatusFailed, Description: nil},
}
} }
func (e DeliveryStatus) String() string { func (e DeliveryStatus) String() string {
@ -146,6 +143,10 @@ func (e DeliveryStatus) VarName() string {
return "" return ""
} }
func (e DeliveryStatus) Meta() EnumMetaValue {
return EnumMetaValue{VarName: e.VarName(), Value: e, Description: nil}
}
func ParseDeliveryStatus(vv string) (DeliveryStatus, bool) { func ParseDeliveryStatus(vv string) (DeliveryStatus, bool) {
for _, ev := range __DeliveryStatusValues { for _, ev := range __DeliveryStatusValues {
if string(ev) == vv { if string(ev) == vv {
@ -161,9 +162,9 @@ func DeliveryStatusValues() []DeliveryStatus {
func DeliveryStatusValuesMeta() []EnumMetaValue { func DeliveryStatusValuesMeta() []EnumMetaValue {
return []EnumMetaValue{ return []EnumMetaValue{
EnumMetaValue{VarName: "DeliveryStatusRetry", Value: DeliveryStatusRetry, Description: nil}, DeliveryStatusRetry.Meta(),
EnumMetaValue{VarName: "DeliveryStatusSuccess", Value: DeliveryStatusSuccess, Description: nil}, DeliveryStatusSuccess.Meta(),
EnumMetaValue{VarName: "DeliveryStatusFailed", Value: DeliveryStatusFailed, Description: nil}, DeliveryStatusFailed.Meta(),
} }
} }
@ -208,12 +209,7 @@ func (e TokenPerm) ValuesAny() []any {
} }
func (e TokenPerm) ValuesMeta() []EnumMetaValue { func (e TokenPerm) ValuesMeta() []EnumMetaValue {
return []EnumMetaValue{ return TokenPermValuesMeta()
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")},
}
} }
func (e TokenPerm) String() string { func (e TokenPerm) String() string {
@ -234,6 +230,10 @@ func (e TokenPerm) VarName() string {
return "" return ""
} }
func (e TokenPerm) Meta() EnumMetaValue {
return EnumMetaValue{VarName: e.VarName(), Value: e, Description: langext.Ptr(e.Description())}
}
func ParseTokenPerm(vv string) (TokenPerm, bool) { func ParseTokenPerm(vv string) (TokenPerm, bool) {
for _, ev := range __TokenPermValues { for _, ev := range __TokenPermValues {
if string(ev) == vv { if string(ev) == vv {
@ -249,9 +249,9 @@ func TokenPermValues() []TokenPerm {
func TokenPermValuesMeta() []EnumMetaValue { func TokenPermValuesMeta() []EnumMetaValue {
return []EnumMetaValue{ return []EnumMetaValue{
EnumMetaValue{VarName: "PermAdmin", Value: PermAdmin, Description: langext.Ptr("Edit userdata (+ includes all other permissions)")}, PermAdmin.Meta(),
EnumMetaValue{VarName: "PermChannelRead", Value: PermChannelRead, Description: langext.Ptr("Read messages")}, PermChannelRead.Meta(),
EnumMetaValue{VarName: "PermChannelSend", Value: PermChannelSend, Description: langext.Ptr("Send messages")}, PermChannelSend.Meta(),
EnumMetaValue{VarName: "PermUserRead", Value: PermUserRead, Description: langext.Ptr("Read userdata")}, PermUserRead.Meta(),
} }
} }

View File

@ -14,8 +14,7 @@ const (
type Message struct { type Message struct {
MessageID MessageID MessageID MessageID
SenderUserID UserID // user that sent the message SenderUserID UserID // user that sent the message (this is also the owner of the channel that contains it)
OwnerUserID UserID // oner of the message (= owner of the channel that contains it)
ChannelInternalName string ChannelInternalName string
ChannelID ChannelID ChannelID ChannelID
SenderName *string SenderName *string
@ -34,7 +33,6 @@ func (m Message) FullJSON() MessageJSON {
return MessageJSON{ return MessageJSON{
MessageID: m.MessageID, MessageID: m.MessageID,
SenderUserID: m.SenderUserID, SenderUserID: m.SenderUserID,
OwnerUserID: m.OwnerUserID,
ChannelInternalName: m.ChannelInternalName, ChannelInternalName: m.ChannelInternalName,
ChannelID: m.ChannelID, ChannelID: m.ChannelID,
SenderName: m.SenderName, SenderName: m.SenderName,
@ -53,7 +51,6 @@ func (m Message) TrimmedJSON() MessageJSON {
return MessageJSON{ return MessageJSON{
MessageID: m.MessageID, MessageID: m.MessageID,
SenderUserID: m.SenderUserID, SenderUserID: m.SenderUserID,
OwnerUserID: m.OwnerUserID,
ChannelInternalName: m.ChannelInternalName, ChannelInternalName: m.ChannelInternalName,
ChannelID: m.ChannelID, ChannelID: m.ChannelID,
SenderName: m.SenderName, SenderName: m.SenderName,
@ -99,7 +96,6 @@ func (m Message) ShortContent() string {
type MessageJSON struct { type MessageJSON struct {
MessageID MessageID `json:"message_id"` MessageID MessageID `json:"message_id"`
SenderUserID UserID `json:"sender_user_id"` SenderUserID UserID `json:"sender_user_id"`
OwnerUserID UserID `json:"owner_user_id"`
ChannelInternalName string `json:"channel_internal_name"` ChannelInternalName string `json:"channel_internal_name"`
ChannelID ChannelID `json:"channel_id"` ChannelID ChannelID `json:"channel_id"`
SenderName *string `json:"sender_name"` SenderName *string `json:"sender_name"`
@ -116,7 +112,6 @@ type MessageJSON struct {
type MessageDB struct { type MessageDB struct {
MessageID MessageID `db:"message_id"` MessageID MessageID `db:"message_id"`
SenderUserID UserID `db:"sender_user_id"` SenderUserID UserID `db:"sender_user_id"`
OwnerUserID UserID `db:"owner_user_id"`
ChannelInternalName string `db:"channel_internal_name"` ChannelInternalName string `db:"channel_internal_name"`
ChannelID ChannelID `db:"channel_id"` ChannelID ChannelID `db:"channel_id"`
SenderName *string `db:"sender_name"` SenderName *string `db:"sender_name"`
@ -135,7 +130,6 @@ func (m MessageDB) Model() Message {
return Message{ return Message{
MessageID: m.MessageID, MessageID: m.MessageID,
SenderUserID: m.SenderUserID, SenderUserID: m.SenderUserID,
OwnerUserID: m.OwnerUserID,
ChannelInternalName: m.ChannelInternalName, ChannelInternalName: m.ChannelInternalName,
ChannelID: m.ChannelID, ChannelID: m.ChannelID,
SenderName: m.SenderName, SenderName: m.SenderName,

View File

@ -17,7 +17,6 @@ type MessageFilter struct {
ConfirmedSubscriptionBy *UserID ConfirmedSubscriptionBy *UserID
SearchString *[]string SearchString *[]string
Sender *[]UserID Sender *[]UserID
Owner *[]UserID
ChannelNameCS *[]string // case-sensitive ChannelNameCS *[]string // case-sensitive
ChannelNameCI *[]string // case-insensitive ChannelNameCI *[]string // case-insensitive
ChannelID *[]ChannelID ChannelID *[]ChannelID
@ -79,15 +78,6 @@ func (f MessageFilter) SQL() (string, string, sq.PP, error) {
sqlClauses = append(sqlClauses, "("+strings.Join(filter, " OR ")+")") 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 { if f.ChannelNameCI != nil {
filter := make([]string, 0) filter := make([]string, 0)
for i, v := range *f.ChannelNameCI { for i, v := range *f.ChannelNameCI {

View File

@ -2952,8 +2952,6 @@
"handler.CreateUserKey.body": { "handler.CreateUserKey.body": {
"type": "object", "type": "object",
"required": [ "required": [
"all_channels",
"channels",
"name", "name",
"permissions" "permissions"
], ],
@ -3574,9 +3572,6 @@
"message_id": { "message_id": {
"type": "string" "type": "string"
}, },
"owner_user_id": {
"type": "string"
},
"priority": { "priority": {
"type": "integer" "type": "integer"
}, },

View File

@ -183,8 +183,6 @@ definitions:
permissions: permissions:
type: string type: string
required: required:
- all_channels
- channels
- name - name
- permissions - permissions
type: object type: object
@ -583,8 +581,6 @@ definitions:
type: string type: string
message_id: message_id:
type: string type: string
owner_user_id:
type: string
priority: priority:
type: integer type: integer
sender_ip: sender_ip:

View File

@ -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)
}
}

View File

@ -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 { func unpointer(v any) any {
if v == nil { if v == nil {
return v return v

View File

@ -75,32 +75,9 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) {
TPrintln("DatabaseFile<requests>: " + dbfile2) TPrintln("DatabaseFile<requests>: " + dbfile2)
TPrintln("DatabaseFile<logs>: " + dbfile3) TPrintln("DatabaseFile<logs>: " + dbfile3)
conf, ok := scn.GetConfig("local-host") scn.Conf = CreateTestConfig(t, dbfile1, dbfile2, dbfile3)
if !ok {
TestFail(t, "conf not found")
}
conf.ServerPort = "0" // simply choose a free port sqlite, err := logic.NewDBPool(scn.Conf)
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)
if err != nil { if err != nil {
TestFailErr(t, err) TestFailErr(t, err)
} }
@ -111,7 +88,7 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) {
TestFailErr(t, err) TestFailErr(t, err)
} }
ginengine := ginext.NewEngine(conf) ginengine := ginext.NewEngine(scn.Conf)
router := api.NewRouter(app) router := api.NewRouter(app)
@ -119,7 +96,7 @@ func StartSimpleWebserver(t *testing.T) (*logic.Application, string, func()) {
apc := google.NewDummy() apc := google.NewDummy()
app.Init(conf, ginengine, nc, apc, []logic.Job{ app.Init(scn.Conf, ginengine, nc, apc, []logic.Job{
jobs.NewDeliveryRetryJob(app), jobs.NewDeliveryRetryJob(app),
jobs.NewRequestLogCollectorJob(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 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<main>: " + dbfile1)
TPrintln("DatabaseFile<requests>: " + dbfile2)
TPrintln("DatabaseFile<logs>: " + 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
}