serv_http.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // package mod_http -- http-модуль для архитектуры
  2. package mod_http
  3. import (
  4. "bytes"
  5. _ "embed"
  6. "encoding/json"
  7. "fmt"
  8. "time"
  9. svg "github.com/ajstarks/svgo"
  10. "github.com/gofiber/fiber/v2"
  11. "gopkg.in/yaml.v3"
  12. "gitp78su.ipnodns.ru/svi/goarch/lev0/alias"
  13. "gitp78su.ipnodns.ru/svi/goarch/lev0/cons"
  14. "gitp78su.ipnodns.ru/svi/goarch/lev0/types"
  15. "gitp78su.ipnodns.ru/svi/goarch/lev1/coord"
  16. "gitp78su.ipnodns.ru/svi/goarch/lev1/size"
  17. "gitp78su.ipnodns.ru/svi/goarch/lev1/view_use_case"
  18. "gitp78su.ipnodns.ru/svi/goarch/pkg/elems/canvas"
  19. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_actor"
  20. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_link"
  21. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_use_group"
  22. "gitp78su.ipnodns.ru/svi/kern/v3"
  23. "gitp78su.ipnodns.ru/svi/kern/v3/kc/log_buf"
  24. "gitp78su.ipnodns.ru/svi/kern/v3/krn/ktypes"
  25. )
  26. //go:embed index.html
  27. var indexHtml []byte
  28. // CanvasSize -- размер хоста
  29. type CanvasSize struct {
  30. SizeX_ int `form:"w"`
  31. SizeY_ int `form:"h"`
  32. }
  33. func (sf *CanvasSize) String() string {
  34. return fmt.Sprintf("w:%v; h:%v", sf.SizeX_, sf.SizeY_)
  35. }
  36. // ModHttp -- http-модуль для архитектуры
  37. type ModHttp struct {
  38. // fibApp *fiber.App
  39. kCtx ktypes.IKernelCtx
  40. log ktypes.ILogBuf
  41. diaMode string // Режим диаграммы
  42. oldBinArch []byte // Кеш старой архитектуры
  43. canvSize CanvasSize // Размер холста
  44. size types.ISize // Размер холста в его координатах
  45. pos types.ICoord // Координаты холста
  46. }
  47. // NewModHttp -- возвращает новый сервис
  48. func NewModHttp() *ModHttp {
  49. optTerm := log_buf.OptIsTerm(true)
  50. optPref := log_buf.OptPrefix("ModHttp")
  51. log := log_buf.NewLogBuf(optTerm, optPref)
  52. sf := &ModHttp{
  53. kCtx: kern.GetKernelCtx(),
  54. log: log,
  55. diaMode: cons.ModeUseCase,
  56. size: size.NewSize(800, 600),
  57. pos: coord.NewCoord(0, 0),
  58. }
  59. sHttp := kern.GetKernelServerHttp()
  60. fibApp := sHttp.Fiber()
  61. fibApp.Static("/static", "./static", fiber.Static{
  62. Compress: true,
  63. Browse: true,
  64. CacheDuration: time.Second * 10,
  65. })
  66. fibApp.Get("/", sf.getIndex)
  67. fibApp.Get("/api/use_case", sf.postSetModeUseCase)
  68. fibApp.Post("/api/arch", sf.postArch)
  69. fibApp.Post("/api/canvas_size", sf.postSetCanvasSize)
  70. return sf
  71. }
  72. // FormArch -- извлекает данные из текстового поля для обновления
  73. type FormArch struct {
  74. StrData string `form:"text"`
  75. }
  76. // Устанавливает размер холста
  77. func (sf *ModHttp) postSetCanvasSize(ctx *fiber.Ctx) error {
  78. err := ctx.BodyParser(&sf.canvSize)
  79. if err != nil {
  80. sf.log.Err("postSetCanvasSize(): on body parser, err=\n\t%v", err)
  81. }
  82. if sf.canvSize.SizeX_ > 0 {
  83. sf.size.W_ = alias.SizeX(sf.canvSize.SizeX_ - 10)
  84. }
  85. if sf.canvSize.SizeY_ > 0 {
  86. sf.size.H_ = alias.SizeY(sf.canvSize.SizeY_ - 10)
  87. }
  88. return ctx.SendString(sf.canvSize.String())
  89. }
  90. // Возвращает новый холст
  91. func (sf *ModHttp) canvas(wr *bytes.Buffer, lstElem []map[string]interface{}) (*svg.SVG, error) {
  92. var (
  93. elem map[string]interface{}
  94. isFind, isOk bool
  95. _section interface{}
  96. )
  97. for _, elem = range lstElem {
  98. _section, isOk = elem["type"]
  99. if !isOk {
  100. continue
  101. }
  102. section, isOk := _section.(string)
  103. if !isOk {
  104. return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, type(%#+v) not string<br>%#+v", _section, elem)
  105. }
  106. if section == cons.SectionConfig {
  107. isFind = true
  108. break
  109. }
  110. }
  111. if !isFind {
  112. return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, config not found")
  113. }
  114. confCanvas := canvas.NewCanvas(sf.diaMode, sf.size, sf.pos)
  115. sizeX, sizeY := confCanvas.Size()
  116. canvas := svg.New(wr)
  117. canvas.Start(int(sizeX), int(sizeY))
  118. confCanvas.Draw(canvas)
  119. return canvas, nil
  120. }
  121. // По запросу парсит, что получилось -- обновляет архитектуру
  122. func (sf *ModHttp) postArch(ctx *fiber.Ctx) error {
  123. form := &FormArch{}
  124. if err := ctx.BodyParser(form); err != nil {
  125. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in BodyParser, err=<br>%v", err))
  126. }
  127. binData := []byte(form.StrData)
  128. if len(binData) == len(sf.oldBinArch) {
  129. return ctx.Send(sf.oldBinArch)
  130. }
  131. mapElem := []map[string]interface{}{}
  132. err := yaml.Unmarshal(binData, &mapElem)
  133. if err != nil {
  134. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in Decode JSON, err=<br>%v", err))
  135. }
  136. wr := bytes.NewBuffer(nil)
  137. canvas, err := sf.canvas(wr, mapElem)
  138. if err != nil {
  139. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in create canvas, err=<br>%v", err))
  140. }
  141. switch sf.diaMode {
  142. case cons.ModeUseCase: // режим вариантов использования
  143. if err := sf.useCase(canvas, mapElem); err != nil {
  144. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in useCase, err=<br>%v", err))
  145. }
  146. default: // неизвестный режим
  147. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): unknown mode=%q", sf.diaMode))
  148. }
  149. canvas.End()
  150. binData = wr.Bytes()
  151. return ctx.Send(binData)
  152. }
  153. // Формирует варианты использования
  154. func (sf *ModHttp) useCase(canvas *svg.SVG, lstElem []map[string]interface{}) error {
  155. mapDraw := make(map[alias.Id]types.IDrawer)
  156. for _, elem := range lstElem {
  157. _id, isOk := elem["id"]
  158. if !isOk {
  159. return fmt.Errorf("ServHttp.useCase(): field 'id' not found<br>%#+v", elem)
  160. }
  161. id0, isOk := _id.(string)
  162. if !isOk {
  163. return fmt.Errorf("ServHttp.useCase(): field 'id'(%T, %+v) not alias.Id", _id, _id)
  164. }
  165. id := alias.Id(id0)
  166. if id == "" {
  167. return fmt.Errorf("ServHttp.useCase(): id=%q, elem is empty<br>%#+v", id, elem)
  168. }
  169. _, isOk = mapDraw[id]
  170. if isOk {
  171. return fmt.Errorf("ServHttp.useCase(): id=%q already exists", id)
  172. }
  173. binElem, _ := json.MarshalIndent(elem, "", " ")
  174. _type, isOk := elem["type"]
  175. if !isOk {
  176. return fmt.Errorf("ServHttp.useCase(): id=%q, field 'type' not found<br>%+v", id, string(binElem))
  177. }
  178. strType, isOk := _type.(string)
  179. if !isOk {
  180. return fmt.Errorf("ServHttp.useCase(): id=%q, field 'type'(%+v) not string<br>%+v", id, _type, string(binElem))
  181. }
  182. var (
  183. drawer types.IDrawer
  184. )
  185. switch strType {
  186. case cons.TypeUseActor: // Нарисовать актора
  187. view_actor.NewViewActor(actor, elem)
  188. case cons.TypeUseCase: // Нарисовать вариант использования
  189. drawer = view_use_case.NewViewUseCase(useCase, elem)
  190. case cons.TypeUseLink: // Нарисовать связь
  191. drawer = view_link.NewViewLink(useLink)
  192. case cons.TypeUseGroup: // Нарисовать группу вариантов использования
  193. drawer = view_use_group.NewViewUseGroup(useGroup, elem)
  194. }
  195. if drawer != nil {
  196. mapDraw[id] = drawer
  197. sf.log.Debug("useCase(): id=%q, elem=\n%v\n", id, elem)
  198. }
  199. }
  200. for _, drawer := range mapDraw {
  201. switch drawer.(type) {
  202. case types.IElemLinker:
  203. }
  204. drawer.Draw(canvas, mapDraw)
  205. }
  206. return nil
  207. }
  208. // Возвращает картинку архитектуры
  209. func (sf *ModHttp) postSetModeUseCase(ctx *fiber.Ctx) error {
  210. sf.diaMode = cons.ModeUseCase
  211. return ctx.SendString("[mode='" + sf.diaMode + "']")
  212. }
  213. // Возвращает главную страницу сервиса
  214. func (sf *ModHttp) getIndex(ctx *fiber.Ctx) error {
  215. ctx.Response().Header.Set("Content-Type", "text/html")
  216. return ctx.Send(indexHtml)
  217. }