264 lines
6.6 KiB
Go
264 lines
6.6 KiB
Go
package handler
|
|
|
|
import (
|
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
|
"blackforestbytes.com/simplecloudnotifier/db/simplectx"
|
|
"blackforestbytes.com/simplecloudnotifier/logic"
|
|
"bytes"
|
|
"errors"
|
|
"github.com/gin-gonic/gin"
|
|
"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"
|
|
"time"
|
|
)
|
|
|
|
type CommonHandler struct {
|
|
app *logic.Application
|
|
}
|
|
|
|
func NewCommonHandler(app *logic.Application) CommonHandler {
|
|
return CommonHandler{
|
|
app: app,
|
|
}
|
|
}
|
|
|
|
type pingResponse struct {
|
|
Message string `json:"message"`
|
|
Info pingResponseInfo `json:"info"`
|
|
}
|
|
type pingResponseInfo struct {
|
|
Method string `json:"method"`
|
|
Request string `json:"request"`
|
|
Headers map[string][]string `json:"headers"`
|
|
URI string `json:"uri"`
|
|
Address string `json:"addr"`
|
|
}
|
|
|
|
// Ping swaggerdoc
|
|
//
|
|
// @Summary Simple endpoint to test connection (any http method)
|
|
// @Tags Common
|
|
//
|
|
// @Success 200 {object} pingResponse
|
|
// @Failure 500 {object} ginresp.apiError
|
|
//
|
|
// @Router /api/ping [get]
|
|
// @Router /api/ping [post]
|
|
// @Router /api/ping [put]
|
|
// @Router /api/ping [delete]
|
|
// @Router /api/ping [patch]
|
|
func (h CommonHandler) Ping(pctx ginext.PreContext) ginext.HTTPResponse {
|
|
ctx, g, errResp := pctx.Start()
|
|
if errResp != nil {
|
|
return *errResp
|
|
}
|
|
defer ctx.Cancel()
|
|
|
|
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
|
|
|
buf := new(bytes.Buffer)
|
|
_, _ = buf.ReadFrom(g.Request.Body)
|
|
resuestBody := buf.String()
|
|
|
|
return ginext.JSON(http.StatusOK, pingResponse{
|
|
Message: "Pong",
|
|
Info: pingResponseInfo{
|
|
Method: g.Request.Method,
|
|
Request: resuestBody,
|
|
Headers: g.Request.Header,
|
|
URI: g.Request.RequestURI,
|
|
Address: g.Request.RemoteAddr,
|
|
},
|
|
})
|
|
|
|
})
|
|
}
|
|
|
|
// DatabaseTest swaggerdoc
|
|
//
|
|
// @Summary Check for a working database connection
|
|
// @ID api-common-dbtest
|
|
// @Tags Common
|
|
//
|
|
// @Success 200 {object} handler.DatabaseTest.response
|
|
// @Failure 500 {object} ginresp.apiError
|
|
//
|
|
// @Router /api/db-test [post]
|
|
func (h CommonHandler) DatabaseTest(pctx ginext.PreContext) ginext.HTTPResponse {
|
|
type response struct {
|
|
Success bool `json:"success"`
|
|
LibVersion string `json:"libVersion"`
|
|
LibVersionNumber int `json:"libVersionNumber"`
|
|
SourceID string `json:"sourceID"`
|
|
}
|
|
|
|
ctx, g, errResp := pctx.Start()
|
|
if errResp != nil {
|
|
return *errResp
|
|
}
|
|
defer ctx.Cancel()
|
|
|
|
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
|
|
|
libVersion, libVersionNumber, sourceID := sqlite3.Version()
|
|
|
|
err := h.app.Database.Ping(ctx)
|
|
if err != nil {
|
|
return ginresp.InternalError(err)
|
|
}
|
|
|
|
return ginext.JSON(http.StatusOK, response{
|
|
Success: true,
|
|
LibVersion: libVersion,
|
|
LibVersionNumber: libVersionNumber,
|
|
SourceID: sourceID,
|
|
})
|
|
|
|
})
|
|
}
|
|
|
|
// Health swaggerdoc
|
|
//
|
|
// @Summary Server Health-checks
|
|
// @ID api-common-health
|
|
// @Tags Common
|
|
//
|
|
// @Success 200 {object} handler.Health.response
|
|
// @Failure 500 {object} ginresp.apiError
|
|
//
|
|
// @Router /api/health [get]
|
|
func (h CommonHandler) Health(pctx ginext.PreContext) ginext.HTTPResponse {
|
|
type response struct {
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
ctx, g, errResp := pctx.Start()
|
|
if errResp != nil {
|
|
return *errResp
|
|
}
|
|
defer ctx.Cancel()
|
|
|
|
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
|
|
|
_, libVersionNumber, _ := sqlite3.Version()
|
|
|
|
if libVersionNumber < 3039000 {
|
|
return ginresp.InternalError(errors.New("sqlite version too low"))
|
|
}
|
|
|
|
tctx := simplectx.CreateSimpleContext(ctx, nil)
|
|
|
|
err := h.app.Database.Ping(tctx)
|
|
if err != nil {
|
|
return ginresp.InternalError(err)
|
|
}
|
|
|
|
for _, subdb := range h.app.Database.List() {
|
|
|
|
uuidKey, _ := langext.NewHexUUID()
|
|
uuidWrite, _ := langext.NewHexUUID()
|
|
|
|
err = subdb.WriteMetaString(tctx, uuidKey, uuidWrite)
|
|
if err != nil {
|
|
return ginresp.InternalError(err)
|
|
}
|
|
|
|
uuidRead, err := subdb.ReadMetaString(tctx, uuidKey)
|
|
if err != nil {
|
|
return ginresp.InternalError(err)
|
|
}
|
|
|
|
if uuidRead == nil || uuidWrite != *uuidRead {
|
|
return ginresp.InternalError(errors.New("writing into DB was not consistent"))
|
|
}
|
|
|
|
err = subdb.DeleteMeta(tctx, uuidKey)
|
|
if err != nil {
|
|
return ginresp.InternalError(err)
|
|
}
|
|
|
|
}
|
|
|
|
return ginext.JSON(http.StatusOK, response{Status: "ok"})
|
|
|
|
})
|
|
}
|
|
|
|
// Sleep swaggerdoc
|
|
//
|
|
// @Summary Return 200 after x seconds
|
|
// @ID api-common-sleep
|
|
// @Tags Common
|
|
//
|
|
// @Param secs path number true "sleep delay (in seconds)"
|
|
//
|
|
// @Success 200 {object} handler.Sleep.response
|
|
// @Failure 400 {object} ginresp.apiError
|
|
// @Failure 500 {object} ginresp.apiError
|
|
//
|
|
// @Router /api/sleep/{secs} [post]
|
|
func (h CommonHandler) Sleep(pctx ginext.PreContext) ginext.HTTPResponse {
|
|
type uri struct {
|
|
Seconds float64 `uri:"secs"`
|
|
}
|
|
type response struct {
|
|
Start string `json:"start"`
|
|
End string `json:"end"`
|
|
Duration float64 `json:"duration"`
|
|
}
|
|
|
|
ctx, g, errResp := pctx.Start()
|
|
if errResp != nil {
|
|
return *errResp
|
|
}
|
|
defer ctx.Cancel()
|
|
|
|
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
|
|
|
t0 := time.Now().Format(time.RFC3339Nano)
|
|
|
|
var u uri
|
|
if err := g.ShouldBindUri(&u); err != nil {
|
|
return ginresp.APIError(g, 400, apierr.BINDFAIL_URI_PARAM, "Failed to read uri", err)
|
|
}
|
|
|
|
time.Sleep(timeext.FromSeconds(u.Seconds))
|
|
|
|
t1 := time.Now().Format(time.RFC3339Nano)
|
|
|
|
return ginext.JSON(http.StatusOK, response{
|
|
Start: t0,
|
|
End: t1,
|
|
Duration: u.Seconds,
|
|
})
|
|
|
|
})
|
|
}
|
|
|
|
func (h CommonHandler) NoRoute(pctx ginext.PreContext) ginext.HTTPResponse {
|
|
ctx, g, errResp := pctx.Start()
|
|
if errResp != nil {
|
|
return *errResp
|
|
}
|
|
defer ctx.Cancel()
|
|
|
|
return h.app.DoRequest(ctx, g, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
|
|
|
|
return ginext.JSON(http.StatusNotFound, gin.H{
|
|
"": "================ ROUTE NOT FOUND ================",
|
|
"FullPath": g.FullPath(),
|
|
"Method": g.Request.Method,
|
|
"URL": g.Request.URL.String(),
|
|
"RequestURI": g.Request.RequestURI,
|
|
"Proto": g.Request.Proto,
|
|
"Header": g.Request.Header,
|
|
"~": "================ ROUTE NOT FOUND ================",
|
|
})
|
|
|
|
})
|
|
}
|