From 790c8c969a464ee7e0bd8ce82484582818237d6c Mon Sep 17 00:00:00 2001 From: DeveloperDurp Date: Sun, 16 Jun 2024 18:21:06 -0500 Subject: [PATCH] rewrite to using new method --- Makefile | 4 +- controller/controller.go | 108 +++++++++++- controller/openai.go | 154 ------------------ docs/docs.go | 39 +++-- docs/swagger.json | 39 +++-- docs/swagger.yaml | 33 ++-- main.go | 31 +--- middleware/auth.go | 17 -- middleware/headers.go | 12 ++ middleware/logging.go | 18 +- model/admin.go | 26 --- model/jokes.go | 5 - model/messages.go | 5 - .../dadjoke.go => services/dadjoke/handler.go | 57 ++++--- .../dadjoke.go => services/dadjoke/service.go | 30 ++-- .../health.go => services/health/handler.go | 16 +- services/openai/handler.go | 103 ++++++++++++ services/openai/service.go | 50 ++++++ storage/postgres.go | 36 ---- 19 files changed, 388 insertions(+), 395 deletions(-) delete mode 100644 controller/openai.go delete mode 100644 middleware/auth.go create mode 100644 middleware/headers.go delete mode 100644 model/admin.go delete mode 100644 model/jokes.go delete mode 100644 model/messages.go rename controller/dadjoke.go => services/dadjoke/handler.go (68%) rename service/dadjoke.go => services/dadjoke/service.go (50%) rename controller/health.go => services/health/handler.go (55%) create mode 100644 services/openai/handler.go create mode 100644 services/openai/service.go delete mode 100644 storage/postgres.go diff --git a/Makefile b/Makefile index 90316b2..3646f68 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/controller/controller.go b/controller/controller.go index 297cbbb..a42fe8b 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -3,18 +3,42 @@ 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/model" - "gitlab.com/DeveloperDurp/DurpAPI/storage" + "gitlab.com/DeveloperDurp/DurpAPI/middleware" + "gitlab.com/DeveloperDurp/DurpAPI/services/dadjoke" + "gitlab.com/DeveloperDurp/DurpAPI/services/health" + "gitlab.com/DeveloperDurp/DurpAPI/services/openai" + "gitlab.com/developerdurp/stdmodels" ) type Controller struct { - Cfg model.Config - Dbcfg model.DBConfig - Db model.Repository + 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 { @@ -24,8 +48,8 @@ func NewController() *Controller { } controller := &Controller{ - Cfg: model.Config{}, - Dbcfg: model.DBConfig{}, + Cfg: Config{}, + Dbcfg: DBConfig{}, } err = env.Parse(&controller.Cfg) @@ -37,11 +61,77 @@ func NewController() *Controller { log.Fatalf("unable to parse database variables: %e", err) } - Db, err := storage.Connect(controller.Dbcfg) + Db, err := connectDB(controller.Dbcfg) if err != nil { panic("Failed to connect to database") } - controller.Db = *Db + 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 /api/health/gethealth", health.Get) + + dadjoke, err := dadjoke.NewHandler(c.Db) + router.HandleFunc("GET /api/jokes/dadjoke", dadjoke.Get) + router.HandleFunc("POST /api/jokes/dadjoke", dadjoke.Post) + router.HandleFunc("DELETE /api/jokes/dadjoke", dadjoke.Delete) + + openai, err := openai.NewHandler(c.Cfg.LlamaURL) + router.HandleFunc("GET /api/openai/general", openai.GeneralOpenAI) + router.HandleFunc("GET /api/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 +} diff --git a/controller/openai.go b/controller/openai.go deleted file mode 100644 index d49d2fb..0000000 --- a/controller/openai.go +++ /dev/null @@ -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 -} diff --git a/docs/docs.go b/docs/docs.go index 0647725..40900eb 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -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": { diff --git a/docs/swagger.json b/docs/swagger.json index c14db5e..71c3fca 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -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": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c436f7a..160c7d5 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -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 diff --git a/main.go b/main.go index bec9016..8c71aaa 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,10 @@ package main import ( - "fmt" - "net/http" - - "github.com/swaggo/http-swagger" + "log" "gitlab.com/DeveloperDurp/DurpAPI/controller" "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() } diff --git a/middleware/auth.go b/middleware/auth.go deleted file mode 100644 index 67f9687..0000000 --- a/middleware/auth.go +++ /dev/null @@ -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) - }) -} diff --git a/middleware/headers.go b/middleware/headers.go new file mode 100644 index 0000000..a70a649 --- /dev/null +++ b/middleware/headers.go @@ -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) + }) +} diff --git a/middleware/logging.go b/middleware/logging.go index 5d62409..ae1aefe 100644 --- a/middleware/logging.go +++ b/middleware/logging.go @@ -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)) }) } diff --git a/model/admin.go b/model/admin.go deleted file mode 100644 index cd32b96..0000000 --- a/model/admin.go +++ /dev/null @@ -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 -} diff --git a/model/jokes.go b/model/jokes.go deleted file mode 100644 index 81b36da..0000000 --- a/model/jokes.go +++ /dev/null @@ -1,5 +0,0 @@ -package model - -type DadJoke struct { - JOKE string `json:"joke"` -} diff --git a/model/messages.go b/model/messages.go deleted file mode 100644 index 98030bc..0000000 --- a/model/messages.go +++ /dev/null @@ -1,5 +0,0 @@ -package model - -type Message struct { - Message string `json:"message" example:"message"` -} diff --git a/controller/dadjoke.go b/services/dadjoke/handler.go similarity index 68% rename from controller/dadjoke.go rename to services/dadjoke/handler.go index 88ad860..083b22b 100644 --- a/controller/dadjoke.go +++ b/services/dadjoke/handler.go @@ -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) } diff --git a/service/dadjoke.go b/services/dadjoke/service.go similarity index 50% rename from service/dadjoke.go rename to services/dadjoke/service.go index 1da79d1..f07006e 100644 --- a/service/dadjoke.go +++ b/services/dadjoke/service.go @@ -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 } diff --git a/controller/health.go b/services/health/handler.go similarity index 55% rename from controller/health.go rename to services/health/handler.go index 7566a5d..54ee8c6 100644 --- a/controller/health.go +++ b/services/health/handler.go @@ -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) } diff --git a/services/openai/handler.go b/services/openai/handler.go new file mode 100644 index 0000000..82ffb47 --- /dev/null +++ b/services/openai/handler.go @@ -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) +} diff --git a/services/openai/service.go b/services/openai/service.go new file mode 100644 index 0000000..7c89341 --- /dev/null +++ b/services/openai/service.go @@ -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 +} diff --git a/storage/postgres.go b/storage/postgres.go deleted file mode 100644 index 5a3f43c..0000000 --- a/storage/postgres.go +++ /dev/null @@ -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 -}