From 7fc4336397ea64946bdb83e0a15428c675ada3a7 Mon Sep 17 00:00:00 2001 From: DeveloperDurp Date: Sun, 11 Jun 2023 12:40:53 -0400 Subject: [PATCH] update --- controller/controller.go | 4 -- controller/health.go | 5 +- controller/openai.go | 10 +-- controller/token.go | 25 -------- controller/unraid.go | 26 ++++---- docs/docs.go | 135 +++++++++++++++++++++++++++++---------- docs/swagger.json | 135 +++++++++++++++++++++++++++++---------- docs/swagger.yaml | 100 +++++++++++++++++++++-------- main.go | 95 +++++++++++++-------------- model/admin.go | 1 + model/error.go | 8 --- model/messages.go | 5 ++ model/unraid.go | 23 +++++++ service/shared.go | 22 +++++++ 14 files changed, 393 insertions(+), 201 deletions(-) delete mode 100644 controller/token.go delete mode 100644 model/error.go create mode 100644 model/messages.go create mode 100644 model/unraid.go create mode 100644 service/shared.go diff --git a/controller/controller.go b/controller/controller.go index a5773d1..8b86fae 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -39,7 +39,3 @@ func NewController() *Controller { TokenURL: TokenURL, } } - -type Message struct { - Message string `json:"message" example:"message"` -} diff --git a/controller/health.go b/controller/health.go index 0d1a5a1..9f0d88f 100644 --- a/controller/health.go +++ b/controller/health.go @@ -12,8 +12,9 @@ import ( // @Description Get the health of the API // @Tags health // @Accept json -// @Produce json -// @Success 200 {string} json "response" +// @Produce application/json +// @Success 200 {object} model.Message "response" +// @failure 400 {object} model.Message "error" // @Router /health/getHealth [get] func (c *Controller) GetHealth(ctx *gin.Context) { // Return the health in the response body diff --git a/controller/openai.go b/controller/openai.go index 8fb5358..b59594a 100644 --- a/controller/openai.go +++ b/controller/openai.go @@ -15,9 +15,10 @@ import ( // @Description Ask ChatGPT a general question // @Tags openai // @Accept json -// @Produce plain +// @Produce application/json // @Param message query string true "Ask ChatGPT a general question" -// @Success 200 {string} string "response" +// @Success 200 {object} model.Message "response" +// @failure 400 {object} model.Message "error" // @Router /openai/general [get] func (c *Controller) GeneralOpenAI(ctx *gin.Context) { message := ctx.Query("message") @@ -39,9 +40,10 @@ func (c *Controller) GeneralOpenAI(ctx *gin.Context) { // @Description Ask ChatGPT for suggestions as if it was a travel agent // @Tags openai // @Accept json -// @Produce plain +// @Produce application/json // @Param message query string true "Ask ChatGPT for suggestions as a travel agent" -// @Success 200 {string} string "response" +// @Success 200 {object} model.Message "response" +// @failure 400 {object} model.Message "error" // @Router /openai/travelagent [get] func (c *Controller) TravelAgentOpenAI(ctx *gin.Context) { message := "I want you to act as a travel guide. I will give you my location and you will give me suggestions. " + ctx.Query("message") diff --git a/controller/token.go b/controller/token.go deleted file mode 100644 index 14684c2..0000000 --- a/controller/token.go +++ /dev/null @@ -1,25 +0,0 @@ -package controller - -import ( - "net/http" - - "github.com/gin-gonic/gin" - "golang.org/x/oauth2" -) - -// GenerateToken godoc -// -// @Summary Generate Health status -// @Description Get the health of the API -// @Tags token -// @Accept json -// @Produce json -// @Success 200 {string} json "response" -// @Router /token/GenerateToken [get] -func (c *Controller) GenerateToken(conf *oauth2.Config) gin.HandlerFunc { - return func(c *gin.Context) { - // Redirect user to the authorization URL - authURL := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) - c.Redirect(http.StatusTemporaryRedirect, authURL) - } -} diff --git a/controller/unraid.go b/controller/unraid.go index f7c6c6a..b00da49 100644 --- a/controller/unraid.go +++ b/controller/unraid.go @@ -3,13 +3,18 @@ package controller import ( "encoding/json" "fmt" - "log" "net/http" "net/http/cookiejar" "net/url" "strings" "github.com/gin-gonic/gin" + "gitlab.com/DeveloperDurp/DurpAPI/model" +) + +var ( + unraidAPIKey = model.UnraidAPIKey + UnraidURI = model.UnraidURI ) // UnraidPowerUsage godoc @@ -19,29 +24,27 @@ import ( // @Tags unraid // @Accept json // @Produce json -// @Success 200 {string} string "response" +// @Success 200 {object} model.PowerSupply "response" +// @failure 412 {object} model.Message "error" // @Router /unraid/powerusage [get] func (c *Controller) UnraidPowerUsage(ctx *gin.Context) { - // Create a cookie jar to hold cookies for the session jar, err := cookiejar.New(nil) if err != nil { fmt.Println(err) return } - // Create an HTTP client with the cookie jar client := &http.Client{ Jar: jar, } form := url.Values{ "username": {"root"}, - "password": {c.unraidAPIKey}, + "password": {unraidAPIKey}, } - // Login to unraid - req, err := http.NewRequest("POST", "https://"+c.unraidURI+"/login", strings.NewReader(form.Encode())) + req, err := http.NewRequest("POST", "https://"+UnraidURI+"/login", strings.NewReader(form.Encode())) if err != nil { fmt.Println(err) return @@ -54,14 +57,12 @@ func (c *Controller) UnraidPowerUsage(ctx *gin.Context) { } defer resp.Body.Close() - // Check if the login was successful by inspecting the response body or headers if resp.StatusCode != http.StatusOK { fmt.Println("Login failed!") return } - // Now you can use the client to send authenticated requests to other endpoints - req, err = http.NewRequest("GET", "https://"+c.unraidURI+"/plugins/corsairpsu/status.php", nil) + req, err = http.NewRequest("GET", "https://"+UnraidURI+"/plugins/corsairpsu/status.php", nil) if err != nil { fmt.Println(err) return @@ -75,10 +76,11 @@ func (c *Controller) UnraidPowerUsage(ctx *gin.Context) { defer resp.Body.Close() - // Convert the returned data to JSON var responseJSON map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&responseJSON); err != nil { - log.Fatal(err) + fmt.Println(err) + ctx.JSON(http.StatusPreconditionFailed, gin.H{"message": "Bad Response from Unraid"}) + return } ctx.JSON(http.StatusOK, responseJSON) diff --git a/docs/docs.go b/docs/docs.go index 2f49bb6..8f06587 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -13,8 +13,8 @@ const docTemplate = `{ "termsOfService": "http://swagger.io/terms/", "contact": { "name": "API Support", - "url": "http://www.swagger.io/support", - "email": "support@swagger.io" + "url": "https://durp.info", + "email": "developerdurp@durp.info" }, "license": { "name": "Apache 2.0", @@ -42,7 +42,13 @@ const docTemplate = `{ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" + } + }, + "400": { + "description": "error", + "schema": { + "$ref": "#/definitions/model.Message" } } } @@ -55,7 +61,7 @@ const docTemplate = `{ "application/json" ], "produces": [ - "text/plain" + "application/json" ], "tags": [ "openai" @@ -74,7 +80,13 @@ const docTemplate = `{ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" + } + }, + "400": { + "description": "error", + "schema": { + "$ref": "#/definitions/model.Message" } } } @@ -87,7 +99,7 @@ const docTemplate = `{ "application/json" ], "produces": [ - "text/plain" + "application/json" ], "tags": [ "openai" @@ -106,30 +118,13 @@ const docTemplate = `{ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" } - } - } - } - }, - "/token/GenerateToken": { - "get": { - "description": "Get the health of the API", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "token" - ], - "summary": "Generate Health status", - "responses": { - "200": { - "description": "response", + }, + "400": { + "description": "error", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" } } } @@ -152,18 +147,90 @@ const docTemplate = `{ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.PowerSupply" + } + }, + "412": { + "description": "error", + "schema": { + "$ref": "#/definitions/model.Message" } } } } } }, - "securityDefinitions": { - "ApiKeyAuth": { - "type": "apiKey", - "name": "Authorization", - "in": "header" + "definitions": { + "model.Message": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "message" + } + } + }, + "model.PowerSupply": { + "type": "object", + "properties": { + "12v_load": { + "type": "integer" + }, + "12v_watts": { + "type": "integer" + }, + "3v_load": { + "type": "integer" + }, + "3v_watts": { + "type": "integer" + }, + "5v_load": { + "type": "integer" + }, + "5v_watts": { + "type": "integer" + }, + "capacity": { + "type": "string" + }, + "efficiency": { + "type": "integer" + }, + "fan_rpm": { + "type": "integer" + }, + "load": { + "type": "integer" + }, + "poweredon": { + "type": "string" + }, + "poweredon_raw": { + "type": "string" + }, + "product": { + "type": "string" + }, + "temp1": { + "type": "integer" + }, + "temp2": { + "type": "integer" + }, + "uptime": { + "type": "string" + }, + "uptime_raw": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "watts": { + "type": "integer" + } + } } } }` diff --git a/docs/swagger.json b/docs/swagger.json index 36422b5..8d280d6 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -6,8 +6,8 @@ "termsOfService": "http://swagger.io/terms/", "contact": { "name": "API Support", - "url": "http://www.swagger.io/support", - "email": "support@swagger.io" + "url": "https://durp.info", + "email": "developerdurp@durp.info" }, "license": { "name": "Apache 2.0", @@ -33,7 +33,13 @@ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" + } + }, + "400": { + "description": "error", + "schema": { + "$ref": "#/definitions/model.Message" } } } @@ -46,7 +52,7 @@ "application/json" ], "produces": [ - "text/plain" + "application/json" ], "tags": [ "openai" @@ -65,7 +71,13 @@ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" + } + }, + "400": { + "description": "error", + "schema": { + "$ref": "#/definitions/model.Message" } } } @@ -78,7 +90,7 @@ "application/json" ], "produces": [ - "text/plain" + "application/json" ], "tags": [ "openai" @@ -97,30 +109,13 @@ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" } - } - } - } - }, - "/token/GenerateToken": { - "get": { - "description": "Get the health of the API", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "token" - ], - "summary": "Generate Health status", - "responses": { - "200": { - "description": "response", + }, + "400": { + "description": "error", "schema": { - "type": "string" + "$ref": "#/definitions/model.Message" } } } @@ -143,18 +138,90 @@ "200": { "description": "response", "schema": { - "type": "string" + "$ref": "#/definitions/model.PowerSupply" + } + }, + "412": { + "description": "error", + "schema": { + "$ref": "#/definitions/model.Message" } } } } } }, - "securityDefinitions": { - "ApiKeyAuth": { - "type": "apiKey", - "name": "Authorization", - "in": "header" + "definitions": { + "model.Message": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "message" + } + } + }, + "model.PowerSupply": { + "type": "object", + "properties": { + "12v_load": { + "type": "integer" + }, + "12v_watts": { + "type": "integer" + }, + "3v_load": { + "type": "integer" + }, + "3v_watts": { + "type": "integer" + }, + "5v_load": { + "type": "integer" + }, + "5v_watts": { + "type": "integer" + }, + "capacity": { + "type": "string" + }, + "efficiency": { + "type": "integer" + }, + "fan_rpm": { + "type": "integer" + }, + "load": { + "type": "integer" + }, + "poweredon": { + "type": "string" + }, + "poweredon_raw": { + "type": "string" + }, + "product": { + "type": "string" + }, + "temp1": { + "type": "integer" + }, + "temp2": { + "type": "integer" + }, + "uptime": { + "type": "string" + }, + "uptime_raw": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "watts": { + "type": "integer" + } + } } } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d156e88..e08284c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,9 +1,57 @@ basePath: /api/v1 +definitions: + model.Message: + properties: + message: + example: message + type: string + type: object + model.PowerSupply: + properties: + 3v_load: + type: integer + 3v_watts: + type: integer + 5v_load: + type: integer + 5v_watts: + type: integer + 12v_load: + type: integer + 12v_watts: + type: integer + capacity: + type: string + efficiency: + type: integer + fan_rpm: + type: integer + load: + type: integer + poweredon: + type: string + poweredon_raw: + type: string + product: + type: string + temp1: + type: integer + temp2: + type: integer + uptime: + type: string + uptime_raw: + type: string + vendor: + type: string + watts: + type: integer + type: object info: contact: - email: support@swagger.io + email: developerdurp@durp.info name: API Support - url: http://www.swagger.io/support + url: https://durp.info description: API for Durp's needs license: name: Apache 2.0 @@ -22,7 +70,11 @@ paths: "200": description: response schema: - type: string + $ref: '#/definitions/model.Message' + "400": + description: error + schema: + $ref: '#/definitions/model.Message' summary: Generate Health status tags: - health @@ -38,12 +90,16 @@ paths: required: true type: string produces: - - text/plain + - application/json responses: "200": description: response schema: - type: string + $ref: '#/definitions/model.Message' + "400": + description: error + schema: + $ref: '#/definitions/model.Message' summary: Gerneral ChatGPT tags: - openai @@ -59,30 +115,19 @@ paths: required: true type: string produces: - - text/plain + - application/json responses: "200": description: response schema: - type: string + $ref: '#/definitions/model.Message' + "400": + description: error + schema: + $ref: '#/definitions/model.Message' summary: Travel Agent ChatGPT tags: - openai - /token/GenerateToken: - get: - consumes: - - application/json - description: Get the health of the API - produces: - - application/json - responses: - "200": - description: response - schema: - type: string - summary: Generate Health status - tags: - - token /unraid/powerusage: get: consumes: @@ -94,13 +139,12 @@ paths: "200": description: response schema: - type: string + $ref: '#/definitions/model.PowerSupply' + "412": + description: error + schema: + $ref: '#/definitions/model.Message' summary: Unraid PSU Stats tags: - unraid -securityDefinitions: - ApiKeyAuth: - in: header - name: Authorization - type: apiKey swagger: "2.0" diff --git a/main.go b/main.go index 4727a1d..6990c8b 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,9 @@ package main import ( - "context" "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" @@ -11,25 +11,12 @@ import ( "gitlab.com/DeveloperDurp/DurpAPI/controller" "gitlab.com/DeveloperDurp/DurpAPI/docs" "gitlab.com/DeveloperDurp/DurpAPI/model" - "golang.org/x/oauth2" ) var ( - host = model.Host - version = model.Version - Conf = &oauth2.Config{ - ClientID: model.ClientID, - ClientSecret: model.ClientSecret, - RedirectURL: model.RedirectURL, - Scopes: []string{ - "email", - "groups", - }, - Endpoint: oauth2.Endpoint{ - AuthURL: model.AuthURL, - TokenURL: model.TokenURL, - }, - } + host = model.Host + version = model.Version + groupsenv = model.Groupsenv ) // @title DurpAPI @@ -37,18 +24,14 @@ var ( // @termsOfService http://swagger.io/terms/ // @contact.name API Support -// @contact.url http://www.swagger.io/support -// @contact.email support@swagger.io +// @contact.url https://durp.info +// @contact.email developerdurp@durp.info // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @BasePath /api/v1 -// @securityDefinitions.apikey ApiKeyAuth -// @in header -// @name Authorization - func main() { r := gin.Default() @@ -62,26 +45,19 @@ func main() { { health.GET("getHealth", c.GetHealth) } - token := v1.Group("/token") - { - token.GET("GenerateToken", c.GenerateToken(Conf)) - } openai := v1.Group("/openai") { - //groups = []string{"openai"} - //openai.Use(authMiddleware()) + openai.Use(authMiddleware([]string{"openai"})) openai.GET("general", c.GeneralOpenAI) openai.GET("travelagent", c.TravelAgentOpenAI) } unraid := v1.Group("/unraid") { - //groups = []string{"unraid"} - //unraid.Use(authMiddleware()) + unraid.Use(authMiddleware([]string{"unraid"})) unraid.GET("powerusage", c.UnraidPowerUsage) } } r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) - r.GET("/callback", CallbackHandler(Conf)) err := r.Run(":8080") if err != nil { @@ -89,27 +65,46 @@ func main() { } } -// CallbackHandler receives the authorization code and exchanges it for a token -func CallbackHandler(conf *oauth2.Config) gin.HandlerFunc { - return func(c *gin.Context) { - // Get the authorization code from the query parameters - code := c.Query("code") +func authMiddleware(allowedGroups []string) gin.HandlerFunc { - // Exchange the authorization code for a token - token, err := conf.Exchange(context.Background(), code) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to exchange authorization code"}) + return func(c *gin.Context) { + + var groups []string + + if groupsenv != "" { + groups = strings.Split(groupsenv, ",") + } else { + // Get the user groups from the request headers + groupsHeader := c.GetHeader("X-authentik-groups") + + // Split the groups header value into individual groups + groups = strings.Split(groupsHeader, "|") + } + + // Check if the user belongs to any of the allowed groups + isAllowed := false + for _, allowedGroup := range allowedGroups { + for _, group := range groups { + if group == allowedGroup { + isAllowed = true + break + } + } + if isAllowed { + break + } + } + + // If the user is not in any of the allowed groups, respond with unauthorized access + if !isAllowed { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "message": "Unauthorized access", + "groups": groups, + }) return } - // Create a response JSON - response := gin.H{ - "access_token": token.AccessToken, - "token_type": token.TokenType, - "refresh_token": token.RefreshToken, - "expiry": token.Expiry, - } - - c.JSON(http.StatusOK, response) + // Call the next handler + c.Next() } } diff --git a/model/admin.go b/model/admin.go index c6aed0b..437025f 100644 --- a/model/admin.go +++ b/model/admin.go @@ -13,4 +13,5 @@ var ( TokenURL = os.Getenv("TokenURL") Host = os.Getenv("host") Version = os.Getenv("version") + Groupsenv = os.Getenv("groups") ) diff --git a/model/error.go b/model/error.go deleted file mode 100644 index 27317dc..0000000 --- a/model/error.go +++ /dev/null @@ -1,8 +0,0 @@ -package model - -import "errors" - -var ( - // ErrNoRow example - ErrNoRow = errors.New("no rows in result set") -) diff --git a/model/messages.go b/model/messages.go new file mode 100644 index 0000000..98030bc --- /dev/null +++ b/model/messages.go @@ -0,0 +1,5 @@ +package model + +type Message struct { + Message string `json:"message" example:"message"` +} diff --git a/model/unraid.go b/model/unraid.go new file mode 100644 index 0000000..973b4e6 --- /dev/null +++ b/model/unraid.go @@ -0,0 +1,23 @@ +package model + +type PowerSupply struct { + TwelveVoltLoad int `json:"12v_load"` + TwelveVoltWatts int `json:"12v_watts"` + ThreeVoltLoad int `json:"3v_load"` + ThreeVoltWatts int `json:"3v_watts"` + FiveVoltLoad int `json:"5v_load"` + FiveVoltWatts int `json:"5v_watts"` + Capacity string `json:"capacity"` + Efficiency int `json:"efficiency"` + FanRPM int `json:"fan_rpm"` + Load int `json:"load"` + PoweredOn string `json:"poweredon"` + PoweredOnRaw string `json:"poweredon_raw"` + Product string `json:"product"` + Temp1 int `json:"temp1"` + Temp2 int `json:"temp2"` + Uptime string `json:"uptime"` + UptimeRaw string `json:"uptime_raw"` + Vendor string `json:"vendor"` + Watts int `json:"watts"` +} diff --git a/service/shared.go b/service/shared.go new file mode 100644 index 0000000..06a0a04 --- /dev/null +++ b/service/shared.go @@ -0,0 +1,22 @@ +package service + +import ( + "fmt" + "time" +) + +func RetryOperation(maxRetries int, delay time.Duration, operation func() error) error { + var err error + for i := 0; i <= maxRetries; i++ { + err = operation() + if err == nil { + return nil + } + fmt.Printf("Error encountered: %v\n", err) + if i < maxRetries { + fmt.Printf("Retrying after %v...\n", delay) + time.Sleep(delay) + } + } + return err +}