2023-05-28 13:39:20 +02:00
package handler
import (
"blackforestbytes.com/simplecloudnotifier/api/apierr"
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
2024-07-16 17:19:55 +02:00
"blackforestbytes.com/simplecloudnotifier/logic"
2023-05-28 13:39:20 +02:00
"blackforestbytes.com/simplecloudnotifier/models"
"database/sql"
2023-07-30 15:58:37 +02:00
"errors"
2023-05-28 13:39:20 +02:00
"fmt"
2024-07-15 17:26:55 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
2023-05-28 13:39:20 +02:00
"gogs.mikescher.com/BlackForestBytes/goext/langext"
"gogs.mikescher.com/BlackForestBytes/goext/mathext"
"net/http"
"strings"
)
// ListChannels swaggerdoc
//
// @Summary List channels of a user (subscribed/owned/all)
// @Description The possible values for 'selector' are:
// @Description - "owned" Return all channels of the user
// @Description - "subscribed" Return all channels that the user is subscribing to
// @Description - "all" Return channels that the user owns or is subscribing
// @Description - "subscribed_any" Return all channels that the user is subscribing to (even unconfirmed)
// @Description - "all_any" Return channels that the user owns or is subscribing (even unconfirmed)
//
// @ID api-channels-list
// @Tags API-v2
//
2023-06-10 00:15:42 +02:00
// @Param uid path string true "UserID"
2023-05-28 13:39:20 +02:00
// @Param selector query string false "Filter channels (default: owned)" Enums(owned, subscribed, all, subscribed_any, all_any)
//
// @Success 200 {object} handler.ListChannels.response
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
// @Failure 500 {object} ginresp.apiError "internal server error"
//
// @Router /api/v2/users/{uid}/channels [GET]
2024-07-15 17:26:55 +02:00
func ( h APIHandler ) ListChannels ( pctx ginext . PreContext ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
type uri struct {
UserID models . UserID ` uri:"uid" binding:"entityid" `
}
type query struct {
Selector * string ` json:"selector" form:"selector" enums:"owned,subscribed_any,all_any,subscribed,all" `
}
type response struct {
2024-09-15 21:07:46 +02:00
Channels [ ] models . ChannelWithSubscription ` json:"channels" `
2023-05-28 13:39:20 +02:00
}
var u uri
var q query
2024-07-15 17:26:55 +02:00
ctx , g , errResp := pctx . URI ( & u ) . Query ( & q ) . Start ( )
if errResp != nil {
return * errResp
}
defer ctx . Cancel ( )
2024-09-16 15:17:20 +02:00
return h . app . DoRequest ( ctx , g , models . TLockRead , func ( ctx * logic . AppContext , finishSuccess func ( r ginext . HTTPResponse ) ginext . HTTPResponse ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if permResp := ctx . CheckPermissionUserRead ( u . UserID ) ; permResp != nil {
return * permResp
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
sel := strings . ToLower ( langext . Coalesce ( q . Selector , "owned" ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if sel == "owned" {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channels , err := h . database . ListChannelsByOwner ( ctx , u . UserID , u . UserID )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channels" , err )
}
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSONWithFilter ( http . StatusOK , response { Channels : channels } , "INCLUDE_KEY" ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} else if sel == "subscribed_any" {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channels , err := h . database . ListChannelsBySubscriber ( ctx , u . UserID , nil )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channels" , err )
}
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSON ( http . StatusOK , response { Channels : channels } ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} else if sel == "all_any" {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channels , err := h . database . ListChannelsByAccess ( ctx , u . UserID , nil )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channels" , err )
}
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSON ( http . StatusOK , response { Channels : channels } ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} else if sel == "subscribed" {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channels , err := h . database . ListChannelsBySubscriber ( ctx , u . UserID , langext . Ptr ( true ) )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channels" , err )
}
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSON ( http . StatusOK , response { Channels : channels } ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} else if sel == "all" {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channels , err := h . database . ListChannelsByAccess ( ctx , u . UserID , langext . Ptr ( true ) )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channels" , err )
}
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSON ( http . StatusOK , response { Channels : channels } ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} else {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
return ginresp . APIError ( g , 400 , apierr . INVALID_ENUM_VALUE , "Invalid value for the [selector] parameter" , nil )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} )
2023-05-28 13:39:20 +02:00
}
// GetChannel swaggerdoc
//
// @Summary Get a single channel
// @ID api-channels-get
// @Tags API-v2
//
2023-05-28 17:04:44 +02:00
// @Param uid path string true "UserID"
// @Param cid path string true "ChannelID"
2023-05-28 13:39:20 +02:00
//
2024-09-15 21:07:46 +02:00
// @Success 200 {object} models.ChannelWithSubscription
2023-05-28 13:39:20 +02:00
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
// @Failure 404 {object} ginresp.apiError "channel not found"
// @Failure 500 {object} ginresp.apiError "internal server error"
//
// @Router /api/v2/users/{uid}/channels/{cid} [GET]
2024-07-15 17:26:55 +02:00
func ( h APIHandler ) GetChannel ( pctx ginext . PreContext ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
type uri struct {
UserID models . UserID ` uri:"uid" binding:"entityid" `
ChannelID models . ChannelID ` uri:"cid" binding:"entityid" `
}
var u uri
2024-07-16 17:19:55 +02:00
ctx , g , errResp := pctx . URI ( & u ) . Start ( )
2023-05-28 13:39:20 +02:00
if errResp != nil {
return * errResp
}
defer ctx . Cancel ( )
2024-09-16 15:17:20 +02:00
return h . app . DoRequest ( ctx , g , models . TLockRead , func ( ctx * logic . AppContext , finishSuccess func ( r ginext . HTTPResponse ) ginext . HTTPResponse ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if permResp := ctx . CheckPermissionUserRead ( u . UserID ) ; permResp != nil {
return * permResp
}
channel , err := h . database . GetChannel ( ctx , u . UserID , u . ChannelID , true )
if errors . Is ( err , sql . ErrNoRows ) {
return ginresp . APIError ( g , 404 , apierr . CHANNEL_NOT_FOUND , "Channel not found" , err )
}
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channel" , err )
}
2023-05-28 13:39:20 +02:00
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSONWithFilter ( http . StatusOK , channel , "INCLUDE_KEY" ) )
2024-07-16 17:19:55 +02:00
} )
2023-05-28 13:39:20 +02:00
}
// CreateChannel swaggerdoc
//
// @Summary Create a new (empty) channel
// @ID api-channels-create
// @Tags API-v2
//
2023-06-10 00:15:42 +02:00
// @Param uid path string true "UserID"
2023-05-28 13:39:20 +02:00
// @Param post_body body handler.CreateChannel.body false " "
//
2024-09-15 21:07:46 +02:00
// @Success 200 {object} models.ChannelWithSubscription
2023-05-28 13:39:20 +02:00
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
// @Failure 409 {object} ginresp.apiError "channel already exists"
// @Failure 500 {object} ginresp.apiError "internal server error"
//
// @Router /api/v2/users/{uid}/channels [POST]
2024-07-15 17:26:55 +02:00
func ( h APIHandler ) CreateChannel ( pctx ginext . PreContext ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
type uri struct {
UserID models . UserID ` uri:"uid" binding:"entityid" `
}
type body struct {
Name string ` json:"name" `
Subscribe * bool ` json:"subscribe" `
2024-02-24 12:20:41 +01:00
Description * string ` json:"description" `
2023-05-28 13:39:20 +02:00
}
var u uri
var b body
2024-07-16 17:19:55 +02:00
ctx , g , errResp := pctx . URI ( & u ) . Body ( & b ) . Start ( )
2023-05-28 13:39:20 +02:00
if errResp != nil {
return * errResp
}
defer ctx . Cancel ( )
2024-09-16 15:17:20 +02:00
return h . app . DoRequest ( ctx , g , models . TLockReadWrite , func ( ctx * logic . AppContext , finishSuccess func ( r ginext . HTTPResponse ) ginext . HTTPResponse ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if permResp := ctx . CheckPermissionUserAdmin ( u . UserID ) ; permResp != nil {
return * permResp
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if b . Name == "" {
return ginresp . APIError ( g , 400 , apierr . INVALID_BODY_PARAM , "Missing parameter: name" , nil )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channelDisplayName := h . app . NormalizeChannelDisplayName ( b . Name )
channelInternalName := h . app . NormalizeChannelInternalName ( b . Name )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channelExisting , err := h . database . GetChannelByName ( ctx , u . UserID , channelInternalName )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channel" , err )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
user , err := h . database . GetUser ( ctx , u . UserID )
if errors . Is ( err , sql . ErrNoRows ) {
return ginresp . APIError ( g , 400 , apierr . USER_NOT_FOUND , "User not found" , nil )
}
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query user" , err )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if len ( channelDisplayName ) > user . MaxChannelNameLength ( ) {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_TOO_LONG , fmt . Sprintf ( "Channel too long (max %d characters)" , user . MaxChannelNameLength ( ) ) , nil )
}
if len ( strings . TrimSpace ( channelDisplayName ) ) == 0 {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_NAME_EMPTY , fmt . Sprintf ( "Channel displayname cannot be empty" ) , nil )
}
if len ( channelInternalName ) > user . MaxChannelNameLength ( ) {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_TOO_LONG , fmt . Sprintf ( "Channel too long (max %d characters)" , user . MaxChannelNameLength ( ) ) , nil )
}
if len ( strings . TrimSpace ( channelInternalName ) ) == 0 {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_NAME_EMPTY , fmt . Sprintf ( "Channel internalname cannot be empty" ) , nil )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if channelExisting != nil {
return ginresp . APIError ( g , 409 , apierr . CHANNEL_ALREADY_EXISTS , "Channel with this name already exists" , nil )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
subscribeKey := h . app . GenerateRandomAuthKey ( )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channel , err := h . database . CreateChannel ( ctx , u . UserID , channelDisplayName , channelInternalName , subscribeKey , b . Description )
2023-05-28 13:39:20 +02:00
if err != nil {
2024-07-16 17:19:55 +02:00
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to create channel" , err )
2023-05-28 13:39:20 +02:00
}
2024-07-16 17:19:55 +02:00
if langext . Coalesce ( b . Subscribe , true ) {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
sub , err := h . database . CreateSubscription ( ctx , u . UserID , channel , true )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to create subscription" , err )
}
2023-05-28 13:39:20 +02:00
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSONWithFilter ( http . StatusOK , channel . WithSubscription ( langext . Ptr ( sub ) ) , "INCLUDE_KEY" ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} else {
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSONWithFilter ( http . StatusOK , channel . WithSubscription ( nil ) , "INCLUDE_KEY" ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
}
} )
2023-05-28 13:39:20 +02:00
}
// UpdateChannel swaggerdoc
//
// @Summary (Partially) update a channel
// @ID api-channels-update
// @Tags API-v2
//
2023-06-10 00:15:42 +02:00
// @Param uid path string true "UserID"
// @Param cid path string true "ChannelID"
2023-05-28 13:39:20 +02:00
//
// @Param subscribe_key body string false "Send `true` to create a new subscribe_key"
// @Param send_key body string false "Send `true` to create a new send_key"
// @Param display_name body string false "Change the cahnnel display-name (only chnages to lowercase/uppercase are allowed - internal_name must stay the same)"
//
2024-09-15 21:07:46 +02:00
// @Success 200 {object} models.ChannelWithSubscription
2023-05-28 13:39:20 +02:00
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
// @Failure 404 {object} ginresp.apiError "channel not found"
// @Failure 500 {object} ginresp.apiError "internal server error"
//
// @Router /api/v2/users/{uid}/channels/{cid} [PATCH]
2024-07-15 17:26:55 +02:00
func ( h APIHandler ) UpdateChannel ( pctx ginext . PreContext ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
type uri struct {
UserID models . UserID ` uri:"uid" binding:"entityid" `
ChannelID models . ChannelID ` uri:"cid" binding:"entityid" `
}
type body struct {
RefreshSubscribeKey * bool ` json:"subscribe_key" `
DisplayName * string ` json:"display_name" `
DescriptionName * string ` json:"description_name" `
}
var u uri
var b body
2024-07-16 17:19:55 +02:00
ctx , g , errResp := pctx . URI ( & u ) . Body ( & b ) . Start ( )
2023-05-28 13:39:20 +02:00
if errResp != nil {
return * errResp
}
defer ctx . Cancel ( )
2024-09-16 15:17:20 +02:00
return h . app . DoRequest ( ctx , g , models . TLockReadWrite , func ( ctx * logic . AppContext , finishSuccess func ( r ginext . HTTPResponse ) ginext . HTTPResponse ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if permResp := ctx . CheckPermissionUserAdmin ( u . UserID ) ; permResp != nil {
return * permResp
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
_ , err := h . database . GetChannel ( ctx , u . UserID , u . ChannelID , true )
if errors . Is ( err , sql . ErrNoRows ) {
return ginresp . APIError ( g , 404 , apierr . CHANNEL_NOT_FOUND , "Channel not found" , err )
}
2023-05-28 13:39:20 +02:00
if err != nil {
2024-07-16 17:19:55 +02:00
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channel" , err )
2023-05-28 13:39:20 +02:00
}
2024-07-16 17:19:55 +02:00
user , err := h . database . GetUser ( ctx , u . UserID )
if errors . Is ( err , sql . ErrNoRows ) {
return ginresp . APIError ( g , 400 , apierr . USER_NOT_FOUND , "User not found" , nil )
}
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query user" , err )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if langext . Coalesce ( b . RefreshSubscribeKey , false ) {
newkey := h . app . GenerateRandomAuthKey ( )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
err := h . database . UpdateChannelSubscribeKey ( ctx , u . ChannelID , newkey )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to update channel" , err )
}
2023-05-28 13:39:20 +02:00
}
2024-07-16 17:19:55 +02:00
if b . DisplayName != nil {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
newDisplayName := h . app . NormalizeChannelDisplayName ( * b . DisplayName )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if len ( newDisplayName ) > user . MaxChannelNameLength ( ) {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_TOO_LONG , fmt . Sprintf ( "Channel too long (max %d characters)" , user . MaxChannelNameLength ( ) ) , nil )
}
if len ( strings . TrimSpace ( newDisplayName ) ) == 0 {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_NAME_EMPTY , fmt . Sprintf ( "Channel displayname cannot be empty" ) , nil )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
err := h . database . UpdateChannelDisplayName ( ctx , u . ChannelID , newDisplayName )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to update channel" , err )
}
2023-05-28 13:39:20 +02:00
}
2024-07-16 17:19:55 +02:00
if b . DescriptionName != nil {
var descName * string = nil
if strings . TrimSpace ( * b . DescriptionName ) != "" {
descName = langext . Ptr ( strings . TrimSpace ( * b . DescriptionName ) )
}
if descName != nil && len ( * descName ) > user . MaxChannelDescriptionLength ( ) {
return ginresp . APIError ( g , 400 , apierr . CHANNEL_DESCRIPTION_TOO_LONG , fmt . Sprintf ( "Channel-Description too long (max %d characters)" , user . MaxChannelDescriptionLength ( ) ) , nil )
}
err := h . database . UpdateChannelDescriptionName ( ctx , u . ChannelID , descName )
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to update channel" , err )
}
2023-05-28 13:39:20 +02:00
}
2024-07-16 17:19:55 +02:00
channel , err := h . database . GetChannel ( ctx , u . UserID , u . ChannelID , true )
2023-05-28 13:39:20 +02:00
if err != nil {
2024-07-16 17:19:55 +02:00
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query (updated) channel" , err )
2023-05-28 13:39:20 +02:00
}
2024-09-15 21:07:46 +02:00
return finishSuccess ( ginext . JSONWithFilter ( http . StatusOK , channel , "INCLUDE_KEY" ) )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
} )
2023-05-28 13:39:20 +02:00
}
// ListChannelMessages swaggerdoc
//
// @Summary List messages of a channel
// @Description The next_page_token is an opaque token, the special value "@start" (or empty-string) is the beginning and "@end" is the end
// @Description Simply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query
// @Description If there are no more entries the token "@end" will be returned
// @Description By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size)
// @ID api-channel-messages
// @Tags API-v2
//
// @Param query_data query handler.ListChannelMessages.query false " "
2023-06-10 00:15:42 +02:00
// @Param uid path string true "UserID"
// @Param cid path string true "ChannelID"
2023-05-28 13:39:20 +02:00
//
// @Success 200 {object} handler.ListChannelMessages.response
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
// @Failure 404 {object} ginresp.apiError "channel not found"
// @Failure 500 {object} ginresp.apiError "internal server error"
//
// @Router /api/v2/users/{uid}/channels/{cid}/messages [GET]
2024-07-15 17:26:55 +02:00
func ( h APIHandler ) ListChannelMessages ( pctx ginext . PreContext ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
type uri struct {
ChannelUserID models . UserID ` uri:"uid" binding:"entityid" `
ChannelID models . ChannelID ` uri:"cid" binding:"entityid" `
}
type query struct {
PageSize * int ` json:"page_size" form:"page_size" `
NextPageToken * string ` json:"next_page_token" form:"next_page_token" `
Trimmed * bool ` json:"trimmed" form:"trimmed" `
}
type response struct {
2024-09-15 21:07:46 +02:00
Messages [ ] models . Message ` json:"messages" `
NextPageToken string ` json:"next_page_token" `
PageSize int ` json:"page_size" `
2024-09-20 15:36:16 +02:00
TotalCount int64 ` json:"total_count" `
2023-05-28 13:39:20 +02:00
}
var u uri
var q query
2024-07-16 17:19:55 +02:00
ctx , g , errResp := pctx . URI ( & u ) . Query ( & q ) . Start ( )
2023-05-28 13:39:20 +02:00
if errResp != nil {
return * errResp
}
defer ctx . Cancel ( )
2024-09-16 15:17:20 +02:00
return h . app . DoRequest ( ctx , g , models . TLockRead , func ( ctx * logic . AppContext , finishSuccess func ( r ginext . HTTPResponse ) ginext . HTTPResponse ) ginext . HTTPResponse {
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
trimmed := langext . Coalesce ( q . Trimmed , true )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
maxPageSize := langext . Conditional ( trimmed , 16 , 256 )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
pageSize := mathext . Clamp ( langext . Coalesce ( q . PageSize , 64 ) , 1 , maxPageSize )
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
channel , err := h . database . GetChannel ( ctx , u . ChannelUserID , u . ChannelID , false )
if errors . Is ( err , sql . ErrNoRows ) {
return ginresp . APIError ( g , 404 , apierr . CHANNEL_NOT_FOUND , "Channel not found" , err )
}
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query channel" , err )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
if permResp := ctx . CheckPermissionChanMessagesRead ( channel . Channel ) ; permResp != nil {
return * permResp
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
tok , err := ct . Decode ( langext . Coalesce ( q . NextPageToken , "" ) )
if err != nil {
return ginresp . APIError ( g , 400 , apierr . PAGETOKEN_ERROR , "Failed to decode next_page_token" , err )
}
2023-05-28 13:39:20 +02:00
2024-07-16 17:19:55 +02:00
filter := models . MessageFilter {
ChannelID : langext . Ptr ( [ ] models . ChannelID { channel . ChannelID } ) ,
}
2023-05-28 13:39:20 +02:00
2024-09-20 15:36:16 +02:00
messages , npt , totalCount , err := h . database . ListMessages ( ctx , filter , & pageSize , tok )
2024-07-16 17:19:55 +02:00
if err != nil {
return ginresp . APIError ( g , 500 , apierr . DATABASE_ERROR , "Failed to query messages" , err )
}
if trimmed {
2024-09-15 21:07:46 +02:00
res := langext . ArrMap ( messages , func ( v models . Message ) models . Message { return v . Trim ( ) } )
2024-09-20 15:36:16 +02:00
return finishSuccess ( ginext . JSON ( http . StatusOK , response { Messages : res , NextPageToken : npt . Token ( ) , PageSize : pageSize , TotalCount : totalCount } ) )
2024-07-16 17:19:55 +02:00
} else {
2024-09-20 15:36:16 +02:00
return finishSuccess ( ginext . JSON ( http . StatusOK , response { Messages : messages , NextPageToken : npt . Token ( ) , PageSize : pageSize , TotalCount : totalCount } ) )
2024-07-16 17:19:55 +02:00
}
} )
2023-05-28 13:39:20 +02:00
}