This commit is contained in:
2023-06-11 12:40:53 -04:00
parent f9b6e2136b
commit 7fc4336397
14 changed files with 393 additions and 201 deletions

View File

@@ -39,7 +39,3 @@ func NewController() *Controller {
TokenURL: TokenURL, TokenURL: TokenURL,
} }
} }
type Message struct {
Message string `json:"message" example:"message"`
}

View File

@@ -12,8 +12,9 @@ import (
// @Description Get the health of the API // @Description Get the health of the API
// @Tags health // @Tags health
// @Accept json // @Accept json
// @Produce json // @Produce application/json
// @Success 200 {string} json "response" // @Success 200 {object} model.Message "response"
// @failure 400 {object} model.Message "error"
// @Router /health/getHealth [get] // @Router /health/getHealth [get]
func (c *Controller) GetHealth(ctx *gin.Context) { func (c *Controller) GetHealth(ctx *gin.Context) {
// Return the health in the response body // Return the health in the response body

View File

@@ -15,9 +15,10 @@ import (
// @Description Ask ChatGPT a general question // @Description Ask ChatGPT a general question
// @Tags openai // @Tags openai
// @Accept json // @Accept json
// @Produce plain // @Produce application/json
// @Param message query string true "Ask ChatGPT a general question" // @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] // @Router /openai/general [get]
func (c *Controller) GeneralOpenAI(ctx *gin.Context) { func (c *Controller) GeneralOpenAI(ctx *gin.Context) {
message := ctx.Query("message") 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 // @Description Ask ChatGPT for suggestions as if it was a travel agent
// @Tags openai // @Tags openai
// @Accept json // @Accept json
// @Produce plain // @Produce application/json
// @Param message query string true "Ask ChatGPT for suggestions as a travel agent" // @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] // @Router /openai/travelagent [get]
func (c *Controller) TravelAgentOpenAI(ctx *gin.Context) { 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") 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")

View File

@@ -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)
}
}

View File

@@ -3,13 +3,18 @@ package controller
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"net/url" "net/url"
"strings" "strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gitlab.com/DeveloperDurp/DurpAPI/model"
)
var (
unraidAPIKey = model.UnraidAPIKey
UnraidURI = model.UnraidURI
) )
// UnraidPowerUsage godoc // UnraidPowerUsage godoc
@@ -19,29 +24,27 @@ import (
// @Tags unraid // @Tags unraid
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Success 200 {string} string "response" // @Success 200 {object} model.PowerSupply "response"
// @failure 412 {object} model.Message "error"
// @Router /unraid/powerusage [get] // @Router /unraid/powerusage [get]
func (c *Controller) UnraidPowerUsage(ctx *gin.Context) { func (c *Controller) UnraidPowerUsage(ctx *gin.Context) {
// Create a cookie jar to hold cookies for the session
jar, err := cookiejar.New(nil) jar, err := cookiejar.New(nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// Create an HTTP client with the cookie jar
client := &http.Client{ client := &http.Client{
Jar: jar, Jar: jar,
} }
form := url.Values{ form := url.Values{
"username": {"root"}, "username": {"root"},
"password": {c.unraidAPIKey}, "password": {unraidAPIKey},
} }
// Login to unraid req, err := http.NewRequest("POST", "https://"+UnraidURI+"/login", strings.NewReader(form.Encode()))
req, err := http.NewRequest("POST", "https://"+c.unraidURI+"/login", strings.NewReader(form.Encode()))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@@ -54,14 +57,12 @@ func (c *Controller) UnraidPowerUsage(ctx *gin.Context) {
} }
defer resp.Body.Close() defer resp.Body.Close()
// Check if the login was successful by inspecting the response body or headers
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
fmt.Println("Login failed!") fmt.Println("Login failed!")
return return
} }
// Now you can use the client to send authenticated requests to other endpoints req, err = http.NewRequest("GET", "https://"+UnraidURI+"/plugins/corsairpsu/status.php", nil)
req, err = http.NewRequest("GET", "https://"+c.unraidURI+"/plugins/corsairpsu/status.php", nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@@ -75,10 +76,11 @@ func (c *Controller) UnraidPowerUsage(ctx *gin.Context) {
defer resp.Body.Close() defer resp.Body.Close()
// Convert the returned data to JSON
var responseJSON map[string]interface{} var responseJSON map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&responseJSON); err != nil { 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) ctx.JSON(http.StatusOK, responseJSON)

View File

@@ -13,8 +13,8 @@ const docTemplate = `{
"termsOfService": "http://swagger.io/terms/", "termsOfService": "http://swagger.io/terms/",
"contact": { "contact": {
"name": "API Support", "name": "API Support",
"url": "http://www.swagger.io/support", "url": "https://durp.info",
"email": "support@swagger.io" "email": "developerdurp@durp.info"
}, },
"license": { "license": {
"name": "Apache 2.0", "name": "Apache 2.0",
@@ -42,7 +42,13 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
}
},
"400": {
"description": "error",
"schema": {
"$ref": "#/definitions/model.Message"
} }
} }
} }
@@ -55,7 +61,7 @@ const docTemplate = `{
"application/json" "application/json"
], ],
"produces": [ "produces": [
"text/plain" "application/json"
], ],
"tags": [ "tags": [
"openai" "openai"
@@ -74,7 +80,13 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
}
},
"400": {
"description": "error",
"schema": {
"$ref": "#/definitions/model.Message"
} }
} }
} }
@@ -87,7 +99,7 @@ const docTemplate = `{
"application/json" "application/json"
], ],
"produces": [ "produces": [
"text/plain" "application/json"
], ],
"tags": [ "tags": [
"openai" "openai"
@@ -106,30 +118,13 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
} }
} },
} "400": {
} "description": "error",
},
"/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",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
} }
} }
} }
@@ -152,18 +147,90 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.PowerSupply"
}
},
"412": {
"description": "error",
"schema": {
"$ref": "#/definitions/model.Message"
} }
} }
} }
} }
} }
}, },
"securityDefinitions": { "definitions": {
"ApiKeyAuth": { "model.Message": {
"type": "apiKey", "type": "object",
"name": "Authorization", "properties": {
"in": "header" "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"
}
}
} }
} }
}` }`

View File

@@ -6,8 +6,8 @@
"termsOfService": "http://swagger.io/terms/", "termsOfService": "http://swagger.io/terms/",
"contact": { "contact": {
"name": "API Support", "name": "API Support",
"url": "http://www.swagger.io/support", "url": "https://durp.info",
"email": "support@swagger.io" "email": "developerdurp@durp.info"
}, },
"license": { "license": {
"name": "Apache 2.0", "name": "Apache 2.0",
@@ -33,7 +33,13 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
}
},
"400": {
"description": "error",
"schema": {
"$ref": "#/definitions/model.Message"
} }
} }
} }
@@ -46,7 +52,7 @@
"application/json" "application/json"
], ],
"produces": [ "produces": [
"text/plain" "application/json"
], ],
"tags": [ "tags": [
"openai" "openai"
@@ -65,7 +71,13 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
}
},
"400": {
"description": "error",
"schema": {
"$ref": "#/definitions/model.Message"
} }
} }
} }
@@ -78,7 +90,7 @@
"application/json" "application/json"
], ],
"produces": [ "produces": [
"text/plain" "application/json"
], ],
"tags": [ "tags": [
"openai" "openai"
@@ -97,30 +109,13 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
} }
} },
} "400": {
} "description": "error",
},
"/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",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.Message"
} }
} }
} }
@@ -143,18 +138,90 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"type": "string" "$ref": "#/definitions/model.PowerSupply"
}
},
"412": {
"description": "error",
"schema": {
"$ref": "#/definitions/model.Message"
} }
} }
} }
} }
} }
}, },
"securityDefinitions": { "definitions": {
"ApiKeyAuth": { "model.Message": {
"type": "apiKey", "type": "object",
"name": "Authorization", "properties": {
"in": "header" "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"
}
}
} }
} }
} }

View File

@@ -1,9 +1,57 @@
basePath: /api/v1 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: info:
contact: contact:
email: support@swagger.io email: developerdurp@durp.info
name: API Support name: API Support
url: http://www.swagger.io/support url: https://durp.info
description: API for Durp's needs description: API for Durp's needs
license: license:
name: Apache 2.0 name: Apache 2.0
@@ -22,7 +70,11 @@ paths:
"200": "200":
description: response description: response
schema: schema:
type: string $ref: '#/definitions/model.Message'
"400":
description: error
schema:
$ref: '#/definitions/model.Message'
summary: Generate Health status summary: Generate Health status
tags: tags:
- health - health
@@ -38,12 +90,16 @@ paths:
required: true required: true
type: string type: string
produces: produces:
- text/plain - application/json
responses: responses:
"200": "200":
description: response description: response
schema: schema:
type: string $ref: '#/definitions/model.Message'
"400":
description: error
schema:
$ref: '#/definitions/model.Message'
summary: Gerneral ChatGPT summary: Gerneral ChatGPT
tags: tags:
- openai - openai
@@ -59,30 +115,19 @@ paths:
required: true required: true
type: string type: string
produces: produces:
- text/plain - application/json
responses: responses:
"200": "200":
description: response description: response
schema: schema:
type: string $ref: '#/definitions/model.Message'
"400":
description: error
schema:
$ref: '#/definitions/model.Message'
summary: Travel Agent ChatGPT summary: Travel Agent ChatGPT
tags: tags:
- openai - 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: /unraid/powerusage:
get: get:
consumes: consumes:
@@ -94,13 +139,12 @@ paths:
"200": "200":
description: response description: response
schema: schema:
type: string $ref: '#/definitions/model.PowerSupply'
"412":
description: error
schema:
$ref: '#/definitions/model.Message'
summary: Unraid PSU Stats summary: Unraid PSU Stats
tags: tags:
- unraid - unraid
securityDefinitions:
ApiKeyAuth:
in: header
name: Authorization
type: apiKey
swagger: "2.0" swagger: "2.0"

95
main.go
View File

@@ -1,9 +1,9 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files" swaggerFiles "github.com/swaggo/files"
@@ -11,25 +11,12 @@ import (
"gitlab.com/DeveloperDurp/DurpAPI/controller" "gitlab.com/DeveloperDurp/DurpAPI/controller"
"gitlab.com/DeveloperDurp/DurpAPI/docs" "gitlab.com/DeveloperDurp/DurpAPI/docs"
"gitlab.com/DeveloperDurp/DurpAPI/model" "gitlab.com/DeveloperDurp/DurpAPI/model"
"golang.org/x/oauth2"
) )
var ( var (
host = model.Host host = model.Host
version = model.Version version = model.Version
Conf = &oauth2.Config{ groupsenv = model.Groupsenv
ClientID: model.ClientID,
ClientSecret: model.ClientSecret,
RedirectURL: model.RedirectURL,
Scopes: []string{
"email",
"groups",
},
Endpoint: oauth2.Endpoint{
AuthURL: model.AuthURL,
TokenURL: model.TokenURL,
},
}
) )
// @title DurpAPI // @title DurpAPI
@@ -37,18 +24,14 @@ var (
// @termsOfService http://swagger.io/terms/ // @termsOfService http://swagger.io/terms/
// @contact.name API Support // @contact.name API Support
// @contact.url http://www.swagger.io/support // @contact.url https://durp.info
// @contact.email support@swagger.io // @contact.email developerdurp@durp.info
// @license.name Apache 2.0 // @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @BasePath /api/v1 // @BasePath /api/v1
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
func main() { func main() {
r := gin.Default() r := gin.Default()
@@ -62,26 +45,19 @@ func main() {
{ {
health.GET("getHealth", c.GetHealth) health.GET("getHealth", c.GetHealth)
} }
token := v1.Group("/token")
{
token.GET("GenerateToken", c.GenerateToken(Conf))
}
openai := v1.Group("/openai") openai := v1.Group("/openai")
{ {
//groups = []string{"openai"} openai.Use(authMiddleware([]string{"openai"}))
//openai.Use(authMiddleware())
openai.GET("general", c.GeneralOpenAI) openai.GET("general", c.GeneralOpenAI)
openai.GET("travelagent", c.TravelAgentOpenAI) openai.GET("travelagent", c.TravelAgentOpenAI)
} }
unraid := v1.Group("/unraid") unraid := v1.Group("/unraid")
{ {
//groups = []string{"unraid"} unraid.Use(authMiddleware([]string{"unraid"}))
//unraid.Use(authMiddleware())
unraid.GET("powerusage", c.UnraidPowerUsage) unraid.GET("powerusage", c.UnraidPowerUsage)
} }
} }
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.GET("/callback", CallbackHandler(Conf))
err := r.Run(":8080") err := r.Run(":8080")
if err != nil { if err != nil {
@@ -89,27 +65,46 @@ func main() {
} }
} }
// CallbackHandler receives the authorization code and exchanges it for a token func authMiddleware(allowedGroups []string) gin.HandlerFunc {
func CallbackHandler(conf *oauth2.Config) gin.HandlerFunc {
return func(c *gin.Context) {
// Get the authorization code from the query parameters
code := c.Query("code")
// Exchange the authorization code for a token return func(c *gin.Context) {
token, err := conf.Exchange(context.Background(), code)
if err != nil { var groups []string
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to exchange authorization code"})
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 return
} }
// Create a response JSON // Call the next handler
response := gin.H{ c.Next()
"access_token": token.AccessToken,
"token_type": token.TokenType,
"refresh_token": token.RefreshToken,
"expiry": token.Expiry,
}
c.JSON(http.StatusOK, response)
} }
} }

View File

@@ -13,4 +13,5 @@ var (
TokenURL = os.Getenv("TokenURL") TokenURL = os.Getenv("TokenURL")
Host = os.Getenv("host") Host = os.Getenv("host")
Version = os.Getenv("version") Version = os.Getenv("version")
Groupsenv = os.Getenv("groups")
) )

View File

@@ -1,8 +0,0 @@
package model
import "errors"
var (
// ErrNoRow example
ErrNoRow = errors.New("no rows in result set")
)

5
model/messages.go Normal file
View File

@@ -0,0 +1,5 @@
package model
type Message struct {
Message string `json:"message" example:"message"`
}

23
model/unraid.go Normal file
View File

@@ -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"`
}

22
service/shared.go Normal file
View File

@@ -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
}