From 0ed49dbbfb27d1f61f1df144a1ac0be97657a04f Mon Sep 17 00:00:00 2001 From: Henry Date: Sat, 20 Jun 2015 00:59:48 +0200 Subject: [PATCH] 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 --- core/corehttp/gateway_handler.go | 80 ++++++--------- core/corehttp/gateway_indexPage.go | 160 +++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 core/corehttp/gateway_indexPage.go diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index 9b66cd13a..11d353a23 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -27,16 +27,6 @@ const ( ipnsPathPrefix = "/ipns/" ) -// shortcut for templating -type webHandler map[string]interface{} - -// struct for directory listing -type directoryItem struct { - Size uint64 - Name string - Path string -} - // gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/) // (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link) type gatewayHandler struct { @@ -50,23 +40,9 @@ func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) (*gatewayHandler node: node, config: conf, } - err := i.loadTemplate() - if err != nil { - return nil, err - } return i, nil } -// Load the directroy list template -func (i *gatewayHandler) loadTemplate() error { - t, err := template.New("dir").Parse(listingTemplate) - if err != nil { - return err - } - i.dirList = t - return nil -} - // TODO(cryptix): find these helpers somewhere else func (i *gatewayHandler) newDagFromReader(r io.Reader) (*dag.Node, error) { // 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 } if !foundIndex { - // template and return directory listing - hndlr := webHandler{ - "listing": dirListing, - "path": urlPath, - } - if r.Method != "HEAD" { - if err := i.dirList.Execute(w, hndlr); err != nil { + // construct the correct back link + // https://github.com/ipfs/go-ipfs/issues/1365 + var backLink string = r.URL.Path + + // don't go further up than /ipfs/$hash/ + pathSplit := strings.Split(backLink, "/") + switch { + // keep backlink + case len(pathSplit) == 3: // url: /ipfs/$hash + + // keep backlink + case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/ + + // add the correct link depending on wether the path ends with a slash + default: + if strings.HasSuffix(backLink, "/") { + backLink += "./.." + } else { + backLink += "/.." + } + } + + tplData := listingTemplateData{ + Listing: dirListing, + Path: urlPath, + BackLink: backLink, + } + err := listingTemplate.Execute(w, tplData) + if err != nil { internalWebError(w, err) return } @@ -441,23 +439,3 @@ func webErrorWithCode(w http.ResponseWriter, message string, err error, code int func internalWebError(w http.ResponseWriter, err error) { webErrorWithCode(w, "internalWebError", err, http.StatusInternalServerError) } - -// Directory listing template -var listingTemplate = ` - - - - - {{ .path }} - - -

Index of {{ .path }}

-
    -
  • ..
  • - {{ range .listing }} -
  • {{ .Name }} - {{ .Size }} bytes
  • - {{ end }} -
- - -` diff --git a/core/corehttp/gateway_indexPage.go b/core/corehttp/gateway_indexPage.go new file mode 100644 index 000000000..242e1ac20 --- /dev/null +++ b/core/corehttp/gateway_indexPage.go @@ -0,0 +1,160 @@ +package corehttp + +import ( + "html/template" + "path" +) + +// structs for directory listing +type listingTemplateData struct { + Listing []directoryItem + Path string + BackLink string +} + +type directoryItem struct { + Size uint64 + Name string + Path string +} + +// Directory listing template +var listingTemplate = template.Must(template.New("dir").Funcs(template.FuncMap{"iconFromExt": iconFromExt}).Parse(` + + + + + + + + + + {{ .Path }} + + + +
+
+
+
+ Index of {{ .Path }} +
+ + + + + + + {{ range .Listing }} + + + + + + {{ end }} +
+
 
+
+ .. +
+
 
+
+ {{ .Name }} + {{ .Size }} bytes
+
+
+ + +`)) + +// helper to guess the type/icon for it by the extension name +func iconFromExt(name string) string { + ext := path.Ext(name) + _, ok := knownIcons[ext] + if !ok { + // default blank icon + return "ipfs-_blank" + } + return "ipfs-" + ext[1:] // slice of the first dot +} + +var knownIcons = map[string]bool{ + ".aac": true, + ".aiff": true, + ".ai": true, + ".avi": true, + ".bmp": true, + ".c": true, + ".cpp": true, + ".css": true, + ".dat": true, + ".dmg": true, + ".doc": true, + ".dotx": true, + ".dwg": true, + ".dxf": true, + ".eps": true, + ".exe": true, + ".flv": true, + ".gif": true, + ".h": true, + ".hpp": true, + ".html": true, + ".ics": true, + ".iso": true, + ".java": true, + ".jpg": true, + ".js": true, + ".key": true, + ".less": true, + ".mid": true, + ".mp3": true, + ".mp4": true, + ".mpg": true, + ".odf": true, + ".ods": true, + ".odt": true, + ".otp": true, + ".ots": true, + ".ott": true, + ".pdf": true, + ".php": true, + ".png": true, + ".ppt": true, + ".psd": true, + ".py": true, + ".qt": true, + ".rar": true, + ".rb": true, + ".rtf": true, + ".sass": true, + ".scss": true, + ".sql": true, + ".tga": true, + ".tgz": true, + ".tiff": true, + ".txt": true, + ".wav": true, + ".xls": true, + ".xlsx": true, + ".xml": true, + ".yml": true, + ".zip": true, +}