@cryptotaxi247 / kubo / commits / 0ed49dbbf

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">&nbsp;</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">&nbsp;</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}}">&nbsp;</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 +}