No changes made

This commit is contained in:
2025-04-16 12:47:04 -04:00
commit 1ed3b0c2d4
98 changed files with 8857 additions and 0 deletions

View File

@@ -0,0 +1,292 @@
package bullets
import (
"context"
"encoding/json"
"git.s.int/reloading-manager/backend/database"
"git.s.int/reloading-manager/backend/handlers"
"git.s.int/reloading-manager/backend/models/bullets"
"github.com/labstack/echo/v4"
"net/http"
"strconv"
)
type BulletResponse struct {
Id string `json:"id"`
Name string `json:"name"`
Diameter int32 `json:"diameter"`
Weight int32 `json:"weight"`
Manufacturer handlers.Manufacturer `json:"manufacturer"`
}
func Photo(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
byId, err := db.Bullets.GetBulletById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
mime := http.DetectContentType(byId.Photo)
c.Response().Header().Set("Content-Type", mime)
c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
c.Response().Header().Set("Pragma", "no-cache")
c.Response().Header().Set("Expires", "0")
_, err = c.Response().Write(byId.Photo)
return err
}
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
err := db.Bullets.DeleteBullet(context.Background(), *uid)
if err != nil {
return err
}
return c.JSON(http.StatusOK, handlers.Response[string]{})
}
func Put(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
byId, err := db.Bullets.GetBulletById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
file, err := c.FormFile("photo")
var fileData []byte
if err == nil {
handler, err := file.Open()
if err != nil {
return err
}
buf := make([]byte, file.Size)
_, _ = handler.Read(buf)
fileData = buf
} else {
fileData = byId.Photo
}
var meta json.RawMessage = []byte(c.FormValue("meta"))
if len(meta) == 0 {
meta = []byte("{}")
}
weight, _ := strconv.ParseInt(c.FormValue("weight"), 10, 32)
diameter, _ := strconv.ParseInt(c.FormValue("diameter"), 10, 32)
manufacturerId := c.FormValue("manufacturer_id")
manufacturerUid := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if manufacturerUid == nil {
return nil
}
name := c.FormValue("name")
if name == "" {
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "Name is required.",
})
return nil
}
manufacturer, err := db.Manufacturer.GetById(context.Background(), *manufacturerUid)
if err != nil {
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "Invalid Manufacturer ID",
})
return nil
}
err = db.Bullets.UpdateBullet(context.Background(), bullets.UpdateBulletParams{
Name: name,
Weight: int32(weight),
Diameter: int32(diameter),
Photo: fileData,
Meta: meta,
ManufacturerID: manufacturer.ID,
ID: byId.ID,
})
if err != nil {
return err
}
return nil
}
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
byId, err := db.Bullets.GetBulletById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
response := handlers.Response[BulletResponse]{
Status: http.StatusText(http.StatusOK),
Payload: BulletResponse{
Id: uid.String(),
Name: byId.Name,
Diameter: byId.Diameter,
Weight: byId.Weight,
Manufacturer: handlers.Manufacturer{Id: byId.ManufactuererID.String(), Name: byId.ManufacutuerName, Url: byId.ManufacturerUrl.String},
},
}
return c.JSON(http.StatusOK, response)
}
bulletList, err := db.Bullets.ForPage(context.Background(), bullets.ForPageParams{
Limit: 100,
Offset: 0,
})
if bulletList == nil {
bulletList = make([]bullets.ForPageRow, 0)
}
var payload = make([]BulletResponse, 0)
for _, bullet := range bulletList {
payload = append(payload, BulletResponse{
Id: bullet.ID.String(),
Name: bullet.Name,
Weight: bullet.Weight,
Diameter: bullet.Diameter,
Manufacturer: handlers.Manufacturer{Id: bullet.ManufacturerID.String(), Name: bullet.ManufacturerName, Url: bullet.ManufacturerUrl.String},
})
}
if err != nil {
return err
}
err = c.JSON(http.StatusOK, handlers.Response[[]BulletResponse]{
Payload: payload,
Status: "OK",
})
if err != nil {
return err
}
return nil
}
func Post(c echo.Context) error {
file, err := c.FormFile("photo")
if err != nil {
c.Logger().Error(err)
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "No file provided",
})
return nil
}
handler, err := file.Open()
if err != nil {
return err
}
buf := make([]byte, file.Size)
_, _ = handler.Read(buf)
weight, _ := strconv.ParseInt(c.FormValue("weight"), 10, 32)
diameter, _ := strconv.ParseInt(c.FormValue("diameter"), 10, 32)
manufacturerId := c.FormValue("manufacturer_id")
name := c.FormValue("name")
if name == "" {
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "Name is required.",
})
return nil
}
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
manufacturerUid := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if manufacturerUid == nil {
return nil
}
manufacturer, err := db.Manufacturer.GetById(context.Background(), *manufacturerUid)
if err != nil {
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "Invalid Manufacturer ID",
})
return nil
}
var meta json.RawMessage = []byte(c.FormValue("meta"))
if len(meta) == 0 {
meta = []byte("{}")
}
id, err := db.Bullets.AddBullet(context.Background(), bullets.AddBulletParams{
Name: name,
Weight: int32(weight),
Diameter: int32(diameter),
Photo: buf,
Meta: meta,
ManufacturerID: manufacturer.ID,
})
if err != nil {
return err
}
err = c.JSON(http.StatusAccepted, handlers.Response[string]{
Status: http.StatusText(http.StatusAccepted),
Payload: id.String(),
})
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,96 @@
package cartridge
import (
"context"
"encoding/json"
"git.s.int/reloading-manager/backend/database"
"git.s.int/reloading-manager/backend/handlers"
"git.s.int/reloading-manager/backend/models/loads"
"github.com/labstack/echo/v4"
"net/http"
)
type response struct {
Id string `json:"id"`
Name string `json:"name"`
}
type postRequest struct {
Name string `json:"name"`
Meta string `json:"meta"`
}
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
cartridges, err := db.Loads.GetCartridges(context.Background())
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
var res = make([]response, 0)
for _, cartridge := range cartridges {
res = append(res, response{
Id: cartridge.ID.String(),
Name: cartridge.Name,
})
}
return c.JSON(http.StatusOK, handlers.Response[[]response]{
Payload: res,
Status: http.StatusText(http.StatusOK),
})
}
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
req := postRequest{}
err := c.Bind(&req)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
if req.Name == "" {
return echo.NewHTTPError(http.StatusBadRequest, "name is required")
}
var meta json.RawMessage
if req.Meta != "" {
meta = []byte(req.Meta)
} else {
meta = []byte("{}")
}
retId, err := db.Loads.CreateCartridge(context.Background(), loads.CreateCartridgeParams{
Name: req.Name,
Meta: meta,
})
if err != nil {
return handlers.BadRequest(c, "already exists")
}
return c.JSON(http.StatusOK, map[string]string{"id": retId.String()})
}
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
uid := handlers.ParseUuidOrBadRequest(c, c.Param("id"))
if uid == nil {
return nil
}
err := db.Loads.DeleteCartridge(context.Background(), *uid)
if err != nil {
return err
}
return c.NoContent(http.StatusOK)
}

