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:
- deploy
- staging
- production
- build
- release
include:
- project: "developerdurp/yml"
ref: "main"
file:
- "jobs/version.yml"
- "jobs/gitlab.yml"
variables:
VAULT_SERVER_URL: https://vault.internal.durp.info
VAULT_AUTH_ROLE: gitlab
VAULT_AUTH_PATH: jwt
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
GO_VERSION: "1.22"
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:
stage: deploy
image: registry.internal.durp.info/debian:latest
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
version:
extends: .version
stage: .pre
#include:
# - project: 'developerdurp/yml'
# ref: 'main'
# file:
# - 'jobs/version.yml'
# - 'jobs/gitlab.yml'
#
#variables:
# PARENT_PIPELINE_ID: $CI_PIPELINE_ID
# GO_VERSION: "1.22"
# GOLANGCI_LINT_VERISON: "v1.58.0"
# UPLOAD_PACKAGE: "false"
# PROJECT_NAME: $CI_PROJECT_NAME
#
#version:
# extends: .version
# stage: .pre
#
# #build:
# # stage: deploy
# # allow_failure: false
# # trigger:
# # include:
# # - project: 'developerdurp/yml'
# # ref: 'main'
# # file:
# # - 'pipelines/go-build.yml'
# # strategy: depend
# # rules:
# # - exists:
# # - "go.mod"
#
#generate-config-staging:
# extends: .generate-config
# stage: staging
# variables:
# ENVIRONMENT: "staging"
#
#child-pipeline-staging:
# stage: staging
# needs:
# - job: generate-config-staging
# trigger:
# include:
# - artifact: generated-config.yml
# job: generate-config-staging
#
#generate-config-production:
# extends: .generate-config
# stage: production
# when: manual
# needs:
# - job: version
# 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
build:
stage: build
allow_failure: false
trigger:
include:
- project: "developerdurp/yml"
ref: "main"
file:
- "pipelines/go-build.yml"
strategy: depend
rules:
- exists:
- "go.mod"
generate-pipeline-dev:
extends: .generate-config
stage: build
variables:
ENVIRONMENT: "dev"
deploy-dev:
stage: release
needs:
- job: build
trigger:
include:
- artifact: generated-config.yml
job: generate-pipeline-dev
strategy: depend
variables:
ENVIRONMENT: "dev"
PARENT_PIPELINE_ID: $CI_PIPELINE_ID
generate-pipeline-prd:
extends: .generate-config
stage: build
variables:
ENVIRONMENT: "prd"
deploy-prd:
stage: release
needs:
- job: deploy-dev
- job: generate-pipeline-prd
when: manual
trigger:
include:
- artifact: generated-config.yml
job: generate-pipeline-prd
strategy: depend
variables:
ENVIRONMENT: "prd"
PARENT_PIPELINE_ID: $CI_PIPELINE_ID

View File

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

View File

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

View File

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

4
go.mod
View File

@@ -7,6 +7,8 @@ require (
github.com/joho/godotenv v1.5.1
github.com/swaggo/http-swagger v1.3.4
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/gorm v1.25.9
)
@@ -26,8 +28,6 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // 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/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect

33
main.go
View File

@@ -1,14 +1,10 @@
package main
import (
"fmt"
"net/http"
"log"
"github.com/swaggo/http-swagger"
"gitlab.com/DeveloperDurp/DurpAPI/controller"
"gitlab.com/DeveloperDurp/DurpAPI/cmd"
"gitlab.com/DeveloperDurp/DurpAPI/docs"
"gitlab.com/DeveloperDurp/DurpAPI/middleware"
)
// @title DurpAPI
@@ -33,30 +29,11 @@ func main() {
docs.SwaggerInfo.Host = c.Cfg.Host
docs.SwaggerInfo.Version = c.Cfg.Version
router := http.NewServeMux()
router.HandleFunc("/swagger/*", httpSwagger.Handler())
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)
if err := c.Run(); err != nil {
log.Fatal(err)
}
// adminRouter := http.NewServeMux()
// 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
import (
"log/slog"
"log"
"net/http"
"time"
)
@@ -27,13 +27,13 @@ func Logging(next http.Handler) http.Handler {
next.ServeHTTP(wrapped, r)
slog.Info(
"Health Check",
slog.Int("Method", wrapped.statusCode),
r.Method,
r.URL.Path,
slog.String("time", time.Since(start).String()),
)
// log.Println("INFO", wrapped.statusCode, r.Method, r.URL.Path, time.Since(start))
//slog.Info(
// "Health Check",
// slog.Int("Method", wrapped.statusCode),
// r.Method,
// r.URL.Path,
// slog.String("time", time.Since(start).String()),
//)
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 (
"encoding/json"
"net/http"
"gitlab.com/DeveloperDurp/DurpAPI/model"
"gitlab.com/DeveloperDurp/DurpAPI/service"
"gitlab.com/developerdurp/logger"
"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
//
// @Summary Get dadjoke
@@ -17,23 +32,21 @@ import (
// @Tags DadJoke
// @Accept json
// @Produce application/json
// @Success 200 {object} model.Message "response"
// @Success 200 {object} stdmodels.StandardMessage "response"
// @failure 500 {object} stdmodels.StandardError"error"
//
// @Security Authorization
//
// @Router /jokes/dadjoke [get]
func (c *Controller) GetDadJoke(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
joke, err := service.GetRandomDadJoke(c.Db.DB)
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
joke, err := h.GetRandomDadJoke()
if err != nil {
stdmodels.FailureReponse("Failed to get Joke", w, http.StatusInternalServerError, []string{err.Error()})
return
}
message := model.Message{
message := stdmodels.StandardMessage{
Message: joke,
}
@@ -48,15 +61,15 @@ func (c *Controller) GetDadJoke(w http.ResponseWriter, r *http.Request) {
// @Accept json
// @Produce application/json
// @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"
//
// @Security Authorization
//
// @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")
var req model.DadJoke
var req DadJoke
if contentType == "application/json" {
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")
}
err := service.PostDadJoke(c.Db.DB, req)
err := h.PostDadJoke(req)
if err != nil {
stdmodels.FailureReponse("Failed to add joke", w, http.StatusInternalServerError, []string{err.Error()})
return
@@ -86,15 +99,15 @@ func (c *Controller) PostDadJoke(w http.ResponseWriter, r *http.Request) {
// @Accept json
// @Produce application/json
// @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"
//
// @Security Authorization
//
// @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")
var req model.DadJoke
var req DadJoke
if contentType == "application/json" {
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")
}
err := service.DeleteDadJoke(c.Db.DB, req)
err := h.DeleteDadJoke(req)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
stdmodels.FailureReponse("Failed to delete joke", w, http.StatusInternalServerError, []string{err.Error()})
return
}
message := model.Message{
Message: "OK",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(message)
stdmodels.SuccessResponse("OK", w, http.StatusOK)
}

View File

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

View File

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