// package mod_http -- http-движок для архитектуры
package engine_http
import (
"bytes"
_ "embed"
"fmt"
"time"
svg "github.com/ajstarks/svgo"
"github.com/gofiber/fiber/v2"
"gitp78su.ipnodns.ru/svi/kern/v3"
"gitp78su.ipnodns.ru/svi/kern/v3/kc/log_buf"
"gitp78su.ipnodns.ru/svi/kern/v3/krn/ktypes"
"gopkg.in/yaml.v3"
"gitp78su.ipnodns.ru/svi/goarch/lev0/alias"
"gitp78su.ipnodns.ru/svi/goarch/lev0/cons"
"gitp78su.ipnodns.ru/svi/goarch/lev0/types"
"gitp78su.ipnodns.ru/svi/goarch/lev1/coord"
"gitp78su.ipnodns.ru/svi/goarch/lev1/size"
"gitp78su.ipnodns.ru/svi/goarch/pkg/elems/canvas"
)
//go:embed index.html
var indexHtml []byte
// CanvasSize -- размер хоста
type CanvasSize struct {
SizeX_ int `form:"w"`
SizeY_ int `form:"h"`
}
func (sf *CanvasSize) String() string {
return fmt.Sprintf("w:%v; h:%v", sf.SizeX_, sf.SizeY_)
}
// EngineHttp -- http-движок для архитектуры
type EngineHttp struct {
// fibApp *fiber.App
kCtx ktypes.IKernelCtx
log ktypes.ILogBuf
diaMode string // Режим диаграммы
oldBinArch []byte // Кеш старой архитектуры
canvSize CanvasSize // Размер холста
size types.IViewSize // Размер холста в его координатах
pos types.IViewCoord // Координаты холста
mapDrawer types.IMapDrawer // Словарь отрисовщиков
}
// NewEngineHttp -- возвращает новый движок HTTP
func NewEngineHttp() *EngineHttp {
optTerm := log_buf.OptIsTerm(true)
optPref := log_buf.OptPrefix("EngineHttp")
log := log_buf.NewLogBuf(optTerm, optPref)
sf := &EngineHttp{
kCtx: kern.GetKernelCtx(),
log: log,
diaMode: cons.ModeUseCase,
size: size.NewSize(800, 600),
pos: coord.NewCoord(0, 0),
}
sHttp := kern.GetKernelServerHttp()
fibApp := sHttp.Fiber()
fibApp.Static("/static", "./static", fiber.Static{
Compress: true,
Browse: true,
CacheDuration: time.Second * 10,
})
fibApp.Get("/", sf.getIndex)
fibApp.Get("/api/use_case", sf.postSetModeUseCase)
fibApp.Post("/api/arch", sf.postArch)
fibApp.Post("/api/canvas_size", sf.postSetCanvasSize)
return sf
}
// FormArch -- извлекает данные из текстового поля для обновления
type FormArch struct {
StrData string `form:"text"`
}
// Устанавливает размер холста
func (sf *EngineHttp) postSetCanvasSize(ctx *fiber.Ctx) error {
err := ctx.BodyParser(&sf.canvSize)
if err != nil {
sf.log.Err("postSetCanvasSize(): on body parser, err=\n\t%v", err)
}
var (
w alias.SizeX
h alias.SizeY
)
if sf.canvSize.SizeX_ > 0 {
w = alias.SizeX(sf.canvSize.SizeX_ - 10)
}
if sf.canvSize.SizeY_ > 0 {
h = alias.SizeY(sf.canvSize.SizeY_ - 10)
}
sf.size = size.NewSize(w, h)
return ctx.SendString(sf.size.String())
}
// Возвращает новый холст
func (sf *EngineHttp) canvas(wr *bytes.Buffer, lstElem []map[string]interface{}) (*svg.SVG, error) {
var (
elem map[string]interface{}
isFind, isOk bool
_section interface{}
)
for _, elem = range lstElem {
_section, isOk = elem["type"]
if !isOk {
continue
}
section, isOk := _section.(string)
if !isOk {
return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, type(%#+v) not string
%#+v", _section, elem)
}
if section == cons.SectionConfig {
isFind = true
break
}
}
if !isFind {
return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, config not found")
}
confCanvas := canvas.NewCanvas(sf.diaMode, sf.size, sf.pos)
sizeX, sizeY := confCanvas.Size().Int()
canvas := svg.New(wr)
canvas.Start(sizeX, sizeY)
confCanvas.Draw(canvas)
return canvas, nil
}
// По запросу парсит, что получилось -- обновляет архитектуру
func (sf *EngineHttp) postArch(ctx *fiber.Ctx) error {
form := &FormArch{}
if err := ctx.BodyParser(form); err != nil {
return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in BodyParser, err=
%v", err))
}
binData := []byte(form.StrData)
if len(binData) == len(sf.oldBinArch) {
return ctx.Send(sf.oldBinArch)
}
mapElem := []map[string]interface{}{}
err := yaml.Unmarshal(binData, &mapElem)
if err != nil {
return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in Decode JSON, err=
%v", err))
}
wr := bytes.NewBuffer(nil)
canvas, err := sf.canvas(wr, mapElem)
if err != nil {
return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in create canvas, err=
%v", err))
}
switch sf.diaMode {
case cons.ModeUseCase: // режим вариантов использования
if err := sf.useCase(canvas); err != nil {
return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in useCase, err=
%v", err))
}
default: // неизвестный режим
return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): unknown mode=%q", sf.diaMode))
}
canvas.End()
binData = wr.Bytes()
return ctx.Send(binData)
}
// Формирует варианты использования
func (sf *EngineHttp) useCase(canvas *svg.SVG) error {
for _, elem := range sf.mapDrawer.LstDrawers() {
elem.Draw(canvas)
}
return nil
}
// Возвращает картинку архитектуры
func (sf *EngineHttp) postSetModeUseCase(ctx *fiber.Ctx) error {
sf.diaMode = cons.ModeUseCase
return ctx.SendString("[mode='" + sf.diaMode + "']")
}
// Возвращает главную страницу сервиса
func (sf *EngineHttp) getIndex(ctx *fiber.Ctx) error {
ctx.Response().Header.Set("Content-Type", "text/html")
return ctx.Send(indexHtml)
}