Implement confirm dialog

Also moved shared styles to separate file which is included by
templates.
This commit is contained in:
Anders Pitman 2020-10-11 13:32:10 -06:00
parent af539f4d68
commit 1b4e6beb61
4 changed files with 226 additions and 122 deletions

View File

@ -10,18 +10,39 @@ import (
"strconv"
)
type IndexData struct {
Styles template.CSS
Tunnels map[string]Tunnel
}
type ConfirmData struct {
Styles template.CSS
Message string
ConfirmUrl string
CancelUrl string
}
func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request) {
box, err := rice.FindBox("webui")
if err != nil {
w.WriteHeader(500)
io.WriteString(w, "Error opening webui")
return
}
stylesText, err := box.String("styles.css")
if err != nil {
w.WriteHeader(500)
io.WriteString(w, "Error reading styles.css")
return
}
switch r.URL.Path {
case "/login":
p.handleLogin(w, r)
case "/":
box, err := rice.FindBox("webui")
if err != nil {
w.WriteHeader(500)
io.WriteString(w, "Error opening webui")
return
}
token, err := extractToken("access_token", r)
if err != nil {
@ -60,7 +81,12 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
return
}
tmpl.Execute(w, p.db.GetTunnels())
indexData := IndexData {
Styles: template.CSS(stylesText),
Tunnels: p.db.GetTunnels(),
}
tmpl.Execute(w, indexData)
//io.WriteString(w, indexTemplate)
@ -81,6 +107,48 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
p.handleTunnels(w, r)
case "/confirm-delete-tunnel":
box, err := rice.FindBox("webui")
if err != nil {
w.WriteHeader(500)
io.WriteString(w, "Error opening webui")
return
}
confirmTemplate, err := box.String("confirm.tmpl")
if err != nil {
w.WriteHeader(500)
io.WriteString(w, "Error reading confirm.tmpl")
return
}
tmpl, err := template.New("test").Parse(confirmTemplate)
if err != nil {
w.WriteHeader(500)
log.Println(err)
io.WriteString(w, "Error compiling confirm.tmpl")
return
}
r.ParseForm()
if len(r.Form["domain"]) != 1 {
w.WriteHeader(400)
w.Write([]byte("Invalid domain parameter"))
return
}
domain := r.Form["domain"][0]
data := &ConfirmData{
Styles: template.CSS(stylesText),
Message: fmt.Sprintf("Are you sure you want to delete %s?", domain),
ConfirmUrl: fmt.Sprintf("/delete-tunnel?domain=%s", domain),
CancelUrl: "/",
}
tmpl.Execute(w, data)
case "/delete-tunnel":
token, err := extractToken("access_token", r)
if err != nil {
@ -97,14 +165,14 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
r.ParseForm()
if len(r.Form["host"]) != 1 {
if len(r.Form["domain"]) != 1 {
w.WriteHeader(400)
w.Write([]byte("Invalid host parameter"))
w.Write([]byte("Invalid domain parameter"))
return
}
host := r.Form["host"][0]
domain := r.Form["domain"][0]
p.tunMan.DeleteTunnel(host)
p.tunMan.DeleteTunnel(domain)
http.Redirect(w, r, "/", 307)
default:
@ -116,20 +184,9 @@ func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request)
func (p *BoringProxy) handleTunnels(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
switch r.Method {
case "POST":
p.handleCreateTunnel(w, r)
case "DELETE":
if len(query["host"]) != 1 {
w.WriteHeader(400)
w.Write([]byte("Invalid host parameter"))
return
}
host := query["host"][0]
p.tunMan.DeleteTunnel(host)
default:
w.WriteHeader(405)
w.Write([]byte("Invalid method for /tunnels"))

42
webui/confirm.tmpl Normal file
View File

@ -0,0 +1,42 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>boringproxy</title>
<link rel="icon" href="data:image/gif;base64,R0lGODlhEAAQAAAAACwAAAAAAQABAAACASgAOw==">
<style>
{{.Styles}}
.dialog {
margin: 20px;
padding: 20px;
font-size: 18px;
border: 1px solid black;
}
</style>
</head>
<body>
<main>
<div class='dialog'>
<p>
{{.Message}}
</p>
<div class='button-row'>
<a href="{{.ConfirmUrl}}">
<button class='button red-button'>Confirm</button>
</a>
<a href="{{.CancelUrl}}">
<button class='button green-button'>Cancel</button>
</a>
</div>
</div>
</main>
</body>
</html>

View File

@ -9,102 +9,7 @@
<link rel="icon" href="data:image/gif;base64,R0lGODlhEAAQAAAAACwAAAAAAQABAAACASgAOw==">
<style>
:root {
--hover-color: #ddd;
}
body {
font-family: Helvetica;
display: flex;
justify-content: center;
}
main {
max-width: 900px;
}
#menu-label {
font-size: 18px;
font-weight: bold;
display: block;
width: 100%;
border: 1px solid black;
cursor: pointer;
padding: 5px;
user-select: none;
}
#menu-label:hover {
background: var(--hover-color);
}
.menu {
display: none;
flex-direction: column;
position: absolute;
background: #fff;
border: 1px solid black;
min-width: 256px;
}
.menu-item {
border-bottom: 1px solid black;
padding: 5px;
text-decoration: none;
}
.menu-item:hover {
background: var(--hover-color);
}
.menu-item:visited {
color: #000;
}
#menu-toggle {
display: none;
}
#menu-toggle:checked ~ .menu {
display: flex;
}
.button {
background: #ccc;
border: none;
border-radius: .25em;
padding: .4em .8em;
cursor: pointer;
}
.add-button {
background: #4CAF50;
color: #fff;
}
.delete-button {
background: #f44336;
color: #fff;
}
.tunnel-list {
display: flex;
flex-direction: column;
}
.tunnel {
padding: 5px;
border-bottom: 1px solid black;
display: flex;
justify-content: space-between;
align-items: center;
}
.tunnel:hover {
background: var(--hover-color);
}
.tunnel-adder {
padding: 5px;
}
{{.Styles}}
</style>
</head>
@ -118,13 +23,13 @@
<a class='menu-item' href='/tokens'>Tokens</a>
</div>
<div class='tunnel-list'>
{{range $domain, $tunnel:= .}}
{{range $domain, $tunnel:= .Tunnels}}
<div class='tunnel'>
<div>
<a href="https://{{$domain}}">{{$domain}}</a>:{{$tunnel.TunnelPort}} -> {{$tunnel.ClientName}}:{{$tunnel.ClientPort}}
</div>
<a href="/delete-tunnel?host={{$domain}}">
<button class='button delete-button'>Delete</button>
<a href="/confirm-delete-tunnel?domain={{$domain}}">
<button class='button red-button'>Delete</button>
</a>
</div>
{{end}}
@ -137,7 +42,7 @@
<input type="text" id="client-name" name="client-name">
<label for="client-port">Client Port:</label>
<input type="text" id="client-port" name="client-port">
<button class='button add-button' type="submit">Add/Update Tunnel</button>
<button class='button green-button' type="submit">Add/Update Tunnel</button>
</form>
</div>
</div>

100
webui/styles.css Normal file
View File

@ -0,0 +1,100 @@
:root {
--hover-color: #ddd;
}
body {
font-family: Helvetica;
display: flex;
justify-content: center;
}
main {
max-width: 900px;
}
#menu-label {
font-size: 18px;
font-weight: bold;
display: block;
width: 100%;
border: 1px solid black;
cursor: pointer;
padding: 5px;
user-select: none;
}
#menu-label:hover {
background: var(--hover-color);
}
.menu {
display: none;
flex-direction: column;
position: absolute;
background: #fff;
border: 1px solid black;
min-width: 256px;
}
.menu-item {
border-bottom: 1px solid black;
padding: 5px;
text-decoration: none;
}
.menu-item:hover {
background: var(--hover-color);
}
.menu-item:visited {
color: #000;
}
#menu-toggle {
display: none;
}
#menu-toggle:checked ~ .menu {
display: flex;
}
.button {
background: #ccc;
border: none;
border-radius: .25em;
margin: .5em;
padding: .4em .8em;
cursor: pointer;
}
.green-button {
background: #4CAF50;
color: #fff;
}
.red-button {
background: #f44336;
color: #fff;
}
.button-row {
display: flex;
justify-content: center;
}
.tunnel-list {
display: flex;
flex-direction: column;
}
.tunnel {
padding: 5px;
border-bottom: 1px solid black;
display: flex;
justify-content: space-between;
align-items: center;
}
.tunnel:hover {
background: var(--hover-color);
}
.tunnel-adder {
padding: 5px;
}