Implement loading screen for slow requests

This commit is contained in:
Anders Pitman 2020-10-16 11:21:41 -06:00
parent 9aafa18254
commit cca211de0e
2 changed files with 110 additions and 20 deletions

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"github.com/GeertJohan/go.rice" "github.com/GeertJohan/go.rice"
"html/template" "html/template"
@ -9,16 +8,20 @@ import (
"log" "log"
"net/http" "net/http"
"strings" "strings"
"sync"
"time"
) )
type WebUiHandler struct { type WebUiHandler struct {
config *BoringProxyConfig config *BoringProxyConfig
db *Database db *Database
api *Api api *Api
auth *Auth auth *Auth
tunMan *TunnelManager tunMan *TunnelManager
box *rice.Box box *rice.Box
headHtml template.HTML headHtml template.HTML
pendingRequests map[string]chan string
mutex *sync.Mutex
} }
type IndexData struct { type IndexData struct {
@ -41,6 +44,11 @@ type ConfirmData struct {
CancelUrl string CancelUrl string
} }
type LoadingData struct {
Head template.HTML
TargetUrl string
}
type AlertData struct { type AlertData struct {
Head template.HTML Head template.HTML
Message string Message string
@ -76,11 +84,13 @@ type TokensData struct {
func NewWebUiHandler(config *BoringProxyConfig, db *Database, api *Api, auth *Auth, tunMan *TunnelManager) *WebUiHandler { func NewWebUiHandler(config *BoringProxyConfig, db *Database, api *Api, auth *Auth, tunMan *TunnelManager) *WebUiHandler {
return &WebUiHandler{ return &WebUiHandler{
config: config, config: config,
db: db, db: db,
api: api, api: api,
auth: auth, auth: auth,
tunMan: tunMan, tunMan: tunMan,
pendingRequests: make(map[string]chan string),
mutex: &sync.Mutex{},
} }
} }
@ -291,6 +301,8 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
cookie := &http.Cookie{Name: "access_token", Value: "", Secure: true, HttpOnly: true} cookie := &http.Cookie{Name: "access_token", Value: "", Secure: true, HttpOnly: true}
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
http.Redirect(w, r, "/#/tunnels", 303) http.Redirect(w, r, "/#/tunnels", 303)
case "/loading":
h.handleLoading(w, r)
default: default:
w.WriteHeader(404) w.WriteHeader(404)
h.alertDialog(w, r, "Unknown page "+r.URL.Path, "/#/tunnels") h.alertDialog(w, r, "Unknown page "+r.URL.Path, "/#/tunnels")
@ -377,16 +389,57 @@ func (h *WebUiHandler) handleTunnels(w http.ResponseWriter, r *http.Request, tok
func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request, tokenData TokenData) { func (h *WebUiHandler) handleCreateTunnel(w http.ResponseWriter, r *http.Request, tokenData TokenData) {
r.ParseForm() pendingId, err := genRandomCode(16)
_, err := h.api.CreateTunnel(tokenData, r.Form)
if err != nil { if err != nil {
w.WriteHeader(400) w.WriteHeader(400)
h.alertDialog(w, r, err.Error(), "/#/tunnels") h.alertDialog(w, r, err.Error(), "/#/tunnels")
return
} }
http.Redirect(w, r, "/#/tunnels", 303) doneSignal := make(chan string)
h.mutex.Lock()
h.pendingRequests[pendingId] = doneSignal
h.mutex.Unlock()
go func() {
r.ParseForm()
_, err := h.api.CreateTunnel(tokenData, r.Form)
if err != nil {
w.WriteHeader(400)
h.alertDialog(w, r, err.Error(), "/#/tunnels")
}
doneSignal <- "/#/tunnels"
}()
timeout := make(chan bool, 1)
go func() {
time.Sleep(100 * time.Millisecond)
timeout <- true
}()
select {
case <-timeout:
url := fmt.Sprintf("/loading?id=%s", pendingId)
tmpl, err := h.loadTemplate("loading.tmpl")
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/#/tunnels")
return
}
data := &LoadingData{
Head: h.headHtml,
TargetUrl: url,
}
tmpl.Execute(w, data)
case <-doneSignal:
http.Redirect(w, r, "/#/tunnels", 303)
}
} }
func (h *WebUiHandler) sendLoginPage(w http.ResponseWriter, r *http.Request, code int) { func (h *WebUiHandler) sendLoginPage(w http.ResponseWriter, r *http.Request, code int) {
@ -555,16 +608,36 @@ func (h *WebUiHandler) alertDialog(w http.ResponseWriter, r *http.Request, messa
return nil return nil
} }
func (h *WebUiHandler) handleLoading(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(405)
h.alertDialog(w, r, "Invalid method for users", "/#/tunnels")
}
r.ParseForm()
pendingId := r.Form.Get("id")
h.mutex.Lock()
doneSignal := h.pendingRequests[pendingId]
h.mutex.Unlock()
redirUrl := <-doneSignal
http.Redirect(w, r, redirUrl, 303)
}
func (h *WebUiHandler) loadTemplate(name string) (*template.Template, error) { func (h *WebUiHandler) loadTemplate(name string) (*template.Template, error) {
tmplStr, err := h.box.String(name) tmplStr, err := h.box.String(name)
if err != nil { if err != nil {
return nil, errors.New("Error reading template " + name) return nil, err
} }
tmpl, err := template.New(name).Parse(tmplStr) tmpl, err := template.New(name).Parse(tmplStr)
if err != nil { if err != nil {
return nil, errors.New("Error compiling template " + name) return nil, err
} }
return tmpl, nil return tmpl, nil

17
webui/loading.tmpl Normal file
View File

@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<meta http-equiv="refresh" content="0; URL='{{$.TargetUrl}}'" />
{{.Head}}
</head>
<body>
<main>
<div class='dialog'>
<p>
Loading...
</p>
</div>
</main>
</body>
</html>