Compare commits

...

10 Commits

Author SHA1 Message Date
80346dfd99 update 2024-06-22 12:46:16 -05:00
e656315c70 Merge branch 'feature/rewrite' into 'main'
rewrite to using new method

See merge request developerdurp/durpapi!3
2024-06-16 23:29:37 +00:00
790c8c969a rewrite to using new method 2024-06-16 18:21:06 -05:00
f07bd666fb update 2024-05-28 22:17:27 -05:00
7362519d30 update 2024-05-28 22:17:08 -05:00
1a84e3822c update 2024-05-28 22:11:22 -05:00
e8c6257cc8 update 2024-05-28 22:05:05 -05:00
79579cb67e update 2024-05-28 21:59:32 -05:00
c67f987b15 update 2024-05-28 21:44:02 -05:00
5755e35ac1 update 2024-05-27 08:41:36 -05:00
22 changed files with 501 additions and 521 deletions

View File

@@ -1,90 +1,77 @@
stages: stages:
- deploy - build
- staging - release
- production
include:
- project: "developerdurp/yml"
ref: "main"
file:
- "jobs/version.yml"
- "jobs/gitlab.yml"
variables: variables:
VAULT_SERVER_URL: https://vault.internal.durp.info PARENT_PIPELINE_ID: $CI_PIPELINE_ID
VAULT_AUTH_ROLE: gitlab GO_VERSION: "1.22"
VAULT_AUTH_PATH: jwt GOLANGCI_LINT_VERISON: "v1.58.0"
UPLOAD_PACKAGE: "false"
PROJECT_NAME: $CI_PROJECT_NAME
GITLAB_PROJECT_ID: "45028985"
GITLAB_CHART_PATH: "durpapi%2FChart.yaml"
test: version:
stage: deploy extends: .version
image: registry.internal.durp.info/debian:latest stage: .pre
script:
- echo $test
- echo $VAULT_ID_TOKEN
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.internal.durp.info
secrets:
test:
vault: gitlab-ci/test@secrets
#include: build:
# - project: 'developerdurp/yml' stage: build
# ref: 'main' allow_failure: false
# file: trigger:
# - 'jobs/version.yml' include:
# - 'jobs/gitlab.yml' - project: "developerdurp/yml"
# ref: "main"
#variables: file:
# PARENT_PIPELINE_ID: $CI_PIPELINE_ID - "pipelines/go-build.yml"
# GO_VERSION: "1.22" strategy: depend
# GOLANGCI_LINT_VERISON: "v1.58.0" rules:
# UPLOAD_PACKAGE: "false" - exists:
# PROJECT_NAME: $CI_PROJECT_NAME - "go.mod"
#
#version: generate-pipeline-dev:
# extends: .version extends: .generate-config
# stage: .pre stage: build
# variables:
# #build: ENVIRONMENT: "dev"
# # stage: deploy
# # allow_failure: false deploy-dev:
# # trigger: stage: release
# # include: needs:
# # - project: 'developerdurp/yml' - job: build
# # ref: 'main' trigger:
# # file: include:
# # - 'pipelines/go-build.yml' - artifact: generated-config.yml
# # strategy: depend job: generate-pipeline-dev
# # rules: strategy: depend
# # - exists: variables:
# # - "go.mod" ENVIRONMENT: "dev"
# PARENT_PIPELINE_ID: $CI_PIPELINE_ID
#generate-config-staging:
# extends: .generate-config generate-pipeline-prd:
# stage: staging extends: .generate-config
# variables: stage: build
# ENVIRONMENT: "staging" variables:
# ENVIRONMENT: "prd"
#child-pipeline-staging:
# stage: staging deploy-prd:
# needs: stage: release
# - job: generate-config-staging needs:
# trigger: - job: deploy-dev
# include: - job: generate-pipeline-prd
# - artifact: generated-config.yml when: manual
# job: generate-config-staging trigger:
# include:
#generate-config-production: - artifact: generated-config.yml
# extends: .generate-config job: generate-pipeline-prd
# stage: production strategy: depend
# when: manual variables:
# needs: ENVIRONMENT: "prd"
# - job: version PARENT_PIPELINE_ID: $CI_PIPELINE_ID
# artifacts: true
# - job: generate-config-staging
# - job: child-pipeline-staging
# variables:
# ENVIRONMENT: "production"
#
#child-pipeline-production:
# stage: production
# needs:
# - job: generate-config-production
# trigger:
# include:
# - artifact: generated-config.yml
# job: generate-config-production

