From 55d0dea835191c07be70c8b1d3018a5cf4b01369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Mon, 15 Jul 2024 17:26:55 +0200 Subject: [PATCH] Refactor server to go-sqlite and ginext [WIP] --- android/.idea/deploymentTargetSelector.xml | 10 + android/.idea/other.xml | 263 +++++++++++++++++++++ scnserver/TODO.md | 2 - scnserver/api/ginext/cors.go | 21 -- scnserver/api/ginext/gin.go | 31 --- scnserver/api/ginext/handler.go | 24 -- scnserver/api/ginresp/resp.go | 145 +++--------- scnserver/api/handler/apiChannel.go | 38 +-- scnserver/api/handler/apiClient.go | 32 +-- scnserver/api/handler/apiKeyToken.go | 38 +-- scnserver/api/handler/apiMessage.go | 20 +- scnserver/api/handler/apiPreview.go | 20 +- scnserver/api/handler/apiSubscription.go | 40 ++-- scnserver/api/handler/apiUser.go | 22 +- scnserver/api/handler/common.go | 56 +++-- scnserver/api/handler/compat.go | 34 +-- scnserver/api/handler/external.go | 6 +- scnserver/api/handler/message.go | 9 +- scnserver/api/handler/website.go | 73 ++++-- scnserver/api/router.go | 154 ++++++------ scnserver/api/{ginresp => }/wrapper.go | 56 +++-- scnserver/cmd/dbhash/main.go | 7 +- scnserver/cmd/scnserver/main.go | 10 +- scnserver/db/impl/logs/database.go | 6 +- scnserver/db/impl/primary/database.go | 6 +- scnserver/db/impl/requests/database.go | 8 +- scnserver/go.mod | 33 ++- scnserver/go.sum | 54 +++-- scnserver/logic/appcontext.go | 3 +- scnserver/logic/application.go | 122 +++------- scnserver/logic/message.go | 2 +- scnserver/logic/permissions.go | 14 +- scnserver/models/enums_gen.go | 2 +- scnserver/models/ids_gen.go | 2 +- scnserver/swagger/swagger.go | 16 +- scnserver/swagger/swagger.json | 250 ++++---------------- scnserver/swagger/swagger.yaml | 245 +++++-------------- scnserver/test/util/log.go | 1 - scnserver/test/util/webserver.go | 1 - 39 files changed, 880 insertions(+), 996 deletions(-) create mode 100644 android/.idea/deploymentTargetSelector.xml create mode 100644 android/.idea/other.xml delete mode 100644 scnserver/api/ginext/cors.go delete mode 100644 scnserver/api/ginext/gin.go delete mode 100644 scnserver/api/ginext/handler.go rename scnserver/api/{ginresp => }/wrapper.go (75%) diff --git a/android/.idea/deploymentTargetSelector.xml b/android/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/android/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/.idea/other.xml b/android/.idea/other.xml new file mode 100644 index 0000000..0d3a1fb --- /dev/null +++ b/android/.idea/other.xml @@ -0,0 +1,263 @@ + + + + + + \ No newline at end of file diff --git a/scnserver/TODO.md b/scnserver/TODO.md index 82ca82a..31eef31 100644 --- a/scnserver/TODO.md +++ b/scnserver/TODO.md @@ -68,8 +68,6 @@ - cli app (?) - - Use "github.com/glebarez/go-sqlite" instead of mattn3 (see ai-sig alarmserver) - #### FUTURE - Remove compat, especially do not create compat id for every new message... \ No newline at end of file diff --git a/scnserver/api/ginext/cors.go b/scnserver/api/ginext/cors.go deleted file mode 100644 index a4f638f..0000000 --- a/scnserver/api/ginext/cors.go +++ /dev/null @@ -1,21 +0,0 @@ -package ginext - -import ( - "github.com/gin-gonic/gin" - "net/http" -) - -func CorsMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "*") - c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") - c.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, PATCH, DELETE") - - if c.Request.Method == "OPTIONS" { - c.AbortWithStatus(http.StatusOK) - } else { - c.Next() - } - } -} diff --git a/scnserver/api/ginext/gin.go b/scnserver/api/ginext/gin.go deleted file mode 100644 index c41045d..0000000 --- a/scnserver/api/ginext/gin.go +++ /dev/null @@ -1,31 +0,0 @@ -package ginext - -import ( - scn "blackforestbytes.com/simplecloudnotifier" - "github.com/gin-gonic/gin" -) - -var SuppressGinLogs = false - -func NewEngine(cfg scn.Config) *gin.Engine { - engine := gin.New() - - engine.RedirectFixedPath = false - engine.RedirectTrailingSlash = false - - if cfg.Cors { - engine.Use(CorsMiddleware()) - } - - if cfg.GinDebug { - ginlogger := gin.Logger() - engine.Use(func(context *gin.Context) { - if SuppressGinLogs { - return - } - ginlogger(context) - }) - } - - return engine -} diff --git a/scnserver/api/ginext/handler.go b/scnserver/api/ginext/handler.go deleted file mode 100644 index 44662e6..0000000 --- a/scnserver/api/ginext/handler.go +++ /dev/null @@ -1,24 +0,0 @@ -package ginext - -import ( - "github.com/gin-gonic/gin" - "net/http" -) - -func RedirectFound(newuri string) gin.HandlerFunc { - return func(g *gin.Context) { - g.Redirect(http.StatusFound, newuri) - } -} - -func RedirectTemporary(newuri string) gin.HandlerFunc { - return func(g *gin.Context) { - g.Redirect(http.StatusTemporaryRedirect, newuri) - } -} - -func RedirectPermanent(newuri string) gin.HandlerFunc { - return func(g *gin.Context) { - g.Redirect(http.StatusPermanentRedirect, newuri) - } -} diff --git a/scnserver/api/ginresp/resp.go b/scnserver/api/ginresp/resp.go index 0d40338..bb1b895 100644 --- a/scnserver/api/ginresp/resp.go +++ b/scnserver/api/ginresp/resp.go @@ -7,114 +7,43 @@ import ( "fmt" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" json "gogs.mikescher.com/BlackForestBytes/goext/gojson" "gogs.mikescher.com/BlackForestBytes/goext/langext" "runtime/debug" "strings" ) -type HTTPResponse interface { - Write(g *gin.Context) - Statuscode() int - BodyString() *string - ContentType() string +type cookieval struct { + name string + value string + maxAge int + path string + domain string + secure bool + httpOnly bool } -type jsonHTTPResponse struct { - statusCode int - data any -} - -func (j jsonHTTPResponse) Write(g *gin.Context) { - g.Render(j.statusCode, json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true}) -} - -func (j jsonHTTPResponse) Statuscode() int { - return j.statusCode -} - -func (j jsonHTTPResponse) BodyString() *string { - v, err := json.Marshal(j.data) - if err != nil { - return nil - } - return langext.Ptr(string(v)) -} - -func (j jsonHTTPResponse) ContentType() string { - return "application/json" -} - -type emptyHTTPResponse struct { - statusCode int -} - -func (j emptyHTTPResponse) Write(g *gin.Context) { - g.Status(j.statusCode) -} - -func (j emptyHTTPResponse) Statuscode() int { - return j.statusCode -} - -func (j emptyHTTPResponse) BodyString() *string { - return nil -} - -func (j emptyHTTPResponse) ContentType() string { - return "" -} - -type textHTTPResponse struct { - statusCode int - data string -} - -func (j textHTTPResponse) Write(g *gin.Context) { - g.String(j.statusCode, "%s", j.data) -} - -func (j textHTTPResponse) Statuscode() int { - return j.statusCode -} - -func (j textHTTPResponse) BodyString() *string { - return langext.Ptr(j.data) -} - -func (j textHTTPResponse) ContentType() string { - return "text/plain" -} - -type dataHTTPResponse struct { - statusCode int - data []byte - contentType string -} - -func (j dataHTTPResponse) Write(g *gin.Context) { - g.Data(j.statusCode, j.contentType, j.data) -} - -func (j dataHTTPResponse) Statuscode() int { - return j.statusCode -} - -func (j dataHTTPResponse) BodyString() *string { - return langext.Ptr(string(j.data)) -} - -func (j dataHTTPResponse) ContentType() string { - return j.contentType +type headerval struct { + Key string + Val string } type errorHTTPResponse struct { statusCode int data any error error + headers []headerval + cookies []cookieval } func (j errorHTTPResponse) Write(g *gin.Context) { + for _, v := range j.headers { + g.Header(v.Key, v.Val) + } + for _, v := range j.cookies { + g.SetCookie(v.name, v.value, v.maxAge, v.path, v.domain, v.secure, v.httpOnly) + } g.JSON(j.statusCode, j.data) } @@ -122,7 +51,7 @@ func (j errorHTTPResponse) Statuscode() int { return j.statusCode } -func (j errorHTTPResponse) BodyString() *string { +func (j errorHTTPResponse) BodyString(g *gin.Context) *string { v, err := json.Marshal(j.data) if err != nil { return nil @@ -134,39 +63,41 @@ func (j errorHTTPResponse) ContentType() string { return "application/json" } -func Status(sc int) HTTPResponse { - return &emptyHTTPResponse{statusCode: sc} +func (j errorHTTPResponse) WithHeader(k string, v string) ginext.HTTPResponse { + j.headers = append(j.headers, headerval{k, v}) + return j } -func JSON(sc int, data any) HTTPResponse { - return &jsonHTTPResponse{statusCode: sc, data: data} +func (j errorHTTPResponse) WithCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool) ginext.HTTPResponse { + j.cookies = append(j.cookies, cookieval{name, value, maxAge, path, domain, secure, httpOnly}) + return j } -func Data(sc int, contentType string, data []byte) HTTPResponse { - return &dataHTTPResponse{statusCode: sc, contentType: contentType, data: data} +func (j errorHTTPResponse) IsSuccess() bool { + return false } -func Text(sc int, data string) HTTPResponse { - return &textHTTPResponse{statusCode: sc, data: data} +func (j errorHTTPResponse) Headers() []string { + return langext.ArrMap(j.headers, func(v headerval) string { return v.Key + "=" + v.Val }) } -func InternalError(e error) HTTPResponse { +func InternalError(e error) ginext.HTTPResponse { return createApiError(nil, "InternalError", 500, apierr.INTERNAL_EXCEPTION, 0, e.Error(), e) } -func APIError(g *gin.Context, status int, errorid apierr.APIError, msg string, e error) HTTPResponse { +func APIError(g *gin.Context, status int, errorid apierr.APIError, msg string, e error) ginext.HTTPResponse { return createApiError(g, "APIError", status, errorid, 0, msg, e) } -func SendAPIError(g *gin.Context, status int, errorid apierr.APIError, highlight apihighlight.ErrHighlight, msg string, e error) HTTPResponse { +func SendAPIError(g *gin.Context, status int, errorid apierr.APIError, highlight apihighlight.ErrHighlight, msg string, e error) ginext.HTTPResponse { return createApiError(g, "SendAPIError", status, errorid, highlight, msg, e) } -func NotImplemented(g *gin.Context) HTTPResponse { +func NotImplemented(pctx ginext.PreContext) ginext.HTTPResponse { return createApiError(g, "NotImplemented", 500, apierr.NOT_IMPLEMENTED, 0, "Not Implemented", nil) } -func createApiError(g *gin.Context, ident string, status int, errorid apierr.APIError, highlight apihighlight.ErrHighlight, msg string, e error) HTTPResponse { +func createApiError(g *gin.Context, ident string, status int, errorid apierr.APIError, highlight apihighlight.ErrHighlight, msg string, e error) ginext.HTTPResponse { reqUri := "" if g != nil && g.Request != nil { reqUri = g.Request.Method + " :: " + g.Request.RequestURI @@ -207,6 +138,6 @@ func createApiError(g *gin.Context, ident string, status int, errorid apierr.API } } -func CompatAPIError(errid int, msg string) HTTPResponse { - return &jsonHTTPResponse{statusCode: 200, data: compatAPIError{Success: false, ErrorID: errid, Message: msg}} +func CompatAPIError(errid int, msg string) ginext.HTTPResponse { + return ginext.JSON(200, compatAPIError{Success: false, ErrorID: errid, Message: msg}) } diff --git a/scnserver/api/handler/apiChannel.go b/scnserver/api/handler/apiChannel.go index 479b1be..754d2a0 100644 --- a/scnserver/api/handler/apiChannel.go +++ b/scnserver/api/handler/apiChannel.go @@ -8,7 +8,7 @@ import ( "database/sql" "errors" "fmt" - "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/mathext" "net/http" @@ -37,7 +37,7 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/channels [GET] -func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListChannels(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -50,6 +50,12 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { var u uri var q query + ctx, g, errResp := pctx.URI(&u).Query(&q).Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + ctx, errResp := h.app.StartRequest(g, &u, &q, nil, nil) if errResp != nil { return *errResp @@ -110,7 +116,7 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Channels: res})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Channels: res})) } // GetChannel swaggerdoc @@ -129,14 +135,14 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/channels/{cid} [GET] -func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetChannel(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` ChannelID models.ChannelID `uri:"cid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -154,7 +160,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON(true))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, channel.JSON(true))) } // CreateChannel swaggerdoc @@ -173,7 +179,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/channels [POST] -func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) CreateChannel(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -186,7 +192,7 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -247,11 +253,11 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create subscription", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.WithSubscription(langext.Ptr(sub)).JSON(true))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, channel.WithSubscription(langext.Ptr(sub)).JSON(true))) } else { - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.WithSubscription(nil).JSON(true))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, channel.WithSubscription(nil).JSON(true))) } @@ -277,7 +283,7 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/channels/{cid} [PATCH] -func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) UpdateChannel(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` ChannelID models.ChannelID `uri:"cid" binding:"entityid"` @@ -290,7 +296,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -367,7 +373,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) channel", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON(true))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, channel.JSON(true))) } // ListChannelMessages swaggerdoc @@ -391,7 +397,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/channels/{cid}/messages [GET] -func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListChannelMessages(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { ChannelUserID models.UserID `uri:"uid" binding:"entityid"` ChannelID models.ChannelID `uri:"cid" binding:"entityid"` @@ -410,7 +416,7 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse { var u uri var q query - ctx, errResp := h.app.StartRequest(g, &u, &q, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Query(&q).Start()) if errResp != nil { return *errResp } @@ -455,5 +461,5 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse { res = langext.ArrMap(messages, func(v models.Message) models.MessageJSON { return v.FullJSON() }) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Messages: res, NextPageToken: npt.Token(), PageSize: pageSize})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Messages: res, NextPageToken: npt.Token(), PageSize: pageSize})) } diff --git a/scnserver/api/handler/apiClient.go b/scnserver/api/handler/apiClient.go index 86161dd..b672789 100644 --- a/scnserver/api/handler/apiClient.go +++ b/scnserver/api/handler/apiClient.go @@ -6,7 +6,7 @@ import ( "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "errors" - "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" ) @@ -25,7 +25,7 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/clients [GET] -func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListClients(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -34,7 +34,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -51,7 +51,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { res := langext.ArrMap(clients, func(v models.Client) models.ClientJSON { return v.JSON() }) - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Clients: res})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Clients: res})) } // GetClient swaggerdoc @@ -70,14 +70,14 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/clients/{cid} [GET] -func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetClient(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` ClientID models.ClientID `uri:"cid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -95,7 +95,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, client.JSON())) } // AddClient swaggerdoc @@ -114,7 +114,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/clients [POST] -func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) AddClient(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -128,7 +128,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -153,7 +153,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, client.JSON())) } // DeleteClient swaggerdoc @@ -172,14 +172,14 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/clients/{cid} [DELETE] -func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) DeleteClient(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` ClientID models.ClientID `uri:"cid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -202,7 +202,7 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, client.JSON())) } // UpdateClient swaggerdoc @@ -225,7 +225,7 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/clients/{cid} [PATCH] -func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) UpdateClient(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` ClientID models.ClientID `uri:"cid" binding:"entityid"` @@ -239,7 +239,7 @@ func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -303,5 +303,5 @@ func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, client.JSON())) } diff --git a/scnserver/api/handler/apiKeyToken.go b/scnserver/api/handler/apiKeyToken.go index 646625b..636679e 100644 --- a/scnserver/api/handler/apiKeyToken.go +++ b/scnserver/api/handler/apiKeyToken.go @@ -6,7 +6,7 @@ import ( "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "errors" - "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" ) @@ -27,7 +27,7 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/keys [GET] -func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListUserKeys(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -36,7 +36,7 @@ func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse { } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -53,7 +53,7 @@ func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse { res := langext.ArrMap(toks, func(v models.KeyToken) models.KeyTokenJSON { return v.JSON() }) - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Keys: res})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Keys: res})) } // GetCurrentUserKey swaggerdoc @@ -73,13 +73,13 @@ func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/keys/current [GET] -func (h APIHandler) GetCurrentUserKey(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetCurrentUserKey(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -102,7 +102,7 @@ func (h APIHandler) GetCurrentUserKey(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, keytoken.JSON().WithToken(keytoken.Token))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, keytoken.JSON().WithToken(keytoken.Token))) } // GetUserKey swaggerdoc @@ -122,14 +122,14 @@ func (h APIHandler) GetCurrentUserKey(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/keys/{kid} [GET] -func (h APIHandler) GetUserKey(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetUserKey(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` KeyID models.KeyTokenID `uri:"kid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -147,7 +147,7 @@ func (h APIHandler) GetUserKey(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, keytoken.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, keytoken.JSON())) } // UpdateUserKey swaggerdoc @@ -168,7 +168,7 @@ func (h APIHandler) GetUserKey(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/keys/{kid} [PATCH] -func (h APIHandler) UpdateUserKey(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) UpdateUserKey(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` KeyID models.KeyTokenID `uri:"kid" binding:"entityid"` @@ -182,7 +182,7 @@ func (h APIHandler) UpdateUserKey(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -245,7 +245,7 @@ func (h APIHandler) UpdateUserKey(g *gin.Context) ginresp.HTTPResponse { keytoken.Channels = *b.Channels } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, keytoken.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, keytoken.JSON())) } // CreateUserKey swaggerdoc @@ -265,7 +265,7 @@ func (h APIHandler) UpdateUserKey(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/keys [POST] -func (h APIHandler) CreateUserKey(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) CreateUserKey(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -278,7 +278,7 @@ func (h APIHandler) CreateUserKey(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -314,7 +314,7 @@ func (h APIHandler) CreateUserKey(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create keytoken in db", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, keytok.JSON().WithToken(token))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, keytok.JSON().WithToken(token))) } // DeleteUserKey swaggerdoc @@ -334,14 +334,14 @@ func (h APIHandler) CreateUserKey(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/keys/{kid} [DELETE] -func (h APIHandler) DeleteUserKey(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) DeleteUserKey(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` KeyID models.KeyTokenID `uri:"kid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -368,5 +368,5 @@ func (h APIHandler) DeleteUserKey(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, client.JSON())) } diff --git a/scnserver/api/handler/apiMessage.go b/scnserver/api/handler/apiMessage.go index b7fdfbc..10f8e47 100644 --- a/scnserver/api/handler/apiMessage.go +++ b/scnserver/api/handler/apiMessage.go @@ -3,6 +3,7 @@ package handler import ( "database/sql" "errors" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "net/http" "strings" "time" @@ -11,7 +12,6 @@ import ( "blackforestbytes.com/simplecloudnotifier/api/ginresp" ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken" "blackforestbytes.com/simplecloudnotifier/models" - "github.com/gin-gonic/gin" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/mathext" ) @@ -34,7 +34,7 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/messages [GET] -func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListMessages(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { PageSize *int `json:"page_size" form:"page_size"` NextPageToken *string `json:"next_page_token" form:"next_page_token"` @@ -155,7 +155,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse { res = langext.ArrMap(messages, func(v models.Message) models.MessageJSON { return v.FullJSON() }) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Messages: res, NextPageToken: npt.Token(), PageSize: pageSize})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Messages: res, NextPageToken: npt.Token(), PageSize: pageSize})) } // GetMessage swaggerdoc @@ -176,13 +176,13 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/messages/{mid} [GET] -func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetMessage(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { MessageID models.MessageID `uri:"mid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -204,7 +204,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { // or we subscribe (+confirmed) to the channel and have read/admin key if ctx.CheckPermissionMessageRead(msg) { - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, msg.FullJSON())) } if uid := ctx.GetPermissionUserID(); uid != nil && ctx.CheckPermissionUserRead(*uid) == nil { @@ -222,7 +222,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { } // => perm okay - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, msg.FullJSON())) } return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil) @@ -244,13 +244,13 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/messages/{mid} [DELETE] -func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) DeleteMessage(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { MessageID models.MessageID `uri:"mid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -282,5 +282,5 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to cancel deliveries", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, msg.FullJSON())) } diff --git a/scnserver/api/handler/apiPreview.go b/scnserver/api/handler/apiPreview.go index 43a415e..d1df9fa 100644 --- a/scnserver/api/handler/apiPreview.go +++ b/scnserver/api/handler/apiPreview.go @@ -6,7 +6,7 @@ import ( "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "errors" - "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "net/http" ) @@ -25,13 +25,13 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/preview/users/{uid} [GET] -func (h APIHandler) GetUserPreview(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetUserPreview(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -49,7 +49,7 @@ func (h APIHandler) GetUserPreview(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, user.JSONPreview())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, user.JSONPreview())) } // GetChannelPreview swaggerdoc @@ -67,13 +67,13 @@ func (h APIHandler) GetUserPreview(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/preview/channels/{cid} [GET] -func (h APIHandler) GetChannelPreview(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetChannelPreview(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { ChannelID models.ChannelID `uri:"cid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -91,7 +91,7 @@ func (h APIHandler) GetChannelPreview(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSONPreview())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, channel.JSONPreview())) } // GetUserKeyPreview swaggerdoc @@ -109,13 +109,13 @@ func (h APIHandler) GetChannelPreview(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/preview/keys/{kid} [GET] -func (h APIHandler) GetUserKeyPreview(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetUserKeyPreview(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { KeyID models.KeyTokenID `uri:"kid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -133,5 +133,5 @@ func (h APIHandler) GetUserKeyPreview(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, keytoken.JSONPreview())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, keytoken.JSONPreview())) } diff --git a/scnserver/api/handler/apiSubscription.go b/scnserver/api/handler/apiSubscription.go index a89fd3c..e88fffa 100644 --- a/scnserver/api/handler/apiSubscription.go +++ b/scnserver/api/handler/apiSubscription.go @@ -6,7 +6,7 @@ import ( "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "errors" - "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" "strings" @@ -47,7 +47,7 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/subscriptions [GET] -func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListUserSubscriptions(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -64,7 +64,7 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { var u uri var q query - ctx, errResp := h.app.StartRequest(g, &u, &q, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Query(&q).Start()) if errResp != nil { return *errResp } @@ -128,7 +128,7 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { jsonres := langext.ArrMap(res, func(v models.Subscription) models.SubscriptionJSON { return v.JSON() }) - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Subscriptions: jsonres})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Subscriptions: jsonres})) } // ListChannelSubscriptions swaggerdoc @@ -147,7 +147,7 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/channels/{cid}/subscriptions [GET] -func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) ListChannelSubscriptions(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` ChannelID models.ChannelID `uri:"cid" binding:"entityid"` @@ -157,7 +157,7 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -182,7 +182,7 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons res := langext.ArrMap(clients, func(v models.Subscription) models.SubscriptionJSON { return v.JSON() }) - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Subscriptions: res})) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{Subscriptions: res})) } // GetSubscription swaggerdoc @@ -201,14 +201,14 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/subscriptions/{sid} [GET] -func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetSubscription(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` SubscriptionID models.SubscriptionID `uri:"sid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -229,7 +229,7 @@ func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 404, apierr.SUBSCRIPTION_USER_MISMATCH, "Subscription not found", nil) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, subscription.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, subscription.JSON())) } // CancelSubscription swaggerdoc @@ -248,14 +248,14 @@ func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/subscriptions/{sid} [DELETE] -func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) CancelSubscription(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` SubscriptionID models.SubscriptionID `uri:"sid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -281,7 +281,7 @@ func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete subscription", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, subscription.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, subscription.JSON())) } // CreateSubscription swaggerdoc @@ -301,7 +301,7 @@ func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/subscriptions [POST] -func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) CreateSubscription(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -317,7 +317,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { var u uri var q query var b body - ctx, errResp := h.app.StartRequest(g, &u, &q, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Query(&q).Body(&b).Start()) if errResp != nil { return *errResp } @@ -378,7 +378,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { existingSub.Confirmed = true } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, existingSub.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, existingSub.JSON())) } sub, err := h.database.CreateSubscription(ctx, u.UserID, channel, channel.OwnerUserID == u.UserID) @@ -386,7 +386,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create subscription", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, sub.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, sub.JSON())) } // UpdateSubscription swaggerdoc @@ -406,7 +406,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid}/subscriptions/{sid} [PATCH] -func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) UpdateSubscription(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` SubscriptionID models.SubscriptionID `uri:"sid" binding:"entityid"` @@ -417,7 +417,7 @@ func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -455,5 +455,5 @@ func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query subscription", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, subscription.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, subscription.JSON())) } diff --git a/scnserver/api/handler/apiUser.go b/scnserver/api/handler/apiUser.go index 30eccc1..2537ec2 100644 --- a/scnserver/api/handler/apiUser.go +++ b/scnserver/api/handler/apiUser.go @@ -7,8 +7,8 @@ import ( "database/sql" "errors" "fmt" - "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" ) @@ -26,7 +26,7 @@ import ( // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users [POST] -func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) CreateUser(pctx ginext.PreContext) ginext.HTTPResponse { type body struct { FCMToken string `json:"fcm_token"` ProToken *string `json:"pro_token"` @@ -39,7 +39,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { } var b body - ctx, errResp := h.app.StartRequest(g, nil, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.Body(&b).Start()) if errResp != nil { return *errResp } @@ -117,7 +117,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { log.Info().Msg(fmt.Sprintf("Sucessfully created new user %s (client: %v)", userobj.UserID, b.NoClient)) if b.NoClient { - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, userobj.JSONWithClients(make([]models.Client, 0), adminKey, sendKey, readKey))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, userobj.JSONWithClients(make([]models.Client, 0), adminKey, sendKey, readKey))) } else { err := h.database.DeleteClientsByFCM(ctx, b.FCMToken) if err != nil { @@ -129,7 +129,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, userobj.JSONWithClients([]models.Client{client}, adminKey, sendKey, readKey))) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, userobj.JSONWithClients([]models.Client{client}, adminKey, sendKey, readKey))) } } @@ -149,13 +149,13 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid} [GET] -func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) GetUser(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } var u uri - ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Start()) if errResp != nil { return *errResp } @@ -173,7 +173,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, user.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, user.JSON())) } // UpdateUser swaggerdoc @@ -195,7 +195,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError "internal server error" // // @Router /api/v2/users/{uid} [PATCH] -func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse { +func (h APIHandler) UpdateUser(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid" binding:"entityid"` } @@ -206,7 +206,7 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse { var u uri var b body - ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil) + ctx, g, errResp := h.app.StartRequest(pctx.URI(&u).Body(&b).Start()) if errResp != nil { return *errResp } @@ -261,5 +261,5 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, user.JSON())) + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, user.JSON())) } diff --git a/scnserver/api/handler/common.go b/scnserver/api/handler/common.go index a069d89..2f3d830 100644 --- a/scnserver/api/handler/common.go +++ b/scnserver/api/handler/common.go @@ -6,10 +6,10 @@ import ( "blackforestbytes.com/simplecloudnotifier/db/simplectx" "blackforestbytes.com/simplecloudnotifier/logic" "bytes" - "context" "errors" "github.com/gin-gonic/gin" - sqlite3 "github.com/mattn/go-sqlite3" + "github.com/mattn/go-sqlite3" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/timeext" "net/http" @@ -51,12 +51,18 @@ type pingResponseInfo struct { // @Router /api/ping [put] // @Router /api/ping [delete] // @Router /api/ping [patch] -func (h CommonHandler) Ping(g *gin.Context) ginresp.HTTPResponse { +func (h CommonHandler) Ping(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + buf := new(bytes.Buffer) _, _ = buf.ReadFrom(g.Request.Body) resuestBody := buf.String() - return ginresp.JSON(http.StatusOK, pingResponse{ + return ginext.JSON(http.StatusOK, pingResponse{ Message: "Pong", Info: pingResponseInfo{ Method: g.Request.Method, @@ -78,7 +84,7 @@ func (h CommonHandler) Ping(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError // // @Router /api/db-test [post] -func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse { +func (h CommonHandler) DatabaseTest(pctx ginext.PreContext) ginext.HTTPResponse { type response struct { Success bool `json:"success"` LibVersion string `json:"libVersion"` @@ -86,8 +92,11 @@ func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse { SourceID string `json:"sourceID"` } - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() + ctx, _, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() libVersion, libVersionNumber, sourceID := sqlite3.Version() @@ -96,7 +105,7 @@ func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse { return ginresp.InternalError(err) } - return ginresp.JSON(http.StatusOK, response{ + return ginext.JSON(http.StatusOK, response{ Success: true, LibVersion: libVersion, LibVersionNumber: libVersionNumber, @@ -114,13 +123,16 @@ func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError // // @Router /api/health [get] -func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse { +func (h CommonHandler) Health(pctx ginext.PreContext) ginext.HTTPResponse { type response struct { Status string `json:"status"` } - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() + ctx, _, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() _, libVersionNumber, _ := sqlite3.Version() @@ -161,7 +173,7 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse { } - return ginresp.JSON(http.StatusOK, response{Status: "ok"}) + return ginext.JSON(http.StatusOK, response{Status: "ok"}) } // Sleep swaggerdoc @@ -177,7 +189,7 @@ func (h CommonHandler) Health(g *gin.Context) ginresp.HTTPResponse { // @Failure 500 {object} ginresp.apiError // // @Router /api/sleep/{secs} [post] -func (h CommonHandler) Sleep(g *gin.Context) ginresp.HTTPResponse { +func (h CommonHandler) Sleep(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { Seconds float64 `uri:"secs"` } @@ -187,6 +199,12 @@ func (h CommonHandler) Sleep(g *gin.Context) ginresp.HTTPResponse { Duration float64 `json:"duration"` } + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + t0 := time.Now().Format(time.RFC3339Nano) var u uri @@ -198,15 +216,21 @@ func (h CommonHandler) Sleep(g *gin.Context) ginresp.HTTPResponse { t1 := time.Now().Format(time.RFC3339Nano) - return ginresp.JSON(http.StatusOK, response{ + return ginext.JSON(http.StatusOK, response{ Start: t0, End: t1, Duration: u.Seconds, }) } -func (h CommonHandler) NoRoute(g *gin.Context) ginresp.HTTPResponse { - return ginresp.JSON(http.StatusNotFound, gin.H{ +func (h CommonHandler) NoRoute(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + + return ginext.JSON(http.StatusNotFound, gin.H{ "": "================ ROUTE NOT FOUND ================", "FullPath": g.FullPath(), "Method": g.Request.Method, diff --git a/scnserver/api/handler/compat.go b/scnserver/api/handler/compat.go index 7074c38..7aa177c 100644 --- a/scnserver/api/handler/compat.go +++ b/scnserver/api/handler/compat.go @@ -11,8 +11,8 @@ import ( "database/sql" "errors" "fmt" - "github.com/gin-gonic/gin" "gogs.mikescher.com/BlackForestBytes/goext/dataext" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" ) @@ -47,7 +47,7 @@ func NewCompatHandler(app *logic.Application) CompatHandler { // @Failure 500 {object} ginresp.apiError // // @Router /send.php [POST] -func (h CompatHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse { type combined struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -92,7 +92,7 @@ func (h CompatHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse { if errResp != nil { return *errResp } else { - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, ErrorID: apierr.NO_ERROR, ErrorHighlight: -1, @@ -127,7 +127,7 @@ func (h CompatHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/register.php [get] -func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Register(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { FCMToken *string `json:"fcm_token" form:"fcm_token"` Pro *string `json:"pro" form:"pro"` @@ -216,7 +216,7 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to create userid", err) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "New user registered", UserID: oldid, @@ -245,7 +245,7 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/info.php [get] -func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Info(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -321,7 +321,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", UserID: *data.UserID, @@ -354,7 +354,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/ack.php [get] -func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Ack(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -434,7 +434,7 @@ func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse { } } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", PrevAckValue: langext.Conditional(ackBefore, 1, 0), @@ -460,7 +460,7 @@ func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/requery.php [get] -func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Requery(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -545,7 +545,7 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse { }) } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", Count: len(compMsgs), @@ -573,7 +573,7 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/update.php [get] -func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Update(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -673,7 +673,7 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "user updated", UserID: *data.UserID, @@ -704,7 +704,7 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/expand.php [get] -func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Expand(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -779,7 +779,7 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query message") } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", Data: models.CompatMessage{ @@ -816,7 +816,7 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse { // @Failure default {object} ginresp.compatAPIError // // @Router /api/upgrade.php [get] -func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse { +func (h CompatHandler) Upgrade(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` @@ -921,7 +921,7 @@ func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse { return ginresp.CompatAPIError(0, "Failed to query user") } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "user updated", UserID: *data.UserID, diff --git a/scnserver/api/handler/external.go b/scnserver/api/handler/external.go index d09877d..ee578de 100644 --- a/scnserver/api/handler/external.go +++ b/scnserver/api/handler/external.go @@ -7,7 +7,7 @@ import ( "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" "fmt" - "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" "time" @@ -41,7 +41,7 @@ func NewExternalHandler(app *logic.Application) ExternalHandler { // @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later" // // @Router /external/v1/uptime-kuma [POST] -func (h ExternalHandler) UptimeKuma(g *gin.Context) ginresp.HTTPResponse { +func (h ExternalHandler) UptimeKuma(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *models.UserID `form:"user_id" example:"7725"` KeyToken *string `form:"key" example:"P3TNH8mvv14fm"` @@ -128,7 +128,7 @@ func (h ExternalHandler) UptimeKuma(g *gin.Context) ginresp.HTTPResponse { return *errResp } - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ MessageID: okResp.Message.MessageID, })) } diff --git a/scnserver/api/handler/message.go b/scnserver/api/handler/message.go index b1f6395..d39fff3 100644 --- a/scnserver/api/handler/message.go +++ b/scnserver/api/handler/message.go @@ -2,12 +2,11 @@ package handler import ( "blackforestbytes.com/simplecloudnotifier/api/apierr" - "blackforestbytes.com/simplecloudnotifier/api/ginresp" primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" - "github.com/gin-gonic/gin" "gogs.mikescher.com/BlackForestBytes/goext/dataext" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" ) @@ -49,7 +48,7 @@ func NewMessageHandler(app *logic.Application) MessageHandler { // // @Router / [POST] // @Router /send [POST] -func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse { +func (h MessageHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse { type combined struct { UserID *models.UserID `json:"user_id" form:"user_id" example:"7725" ` KeyToken *string `json:"key" form:"key" example:"P3TNH8mvv14fm" ` @@ -78,7 +77,7 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse { var b combined var q combined var f combined - ctx, errResp := h.app.StartRequest(g, nil, &q, &b, &f, logic.RequestOptions{IgnoreWrongContentType: true}) + ctx, g, errResp := h.app.StartRequest(pctx.Form(&f).Query(&q).Body(&b).Start()) if errResp != nil { return *errResp } @@ -91,7 +90,7 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse { if errResp != nil { return *errResp } else { - return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + return ctx.FinishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, ErrorID: apierr.NO_ERROR, ErrorHighlight: -1, diff --git a/scnserver/api/handler/website.go b/scnserver/api/handler/website.go index 492bea0..ae04686 100644 --- a/scnserver/api/handler/website.go +++ b/scnserver/api/handler/website.go @@ -7,6 +7,7 @@ import ( "errors" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/rext" "net/http" "regexp" @@ -27,60 +28,104 @@ func NewWebsiteHandler(app *logic.Application) WebsiteHandler { } } -func (h WebsiteHandler) Index(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) Index(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + return h.serveAsset(g, "index.html", true) } -func (h WebsiteHandler) APIDocs(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) APIDocs(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + return h.serveAsset(g, "api.html", true) } -func (h WebsiteHandler) APIDocsMore(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) APIDocsMore(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + return h.serveAsset(g, "api_more.html", true) } -func (h WebsiteHandler) MessageSent(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) MessageSent(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + return h.serveAsset(g, "message_sent.html", true) } -func (h WebsiteHandler) FaviconIco(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) FaviconIco(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + return h.serveAsset(g, "favicon.ico", false) } -func (h WebsiteHandler) FaviconPNG(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) FaviconPNG(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + return h.serveAsset(g, "favicon.png", false) } -func (h WebsiteHandler) Javascript(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) Javascript(pctx ginext.PreContext) ginext.HTTPResponse { + ctx, g, errResp := pctx.Start() + if errResp != nil { + return *errResp + } + defer ctx.Cancel() + type uri struct { Filename string `uri:"fn"` } var u uri if err := g.ShouldBindUri(&u); err != nil { - return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return ginext.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } return h.serveAsset(g, "js/"+u.Filename, false) } -func (h WebsiteHandler) CSS(g *gin.Context) ginresp.HTTPResponse { +func (h WebsiteHandler) CSS(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { Filename string `uri:"fn"` } var u uri - if err := g.ShouldBindUri(&u); err != nil { - return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + ctx, g, errResp := pctx.URI(&u).Start() + if errResp != nil { + return *errResp } + defer ctx.Cancel() return h.serveAsset(g, "css/"+u.Filename, false) } -func (h WebsiteHandler) serveAsset(g *gin.Context, fn string, repl bool) ginresp.HTTPResponse { +func (h WebsiteHandler) serveAsset(g *gin.Context, fn string, repl bool) ginext.HTTPResponse { _data, err := website.Assets.ReadFile(fn) if err != nil { - return ginresp.Status(http.StatusNotFound) + return ginext.Status(http.StatusNotFound) } data := string(_data) @@ -141,7 +186,7 @@ func (h WebsiteHandler) serveAsset(g *gin.Context, fn string, repl bool) ginresp mime = "image/svg+xml" } - return ginresp.Data(http.StatusOK, mime, []byte(data)) + return ginext.Data(http.StatusOK, mime, []byte(data)) } func (h WebsiteHandler) getReplConfig(key string) (string, bool) { diff --git a/scnserver/api/router.go b/scnserver/api/router.go index 0162d88..adbeaf9 100644 --- a/scnserver/api/router.go +++ b/scnserver/api/router.go @@ -1,7 +1,6 @@ package api import ( - "blackforestbytes.com/simplecloudnotifier/api/ginext" "blackforestbytes.com/simplecloudnotifier/api/ginresp" "blackforestbytes.com/simplecloudnotifier/api/handler" "blackforestbytes.com/simplecloudnotifier/logic" @@ -11,6 +10,7 @@ import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" ) type Router struct { @@ -50,7 +50,7 @@ func NewRouter(app *logic.Application) *Router { // @tag.name Common // // @BasePath / -func (r *Router) Init(e *gin.Engine) error { +func (r *Router) Init(e *ginext.GinWrapper) error { if v, ok := binding.Validator.Engine().(*validator.Validate); ok { err := v.RegisterValidation("entityid", models.ValidateEntityID, true) @@ -61,131 +61,129 @@ func (r *Router) Init(e *gin.Engine) error { return errors.New("failed to add validators - wrong engine") } + wrap := func(fn ginext.WHandlerFunc) ginext.WHandlerFunc{return Wrap(r.app, fn)} + // ================ General (unversioned) ================ - commonAPI := e.Group("/api") + commonAPI := e.Routes().Group("/api") { - commonAPI.Any("/ping", r.Wrap(r.commonHandler.Ping)) - commonAPI.POST("/db-test", r.Wrap(r.commonHandler.DatabaseTest)) - commonAPI.GET("/health", r.Wrap(r.commonHandler.Health)) - commonAPI.POST("/sleep/:secs", r.Wrap(r.commonHandler.Sleep)) + commonAPI.Any("/ping").Handle(wrap(r.commonHandler.Ping)) + commonAPI.POST("/db-test").Handle(wrap(r.commonHandler.DatabaseTest)) + commonAPI.GET("/health").Handle(wrap(r.commonHandler.Health)) + commonAPI.POST("/sleep/:secs").Handle(wrap(r.commonHandler.Sleep)) } // ================ Swagger ================ - docs := e.Group("/documentation") + docs := e.Routes().Group("/documentation") { - docs.GET("/swagger", ginext.RedirectTemporary("/documentation/swagger/")) - docs.GET("/swagger/*sub", r.Wrap(swagger.Handle)) + docs.GET("/swagger").Handle(wrap(ginext.RedirectTemporary("/documentation/swagger/"))) + docs.GET("/swagger/*sub").Handle(wrap(swagger.Handle)) } // ================ Website ================ - frontend := e.Group("") + frontend := e.Routes().Group("") { - frontend.GET("/", r.Wrap(r.websiteHandler.Index)) - frontend.GET("/index.php", r.Wrap(r.websiteHandler.Index)) - frontend.GET("/index.html", r.Wrap(r.websiteHandler.Index)) - frontend.GET("/index", r.Wrap(r.websiteHandler.Index)) + frontend.GET("/").Handle(wrap(r.websiteHandler.Index)) + frontend.GET("/index.php").Handle(wrap(r.websiteHandler.Index)) + frontend.GET("/index.html").Handle(wrap(r.websiteHandler.Index)) + frontend.GET("/index").Handle(wrap(r.websiteHandler.Index)) - frontend.GET("/api", r.Wrap(r.websiteHandler.APIDocs)) - frontend.GET("/api.php", r.Wrap(r.websiteHandler.APIDocs)) - frontend.GET("/api.html", r.Wrap(r.websiteHandler.APIDocs)) + frontend.GET("/api").Handle(wrap(r.websiteHandler.APIDocs)) + frontend.GET("/api.php").Handle(wrap(r.websiteHandler.APIDocs)) + frontend.GET("/api.html").Handle(wrap(r.websiteHandler.APIDocs)) - frontend.GET("/api_more", r.Wrap(r.websiteHandler.APIDocsMore)) - frontend.GET("/api_more.php", r.Wrap(r.websiteHandler.APIDocsMore)) - frontend.GET("/api_more.html", r.Wrap(r.websiteHandler.APIDocsMore)) + frontend.GET("/api_more").Handle(wrap(r.websiteHandler.APIDocsMore)) + frontend.GET("/api_more.php").Handle(wrap(r.websiteHandler.APIDocsMore)) + frontend.GET("/api_more.html").Handle(wrap(r.websiteHandler.APIDocsMore)) - frontend.GET("/message_sent", r.Wrap(r.websiteHandler.MessageSent)) - frontend.GET("/message_sent.php", r.Wrap(r.websiteHandler.MessageSent)) - frontend.GET("/message_sent.html", r.Wrap(r.websiteHandler.MessageSent)) + frontend.GET("/message_sent").Handle(wrap(r.websiteHandler.MessageSent)) + frontend.GET("/message_sent.php").Handle(wrap(r.websiteHandler.MessageSent)) + frontend.GET("/message_sent.html").Handle(wrap(r.websiteHandler.MessageSent)) - frontend.GET("/favicon.ico", r.Wrap(r.websiteHandler.FaviconIco)) - frontend.GET("/favicon.png", r.Wrap(r.websiteHandler.FaviconPNG)) + frontend.GET("/favicon.ico").Handle(wrap(r.websiteHandler.FaviconIco)) + frontend.GET("/favicon.png").Handle(wrap(r.websiteHandler.FaviconPNG)) - frontend.GET("/js/:fn", r.Wrap(r.websiteHandler.Javascript)) - frontend.GET("/css/:fn", r.Wrap(r.websiteHandler.CSS)) + frontend.GET("/js/:fn").Handle(wrap(r.websiteHandler.Javascript)) + frontend.GET("/css/:fn").Handle(wrap(r.websiteHandler.CSS)) } // ================ Compat (v1) ================ - compat := e.Group("/api") + compat := e.Routes().Group("/api") { - compat.GET("/register.php", r.Wrap(r.compatHandler.Register)) - compat.GET("/info.php", r.Wrap(r.compatHandler.Info)) - compat.GET("/ack.php", r.Wrap(r.compatHandler.Ack)) - compat.GET("/requery.php", r.Wrap(r.compatHandler.Requery)) - compat.GET("/update.php", r.Wrap(r.compatHandler.Update)) - compat.GET("/expand.php", r.Wrap(r.compatHandler.Expand)) - compat.GET("/upgrade.php", r.Wrap(r.compatHandler.Upgrade)) + compat.GET("/register.php").Handle(wrap(r.compatHandler.Register)) + compat.GET("/info.php").Handle(wrap(r.compatHandler.Info)) + compat.GET("/ack.php").Handle(wrap(r.compatHandler.Ack)) + compat.GET("/requery.php").Handle(wrap(r.compatHandler.Requery)) + compat.GET("/update.php").Handle(wrap(r.compatHandler.Update)) + compat.GET("/expand.php").Handle(wrap(r.compatHandler.Expand)) + compat.GET("/upgrade.php").Handle(wrap(r.compatHandler.Upgrade)) } // ================ Manage API (v2) ================ - apiv2 := e.Group("/api/v2/") + apiv2 := e.Routes().Group("/api/v2/") { - apiv2.POST("/users", r.Wrap(r.apiHandler.CreateUser)) - apiv2.GET("/users/:uid", r.Wrap(r.apiHandler.GetUser)) - apiv2.PATCH("/users/:uid", r.Wrap(r.apiHandler.UpdateUser)) + apiv2.POST("/users").Handle(wrap(r.apiHandler.CreateUser)) + apiv2.GET("/users/:uid").Handle(wrap(r.apiHandler.GetUser)) + apiv2.PATCH("/users/:uid").Handle(wrap(r.apiHandler.UpdateUser)) - apiv2.GET("/users/:uid/keys", r.Wrap(r.apiHandler.ListUserKeys)) - apiv2.POST("/users/:uid/keys", r.Wrap(r.apiHandler.CreateUserKey)) - apiv2.GET("/users/:uid/keys/current", r.Wrap(r.apiHandler.GetCurrentUserKey)) - apiv2.GET("/users/:uid/keys/:kid", r.Wrap(r.apiHandler.GetUserKey)) - apiv2.PATCH("/users/:uid/keys/:kid", r.Wrap(r.apiHandler.UpdateUserKey)) - apiv2.DELETE("/users/:uid/keys/:kid", r.Wrap(r.apiHandler.DeleteUserKey)) + apiv2.GET("/users/:uid/keys").Handle(wrap(r.apiHandler.ListUserKeys)) + apiv2.POST("/users/:uid/keys").Handle(wrap(r.apiHandler.CreateUserKey)) + apiv2.GET("/users/:uid/keys/current").Handle(wrap(r.apiHandler.GetCurrentUserKey)) + apiv2.GET("/users/:uid/keys/:kid").Handle(wrap(r.apiHandler.GetUserKey)) + apiv2.PATCH("/users/:uid/keys/:kid").Handle(wrap(r.apiHandler.UpdateUserKey)) + apiv2.DELETE("/users/:uid/keys/:kid").Handle(wrap(r.apiHandler.DeleteUserKey)) - apiv2.GET("/users/:uid/clients", r.Wrap(r.apiHandler.ListClients)) - apiv2.GET("/users/:uid/clients/:cid", r.Wrap(r.apiHandler.GetClient)) - apiv2.PATCH("/users/:uid/clients/:cid", r.Wrap(r.apiHandler.UpdateClient)) - apiv2.POST("/users/:uid/clients", r.Wrap(r.apiHandler.AddClient)) - apiv2.DELETE("/users/:uid/clients/:cid", r.Wrap(r.apiHandler.DeleteClient)) + apiv2.GET("/users/:uid/clients").Handle(wrap(r.apiHandler.ListClients)) + apiv2.GET("/users/:uid/clients/:cid").Handle(wrap(r.apiHandler.GetClient)) + apiv2.PATCH("/users/:uid/clients/:cid").Handle(wrap(r.apiHandler.UpdateClient)) + apiv2.POST("/users/:uid/clients").Handle(wrap(r.apiHandler.AddClient)) + apiv2.DELETE("/users/:uid/clients/:cid").Handle(wrap(r.apiHandler.DeleteClient)) - apiv2.GET("/users/:uid/channels", r.Wrap(r.apiHandler.ListChannels)) - apiv2.POST("/users/:uid/channels", r.Wrap(r.apiHandler.CreateChannel)) - apiv2.GET("/users/:uid/channels/:cid", r.Wrap(r.apiHandler.GetChannel)) - apiv2.PATCH("/users/:uid/channels/:cid", r.Wrap(r.apiHandler.UpdateChannel)) - apiv2.GET("/users/:uid/channels/:cid/messages", r.Wrap(r.apiHandler.ListChannelMessages)) - apiv2.GET("/users/:uid/channels/:cid/subscriptions", r.Wrap(r.apiHandler.ListChannelSubscriptions)) + apiv2.GET("/users/:uid/channels").Handle(wrap(r.apiHandler.ListChannels)) + apiv2.POST("/users/:uid/channels").Handle(wrap(r.apiHandler.CreateChannel)) + apiv2.GET("/users/:uid/channels/:cid").Handle(wrap(r.apiHandler.GetChannel)) + apiv2.PATCH("/users/:uid/channels/:cid").Handle(wrap(r.apiHandler.UpdateChannel)) + apiv2.GET("/users/:uid/channels/:cid/messages").Handle(wrap(r.apiHandler.ListChannelMessages)) + apiv2.GET("/users/:uid/channels/:cid/subscriptions").Handle(wrap(r.apiHandler.ListChannelSubscriptions)) - apiv2.GET("/users/:uid/subscriptions", r.Wrap(r.apiHandler.ListUserSubscriptions)) - apiv2.POST("/users/:uid/subscriptions", r.Wrap(r.apiHandler.CreateSubscription)) - apiv2.GET("/users/:uid/subscriptions/:sid", r.Wrap(r.apiHandler.GetSubscription)) - apiv2.DELETE("/users/:uid/subscriptions/:sid", r.Wrap(r.apiHandler.CancelSubscription)) - apiv2.PATCH("/users/:uid/subscriptions/:sid", r.Wrap(r.apiHandler.UpdateSubscription)) + apiv2.GET("/users/:uid/subscriptions").Handle(wrap(r.apiHandler.ListUserSubscriptions)) + apiv2.POST("/users/:uid/subscriptions").Handle(wrap(r.apiHandler.CreateSubscription)) + apiv2.GET("/users/:uid/subscriptions/:sid").Handle(wrap(r.apiHandler.GetSubscription)) + apiv2.DELETE("/users/:uid/subscriptions/:sid").Handle(wrap(r.apiHandler.CancelSubscription)) + apiv2.PATCH("/users/:uid/subscriptions/:sid").Handle(wrap(r.apiHandler.UpdateSubscription)) - apiv2.GET("/messages", r.Wrap(r.apiHandler.ListMessages)) - apiv2.GET("/messages/:mid", r.Wrap(r.apiHandler.GetMessage)) - apiv2.DELETE("/messages/:mid", r.Wrap(r.apiHandler.DeleteMessage)) + apiv2.GET("/messages").Handle(wrap(r.apiHandler.ListMessages)) + apiv2.GET("/messages/:mid").Handle(wrap(r.apiHandler.GetMessage)) + apiv2.DELETE("/messages/:mid").Handle(wrap(r.apiHandler.DeleteMessage)) - apiv2.GET("/preview/users/:uid", r.Wrap(r.apiHandler.GetUserPreview)) - apiv2.GET("/preview/keys/:kid", r.Wrap(r.apiHandler.GetUserKeyPreview)) - apiv2.GET("/preview/channels/:cid", r.Wrap(r.apiHandler.GetChannelPreview)) + apiv2.GET("/preview/users/:uid").Handle(wrap(r.apiHandler.GetUserPreview)) + apiv2.GET("/preview/keys/:kid").Handle(wrap(r.apiHandler.GetUserKeyPreview)) + apiv2.GET("/preview/channels/:cid").Handle(wrap(r.apiHandler.GetChannelPreview)) } // ================ Send API (unversioned) ================ - sendAPI := e.Group("") + sendAPI := e.Routes().Group("") { - sendAPI.POST("/", r.Wrap(r.messageHandler.SendMessage)) - sendAPI.POST("/send", r.Wrap(r.messageHandler.SendMessage)) - sendAPI.POST("/send.php", r.Wrap(r.compatHandler.SendMessage)) + sendAPI.POST("/").Handle(wrap(r.messageHandler.SendMessage) + sendAPI.POST("/send").Handle(wrap(r.messageHandler.SendMessage) + sendAPI.POST("/send.php").Handle(wrap(r.compatHandler.SendMessage) - sendAPI.POST("/external/v1/uptime-kuma", r.Wrap(r.externalHandler.UptimeKuma)) + sendAPI.POST("/external/v1/uptime-kuma").Handle(wrap(r.externalHandler.UptimeKuma) } // ================ if r.app.Config.ReturnRawErrors { - e.NoRoute(r.Wrap(r.commonHandler.NoRoute)) + e.NoRoute(r.commonHandler.NoRoute) } // ================ return nil } - -func (r *Router) Wrap(fn ginresp.WHandlerFunc) gin.HandlerFunc { - return ginresp.Wrap(r.app, fn) -} diff --git a/scnserver/api/ginresp/wrapper.go b/scnserver/api/wrapper.go similarity index 75% rename from scnserver/api/ginresp/wrapper.go rename to scnserver/api/wrapper.go index 3994a98..73111fb 100644 --- a/scnserver/api/ginresp/wrapper.go +++ b/scnserver/api/wrapper.go @@ -1,33 +1,33 @@ -package ginresp +package api import ( scn "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/api/apierr" + "blackforestbytes.com/simplecloudnotifier/api/ginresp" "blackforestbytes.com/simplecloudnotifier/models" "errors" "fmt" "github.com/gin-gonic/gin" - "github.com/mattn/go-sqlite3" + "github.com/glebarez/go-sqlite" "github.com/rs/zerolog/log" "gogs.mikescher.com/BlackForestBytes/goext/dataext" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "math/rand" "runtime/debug" "time" ) -type WHandlerFunc func(*gin.Context) HTTPResponse - type RequestLogAcceptor interface { InsertRequestLog(data models.RequestLog) } -func Wrap(rlacc RequestLogAcceptor, fn WHandlerFunc) gin.HandlerFunc { +func Wrap(rlacc RequestLogAcceptor, fn ginext.WHandlerFunc) ginext.WHandlerFunc { maxRetry := scn.Conf.RequestMaxRetry retrySleep := scn.Conf.RequestRetrySleep - return func(g *gin.Context) { + return func(pctx *ginext.PreContext) { reqctx := g.Request.Context() @@ -43,7 +43,7 @@ func Wrap(rlacc RequestLogAcceptor, fn WHandlerFunc) gin.HandlerFunc { if panicObj != nil { log.Error().Interface("panicObj", panicObj).Msg("Panic occured (in gin handler)") log.Error().Msg(stackTrace) - wrap = APIError(g, 500, apierr.PANIC, "A panic occured in the HTTP handler", errors.New(fmt.Sprintf("%+v\n\n@:\n%s", panicObj, stackTrace))) + wrap = ginresp.APIError(g, 500, apierr.PANIC, "A panic occured in the HTTP handler", errors.New(fmt.Sprintf("%+v\n\n@:\n%s", panicObj, stackTrace))) } if g.Writer.Written() { @@ -70,9 +70,14 @@ func Wrap(rlacc RequestLogAcceptor, fn WHandlerFunc) gin.HandlerFunc { rlacc.InsertRequestLog(createRequestLog(g, t0, ctr, wrap, nil)) } - statuscode := wrap.Statuscode() - if statuscode/100 != 2 { - log.Warn().Str("url", g.Request.Method+"::"+g.Request.URL.String()).Msg(fmt.Sprintf("Request failed with statuscode %d", statuscode)) + if scw, ok := wrap.(ginext.InspectableHTTPResponse); ok { + + statuscode := scw.Statuscode() + if statuscode/100 != 2 { + log.Warn().Str("url", g.Request.Method+"::"+g.Request.URL.String()).Msg(fmt.Sprintf("Request failed with statuscode %d", statuscode)) + } + } else { + log.Warn().Str("url", g.Request.Method+"::"+g.Request.URL.String()).Msg(fmt.Sprintf("Request failed with statuscode [unknown]")) } wrap.Write(g) @@ -85,7 +90,7 @@ func Wrap(rlacc RequestLogAcceptor, fn WHandlerFunc) gin.HandlerFunc { } -func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp HTTPResponse, panicstr *string) models.RequestLog { +func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp ginext.HTTPResponse, panicstr *string) models.RequestLog { t1 := time.Now() @@ -109,9 +114,11 @@ func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp HTTPResponse, var strrespbody *string = nil if resp != nil { - respbody = resp.BodyString() - if respbody != nil && len(*respbody) < scn.Conf.ReqLogMaxBodySize { - strrespbody = respbody + if resp2, ok := resp.(ginext.InspectableHTTPResponse); ok { + respbody = resp2.BodyString(g) + if respbody != nil && len(*respbody) < scn.Conf.ReqLogMaxBodySize { + strrespbody = respbody + } } } @@ -147,7 +154,7 @@ func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp HTTPResponse, } } -func callPanicSafe(fn WHandlerFunc, g *gin.Context) (res HTTPResponse, stackTrace string, panicObj any) { +func callPanicSafe(fn ginext.WHandlerFunc, g ginext.PreContext) (res ginext.HTTPResponse, stackTrace string, panicObj any) { defer func() { if rec := recover(); rec != nil { res = nil @@ -173,17 +180,14 @@ func resetBody(g *gin.Context) error { return nil } -func isSqlite3Busy(r HTTPResponse) bool { - if errwrap, ok := r.(*errorHTTPResponse); ok && errwrap != nil { - - if errors.Is(errwrap.error, sqlite3.ErrBusy) { - return true - } - - var s3err sqlite3.Error - if errors.As(errwrap.error, &s3err) { - if errors.Is(s3err.Code, sqlite3.ErrBusy) { - return true +func isSqlite3Busy(r ginext.HTTPResponse) bool { + if errwrap, ok := r.(interface{ Unwrap() error }); ok && errwrap != nil { + { + var s3err *sqlite.Error + if errors.As(errwrap.Unwrap(), &s3err) { + if s3err.Code() == 5 { // [5] == SQLITE_BUSY + return true + } } } } diff --git a/scnserver/cmd/dbhash/main.go b/scnserver/cmd/dbhash/main.go index ca64535..f6e9015 100644 --- a/scnserver/cmd/dbhash/main.go +++ b/scnserver/cmd/dbhash/main.go @@ -4,7 +4,6 @@ import ( "blackforestbytes.com/simplecloudnotifier/db/schema" "context" "fmt" - "github.com/mattn/go-sqlite3" "gogs.mikescher.com/BlackForestBytes/goext/exerr" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" @@ -21,7 +20,7 @@ func main() { fmt.Println() for i := 2; i <= schema.PrimarySchemaVersion; i++ { - h0, err := sq.HashMattnSqliteSchema(ctx, schema.PrimarySchema[i].SQL) + h0, err := sq.HashGoSqliteSchema(ctx, schema.PrimarySchema[i].SQL) if err != nil { h0 = "ERR" } @@ -29,7 +28,7 @@ func main() { } for i := 1; i <= schema.RequestsSchemaVersion; i++ { - h0, err := sq.HashMattnSqliteSchema(ctx, schema.RequestsSchema[i].SQL) + h0, err := sq.HashGoSqliteSchema(ctx, schema.RequestsSchema[i].SQL) if err != nil { h0 = "ERR" } @@ -37,7 +36,7 @@ func main() { } for i := 1; i <= schema.LogsSchemaVersion; i++ { - h0, err := sq.HashMattnSqliteSchema(ctx, schema.LogsSchema[i].SQL) + h0, err := sq.HashGoSqliteSchema(ctx, schema.LogsSchema[i].SQL) if err != nil { h0 = "ERR" } diff --git a/scnserver/cmd/scnserver/main.go b/scnserver/cmd/scnserver/main.go index 204f658..5dc44e9 100644 --- a/scnserver/cmd/scnserver/main.go +++ b/scnserver/cmd/scnserver/main.go @@ -3,13 +3,14 @@ package main import ( scn "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/api" - "blackforestbytes.com/simplecloudnotifier/api/ginext" "blackforestbytes.com/simplecloudnotifier/google" "blackforestbytes.com/simplecloudnotifier/jobs" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/push" "fmt" "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" + "gogs.mikescher.com/BlackForestBytes/goext/langext" ) func main() { @@ -31,7 +32,12 @@ func main() { return } - ginengine := ginext.NewEngine(conf) + ginengine := ginext.NewEngine(ginext.Options{ + AllowCors: &conf.Cors, + GinDebug: &conf.GinDebug, + BufferBody: langext.PTrue, + Timeout: &conf.RequestTimeout, + }) router := api.NewRouter(app) diff --git a/scnserver/db/impl/logs/database.go b/scnserver/db/impl/logs/database.go index 0863ec9..f12aefa 100644 --- a/scnserver/db/impl/logs/database.go +++ b/scnserver/db/impl/logs/database.go @@ -9,8 +9,8 @@ import ( "database/sql" "errors" "fmt" + "github.com/glebarez/go-sqlite" "github.com/jmoiron/sqlx" - _ "github.com/mattn/go-sqlite3" "github.com/rs/zerolog/log" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" @@ -28,6 +28,10 @@ func NewLogsDatabase(cfg server.Config) (*Database, error) { url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"), conf.BusyTimeout.Milliseconds()) + if !langext.InArray("sqlite3", sql.Drivers()) { + sqlite.RegisterAsSQLITE3() + } + xdb, err := sqlx.Open("sqlite3", url) if err != nil { return nil, err diff --git a/scnserver/db/impl/primary/database.go b/scnserver/db/impl/primary/database.go index 52ad6e9..0b835d9 100644 --- a/scnserver/db/impl/primary/database.go +++ b/scnserver/db/impl/primary/database.go @@ -9,8 +9,8 @@ import ( "database/sql" "errors" "fmt" + "github.com/glebarez/go-sqlite" "github.com/jmoiron/sqlx" - _ "github.com/mattn/go-sqlite3" "github.com/rs/zerolog/log" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" @@ -28,6 +28,10 @@ func NewPrimaryDatabase(cfg server.Config) (*Database, error) { url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"), conf.BusyTimeout.Milliseconds()) + if !langext.InArray("sqlite3", sql.Drivers()) { + sqlite.RegisterAsSQLITE3() + } + xdb, err := sqlx.Open("sqlite3", url) if err != nil { return nil, err diff --git a/scnserver/db/impl/requests/database.go b/scnserver/db/impl/requests/database.go index 283bf5f..9dea758 100644 --- a/scnserver/db/impl/requests/database.go +++ b/scnserver/db/impl/requests/database.go @@ -9,8 +9,8 @@ import ( "database/sql" "errors" "fmt" + "github.com/glebarez/go-sqlite" "github.com/jmoiron/sqlx" - _ "github.com/mattn/go-sqlite3" "github.com/rs/zerolog/log" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/sq" @@ -28,6 +28,10 @@ func NewRequestsDatabase(cfg server.Config) (*Database, error) { url := fmt.Sprintf("file:%s?_journal=%s&_timeout=%d&_fk=%s&_busy_timeout=%d", conf.File, conf.Journal, conf.Timeout.Milliseconds(), langext.FormatBool(conf.CheckForeignKeys, "true", "false"), conf.BusyTimeout.Milliseconds()) + if !langext.InArray("sqlite3", sql.Drivers()) { + sqlite.RegisterAsSQLITE3() + } + xdb, err := sqlx.Open("sqlite3", url) if err != nil { return nil, err @@ -92,7 +96,7 @@ func (db *Database) Migrate(outerctx context.Context) error { schemastr := schema.RequestsSchema[schema.RequestsSchemaVersion].SQL schemahash := schema.RequestsSchema[schema.RequestsSchemaVersion].Hash - schemahash, err := sq.HashMattnSqliteSchema(tctx, schemastr) + schemahash, err := sq.HashGoSqliteSchema(tctx, schemastr) if err != nil { return err } diff --git a/scnserver/go.mod b/scnserver/go.mod index c5d28ea..dd9b986 100644 --- a/scnserver/go.mod +++ b/scnserver/go.mod @@ -6,31 +6,33 @@ toolchain go1.22.3 require ( github.com/gin-gonic/gin v1.10.0 - github.com/go-playground/validator/v10 v10.20.0 + github.com/glebarez/go-sqlite v1.22.0 + github.com/go-playground/validator/v10 v10.22.0 github.com/go-sql-driver/mysql v1.8.1 github.com/jmoiron/sqlx v1.4.0 github.com/mattn/go-sqlite3 v1.14.22 github.com/rs/zerolog v1.33.0 - gogs.mikescher.com/BlackForestBytes/goext v0.0.463 + gogs.mikescher.com/BlackForestBytes/goext v0.0.482 gopkg.in/loremipsum.v1 v1.1.2 ) require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/bytedance/sonic v1.11.8 // indirect + github.com/bytedance/sonic v1.11.9 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.8 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -38,6 +40,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/xid v1.5.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect @@ -45,14 +48,18 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect - go.mongodb.org/mongo-driver v1.15.0 // indirect + go.mongodb.org/mongo-driver v1.16.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.37.6 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.28.0 // indirect ) diff --git a/scnserver/go.sum b/scnserver/go.sum index d35a87d..5a9c70a 100644 --- a/scnserver/go.sum +++ b/scnserver/go.sum @@ -1,7 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/bytedance/sonic v1.11.8 h1:Zw/j1KfiS+OYTi9lyB3bb0CFxPJVkM17k1wyDG32LRA= -github.com/bytedance/sonic v1.11.8/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg= +github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= @@ -28,8 +28,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= @@ -37,20 +37,22 @@ github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= @@ -107,23 +109,23 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 h1:tBiBTKHnIjovYoLX/TPkcf+OjqqKGQrPtGT3Foz+Pgo= github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76/go.mod h1:SQliXeA7Dhkt//vS29v3zpbEwoa+zb2Cn5xj5uO4K5U= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= -go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= -gogs.mikescher.com/BlackForestBytes/goext v0.0.463 h1:1sdU/jI7gzzucKv3CBefT1Hk5frGAYvgl/ItC9PdoqA= -gogs.mikescher.com/BlackForestBytes/goext v0.0.463/go.mod h1:ZEaw70t0Wx044Ifkt8fcDHO/KtD3dwgxclX3OF6ElvA= +go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4= +go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= +gogs.mikescher.com/BlackForestBytes/goext v0.0.482 h1:veU8oJdGZ9rjLB8sluagBduiBs3BbEDf60sGmEEv8lk= +gogs.mikescher.com/BlackForestBytes/goext v0.0.482/go.mod h1:GxqLkJwPWQB5lVgWhmBPnx9RC+F0Dvi2xHKwfCmCQgM= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= @@ -137,24 +139,24 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/loremipsum.v1 v1.1.2 h1:12APklfJKuGszqZsrArW5QoQh03/W+qyCCjvnDuS6Tw= diff --git a/scnserver/logic/appcontext.go b/scnserver/logic/appcontext.go index 75a86c8..92c50c4 100644 --- a/scnserver/logic/appcontext.go +++ b/scnserver/logic/appcontext.go @@ -9,6 +9,7 @@ import ( "errors" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/sq" "time" ) @@ -81,7 +82,7 @@ func (ac *AppContext) RequestURI() string { } } -func (ac *AppContext) FinishSuccess(res ginresp.HTTPResponse) ginresp.HTTPResponse { +func (ac *AppContext) FinishSuccess(res ginext.HTTPResponse) ginext.HTTPResponse { if ac.cancelled { panic("Cannot finish a cancelled request") } diff --git a/scnserver/logic/application.go b/scnserver/logic/application.go index 6d058e4..620b6aa 100644 --- a/scnserver/logic/application.go +++ b/scnserver/logic/application.go @@ -4,6 +4,7 @@ import ( scn "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/api/apierr" "blackforestbytes.com/simplecloudnotifier/api/ginresp" + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/db/simplectx" "blackforestbytes.com/simplecloudnotifier/google" "blackforestbytes.com/simplecloudnotifier/models" @@ -11,13 +12,12 @@ import ( "context" "errors" "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" "github.com/rs/zerolog/log" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "gogs.mikescher.com/BlackForestBytes/goext/rext" "gogs.mikescher.com/BlackForestBytes/goext/syncext" "net" - "net/http" "os" "os/signal" "regexp" @@ -33,7 +33,7 @@ var rexCompatTitleChannel = rext.W(regexp.MustCompile("^\\[(?P[A-Za-z\\ type Application struct { Config scn.Config - Gin *gin.Engine + Gin *ginext.GinWrapper Database *DBPool Pusher push.NotificationClient AndroidPublisher google.AndroidPublisherClient @@ -53,7 +53,7 @@ func NewApp(db *DBPool) *Application { } } -func (app *Application) Init(cfg scn.Config, g *gin.Engine, fb push.NotificationClient, apc google.AndroidPublisherClient, jobs []Job) { +func (app *Application) Init(cfg scn.Config, g *ginext.GinWrapper, fb push.NotificationClient, apc google.AndroidPublisherClient, jobs []Job) { app.Config = cfg app.Gin = g app.Pusher = fb @@ -69,38 +69,17 @@ func (app *Application) Stop() { } func (app *Application) Run() { - httpserver := &http.Server{ - Addr: net.JoinHostPort(app.Config.ServerIP, app.Config.ServerPort), - Handler: app.Gin, - } - errChan := make(chan error) + // ================== START HTTP ================== - go func() { - - ln, err := net.Listen("tcp", httpserver.Addr) - if err != nil { - errChan <- err - return - } - - _, port, err := net.SplitHostPort(ln.Addr().String()) - if err != nil { - errChan <- err - return - } - - log.Info().Str("address", httpserver.Addr).Msg("HTTP-Server started on http://localhost:" + port) + addr := net.JoinHostPort(app.Config.ServerIP, app.Config.ServerPort) + errChan, httpserver := app.Gin.ListenAndServeHTTP(addr, func(port string) { app.Port = port + app.IsRunning.Set(true) + }) - app.IsRunning.Set(true) // the net.Listener a few lines above is at this point actually already buffering requests - - errChan <- httpserver.Serve(ln) - }() - - sigstop := make(chan os.Signal, 1) - signal.Notify(sigstop, os.Interrupt, syscall.SIGTERM) + // ================== START JOBS ================== for _, job := range app.Jobs { err := job.Start() @@ -109,6 +88,11 @@ func (app *Application) Run() { } } + // ================== LISTEN FOR SIGNALS ================== + + sigstop := make(chan os.Signal, 1) + signal.Notify(sigstop, os.Interrupt, syscall.SIGTERM) + select { case <-sigstop: ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) @@ -127,7 +111,7 @@ func (app *Application) Run() { case err := <-errChan: log.Error().Err(err).Msg("HTTP-Server failed") - case _ = <-app.stopChan: + case <-app.stopChan: ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -142,20 +126,25 @@ func (app *Application) Run() { } } + // ================== STOP JOBS ================== + for _, job := range app.Jobs { job.Stop() } - log.Info().Msg("Manually stopped Jobs") + // ================== STOP DB ================== - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - err := app.Database.Stop(ctx) - if err != nil { - log.Info().Err(err).Msg("Error while stopping the database") + { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + err := app.Database.Stop(ctx) + if err != nil { + log.Err(err).Msg("Failed to stop database") + } } + log.Info().Msg("Stopped Databases") - log.Info().Msg("Manually closed database connection") + // ================== FINISH ================== app.IsRunning.Set(false) } @@ -223,65 +212,26 @@ type RequestOptions struct { IgnoreWrongContentType bool } -func (app *Application) StartRequest(g *gin.Context, uri any, query any, body any, form any, opts ...RequestOptions) (*AppContext, *ginresp.HTTPResponse) { +func (app *Application) StartRequest(gectx *ginext.AppContext, g *gin.Context, r *ginext.HTTPResponse) (*AppContext, *gin.Context, *ginext.HTTPResponse) { - ignoreWrongContentType := langext.ArrAny(opts, func(o RequestOptions) bool { return o.IgnoreWrongContentType }) - - if uri != nil { - if err := g.ShouldBindUri(uri); err != nil { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_URI_PARAM, "Failed to read uri", err)) - } + if r != nil { + return nil, g, r } - if query != nil { - if err := g.ShouldBindQuery(query); err != nil { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_QUERY_PARAM, "Failed to read query", err)) - } - } - - if body != nil { - if g.ContentType() == "application/json" { - if err := g.ShouldBindJSON(body); err != nil { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_BODY_PARAM, "Failed to read body", err)) - } - } else { - if !ignoreWrongContentType { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_BODY_PARAM, "missing JSON body", nil)) - } - } - } - - if form != nil { - if g.ContentType() == "multipart/form-data" { - if err := g.ShouldBindWith(form, binding.Form); err != nil { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_BODY_PARAM, "Failed to read multipart-form", err)) - } - } else if g.ContentType() == "application/x-www-form-urlencoded" { - if err := g.ShouldBindWith(form, binding.Form); err != nil { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_BODY_PARAM, "Failed to read urlencoded-form", err)) - } - } else { - if !ignoreWrongContentType { - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.BINDFAIL_BODY_PARAM, "missing form body", nil)) - } - } - } - - ictx, cancel := context.WithTimeout(context.Background(), app.Config.RequestTimeout) - actx := CreateAppContext(app, g, ictx, cancel) + actx := CreateAppContext(app, g, gectx, gectx.Cancel) authheader := g.GetHeader("Authorization") perm, err := app.getPermissions(actx, authheader) if err != nil { - cancel() - return nil, langext.Ptr(ginresp.APIError(g, 400, apierr.PERM_QUERY_FAIL, "Failed to determine permissions", err)) + gectx.Cancel() + return nil, g, langext.Ptr(ginresp.APIError(g, 400, apierr.PERM_QUERY_FAIL, "Failed to determine permissions", err)) } actx.permissions = perm g.Set("perm", perm) - return actx, nil + return actx, g, nil } func (app *Application) NewSimpleTransactionContext(timeout time.Duration) *simplectx.SimpleContext { @@ -289,7 +239,7 @@ func (app *Application) NewSimpleTransactionContext(timeout time.Duration) *simp return simplectx.CreateSimpleContext(ictx, cancel) } -func (app *Application) getPermissions(ctx *AppContext, hdr string) (models.PermissionSet, error) { +func (app *Application) getPermissions(ctx db.TxContext, hdr string) (models.PermissionSet, error) { if hdr == "" { return models.NewEmptyPermissions(), nil } diff --git a/scnserver/logic/message.go b/scnserver/logic/message.go index bb792c3..de4617e 100644 --- a/scnserver/logic/message.go +++ b/scnserver/logic/message.go @@ -24,7 +24,7 @@ type SendMessageResponse struct { CompatMessageID int64 } -func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *models.UserID, Key *string, Channel *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) (*SendMessageResponse, *ginresp.HTTPResponse) { +func (app *Application) SendMessage(g *gin.Context, ctx *AppContext, UserID *models.UserID, Key *string, Channel *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) (*SendMessageResponse, *ginext.HTTPResponse) { if Title != nil { Title = langext.Ptr(strings.TrimSpace(*Title)) } diff --git a/scnserver/logic/permissions.go b/scnserver/logic/permissions.go index bcc873c..e9d48cf 100644 --- a/scnserver/logic/permissions.go +++ b/scnserver/logic/permissions.go @@ -9,7 +9,7 @@ import ( "gogs.mikescher.com/BlackForestBytes/goext/langext" ) -func (ac *AppContext) CheckPermissionUserRead(userid models.UserID) *ginresp.HTTPResponse { +func (ac *AppContext) CheckPermissionUserRead(userid models.UserID) *ginext.HTTPResponse { p := ac.permissions if p.Token != nil && p.Token.IsUserRead(userid) { return nil @@ -18,7 +18,7 @@ func (ac *AppContext) CheckPermissionUserRead(userid models.UserID) *ginresp.HTT return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)) } -func (ac *AppContext) CheckPermissionSelfAllMessagesRead() *ginresp.HTTPResponse { +func (ac *AppContext) CheckPermissionSelfAllMessagesRead() *ginext.HTTPResponse { p := ac.permissions if p.Token != nil && p.Token.IsAllMessagesRead(p.Token.OwnerUserID) { return nil @@ -27,7 +27,7 @@ func (ac *AppContext) CheckPermissionSelfAllMessagesRead() *ginresp.HTTPResponse return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)) } -func (ac *AppContext) CheckPermissionAllMessagesRead(userid models.UserID) *ginresp.HTTPResponse { +func (ac *AppContext) CheckPermissionAllMessagesRead(userid models.UserID) *ginext.HTTPResponse { p := ac.permissions if p.Token != nil && p.Token.IsAllMessagesRead(userid) { return nil @@ -36,7 +36,7 @@ func (ac *AppContext) CheckPermissionAllMessagesRead(userid models.UserID) *ginr return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)) } -func (ac *AppContext) CheckPermissionChanMessagesRead(channel models.Channel) *ginresp.HTTPResponse { +func (ac *AppContext) CheckPermissionChanMessagesRead(channel models.Channel) *ginext.HTTPResponse { p := ac.permissions if p.Token != nil && p.Token.IsChannelMessagesRead(channel.ChannelID) { @@ -63,7 +63,7 @@ func (ac *AppContext) CheckPermissionChanMessagesRead(channel models.Channel) *g return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)) } -func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginresp.HTTPResponse { +func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginext.HTTPResponse { p := ac.permissions if p.Token != nil && p.Token.IsAdmin(userid) { return nil @@ -72,7 +72,7 @@ func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginresp.HT return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)) } -func (ac *AppContext) CheckPermissionSend(channel models.Channel, key string) (*models.KeyToken, *ginresp.HTTPResponse) { +func (ac *AppContext) CheckPermissionSend(channel models.Channel, key string) (*models.KeyToken, *ginext.HTTPResponse) { keytok, err := ac.app.Database.Primary.GetKeyTokenByToken(ac, key) if err != nil { @@ -107,7 +107,7 @@ func (ac *AppContext) CheckPermissionMessageDelete(msg models.Message) bool { return false } -func (ac *AppContext) CheckPermissionAny() *ginresp.HTTPResponse { +func (ac *AppContext) CheckPermissionAny() *ginext.HTTPResponse { p := ac.permissions if p.Token == nil { return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)) diff --git a/scnserver/models/enums_gen.go b/scnserver/models/enums_gen.go index 6d1ef2f..7cec25e 100644 --- a/scnserver/models/enums_gen.go +++ b/scnserver/models/enums_gen.go @@ -5,7 +5,7 @@ package models import "gogs.mikescher.com/BlackForestBytes/goext/langext" import "gogs.mikescher.com/BlackForestBytes/goext/enums" -const ChecksumEnumGenerator = "e500346e3f60b3abf78558ec3df128c3be2a1cefa71c4f1feba9293d14eb85d1" // GoExtVersion: 0.0.463 +const ChecksumEnumGenerator = "fd2e0463f7720d853f7a3394352c084ac7d086e9e012caa3d3d70a6e83749970" // GoExtVersion: 0.0.482 // ================================ ClientType ================================ // diff --git a/scnserver/models/ids_gen.go b/scnserver/models/ids_gen.go index a6dac6e..90df989 100644 --- a/scnserver/models/ids_gen.go +++ b/scnserver/models/ids_gen.go @@ -15,7 +15,7 @@ import "reflect" import "regexp" import "strings" -const ChecksumCharsetIDGenerator = "e500346e3f60b3abf78558ec3df128c3be2a1cefa71c4f1feba9293d14eb85d1" // GoExtVersion: 0.0.463 +const ChecksumCharsetIDGenerator = "fd2e0463f7720d853f7a3394352c084ac7d086e9e012caa3d3d70a6e83749970" // GoExtVersion: 0.0.482 const idlen = 24 diff --git a/scnserver/swagger/swagger.go b/scnserver/swagger/swagger.go index deb123f..8b63917 100644 --- a/scnserver/swagger/swagger.go +++ b/scnserver/swagger/swagger.go @@ -1,10 +1,10 @@ package swagger import ( - "blackforestbytes.com/simplecloudnotifier/api/ginresp" "embed" _ "embed" "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/ginext" "net/http" "strings" ) @@ -46,26 +46,28 @@ func getAsset(fn string) ([]byte, string, bool) { return data, mime, true } -func Handle(g *gin.Context) ginresp.HTTPResponse { +func Handle(pctx ginext.PreContext) ginext.HTTPResponse { type uri struct { Filename string `uri:"sub"` } var u uri - if err := g.ShouldBindUri(&u); err != nil { - return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + ctx, _, errResp := pctx.URI(&u).Start() + if errResp != nil { + return *errResp } + defer ctx.Cancel() u.Filename = strings.TrimLeft(u.Filename, "/") if u.Filename == "" { index, _, _ := getAsset("index.html") - return ginresp.Data(http.StatusOK, "text/html", index) + return ginext.Data(http.StatusOK, "text/html", index) } if data, mime, ok := getAsset(u.Filename); ok { - return ginresp.Data(http.StatusOK, mime, data) + return ginext.Data(http.StatusOK, mime, data) } - return ginresp.JSON(http.StatusNotFound, gin.H{"error": "AssetNotFound", "filename": u.Filename}) + return ginext.JSON(http.StatusNotFound, gin.H{"error": "AssetNotFound", "filename": u.Filename}) } diff --git a/scnserver/swagger/swagger.json b/scnserver/swagger/swagger.json index 03c7606..cd5657e 100644 --- a/scnserver/swagger/swagger.json +++ b/scnserver/swagger/swagger.json @@ -19,63 +19,39 @@ "parameters": [ { "type": "string", - "example": "test", - "name": "channel", - "in": "query" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "query" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "query" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "query" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "query" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "query" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "query" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "query" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "query" }, + { + "type": "string", + "name": "user_key", + "in": "query" + }, { "description": " ", "name": "post_body", @@ -86,62 +62,38 @@ }, { "type": "string", - "example": "test", - "name": "channel", - "in": "formData" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "formData" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "formData" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "formData" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "formData" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "formData" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "formData" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "formData" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "formData" + }, + { + "type": "string", + "name": "user_key", + "in": "formData" } ], "responses": { @@ -2765,63 +2717,39 @@ "parameters": [ { "type": "string", - "example": "test", - "name": "channel", - "in": "query" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "query" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "query" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "query" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "query" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "query" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "query" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "query" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "query" }, + { + "type": "string", + "name": "user_key", + "in": "query" + }, { "description": " ", "name": "post_body", @@ -2832,62 +2760,38 @@ }, { "type": "string", - "example": "test", - "name": "channel", - "in": "formData" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "formData" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "formData" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "formData" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "formData" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "formData" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "formData" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "formData" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "formData" + }, + { + "type": "string", + "name": "user_key", + "in": "formData" } ], "responses": { @@ -2935,121 +2839,73 @@ "parameters": [ { "type": "string", - "example": "test", - "name": "channel", - "in": "query" - }, - { - "type": "string", - "example": "This is a message", "name": "content", "in": "query" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "query" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "query" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "query" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "query" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "query" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "query" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "query" }, { "type": "string", - "example": "test", - "name": "channel", - "in": "formData" + "name": "user_key", + "in": "query" }, { "type": "string", - "example": "This is a message", "name": "content", "in": "formData" }, { "type": "string", - "example": "P3TNH8mvv14fm", - "name": "key", - "in": "formData" - }, - { - "type": "string", - "example": "db8b0e6a-a08c-4646", "name": "msg_id", "in": "formData" }, { - "enum": [ - 0, - 1, - 2 - ], "type": "integer", - "example": 1, "name": "priority", "in": "formData" }, - { - "type": "string", - "example": "example-server", - "name": "sender_name", - "in": "formData" - }, { "type": "number", - "example": 1669824037, "name": "timestamp", "in": "formData" }, { "type": "string", - "example": "Hello World", "name": "title", "in": "formData" }, { - "type": "string", - "example": "7725", + "type": "integer", "name": "user_id", "in": "formData" + }, + { + "type": "string", + "name": "user_key", + "in": "formData" } ], "responses": { @@ -3545,46 +3401,26 @@ "handler.SendMessage.combined": { "type": "object", "properties": { - "channel": { - "type": "string", - "example": "test" - }, "content": { - "type": "string", - "example": "This is a message" - }, - "key": { - "type": "string", - "example": "P3TNH8mvv14fm" + "type": "string" }, "msg_id": { - "type": "string", - "example": "db8b0e6a-a08c-4646" + "type": "string" }, "priority": { - "type": "integer", - "enum": [ - 0, - 1, - 2 - ], - "example": 1 - }, - "sender_name": { - "type": "string", - "example": "example-server" + "type": "integer" }, "timestamp": { - "type": "number", - "example": 1669824037 + "type": "number" }, "title": { - "type": "string", - "example": "Hello World" + "type": "string" }, "user_id": { - "type": "string", - "example": "7725" + "type": "integer" + }, + "user_key": { + "type": "string" } } }, @@ -3613,7 +3449,7 @@ "type": "integer" }, "scn_msg_id": { - "type": "string" + "type": "integer" }, "success": { "type": "boolean" diff --git a/scnserver/swagger/swagger.yaml b/scnserver/swagger/swagger.yaml index 81046da..9a33c92 100644 --- a/scnserver/swagger/swagger.yaml +++ b/scnserver/swagger/swagger.yaml @@ -327,36 +327,19 @@ definitions: type: object handler.SendMessage.combined: properties: - channel: - example: test - type: string content: - example: This is a message - type: string - key: - example: P3TNH8mvv14fm type: string msg_id: - example: db8b0e6a-a08c-4646 type: string priority: - enum: - - 0 - - 1 - - 2 - example: 1 type: integer - sender_name: - example: example-server - type: string timestamp: - example: 1669824037 type: number title: - example: Hello World type: string user_id: - example: "7725" + type: integer + user_key: type: string type: object handler.SendMessage.response: @@ -376,7 +359,7 @@ definitions: quota_max: type: integer scn_msg_id: - type: string + type: integer success: type: boolean suppress_send: @@ -802,90 +785,52 @@ paths: description: All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required parameters: - - example: test - in: query - name: channel - type: string - - example: This is a message - in: query + - in: query name: content type: string - - example: P3TNH8mvv14fm - in: query - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: query + - in: query name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: query + - in: query name: priority type: integer - - example: example-server - in: query - name: sender_name - type: string - - example: 1669824037 - in: query + - in: query name: timestamp type: number - - example: Hello World - in: query + - in: query name: title type: string - - example: "7725" - in: query + - in: query name: user_id + type: integer + - in: query + name: user_key type: string - description: ' ' in: body name: post_body schema: $ref: '#/definitions/handler.SendMessage.combined' - - example: test - in: formData - name: channel - type: string - - example: This is a message - in: formData + - in: formData name: content type: string - - example: P3TNH8mvv14fm - in: formData - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: formData + - in: formData name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: formData + - in: formData name: priority type: integer - - example: example-server - in: formData - name: sender_name - type: string - - example: 1669824037 - in: formData + - in: formData name: timestamp type: number - - example: Hello World - in: formData + - in: formData name: title type: string - - example: "7725" - in: formData + - in: formData name: user_id + type: integer + - in: formData + name: user_key type: string responses: "200": @@ -2685,90 +2630,52 @@ paths: description: All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required parameters: - - example: test - in: query - name: channel - type: string - - example: This is a message - in: query + - in: query name: content type: string - - example: P3TNH8mvv14fm - in: query - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: query + - in: query name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: query + - in: query name: priority type: integer - - example: example-server - in: query - name: sender_name - type: string - - example: 1669824037 - in: query + - in: query name: timestamp type: number - - example: Hello World - in: query + - in: query name: title type: string - - example: "7725" - in: query + - in: query name: user_id + type: integer + - in: query + name: user_key type: string - description: ' ' in: body name: post_body schema: $ref: '#/definitions/handler.SendMessage.combined' - - example: test - in: formData - name: channel - type: string - - example: This is a message - in: formData + - in: formData name: content type: string - - example: P3TNH8mvv14fm - in: formData - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: formData + - in: formData name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: formData + - in: formData name: priority type: integer - - example: example-server - in: formData - name: sender_name - type: string - - example: 1669824037 - in: formData + - in: formData name: timestamp type: number - - example: Hello World - in: formData + - in: formData name: title type: string - - example: "7725" - in: formData + - in: formData name: user_id + type: integer + - in: formData + name: user_key type: string responses: "200": @@ -2801,85 +2708,47 @@ paths: description: All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required parameters: - - example: test - in: query - name: channel - type: string - - example: This is a message - in: query + - in: query name: content type: string - - example: P3TNH8mvv14fm - in: query - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: query + - in: query name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: query + - in: query name: priority type: integer - - example: example-server - in: query - name: sender_name - type: string - - example: 1669824037 - in: query + - in: query name: timestamp type: number - - example: Hello World - in: query + - in: query name: title type: string - - example: "7725" - in: query + - in: query name: user_id + type: integer + - in: query + name: user_key type: string - - example: test - in: formData - name: channel - type: string - - example: This is a message - in: formData + - in: formData name: content type: string - - example: P3TNH8mvv14fm - in: formData - name: key - type: string - - example: db8b0e6a-a08c-4646 - in: formData + - in: formData name: msg_id type: string - - enum: - - 0 - - 1 - - 2 - example: 1 - in: formData + - in: formData name: priority type: integer - - example: example-server - in: formData - name: sender_name - type: string - - example: 1669824037 - in: formData + - in: formData name: timestamp type: number - - example: Hello World - in: formData + - in: formData name: title type: string - - example: "7725" - in: formData + - in: formData name: user_id + type: integer + - in: formData + name: user_key type: string responses: "200": diff --git a/scnserver/test/util/log.go b/scnserver/test/util/log.go index a84cd11..545b7c0 100644 --- a/scnserver/test/util/log.go +++ b/scnserver/test/util/log.go @@ -1,7 +1,6 @@ package util import ( - "blackforestbytes.com/simplecloudnotifier/api/ginext" "fmt" "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" diff --git a/scnserver/test/util/webserver.go b/scnserver/test/util/webserver.go index e15b5bd..2477eea 100644 --- a/scnserver/test/util/webserver.go +++ b/scnserver/test/util/webserver.go @@ -3,7 +3,6 @@ package util import ( scn "blackforestbytes.com/simplecloudnotifier" "blackforestbytes.com/simplecloudnotifier/api" - "blackforestbytes.com/simplecloudnotifier/api/ginext" "blackforestbytes.com/simplecloudnotifier/google" "blackforestbytes.com/simplecloudnotifier/jobs" "blackforestbytes.com/simplecloudnotifier/logic"