serv_http.go 7.3 KB

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