Add various deleted flags to entities | Add active to subscriptions | Add DeleteUser && DeleteChannel endpoints [skip-tests]
All checks were successful
Build Docker and Deploy / Run Unit-Tests (push) Has been skipped
Build Docker and Deploy / Build Docker Container (push) Successful in 43s
Build Docker and Deploy / Deploy to Server (push) Successful in 16s

This commit is contained in:
2025-04-13 16:12:15 +02:00
parent aac34ef738
commit 8c0f0e3e8f
47 changed files with 2453 additions and 243 deletions

View File

@@ -182,7 +182,7 @@ func (db *Database) migrateSingle(tctx *simplectx.SimpleContext, tx sq.Tx, schem
if schemaFrom == schemaTo-1 {
migSQL := db.schema[schemaTo].MigScript
if migSQL == "" {
if len(migSQL) == 0 {
return exerr.New(exerr.TypeInternal, fmt.Sprintf("missing %s migration from %d to %d", db.name, schemaFrom, schemaTo)).Build()
}
@@ -192,7 +192,7 @@ func (db *Database) migrateSingle(tctx *simplectx.SimpleContext, tx sq.Tx, schem
return exerr.New(exerr.TypeInternal, fmt.Sprintf("missing %s migration from %d to %d", db.name, schemaFrom, schemaTo)).Build()
}
func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts string, currSchemaVers int, resultSchemVers int, resultHash string, post func(tctx *simplectx.SimpleContext, tx sq.Tx) error) error {
func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts []string, currSchemaVers int, resultSchemVers int, resultHash string, post func(tctx *simplectx.SimpleContext, tx sq.Tx) error) error {
schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash")
if err != nil {
@@ -215,9 +215,13 @@ func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts
log.Info().Msgf("Upgrade schema from %d -> %d", currSchemaVers, resultSchemVers)
_, err = tx.Exec(tctx, stmts, sq.PP{})
if err != nil {
return err
for i, stmt := range stmts {
log.Info().Msgf("SQL-Migration of [%s]: %d/%d", db.name, i+1, len(stmts))
_, err := tx.Exec(tctx, stmt, sq.PP{})
if err != nil {
return err
}
}
schemHashDBAfter, err := sq.HashSqliteDatabase(tctx, tx)

View File

@@ -13,7 +13,7 @@ func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, cha
return nil, err
}
return sq.QuerySingleOpt[models.Channel](ctx, tx, "SELECT * FROM channels WHERE owner_user_id = :uid AND internal_name = :nam LIMIT 1", sq.PP{"uid": userid, "nam": chanName}, sq.SModeExtended, sq.Safe)
return sq.QuerySingleOpt[models.Channel](ctx, tx, "SELECT * FROM channels WHERE owner_user_id = :uid AND internal_name = :nam AND deleted=0 LIMIT 1", sq.PP{"uid": userid, "nam": chanName}, sq.SModeExtended, sq.Safe)
}
func (db *Database) GetChannelByID(ctx db.TxContext, chanid models.ChannelID) (*models.Channel, error) {
@@ -22,7 +22,7 @@ func (db *Database) GetChannelByID(ctx db.TxContext, chanid models.ChannelID) (*
return nil, err
}
return sq.QuerySingleOpt[models.Channel](ctx, tx, "SELECT * FROM channels WHERE channel_id = :cid LIMIT 1", sq.PP{"cid": chanid}, sq.SModeExtended, sq.Safe)
return sq.QuerySingleOpt[models.Channel](ctx, tx, "SELECT * FROM channels WHERE channel_id = :cid AND deleted=0 LIMIT 1", sq.PP{"cid": chanid}, sq.SModeExtended, sq.Safe)
}
type CreateChanel struct {
@@ -49,6 +49,7 @@ func (db *Database) CreateChannel(ctx db.TxContext, userid models.UserID, dispNa
TimestampCreated: models.NowSCNTime(),
TimestampLastSent: nil,
MessagesSent: 0,
Deleted: false,
}
_, err = sq.InsertSingle(ctx, tx, "channels", entity)
@@ -67,7 +68,7 @@ func (db *Database) ListChannelsByOwner(ctx db.TxContext, userid models.UserID,
order := " ORDER BY channels.timestamp_created ASC, channels.channel_id ASC "
sql := "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub ON channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE owner_user_id = :ouid" + order
sql := "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub ON channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE channels.deleted=0 AND owner_user_id = :ouid" + order
pp := sq.PP{
"ouid": userid,
@@ -90,9 +91,11 @@ func (db *Database) ListChannelsBySubscriber(ctx db.TxContext, userid models.Use
confCond = " AND sub.confirmed = 0"
}
cond := "channels.deleted=0 AND sub.subscription_id IS NOT NULL " + confCond
order := " ORDER BY channels.timestamp_created ASC, channels.channel_id ASC "
sql := "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE sub.subscription_id IS NOT NULL " + confCond + order
sql := "SELECT channels.*, sub.* " + "FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid AND sub.deleted=0 WHERE " + cond + order
pp := sq.PP{
"subuid": userid,
@@ -114,9 +117,11 @@ func (db *Database) ListChannelsByAccess(ctx db.TxContext, userid models.UserID,
confCond = "OR (sub.subscription_id IS NOT NULL AND sub.confirmed = 0)"
}
cond := "channels.deleted=0 AND (owner_user_id = :ouid " + confCond + ")"
order := " ORDER BY channels.timestamp_created ASC, channels.channel_id ASC "
sql := "SELECT channels.*, sub.* FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid WHERE owner_user_id = :ouid " + confCond + order
sql := "SELECT channels.*, sub.* " + "FROM channels LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid AND sub.deleted=0 WHERE " + cond + order
pp := sq.PP{
"ouid": userid,
@@ -139,13 +144,14 @@ func (db *Database) GetChannel(ctx db.TxContext, userid models.UserID, channelid
selectors := "channels.*, sub.*"
join := "LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid"
join := "LEFT JOIN subscriptions AS sub on channels.channel_id = sub.channel_id AND sub.subscriber_user_id = :subuid AND sub.deleted=0"
cond := "channels.channel_id = :cid"
if enforceOwner {
cond = "owner_user_id = :ouid AND channels.channel_id = :cid"
params["ouid"] = userid
}
cond += " AND channels.deleted=0"
sql := "SELECT " + selectors + " FROM channels " + join + " WHERE " + cond + " LIMIT 1"
@@ -160,7 +166,7 @@ func (db *Database) IncChannelMessageCounter(ctx db.TxContext, channel *models.C
now := time.Now()
_, err = tx.Exec(ctx, "UPDATE channels SET messages_sent = messages_sent+1, timestamp_lastsent = :ts WHERE channel_id = :cid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE channels SET messages_sent = messages_sent+1, timestamp_lastsent = :ts WHERE channel_id = :cid AND deleted=0", sq.PP{
"ts": time2DB(now),
"cid": channel.ChannelID,
})
@@ -180,7 +186,7 @@ func (db *Database) UpdateChannelSubscribeKey(ctx db.TxContext, channelid models
return err
}
_, err = tx.Exec(ctx, "UPDATE channels SET subscribe_key = :key WHERE channel_id = :cid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE channels SET subscribe_key = :key WHERE channel_id = :cid AND deleted=0", sq.PP{
"key": newkey,
"cid": channelid,
})
@@ -197,7 +203,7 @@ func (db *Database) UpdateChannelDisplayName(ctx db.TxContext, channelid models.
return err
}
_, err = tx.Exec(ctx, "UPDATE channels SET display_name = :nam WHERE channel_id = :cid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE channels SET display_name = :nam WHERE channel_id = :cid AND deleted=0", sq.PP{
"nam": dispname,
"cid": channelid,
})
@@ -214,7 +220,7 @@ func (db *Database) UpdateChannelDescriptionName(ctx db.TxContext, channelid mod
return err
}
_, err = tx.Exec(ctx, "UPDATE channels SET description_name = :nam WHERE channel_id = :cid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE channels SET description_name = :nam WHERE channel_id = :cid AND deleted=0", sq.PP{
"nam": descname,
"cid": channelid,
})
@@ -224,3 +230,31 @@ func (db *Database) UpdateChannelDescriptionName(ctx db.TxContext, channelid mod
return nil
}
func (db *Database) DeleteChannelsOfUser(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE channels SET deleted=1 WHERE owner_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteChannel(ctx db.TxContext, channelid models.ChannelID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE channels SET deleted=1 WHERE channel_id = :cid AND deleted=0", sq.PP{"cid": channelid})
if err != nil {
return err
}
return nil
}

View File

@@ -21,6 +21,7 @@ func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype m
AgentModel: agentModel,
AgentVersion: agentVersion,
Name: name,
Deleted: false,
}
_, err = sq.InsertSingle(ctx, tx, "clients", entity)
@@ -159,3 +160,17 @@ func (db *Database) UpdateClientDescriptionName(ctx db.TxContext, clientid model
return nil
}
func (db *Database) DeleteClientsOfUser(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE clients SET deleted=1 WHERE user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}

View File

@@ -182,7 +182,7 @@ func (db *Database) migrateSingle(tctx *simplectx.SimpleContext, tx sq.Tx, schem
if schemaFrom == schemaTo-1 {
migSQL := db.schema[schemaTo].MigScript
if migSQL == "" {
if len(migSQL) == 0 {
return exerr.New(exerr.TypeInternal, fmt.Sprintf("missing %s migration from %d to %d", db.name, schemaFrom, schemaTo)).Build()
}
@@ -192,7 +192,7 @@ func (db *Database) migrateSingle(tctx *simplectx.SimpleContext, tx sq.Tx, schem
return exerr.New(exerr.TypeInternal, fmt.Sprintf("missing %s migration from %d to %d", db.name, schemaFrom, schemaTo)).Build()
}
func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts string, currSchemaVers int, resultSchemVers int, resultHash string, post func(tctx *simplectx.SimpleContext, tx sq.Tx) error) error {
func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts []string, currSchemaVers int, resultSchemVers int, resultHash string, post func(tctx *simplectx.SimpleContext, tx sq.Tx) error) error {
schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash")
if err != nil {
@@ -215,9 +215,13 @@ func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts
log.Info().Msgf("Upgrade schema from %d -> %d", currSchemaVers, resultSchemVers)
_, err = tx.Exec(tctx, stmts, sq.PP{})
if err != nil {
return err
for i, stmt := range stmts {
log.Info().Msgf("SQL-Migration of [%s]: %d/%d", db.name, i+1, len(stmts))
_, err := tx.Exec(tctx, stmt, sq.PP{})
if err != nil {
return err
}
}
schemHashDBAfter, err := sq.HashSqliteDatabase(tctx, tx)

View File

@@ -29,6 +29,7 @@ func (db *Database) CreateRetryDelivery(ctx db.TxContext, client models.Client,
RetryCount: 0,
NextDelivery: models.NewSCNTimePtr(&next),
FCMMessageID: nil,
Deleted: false,
}
_, err = sq.InsertSingle(ctx, tx, "deliveries", entity)
@@ -58,6 +59,7 @@ func (db *Database) CreateSuccessDelivery(ctx db.TxContext, client models.Client
RetryCount: 0,
NextDelivery: nil,
FCMMessageID: langext.Ptr(fcmDelivID),
Deleted: false,
}
_, err = sq.InsertSingle(ctx, tx, "deliveries", entity)
@@ -74,7 +76,7 @@ func (db *Database) ListRetrieableDeliveries(ctx db.TxContext, pageSize int) ([]
return nil, err
}
return sq.QueryAll[models.Delivery](ctx, tx, "SELECT * FROM deliveries WHERE status = 'RETRY' AND next_delivery < :next ORDER BY next_delivery ASC LIMIT :lim", sq.PP{
return sq.QueryAll[models.Delivery](ctx, tx, "SELECT * FROM deliveries WHERE status = 'RETRY' AND next_delivery < :next AND deleted=0 ORDER BY next_delivery ASC LIMIT :lim", sq.PP{
"next": time2DB(time.Now()),
"lim": pageSize,
}, sq.SModeExtended, sq.Safe)
@@ -86,7 +88,7 @@ func (db *Database) SetDeliverySuccess(ctx db.TxContext, delivery models.Deliver
return err
}
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'SUCCESS', next_delivery = NULL, retry_count = :rc, timestamp_finalized = :ts, fcm_message_id = :fcm WHERE delivery_id = :did", sq.PP{
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'SUCCESS', next_delivery = NULL, retry_count = :rc, timestamp_finalized = :ts, fcm_message_id = :fcm WHERE delivery_id = :did AND deleted=0", sq.PP{
"rc": delivery.RetryCount + 1,
"ts": time2DB(time.Now()),
"fcm": fcmDelivID,
@@ -105,7 +107,7 @@ func (db *Database) SetDeliveryFailed(ctx db.TxContext, delivery models.Delivery
return err
}
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'FAILED', next_delivery = NULL, retry_count = :rc, timestamp_finalized = :ts WHERE delivery_id = :did",
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'FAILED', next_delivery = NULL, retry_count = :rc, timestamp_finalized = :ts WHERE delivery_id = :did AND deleted=0",
sq.PP{
"rc": delivery.RetryCount + 1,
"ts": time2DB(time.Now()),
@@ -124,7 +126,7 @@ func (db *Database) SetDeliveryRetry(ctx db.TxContext, delivery models.Delivery)
return err
}
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'RETRY', next_delivery = :next, retry_count = :rc WHERE delivery_id = :did", sq.PP{
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'RETRY', next_delivery = :next, retry_count = :rc WHERE delivery_id = :did AND deleted=0", sq.PP{
"next": time2DB(scn.NextDeliveryTimestamp(time.Now())),
"rc": delivery.RetryCount + 1,
"did": delivery.DeliveryID,
@@ -142,7 +144,7 @@ func (db *Database) CancelPendingDeliveries(ctx db.TxContext, messageID models.M
return err
}
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'FAILED', next_delivery = NULL, timestamp_finalized = :ts WHERE message_id = :mid AND status = 'RETRY'", sq.PP{
_, err = tx.Exec(ctx, "UPDATE deliveries SET status = 'FAILED', next_delivery = NULL, timestamp_finalized = :ts WHERE message_id = :mid AND status = 'RETRY' AND deleted=0", sq.PP{
"ts": time.Now(),
"mid": messageID,
})
@@ -152,3 +154,31 @@ func (db *Database) CancelPendingDeliveries(ctx db.TxContext, messageID models.M
return nil
}
func (db *Database) DeleteDeliveriesOfUser(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE deliveries SET deleted=1 WHERE receiver_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteDeliveriesOfChannel(ctx db.TxContext, channelid models.ChannelID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE deliveries SET deleted=1 WHERE (SELECT channel_id FROM messages WHERE messages.message_id = deliveries.message_id) = :cid AND deleted=0", sq.PP{"cid": channelid})
if err != nil {
return err
}
return nil
}

View File

@@ -26,6 +26,7 @@ func (db *Database) CreateKeyToken(ctx db.TxContext, name string, owner models.U
Token: token,
Permissions: permissions,
MessagesSent: 0,
Deleted: false,
}
_, err = sq.InsertSingle(ctx, tx, "keytokens", entity)
@@ -42,7 +43,7 @@ func (db *Database) ListKeyTokens(ctx db.TxContext, ownerID models.UserID) ([]mo
return nil, err
}
return sq.QueryAll[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE owner_user_id = :uid ORDER BY keytokens.timestamp_created DESC, keytokens.keytoken_id ASC", sq.PP{"uid": ownerID}, sq.SModeExtended, sq.Safe)
return sq.QueryAll[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE owner_user_id = :uid AND deleted=0 ORDER BY keytokens.timestamp_created DESC, keytokens.keytoken_id ASC", sq.PP{"uid": ownerID}, sq.SModeExtended, sq.Safe)
}
func (db *Database) GetKeyToken(ctx db.TxContext, userid models.UserID, keyTokenid models.KeyTokenID) (models.KeyToken, error) {
@@ -51,7 +52,7 @@ func (db *Database) GetKeyToken(ctx db.TxContext, userid models.UserID, keyToken
return models.KeyToken{}, err
}
return sq.QuerySingle[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE owner_user_id = :uid AND keytoken_id = :cid LIMIT 1", sq.PP{
return sq.QuerySingle[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE owner_user_id = :uid AND keytoken_id = :cid AND deleted=0 LIMIT 1", sq.PP{
"uid": userid,
"cid": keyTokenid,
}, sq.SModeExtended, sq.Safe)
@@ -63,7 +64,7 @@ func (db *Database) GetKeyTokenByID(ctx db.TxContext, keyTokenid models.KeyToken
return models.KeyToken{}, err
}
return sq.QuerySingle[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE keytoken_id = :cid LIMIT 1", sq.PP{"cid": keyTokenid}, sq.SModeExtended, sq.Safe)
return sq.QuerySingle[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE keytoken_id = :cid AND deleted=0 LIMIT 1", sq.PP{"cid": keyTokenid}, sq.SModeExtended, sq.Safe)
}
func (db *Database) GetKeyTokenByToken(ctx db.TxContext, key string) (*models.KeyToken, error) {
@@ -72,7 +73,7 @@ func (db *Database) GetKeyTokenByToken(ctx db.TxContext, key string) (*models.Ke
return nil, err
}
return sq.QuerySingleOpt[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE token = :key LIMIT 1", sq.PP{"key": key}, sq.SModeExtended, sq.Safe)
return sq.QuerySingleOpt[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE token = :key AND deleted=0 LIMIT 1", sq.PP{"key": key}, sq.SModeExtended, sq.Safe)
}
func (db *Database) DeleteKeyToken(ctx db.TxContext, keyTokenid models.KeyTokenID) error {
@@ -81,7 +82,7 @@ func (db *Database) DeleteKeyToken(ctx db.TxContext, keyTokenid models.KeyTokenI
return err
}
_, err = tx.Exec(ctx, "DELETE FROM keytokens WHERE keytoken_id = :tid", sq.PP{"tid": keyTokenid})
_, err = tx.Exec(ctx, "UPDATE keytokens SET deleted=1 WHERE keytoken_id = :tid AND deleted=0", sq.PP{"tid": keyTokenid})
if err != nil {
return err
}
@@ -95,7 +96,7 @@ func (db *Database) UpdateKeyTokenName(ctx db.TxContext, keyTokenid models.KeyTo
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET name = :nam WHERE keytoken_id = :tid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE keytokens SET name = :nam WHERE keytoken_id = :tid AND deleted=0", sq.PP{
"nam": name,
"tid": keyTokenid,
})
@@ -112,7 +113,7 @@ func (db *Database) UpdateKeyTokenPermissions(ctx db.TxContext, keyTokenid model
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET permissions = :prm WHERE keytoken_id = :tid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE keytokens SET permissions = :prm WHERE keytoken_id = :tid AND deleted=0", sq.PP{
"tid": keyTokenid,
"prm": perm.String(),
})
@@ -129,7 +130,7 @@ func (db *Database) UpdateKeyTokenAllChannels(ctx db.TxContext, keyTokenid model
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET all_channels = :all WHERE keytoken_id = :tid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE keytokens SET all_channels = :all WHERE keytoken_id = :tid AND deleted=0", sq.PP{
"tid": keyTokenid,
"all": bool2DB(allChannels),
})
@@ -146,7 +147,7 @@ func (db *Database) UpdateKeyTokenChannels(ctx db.TxContext, keyTokenid models.K
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET channels = :cha WHERE keytoken_id = :tid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE keytokens SET channels = :cha WHERE keytoken_id = :tid AND deleted=0", sq.PP{
"tid": keyTokenid,
"cha": strings.Join(langext.ArrMap(channels, func(v models.ChannelID) string { return v.String() }), ";"),
})
@@ -165,7 +166,7 @@ func (db *Database) IncKeyTokenMessageCounter(ctx db.TxContext, keyToken *models
now := time.Now()
_, err = tx.Exec(ctx, "UPDATE keytokens SET messages_sent = messages_sent+1, timestamp_lastused = :ts WHERE keytoken_id = :tid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE keytokens SET messages_sent = messages_sent+1, timestamp_lastused = :ts WHERE keytoken_id = :tid AND deleted=0", sq.PP{
"ts": time2DB(now),
"tid": keyToken.KeyTokenID,
})
@@ -185,7 +186,7 @@ func (db *Database) UpdateKeyTokenLastUsed(ctx db.TxContext, keyTokenid models.K
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET timestamp_lastused = :ts WHERE keytoken_id = :tid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE keytokens SET timestamp_lastused = :ts WHERE keytoken_id = :tid AND deleted=0", sq.PP{
"ts": time2DB(time.Now()),
"tid": keyTokenid,
})
@@ -195,3 +196,46 @@ func (db *Database) UpdateKeyTokenLastUsed(ctx db.TxContext, keyTokenid models.K
return nil
}
func (db *Database) DeleteKeyTokensOfUser(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET deleted=1 WHERE owner_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteChannelFromKeyTokens(ctx db.TxContext, channelid models.ChannelID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
tok, err := sq.QueryAll[models.KeyToken](ctx, tx, "SELECT * FROM keytokens WHERE instr(channels, :cid) AND deleted=0", sq.PP{"cid": channelid}, sq.SModeExtended, sq.Safe)
if err != nil {
return err
}
for _, t := range tok {
newChanList := models.ChannelIDArr(langext.ArrRemove(t.Channels, channelid))
chanListDB, err := newChanList.MarshalToDB(newChanList)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE keytokens SET channels=:clist WHERE owner_user_id = :uid AND deleted=0", sq.PP{"clist": chanListDB})
if err != nil {
return err
}
}
return nil
}

View File

@@ -15,7 +15,7 @@ func (db *Database) GetMessageByUserMessageID(ctx db.TxContext, usrMsgId string)
return nil, err
}
return sq.QuerySingleOpt[models.Message](ctx, tx, "SELECT * FROM messages WHERE usr_message_id = :umid LIMIT 1", sq.PP{"umid": usrMsgId}, sq.SModeExtended, sq.Safe)
return sq.QuerySingleOpt[models.Message](ctx, tx, "SELECT * FROM messages WHERE usr_message_id = :umid LIMIT 1", sq.PP{"umid": usrMsgId}, sq.SModeExtended, sq.Safe) // no deleted=0 check!
}
func (db *Database) GetMessage(ctx db.TxContext, scnMessageID models.MessageID, allowDeleted bool) (models.Message, error) {
@@ -45,6 +45,7 @@ func (db *Database) CreateMessage(ctx db.TxContext, senderUserID models.UserID,
SenderUserID: senderUserID,
ChannelInternalName: channel.InternalName,
ChannelID: channel.ChannelID,
ChannelOwnerUserID: channel.OwnerUserID,
SenderIP: senderIP,
SenderName: senderName,
TimestampReal: models.NowSCNTime(),
@@ -164,3 +165,45 @@ func (db *Database) CountMessages(ctx db.TxContext, filter models.MessageFilter)
return countRes, nil
}
func (db *Database) DeleteMessagesOfUserBySenderUserID(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE messages SET deleted=1 WHERE sender_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteMessagesOfUserByChannelOwnerUserID(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE messages SET deleted=1 WHERE channel_owner_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteMessagesOfChannel(ctx db.TxContext, channelid models.ChannelID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE messages SET deleted=1 WHERE channel_id = :cid AND deleted=0", sq.PP{"cid": channelid})
if err != nil {
return err
}
return nil
}

View File

@@ -17,7 +17,7 @@ func (db *Database) ListSenderNames(ctx db.TxContext, userid models.UserID, incl
prepParams := sq.PP{"uid": userid}
if includeForeignSubscribed {
sqlStr = "SELECT sender_name AS name, MAX(timestamp_real) AS ts_last, MIN(timestamp_real) AS ts_first, COUNT(*) AS count FROM messages LEFT JOIN subscriptions AS subs on messages.channel_id = subs.channel_id WHERE (subs.subscriber_user_id = :uid AND subs.confirmed = 1) AND sender_NAME NOT NULL GROUP BY sender_name ORDER BY ts_last DESC"
sqlStr = "SELECT sender_name AS name, MAX(timestamp_real) AS ts_last, MIN(timestamp_real) AS ts_first, COUNT(*) AS count FROM messages LEFT JOIN subscriptions AS subs ON messages.channel_id = subs.channel_id AND subs.deleted=0 WHERE (subs.subscriber_user_id = :uid AND subs.confirmed = 1) AND sender_NAME NOT NULL GROUP BY sender_name ORDER BY ts_last DESC"
} else {
sqlStr = "SELECT sender_name AS name, MAX(timestamp_real) AS ts_last, MIN(timestamp_real) AS ts_first, COUNT(*) AS count FROM messages WHERE sender_user_id = :uid AND sender_NAME NOT NULL GROUP BY sender_name ORDER BY ts_last DESC"
}

View File

@@ -20,6 +20,8 @@ func (db *Database) CreateSubscription(ctx db.TxContext, subscriberUID models.Us
ChannelInternalName: channel.InternalName,
TimestampCreated: models.NowSCNTime(),
Confirmed: confirmed,
Active: true,
Deleted: false,
}
_, err = sq.InsertSingle(ctx, tx, "subscriptions", entity)
@@ -40,7 +42,7 @@ func (db *Database) ListSubscriptions(ctx db.TxContext, filter models.Subscripti
orderClause := " ORDER BY subscriptions.timestamp_created DESC, subscriptions.subscription_id DESC "
sqlQuery := "SELECT " + "subscriptions.*" + " FROM subscriptions " + filterJoin + " WHERE ( " + filterCond + " ) " + orderClause
sqlQuery := "SELECT " + "subscriptions.*" + " FROM subscriptions " + filterJoin + " WHERE ( " + filterCond + " ) AND subscriptions.deleted=0 " + orderClause
return sq.QueryAll[models.Subscription](ctx, tx, sqlQuery, prepParams, sq.SModeExtended, sq.Safe)
}
@@ -51,7 +53,7 @@ func (db *Database) GetSubscription(ctx db.TxContext, subid models.SubscriptionI
return models.Subscription{}, err
}
return sq.QuerySingle[models.Subscription](ctx, tx, "SELECT * FROM subscriptions WHERE subscription_id = :sid LIMIT 1", sq.PP{"sid": subid}, sq.SModeExtended, sq.Safe)
return sq.QuerySingle[models.Subscription](ctx, tx, "SELECT * FROM subscriptions WHERE subscription_id = :sid AND deleted=0 LIMIT 1", sq.PP{"sid": subid}, sq.SModeExtended, sq.Safe)
}
func (db *Database) GetSubscriptionBySubscriber(ctx db.TxContext, subscriberId models.UserID, channelId models.ChannelID) (*models.Subscription, error) {
@@ -60,7 +62,7 @@ func (db *Database) GetSubscriptionBySubscriber(ctx db.TxContext, subscriberId m
return nil, err
}
return sq.QuerySingleOpt[models.Subscription](ctx, tx, "SELECT * FROM subscriptions WHERE subscriber_user_id = :suid AND channel_id = :cid LIMIT 1", sq.PP{
return sq.QuerySingleOpt[models.Subscription](ctx, tx, "SELECT * FROM subscriptions WHERE subscriber_user_id = :suid AND channel_id = :cid AND deleted=0 LIMIT 1", sq.PP{
"suid": subscriberId,
"cid": channelId,
}, sq.SModeExtended, sq.Safe)
@@ -72,7 +74,7 @@ func (db *Database) DeleteSubscription(ctx db.TxContext, subid models.Subscripti
return err
}
_, err = tx.Exec(ctx, "DELETE FROM subscriptions WHERE subscription_id = :sid", sq.PP{"sid": subid})
_, err = tx.Exec(ctx, "UPDATE subscriptions SET deleted=1 WHERE subscription_id = :sid AND deleted=0", sq.PP{"sid": subid})
if err != nil {
return err
}
@@ -86,7 +88,7 @@ func (db *Database) UpdateSubscriptionConfirmed(ctx db.TxContext, subscriptionID
return err
}
_, err = tx.Exec(ctx, "UPDATE subscriptions SET confirmed = :conf WHERE subscription_id = :sid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE subscriptions SET confirmed = :conf WHERE subscription_id = :sid AND deleted=0", sq.PP{
"conf": confirmed,
"sid": subscriptionID,
})
@@ -96,3 +98,62 @@ func (db *Database) UpdateSubscriptionConfirmed(ctx db.TxContext, subscriptionID
return nil
}
func (db *Database) UpdateSubscriptionActive(ctx db.TxContext, subscriptionID models.SubscriptionID, active bool) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE subscriptions SET active = :act WHERE subscription_id = :sid AND deleted=0", sq.PP{
"act": active,
"sid": subscriptionID,
})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteSubscriptionsOfUserByChannelOwner(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE subscriptions SET deleted=1 WHERE channel_owner_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteSubscriptionsOfUserBySubscriber(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE subscriptions SET deleted=1 WHERE subscriber_user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}
func (db *Database) DeleteSubscriptionsByChannel(ctx db.TxContext, channelid models.ChannelID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE subscriptions SET deleted=1 WHERE channel_id = :cid AND deleted=0", sq.PP{"cid": channelid})
if err != nil {
return err
}
return nil
}

View File

@@ -27,6 +27,7 @@ func (db *Database) CreateUser(ctx db.TxContext, protoken *string, username *str
IsPro: protoken != nil,
ProToken: protoken,
UserExtra: models.UserExtra{},
Deleted: false,
}
entity.PreMarshal()
@@ -45,7 +46,7 @@ func (db *Database) ClearProTokens(ctx db.TxContext, protoken string) error {
return err
}
_, err = tx.Exec(ctx, "UPDATE users SET is_pro=0, pro_token=NULL WHERE pro_token = :tok", sq.PP{"tok": protoken})
_, err = tx.Exec(ctx, "UPDATE users SET is_pro=0, pro_token=NULL WHERE pro_token = :tok AND deleted=0", sq.PP{"tok": protoken})
if err != nil {
return err
}
@@ -59,7 +60,7 @@ func (db *Database) GetUser(ctx db.TxContext, userid models.UserID) (models.User
return models.User{}, err
}
return sq.QuerySingle[models.User](ctx, tx, "SELECT * FROM users WHERE user_id = :uid LIMIT 1", sq.PP{"uid": userid}, sq.SModeExtended, sq.Safe)
return sq.QuerySingle[models.User](ctx, tx, "SELECT * FROM users WHERE user_id = :uid AND deleted=0 LIMIT 1", sq.PP{"uid": userid}, sq.SModeExtended, sq.Safe)
}
func (db *Database) GetUserOpt(ctx db.TxContext, userid models.UserID) (*models.User, error) {
@@ -68,7 +69,7 @@ func (db *Database) GetUserOpt(ctx db.TxContext, userid models.UserID) (*models.
return nil, err
}
return sq.QuerySingleOpt[models.User](ctx, tx, "SELECT * FROM users WHERE user_id = :uid LIMIT 1", sq.PP{"uid": userid}, sq.SModeExtended, sq.Safe)
return sq.QuerySingleOpt[models.User](ctx, tx, "SELECT * FROM users WHERE user_id = :uid AND deleted=0 LIMIT 1", sq.PP{"uid": userid}, sq.SModeExtended, sq.Safe)
}
func (db *Database) UpdateUserUsername(ctx db.TxContext, userid models.UserID, username *string) error {
@@ -77,7 +78,7 @@ func (db *Database) UpdateUserUsername(ctx db.TxContext, userid models.UserID, u
return err
}
_, err = tx.Exec(ctx, "UPDATE users SET username = :nam WHERE user_id = :uid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE users SET username = :nam WHERE user_id = :uid AND deleted=0", sq.PP{
"nam": username,
"uid": userid,
})
@@ -94,7 +95,7 @@ func (db *Database) UpdateUserProToken(ctx db.TxContext, userid models.UserID, p
return err
}
_, err = tx.Exec(ctx, "UPDATE users SET pro_token = :tok, is_pro = :pro WHERE user_id = :uid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE users SET pro_token = :tok, is_pro = :pro WHERE user_id = :uid AND deleted=0", sq.PP{
"tok": protoken,
"pro": bool2DB(protoken != nil),
"uid": userid,
@@ -119,7 +120,7 @@ func (db *Database) IncUserMessageCounter(ctx db.TxContext, user *models.User) e
user.QuotaUsed = quota
user.QuotaUsedDay = langext.Ptr(scn.QuotaDayString())
_, err = tx.Exec(ctx, "UPDATE users SET timestamp_lastsent = :ts, messages_sent = messages_sent+1, quota_used = :qu, quota_used_day = :qd WHERE user_id = :uid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE users SET timestamp_lastsent = :ts, messages_sent = messages_sent+1, quota_used = :qu, quota_used_day = :qd WHERE user_id = :uid AND deleted=0", sq.PP{
"ts": time2DB(now),
"qu": user.QuotaUsed,
"qd": user.QuotaUsedDay,
@@ -141,7 +142,7 @@ func (db *Database) UpdateUserLastRead(ctx db.TxContext, userid models.UserID) e
return err
}
_, err = tx.Exec(ctx, "UPDATE users SET timestamp_lastread = :ts WHERE user_id = :uid", sq.PP{
_, err = tx.Exec(ctx, "UPDATE users SET timestamp_lastread = :ts WHERE user_id = :uid AND deleted=0", sq.PP{
"ts": time2DB(time.Now()),
"uid": userid,
})
@@ -151,3 +152,17 @@ func (db *Database) UpdateUserLastRead(ctx db.TxContext, userid models.UserID) e
return nil
}
func (db *Database) DeleteUser(ctx db.TxContext, userid models.UserID) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE users SET deleted=1 WHERE user_id = :uid AND deleted=0", sq.PP{"uid": userid})
if err != nil {
return err
}
return nil
}

View File

@@ -182,7 +182,7 @@ func (db *Database) migrateSingle(tctx *simplectx.SimpleContext, tx sq.Tx, schem
if schemaFrom == schemaTo-1 {
migSQL := db.schema[schemaTo].MigScript
if migSQL == "" {
if len(migSQL) == 0 {
return exerr.New(exerr.TypeInternal, fmt.Sprintf("missing %s migration from %d to %d", db.name, schemaFrom, schemaTo)).Build()
}
@@ -192,7 +192,7 @@ func (db *Database) migrateSingle(tctx *simplectx.SimpleContext, tx sq.Tx, schem
return exerr.New(exerr.TypeInternal, fmt.Sprintf("missing %s migration from %d to %d", db.name, schemaFrom, schemaTo)).Build()
}
func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts string, currSchemaVers int, resultSchemVers int, resultHash string, post func(tctx *simplectx.SimpleContext, tx sq.Tx) error) error {
func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts []string, currSchemaVers int, resultSchemVers int, resultHash string, post func(tctx *simplectx.SimpleContext, tx sq.Tx) error) error {
schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash")
if err != nil {
@@ -215,9 +215,13 @@ func (db *Database) migrateBySQL(tctx *simplectx.SimpleContext, tx sq.Tx, stmts
log.Info().Msgf("Upgrade schema from %d -> %d", currSchemaVers, resultSchemVers)
_, err = tx.Exec(tctx, stmts, sq.PP{})
if err != nil {
return err
for i, stmt := range stmts {
log.Info().Msgf("SQL-Migration of [%s]: %d/%d", db.name, i+1, len(stmts))
_, err := tx.Exec(tctx, stmt, sq.PP{})
if err != nil {
return err
}
}
schemHashDBAfter, err := sq.HashSqliteDatabase(tctx, tx)