diff --git a/.vscode/launch.json b/.vscode/launch.json index 6f0f551..a84ab74 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -51,6 +51,20 @@ "--outdir-frontend", "../zmap/frontend" ] + }, + { + "name": "zmap docs", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/main.go", + "args": [ + "--metafile", + "/home/ymnuk/projects/zmap/zmap.yml", + "--outdir-doc", + "../zmap/docs", + "--format-markdown" + ] } ] } \ No newline at end of file diff --git a/go.mod b/go.mod index af6d3c8..200840c 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,13 @@ go 1.21 require ( github.com/alexflint/go-arg v1.4.3 github.com/creasty/defaults v1.7.0 + github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 github.com/satori/go.uuid v1.2.0 + github.com/yuzutech/kroki-go v0.8.1 gopkg.in/yaml.v3 v3.0.1 ) -require github.com/alexflint/go-scalar v1.2.0 // indirect +require ( + github.com/alexflint/go-scalar v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect +) diff --git a/go.sum b/go.sum index a1d963c..d673e8e 100644 --- a/go.sum +++ b/go.sum @@ -8,12 +8,18 @@ github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdB github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4= +github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuzutech/kroki-go v0.8.1 h1:p6doXpgWUXgLrVWt6MA1/ODH/oMBsnNGC3f7wGUewbM= +github.com/yuzutech/kroki-go v0.8.1/go.mod h1:JyGBdT3Od/PHEDf95GirXwhf1PM31k+tevrXGVjqNpg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/lib/config.go b/lib/config.go index 5f3a48e..9ec6d76 100644 --- a/lib/config.go +++ b/lib/config.go @@ -16,6 +16,11 @@ var AppConfig struct { Filename string `arg:"-m,--metafile" help:"Файл с метоописанием в формате YAML или JSON"` // Файл с метоописанием в формате YAML или JSON OutdirBackend string `arg:"-b,--outdir-backend" help:"Директория для сохранения кода сервера"` // Директория для сохранения кода сервера OutdirFrontend string `arg:"-f,--outdir-frontend" help:"Директория для сохранения кода клиента"` // Директория для сохранения кода клиента + OutdirDoc string `arg:"-d,--outdir-doc" help:"Директория для сохранения сгенерированной документации по проекта"` + IsMarkdown bool `arg:"--format-markdown" help:"Выходная документация в формате Markdown"` + IsHtml bool `arg:"--format-html" help:"Выходная документация в формате HTML"` + KrokiAddr string `arg:"--kroki-addr" default:"https://kroki.io" help:"Адрес сервера Kroki."` + IsSingleDoc bool `arg:"--single-doc" help:"Генерировать документацию в одном файле без разделения"` } var Project *structs.Project diff --git a/lib/documentation.go b/lib/documentation.go new file mode 100644 index 0000000..5511cf2 --- /dev/null +++ b/lib/documentation.go @@ -0,0 +1,216 @@ +package lib + +import ( + "fmt" + "log" + "strings" + "text/template" + + "git.ymnuktech.ru/ymnuk/yt-gen-app/lib/templ" + "git.ymnuktech.ru/ymnuk/yt-gen-app/structs" + uuid "github.com/satori/go.uuid" + "github.com/yuzutech/kroki-go" +) + +func Documentation() { + if !AppConfig.IsHtml { + AppConfig.IsMarkdown = true + } + // Генерация общей схемы БД + fullDocDBGen() + // Генерация схем для каждой таблицы + for _, table := range Project.DB.Tables { + docDBGen(&table) + } + var err error + var tmpl *template.Template + var out []byte + if AppConfig.IsSingleDoc { + if tmpl, err = templ.ReadTmplFile("tmpl/docs/single-index.tmpl"); err != nil { + panic(err) + } + if out, err = templ.ExecuteTmplFile(tmpl, Project); err != nil { + panic(err) + } + if AppConfig.IsMarkdown { + if err = templ.WriteFile(AppConfig.OutdirDoc+"/index.md", out); err != nil { + panic(err) + } + } + if AppConfig.IsHtml { + var buff []byte + if buff, err = templ.Content.ReadFile("tmpl/docs/styles.tmpl"); err != nil { + panic(err) + } + //{{ includeTemplPart "tmpl/docs/styles.tmpl" nil }} + out = append(buff, out...) + if err = templ.WriteFile(AppConfig.OutdirDoc+"/index.html", mdToHTML(out)); err != nil { + panic(err) + } + } + } else { + if tmpl, err = templ.ReadTmplFile("tmpl/docs/index.tmpl"); err != nil { + panic(err) + } + if out, err = templ.ExecuteTmplFile(tmpl, Project); err != nil { + panic(err) + } + if AppConfig.IsMarkdown { + if err = templ.WriteFile(AppConfig.OutdirDoc+"/index.md", out); err != nil { + panic(err) + } + } + if AppConfig.IsHtml { + var buff []byte + if buff, err = templ.Content.ReadFile("tmpl/docs/styles.tmpl"); err != nil { + panic(err) + } + //{{ includeTemplPart "tmpl/docs/styles.tmpl" nil }} + out = append(buff, out...) + out = []byte(strings.ReplaceAll(string(out), ".md", ".html")) + out = mdToHTML(out) + if err = templ.WriteFile(AppConfig.OutdirDoc+"/index.html", out); err != nil { + panic(err) + } + } + // Генерация каждого отдельного файла для каждой таблицы + if tmpl, err = templ.ReadTmplFile("tmpl/docs/db/entity-table-href.tmpl"); err != nil { + panic(err) + } + for _, table := range Project.DB.Tables { + // TODO + if out, err = templ.ExecuteTmplFile(tmpl, table); err != nil { + panic(err) + } + if AppConfig.IsMarkdown { + if err = templ.WriteFile(AppConfig.OutdirDoc+"/"+templ.FieldNameLowerPrepare(table.Name)+".md", out); err != nil { + panic(err) + } + } + if AppConfig.IsHtml { + var buff1 []byte + if buff1, err = templ.Content.ReadFile("tmpl/docs/styles.tmpl"); err != nil { + panic(err) + } + //{{ includeTemplPart "tmpl/docs/styles.tmpl" nil }} + out = append(buff1, out...) + out = []byte(strings.ReplaceAll(string(out), ".md", ".html")) + out = mdToHTML(out) + if err = templ.WriteFile(AppConfig.OutdirDoc+"/"+templ.FieldNameLowerPrepare(table.Name)+".html", out); err != nil { + panic(err) + } + } + } + } + // Генерация каждой таблицы по отдельности с прилегающими ближайшими таблицами + /*for i := range Project.DB.Tables { + docDBGen(Project.DB.Tables[i]) + }*/ + // TODO +} + +// Генерирования списка таблиц +/*func listTablesDocGen() string { + // TODO +} + +// Генерирование полей таблицы +func tableDocGen(table structs.Table) string { + // TODO +}*/ + +// Генерация общей схемы БД +func fullDocDBGen() { + // TODO + if tmpl, err := templ.ReadTmplFile("tmpl/docs/db/full-schema.tmpl"); err != nil { + panic(err) + } else { + if buff, err := templ.ExecuteTmplFile(tmpl, Project.DB); err != nil { + panic(err) + } else { + //fmt.Println(string(buff)) + //templ.WriteFile(AppConfig.OutdirDoc+"/test.txt", buff) + client := NewKrokiClient() + if result, err := client.FromString(string(buff), "dbml", kroki.SVG); err != nil { + panic(err) + } else { + //fmt.Println(result) + templ.WriteFile(AppConfig.OutdirDoc+"/full-schemadb.svg", []byte(result)) + } + + } + } +} + +// Генерация отдельной таблицы с прилегающими ближайшими таблицами +func docDBGen(table *structs.Table) { + restStruct := structs.RestStruct{ + Table: table, + Children: GetChildrenTables(table), + Parents: GetParentsTables(table), + } + if tmpl, err := templ.ReadTmplFile("tmpl/docs/db/adjacents-tables.tmpl"); err != nil { + panic(err) + } else { + if buff, err := templ.ExecuteTmplFile(tmpl, restStruct); err != nil { + fmt.Println(string(buff)) + panic(err) + } else { + //fmt.Println(string(buff)) + //templ.WriteFile(AppConfig.OutdirDoc+"/test.txt", buff) + client := NewKrokiClient() + if result, err := client.FromString(string(buff), "dbml", kroki.SVG); err != nil { + fmt.Println(string(buff)) + panic(err) + } else { + //fmt.Println(result) + templ.WriteFile(AppConfig.OutdirDoc+"/"+templ.FieldNameLowerPrepare(restStruct.Table.Name)+".svg", []byte(result)) + } + + } + } + // TODO +} + +func GetParentsTables(table *structs.Table) (tables []structs.Table) { + for _, fk := range table.FKs { + for _, tbl := range Project.DB.Tables { + if fk.TableID == uuid.Nil { + // Это одна из трех таблиц: user, role, user_role + switch templ.FieldNameLowerPrepare(fk.TableName) { + case "user": + tables = append(tables, structs.Table{ + ID: uuid.Nil, + Name: "user", + Pk: "uuid", + }) + case "role": + tables = append(tables, structs.Table{ + ID: uuid.Nil, + Name: "role", + Pk: "uuid", + }) + default: + log.Fatalf("Not found standart table %s", templ.FieldNameLowerPrepare(fk.TableName)) + } + break + } + if fk.TableID == tbl.ID { + tables = append(tables, tbl) + break + } + } + } + return +} + +func GetChildrenTables(table *structs.Table) (tables []structs.Table) { + for _, tbl := range Project.DB.Tables { + for _, fk := range tbl.FKs { + if fk.TableID == table.ID { + tables = append(tables, tbl) + } + } + } + return +} diff --git a/lib/generate.go b/lib/generate.go index 910a0b6..14303b0 100644 --- a/lib/generate.go +++ b/lib/generate.go @@ -21,4 +21,12 @@ func Generate() { } Frontend() } + + if AppConfig.OutdirDoc != "" { + // Генерация документации + if err := os.MkdirAll(AppConfig.OutdirDoc, 0755); err != nil { + log.Fatal(err) + } + Documentation() + } } diff --git a/lib/kroki-client.go b/lib/kroki-client.go new file mode 100644 index 0000000..949051b --- /dev/null +++ b/lib/kroki-client.go @@ -0,0 +1,20 @@ +package lib + +import ( + "time" + + "github.com/yuzutech/kroki-go" +) + +var krokiClient *kroki.Client + +func NewKrokiClient() *kroki.Client { + if krokiClient == nil { + tmp := kroki.New(kroki.Configuration{ + URL: AppConfig.KrokiAddr, + Timeout: time.Second * 60, + }) + krokiClient = &tmp + } + return krokiClient +} diff --git a/lib/md2html.go b/lib/md2html.go new file mode 100644 index 0000000..6c4d154 --- /dev/null +++ b/lib/md2html.go @@ -0,0 +1,21 @@ +package lib + +import ( + "github.com/gomarkdown/markdown" + "github.com/gomarkdown/markdown/html" + "github.com/gomarkdown/markdown/parser" +) + +func mdToHTML(md []byte) []byte { + // create markdown parser with extensions + extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock + p := parser.NewWithExtensions(extensions) + doc := p.Parse(md) + + // create HTML renderer with extensions + htmlFlags := html.CommonFlags | html.HrefTargetBlank + opts := html.RendererOptions{Flags: htmlFlags} + renderer := html.NewRenderer(opts) + + return markdown.Render(doc, renderer) +} diff --git a/lib/prepare/prepare-db.go b/lib/prepare/prepare-db.go index 229b4d2..ce4ac55 100644 --- a/lib/prepare/prepare-db.go +++ b/lib/prepare/prepare-db.go @@ -84,6 +84,9 @@ func prepareDB(project *structs.Project) *structs.Project { TypeParentTable: project.DB.Tables[i].FKs[j].Type, //project.DB.Tables[i].Pk, Description: fmt.Sprintf("Foreign key for \\\"%s\\\" table", project.DB.Tables[i].FKs[j].TableName), } + if project.DB.Tables[i].FKs[j].Description != "" { + fkTmp.Description = project.DB.Tables[i].FKs[j].Description + } if strings.ToLower(project.DB.Tables[i].FKs[j].TableName) == "user" { fkTmp.TypeParentTable = "uuid" } diff --git a/lib/templ/template.go b/lib/templ/template.go index e62ca65..1b42df0 100644 --- a/lib/templ/template.go +++ b/lib/templ/template.go @@ -21,14 +21,20 @@ import ( var Content embed.FS var funcMap = template.FuncMap{ + + //"includeTemplPart": IncludeTemplPart, + "fieldName": FieldName, "fieldChildName": FieldChildName, "fieldNamePrepare": FieldNamePrepare, "fieldNameLowerPrepare": FieldNameLowerPrepare, "fieldJsonNameStr": FieldJsonNameStr, "fieldType": FieldType, + "fieldTypeStr": FieldTypeStr, "fieldStringToType": FieldStringToType, //"fieldDbTableType": FieldDbTableType, + "fieldTypeDB": FieldTypeDB, + "fieldTypeDBStr": FieldTypeDBStr, "fieldTypeParentTable": FieldTypeParentTable, "hasFieldType": HasFieldType, "fieldDescript": FieldDescript, @@ -71,6 +77,22 @@ var funcMap = template.FuncMap{ "fieldInTableByName": FieldInTableByName, } +func IncludeTemplPart(templName string, data interface{}) string { + var err error + var tmpl *template.Template + if tmpl, err = ReadTmplFile(templName); err != nil { + panic(err) + } + var out []byte + if out, err = ExecuteTmplFile(tmpl, data); err != nil { + panic(err) + } + /*arr:=strings.Split(string(out)) + var newArr []string + for*/ + return string(out) +} + func IsMethod(arr map[string][]string, method string) bool { if arr == nil { return false @@ -153,15 +175,12 @@ func FieldTypeParentTable(field *structs.Field) string { return FieldType(&structs.Field{Type: field.TypeParentTable}) } -func FieldType(field *structs.Field) string { - if field == nil { - log.Fatal("field is empty") +func FieldTypeStr(value string) string { + value = strings.ToLower(strings.Trim(value, " ")) + if value == "" { + return value } - field.Type = strings.ToLower(strings.Trim(field.Type, " ")) - if field.Type == "" { - field.Type = "text" - } - switch field.Type { + switch value { case "text": return "*string" case "string": @@ -186,11 +205,24 @@ func FieldType(field *structs.Field) string { return "[]byte" default: fkTmp := &structs.Field{ - Name: field.Type, + Name: value, } return FieldName(fkTmp) //log.Fatalf("Unknow format %s", field.Type) } +} + +func FieldType(field *structs.Field) string { + if field == nil { + log.Fatal("field is empty") + } + field.Type = strings.ToLower(strings.Trim(field.Type, " ")) + value := FieldTypeStr(field.Type) + if value == "" { + field.Type = "text" + value = "text" + } + return value //return "" } @@ -212,15 +244,14 @@ func HasFieldType(fields []structs.Field, typeName string) (has bool) { return false } -func FieldTypeDB(field *structs.Field) string { - field.Type = strings.ToLower(strings.Trim(field.Type, " ")) - switch field.Type { +func FieldTypeDBStr(value string, length *int) string { + switch value { case "text": return "TEXT" case "string": tmp := "VARCHAR" - if field.Length != nil && *field.Length == 0 { - tmp += fmt.Sprintf("(%d)", *field.Length) + if length != nil && *length == 0 { + tmp += fmt.Sprintf("(%d)", *length) } else { tmp += "(255)" } @@ -243,11 +274,16 @@ func FieldTypeDB(field *structs.Field) string { case "bool": return "int" default: - log.Fatalf("Unknow format %s", field.Type) + log.Fatalf("Unknow format %s", value) } return "" } +func FieldTypeDB(field *structs.Field) string { + field.Type = strings.ToLower(strings.Trim(field.Type, " ")) + return FieldTypeDBStr(field.Type, field.Length) +} + // Генерирование описания таблиц в БД func FieldDescript(field *structs.Field, fieldId bool) (str string) { str = "`" @@ -317,7 +353,6 @@ func ConfigParamTag(field *structs.ParamConfig) string { tag += fmt.Sprintf(" help:\"%s\"", field.Help) } tag += "`" - // TODO return tag } @@ -333,6 +368,46 @@ func WriteTmplFile(filename string, outname string) error { return nil } +// Чтение шаблона и его подготовка +func ReadTmplFile(filename string) (tmpl *template.Template, err error) { + var ( + buff []byte + ) + if buff, err = Content.ReadFile(filename); err != nil { + return + } + var ( + tmp string + //tmpl *template.Template + ) + if tmp, err = RandomHex(4); err != nil { + return + } + if _, ok := funcMap["includeTemplPart"]; !ok { + funcMap["includeTemplPart"] = IncludeTemplPart + } + tmpl, err = template.New(tmp).Funcs(funcMap).Parse(string(buff)) + return +} + +// Запись файла на диск +func WriteFile(outname string, data []byte) (err error) { + err = os.WriteFile(outname, data, 0755) + return +} + +// Выполнить шаблон +func ExecuteTmplFile(tmpl *template.Template, data interface{}) (out []byte, err error) { + var b bytes.Buffer + w := bufio.NewWriter(&b) + if err = tmpl.Execute(w, data); err != nil { + return + } + w.Flush() + out = b.Bytes() + return +} + // Использование шаблона func PrepareTmplFile(filename string, data interface{}, outname string) (err error) { dir := filepath.Dir(outname) @@ -341,36 +416,25 @@ func PrepareTmplFile(filename string, data interface{}, outname string) (err err } fmt.Printf("Generate: %s\n", outname) - var ( - buff []byte - ) - if buff, err = Content.ReadFile(filename); err != nil { - return err - } - var ( - tmp string - tmpl *template.Template - ) - if tmp, err = RandomHex(4); err != nil { - return err - } - if tmpl, err = template.New(tmp).Funcs(funcMap).Parse(string(buff)); err != nil { + /*var b bytes.Buffer + w := bufio.NewWriter(&b)*/ + var tmpl *template.Template + if tmpl, err = ReadTmplFile(filename); err != nil { + return + } + /*if err = tmpl.Execute(w, data); err != nil { return err } - var b bytes.Buffer - w := bufio.NewWriter(&b) - - if err = tmpl.Execute(w, data); err != nil { - return err + w.Flush()*/ + var out []byte + if out, err = ExecuteTmplFile(tmpl, data); err != nil { + return } - w.Flush() - if err = os.WriteFile(outname, b.Bytes(), 0755); err != nil { - return err - } - return + return WriteFile(outname, out) } +// Подготовка файла, если его нет на диске func PrepareTmplIsNotExists(filename string, data interface{}, outname string) (err error) { if err = MkdirIsNotExists(filepath.Dir(outname)); err != nil { diff --git a/lib/templ/tmpl/backend/route/route.tmpl b/lib/templ/tmpl/backend/route/route.tmpl index c8d6899..36b456b 100644 --- a/lib/templ/tmpl/backend/route/route.tmpl +++ b/lib/templ/tmpl/backend/route/route.tmpl @@ -1,20 +1,22 @@ package route import ( - "{{ .Name }}/route/api" "embed" + "mime" "net/http" + "path/filepath" + "strings" + "zmap/route/api" "github.com/labstack/echo/v4" - "github.com/labstack/echo/v4/middleware" - _ "{{ .Name }}/docs" + _ "zmap/docs" echoSwagger "github.com/swaggo/echo-swagger" ) //go:embed static -var content embed.FS +var Content embed.FS func Init(e *echo.Echo) { @@ -22,9 +24,25 @@ func Init(e *echo.Echo) { api.Init(e.Group("/api")) - var contentHandler = echo.WrapHandler(http.FileServer(http.FS(content))) - var contentRewrite = middleware.Rewrite(map[string]string{"/*": "/static/$1"}) //e.GET("/", main) - e.GET("/*", contentHandler, contentRewrite) - e.GET("*", contentHandler, contentRewrite) + e.GET("/*", defaultHandler) + e.GET("*", defaultHandler) +} + +func defaultHandler(c echo.Context) error { + buff, err := Content.ReadFile(strings.ReplaceAll("static/"+c.Request().URL.Path, "//", "/")) + if err != nil { + buff, err = Content.ReadFile("static/index.html") + if err != nil { + return c.String(http.StatusNotFound, "Page not found") + } + return c.Blob(http.StatusOK, http.DetectContentType(buff), buff) + } + contentType := http.DetectContentType(buff) + tmpArr := strings.Split(contentType, ";") + if len(tmpArr) > 0 && strings.Trim(tmpArr[0], " ") == "text/plain" { + ext1 := filepath.Ext(c.Request().URL.Path) + contentType = mime.TypeByExtension(ext1) + } + return c.Blob(http.StatusOK, contentType, buff) } diff --git a/lib/templ/tmpl/docs/db/adjacents-tables.tmpl b/lib/templ/tmpl/docs/db/adjacents-tables.tmpl new file mode 100644 index 0000000..b9c7ec7 --- /dev/null +++ b/lib/templ/tmpl/docs/db/adjacents-tables.tmpl @@ -0,0 +1,31 @@ +{{ includeTemplPart "tmpl/docs/db/entity.tmpl" .Table }} + +{{ range $indexi, $table := .Children }} +{{ includeTemplPart "tmpl/docs/db/entity.tmpl" $table }} +{{ end }} + +{{ range $indexi, $table := .Parents }} +{{ includeTemplPart "tmpl/docs/db/entity.tmpl" $table }} +{{ end }} + +{{ range $indexi, $table := .Parents }} +{{ $varNameTable := fieldNamePrepare $.Table.Name }} +{{ range $indexj, $field := $.Table.FKs }} +{{ if eq $field.TableID $table.ID }} +{{ $fieldTypeParentTable := "" }} +{{ if eq $field.Name "Parent" }}{{ $fieldTypeParentTable = fieldNamePrepare $.Name }}{{ else }}{{ $newField := index $.Table.FkFields $indexj }}{{ $fieldTypeParentTable = fieldType $newField }}{{ end }} +Ref: {{ fieldNameLowerPrepare $varNameTable }}.{{ fieldNameLowerPrepare $field.Name }}_id > {{ fieldNameLowerPrepare $fieldTypeParentTable }}.id +{{ end }} +{{ end }} +{{ end }} + +{{ range $indexi, $table := .Children }} +{{ range $indexj, $field := $table.FKs }} +{{ if eq $field.TableID $.Table.ID }} +{{ $varNameTable := fieldNamePrepare $table.Name }} +{{ $fieldTypeParentTable := "" }} +{{ if eq $field.Name "Parent" }}{{ $fieldTypeParentTable = fieldNamePrepare $.Name }}{{ else }}{{ $newField := index $table.FkFields $indexj }}{{ $fieldTypeParentTable = fieldType $newField }}{{ end }} +Ref: {{ fieldNameLowerPrepare $varNameTable }}.{{ fieldNameLowerPrepare $field.Name }}_id > {{ fieldNameLowerPrepare $fieldTypeParentTable }}.id +{{ end }} +{{ end }} +{{ end }} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/entity-table-href.tmpl b/lib/templ/tmpl/docs/db/entity-table-href.tmpl new file mode 100644 index 0000000..03848b5 --- /dev/null +++ b/lib/templ/tmpl/docs/db/entity-table-href.tmpl @@ -0,0 +1,22 @@ + + +# {{ fieldNameLowerPrepare $.Name }} + +![Таблица {{ fieldNameLowerPrepare $.Name }}]({{ fieldNameLowerPrepare $.Name }}.svg "Таблица {{ fieldNameLowerPrepare $.Name }}") + +## Поля + +|Поле|Тип|Описание| +|:---|:--|:-------| +|id|{{ fieldTypeDBStr .Pk nil }}|Первичный ключ| +{{ range $index, $field := .Fields }}|{{ fieldNameLowerPrepare $field.Name }}|{{ fieldTypeDB $field }}|{{ $field.Description }}| +{{ end }} + +## Внешние ключи + +|Ключ|Тип|Талица|Описание| +|:---|:--|:-----|:-------| +{{ range $index, $field := .FkFields }}|{{ fieldNameLowerPrepare $field.Name }}_id|{{ fieldNameLowerPrepare $field.TypeParentTable }}|{{ $tableNameVar := "" }}{{ if eq $field.Name "Parent" }}{{ $tableNameVar = fieldNamePrepare $.Name }}{{ else }}{{ $tableNameVar = fieldType $field }}{{ end }}{{ fieldNameLowerPrepare $tableNameVar }}|{{ $field.Description }}| +{{ end }} + +[На главную](index.md) \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/entity-table-no-href.tmpl b/lib/templ/tmpl/docs/db/entity-table-no-href.tmpl new file mode 100644 index 0000000..e66f456 --- /dev/null +++ b/lib/templ/tmpl/docs/db/entity-table-no-href.tmpl @@ -0,0 +1,16 @@ +![Таблица {{ fieldNameLowerPrepare $.Name }}]({{ fieldNameLowerPrepare $.Name }}.svg "Таблица {{ fieldNameLowerPrepare $.Name }}") + +### Поля + +|Поле|Тип|Описание| +|:---|:--|:-------| +|id|{{ fieldTypeDBStr .Pk nil }}|Первичный ключ| +{{ range $index, $field := .Fields }}|{{ fieldNameLowerPrepare $field.Name }}|{{ fieldTypeDB $field }}|{{ $field.Description }}| +{{ end }} + +### Внешние ключи + +|Ключ|Тип|Талица|Описание| +|:---|:--|:-----|:-------| +{{ range $index, $field := .FkFields }}|{{ fieldNameLowerPrepare $field.Name }}_id|{{ fieldNameLowerPrepare $field.TypeParentTable }}|{{ $tableNameVar := "" }}{{ if eq $field.Name "Parent" }}{{ $tableNameVar = fieldNamePrepare $.Name }}{{ else }}{{ $tableNameVar = fieldType $field }}{{ end }}{{ fieldNameLowerPrepare $tableNameVar }}|{{ $field.Description }}| +{{ end }} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/entity.tmpl b/lib/templ/tmpl/docs/db/entity.tmpl new file mode 100644 index 0000000..16a9523 --- /dev/null +++ b/lib/templ/tmpl/docs/db/entity.tmpl @@ -0,0 +1,10 @@ +{{ $varNameField := fieldNamePrepare .Name }} +Table {{ fieldNameLowerPrepare $varNameField }} { + id {{ fieldTypeDBStr .Pk nil }} [primary key] + {{ range $index, $field := .Fields }} + {{ fieldNameLowerPrepare $field.Name }} {{ fieldTypeDB $field }} + {{ end }} + {{ range $index, $field := .FkFields }} + {{ fieldNameLowerPrepare $field.Name }}_id {{ fieldNameLowerPrepare $field.TypeParentTable }} + {{ end }} +} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/full-schema.tmpl b/lib/templ/tmpl/docs/db/full-schema.tmpl new file mode 100644 index 0000000..ec69b68 --- /dev/null +++ b/lib/templ/tmpl/docs/db/full-schema.tmpl @@ -0,0 +1,17 @@ +{{ includeTemplPart "tmpl/docs/db/user.tmpl" nil }} +{{ includeTemplPart "tmpl/docs/db/role.tmpl" nil }} +{{ includeTemplPart "tmpl/docs/db/user-role.tmpl" nil }} +{{ range $index, $table := .Tables }} +{{ includeTemplPart "tmpl/docs/db/entity.tmpl" $table }} +{{ end }} + +{{ range $indexi, $table := .Tables }} +{{ $varNameTable := fieldNamePrepare $table.Name }} +{{ range $indexj, $field := $table.FkFields }} +{{ $fieldTypeParentTable := "" }} +{{ if eq $field.Name "Parent" }}{{ $fieldTypeParentTable = fieldNamePrepare $.Name }}{{ else }}{{ $fieldTypeParentTable = fieldType $field }}{{ end }} +Ref: {{ fieldNameLowerPrepare $varNameTable }}.{{ fieldNameLowerPrepare $field.Name }}_id > {{ fieldNameLowerPrepare $fieldTypeParentTable }}.id +{{ end }} +{{ end }} + +{{ includeTemplPart "tmpl/docs/db/user-role-relations.tmpl" nil }} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/role.tmpl b/lib/templ/tmpl/docs/db/role.tmpl new file mode 100644 index 0000000..4029575 --- /dev/null +++ b/lib/templ/tmpl/docs/db/role.tmpl @@ -0,0 +1,5 @@ +Table role { + id UUID [primary key] + name VARCHAR(20) + description VARCHAR(20) +} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/table-list-single.tmpl b/lib/templ/tmpl/docs/db/table-list-single.tmpl new file mode 100644 index 0000000..5f50192 --- /dev/null +++ b/lib/templ/tmpl/docs/db/table-list-single.tmpl @@ -0,0 +1,4 @@ +|Название|Описание| +|:-------|:-------| +{{ range $index, $table := .Tables }}|{{ $varNameField := fieldNamePrepare $table.Name }}{{ fieldNameLowerPrepare $varNameField }}|{{ $table.Description }}| +{{end}} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/table-list.tmpl b/lib/templ/tmpl/docs/db/table-list.tmpl new file mode 100644 index 0000000..e90c4df --- /dev/null +++ b/lib/templ/tmpl/docs/db/table-list.tmpl @@ -0,0 +1,4 @@ +|Название|Описание| +|:-------|:-------| +{{ range $index, $table := .Tables }}|{{ $varNameField := fieldNamePrepare $table.Name }}[{{ fieldNameLowerPrepare $varNameField }}]({{ fieldNameLowerPrepare $varNameField }}.md)|{{ $table.Description }}| +{{end}} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/user-role-relations.tmpl b/lib/templ/tmpl/docs/db/user-role-relations.tmpl new file mode 100644 index 0000000..baf057b --- /dev/null +++ b/lib/templ/tmpl/docs/db/user-role-relations.tmpl @@ -0,0 +1,2 @@ +Ref: user_role.user_id > user.id +Ref: user_role.role_id > role.id \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/user-role.tmpl b/lib/templ/tmpl/docs/db/user-role.tmpl new file mode 100644 index 0000000..ba50e7b --- /dev/null +++ b/lib/templ/tmpl/docs/db/user-role.tmpl @@ -0,0 +1,5 @@ +Table user_role { + id UUID + user_id UUID + role_id UUID +} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/db/user.tmpl b/lib/templ/tmpl/docs/db/user.tmpl new file mode 100644 index 0000000..d0d87d4 --- /dev/null +++ b/lib/templ/tmpl/docs/db/user.tmpl @@ -0,0 +1,11 @@ +Table user { + id UUID [primary key] + login VARCHAR + surname VARCHAR(100) + name VARCHAR(100) + farname VARCHAR(100) + email VARCHAR(200) + password VARCGAR + ldap BOOLEAN + attempt INT +} \ No newline at end of file diff --git a/lib/templ/tmpl/docs/index.tmpl b/lib/templ/tmpl/docs/index.tmpl new file mode 100644 index 0000000..20c85f9 --- /dev/null +++ b/lib/templ/tmpl/docs/index.tmpl @@ -0,0 +1,6 @@ + +# База данных + +{{ includeTemplPart "tmpl/docs/db/table-list.tmpl" .DB }} + +![Полная структура БД](full-schemadb.svg "Полная структура БД") diff --git a/lib/templ/tmpl/docs/single-index.tmpl b/lib/templ/tmpl/docs/single-index.tmpl new file mode 100644 index 0000000..21890be --- /dev/null +++ b/lib/templ/tmpl/docs/single-index.tmpl @@ -0,0 +1,13 @@ + +# База данных + +{{ includeTemplPart "tmpl/docs/db/table-list-single.tmpl" .DB }} + +![Полная структура БД](full-schemadb.svg "Полная структура БД") + +{{ range $index, $table := .DB.Tables }} +{{ $varNameField := fieldNamePrepare .Name }} +## Таблица {{ fieldNameLowerPrepare $varNameField }} + +{{ includeTemplPart "tmpl/docs/db/entity-table-no-href.tmpl" $table }} +{{ end }} diff --git a/lib/templ/tmpl/docs/styles.tmpl b/lib/templ/tmpl/docs/styles.tmpl new file mode 100644 index 0000000..788cd87 --- /dev/null +++ b/lib/templ/tmpl/docs/styles.tmpl @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/structs/rest-struct.go b/structs/rest-struct.go index 7df8bcb..2c64c00 100644 --- a/structs/rest-struct.go +++ b/structs/rest-struct.go @@ -6,4 +6,9 @@ type RestStruct struct { Project *Project Rest *Rest PathParams []Field + + // Поля для формирования документации к БД + Table *Table + Children []Table + Parents []Table }