add basic UI

This commit is contained in:
Alexander Baryshnikov 2020-09-28 23:12:57 +08:00
parent 48094464eb
commit 4698e52dca
5 changed files with 188 additions and 50 deletions

View File

@ -15,6 +15,7 @@ import (
type runCmd struct { type runCmd struct {
Directory string `long:"directory" short:"d" env:"DIRECTORY" description:"Data directory" default:"run"` Directory string `long:"directory" short:"d" env:"DIRECTORY" description:"Data directory" default:"run"`
UI string `long:"ui" env:"UI" description:"Path to UI directory" default:"templates"`
Interval time.Duration `long:"interval" short:"i" env:"INTERVAL" description:"Requeue interval" default:"3s"` Interval time.Duration `long:"interval" short:"i" env:"INTERVAL" description:"Requeue interval" default:"3s"`
Attempts int `long:"attempts" short:"a" env:"ATTEMPTS" description:"Max number of attempts" default:"5"` Attempts int `long:"attempts" short:"a" env:"ATTEMPTS" description:"Max number of attempts" default:"5"`
Concurrency int `long:"concurrency" short:"c" env:"CONCURRENCY" description:"Number of parallel worker (0 - mean number of CPU)" default:"0"` Concurrency int `long:"concurrency" short:"c" env:"CONCURRENCY" description:"Number of parallel worker (0 - mean number of CPU)" default:"0"`
@ -35,6 +36,7 @@ func (cfg *runCmd) Execute([]string) error {
srv := runner.DefaultConfig() srv := runner.DefaultConfig()
srv.Bind = cfg.Bind srv.Bind = cfg.Bind
srv.WorkingDirectory = cfg.Directory srv.WorkingDirectory = cfg.Directory
srv.UIDirectory = cfg.UI
srv.ConfigDirectory = tmpDir srv.ConfigDirectory = tmpDir
unit := server.DefaultUnit() unit := server.DefaultUnit()

View File

@ -91,14 +91,13 @@ func (cfg Config) Create(global context.Context) (*Server, error) {
ctx, cancel := context.WithCancel(global) ctx, cancel := context.WithCancel(global)
router := gin.Default() router := gin.Default()
router.LoadHTMLGlob(filepath.Join(cfg.UIDirectory, "*.html"))
router.Static("/static", filepath.Join(cfg.UIDirectory, "static"))
server.Attach(router.Group("/api/"), units, workers) server.Attach(router.Group("/api/"), units, workers)
ui.Attach(router.Group("/ui/"), units, cfg.UIDirectory) ui.Attach(router.Group("/ui/"), units)
router.Group("/", func(gctx *gin.Context) { router.GET("/", func(gctx *gin.Context) {
gctx.Redirect(http.StatusTemporaryRedirect, "ui") gctx.Redirect(http.StatusTemporaryRedirect, "ui")
}) })
//router.Path("/").Methods("GET").HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
// http.Redirect(writer, request, "ui", http.StatusTemporaryRedirect)
//})
srv := &Server{ srv := &Server{
Handler: router, Handler: router,

View File

@ -1,11 +1,9 @@
package ui package ui
import ( import (
"html/template"
"net/http" "net/http"
"path/filepath" "path/filepath"
"github.com/Masterminds/sprig"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"nano-run/server" "nano-run/server"
@ -13,21 +11,23 @@ import (
func Expose(units []server.Unit, uiDir string) http.Handler { func Expose(units []server.Unit, uiDir string) http.Handler {
router := gin.New() router := gin.New()
Attach(router, units, uiDir) router.LoadHTMLGlob(filepath.Join(uiDir, "*.html"))
Attach(router, units)
return router return router
} }
func Attach(router gin.IRouter, units []server.Unit, uiDir string) { func Attach(router gin.IRouter, units []server.Unit) {
ui := &uiRouter{ ui := &uiRouter{
dir: uiDir,
units: units, units: units,
} }
router.GET("", func(gctx *gin.Context) {
gctx.Redirect(http.StatusTemporaryRedirect, "units")
})
router.GET("/units", ui.listUnits) router.GET("/units", ui.listUnits)
router.GET("/unit/:name", ui.unitInfo) router.GET("/unit/:name", ui.unitInfo)
} }
type uiRouter struct { type uiRouter struct {
dir string
units []server.Unit units []server.Unit
} }
@ -58,15 +58,3 @@ func (ui *uiRouter) listUnits(gctx *gin.Context) {
reply.Units = ui.units reply.Units = ui.units
gctx.HTML(http.StatusOK, "units-list.html", reply) gctx.HTML(http.StatusOK, "units-list.html", reply)
} }
func (ui *uiRouter) getTemplate(name string) *template.Template {
t, err := template.New("").Funcs(sprig.HtmlFuncMap()).ParseFiles(filepath.Join(ui.dir, name))
if err == nil {
return t
}
t, err = template.New("").Parse("<html><body>Ooops... Page not found</body></html>")
if err != nil {
panic(err)
}
return t
}

View File

@ -1,18 +1,106 @@
<html> <html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/mvp.css">
</head>
<body> <body>
<div> <main>
<a href="unit/{{.Unit.Name}}"> <section>
<h2>{{.Unit.Name}}</h2> <header>
{{if .Unit.Secured}} <h2><a href="../units">Units</a> :: {{.Unit.Name}}</h2>
secured <h4>
<code><script>document.write((new URL("../../api/{{.Unit.Name}}/", window.location).href))</script></code>
</h4>
</header>
<aside>
<h3>Configuration</h3>
<ul>
{{with .Unit}}
<li>
<b>Mode: </b>{{.Mode}}
</li>
<li>
<b>Concurrency: </b>{{.Workers}}
</li>
<li>
<b>Attempts: </b>{{.Attempts}}
</li>
<li>
<b>Interval: </b>{{.Interval}}
</li>
<li>
<b>Timeout: </b>
{{with .Timeout}}
{{.}}
{{else}}
{{end}}
</li>
<li>
<b>Max request size: </b>
{{with .MaxRequest}}
{{.}}
{{else}}
{{end}}
</li>
<li>
<b>Working directory: </b>
{{with .WorkDir}}
<sup>static</sup>
<span style="overflow-x: auto; word-break: break-all">{{.}}</span>
{{else}}
<i>dynamic</i>
{{end}}
</li>
{{end}}
</ul>
</aside>
<aside>
<h3>Environment</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{{range $k,$v := .Unit.Environment}}
<tr>
<td>
<pre>{{$k}}</pre>
</td>
<td>{{$v}}</td>
</tr>
{{end}}
</tbody>
</table>
</aside>
{{with .Unit.Authorization}}
<aside>
<h3>Authorization{{if not $.Unit.Secured}} ⚠️{{end}}</h3>
<table>
<tr>
<th>JWT</th>
<td>{{if .JWT.Enable}}✅{{else}}❌{{end}}</td>
</tr>
<tr>
<th>Token in query</th>
<td>{{if .QueryToken.Enable}}✅{{else}}❌{{end}}</td>
</tr>
<tr>
<th>Token in header</th>
<td>{{if .HeaderToken.Enable}}✅{{else}}❌{{end}}</td>
</tr>
<tr>
<th>Basic auth</th>
<td>{{if .Basic.Enable}}✅{{else}}❌{{end}}</td>
</tr>
</table>
</aside>
{{end}} {{end}}
</a> </section>
<p> </main>
<span class="unit-mode unit-mode-{{.Unit.Mode}}">{{.Unit.Mode}}</span>,
<span class="unit-mode unit-mode-workers">{{.Unit.Workers}}</span>,
<span class="unit-mode unit-mode-interval">{{.Unit.Interval}}</span>,
<span class="unit-mode unit-mode-timeout">{{.Unit.Timeout}}</span>,
</p>
</div>
</body> </body>
</html> </html>

View File

@ -1,17 +1,78 @@
<html> <html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/mvp.css">
</head>
<body> <body>
{{range .Units}} <header>
<div> <nav>
<a href="unit/{{.Name}}"> </nav>
<h2>{{.Name}}</h2> <h1> Units </h1>
</a> </header>
<p> <main>
<span class="unit-mode unit-mode-{{.Mode}}">{{.Mode}}</span>, <section>
<span class="unit-mode unit-mode-workers">{{.Workers}}</span>, <table>
<span class="unit-mode unit-mode-interval">{{.Interval}}</span>, <thead>
<span class="unit-mode unit-mode-timeout">{{.Timeout}}</span>, <tr>
</p> <th>Name</th>
</div> <th>Mode</th>
{{end}} <th>Concurrency</th>
<th>Attempts</th>
<th>Interval</th>
<th>Timeout</th>
<th>Max request size</th>
<th>Working directory</th>
</tr>
</thead>
<tbody>
{{range .Units}}
<tr>
<td>
<a href="unit/{{.Name}}">
{{.Name}}{{if .Secured}} (secured){{end}}
</a>
</td>
<td>
<span class="unit-mode unit-mode-{{.Mode}}">{{.Mode}}</span>
</td>
<td>
<span class="unit-mode unit-mode-workers">{{.Workers}}</span>
</td>
<td>
{{.Attempts}}
</td>
<td>
<span class="unit-mode unit-mode-interval">{{.Interval}}</span>
</td>
<td>
{{with .Timeout}}
{{.}}
{{else}}
{{end}}
</td>
<td>
{{with .MaxRequest}}
{{.}}
{{else}}
{{end}}
</td>
<td>
{{with .WorkDir}}
<details>
<summary>static</summary>
<p>{{.}}</p>
</details>
{{else}}
<i>dynamic</i>
{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</section>
</main>
</body> </body>
</html> </html>