View File

@@ -1,4 +1,4 @@
start: start:
sudo docker run --name postgres-db -e POSTGRES_PASSWORD=docker -p 5432:5432 -d postgres sudo podman run --name postgres-db -e POSTGRES_PASSWORD=docker -p 5432:5432 -d postgres
stop: stop:
sudo docker rm postgres-db -f sudo podman rm postgres-db -f

137
cmd/controller.go Normal file
View File

@@ -0,0 +1,137 @@
package controller
import (
"fmt"
"log"
"net/http"
"github.com/caarlos0/env/v6"
"github.com/joho/godotenv"
httpSwagger "github.com/swaggo/http-swagger"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gitlab.com/DeveloperDurp/DurpAPI/middleware"
"gitlab.com/DeveloperDurp/DurpAPI/pkg/dadjoke"
"gitlab.com/DeveloperDurp/DurpAPI/pkg/health"
"gitlab.com/DeveloperDurp/DurpAPI/pkg/openai"
"gitlab.com/developerdurp/stdmodels"
)
type Controller struct {
Cfg Config
Dbcfg DBConfig
Db *gorm.DB
}
type Config struct {
Host string `env:"host"`
Version string `env:"version"`
Groupsenv string `env:"groupsenv"`
JwksURL string `env:"jwksurl"`
LlamaURL string `env:"llamaurl"`
}
type DBConfig struct {
Host string `env:"db_host"`
Port string `env:"db_port"`
Password string `env:"db_pass"`
User string `env:"db_user"`
DBName string `env:"db_name"`
SSLMode string `env:"db_sslmode"`
}
func NewController() *Controller {
err := godotenv.Load(".env")
if err != nil {
fmt.Println("no env file found")
}
controller := &Controller{
Cfg: Config{},
Dbcfg: DBConfig{},
}
err = env.Parse(&controller.Cfg)
if err != nil {
log.Fatalf("unable to parse environment variables: %e", err)
}
err = env.Parse(&controller.Dbcfg)
if err != nil {
log.Fatalf("unable to parse database variables: %e", err)
}
Db, err := connectDB(controller.Dbcfg)
if err != nil {
panic("Failed to connect to database")
}
controller.Db = Db
return controller
}
func (c *Controller) Run() error {
router := http.NewServeMux()
err := c.loadAll(router)
if err != nil {
return err
}
stack := middleware.CreateStack(
middleware.Logging,
middleware.Headers,
)
server := http.Server{
Addr: ":8080",
Handler: stack(router),
}
fmt.Println("Server listening on port :8080")
return server.ListenAndServe()
}
func (c *Controller) loadAll(router *http.ServeMux) error {
// adminRouter := http.NewServeMux()
// router.Handle("/", middleware.EnsureAdmin(adminRouter))
router.HandleFunc("/", defaultHandler)
router.HandleFunc("/swagger/*", httpSwagger.Handler())
health, err := health.NewHandler()
router.HandleFunc("GET /health/gethealth", health.Get)
dadjoke, err := dadjoke.NewHandler(c.Db)
router.HandleFunc("GET /jokes/dadjoke", dadjoke.Get)
router.HandleFunc("POST /jokes/dadjoke", dadjoke.Post)
router.HandleFunc("DELETE /jokes/dadjoke", dadjoke.Delete)
openai, err := openai.NewHandler(c.Cfg.LlamaURL)
router.HandleFunc("GET /openai/general", openai.GeneralOpenAI)
router.HandleFunc("GET /openai/travelagent", openai.TravelAgentOpenAI)
if err != nil {
return err
}
return nil
}
func defaultHandler(w http.ResponseWriter, r *http.Request) {
stdmodels.FailureReponse("Page Does not exist", w, http.StatusNotFound, []string{"Page is not found"})
}
func connectDB(config DBConfig) (*gorm.DB, error) {
dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
config.Host, config.Port, config.User, config.Password, config.DBName, config.SSLMode,
)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
return db, nil
}

View File

