aboutsummaryrefslogtreecommitdiffstats
path: root/dashboard/app/handler.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-04-05 17:59:52 +0200
committerDmitry Vyukov <dvyukov@google.com>2019-04-08 14:32:32 +0200
commit3ef496b7ba14f5099f3d09f9da0e931411c2afc0 (patch)
treea8be358be459834235232c96bbb6bdd9d0a2e1ba /dashboard/app/handler.go
parentc34fde03ec2b778c7cb3f4463dac2e6b9c7934c9 (diff)
dashboard/app: split dashboard per-namespace
We now have too many namespaces and bugs. Main page takes infinity to load. Also almost nobody is interested in more than 1 namespace. So split main page per-namespaces.
Diffstat (limited to 'dashboard/app/handler.go')
-rw-r--r--dashboard/app/handler.go142
1 files changed, 137 insertions, 5 deletions
diff --git a/dashboard/app/handler.go b/dashboard/app/handler.go
index 7fc133bee..8e2d66ad7 100644
--- a/dashboard/app/handler.go
+++ b/dashboard/app/handler.go
@@ -5,7 +5,12 @@ package dash
import (
"bytes"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
"net/http"
+ "sort"
+ "strings"
"github.com/google/syzkaller/pkg/html"
"golang.org/x/net/context"
@@ -26,11 +31,12 @@ func handleContext(fn contextHandler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
if err := fn(c, w, r); err != nil {
+ hdr, _ := commonHeader(c, r, w, "")
data := &struct {
Header *uiHeader
Error string
}{
- Header: commonHeader(c, r),
+ Header: hdr,
Error: err.Error(),
}
if err == ErrAccess {
@@ -41,6 +47,10 @@ func handleContext(fn contextHandler) http.Handler {
}
return
}
+ if redir, ok := err.(ErrRedirect); ok {
+ http.Redirect(w, r, redir.Error(), http.StatusMovedPermanently)
+ return
+ }
if _, dontlog := err.(ErrDontLog); !dontlog {
log.Errorf(c, "%v", err)
}
@@ -52,7 +62,10 @@ func handleContext(fn contextHandler) http.Handler {
})
}
-type ErrDontLog error
+type (
+ ErrDontLog error
+ ErrRedirect error
+)
func handleAuth(fn contextHandler) contextHandler {
return func(c context.Context, w http.ResponseWriter, r *http.Request) error {
@@ -76,17 +89,136 @@ type uiHeader struct {
Admin bool
LoginLink string
AnalyticsTrackingID string
+ Subpage string
+ Namespace string
+ Namespaces []uiNamespace
+ Redirects []uiRedirect
+}
+
+type uiNamespace struct {
+ Name string
+ Caption string
+}
+
+type uiRedirect struct {
+ From string
+ To string
+}
+
+type cookieData struct {
+ Namespace string `json:"namespace"`
}
-func commonHeader(c context.Context, r *http.Request) *uiHeader {
+func commonHeader(c context.Context, r *http.Request, w http.ResponseWriter, ns string) (*uiHeader, error) {
+ accessLevel := accessLevel(c, r)
+ if ns == "" {
+ ns = strings.ToLower(r.URL.Path)
+ if ns != "" && ns[0] == '/' {
+ ns = ns[1:]
+ }
+ if pos := strings.IndexByte(ns, '/'); pos != -1 {
+ ns = ns[:pos]
+ }
+ }
h := &uiHeader{
- Admin: accessLevel(c, r) == AccessAdmin,
+ Admin: accessLevel == AccessAdmin,
AnalyticsTrackingID: config.AnalyticsTrackingID,
}
+ const adminPage = "admin"
+ isAdminPage := r.URL.Path == "/"+adminPage
+ isBugPage := r.URL.Path == "/bug"
+ found := false
+ for ns1, cfg := range config.Namespaces {
+ if accessLevel < cfg.AccessLevel {
+ if ns1 == ns {
+ return nil, ErrAccess
+ }
+ continue
+ }
+ if ns1 == ns {
+ found = true
+ }
+ h.Namespaces = append(h.Namespaces, uiNamespace{
+ Name: ns1,
+ Caption: cfg.DisplayTitle,
+ })
+ // This handles redirects from old URL scheme to new scheme.
+ // This this should be removed at some point (Apr 5, 2019).
+ // Also see handling of "fixed" parameter in handleMain.
+ if isBugPage {
+ continue
+ }
+ h.Redirects = append(h.Redirects, uiRedirect{
+ From: "#" + ns1,
+ To: "/" + ns1,
+ })
+ fragments := []string{"managers", "open", "pending"}
+ for _, reporting := range cfg.Reporting {
+ if !reporting.moderation || accessLevel < reporting.AccessLevel {
+ continue
+ }
+ fragments = append(fragments, reporting.Name)
+ }
+ for _, frag := range fragments {
+ h.Redirects = append(h.Redirects, uiRedirect{
+ From: "#" + ns1 + "-" + frag,
+ To: "/" + ns1 + "#" + frag,
+ })
+ }
+ }
+ sort.Slice(h.Namespaces, func(i, j int) bool {
+ return h.Namespaces[i].Caption < h.Namespaces[j].Caption
+ })
+ cookie := decodeCookie(r)
+ if !found {
+ ns = config.DefaultNamespace
+ if cfg := config.Namespaces[cookie.Namespace]; cfg != nil && cfg.AccessLevel <= accessLevel {
+ ns = cookie.Namespace
+ }
+ if accessLevel == AccessAdmin {
+ ns = adminPage
+ }
+ if ns != adminPage || !isAdminPage {
+ return nil, ErrRedirect(fmt.Errorf("/%v", ns))
+ }
+ }
+ if ns != adminPage {
+ h.Namespace = ns
+ cookie.Namespace = ns
+ encodeCookie(w, cookie)
+ }
if user.Current(c) == nil {
h.LoginLink, _ = user.LoginURL(c, r.URL.String())
}
- return h
+ return h, nil
+}
+
+const cookieName = "syzkaller"
+
+func decodeCookie(r *http.Request) *cookieData {
+ cd := new(cookieData)
+ cookie, err := r.Cookie(cookieName)
+ if err != nil {
+ return cd
+ }
+ decoded, err := base64.StdEncoding.DecodeString(cookie.Value)
+ if err != nil {
+ return cd
+ }
+ json.Unmarshal(decoded, cd)
+ return cd
+}
+
+func encodeCookie(w http.ResponseWriter, cd *cookieData) {
+ data, err := json.Marshal(cd)
+ if err != nil {
+ return
+ }
+ cookie := &http.Cookie{
+ Name: cookieName,
+ Value: base64.StdEncoding.EncodeToString(data),
+ }
+ http.SetCookie(w, cookie)
}
var templates = html.CreateGlob("*.html")