Skip to content

Commit ee83862

Browse files
committed
Merge pull request #14 from github/mgmt
Management UI updates
2 parents d86d966 + bd10524 commit ee83862

16 files changed

+344
-58
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
dist
2+
lfs-test-server
23
lfs-test-server-out.*
34
lfs-test-server-release.*

CONTRIBUTING.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ would like to make that are not dependent upon each other, consider submitting
2323
them as separate pull requests.
2424
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
2525

26+
## Updating management templates
27+
0. Install go.rice with `go install github.com/GeertJohan/go.rice`
28+
0. Run `rice embed-go` after all template changes have been made
29+
0. Commit the change
30+
0. Submit a pull request.
31+
2632
## Updating 3rd party packages
2733

2834
0. Run `godep save` to add new dependencies.

content_store.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ func NewContentStore(base string) (*ContentStore, error) {
3030

3131
// Get takes a Meta object and retreives the content from the store, returning
3232
// it as an io.Reader.
33-
func (s *ContentStore) Get(meta *Meta) (io.Reader, error) {
33+
func (s *ContentStore) Get(meta *MetaObject) (io.Reader, error) {
3434
path := filepath.Join(s.basePath, transformKey(meta.Oid))
3535

3636
return os.Open(path)
3737
}
3838

3939
// Put takes a Meta object and an io.Reader and writes the content to the store.
40-
func (s *ContentStore) Put(meta *Meta, r io.Reader) error {
40+
func (s *ContentStore) Put(meta *MetaObject, r io.Reader) error {
4141
path := filepath.Join(s.basePath, transformKey(meta.Oid))
4242
tmpPath := path + ".tmp"
4343

@@ -78,7 +78,7 @@ func (s *ContentStore) Put(meta *Meta, r io.Reader) error {
7878
}
7979

8080
// Exists returns true if the object exists in the content store.
81-
func (s *ContentStore) Exists(meta *Meta) bool {
81+
func (s *ContentStore) Exists(meta *MetaObject) bool {
8282
path := filepath.Join(s.basePath, transformKey(meta.Oid))
8383
if _, err := os.Stat(path); os.IsNotExist(err) {
8484
return false

content_store_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestContenStorePut(t *testing.T) {
1414
setup()
1515
defer teardown()
1616

17-
m := &Meta{
17+
m := &MetaObject{
1818
Oid: "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72",
1919
Size: 12,
2020
}
@@ -35,7 +35,7 @@ func TestContenStorePutHashMismatch(t *testing.T) {
3535
setup()
3636
defer teardown()
3737

38-
m := &Meta{
38+
m := &MetaObject{
3939
Oid: "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72",
4040
Size: 12,
4141
}
@@ -56,7 +56,7 @@ func TestContenStorePutSizeMismatch(t *testing.T) {
5656
setup()
5757
defer teardown()
5858

59-
m := &Meta{
59+
m := &MetaObject{
6060
Oid: "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72",
6161
Size: 14,
6262
}
@@ -77,7 +77,7 @@ func TestContenStoreGet(t *testing.T) {
7777
setup()
7878
defer teardown()
7979

80-
m := &Meta{
80+
m := &MetaObject{
8181
Oid: "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72",
8282
Size: 12,
8383
}
@@ -103,7 +103,7 @@ func TestContenStoreGetNonExisting(t *testing.T) {
103103
setup()
104104
defer teardown()
105105

106-
_, err := contentStore.Get(&Meta{Oid: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"})
106+
_, err := contentStore.Get(&MetaObject{Oid: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"})
107107
if err == nil {
108108
t.Fatalf("expected to get an error, but content existed")
109109
}
@@ -113,7 +113,7 @@ func TestContenStoreExists(t *testing.T) {
113113
setup()
114114
defer teardown()
115115

116-
m := &Meta{
116+
m := &MetaObject{
117117
Oid: "6ae8a75555209fd6c44157c0aed8016e763ff435a19cf186f76863140143ff72",
118118
Size: 12,
119119
}

harbour_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func TestPut(t *testing.T) {
240240
t.Fatalf("expected status 200, got %d", res.StatusCode)
241241
}
242242

243-
r, err := testContentStore.Get(&Meta{Oid: contentOid})
243+
r, err := testContentStore.Get(&MetaObject{Oid: contentOid})
244244
if err != nil {
245245
t.Fatalf("error retreiving from content store: %s", err)
246246
}
@@ -361,7 +361,7 @@ func seedMetaStore() error {
361361
}
362362

363363
func seedContentStore() error {
364-
meta := &Meta{Oid: contentOid, Size: contentSize}
364+
meta := &MetaObject{Oid: contentOid, Size: contentSize}
365365
buf := bytes.NewBuffer([]byte(content))
366366
if err := testContentStore.Put(meta, buf); err != nil {
367367
return err

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
const (
1212
contentMediaType = "application/vnd.git-lfs"
1313
metaMediaType = contentMediaType + "+json"
14-
version = "0.1.0"
14+
version = "0.1.1"
1515
)
1616

1717
var (

meta_store.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ func NewMetaStore(dbFile string) (*MetaStore, error) {
5151

5252
// Get retrieves the Meta information for an object given information in
5353
// RequestVars
54-
func (s *MetaStore) Get(v *RequestVars) (*Meta, error) {
54+
func (s *MetaStore) Get(v *RequestVars) (*MetaObject, error) {
5555
if !s.authenticate(v.Authorization) {
5656
return nil, newAuthError()
5757
}
5858

59-
var meta Meta
59+
var meta MetaObject
6060
var value []byte
6161

6262
err := s.db.View(func(tx *bolt.Tx) error {
@@ -87,7 +87,7 @@ func (s *MetaStore) Get(v *RequestVars) (*Meta, error) {
8787
}
8888

8989
// Put writes meta information from RequestVars to the store.
90-
func (s *MetaStore) Put(v *RequestVars) (*Meta, error) {
90+
func (s *MetaStore) Put(v *RequestVars) (*MetaObject, error) {
9191
if !s.authenticate(v.Authorization) {
9292
return nil, newAuthError()
9393
}
@@ -100,7 +100,7 @@ func (s *MetaStore) Put(v *RequestVars) (*Meta, error) {
100100

101101
var buf bytes.Buffer
102102
enc := gob.NewEncoder(&buf)
103-
meta := Meta{Oid: v.Oid, Size: v.Size}
103+
meta := MetaObject{Oid: v.Oid, Size: v.Size}
104104
err := enc.Encode(meta)
105105
if err != nil {
106106
return nil, err
@@ -190,6 +190,32 @@ func (s *MetaStore) Users() ([]*MetaUser, error) {
190190
return users, err
191191
}
192192

193+
// Objects returns all MetaObjects in the meta store
194+
func (s *MetaStore) Objects() ([]*MetaObject, error) {
195+
var objects []*MetaObject
196+
197+
err := s.db.View(func(tx *bolt.Tx) error {
198+
bucket := tx.Bucket(objectsBucket)
199+
if bucket == nil {
200+
return errNoBucket
201+
}
202+
203+
bucket.ForEach(func(k, v []byte) error {
204+
var meta MetaObject
205+
dec := gob.NewDecoder(bytes.NewBuffer(v))
206+
err := dec.Decode(&meta)
207+
if err != nil {
208+
return err
209+
}
210+
objects = append(objects, &meta)
211+
return nil
212+
})
213+
return nil
214+
})
215+
216+
return objects, err
217+
}
218+
193219
// authenticate uses the authorization string to determine whether
194220
// or not to proceed. This server assumes an HTTP Basic auth format.
195221
func (s *MetaStore) authenticate(authorization string) bool {

mgmt-css.rice-box.go

Lines changed: 41 additions & 0 deletions
Large diffs are not rendered by default.

mgmt-templates.rice-box.go

Lines changed: 90 additions & 0 deletions
Large diffs are not rendered by default.

mgmt.go

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,68 @@ import (
44
"errors"
55
"fmt"
66
"html/template"
7+
"io"
78
"net/http"
89

10+
"github.com/GeertJohan/go.rice"
911
"github.com/gorilla/mux"
1012
)
1113

14+
var (
15+
cssBox *rice.Box
16+
templateBox *rice.Box
17+
)
18+
19+
type pageData struct {
20+
Name string
21+
Config *Configuration
22+
Users []*MetaUser
23+
Objects []*MetaObject
24+
}
25+
1226
func (a *App) addMgmt(r *mux.Router) {
1327
r.HandleFunc("/mgmt", basicAuth(a.indexHandler)).Methods("GET")
28+
r.HandleFunc("/mgmt/objects", basicAuth(a.objectsHandler)).Methods("GET")
29+
r.HandleFunc("/mgmt/users", basicAuth(a.usersHandler)).Methods("GET")
1430
r.HandleFunc("/mgmt/add", basicAuth(a.addUserHandler)).Methods("POST")
1531
r.HandleFunc("/mgmt/del", basicAuth(a.delUserHandler)).Methods("POST")
32+
33+
cssBox = rice.MustFindBox("mgmt/css")
34+
templateBox = rice.MustFindBox("mgmt/templates")
35+
r.HandleFunc("/mgmt/css/{file}", basicAuth(cssHandler))
36+
}
37+
38+
func cssHandler(w http.ResponseWriter, r *http.Request) {
39+
file := mux.Vars(r)["file"]
40+
f, err := cssBox.Open(file)
41+
if err != nil {
42+
writeStatus(w, r, 404)
43+
return
44+
}
45+
46+
w.Header().Set("Content-Type", "text/css")
47+
48+
io.Copy(w, f)
49+
f.Close()
1650
}
1751

1852
func basicAuth(h http.HandlerFunc) http.HandlerFunc {
1953
return func(w http.ResponseWriter, r *http.Request) {
2054
if Config.AdminUser == "" || Config.AdminPass == "" {
21-
http.Error(w, "Not Found", 404)
22-
logRequest(r, 404)
55+
writeStatus(w, r, 404)
2356
return
2457
}
2558

2659
user, pass, ok := r.BasicAuth()
2760
if !ok {
2861
w.Header().Set("WWW-Authenticate", "Basic realm=mgmt")
29-
http.Error(w, "authorization failed", 401)
30-
logRequest(r, 401)
62+
writeStatus(w, r, 401)
3163
return
3264
}
3365

3466
if user != Config.AdminUser || pass != Config.AdminPass {
3567
w.Header().Set("WWW-Authenticate", "Basic realm=mgmt")
36-
http.Error(w, "authorization failed", 401)
37-
logRequest(r, 401)
68+
writeStatus(w, r, 401)
3869
return
3970
}
4071

@@ -44,22 +75,33 @@ func basicAuth(h http.HandlerFunc) http.HandlerFunc {
4475
}
4576

4677
func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) {
47-
t := template.Must(template.New("main").Parse(bodyTemplate))
48-
t.New("body").Parse(indexTemplate)
78+
if err := render(w, "config.tmpl", pageData{Name: "index", Config: Config}); err != nil {
79+
writeStatus(w, r, 404)
80+
}
81+
}
4982

50-
type lfs struct {
51-
Users []*MetaUser
83+
func (a *App) objectsHandler(w http.ResponseWriter, r *http.Request) {
84+
objects, err := a.metaStore.Objects()
85+
if err != nil {
86+
fmt.Fprintf(w, "Error retrieving objects: %s", err)
87+
return
5288
}
5389

90+
if err := render(w, "objects.tmpl", pageData{Name: "objects", Objects: objects}); err != nil {
91+
writeStatus(w, r, 404)
92+
}
93+
}
94+
95+
func (a *App) usersHandler(w http.ResponseWriter, r *http.Request) {
5496
users, err := a.metaStore.Users()
5597
if err != nil {
5698
fmt.Fprintf(w, "Error retrieving users: %s", err)
5799
return
58100
}
59101

60-
l := &lfs{Users: users}
61-
62-
t.Execute(w, l)
102+
if err := render(w, "users.tmpl", pageData{Name: "users", Users: users}); err != nil {
103+
writeStatus(w, r, 404)
104+
}
63105
}
64106

65107
func (a *App) addUserHandler(w http.ResponseWriter, r *http.Request) {
@@ -75,7 +117,7 @@ func (a *App) addUserHandler(w http.ResponseWriter, r *http.Request) {
75117
return
76118
}
77119

78-
http.Redirect(w, r, "/mgmt", 302)
120+
http.Redirect(w, r, "/mgmt/users", 302)
79121
}
80122

81123
func (a *App) delUserHandler(w http.ResponseWriter, r *http.Request) {
@@ -90,7 +132,24 @@ func (a *App) delUserHandler(w http.ResponseWriter, r *http.Request) {
90132
return
91133
}
92134

93-
http.Redirect(w, r, "/mgmt", 302)
135+
http.Redirect(w, r, "/mgmt/users", 302)
136+
}
137+
138+
func render(w http.ResponseWriter, tmpl string, data pageData) error {
139+
bodyString, err := templateBox.String("body.tmpl")
140+
if err != nil {
141+
return err
142+
}
143+
144+
contentString, err := templateBox.String(tmpl)
145+
if err != nil {
146+
return err
147+
}
148+
149+
t := template.Must(template.New("main").Parse(bodyString))
150+
t.New("content").Parse(contentString)
151+
152+
return t.Execute(w, data)
94153
}
95154

96155
func authenticate(r *http.Request) error {
@@ -110,27 +169,3 @@ func authenticate(r *http.Request) error {
110169
}
111170
return err
112171
}
113-
114-
var bodyTemplate = `<html>
115-
<head>
116-
<title>Git LFS Server Management</title>
117-
</head>
118-
<body>
119-
{{template "body" .}}
120-
</body>
121-
</html>
122-
`
123-
124-
var indexTemplate = `
125-
<h2>Users</h2>
126-
{{range .Users}}
127-
<div>{{.Name}} <form method="POST" action="/mgmt/del"><input type="hidden" name="name" value="{{.Name}}"/><input type="submit" value="Delete"/></form></div>
128-
{{end}}
129-
130-
<form method="POST" action="/mgmt/add">
131-
<label id="name">Name:</label>
132-
<input type="text" name="name" />
133-
<input type="password" name="password" />
134-
<input type="submit" value="Add User" />
135-
</form>
136-
`

0 commit comments

Comments
 (0)