Merge pull request 'db-visual' (#1) from db-visual into main
Reviewed-on: #1
This commit is contained in:
commit
e3fcf741ab
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
7
go.mod
7
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
|
||||
)
|
||||
|
|
6
go.sum
6
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=
|
||||
|
|
|
@ -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
|
||||
|
|
216
lib/documentation.go
Normal file
216
lib/documentation.go
Normal file
|
@ -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
|
||||
}
|
|
@ -21,4 +21,12 @@ func Generate() {
|
|||
}
|
||||
Frontend()
|
||||
}
|
||||
|
||||
if AppConfig.OutdirDoc != "" {
|
||||
// Генерация документации
|
||||
if err := os.MkdirAll(AppConfig.OutdirDoc, 0755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
Documentation()
|
||||
}
|
||||
}
|
||||
|
|
20
lib/kroki-client.go
Normal file
20
lib/kroki-client.go
Normal file
|
@ -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
|
||||
}
|
21
lib/md2html.go
Normal file
21
lib/md2html.go
Normal file
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
31
lib/templ/tmpl/docs/db/adjacents-tables.tmpl
Normal file
31
lib/templ/tmpl/docs/db/adjacents-tables.tmpl
Normal file
|
@ -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 }}
|
22
lib/templ/tmpl/docs/db/entity-table-href.tmpl
Normal file
22
lib/templ/tmpl/docs/db/entity-table-href.tmpl
Normal file
|
@ -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)
|
16
lib/templ/tmpl/docs/db/entity-table-no-href.tmpl
Normal file
16
lib/templ/tmpl/docs/db/entity-table-no-href.tmpl
Normal file
|
@ -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 }}
|
10
lib/templ/tmpl/docs/db/entity.tmpl
Normal file
10
lib/templ/tmpl/docs/db/entity.tmpl
Normal file
|
@ -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 }}
|
||||
}
|
17
lib/templ/tmpl/docs/db/full-schema.tmpl
Normal file
17
lib/templ/tmpl/docs/db/full-schema.tmpl
Normal file
|
@ -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 }}
|
5
lib/templ/tmpl/docs/db/role.tmpl
Normal file
5
lib/templ/tmpl/docs/db/role.tmpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
Table role {
|
||||
id UUID [primary key]
|
||||
name VARCHAR(20)
|
||||
description VARCHAR(20)
|
||||
}
|
4
lib/templ/tmpl/docs/db/table-list-single.tmpl
Normal file
4
lib/templ/tmpl/docs/db/table-list-single.tmpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
|Название|Описание|
|
||||
|:-------|:-------|
|
||||
{{ range $index, $table := .Tables }}|{{ $varNameField := fieldNamePrepare $table.Name }}{{ fieldNameLowerPrepare $varNameField }}|{{ $table.Description }}|
|
||||
{{end}}
|
4
lib/templ/tmpl/docs/db/table-list.tmpl
Normal file
4
lib/templ/tmpl/docs/db/table-list.tmpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
|Название|Описание|
|
||||
|:-------|:-------|
|
||||
{{ range $index, $table := .Tables }}|{{ $varNameField := fieldNamePrepare $table.Name }}[{{ fieldNameLowerPrepare $varNameField }}]({{ fieldNameLowerPrepare $varNameField }}.md)|{{ $table.Description }}|
|
||||
{{end}}
|
2
lib/templ/tmpl/docs/db/user-role-relations.tmpl
Normal file
2
lib/templ/tmpl/docs/db/user-role-relations.tmpl
Normal file
|
@ -0,0 +1,2 @@
|
|||
Ref: user_role.user_id > user.id
|
||||
Ref: user_role.role_id > role.id
|
5
lib/templ/tmpl/docs/db/user-role.tmpl
Normal file
5
lib/templ/tmpl/docs/db/user-role.tmpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
Table user_role {
|
||||
id UUID
|
||||
user_id UUID
|
||||
role_id UUID
|
||||
}
|
11
lib/templ/tmpl/docs/db/user.tmpl
Normal file
11
lib/templ/tmpl/docs/db/user.tmpl
Normal file
|
@ -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
|
||||
}
|
6
lib/templ/tmpl/docs/index.tmpl
Normal file
6
lib/templ/tmpl/docs/index.tmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
# База данных
|
||||
|
||||
{{ includeTemplPart "tmpl/docs/db/table-list.tmpl" .DB }}
|
||||
|
||||
![Полная структура БД](full-schemadb.svg "Полная структура БД")
|
13
lib/templ/tmpl/docs/single-index.tmpl
Normal file
13
lib/templ/tmpl/docs/single-index.tmpl
Normal file
|
@ -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 }}
|
6
lib/templ/tmpl/docs/styles.tmpl
Normal file
6
lib/templ/tmpl/docs/styles.tmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
<style>
|
||||
table, th, td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
</style>
|
|
@ -6,4 +6,9 @@ type RestStruct struct {
|
|||
Project *Project
|
||||
Rest *Rest
|
||||
PathParams []Field
|
||||
|
||||
// Поля для формирования документации к БД
|
||||
Table *Table
|
||||
Children []Table
|
||||
Parents []Table
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user