From e98a804efca23c738038252bb5343182e1744f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Fri, 27 Mar 2026 12:57:19 +0100 Subject: [PATCH] Fix panic in /preview/channel/{id} --- scnserver/api/handler/apiPreview.go | 24 ++++++++++++------------ scnserver/api/handler/apiSubscription.go | 2 +- scnserver/api/handler/compat.go | 12 ++++++------ scnserver/db/impl/primary/channels.go | 5 +++-- scnserver/db/impl/primary/clients.go | 2 +- scnserver/db/impl/primary/keytokens.go | 7 ++++--- scnserver/jobs/deliveryRetryJob.go | 9 +++++---- scnserver/logic/application.go | 2 +- scnserver/logic/permissions.go | 2 +- 9 files changed, 34 insertions(+), 31 deletions(-) diff --git a/scnserver/api/handler/apiPreview.go b/scnserver/api/handler/apiPreview.go index e521c49..8969de4 100644 --- a/scnserver/api/handler/apiPreview.go +++ b/scnserver/api/handler/apiPreview.go @@ -93,13 +93,13 @@ func (h APIHandler) GetChannelPreview(pctx ginext.PreContext) ginext.HTTPRespons userid := *ctx.GetPermissionUserID() - channel, err := h.database.GetChannelByID(ctx, u.ChannelID) - if errors.Is(err, sql.ErrNoRows) { - return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err) - } + channel, err := h.database.GetChannelByIDOpt(ctx, u.ChannelID) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err) } + if channel == nil { + return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err) + } sub, err := h.database.GetSubscriptionBySubscriber(ctx, userid, channel.ChannelID) if err != nil { @@ -162,13 +162,13 @@ func (h APIHandler) GetUserKeyPreview(pctx ginext.PreContext) ginext.HTTPRespons // Query by token.token - keytoken, err := h.database.GetKeyTokenByToken(ctx, u.KeyID) - if keytoken == nil { - return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err) - } + keytoken, err := h.database.GetKeyTokenByTokenOpt(ctx, u.KeyID) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err) } + if keytoken == nil { + return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err) + } return finishSuccess(ginext.JSON(http.StatusOK, keytoken.Preview())) @@ -215,13 +215,13 @@ func (h APIHandler) GetClientPreview(pctx ginext.PreContext) ginext.HTTPResponse return *permResp } - client, err := h.database.GetClientByID(ctx, u.ClientID) - if client == nil { - return ginresp.APIError(g, 404, apierr.CLIENT_NOT_FOUND, "Client not found", err) - } + client, err := h.database.GetClientByIDOpt(ctx, u.ClientID) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err) } + if client == nil { + return ginresp.APIError(g, 404, apierr.CLIENT_NOT_FOUND, "Client not found", err) + } user, err := h.database.GetUser(ctx, client.UserID) if errors.Is(err, sql.ErrNoRows) { diff --git a/scnserver/api/handler/apiSubscription.go b/scnserver/api/handler/apiSubscription.go index b0436d9..769086f 100644 --- a/scnserver/api/handler/apiSubscription.go +++ b/scnserver/api/handler/apiSubscription.go @@ -362,7 +362,7 @@ func (h APIHandler) CreateSubscription(pctx ginext.PreContext) ginext.HTTPRespon } else if b.ChannelOwnerUserID == nil && b.ChannelInternalName == nil && b.ChannelID != nil { - outchannel, err := h.database.GetChannelByID(ctx, *b.ChannelID) + outchannel, err := h.database.GetChannelByIDOpt(ctx, *b.ChannelID) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err) } diff --git a/scnserver/api/handler/compat.go b/scnserver/api/handler/compat.go index dfc6733..9fa27df 100644 --- a/scnserver/api/handler/compat.go +++ b/scnserver/api/handler/compat.go @@ -305,7 +305,7 @@ func (h CompatHandler) Info(pctx ginext.PreContext) ginext.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) + keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } @@ -417,7 +417,7 @@ func (h CompatHandler) Ack(pctx ginext.PreContext) ginext.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) + keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } @@ -523,7 +523,7 @@ func (h CompatHandler) Requery(pctx ginext.PreContext) ginext.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) + keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } @@ -644,7 +644,7 @@ func (h CompatHandler) Update(pctx ginext.PreContext) ginext.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) + keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } @@ -778,7 +778,7 @@ func (h CompatHandler) Expand(pctx ginext.PreContext) ginext.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) + keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } @@ -901,7 +901,7 @@ func (h CompatHandler) Upgrade(pctx ginext.PreContext) ginext.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) + keytok, err := h.database.GetKeyTokenByTokenOpt(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } diff --git a/scnserver/db/impl/primary/channels.go b/scnserver/db/impl/primary/channels.go index c764438..5fdb650 100644 --- a/scnserver/db/impl/primary/channels.go +++ b/scnserver/db/impl/primary/channels.go @@ -1,10 +1,11 @@ package primary import ( + "time" + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "git.blackforestbytes.com/BlackForestBytes/goext/sq" - "time" ) func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, chanName string) (*models.Channel, error) { @@ -16,7 +17,7 @@ func (db *Database) GetChannelByName(ctx db.TxContext, userid models.UserID, cha 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) { +func (db *Database) GetChannelByIDOpt(ctx db.TxContext, chanid models.ChannelID) (*models.Channel, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err diff --git a/scnserver/db/impl/primary/clients.go b/scnserver/db/impl/primary/clients.go index 028d1ec..cedfb04 100644 --- a/scnserver/db/impl/primary/clients.go +++ b/scnserver/db/impl/primary/clients.go @@ -53,7 +53,7 @@ func (db *Database) GetClient(ctx db.TxContext, userid models.UserID, clientid m }, sq.SModeExtended, sq.Safe) } -func (db *Database) GetClientByID(ctx db.TxContext, clientid models.ClientID) (*models.Client, error) { +func (db *Database) GetClientByIDOpt(ctx db.TxContext, clientid models.ClientID) (*models.Client, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err diff --git a/scnserver/db/impl/primary/keytokens.go b/scnserver/db/impl/primary/keytokens.go index 1554268..a78e557 100644 --- a/scnserver/db/impl/primary/keytokens.go +++ b/scnserver/db/impl/primary/keytokens.go @@ -1,12 +1,13 @@ package primary import ( + "strings" + "time" + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/models" "git.blackforestbytes.com/BlackForestBytes/goext/langext" "git.blackforestbytes.com/BlackForestBytes/goext/sq" - "strings" - "time" ) 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) { @@ -67,7 +68,7 @@ func (db *Database) GetKeyTokenByID(ctx db.TxContext, keyTokenid models.KeyToken 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) { +func (db *Database) GetKeyTokenByTokenOpt(ctx db.TxContext, key string) (*models.KeyToken, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return nil, err diff --git a/scnserver/jobs/deliveryRetryJob.go b/scnserver/jobs/deliveryRetryJob.go index eea14e0..59cb1ae 100644 --- a/scnserver/jobs/deliveryRetryJob.go +++ b/scnserver/jobs/deliveryRetryJob.go @@ -1,14 +1,15 @@ package jobs import ( + "errors" + "fmt" + "time" + "blackforestbytes.com/simplecloudnotifier/db/simplectx" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" - "errors" - "fmt" "git.blackforestbytes.com/BlackForestBytes/goext/syncext" "github.com/rs/zerolog/log" - "time" ) type DeliveryRetryJob struct { @@ -208,7 +209,7 @@ func (j *DeliveryRetryJob) redeliver(ctx *simplectx.SimpleContext, delivery mode return } - channel, err := j.app.Database.Primary.GetChannelByID(ctx, msg.ChannelID) + channel, err := j.app.Database.Primary.GetChannelByIDOpt(ctx, msg.ChannelID) if err != nil { log.Err(err).Str("ChannelID", msg.ChannelID.String()).Msg("Failed to get channel") ctx.RollbackTransaction() diff --git a/scnserver/logic/application.go b/scnserver/logic/application.go index e1c6aca..5393ff2 100644 --- a/scnserver/logic/application.go +++ b/scnserver/logic/application.go @@ -245,7 +245,7 @@ func (app *Application) getPermissions(ctx db.TxContext, hdr string) (models.Per key := strings.TrimSpace(hdr[4:]) - tok, err := app.Database.Primary.GetKeyTokenByToken(ctx, key) + tok, err := app.Database.Primary.GetKeyTokenByTokenOpt(ctx, key) if err != nil { return models.PermissionSet{}, err } diff --git a/scnserver/logic/permissions.go b/scnserver/logic/permissions.go index 982786d..ab7a26b 100644 --- a/scnserver/logic/permissions.go +++ b/scnserver/logic/permissions.go @@ -75,7 +75,7 @@ func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginext.HTT func (ac *AppContext) CheckPermissionSend(channel models.Channel, key string) (*models.KeyToken, *ginext.HTTPResponse) { - keytok, err := ac.app.Database.Primary.GetKeyTokenByToken(ac, key) + keytok, err := ac.app.Database.Primary.GetKeyTokenByTokenOpt(ac, key) if err != nil { return nil, langext.Ptr(ginresp.APIError(ac.ginContext, 500, apierr.DATABASE_ERROR, "Failed to query token", err)) }