29
backend/handlers/file.go Normal file
View File

@@ -0,0 +1,29 @@
package handlers
import (
"github.com/labstack/echo/v4"
"net/http"
)
func ReadFile(c echo.Context, formName string) ([]byte, error) {
file, err := c.FormFile(formName)
if err != nil {
c.Logger().Error(err)
_ = c.JSON(http.StatusBadRequest, struct {
Message string `json:"message"`
}{
Message: "No file provided",
})
return nil, err
}
handler, err := file.Open()
if err != nil {
return nil, err
}
buf := make([]byte, file.Size)
_, _ = handler.Read(buf)
return buf, nil
}

View File

@@ -0,0 +1,215 @@
package loads
import (
"context"
"git.s.int/reloading-manager/backend/database"
"git.s.int/reloading-manager/backend/handlers"
"git.s.int/reloading-manager/backend/handlers/bullets"
"git.s.int/reloading-manager/backend/handlers/primers"
"git.s.int/reloading-manager/backend/models/loads"
"github.com/labstack/echo/v4"
"net/http"
)
type loadResponseResults struct {
Id string `json:"id"`
Cartridge string `json:"cartridge"`
Col float32 `json:"col"`
Powder handlers.Powder `json:"powder"`
PowderGr float32 `json:"powder_gr"`
Primer primers.PrimerResponse `json:"primer"`
Bullet bullets.BulletResponse `json:"bullet"`
}
type loadResponse struct {
Total int `json:"total"`
Results []loadResponseResults `json:"results"`
}
type ResultChan[T any] struct {
Result T
Err error
}
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
cartridgeID, err := handlers.ParseUuid(c.FormValue("cartridge_id"))
if err != nil {
return handlers.BadRequest(c, "cartridge_id is not a valid UUID")
}
powderId, err := handlers.ParseUuid(c.FormValue("powder_id"))
if err != nil {
return handlers.BadRequest(c, "powder_id is not a valid UUID")
}
primerId, err := handlers.ParseUuid(c.FormValue("primer_id"))
if err != nil {
return handlers.BadRequest(c, "primer_id is not a valid UUID")
}
bulletId, err := handlers.ParseUuid(c.FormValue("bullet_id"))
if err != nil {
return handlers.BadRequest(c, "bullet_id is not a valid UUID")
}
file, err := handlers.ReadFile(c, "photo")
if err != nil {
return handlers.BadRequest(c, "photo is not valid")
}
meta := c.FormValue("meta")
if meta == "" {
meta = "{}"
}
powderGr := c.FormValue("powder_gr")
if powderGr == "" {
return handlers.BadRequest(c, "powder_gr is not valid")
}
powderGrFl, err := handlers.ParseFloat32(powderGr)
if err != nil {
return handlers.BadRequest(c, "powder_gr is not valid")
}
col := c.FormValue("col")
if col == "" {
return handlers.BadRequest(c, "col is not valid")
}
colFl, err := handlers.ParseFloat32(col)
if err != nil {
return handlers.BadRequest(c, "col is not valid")
}
uid, err := db.Loads.CreateLoad(context.Background(), loads.CreateLoadParams{
CartridgeID: *cartridgeID,
Col: colFl,
PowderID: *powderId,
PowderGr: powderGrFl,
PrimerID: *primerId,
BulletID: *bulletId,
Photo: file,
Meta: []byte(meta),
})
if err != nil {
return err
}
return c.JSON(http.StatusCreated, handlers.Response[string]{Payload: uid.String()})
}
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
cTotal := make(chan ResultChan[int64])
cResults := make(chan ResultChan[[]loadResponseResults])
go func(c chan ResultChan[int64]) {
db := database.GetNewDatabase()
defer db.Db.Close(context.Background())
total, err := db.Loads.TotalLoads(context.Background())
if err != nil {
c <- ResultChan[int64]{Err: err}
} else {
c <- ResultChan[int64]{Result: total}
}
}(cTotal)
go func(ch chan ResultChan[[]loadResponseResults]) {
rows, err := db.Loads.GetLoads(context.Background(), loads.GetLoadsParams{
Limit: handlers.ParseInt32WithDefault(c.QueryParam("limit"), 50),
Offset: handlers.ParseInt32WithDefault(c.QueryParam("offset"), 0),
OrderBy: "cartridge_name asc",
SearchCartridge: c.QueryParam("search_cartridge") != "",
Cartridge: c.QueryParam("search_cartridge"),
SearchBullet: c.QueryParam("search_bullet") != "",
Bullet: c.QueryParam("search_bullet") + "%",
SearchBulletManufacturer: c.QueryParam("search_bullet_manufacturer") != "",
BulletManufacturer: c.QueryParam("search_bullet_manufacturer") + "%",
SearchPrimer: c.QueryParam("search_primer") != "",
Primer: c.QueryParam("search_primer") + "%",
SearchPrimerManufacturer: c.QueryParam("search_primer_manufacturer") != "",
PrimeManufacturer: c.QueryParam("search_primer_manufacturer") + "%",
SearchPowder: c.QueryParam("search_powder") != "",
Powder: c.QueryParam("search_powder") + "%",
SearchPowderManufacturer: c.QueryParam("search_powder_manufacturer") != "",
PowderManufacturer: c.QueryParam("search_powder_manufacturer") + "%",
SearchCartridgeID: c.QueryParam("search_cartridge_id") != "",
CartridgeID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_cartridge_id")),
SearchBulletID: c.QueryParam("search_bullet_id") != "",
BulletID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_bullet_id")),
SearchPrimerID: c.QueryParam("search_primer_id") != "",
PrimerID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_primer_id")),
SearchPowderID: c.QueryParam("search_powder_id") != "",
PowderID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_powder_id")),
})
if err != nil {
ch <- ResultChan[[]loadResponseResults]{Err: err}
return
}
results := make([]loadResponseResults, 0)
for _, row := range rows {
results = append(results, loadResponseResults{
Id: row.ID.String(),
Cartridge: row.CartridgeName,
Col: row.Col,
Powder: handlers.Powder{
Id: row.PowderID.String(),
Name: row.PowderName,
Meta: string(row.PowderMeta),
Manufacturer: handlers.Manufacturer{
Name: row.PowderManufacturerName,
Url: row.PowderManufacturerUrl.String,
},
},
PowderGr: row.PowderGr,
Primer: primers.PrimerResponse{
ID: row.PrimerID.String(),
Name: row.PrimerName,
Manufacturer: handlers.Manufacturer{
Name: row.PrimerManufacturerName,
Url: row.PrimerManufacturerUrl.String,
},
},
Bullet: bullets.BulletResponse{
Id: row.BulletID.String(),
Name: row.BulletName,
Diameter: row.BulletDiameter,
Weight: row.BulletWeight,
Manufacturer: handlers.Manufacturer{
Name: row.BulletManufacturerName,
Url: row.BulletManufacturerUrl.String,
},
},
})
}
ch <- ResultChan[[]loadResponseResults]{Result: results}
}(cResults)
total := <-cTotal
if total.Err != nil {
return total.Err
}
results := <-cResults
if results.Err != nil {
return results.Err
}
return c.JSON(http.StatusOK, handlers.Response[loadResponse]{Status: http.StatusText(http.StatusOK), Payload: loadResponse{Total: int(total.Result), Results: results.Result}})
}

