forked from harness/harness
-
Notifications
You must be signed in to change notification settings - Fork 0
/
web.go
127 lines (108 loc) · 2.61 KB
/
web.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package web
import (
"context"
"crypto/md5"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"path/filepath"
"time"
"github.com/drone/drone-ui/dist"
"github.com/drone/drone/model"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/version"
"github.com/dimfeld/httptreemux"
)
// Endpoint provides the website endpoints.
type Endpoint interface {
// Register registers the provider endpoints.
Register(*httptreemux.ContextMux)
}
// New returns the default website endpoint.
func New(opt ...Option) Endpoint {
opts := new(Options)
for _, f := range opt {
f(opts)
}
if opts.path != "" {
return fromPath(opts)
}
return &website{
fs: dist.New(),
opts: opts,
tmpl: mustCreateTemplate(
string(dist.MustLookup("/index.html")),
),
}
}
func fromPath(opts *Options) *website {
f := filepath.Join(opts.path, "index.html")
b, err := ioutil.ReadFile(f)
if err != nil {
panic(err)
}
return &website{
fs: http.Dir(opts.path),
tmpl: mustCreateTemplate(string(b)),
opts: opts,
}
}
type website struct {
opts *Options
fs http.FileSystem
tmpl *template.Template
}
func (w *website) Register(mux *httptreemux.ContextMux) {
h := http.FileServer(w.fs)
h = setupCache(h)
mux.Handler("GET", "/favicon.png", h)
mux.Handler("GET", "/static/*filepath", h)
mux.NotFoundHandler = w.handleIndex
}
func (w *website) handleIndex(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(200)
var csrf string
var user, _ = ToUser(r.Context())
if user != nil {
csrf, _ = token.New(
token.CsrfToken,
user.Login,
).Sign(user.Hash)
}
var syncing bool
if user != nil {
syncing = time.Unix(user.Synced, 0).Add(w.opts.sync).Before(time.Now())
}
params := map[string]interface{}{
"user": user,
"csrf": csrf,
"syncing": syncing,
"version": version.Version.String(),
}
rw.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.tmpl.Execute(rw, params)
}
func setupCache(h http.Handler) http.Handler {
data := []byte(time.Now().String())
etag := fmt.Sprintf("%x", md5.Sum(data))
return http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=31536000")
w.Header().Del("Expires")
w.Header().Set("ETag", etag)
h.ServeHTTP(w, r)
},
)
}
// WithUser returns a context with the current authenticated user.
func WithUser(c context.Context, user *model.User) context.Context {
return context.WithValue(c, userKey, user)
}
// ToUser returns a user from the context.
func ToUser(c context.Context) (*model.User, bool) {
user, ok := c.Value(userKey).(*model.User)
return user, ok
}
type key int
const userKey key = 0