This commit is contained in:
2023-05-26 20:06:00 -05:00
parent eab2cb9963
commit 5ec35ec521
13 changed files with 188 additions and 58 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ __debug_bin
.env
.idea
output
docs

View File

@@ -1,7 +0,0 @@
connectivity_policy {
allow_deployments_to_no_targets = true
}
versioning_strategy {
template = "#{Octopus.Version.LastMajor}.#{Octopus.Version.LastMinor}.#{Octopus.Version.NextPatch}"
}

View File

@@ -1 +0,0 @@
version = 6

View File

@@ -1,3 +0,0 @@
variable "TestVariable" {
value "1234" {}
}

View File

@@ -1,10 +1,8 @@
package controller
import (
"fmt"
"os"
"github.com/joho/godotenv"
openai "github.com/sashabaranov/go-openai"
)
@@ -12,23 +10,33 @@ type Controller struct {
openaiClient *openai.Client
unraidAPIKey string
unraidURI string
ClientID string
ClientSecret string
RedirectURL string
AuthURL string
TokenURL string
}
func NewController() *Controller {
err := godotenv.Load(".env")
openaiApiKey := os.Getenv("OPENAI_API_KEY")
openaiClient := openai.NewClient(openaiApiKey)
unraidAPIKey := os.Getenv("UNRAID_API_KEY")
unraidURI := os.Getenv("UNRAID_URI")
ClientID := os.Getenv("ClientID")
ClientSecret := os.Getenv("ClientSecret")
RedirectURL := os.Getenv("RedirectURL")
AuthURL := os.Getenv("AuthURL")
TokenURL := os.Getenv("TokenURL")
if err != nil {
fmt.Println(".env file not found, using environment variables")
}
return &Controller{
openaiClient: openaiClient,
openaiClient: openai.NewClient(openaiApiKey),
unraidAPIKey: unraidAPIKey,
unraidURI: unraidURI,
ClientID: ClientID,
ClientSecret: ClientSecret,
RedirectURL: RedirectURL,
AuthURL: AuthURL,
TokenURL: TokenURL,
}
}

25
controller/token.go Normal file
View File

@@ -0,0 +1,25 @@
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

@@ -112,6 +112,29 @@ const docTemplate = `{
}
}
},
"/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": {
"type": "string"
}
}
}
}
},
"/unraid/powerusage": {
"get": {
"description": "Gets the PSU Data from unraid",
@@ -148,7 +171,7 @@ const docTemplate = `{
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "durpapi.durp.info",
Host: "localhost:8080",
BasePath: "/api/v1",
Schemes: []string{},
Title: "DurpAPI",

View File

@@ -15,7 +15,7 @@
},
"version": "1.0"
},
"host": "durpapi.durp.info",
"host": "localhost:8080",
"basePath": "/api/v1",
"paths": {
"/health/getHealth": {
@@ -105,6 +105,29 @@
}
}
},
"/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": {
"type": "string"
}
}
}
}
},
"/unraid/powerusage": {
"get": {
"description": "Gets the PSU Data from unraid",

View File

@@ -1,5 +1,5 @@
basePath: /api/v1
host: durpapi.durp.info
host: localhost:8080
info:
contact:
email: support@swagger.io
@@ -70,6 +70,21 @@ paths:
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:

11
go.mod
View File

@@ -11,7 +11,11 @@ require (
github.com/swaggo/swag v1.8.12
)
require github.com/google/go-cmp v0.5.8 // indirect
require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
google.golang.org/appengine v1.6.7 // indirect
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
@@ -43,8 +47,9 @@ require (
github.com/ugorji/go/codec v1.2.9 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/oauth2 v0.8.0
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect

14
go.sum
View File

@@ -42,7 +42,10 @@ github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -110,6 +113,7 @@ golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
@@ -117,6 +121,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -129,10 +137,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -146,7 +157,10 @@ golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

93
main.go
View File

@@ -1,16 +1,16 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"strings"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"gitlab.com/DeveloperDurp/DurpAPI/controller"
_ "gitlab.com/DeveloperDurp/DurpAPI/docs"
"golang.org/x/oauth2"
)
// @title DurpAPI
@@ -37,6 +37,19 @@ func main() {
r := gin.Default()
c := controller.NewController()
var groups []string
conf := &oauth2.Config{
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
RedirectURL: c.RedirectURL,
Scopes: []string{
"email",
"groups",
},
Endpoint: oauth2.Endpoint{
AuthURL: c.AuthURL,
TokenURL: c.TokenURL,
},
}
v1 := r.Group("/api/v1")
{
@@ -44,6 +57,10 @@ func main() {
{
health.GET("getHealth", c.GetHealth)
}
token := v1.Group("/token")
{
token.GET("GenerateToken", c.GenerateToken(conf))
}
openai := v1.Group("/openai")
{
groups = []string{"openai"}
@@ -59,52 +76,62 @@ func main() {
}
}
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.GET("/callback", CallbackHandler(conf))
err := r.Run(":8080")
if err != nil {
fmt.Println("Failed to start server")
}
}
func authMiddleware(allowedGroups []string) gin.HandlerFunc {
func authMiddleware(groups []string) gin.HandlerFunc {
return func(c *gin.Context) {
var groups []string
groupsenv := os.Getenv("groups")
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, "|")
// Get the access token from the request header or query parameters
accessToken := c.GetHeader("Authorization")
if accessToken == "" {
accessToken = c.Query("access_token")
}
// 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
}
}
// Create an OAuth2 token from the access token
token := &oauth2.Token{AccessToken: accessToken}
// 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,
})
// Validate the token
if !token.Valid() {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
// Add the token to the request context for later use
ctx := context.WithValue(c.Request.Context(), "token", token)
c.Request = c.Request.WithContext(ctx)
// Call the next handler
c.Next()
}
}
// 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")
// 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
}
// 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)
}
}