View File

@@ -0,0 +1,167 @@
package manufacturer
import (
"context"
"encoding/json"
"git.s.int/reloading-manager/backend/database"
"git.s.int/reloading-manager/backend/handlers"
"git.s.int/reloading-manager/backend/models/manufacturer"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"net/http"
)
type manufacturerResponse struct {
Id string `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
CreatedAt string `json:"created_at"`
}
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
byId, err := db.Manufacturer.GetById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
response := handlers.Response[manufacturerResponse]{
Status: http.StatusText(http.StatusOK),
Payload: manufacturerResponse{
Id: byId.ID.String(),
Name: byId.Name,
Url: byId.Url.String,
CreatedAt: byId.CreatedAt.Time.Format(handlers.DateFormat),
},
}
return c.JSON(http.StatusOK, response)
}
all, err := db.Manufacturer.GetAll(context.Background())
if err != nil {
return err
}
var payload []manufacturerResponse
for _, m := range all {
payload = append(payload, manufacturerResponse{
Id: m.ID.String(),
Name: m.Name,
Url: m.Url.String,
CreatedAt: m.CreatedAt.Time.Format(handlers.DateFormat),
})
}
response := handlers.Response[[]manufacturerResponse]{
Status: http.StatusText(http.StatusOK),
Payload: payload,
}
return c.JSON(http.StatusOK, response)
}
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
_, err := db.Manufacturer.GetById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
err = db.Manufacturer.DeleteById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Cannot delete manufacturer")
}
return c.NoContent(http.StatusNoContent)
}
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
_, err := db.Manufacturer.GetById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
jsonMap := make(map[string]interface{})
_ = json.NewDecoder(c.Request().Body).Decode(&jsonMap)
if jsonMap["name"] == "" {
return handlers.BadRequest(c, "Name is required.")
}
err = db.Manufacturer.UpdateManufacturer(context.Background(), manufacturer.UpdateManufacturerParams{
Name: jsonMap["name"].(string),
Url: pgtype.Text{
String: jsonMap["url"].(string),
Valid: true,
},
ID: *uid,
})
if err != nil {
return err
}
return nil
}
newUuid := uuid.New().String()
uid, _ := handlers.ParseUuid(newUuid)
jsonMap := make(map[string]interface{})
_ = json.NewDecoder(c.Request().Body).Decode(&jsonMap)
if jsonMap["name"] == nil || jsonMap["name"] == "" {
return handlers.BadRequest(c, "Name is required.")
}
var url string
if jsonMap["url"] != nil {
url = jsonMap["url"].(string)
}
err := db.Manufacturer.CreateManufacturer(context.Background(), manufacturer.CreateManufacturerParams{
ID: *uid,
Name: jsonMap["name"].(string),
Url: pgtype.Text{
String: url,
Valid: true,
},
})
if err != nil {
return err
}
return c.JSON(http.StatusAccepted, handlers.Response[string]{
Status: http.StatusText(http.StatusAccepted),
Payload: newUuid,
})
}

View File

@@ -0,0 +1,21 @@
package handlers
import "strconv"
func ParseFloat32(v string) (float32, error) {
fl, err := strconv.ParseFloat(v, 32)
if err != nil {
return 0, err
}
return float32(fl), nil
}
func ParseInt32WithDefault(s string, def int32) int32 {
sInt, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return def
}
return int32(sInt)
}

View File

@@ -0,0 +1,217 @@
package powder
import (
"context"
"encoding/json"
"git.s.int/reloading-manager/backend/database"
"git.s.int/reloading-manager/backend/handlers"
"git.s.int/reloading-manager/backend/models/powder"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"net/http"
)
func Photo(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "Invalid UUID")
}
byId, err := db.Powder.GetPowderById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
mime := http.DetectContentType(byId.Photo)
c.Response().Header().Set("Content-Type", mime)
c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
c.Response().Header().Set("Pragma", "no-cache")
c.Response().Header().Set("Expires", "0")
_, err = c.Response().Write(byId.Photo)
return err
}
func Delete(c echo.Context) error {
id := c.Param("id")
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
uid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "Invalid UUID")
}
err = db.Powder.DeletePowder(context.Background(), *uid)
if err != nil {
return err
}
return c.JSON(http.StatusOK, handlers.Response[string]{})
}
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "Invalid UUID")
}
p, err := db.Powder.GetPowderById(context.Background(), *uid)
if err != nil {
return handlers.NotFound(c, "Not found")
}
file, _ := c.FormFile("photo")
if file != nil {
handler, _ := file.Open()
buf := make([]byte, file.Size)
_, _ = handler.Read(buf)
p.Photo = buf
}
if c.FormValue("name") != "" {
p.Name = c.FormValue("name")
}
muid, err := handlers.ParseUuid(c.FormValue("manufacturer_id"))
if err != nil {
return handlers.BadRequest(c, "Invalid UUID")
}
if c.FormValue("manufacturer_id") != "" {
p.ManufacturerID = *muid
}
metaString := c.FormValue("meta")
if metaString == "" {
metaString = string(p.Meta)
}
err = db.Powder.UpdatePowder(context.Background(), powder.UpdatePowderParams{
ID: p.ID,
Name: p.Name,
ManufacturerID: p.ManufacturerID,
Photo: p.Photo,
Meta: []byte(metaString),
})
if err != nil {
return err
}
return c.JSON(http.StatusOK, handlers.Response[string]{})
}
buf, err := handlers.ReadFile(c, "photo")
if err != nil {
return err
}
newUuid := uuid.New().String()
pgUuid, _ := handlers.ParseUuid(newUuid)
mfUid, _ := handlers.ParseUuid(c.FormValue("manufacturer_id"))
jsonMap := make(map[string]interface{})
jsonMap["name"] = c.FormValue("name")
if jsonMap["name"] == nil || jsonMap["name"] == "" {
return handlers.BadRequest(c, "Name is required.")
}
jsonMap["manufacturer_id"] = c.FormValue("manufacturer_id")
if jsonMap["manufacturer_id"] == nil || jsonMap["manufacturer_id"] == "" {
return handlers.BadRequest(c, "manufacturer_id is required.")
}
metaString := c.FormValue("meta")
if metaString == "" {
metaString = "{}"
}
var meta json.RawMessage = []byte(metaString)
err = db.Powder.InsertPowder(context.Background(), powder.InsertPowderParams{
ID: *pgUuid,
Name: jsonMap["name"].(string),
ManufacturerID: *mfUid,
Meta: meta,
Photo: buf,
})
if err != nil {
return err
}
return c.JSON(http.StatusCreated, handlers.Response[struct {
Id string `json:"id"`
}]{
Status: http.StatusText(http.StatusCreated),
Payload: struct {
Id string `json:"id"`
}{Id: newUuid},
})
}
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "Invalid UUID")
}
p, err := db.Powder.GetPowderById(context.Background(), *uid)
if err != nil {
return handlers.BadRequest(c, "Not found")
}
response := handlers.Response[handlers.Powder]{
Payload: handlers.Powder{
Id: uid.String(),
Name: p.Name,
Manufacturer: handlers.Manufacturer{Id: p.ManufacturerID.String(), Name: p.ManufacturerName, Url: p.ManufacturerUrl.String},
},
}
response.Status = http.StatusText(http.StatusOK)
return c.JSON(http.StatusOK, response)
}
powders, err := db.Powder.GetPowders(context.Background())
if err != nil {
return err
}
response := handlers.Response[[]handlers.Powder]{
Payload: make([]handlers.Powder, 0),
}
for _, p := range powders {
response.Payload = append(response.Payload, handlers.Powder{
Id: p.ID.String(),
Name: p.Name,
Manufacturer: handlers.Manufacturer{
Id: p.ManufacturerID.String(),
Name: p.ManufacturerName,
Url: p.ManufacturerUrl.String,
},
})
}
response.Status = http.StatusText(http.StatusOK)
return c.JSON(200, response)
}

View File

@@ -0,0 +1,226 @@
package primers
import (
"context"
"encoding/json"
"git.s.int/reloading-manager/backend/database"
"git.s.int/reloading-manager/backend/handlers"
"git.s.int/reloading-manager/backend/models/primers"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"net/http"
)
type PrimerResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Manufacturer handlers.Manufacturer `json:"manufacturer"`
}
func Delete(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid, err := handlers.ParseUuid(id)
if err != nil {
return handlers.BadRequest(c, "Invalid UUID.")
}
err = db.Primer.DeletePrimer(context.Background(), *uid)
if err != nil {
return err
}
return c.JSON(http.StatusNoContent, handlers.Response[string]{})
}
func Post(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
p, err := db.Primer.GetPrimerById(context.Background(), *uid)
if err != nil {
return handlers.NotFound(c, "Primer not found.")
}
if c.FormValue("name") != "" {
p.Name = c.FormValue("name")
}
if c.FormValue("manufacturer_id") != "" {
mid := handlers.ParseUuidOrBadRequest(c, c.FormValue("manufacturer_id"))
if mid == nil {
return nil
}
p.ManufacturerID = *mid
}
if c.FormValue("meta") != "" {
p.Meta = json.RawMessage(c.Param("meta"))
}
file, _ := c.FormFile("photo")
if file != nil {
photo, err := handlers.ReadFile(c, "photo")
if err != nil {
return err
}
p.Photo = photo
}
err = db.Primer.UpdatePrimer(context.Background(), primers.UpdatePrimerParams{
ID: p.ID,
Name: p.Name,
ManufacturerID: p.ManufacturerID,
Meta: p.Meta,
Photo: p.Photo,
})
if err != nil {
return err
}
return c.JSON(http.StatusOK, handlers.Response[string]{Payload: p.ID.String(), Status: http.StatusText(http.StatusOK)})
}
buf, err := handlers.ReadFile(c, "photo")
if err != nil {
return err
}
name := c.FormValue("name")
manufacturerId := c.FormValue("manufacturer_id")
metaString := c.FormValue("meta")
if name == "" {
return handlers.BadRequest(c, "Name is required.")
}
if manufacturerId == "" {
return handlers.BadRequest(c, "Manufacturer ID is required.")
}
if metaString == "" {
metaString = "{}"
}
var meta json.RawMessage = []byte(metaString)
newUuid := uuid.New().String()
uid, _ := handlers.ParseUuid(newUuid)
mid := handlers.ParseUuidOrBadRequest(c, manufacturerId)
if mid == nil {
return nil
}
err = db.Primer.InsertPrimer(context.Background(), primers.InsertPrimerParams{
ID: *uid,
Name: name,
ManufacturerID: *mid,
Meta: meta,
Photo: buf,
})
if err != nil {
return err
}
return c.JSON(http.StatusCreated, handlers.Response[struct {
Id string `json:"id"`
}]{
Status: http.StatusText(http.StatusCreated),
Payload: struct {
Id string `json:"id"`
}{Id: newUuid},
})
}
func Photo(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
byId, err := db.Primer.GetPrimerById(context.Background(), *uid)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound, "Not found")
}
mime := http.DetectContentType(byId.Photo)
c.Response().Header().Set("Content-Type", mime)
c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
c.Response().Header().Set("Pragma", "no-cache")
c.Response().Header().Set("Expires", "0")
_, err = c.Response().Write(byId.Photo)
return err
}
func Get(c echo.Context) error {
db := c.(*database.CustomContext).Db
defer db.Db.Close(context.Background())
if c.Param("id") != "" {
id := c.Param("id")
uid := handlers.ParseUuidOrBadRequest(c, id)
if uid == nil {
return nil
}
row, err := db.Primer.GetPrimerById(context.Background(), *uid)
if err != nil {
return err
}
return c.JSON(http.StatusOK, handlers.Response[PrimerResponse]{
Status: http.StatusText(http.StatusOK),
Payload: PrimerResponse{
ID: row.ID.String(),
Name: row.Name,
Manufacturer: handlers.Manufacturer{
Id: row.ManufacturerID.String(),
Name: row.ManufacturerName,
Url: row.ManufacturerUrl.String,
},
},
})
}
rows, err := db.Primer.GetPrimers(context.Background())
if err != nil {
return err
}
response := handlers.Response[[]PrimerResponse]{
Status: "OK",
Payload: make([]PrimerResponse, 0),
}
for _, row := range rows {
response.Payload = append(response.Payload, PrimerResponse{
ID: row.ID.String(),
Name: row.Name,
Manufacturer: handlers.Manufacturer{
Id: row.ManufacturerID.String(),
Name: row.ManufacturerName,
Url: row.ManufacturerUrl.String,
},
})
}
return c.JSON(http.StatusOK, response)
}

View File

@@ -0,0 +1,42 @@
package handlers
import (
"github.com/labstack/echo/v4"
"net/http"
)
const DateFormat = "2006-01-02 15:04:05"
type Response[T any] struct {
Status string `json:"status"`
Message string `json:"message,omitempty"`
Payload T `json:"payload"`
}
func BadRequest(c echo.Context, message string) error {
return c.JSON(http.StatusBadRequest, Response[string]{
Status: http.StatusText(http.StatusBadRequest),
Message: message,
})
}
func NotFound(c echo.Context, message string) error {
return c.JSON(http.StatusNotFound, Response[string]{
Status: http.StatusText(http.StatusNotFound),
Message: message,
})
}
type Manufacturer struct {
Id string `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
}
type Powder struct {
Id string `json:"id"`
Name string `json:"name"`
Meta string `json:"meta"`
Photo string `json:"photo,omitempty"`
Manufacturer Manufacturer `json:"manufacturer"`
}

