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,
}
}
type Message struct {
Message string `json:"message" example:"message"`
}

View File

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

View File

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

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 (
"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)

View File

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

View File

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

View File

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

95
main.go
View File

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

View File

@@ -13,4 +13,5 @@ var (
TokenURL = os.Getenv("TokenURL")
Host = os.Getenv("host")
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
}