http/gateway:
- First tab at integrating @krl's new index page File icons are embedded in side icons.css (QmXB7PLRWH6bCiwrGh2MrBBjNkLv3mY3JdYXCikYZSwLED contains both the icons and bootstrap) - Fix back links (..) (fixes #1365) Thanks @JasonWoof for the insight. The back links now stop a t the root hash and work for links that do and dont end with a slash. License: MIT Signed-off-by: Henry <cryptix@riseup.net>
Henry committed
Jun 20, 2015 at 00:59 UTC
0ed49dbbfb27d1f61f1df144a1ac0be97657a04f
2 files changed
+189
-51
core/corehttp/gateway_handler.go
+29
-51
@@ -27,16 +27,6 @@ const (
27
ipnsPathPrefix = "/ipns/"
28
)
29
30
-// shortcut for templating
31
-type webHandler map[string]interface{}
32
-
33
-// struct for directory listing
34
-type directoryItem struct {
35
- Size uint64
36
- Name string
37
- Path string
38
-}
39
-
30
// gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
31
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
32
type gatewayHandler struct {
@@ -50,23 +40,9 @@ func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) (*gatewayHandler
40
node: node,
41
config: conf,
42
}
53
- err := i.loadTemplate()
54
- if err != nil {
55
- return nil, err
56
- }
43
return i, nil
44
}
45
60
-// Load the directroy list template
61
-func (i *gatewayHandler) loadTemplate() error {
62
- t, err := template.New("dir").Parse(listingTemplate)
63
- if err != nil {
64
- return err
65
- }
66
- i.dirList = t
67
- return nil
68
-}
69
-
46
// TODO(cryptix): find these helpers somewhere else
47
func (i *gatewayHandler) newDagFromReader(r io.Reader) (*dag.Node, error) {
48
// TODO(cryptix): change and remove this helper once PR1136 is merged
@@ -205,14 +181,36 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
181
}
182
183
if !foundIndex {
208
- // template and return directory listing
209
- hndlr := webHandler{
210
- "listing": dirListing,
211
- "path": urlPath,
212
- }
213
-
184
if r.Method != "HEAD" {
215
- if err := i.dirList.Execute(w, hndlr); err != nil {
185
+ // construct the correct back link
186
+ // https://github.com/ipfs/go-ipfs/issues/1365
187
+ var backLink string = r.URL.Path
188
+
189
+ // don't go further up than /ipfs/$hash/
190
+ pathSplit := strings.Split(backLink, "/")
191
+ switch {
192
+ // keep backlink
193
+ case len(pathSplit) == 3: // url: /ipfs/$hash
194
+
195
+ // keep backlink
196
+ case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/
197
+
198
+ // add the correct link depending on wether the path ends with a slash
199
+ default:
200
+ if strings.HasSuffix(backLink, "/") {
201
+ backLink += "./.."
202
+ } else {
203
+ backLink += "/.."
204
+ }
205
+ }
206
+
207
+ tplData := listingTemplateData{
208
+ Listing: dirListing,
209
+ Path: urlPath,
210
+ BackLink: backLink,
211
+ }
212
+ err := listingTemplate.Execute(w, tplData)
213
+ if err != nil {
214
internalWebError(w, err)
215
return
216
}
@@ -441,23 +439,3 @@ func webErrorWithCode(w http.ResponseWriter, message string, err error, code int
439
func internalWebError(w http.ResponseWriter, err error) {
440
webErrorWithCode(w, "internalWebError", err, http.StatusInternalServerError)
441
}
444
-
445
-// Directory listing template
446
-var listingTemplate = `
447
-<!DOCTYPE html>
448
-<html>
449
- <head>
450
- <meta charset="utf-8" />
451
- <title>{{ .path }}</title>
452
- </head>
453
- <body>
454
- <h2>Index of {{ .path }}</h2>
455
- <ul>
456
- <li><a href="./..">..</a></li>
457
- {{ range .listing }}
458
- <li><a href="{{ .Path }}">{{ .Name }}</a> - {{ .Size }} bytes</li>
459
- {{ end }}
460
- </ul>
461
- </body>
462
-</html>
463
-`
core/corehttp/gateway_indexPage.go
new
+160
@@ -0,0 +1,160 @@
1
+package corehttp
2
+
3
+import (
4
+ "html/template"
5
+ "path"
6
+)
7
+
8
+// structs for directory listing
9
+type listingTemplateData struct {
10
+ Listing []directoryItem
11
+ Path string
12
+ BackLink string
13
+}
14
+
15
+type directoryItem struct {
16
+ Size uint64
17
+ Name string
18
+ Path string
19
+}
20
+
21
+// Directory listing template
22
+var listingTemplate = template.Must(template.New("dir").Funcs(template.FuncMap{"iconFromExt": iconFromExt}).Parse(`
23
+<!DOCTYPE html>
24
+<html>
25
+ <head>
26
+ <meta charset="utf-8" />
27
+ <!-- TODO: seed these - maybe like the starter ex or the webui? -->
28
+ <link rel="stylesheet" href="/ipfs/QmXB7PLRWH6bCiwrGh2MrBBjNkLv3mY3JdYXCikYZSwLED/bootstrap.min.css"/>
29
+ <!-- helper to construct this is here: https://github.com/cryptix/exp/blob/master/imgesToCSSData/convert.go -->
30
+ <link rel="stylesheet" href="/ipfs/QmXB7PLRWH6bCiwrGh2MrBBjNkLv3mY3JdYXCikYZSwLED/icons.css">
31
+ <style>
32
+ .narrow {width: 0px;}
33
+ .padding { margin: 100px;}
34
+ #header {
35
+ background: #000;
36
+ }
37
+ #logo {
38
+ height: 25px;
39
+ margin: 10px;
40
+ }
41
+ .ipfs-icon {
42
+ width:16px;
43
+ }
44
+ </style>
45
+ <title>{{ .Path }}</title>
46
+ </head>
47
+ <body>
48
+ <div id="header" class="row">
49
+ <div class="col-xs-2">
50
+ <div id="logo" class="ipfs-logo"> </div>
51
+ </div>
52
+ </div>
53
+ <br/>
54
+ <div class="col-xs-12">
55
+ <div class="panel panel-default">
56
+ <div class="panel-heading">
57
+ <strong>Index of {{ .Path }}</strong>
58
+ </div>
59
+ <table class="table table-striped">
60
+ <tr>
61
+ <td class="narrow">
62
+ <div class="ipfs-icon ipfs-_blank"> </div>
63
+ </td>
64
+ <td class="padding">
65
+ <a href="{{.BackLink}}">..</a>
66
+ </td>
67
+ <td></td>
68
+ </tr>
69
+ {{ range .Listing }}
70
+ <tr>
71
+ <td>
72
+ <div class="ipfs-icon {{iconFromExt .Name}}"> </div>
73
+ </td>
74
+ <td>
75
+ <a href="{{ .Path }}">{{ .Name }}</a>
76
+ </td>
77
+ <td>{{ .Size }} bytes</td>
78
+ </tr>
79
+ {{ end }}
80
+ </table>
81
+ </div>
82
+ </div>
83
+ </body>
84
+</html>
85
+`))
86
+
87
+// helper to guess the type/icon for it by the extension name
88
+func iconFromExt(name string) string {
89
+ ext := path.Ext(name)
90
+ _, ok := knownIcons[ext]
91
+ if !ok {
92
+ // default blank icon
93
+ return "ipfs-_blank"
94
+ }
95
+ return "ipfs-" + ext[1:] // slice of the first dot
96
+}
97
+
98
+var knownIcons = map[string]bool{
99
+ ".aac": true,
100
+ ".aiff": true,
101
+ ".ai": true,
102
+ ".avi": true,
103
+ ".bmp": true,
104
+ ".c": true,
105
+ ".cpp": true,
106
+ ".css": true,
107
+ ".dat": true,
108
+ ".dmg": true,
109
+ ".doc": true,
110
+ ".dotx": true,
111
+ ".dwg": true,
112
+ ".dxf": true,
113
+ ".eps": true,
114
+ ".exe": true,
115
+ ".flv": true,
116
+ ".gif": true,
117
+ ".h": true,
118
+ ".hpp": true,
119
+ ".html": true,
120
+ ".ics": true,
121
+ ".iso": true,
122
+ ".java": true,
123
+ ".jpg": true,
124
+ ".js": true,
125
+ ".key": true,
126
+ ".less": true,
127
+ ".mid": true,
128
+ ".mp3": true,
129
+ ".mp4": true,
130
+ ".mpg": true,
131
+ ".odf": true,
132
+ ".ods": true,
133
+ ".odt": true,
134
+ ".otp": true,
135
+ ".ots": true,
136
+ ".ott": true,
137
+ ".pdf": true,
138
+ ".php": true,
139
+ ".png": true,
140
+ ".ppt": true,
141
+ ".psd": true,
142
+ ".py": true,
143
+ ".qt": true,
144
+ ".rar": true,
145
+ ".rb": true,
146
+ ".rtf": true,
147
+ ".sass": true,
148
+ ".scss": true,
149
+ ".sql": true,
150
+ ".tga": true,
151
+ ".tgz": true,
152
+ ".tiff": true,
153
+ ".txt": true,
154
+ ".wav": true,
155
+ ".xls": true,
156
+ ".xlsx": true,
157
+ ".xml": true,
158
+ ".yml": true,
159
+ ".zip": true,
160
+}