| // Copyright 2017 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| package dash |
| |
| import ( |
| "bytes" |
| "fmt" |
| "html/template" |
| "net/http" |
| "time" |
| |
| "github.com/google/syzkaller/dashboard/dashapi" |
| "golang.org/x/net/context" |
| "google.golang.org/appengine" |
| "google.golang.org/appengine/log" |
| "google.golang.org/appengine/user" |
| ) |
| |
| // This file contains common middleware for UI handlers (auth, html templates, etc). |
| |
| type contextHandler func(c context.Context, w http.ResponseWriter, r *http.Request) error |
| |
| func handlerWrapper(fn contextHandler) http.Handler { |
| return handleContext(handleAuth(fn)) |
| } |
| |
| 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 { |
| data := &struct { |
| Header *uiHeader |
| Error string |
| }{ |
| Header: commonHeader(c, r), |
| Error: err.Error(), |
| } |
| if err == ErrAccess { |
| w.WriteHeader(http.StatusForbidden) |
| err1 := templates.ExecuteTemplate(w, "forbidden.html", data) |
| if err1 != nil { |
| http.Error(w, err.Error(), http.StatusInternalServerError) |
| } |
| return |
| } |
| if _, dontlog := err.(ErrDontLog); !dontlog { |
| log.Errorf(c, "%v", err) |
| } |
| w.WriteHeader(http.StatusInternalServerError) |
| if err1 := templates.ExecuteTemplate(w, "error.html", data); err1 != nil { |
| http.Error(w, err.Error(), http.StatusInternalServerError) |
| } |
| } |
| }) |
| } |
| |
| type ErrDontLog error |
| |
| func handleAuth(fn contextHandler) contextHandler { |
| return func(c context.Context, w http.ResponseWriter, r *http.Request) error { |
| if err := checkAccessLevel(c, r, config.AccessLevel); err != nil { |
| return err |
| } |
| return fn(c, w, r) |
| } |
| } |
| |
| func serveTemplate(w http.ResponseWriter, name string, data interface{}) error { |
| buf := new(bytes.Buffer) |
| if err := templates.ExecuteTemplate(buf, name, data); err != nil { |
| return err |
| } |
| w.Write(buf.Bytes()) |
| return nil |
| } |
| |
| type uiHeader struct { |
| LoginLink string |
| AnalyticsTrackingID string |
| } |
| |
| func commonHeader(c context.Context, r *http.Request) *uiHeader { |
| h := &uiHeader{ |
| AnalyticsTrackingID: config.AnalyticsTrackingID, |
| } |
| if user.Current(c) == nil { |
| h.LoginLink, _ = user.LoginURL(c, r.URL.String()) |
| } |
| return h |
| } |
| |
| func formatTime(t time.Time) string { |
| if t.IsZero() { |
| return "" |
| } |
| return t.Format("2006/01/02 15:04") |
| } |
| |
| func formatClock(t time.Time) string { |
| if t.IsZero() { |
| return "" |
| } |
| return t.Format("15:04") |
| } |
| |
| func formatDuration(d time.Duration) string { |
| if d == 0 { |
| return "" |
| } |
| days := int(d / (24 * time.Hour)) |
| hours := int(d / time.Hour % 24) |
| mins := int(d / time.Minute % 60) |
| if days >= 10 { |
| return fmt.Sprintf("%vd", days) |
| } else if days != 0 { |
| return fmt.Sprintf("%vd%02vh", days, hours) |
| } else if hours != 0 { |
| return fmt.Sprintf("%vh%02vm", hours, mins) |
| } |
| return fmt.Sprintf("%vm", mins) |
| } |
| |
| func formatLateness(now, t time.Time) string { |
| if t.IsZero() { |
| return "never" |
| } |
| d := now.Sub(t) |
| if d < 5*time.Minute { |
| return "now" |
| } |
| return formatDuration(d) |
| } |
| |
| func formatReproLevel(l dashapi.ReproLevel) string { |
| switch l { |
| case ReproLevelSyz: |
| return "syz" |
| case ReproLevelC: |
| return "C" |
| default: |
| return "" |
| } |
| } |
| |
| func formatStat(v int64) string { |
| if v == 0 { |
| return "" |
| } |
| return fmt.Sprint(v) |
| } |
| |
| var ( |
| templates = template.Must(template.New("").Funcs(templateFuncs).ParseGlob("*.html")) |
| |
| templateFuncs = template.FuncMap{ |
| "formatTime": formatTime, |
| "formatClock": formatClock, |
| "formatDuration": formatDuration, |
| "formatLateness": formatLateness, |
| "formatReproLevel": formatReproLevel, |
| "formatStat": formatStat, |
| } |
| ) |