mirror of
https://github.com/hazemKrimi/crimson-vault.git
synced 2026-05-01 18:20:27 +00:00
chore: use the echo framework instead of plain go net/http
This commit is contained in:
+2
-8
@@ -4,8 +4,6 @@ Copyright © 2025 Hazem Krimi me@hazemkrimi.tech
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/hazemKrimi/crimson-vault/internal/api"
|
||||
@@ -25,13 +23,9 @@ to quickly create a Cobra application.`,
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
apiWrapper := api.APIWrapper{}
|
||||
mux := http.NewServeMux()
|
||||
server := api.API{}
|
||||
|
||||
apiWrapper.Initialize()
|
||||
mux.Handle("/clients/", api.ClientRoutes(&apiWrapper))
|
||||
fmt.Println("Server listening on PORT 5000...")
|
||||
http.ListenAndServe(":5000", mux)
|
||||
server.Initialize()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ module github.com/hazemKrimi/crimson-vault
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/labstack/echo/v4 v4.13.4
|
||||
github.com/spf13/cobra v1.9.1
|
||||
gorm.io/driver/sqlite v1.5.7
|
||||
gorm.io/gorm v1.30.0
|
||||
@@ -12,7 +13,16 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
||||
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
|
||||
+21
-8
@@ -1,16 +1,29 @@
|
||||
package api
|
||||
|
||||
import "github.com/hazemKrimi/crimson-vault/internal/models"
|
||||
import (
|
||||
"github.com/hazemKrimi/crimson-vault/internal/models"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
type APIWrapper struct {
|
||||
dbWrapper *models.DBWrapper
|
||||
type API struct {
|
||||
instance *echo.Echo
|
||||
db *models.DB
|
||||
}
|
||||
|
||||
func (api *APIWrapper) Initialize() {
|
||||
wrapper := models.DBWrapper{}
|
||||
func (api *API) Initialize() {
|
||||
db := &models.DB{}
|
||||
ech := echo.New()
|
||||
|
||||
wrapper.Connect()
|
||||
wrapper.MigrateClients()
|
||||
db.Connect()
|
||||
db.MigrateClients()
|
||||
|
||||
api.dbWrapper = &wrapper;
|
||||
api.db = db
|
||||
api.instance = ech
|
||||
|
||||
api.ClientRoutes()
|
||||
api.instance.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||
AllowOrigins: []string{"*"},
|
||||
}))
|
||||
api.instance.Logger.Fatal(api.instance.Start(":5000"))
|
||||
}
|
||||
|
||||
+86
-34
@@ -1,60 +1,112 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/hazemKrimi/crimson-vault/internal/models"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func (api *APIWrapper) CreateClientHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
func (api *API) CreateClientHandler(context echo.Context) error {
|
||||
var body models.CreateClientBody
|
||||
|
||||
if err := json.NewDecoder(request.Body).Decode(&body); err != nil {
|
||||
http.Error(writer, "Invalid JSON", http.StatusBadRequest)
|
||||
if err := context.Bind(&body); err != nil {
|
||||
log.Println(fmt.Sprintf("Error creating Client: %v.", err))
|
||||
return
|
||||
return context.String(http.StatusBadRequest, "Invalid JSON!")
|
||||
}
|
||||
|
||||
client := api.dbWrapper.CreateClient(body)
|
||||
client := api.db.CreateClient(body)
|
||||
|
||||
log.Println(fmt.Sprintf("Client created with ID %d.", client.ID))
|
||||
json.NewEncoder(writer).Encode(client)
|
||||
return context.JSON(http.StatusOK, client)
|
||||
}
|
||||
|
||||
func (api *APIWrapper) GetClientsHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
idString := request.URL.Query().Get("id")
|
||||
|
||||
if idString != "" {
|
||||
id, err := strconv.Atoi(idString)
|
||||
|
||||
if err != nil {
|
||||
http.Error(writer, "Unexpected error getting Client.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var client models.Client
|
||||
|
||||
if err := api.dbWrapper.GetClient(id, &client); err != nil {
|
||||
http.Error(writer, "Client not found.", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Got client with ID %d.", client.ID))
|
||||
json.NewEncoder(writer).Encode(client)
|
||||
return
|
||||
}
|
||||
|
||||
clients, err := api.dbWrapper.GetClients()
|
||||
func (api *API) GetAllClientsHandler(context echo.Context) error {
|
||||
clients, err := api.db.GetClients()
|
||||
|
||||
if err != nil {
|
||||
http.Error(writer, "Unexpected error getting Clients.", http.StatusInternalServerError)
|
||||
return
|
||||
return context.String(http.StatusInternalServerError, "Unexpected error getting Clients!")
|
||||
}
|
||||
|
||||
log.Println("Got all Clients.")
|
||||
json.NewEncoder(writer).Encode(clients)
|
||||
return context.JSON(http.StatusOK, clients)
|
||||
}
|
||||
|
||||
func (api *API) GetClientHandler(context echo.Context) error {
|
||||
idString := context.Param("id")
|
||||
|
||||
if idString == "" {
|
||||
return context.String(http.StatusBadRequest, "ID is required to get a Client!")
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(idString)
|
||||
|
||||
if err != nil {
|
||||
return context.String(http.StatusInternalServerError, "Unexpected error getting Client!")
|
||||
}
|
||||
|
||||
var client models.Client
|
||||
|
||||
if err := api.db.GetClient(id, &client); err != nil {
|
||||
return context.String(http.StatusNotFound, "Client not found!")
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Got client with ID %d.", client.ID))
|
||||
return context.JSON(http.StatusOK, client)
|
||||
}
|
||||
|
||||
func (api *API) UpdateClientHandler(context echo.Context) error {
|
||||
idString := context.Param("id")
|
||||
|
||||
if idString == "" {
|
||||
return context.String(http.StatusBadRequest, "ID is required to update a Client!")
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(idString)
|
||||
|
||||
if err != nil {
|
||||
return context.String(http.StatusInternalServerError, "Unexpected error updating Client!")
|
||||
}
|
||||
|
||||
var body models.UpdateClientBody
|
||||
|
||||
if err := context.Bind(&body); err != nil {
|
||||
log.Println(fmt.Sprintf("Error updating Client: %v.", err))
|
||||
return context.String(http.StatusBadRequest, "Invalid JSON!")
|
||||
}
|
||||
|
||||
var client models.Client
|
||||
|
||||
if err := api.db.UpdateClient(id, body, &client); err != nil {
|
||||
return context.String(http.StatusNotFound, "Client not found!")
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Updated client with ID %d.", client.ID))
|
||||
return context.JSON(http.StatusOK, client)
|
||||
}
|
||||
|
||||
func (api *API) DeleteClientHandler(context echo.Context) error {
|
||||
idString := context.Param("id")
|
||||
|
||||
if idString == "" {
|
||||
return context.String(http.StatusBadRequest, "ID is required to delete a Client!")
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(idString)
|
||||
|
||||
if err != nil {
|
||||
return context.String(http.StatusInternalServerError, "Unexpected error deleting Client!")
|
||||
}
|
||||
|
||||
var client models.Client
|
||||
|
||||
if err := api.db.DeleteClient(id); err != nil {
|
||||
return context.String(http.StatusNotFound, "Client not found!")
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Deleted client with ID %d.", client.ID))
|
||||
return context.String(http.StatusOK, "Client deleted successfully!")
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package api
|
||||
|
||||
import "net/http"
|
||||
|
||||
func ClientRoutes(api *APIWrapper) (*http.ServeMux) {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("GET /", api.GetClientsHandler)
|
||||
mux.HandleFunc("POST /", api.CreateClientHandler)
|
||||
|
||||
return mux
|
||||
func (api *API) ClientRoutes() {
|
||||
group := api.instance.Group("/clients")
|
||||
|
||||
group.GET("/", api.GetAllClientsHandler)
|
||||
group.POST("/", api.CreateClientHandler)
|
||||
group.GET("/:id", api.GetClientHandler)
|
||||
group.PUT("/:id", api.UpdateClientHandler)
|
||||
group.DELETE("/:id", api.DeleteClientHandler)
|
||||
}
|
||||
|
||||
@@ -22,21 +22,27 @@ type CreateClientBody struct {
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
func (wrapper *DBWrapper) MigrateClients() {
|
||||
wrapper.db.AutoMigrate(&Client{})
|
||||
type UpdateClientBody struct {
|
||||
Name string `json:"name"`
|
||||
Country string `json:"country"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
func (wrapper *DBWrapper) CreateClient(body CreateClientBody) Client {
|
||||
func (db *DB) MigrateClients() {
|
||||
db.instance.AutoMigrate(&Client{})
|
||||
}
|
||||
|
||||
func (db *DB) CreateClient(body CreateClientBody) Client {
|
||||
client := Client{Name: body.Name, Country: body.Country, Phone: body.Phone}
|
||||
|
||||
wrapper.db.Create(&client)
|
||||
db.instance.Create(&client)
|
||||
return client
|
||||
}
|
||||
|
||||
func (wrapper *DBWrapper) GetClients() ([]Client, error) {
|
||||
func (db *DB) GetClients() ([]Client, error) {
|
||||
var clients []Client
|
||||
|
||||
result := wrapper.db.Find(&clients)
|
||||
result := db.instance.Find(&clients)
|
||||
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
@@ -45,8 +51,34 @@ func (wrapper *DBWrapper) GetClients() ([]Client, error) {
|
||||
return clients, nil
|
||||
}
|
||||
|
||||
func (wrapper *DBWrapper) GetClient(id int, client *Client) error {
|
||||
result := wrapper.db.Where("id = ?", id).First(&client, id)
|
||||
func (db *DB) GetClient(id int, client *Client) error {
|
||||
result := db.instance.Where("id = ?", id).First(&client, id)
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateClient(id int, body UpdateClientBody, client *Client) error {
|
||||
result := db.instance.Where("id = ?", id).First(&client, id)
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
result = db.instance.Model(&client).Updates(Client{Name: body.Name, Country: body.Country, Phone: body.Phone})
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) DeleteClient(id int) error {
|
||||
result := db.instance.Delete(&Client{}, id)
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
|
||||
@@ -7,16 +7,16 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type DBWrapper struct {
|
||||
db *gorm.DB
|
||||
type DB struct {
|
||||
instance *gorm.DB
|
||||
}
|
||||
|
||||
func (wrapper *DBWrapper) Connect() {
|
||||
func (wrapper *DB) Connect() {
|
||||
db, err := gorm.Open(sqlite.Open("crimson_vault.db"), &gorm.Config{})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
wrapper.db = db
|
||||
wrapper.instance = db
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user