@@ -1,47 +0,0 @@
package controller
import (
"fmt"
"log"
"github.com/caarlos0/env/v6"
"github.com/joho/godotenv"
"gitlab.com/DeveloperDurp/DurpAPI/model"
"gitlab.com/DeveloperDurp/DurpAPI/storage"
)
type Controller struct {
Cfg model.Config
Dbcfg model.DBConfig
Db model.Repository
}
func NewController() *Controller {
err := godotenv.Load(".env")
if err != nil {
fmt.Println("no env file found")
}
controller := &Controller{
Cfg: model.Config{},
Dbcfg: model.DBConfig{},
}
err = env.Parse(&controller.Cfg)
if err != nil {
log.Fatalf("unable to parse environment variables: %e", err)
}
err = env.Parse(&controller.Dbcfg)
if err != nil {
log.Fatalf("unable to parse database variables: %e", err)
}
Db, err := storage.Connect(controller.Dbcfg)
if err != nil {
panic("Failed to connect to database")
}
controller.Db = *Db
return controller
}

View File

@@ -1,154 +0,0 @@
package controller
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"gitlab.com/DeveloperDurp/DurpAPI/model"
)
type ChatRequest struct {
Message string `json:"message"`
}
// Response struct to unmarshal the JSON response
type Response struct {
Response string `json:"response"`
}
// GeneralOpenAI godoc
//
// @Summary Gerneral ChatGPT
// @Description Ask ChatGPT a general question
// @Tags openai
// @Accept json
// @Produce application/json
// @Param message query string true "Ask ChatGPT a general question"
// @Success 200 {object} model.Message "response"
//
// @failure 400 {object} model.Message "error"
//
// @Security Authorization
//
// @Router /openai/general [get]
func (c *Controller) GeneralOpenAI(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type")
var req ChatRequest
if contentType == "application/json" {
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
return
}
} else {
queryParams := r.URL.Query()
req.Message = queryParams.Get("message")
}
result, err := c.createChatCompletion(req.Message, "mistral:instruct")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
return
}
message := model.Message{
Message: result,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(message)
}
// TravelAgentOpenAI godoc
//
// @Summary Travel Agent ChatGPT
// @Description Ask ChatGPT for suggestions as if it was a travel agent
// @Tags openai
// @Accept json
// @Produce application/json
// @Param message query string true "Ask ChatGPT for suggestions as a travel agent"
// @Success 200 {object} model.Message "response"
// @failure 400 {object} model.Message "error"
//
// @Security Authorization
//
// @Router /openai/travelagent [get]
func (c *Controller) TravelAgentOpenAI(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type")
var req ChatRequest
if contentType == "application/json" {
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
return
}
} else {
queryParams := r.URL.Query()
req.Message = queryParams.Get("message")
}
req.Message = "I want you to act as a travel guide. I will give you my location and you will give me suggestions. " + req.Message
result, err := c.createChatCompletion(req.Message, "mistral:instruct")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
return
}
message := model.Message{
Message: result,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(message)
}
func (c *Controller) createChatCompletion(message string, model string) (string, error) {
// Define the request body
requestBody := map[string]interface{}{
"model": model,
"prompt": message,
"stream": false,
}
// Convert the request body to JSON
requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
return "", fmt.Errorf("error encoding request body: %v", err)
}
// Send a POST request to the specified URL with the request body
response, err := http.Post(
"http://"+c.Cfg.LlamaURL+"/api/generate",
"application/json",
bytes.NewBuffer(requestBodyBytes),
)
if err != nil {
return "", fmt.Errorf("error sending POST request: %v", err)
}
defer response.Body.Close()
// Read the response body
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}
// Unmarshal the JSON response
var resp Response
if err := json.Unmarshal(responseBody, &resp); err != nil {
return "", fmt.Errorf("error decoding response body: %v", err)
}
// Return the response
return resp.Response, nil
}

View File