View File

@@ -0,0 +1,38 @@
package handlers
import (
"github.com/labstack/echo/v4"
"testing"
)
type TestContext struct {
echo.Context
t *testing.T
}
func (t TestContext) JSON(code int, i interface{}) error {
if code != 400 {
t.t.Fatal("expected 400")
}
switch i.(type) {
case Response[string]:
break
default:
t.t.Fatal("expected response")
}
return nil
}
func TestBadRequest(t *testing.T) {
context := TestContext{
t: t,
}
err := BadRequest(context, "Something is wrong")
if err != nil {
t.Errorf("Should not have returned an error")
}
}

45
backend/handlers/uuid.go Normal file
View File

@@ -0,0 +1,45 @@
package handlers
import (
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
)
func ParseUuidOrEmpty(s string) *pgtype.UUID {
ParsedUuid, err := uuid.Parse(s)
if err != nil {
return &pgtype.UUID{
Valid: false,
}
}
return &pgtype.UUID{
Bytes: ParsedUuid,
Valid: true,
}
}
func ParseUuid(s string) (*pgtype.UUID, error) {
uid, err := uuid.Parse(s)
if err != nil {
return nil, err
}
return &pgtype.UUID{
Bytes: uid,
Valid: true,
}, nil
}
func ParseUuidOrBadRequest(c echo.Context, s string) *pgtype.UUID {
uid, err := ParseUuid(s)
if err != nil {
_ = BadRequest(c, "Invalid UUID.")
return nil
}
return uid
}