diff --git a/docs/docs.go b/docs/docs.go index 0894f37..9c7572d 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -50,13 +50,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } }, "401": { "description": "authFailed is a 401 error when logging in fails, includes realm", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } } } @@ -100,13 +100,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } }, "500": { "description": "internalServerError is a 500 server error with a logged error call back", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } } } @@ -150,13 +150,13 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } }, "500": { "description": "internalServerError is a 500 server error with a logged error call back", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } } } @@ -164,7 +164,15 @@ const docTemplate = `{ } }, "definitions": { - "internal.GenericResponse-internal_Response": { + "internal.Response": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "internal.SwaggerGenericResponse-internal_Response": { "type": "object", "properties": { "response": { @@ -176,17 +184,6 @@ const docTemplate = `{ ] } } - }, - "internal.Response": { - "type": "object", - "properties": { - "message": { - "type": "string" - }, - "status": { - "type": "integer" - } - } } }, "securityDefinitions": { diff --git a/docs/swagger.json b/docs/swagger.json index 6a98e86..be72e9e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -41,13 +41,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } }, "401": { "description": "authFailed is a 401 error when logging in fails, includes realm", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } } } @@ -91,13 +91,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } }, "500": { "description": "internalServerError is a 500 server error with a logged error call back", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } } } @@ -141,13 +141,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } }, "500": { "description": "internalServerError is a 500 server error with a logged error call back", "schema": { - "$ref": "#/definitions/internal.GenericResponse-internal_Response" + "$ref": "#/definitions/internal.SwaggerGenericResponse-internal_Response" } } } @@ -155,7 +155,15 @@ } }, "definitions": { - "internal.GenericResponse-internal_Response": { + "internal.Response": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "internal.SwaggerGenericResponse-internal_Response": { "type": "object", "properties": { "response": { @@ -167,17 +175,6 @@ ] } } - }, - "internal.Response": { - "type": "object", - "properties": { - "message": { - "type": "string" - }, - "status": { - "type": "integer" - } - } } }, "securityDefinitions": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 537535e..ea7667b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,18 +1,16 @@ definitions: - internal.GenericResponse-internal_Response: + internal.Response: + properties: + message: + type: string + type: object + internal.SwaggerGenericResponse-internal_Response: properties: response: allOf: - $ref: '#/definitions/internal.Response' description: Response items type: object - internal.Response: - properties: - message: - type: string - status: - type: integer - type: object info: contact: {} description: Pomme is a service that parses zonefiles @@ -44,11 +42,11 @@ paths: "200": description: OK schema: - $ref: '#/definitions/internal.GenericResponse-internal_Response' + $ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response' "401": description: authFailed is a 401 error when logging in fails, includes realm schema: - $ref: '#/definitions/internal.GenericResponse-internal_Response' + $ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response' summary: authenticate as a regular user tags: - accounts @@ -77,12 +75,12 @@ paths: "200": description: OK schema: - $ref: '#/definitions/internal.GenericResponse-internal_Response' + $ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response' "500": description: internalServerError is a 500 server error with a logged error call back schema: - $ref: '#/definitions/internal.GenericResponse-internal_Response' + $ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response' security: - Bearer: [] summary: parse your zonefile @@ -113,12 +111,12 @@ paths: "200": description: OK schema: - $ref: '#/definitions/internal.GenericResponse-internal_Response' + $ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response' "500": description: internalServerError is a 500 server error with a logged error call back schema: - $ref: '#/definitions/internal.GenericResponse-internal_Response' + $ref: '#/definitions/internal.SwaggerGenericResponse-internal_Response' security: - Bearer: [] summary: upload a zonefile diff --git a/internal/api/api.go b/internal/api/api.go index 8a554f2..e0026ef 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -1,83 +1,16 @@ package api import ( - "context" - "fmt" - "log" "net/http" "time" "git.freecumextremist.com/grumbulon/pomme/internal" - "git.freecumextremist.com/grumbulon/pomme/internal/db" "github.com/go-chi/chi/v5" - "github.com/go-chi/httplog" "github.com/go-chi/httprate" "github.com/go-chi/jwtauth/v5" "github.com/go-chi/render" - "gorm.io/gorm" ) -type key int - -const ( - keyPrincipalContextID key = iota -) - -// setDBMiddleware is the http Handler func for the GORM middleware with context. -func setDBMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var pommeDB *gorm.DB - c, err := internal.ReadConfig() - if err != nil { - log.Printf("No config file defined: %v", err) - } - - switch r.Header.Get("User-Agent") { - case "pomme-api-test-slave": - pommeDB = db.InitDb(c.TestDB) - default: - pommeDB = db.InitDb(c.DB) - } - - timeoutContext, cancelContext := context.WithTimeout(context.Background(), time.Second) - ctx := context.WithValue(r.Context(), keyPrincipalContextID, pommeDB.WithContext(timeoutContext)) - defer cancelContext() - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} - -type GenericResponse[T map[string]any] struct { - Response map[string]any `json:"response,omitempty"` -} - -func APIError[T map[string]any](w http.ResponseWriter, r *http.Request, v map[string]any) { - logger := httplog.NewLogger("Pomme", httplog.Options{ - JSON: true, - }) - - w.Header().Set("X-Content-Type-Options", "nosniff") - w.Header().Set("Content-Type", "application/json; charset=utf-8") - - switch v["realm"] { - case nil: - w.Header().Add("API Error", v["message"].(string)) - default: - w.Header().Add("WWW-Authenticate", fmt.Sprintf(`realm="%s"`, v["realm"].(string))) - w.Header().Add("API Error", v["message"].(string)) - } - - w.WriteHeader(v["status"].(int)) - - render.JSON(w, r, v) - - switch v["error"] { - case nil: - logger.Info().Msg(fmt.Sprint(v["message"])) - default: - logger.Error().Msg(fmt.Sprint(v["error"])) - } -} - // API subroute handler. func API() http.Handler { api := chi.NewRouter() diff --git a/internal/api/auth.go b/internal/api/auth.go index 137208b..7dbcbd2 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -22,8 +22,8 @@ import ( // @Produce json // @Param username query string true "Username" // @Param password query string true "Password" -// @Success 200 {object} internal.GenericResponse[internal.Response] -// @failure 401 {object} internal.GenericResponse[internal.Response] "authFailed is a 401 error when logging in fails, includes realm" +// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response] +// @failure 401 {object} internal.SwaggerGenericResponse[internal.Response] "authFailed is a 401 error when logging in fails, includes realm" // @Router /api/login [post] func Login(w http.ResponseWriter, r *http.Request) { var result internal.User diff --git a/internal/api/helpers.go b/internal/api/helpers.go new file mode 100644 index 0000000..7d2af27 --- /dev/null +++ b/internal/api/helpers.go @@ -0,0 +1,75 @@ +package api + +import ( + "context" + "fmt" + "log" + "net/http" + "time" + + "git.freecumextremist.com/grumbulon/pomme/internal" + "git.freecumextremist.com/grumbulon/pomme/internal/db" + "github.com/go-chi/httplog" + "github.com/go-chi/render" + "gorm.io/gorm" +) + +// setDBMiddleware is the http Handler func for the GORM middleware with context. +func setDBMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var pommeDB *gorm.DB + c, err := internal.ReadConfig() + if err != nil { + log.Printf("No config file defined: %v", err) + } + + switch r.Header.Get("User-Agent") { + case "pomme-api-test-slave": + pommeDB = db.InitDb(c.TestDB) + default: + pommeDB = db.InitDb(c.DB) + } + + timeoutContext, cancelContext := context.WithTimeout(context.Background(), time.Second) + ctx := context.WithValue(r.Context(), keyPrincipalContextID, pommeDB.WithContext(timeoutContext)) + defer cancelContext() + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func APIError[T map[string]any](w http.ResponseWriter, r *http.Request, v map[string]any) { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Content-Type", "application/json; charset=utf-8") + + logHandler(v) + + switch v["realm"] { + case nil: + w.Header().Add("API Error", v["message"].(string)) + default: + w.Header().Add("WWW-Authenticate", fmt.Sprintf(`realm="%s"`, v["realm"].(string))) + w.Header().Add("API Error", v["message"].(string)) + } + + w.WriteHeader(v["status"].(int)) + + delete(v, "error") + + render.JSON(w, r, v) +} + +func logHandler(v map[string]any) { + logger := httplog.NewLogger("Pomme", httplog.Options{ + JSON: true, + }) + + switch v["error"] { + default: + logger.Error().Msg(v["error"].(string)) + fallthrough + case nil: + logger.Info().Msg(v["message"].(string)) + + } + +} diff --git a/internal/api/types.go b/internal/api/types.go index 66ea90d..a56a1cc 100644 --- a/internal/api/types.go +++ b/internal/api/types.go @@ -2,6 +2,14 @@ package api import "gorm.io/gorm" +const ( + keyPrincipalContextID key = iota +) + +type genericResponseFields map[string]any + +type key int + // ZoneRequest represents a Zone file request. type ZoneRequest struct { *Zone @@ -17,4 +25,6 @@ type Zone struct { Body string `json:"body,omitempty"` } -type genericResponseFields map[string]any +type GenericResponse[T map[string]any] struct { + Response map[string]any `json:"response,omitempty"` +} diff --git a/internal/api/zone.go b/internal/api/zone.go index 4e1480c..489d719 100644 --- a/internal/api/zone.go +++ b/internal/api/zone.go @@ -29,8 +29,8 @@ import ( // @Accept mpfd // @Produce json // @Param file formData file true "Zonefile to upload" -// @Success 200 {object} internal.GenericResponse[internal.Response] -// @Failure 500 {object} internal.GenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back" +// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response] +// @Failure 500 {object} internal.SwaggerGenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back" // @Param Authorization header string true "Bearer Token" // // @Security Bearer @@ -114,8 +114,8 @@ func ReceiveFile(w http.ResponseWriter, r *http.Request) { // @Accept mpfd // @Produce json // @Param filename query string true "Zonefile name" -// @Success 200 {object} internal.GenericResponse[internal.Response] -// @Failure 500 {object} internal.GenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back" +// @Success 200 {object} internal.SwaggerGenericResponse[internal.Response] +// @Failure 500 {object} internal.SwaggerGenericResponse[internal.Response] "internalServerError is a 500 server error with a logged error call back" // @Param Authorization header string true "Bearer Token" // // @Security Bearer diff --git a/internal/types.go b/internal/types.go index 6e59d45..5d049e8 100644 --- a/internal/types.go +++ b/internal/types.go @@ -37,9 +37,9 @@ type Config struct { TestDB string } -// GenericNestedResponse[T] -// @Description Some Generic List Response. -type GenericResponse[T any] struct { +// SwaggerGenericResponse[T] +// @Description Generic Response for Swagger. +type SwaggerGenericResponse[T any] struct { // Response items Response T }