Skip to content

Commit

Permalink
add ui support
Browse files Browse the repository at this point in the history
  • Loading branch information
magiconair committed Feb 2, 2018
1 parent 17363fc commit 6738ce8
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 32 deletions.
10 changes: 7 additions & 3 deletions admin/api/manual.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
)

// ManualHandler provides a fetch and update handler for the manual overrides api.
type ManualHandler struct{}
type ManualHandler struct {
BasePath string
}

type manual struct {
Value string `json:"value"`
Expand All @@ -23,9 +25,11 @@ func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

path := r.RequestURI[len(h.BasePath):]

switch r.Method {
case "GET":
value, version, err := registry.Default.ReadManual()
value, version, err := registry.Default.ReadManual(path)
if err != nil {
log.Print("[ERROR] ", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand All @@ -43,7 +47,7 @@ func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
defer r.Body.Close()

ok, err := registry.Default.WriteManual(m.Value, m.Version)
ok, err := registry.Default.WriteManual(path, m.Value, m.Version)
if err != nil {
log.Print("[ERROR] ", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down
39 changes: 39 additions & 0 deletions admin/api/paths.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package api

import (
"log"
"net/http"
"strings"

"github.com/fabiolb/fabio/registry"
)

type ManualPathsHandler struct {
Prefix string
}

func (h *ManualPathsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// we need this for testing.
// under normal circumstances this is never nil
if registry.Default == nil {
return
}

switch r.Method {
case "GET":
paths, err := registry.Default.ManualPaths()
if err != nil {
log.Print("[ERROR] ", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
for i, p := range paths {
paths[i] = strings.TrimPrefix(p, h.Prefix)
}
writeJSON(w, r, paths)
return

default:
http.Error(w, "not allowed", http.StatusMethodNotAllowed)
}
}
26 changes: 24 additions & 2 deletions admin/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/tls"
"fmt"
"net/http"
"strings"

"github.com/fabiolb/fabio/admin/api"
"github.com/fabiolb/fabio/admin/ui"
Expand Down Expand Up @@ -31,11 +32,32 @@ func (s *Server) handler() http.Handler {

switch s.Access {
case "ro":
mux.HandleFunc("/api/paths", forbidden)
mux.HandleFunc("/api/manual", forbidden)
mux.HandleFunc("/api/manual/", forbidden)
mux.HandleFunc("/manual", forbidden)
mux.HandleFunc("/manual/", forbidden)
case "rw":
mux.Handle("/api/manual", &api.ManualHandler{})
mux.Handle("/manual", &ui.ManualHandler{Color: s.Color, Title: s.Title, Version: s.Version, Commands: s.Commands})
// for historical reasons the configured config path starts with a '/'
// but Consul treats all KV paths without a leading slash.
pathsPrefix := strings.TrimPrefix(s.Cfg.Registry.Consul.KVPath, "/")
mux.Handle("/api/paths", &api.ManualPathsHandler{Prefix: pathsPrefix})
mux.Handle("/api/manual", &api.ManualHandler{BasePath: "/api/manual"})
mux.Handle("/api/manual/", &api.ManualHandler{BasePath: "/api/manual"})
mux.Handle("/manual", &ui.ManualHandler{
BasePath: "/manual",
Color: s.Color,
Title: s.Title,
Version: s.Version,
Commands: s.Commands,
})
mux.Handle("/manual/", &ui.ManualHandler{
BasePath: "/manual",
Color: s.Color,
Title: s.Title,
Version: s.Version,
Commands: s.Commands,
})
}

mux.Handle("/api/config", &api.ConfigHandler{Config: s.Cfg})
Expand Down
60 changes: 59 additions & 1 deletion admin/ui/logo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import "net/http"

func HandleLogo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml")
w.Write(logo)
if r.FormValue("format") == "bw" {
w.Write(logoBW)
} else {
w.Write(logo)
}
}

var logo = []byte(`<?xml version="1.0" encoding="UTF-8"?>
Expand All @@ -29,3 +33,57 @@ var logo = []byte(`<?xml version="1.0" encoding="UTF-8"?>
</g>
</g>
</svg>`)

var logoBW = []byte(`<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 724.7 143.1" style="enable-background:new 0 0 724.7 143.1;" xml:space="preserve">
<style type="text/css">
.st0{enable-background:new ;}
.st1{fill:#FFFFFF;}
.st2{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:6;stroke-linecap:round;}
</style>
<title>Fabio</title>
<g class="st0">
<path class="st1" d="M304.4,140V38.6H401v11.2h-82.9v32.3h79.7v11.2h-79.7V140H304.4z"/>
<path class="st1" d="M482.4,140v-7.1h-0.3c-0.8,1.6-1.8,2.9-2.8,4c-1,1.1-2.5,1.9-4.3,2.6c-1.8,0.7-4.2,1.1-7.1,1.3
c-2.9,0.2-6.7,0.4-11.3,0.4h-28.8c-4.6,0-8.3-0.5-11.3-1.5c-2.9-1-5.3-2.4-7-4.2c-1.7-1.8-2.9-4.1-3.6-6.7c-0.7-2.7-1-5.7-1-9v-4.8
c0-4,0.7-7.2,2.1-9.7s3.5-4.3,6.2-5.5c2.8-1.3,6.2-2.1,10.2-2.5c4.1-0.4,8.7-0.6,13.9-0.6h19.3c3.9,0,7.3,0.1,10,0.3
s5.1,0.7,7.1,1.3c2,0.7,3.6,1.5,4.9,2.7s2.5,2.5,3.5,4.2h0.3V91.8c0-3.2-0.5-5.7-1.5-7.7c-1-2-2.6-3.4-4.8-4.4c-2.2-1-5-1.6-8.3-2
c-3.4-0.3-7.3-0.5-11.9-0.5h-17.6c-3.8,0-6.9,0.2-9.2,0.6c-2.3,0.4-4.2,1-5.5,1.9c-1.3,0.8-2.2,1.8-2.7,3c-0.5,1.2-0.7,2.5-0.7,4
v2.5h-12v-3.5c0-3.3,0.5-6.1,1.6-8.4c1.1-2.3,2.8-4.2,5.2-5.7c2.4-1.4,5.5-2.5,9.2-3.2c3.8-0.7,8.4-1,13.8-1h19
c5.6,0,10.7,0.3,15.3,0.8c4.6,0.6,8.6,1.7,11.8,3.5c3.3,1.8,5.8,4.3,7.6,7.5c1.8,3.2,2.7,7.5,2.7,12.8V140H482.4z M482.4,120.8
v-3.2c0-2.4-0.6-4.4-1.7-5.8c-1.1-1.4-2.8-2.6-5-3.4c-2.2-0.8-5.1-1.3-8.5-1.5c-3.4-0.2-7.4-0.4-12-0.4h-18.5
c-3.9,0-7.1,0.2-9.7,0.5c-2.5,0.3-4.5,0.9-6.1,1.8s-2.6,2-3.2,3.4c-0.6,1.4-0.9,3.3-0.9,5.5v2.2c0,2,0.2,3.7,0.7,5.1
c0.5,1.4,1.4,2.6,2.8,3.6c1.4,0.9,3.4,1.6,5.9,2s5.8,0.6,9.9,0.6h16c5.4,0,10.1-0.1,13.9-0.4s7-0.7,9.4-1.5
c2.4-0.8,4.1-1.9,5.2-3.2C481.8,124.9,482.4,123.1,482.4,120.8z"/>
<path class="st1" d="M504.5,140V38.6h12v39.5h0.3c1.3-3.7,3.7-6.4,7.2-8.1c3.5-1.7,8.7-2.5,15.6-2.5H558c7.3,0,13.3,0.5,18.1,1.6
c4.8,1.1,8.7,2.8,11.6,5.3c2.9,2.5,5,5.7,6.2,9.6c1.2,3.9,1.8,8.7,1.8,14.4V111c0,6.2-0.9,11.2-2.7,15.1c-1.8,3.9-4.4,7-7.6,9.2
c-3.3,2.2-7.2,3.7-11.8,4.6c-4.6,0.8-9.6,1.3-15,1.3h-16.8c-6.3,0-11.8-1-16.4-2.9s-7.7-5.1-9.5-9.5h-0.3V140H504.5z M583.7,110.9
v-14c0-4.1-0.5-7.4-1.5-10c-1-2.6-2.6-4.5-4.6-6c-2.1-1.4-4.6-2.4-7.8-2.9c-3.1-0.5-6.7-0.8-10.9-0.8h-16.2c-4.1,0-7.8,0.2-11,0.7
c-3.2,0.5-6,1.4-8.2,2.8c-2.2,1.4-4,3.4-5.2,6c-1.2,2.6-1.8,6.1-1.8,10.4v13.6c0,3.3,0.4,6.2,1.2,8.8c0.8,2.6,2.1,4.7,3.9,6.4
s4.2,3.1,7.3,4s6.8,1.4,11.3,1.4h19.6c4.9,0,8.8-0.4,12-1.3c3.1-0.8,5.6-2.1,7.3-3.8c1.8-1.7,3-3.8,3.6-6.4
S583.7,114.3,583.7,110.9z"/>
<path class="st1" d="M605.7,51.8V38.6h12v13.2H605.7z M605.7,140V68.6h12V140H605.7z"/>
<path class="st1" d="M628.5,112.1V96.5c0-5.9,1-10.7,2.9-14.4s4.7-6.7,8.2-8.9s7.7-3.7,12.5-4.5c4.9-0.8,10.1-1.2,15.8-1.2h17.2
c5.7,0,11,0.4,15.8,1.2c4.9,0.8,9,2.3,12.5,4.5s6.2,5.2,8.2,8.9s2.9,8.5,2.9,14.4v15.7c0,5.9-1,10.7-2.9,14.4
c-2,3.7-4.7,6.7-8.2,8.9s-7.7,3.7-12.5,4.5c-4.9,0.8-10.1,1.2-15.8,1.2H668c-5.7,0-11-0.4-15.8-1.2c-4.9-0.8-9-2.3-12.5-4.5
s-6.2-5.2-8.2-8.9C629.5,122.8,628.5,118,628.5,112.1z M712.7,111.4V97.2c0-4.2-0.6-7.6-1.8-10.2c-1.2-2.6-2.8-4.6-4.9-6
c-2.1-1.4-4.6-2.4-7.6-2.9c-2.9-0.5-6.1-0.8-9.6-0.8h-24.5c-3.5,0-6.8,0.3-9.7,0.8c-2.9,0.5-5.4,1.5-7.5,2.9
c-2.1,1.4-3.7,3.5-4.9,6c-1.2,2.6-1.8,6-1.8,10.2v14.3c0,4.2,0.6,7.6,1.8,10.1s2.8,4.6,4.9,6c2.1,1.4,4.6,2.4,7.5,2.9
c2.9,0.5,6.1,0.8,9.7,0.8h24.5c3.5,0,6.6-0.3,9.6-0.8c2.9-0.5,5.5-1.5,7.6-2.9c2.1-1.4,3.7-3.5,4.9-6S712.7,115.6,712.7,111.4z"/>
</g>
<path class="st1" d="M106,0h60c5.5,0,10,4.5,10,10v20c0,5.5-4.5,10-10,10h-60c-5.5,0-10-4.5-10-10V10C96,4.5,100.5,0,106,0z"/>
<path class="st1" d="M10,104h60c5.5,0,10,4.5,10,10v20c0,5.5-4.5,10-10,10H10c-5.5,0-10-4.5-10-10v-20C0,108.5,4.5,104,10,104z"/>
<path class="st1" d="M106,104h60c5.5,0,10,4.5,10,10v20c0,5.5-4.5,10-10,10h-60c-5.5,0-10-4.5-10-10v-20C96,108.5,100.5,104,106,104
z"/>
<path class="st1" d="M202,104h60c5.5,0,10,4.5,10,10v20c0,5.5-4.5,10-10,10h-60c-5.5,0-10-4.5-10-10v-20
C192,108.5,196.5,104,202,104z"/>
<line class="st2" x1="136" y1="48" x2="136" y2="96"/>
<line class="st2" x1="40" y1="80" x2="40" y2="96"/>
<line class="st2" x1="232" y1="80" x2="232" y2="96"/>
<line class="st2" x1="80" y1="64" x2="192" y2="64"/>
<line class="st2" x1="80" y1="64" x2="40" y2="80"/>
<line class="st2" x1="192" y1="64" x2="232" y2="80"/>
</svg>
`)
59 changes: 46 additions & 13 deletions admin/ui/manual.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,48 @@ import (
)

type ManualHandler struct {
BasePath string
Color string
Title string
Version string
Commands string
}

type paths struct {
Path string
Name string
}

func (h *ManualHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tmplManual.ExecuteTemplate(w, "manual", h)
path := r.RequestURI[len(h.BasePath):]
data := struct {
*ManualHandler
Path string
APIPath string
}{
ManualHandler: h,
Path: path,
APIPath: "/api/manual" + path,
}
tmplManual.ExecuteTemplate(w, "manual", data)
}

var funcs = template.FuncMap{
"noescape": func(str string) template.HTML {
return template.HTML(str)
},
}

var tmplManual = template.Must(template.New("manual").Parse(`
var tmplManual = template.Must(template.New("manual").Funcs(funcs).Parse(`
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>fabio{{if .Title}} - {{.Title}}{{end}}</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/js/materialize.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style type="text/css">
Expand All @@ -34,13 +57,16 @@ var tmplManual = template.Must(template.New("manual").Parse(`
</head>
<body>
<ul id="overrides" class="dropdown-content"></ul>
<nav class="top-nav {{.Color}}">
<div class="container">
<div class="nav-wrapper">
<a href="/" class="brand-logo">fabio{{if .Title}} - {{.Title}}{{end}}</a>
<a href="/" class="brand-logo"><img style="margin: 15px 0" class="logo" src="/logo.svg?format=bw"> {{if .Title}} - {{.Title}}{{end}}</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a href="/routes">Routes</a></li>
<li><a class="dropdown-button" href="#!" data-activates="overrides">Overrides<i class="material-icons right">arrow_drop_down</i></a></li>
<li><a href="https://github.com/fabiolb/fabio/blob/master/CHANGELOG.md">{{.Version}}</a></li>
<li><a href="https://github.com/fabiolb/fabio">Github</a></li>
</ul>
Expand All @@ -52,7 +78,7 @@ var tmplManual = template.Must(template.New("manual").Parse(`
<div class="container">
<div class="section">
<h5>Manual Overrides</h5>
<h5>Manual Routes{{if .Path}} for "{{.Path}}"{{end}}</h5>
<div class="row">
<form class="col s12">
Expand All @@ -73,23 +99,30 @@ var tmplManual = template.Must(template.New("manual").Parse(`
</div>
</div>
<div class="section footer">
<img class="logo" src="/logo.svg">
</div>
</div>
<script>
$(function(){
var params={};window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi,function(str,key,value){params[key] = value;});
$.get("/api/manual", function(data) {
$.get({{.APIPath}}, function(data) {
$("input[name=version]").val(data.version);
$("textarea>label").val("Version " + data.version);
$("#textarea1").val(data.value);
$("#textarea1").trigger('autoresize');
});
$.get('/api/paths', function(data) {
var d = $("#overrides");
$.each(data, function(idx, val) {
var path = val;
if (val == "") {
val = "default"
}
d.append('<li><a href="/manual'+path+'">'+val+'</a></li>');
});
});
$("button[name=help]").click(function() {
$("pre.help").toggleClass("hide");
});
Expand All @@ -99,7 +132,7 @@ $(function(){
value : $("#textarea1").val(),
version : $("input[name=version]").val()
}
$.ajax('/api/manual', {
$.ajax({{.APIPath}}, {
type: 'PUT',
data: JSON.stringify(data),
contentType: 'application/json',
Expand Down
22 changes: 18 additions & 4 deletions admin/ui/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ var tmplRoutes = template.Must(template.New("routes").Parse(`
<head>
<meta charset="utf-8">
<title>fabio{{if .Title}} - {{.Title}}{{end}}</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/js/materialize.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/css/materialize.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<style type="text/css">
Expand All @@ -37,13 +38,15 @@ var tmplRoutes = template.Must(template.New("routes").Parse(`
</head>
<body>
<ul id="overrides" class="dropdown-content"></ul>
<nav class="top-nav {{.Color}}">
<div class="container">
<div class="nav-wrapper">
<a href="/" class="brand-logo">fabio{{if .Title}} - {{.Title}}{{end}}</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a href="/manual">Overrides</a></li>
<li><a class="dropdown-button" href="#!" data-activates="overrides">Overrides<i class="material-icons right">arrow_drop_down</i></a></li>
<li><a href="https://github.com/fabiolb/fabio/blob/master/CHANGELOG.md">{{.Version}}</a></li>
<li><a href="https://github.com/fabiolb/fabio">Github</a></li>
</ul>
Expand Down Expand Up @@ -125,6 +128,17 @@ $(function(){
doFilter(v);
});
$.get('/api/paths', function(data) {
var d = $("#overrides");
$.each(data, function(idx, val) {
var path = val;
if (val == "") {
val = "default"
}
d.append('<li><a href="/manual'+path+'">'+val+'</a></li>');
});
});
})
</script>
Expand Down
8 changes: 6 additions & 2 deletions registry/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ type Backend interface {
// Deregister removes the service registration for fabio.
Deregister() error

// ManualPaths returns the list of paths for which there
// are overrides.
ManualPaths() ([]string, error)

// ReadManual returns the current manual overrides and
// their version as seen by the registry.
ReadManual() (value string, version uint64, err error)
ReadManual(path string) (value string, version uint64, err error)

// WriteManual writes the new value to the registry if the
// version of the stored document still matchhes version.
WriteManual(value string, version uint64) (ok bool, err error)
WriteManual(path string, value string, version uint64) (ok bool, err error)

// WatchServices watches the registry for changes in service
// registration and health and pushes them if there is a difference.
Expand Down
Loading

0 comments on commit 6738ce8

Please sign in to comment.