admin-dashboard-backend/modules/equipment/equipment.go

300 lines
9.1 KiB
Go

package equipment
import (
"encoding/base64"
"encoding/json"
"jannex/admin-dashboard-backend/modules/config"
"jannex/admin-dashboard-backend/modules/database"
"jannex/admin-dashboard-backend/modules/logger"
"jannex/admin-dashboard-backend/modules/requestclient"
"jannex/admin-dashboard-backend/modules/structs"
"jannex/admin-dashboard-backend/modules/utils"
"os"
"strconv"
"strings"
"git.ex.umbach.dev/Alex/roese-utils/rspagination"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
type Notes struct {
Image string
Description string
}
func getImageType(data string) string {
switch data {
case "data:image/png;base64":
return "png"
default:
return "jpeg"
}
}
func CreateEquipmentDocumentation(c *fiber.Ctx, body structs.CreateEquipmentDocumentationRequest) error {
var bodyNotes []map[string]string
err := json.Unmarshal(body.Notes, &bodyNotes)
if err != nil {
log.Error().Msgf("Failed to unmarshal json, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
userId := c.Locals("userId").(string)
newEquipmentDocumentation := structs.EquipmentDocumentation{
Id: uuid.New().String(),
StockItemId: body.StockItemId,
Type: body.Type,
Title: body.Title,
CreatedByUserId: userId,
}
if err := os.Mkdir(config.Cfg.FolderPaths.PublicStatic+"/equipmentdocumentation/"+newEquipmentDocumentation.Id, os.ModePerm); err != nil {
log.Error().Msgf("Failed to create folder, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
var notes []Notes
// loop throught bodyNotes and save image to static folder under random name
for _, bodyNote := range bodyNotes {
var newFileName string
if bodyNote["Image"] != "" {
// image is sent as base64 string
// decode it and save it to the static folder
// encoded base64 image structure is: data:image/jpeg;base64,/9j/4AA...
splittedImageData := strings.Split(bodyNote["Image"], ",")
decodedImage, err := base64.StdEncoding.DecodeString(splittedImageData[1])
if err != nil {
log.Error().Msgf("Failed to decode base64 string, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
newFileName = uuid.New().String() + "." + getImageType(splittedImageData[0])
err = os.WriteFile(config.Cfg.FolderPaths.PublicStatic+"/equipmentdocumentation/"+newEquipmentDocumentation.Id+"/"+newFileName, decodedImage, 0644)
if err != nil {
log.Error().Msgf("Failed to save image, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
}
notes = append(notes, Notes{
Image: newFileName,
Description: bodyNote["Description"],
})
}
marshaledNotes, err := json.Marshal(notes)
if err != nil {
log.Error().Msgf("Failed to marshal notes, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
newEquipmentDocumentation.Notes = string(marshaledNotes)
database.DB.Create(&newEquipmentDocumentation)
logger.AddSystemLog("User %s has created the equipment document %s for the stock item %s with the title %s type %v description %s",
userId, newEquipmentDocumentation.Id, newEquipmentDocumentation.StockItemId, body.Title, body.Type, string(marshaledNotes))
return c.JSON(fiber.Map{"message": "ok"})
}
func GetEquipmentDocumentations(stockItemId string, query rspagination.PageQuery, c *fiber.Ctx) error {
var documentations []structs.EquipmentDocumentation
rspagination.DbPageQuery(database.DB, query,
utils.EquipmentDocumentationsPaginationLimit,
&documentations,
"created_at DESC",
"stock_item_id = ?",
stockItemId)
var err error
totalPages := 0
statusCode := 200
if len(documentations) == 0 {
// there are no documentations for this equipment on the our database
// so there will be checked on invex if the stock item exists
statusCode, _, err = requestclient.InvexApiRequestClient(fiber.MethodGet, config.Cfg.InvexAPI.Base+"/api/stock/"+stockItemId+"/")
if err != nil {
log.Error().Msgf("Invex api request error: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
} else {
totalPages = rspagination.GetTotalPages(database.DB, utils.EquipmentDocumentationsPaginationLimit,
&documentations,
"stock_item_id = ?",
stockItemId)
}
logger.AddSystemLog("User %s has viewed equipment documentation of the stock item %s. StatusCode: %s",
c.Locals("userId").(string), stockItemId, strconv.Itoa(statusCode))
return c.JSON(structs.EquipmentDocumentationResponse{
Status: statusCode,
Documentations: documentations,
TotalPages: totalPages})
}
func GetEquipmentDocumentation(stockItemId string, documentationId string, c *fiber.Ctx) error {
var documentation structs.EquipmentDocumentation
database.DB.Where("id = ? AND stock_item_id = ?", documentationId, stockItemId).Find(&documentation)
return c.JSON(documentation)
}
func EditEquipmentDocumentation(c *fiber.Ctx, body structs.EditEquipmentDocumentationRequest) error {
var bodyNotes []map[string]string
err := json.Unmarshal(body.Notes, &bodyNotes)
if err != nil {
log.Error().Msgf("Failed to unmarshal json, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
var notes []Notes
// loop throught bodyNotes and save image to static folder under random name
for _, bodyNote := range bodyNotes {
newFileName := bodyNote["Image"]
if bodyNote["Image"] != "" {
// check if image starts with base64 string
if strings.HasPrefix(bodyNote["Image"], "data:") {
// image is sent as base64 string
// decode it and save it to the static folder
// encoded base64 image structure is: data:image/jpeg;base64,/9j/4AA...
splittedImageData := strings.Split(bodyNote["Image"], ",")
decodedImage, err := base64.StdEncoding.DecodeString(splittedImageData[1])
if err != nil {
log.Error().Msgf("Failed to decode base64 string, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
newFileName = uuid.New().String() + "." + getImageType(splittedImageData[0])
err = os.WriteFile(config.Cfg.FolderPaths.PublicStatic+"/equipmentdocumentation/"+body.DocumentationId+"/"+newFileName, decodedImage, 0644)
if err != nil {
log.Error().Msgf("Failed to save image, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
}
}
notes = append(notes, Notes{
Image: newFileName,
Description: bodyNote["Description"],
})
}
// read all images on the folder and delete images that are not longer used in notes
files, err := os.ReadDir(config.Cfg.FolderPaths.PublicStatic + "/equipmentdocumentation/" + body.DocumentationId)
if err != nil {
log.Error().Msgf("Failed to read directory, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
for _, file := range files {
if file.IsDir() {
continue
}
if !isInList(file.Name(), notes) {
err := os.Remove(config.Cfg.FolderPaths.PublicStatic + "/equipmentdocumentation/" + body.DocumentationId + "/" + file.Name())
if err != nil {
log.Error().Msgf("Failed to remove file, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
}
}
// marshal notes to json
marshaledNotes, err := json.Marshal(notes)
if err != nil {
log.Error().Msgf("Failed to marshal notes, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
database.DB.Model(&structs.EquipmentDocumentation{}).Where("id = ?", body.DocumentationId).Updates(structs.EquipmentDocumentation{
Type: body.Type,
Title: body.Title,
Notes: string(marshaledNotes),
})
logger.AddSystemLog("User %s has updated the equipment document %s to title %s type %s and description %s",
c.Locals("userId").(string), body.DocumentationId, body.Title, strconv.Itoa(int(body.Type)), string(marshaledNotes))
return c.JSON(fiber.Map{"message": "ok"})
}
func isInList(fileName string, notes []Notes) bool {
for _, note := range notes {
if note.Image == fileName {
return true
}
}
return false
}
// fetching the thumbnail from the invex server and sending it back to the client
func GetEquipmentInvexThumbnail(c *fiber.Ctx, stockItemId string) error {
// first request to /api/stock/:stockItemId/ to get the thumbnail url
s, body, err := requestclient.InvexApiRequestClient(fiber.MethodGet, config.Cfg.InvexAPI.Base+"/api/stock/"+stockItemId+"/")
if err != nil {
log.Error().Msgf("Invex api request error: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
log.Info().Msgf("StatusCode: %d", s)
// parse body as json
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
log.Error().Msgf("Failed to unmarshal json, err: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
partDetail := data["part_detail"].(map[string]interface{})
thumbnail := partDetail["thumbnail"].(string)
// second request to /media/part_images/:thumbnail to get the thumbnail image
_, body, err = requestclient.InvexApiRequestClient(fiber.MethodGet, config.Cfg.InvexAPI.Base+"/"+thumbnail)
if err != nil {
log.Error().Msgf("Invex api request error: %s", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
c.Set("Content-Type", "image/png")
return c.Send(body)
}