@@ -46,13 +46,13 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
"description": "error", "description": "error",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardError"
} }
} }
} }
@@ -80,7 +80,7 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
@@ -121,7 +121,7 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
@@ -162,7 +162,7 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
@@ -205,13 +205,13 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"400": { "500": {
"description": "error", "description": "error",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardError"
} }
} }
} }
@@ -248,13 +248,13 @@ const docTemplate = `{
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"400": { "500": {
"description": "error", "description": "error",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardError"
} }
} }
} }
@@ -262,15 +262,6 @@ const docTemplate = `{
} }
}, },
"definitions": { "definitions": {
"model.Message": {
"type": "object",
"properties": {
"message": {
"type": "string",
"example": "message"
}
}
},
"stdmodels.StandardError": { "stdmodels.StandardError": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -287,6 +278,14 @@ const docTemplate = `{
"type": "integer" "type": "integer"
} }
} }
},
"stdmodels.StandardMessage": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
}
} }
}, },
"securityDefinitions": { "securityDefinitions": {

View File

@@ -38,13 +38,13 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
"description": "error", "description": "error",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardError"
} }
} }
} }
@@ -72,7 +72,7 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
@@ -113,7 +113,7 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
@@ -154,7 +154,7 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"500": { "500": {
@@ -197,13 +197,13 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"400": { "500": {
"description": "error", "description": "error",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardError"
} }
} }
} }
@@ -240,13 +240,13 @@
"200": { "200": {
"description": "response", "description": "response",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardMessage"
} }
}, },
"400": { "500": {
"description": "error", "description": "error",
"schema": { "schema": {
"$ref": "#/definitions/model.Message" "$ref": "#/definitions/stdmodels.StandardError"
} }
} }
} }
@@ -254,15 +254,6 @@
} }
}, },
"definitions": { "definitions": {
"model.Message": {
"type": "object",
"properties": {
"message": {
"type": "string",
"example": "message"
}
}
},
"stdmodels.StandardError": { "stdmodels.StandardError": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -279,6 +270,14 @@
"type": "integer" "type": "integer"
} }
} }
},
"stdmodels.StandardMessage": {
"type": "object",
"properties": {
"message": {
"type": "string"
}
}
} }
}, },
"securityDefinitions": { "securityDefinitions": {

View File

@@ -1,11 +1,5 @@
basePath: / basePath: /
definitions: definitions:
model.Message:
properties:
message:
example: message
type: string
type: object
stdmodels.StandardError: stdmodels.StandardError:
properties: properties:
description: description:
@@ -17,6 +11,11 @@ definitions:
status: status:
type: integer type: integer
type: object type: object
stdmodels.StandardMessage:
properties:
message:
type: string
type: object
info: info:
contact: contact:
email: developerdurp@durp.info email: developerdurp@durp.info
@@ -40,11 +39,11 @@ paths:
"200": "200":
description: response description: response
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardMessage'
"500": "500":
description: error description: error
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardError'
security: security:
- Authorization: [] - Authorization: []
summary: Generate Health status summary: Generate Health status
@@ -67,7 +66,7 @@ paths:
"200": "200":
description: response description: response
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardMessage'
"500": "500":
description: error description: error
schema: schema:
@@ -87,7 +86,7 @@ paths:
"200": "200":
description: response description: response
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardMessage'
"500": "500":
description: error description: error
schema: schema:
@@ -113,7 +112,7 @@ paths:
"200": "200":
description: response description: response
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardMessage'
"500": "500":
description: error description: error
schema: schema:
@@ -140,11 +139,11 @@ paths:
"200": "200":
description: response description: response
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardMessage'
"400": "500":
description: error description: error
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardError'
security: security:
- Authorization: [] - Authorization: []
summary: Gerneral ChatGPT summary: Gerneral ChatGPT
@@ -167,11 +166,11 @@ paths:
"200": "200":
description: response description: response
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardMessage'
"400": "500":
description: error description: error
schema: schema:
$ref: '#/definitions/model.Message' $ref: '#/definitions/stdmodels.StandardError'
security: security:
- Authorization: [] - Authorization: []
summary: Travel Agent ChatGPT summary: Travel Agent ChatGPT

4
go.mod
View File

@@ -7,6 +7,8 @@ require (
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/swaggo/http-swagger v1.3.4 github.com/swaggo/http-swagger v1.3.4
github.com/swaggo/swag v1.16.3 github.com/swaggo/swag v1.16.3
gitlab.com/developerdurp/logger v1.0.0
gitlab.com/developerdurp/stdmodels v1.0.0
gorm.io/driver/postgres v1.5.7 gorm.io/driver/postgres v1.5.7
gorm.io/gorm v1.25.9 gorm.io/gorm v1.25.9
) )
@@ -26,8 +28,6 @@ require (
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/swaggo/files v1.0.1 // indirect github.com/swaggo/files v1.0.1 // indirect
gitlab.com/developerdurp/logger v1.0.0 // indirect
gitlab.com/developerdurp/stdmodels v1.0.0 // indirect
golang.org/x/crypto v0.21.0 // indirect golang.org/x/crypto v0.21.0 // indirect
golang.org/x/net v0.22.0 // indirect golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/sync v0.6.0 // indirect

33
main.go
View File

@@ -1,14 +1,10 @@
package main package main
import ( import (
"fmt" "log"
"net/http"
"github.com/swaggo/http-swagger" "gitlab.com/DeveloperDurp/DurpAPI/cmd"
"gitlab.com/DeveloperDurp/DurpAPI/controller"
"gitlab.com/DeveloperDurp/DurpAPI/docs" "gitlab.com/DeveloperDurp/DurpAPI/docs"
"gitlab.com/DeveloperDurp/DurpAPI/middleware"
) )
// @title DurpAPI // @title DurpAPI
@@ -33,30 +29,11 @@ func main() {
docs.SwaggerInfo.Host = c.Cfg.Host docs.SwaggerInfo.Host = c.Cfg.Host
docs.SwaggerInfo.Version = c.Cfg.Version docs.SwaggerInfo.Version = c.Cfg.Version
router := http.NewServeMux() if err := c.Run(); err != nil {
router.HandleFunc("/swagger/*", httpSwagger.Handler()) log.Fatal(err)
}
router.HandleFunc("GET /api/health/gethealth", c.GetHealth)
router.HandleFunc("GET /api/jokes/dadjoke", c.GetDadJoke)
router.HandleFunc("POST /api/jokes/dadjoke", c.PostDadJoke)
router.HandleFunc("DELETE /api/jokes/dadjoke", c.DeleteDadJoke)
router.HandleFunc("GET /api/openai/general", c.GeneralOpenAI)
router.HandleFunc("GET /api/openai/travelagent", c.TravelAgentOpenAI)
// adminRouter := http.NewServeMux() // adminRouter := http.NewServeMux()
// router.Handle("/", middleware.EnsureAdmin(adminRouter)) // router.Handle("/", middleware.EnsureAdmin(adminRouter))
stack := middleware.CreateStack(
middleware.Logging,
)
server := http.Server{
Addr: ":8080",
Handler: stack(router),
}
fmt.Println("Server listening on port :8080")
server.ListenAndServe()
} }

View File

@@ -1,17 +0,0 @@
package middleware
import (
"net/http"
"strings"
)
func EnsureAdmin(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Authorization"), "Admin") {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(http.StatusText(http.StatusUnauthorized)))
return
}
next.ServeHTTP(w, r)
})
}

12
middleware/headers.go Normal file
View File

@@ -0,0 +1,12 @@
package middleware
import "net/http"
func Headers(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
next.ServeHTTP(w, r)
})
}

View File

@@ -1,7 +1,7 @@
package middleware package middleware
import ( import (
"log/slog" "log"
"net/http" "net/http"
"time" "time"
) )
@@ -27,13 +27,13 @@ func Logging(next http.Handler) http.Handler {
next.ServeHTTP(wrapped, r) next.ServeHTTP(wrapped, r)
slog.Info( //slog.Info(
"Health Check", // "Health Check",
slog.Int("Method", wrapped.statusCode), // slog.Int("Method", wrapped.statusCode),
r.Method, // r.Method,
r.URL.Path, // r.URL.Path,
slog.String("time", time.Since(start).String()), // slog.String("time", time.Since(start).String()),
) //)
// log.Println("INFO", wrapped.statusCode, r.Method, r.URL.Path, time.Since(start)) log.Println("INFO", wrapped.statusCode, r.Method, r.URL.Path, time.Since(start))
}) })
} }

View File

@@ -1,26 +0,0 @@
package model
import (
"gorm.io/gorm"
)
type Config struct {
Host string `env:"host"`
Version string `env:"version"`
Groupsenv string `env:"groupsenv"`
JwksURL string `env:"jwksurl"`
LlamaURL string `env:"llamaurl"`
}
type DBConfig struct {
Host string `env:"db_host"`
Port string `env:"db_port"`
Password string `env:"db_pass"`
User string `env:"db_user"`
DBName string `env:"db_name"`
SSLMode string `env:"db_sslmode"`
}
type Repository struct {
DB *gorm.DB
}

View File

@@ -1,5 +0,0 @@
package model
type DadJoke struct {
JOKE string `json:"joke"`
}

View File

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

View File

@@ -1,15 +1,30 @@
package controller package dadjoke
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"gitlab.com/DeveloperDurp/DurpAPI/model"
"gitlab.com/DeveloperDurp/DurpAPI/service"
"gitlab.com/developerdurp/logger" "gitlab.com/developerdurp/logger"
"gitlab.com/developerdurp/stdmodels" "gitlab.com/developerdurp/stdmodels"
"gorm.io/gorm"
) )
type Handler struct {
db *gorm.DB
}
type DadJoke struct {
JOKE string `json:"joke"`
}
func NewHandler(db *gorm.DB) (*Handler, error) {
err := db.AutoMigrate(&DadJoke{})
if err != nil {
return nil, err
}
return &Handler{db: db}, nil
}
// GetDadJoke godoc // GetDadJoke godoc
// //
// @Summary Get dadjoke // @Summary Get dadjoke
@@ -17,23 +32,21 @@ import (
// @Tags DadJoke // @Tags DadJoke
// @Accept json // @Accept json
// @Produce application/json // @Produce application/json
// @Success 200 {object} model.Message "response" // @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} stdmodels.StandardError"error" // @failure 500 {object} stdmodels.StandardError"error"
// //
// @Security Authorization // @Security Authorization
// //
// @Router /jokes/dadjoke [get] // @Router /jokes/dadjoke [get]
func (c *Controller) GetDadJoke(w http.ResponseWriter, r *http.Request) { func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") joke, err := h.GetRandomDadJoke()
joke, err := service.GetRandomDadJoke(c.Db.DB)
if err != nil { if err != nil {
stdmodels.FailureReponse("Failed to get Joke", w, http.StatusInternalServerError, []string{err.Error()}) stdmodels.FailureReponse("Failed to get Joke", w, http.StatusInternalServerError, []string{err.Error()})
return return
} }
message := model.Message{ message := stdmodels.StandardMessage{
Message: joke, Message: joke,
} }
@@ -48,15 +61,15 @@ func (c *Controller) GetDadJoke(w http.ResponseWriter, r *http.Request) {
// @Accept json // @Accept json
// @Produce application/json // @Produce application/json
// @Param joke query string true "Dad Joke you wish to enter into database" // @Param joke query string true "Dad Joke you wish to enter into database"
// @Success 200 {object} model.Message "response" // @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} stdmodels.StandardError"error" // @failure 500 {object} stdmodels.StandardError"error"
// //
// @Security Authorization // @Security Authorization
// //
// @Router /jokes/dadjoke [post] // @Router /jokes/dadjoke [post]
func (c *Controller) PostDadJoke(w http.ResponseWriter, r *http.Request) { func (h *Handler) Post(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type") contentType := r.Header.Get("Content-Type")
var req model.DadJoke var req DadJoke
if contentType == "application/json" { if contentType == "application/json" {
err := json.NewDecoder(r.Body).Decode(&req) err := json.NewDecoder(r.Body).Decode(&req)
@@ -69,7 +82,7 @@ func (c *Controller) PostDadJoke(w http.ResponseWriter, r *http.Request) {
req.JOKE = queryParams.Get("joke") req.JOKE = queryParams.Get("joke")
} }
err := service.PostDadJoke(c.Db.DB, req) err := h.PostDadJoke(req)
if err != nil { if err != nil {
stdmodels.FailureReponse("Failed to add joke", w, http.StatusInternalServerError, []string{err.Error()}) stdmodels.FailureReponse("Failed to add joke", w, http.StatusInternalServerError, []string{err.Error()})
return return
@@ -86,15 +99,15 @@ func (c *Controller) PostDadJoke(w http.ResponseWriter, r *http.Request) {
// @Accept json // @Accept json
// @Produce application/json // @Produce application/json
// @Param joke query string true "Dad joke you wish to delete from the database" // @Param joke query string true "Dad joke you wish to delete from the database"
// @Success 200 {object} model.Message "response" // @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} stdmodels.StandardError"error" // @failure 500 {object} stdmodels.StandardError"error"
// //
// @Security Authorization // @Security Authorization
// //
// @Router /jokes/dadjoke [delete] // @Router /jokes/dadjoke [delete]
func (c *Controller) DeleteDadJoke(w http.ResponseWriter, r *http.Request) { func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type") contentType := r.Header.Get("Content-Type")
var req model.DadJoke var req DadJoke
if contentType == "application/json" { if contentType == "application/json" {
err := json.NewDecoder(r.Body).Decode(&req) err := json.NewDecoder(r.Body).Decode(&req)
@@ -108,17 +121,11 @@ func (c *Controller) DeleteDadJoke(w http.ResponseWriter, r *http.Request) {
req.JOKE = queryParams.Get("joke") req.JOKE = queryParams.Get("joke")
} }
err := service.DeleteDadJoke(c.Db.DB, req) err := h.DeleteDadJoke(req)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) stdmodels.FailureReponse("Failed to delete joke", w, http.StatusInternalServerError, []string{err.Error()})
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
return return
} }
message := model.Message{ stdmodels.SuccessResponse("OK", w, http.StatusOK)
Message: "OK",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(message)
} }

View File

@@ -1,16 +1,12 @@
package service package dadjoke
import ( import (
"errors" "errors"
"math/rand" "math/rand"
"gorm.io/gorm"
"gitlab.com/DeveloperDurp/DurpAPI/model"
) )
func GetRandomDadJoke(db *gorm.DB) (string, error) { func (h *Handler) GetRandomDadJoke() (string, error) {
jokes, err := getDadJokes(db) jokes, err := h.getDadJokes()
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -22,8 +18,8 @@ func GetRandomDadJoke(db *gorm.DB) (string, error) {
return randomElement.JOKE, err return randomElement.JOKE, err
} }
func PostDadJoke(db *gorm.DB, joke model.DadJoke) error { func (h *Handler) PostDadJoke(joke DadJoke) error {
jokes, err := getDadJokes(db) jokes, err := h.getDadJokes()
if err != nil { if err != nil {
return err return err
} }
@@ -39,7 +35,7 @@ func PostDadJoke(db *gorm.DB, joke model.DadJoke) error {
if found { if found {
return errors.New("Joke is already in database") return errors.New("Joke is already in database")
} else { } else {
err = db.Create(&joke).Error err = h.db.Create(&joke).Error
if err != nil { if err != nil {
return err return err
} }
@@ -47,24 +43,24 @@ func PostDadJoke(db *gorm.DB, joke model.DadJoke) error {
} }
} }
func DeleteDadJoke(db *gorm.DB, joke model.DadJoke) error { func (h *Handler) DeleteDadJoke(joke DadJoke) error {
check := &model.DadJoke{} check := &DadJoke{}
db.Where("joke = ?", joke.JOKE).First(check) h.db.Where("joke = ?", joke.JOKE).First(check)
if check.JOKE == "" { if check.JOKE == "" {
return errors.New("Joke does not exist") return errors.New("Joke does not exist")
} }
err := db.Where("joke = ?", joke.JOKE).Delete(joke).Error err := h.db.Where("joke = ?", joke.JOKE).Delete(joke).Error
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
func getDadJokes(db *gorm.DB) ([]model.DadJoke, error) { func (h *Handler) getDadJokes() ([]DadJoke, error) {
req := []model.DadJoke{} req := []DadJoke{}
err := db.Find(&req).Error err := h.db.Find(&req).Error
return req, err return req, err
} }

View File

@@ -1,12 +1,17 @@
package controller package health
import ( import (
"net/http" "net/http"
"gitlab.com/developerdurp/logger"
"gitlab.com/developerdurp/stdmodels" "gitlab.com/developerdurp/stdmodels"
) )
type Handler struct{}
func NewHandler() (*Handler, error) {
return &Handler{}, nil
}
// getHealth godoc // getHealth godoc
// //
// @Summary Generate Health status // @Summary Generate Health status
@@ -14,13 +19,12 @@ import (
// @Tags health // @Tags health
// @Accept json // @Accept json
// @Produce application/json // @Produce application/json
// @Success 200 {object} model.Message "response" // @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} model.Message "error" // @failure 500 {object} stdmodels.StandardError"error"
// //
// @Security Authorization // @Security Authorization
// //
// @Router /health/gethealth [get] // @Router /health/gethealth [get]
func (c *Controller) GetHealth(w http.ResponseWriter, r *http.Request) { func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
logger.LogInfo("Health Check")
stdmodels.SuccessResponse("OK", w, http.StatusOK) stdmodels.SuccessResponse("OK", w, http.StatusOK)
} }

103
pkg/openai/handler.go Normal file
View File

@@ -0,0 +1,103 @@
package openai
import (
"encoding/json"
"net/http"
"gitlab.com/developerdurp/stdmodels"
)
type Handler struct {
LlamaURL string
}
func NewHandler(LlamaURL string) (*Handler, error) {
return &Handler{LlamaURL: LlamaURL}, nil
}
type ChatRequest struct {
Message string `json:"message"`
}
// Response struct to unmarshal the JSON response
type Response struct {
Response string `json:"response"`
}
// GeneralOpenAI godoc
//
// @Summary Gerneral ChatGPT
// @Description Ask ChatGPT a general question
// @Tags openai
// @Accept json
// @Produce application/json
// @Param message query string true "Ask ChatGPT a general question"
// @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} stdmodels.StandardError"error"
//
// @Security Authorization
//
// @Router /openai/general [get]
func (h *Handler) GeneralOpenAI(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type")
var req ChatRequest
if contentType == "application/json" {
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
stdmodels.FailureReponse("Failed To decode content", w, http.StatusInternalServerError, []string{err.Error()})
return
}
} else {
queryParams := r.URL.Query()
req.Message = queryParams.Get("message")
}
result, err := h.createChatCompletion(req.Message, "mistral:instruct")
if err != nil {
stdmodels.FailureReponse("Failed to Send Message", w, http.StatusInternalServerError, []string{err.Error()})
return
}
stdmodels.SuccessResponse(result, w, http.StatusOK)
}
// TravelAgentOpenAI godoc
//
// @Summary Travel Agent ChatGPT
// @Description Ask ChatGPT for suggestions as if it was a travel agent
// @Tags openai
// @Accept json
// @Produce application/json
// @Param message query string true "Ask ChatGPT for suggestions as a travel agent"
// @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} stdmodels.StandardError"error"
//
// @Security Authorization
//
// @Router /openai/travelagent [get]
func (h *Handler) TravelAgentOpenAI(w http.ResponseWriter, r *http.Request) {
contentType := r.Header.Get("Content-Type")
var req ChatRequest
if contentType == "application/json" {
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
stdmodels.FailureReponse("Failed To decode content", w, http.StatusInternalServerError, []string{err.Error()})
return
}
} else {
queryParams := r.URL.Query()
req.Message = queryParams.Get("message")
}
req.Message = "I want you to act as a travel guide. I will give you my location and you will give me suggestions. " + req.Message
result, err := h.createChatCompletion(req.Message, "mistral:instruct")
if err != nil {
stdmodels.FailureReponse("Failed to Send Message", w, http.StatusInternalServerError, []string{err.Error()})
return
}
stdmodels.SuccessResponse(result, w, http.StatusOK)
}

50
pkg/openai/service.go Normal file
View File

@@ -0,0 +1,50 @@
package openai
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func (h *Handler) createChatCompletion(message string, model string) (string, error) {
// Define the request body
requestBody := map[string]interface{}{
"model": model,
"prompt": message,
"stream": false,
}
// Convert the request body to JSON
requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
return "", fmt.Errorf("error encoding request body: %v", err)
}
// Send a POST request to the specified URL with the request body
response, err := http.Post(
"http://"+h.LlamaURL+"/api/generate",
"application/json",
bytes.NewBuffer(requestBodyBytes),
)
if err != nil {
return "", fmt.Errorf("error sending POST request: %v", err)
}
defer response.Body.Close()
// Read the response body
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}
// Unmarshal the JSON response
var resp Response
if err := json.Unmarshal(responseBody, &resp); err != nil {
return "", fmt.Errorf("error decoding response body: %v", err)
}
// Return the response
return resp.Response, nil
}

View File

@@ -1,36 +0,0 @@
package storage
import (
"fmt"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gitlab.com/DeveloperDurp/DurpAPI/model"
)
func Connect(config model.DBConfig) (*model.Repository, error) {
dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
config.Host, config.Port, config.User, config.Password, config.DBName, config.SSLMode,
)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, err
}
err = runMigrations(db)
if err != nil {
return nil, err
}
return &model.Repository{
DB: db,
}, nil
}
func runMigrations(db *gorm.DB) error {
err := db.AutoMigrate(&model.DadJoke{})
if err != nil {
return err
}
return nil
}