-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nicer index #1406
Nicer index #1406
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ package corehttp | |
import ( | ||
"errors" | ||
"fmt" | ||
"html/template" | ||
"io" | ||
"net/http" | ||
gopath "path" | ||
|
@@ -27,46 +26,21 @@ 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/<path>) | ||
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link) | ||
type gatewayHandler struct { | ||
node *core.IpfsNode | ||
dirList *template.Template | ||
config GatewayConfig | ||
node *core.IpfsNode | ||
config GatewayConfig | ||
} | ||
|
||
func newGatewayHandler(node *core.IpfsNode, conf GatewayConfig) (*gatewayHandler, error) { | ||
i := &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 +179,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 += "/.." | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
tplData := listingTemplateData{ | ||
Listing: dirListing, | ||
Path: urlPath, | ||
BackLink: backLink, | ||
} | ||
err := listingTemplate.Execute(w, tplData) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
|
@@ -441,23 +437,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 = ` | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<title>{{ .path }}</title> | ||
</head> | ||
<body> | ||
<h2>Index of {{ .path }}</h2> | ||
<ul> | ||
<li><a href="./..">..</a></li> | ||
{{ range .listing }} | ||
<li><a href="{{ .Path }}">{{ .Name }}</a> - {{ .Size }} bytes</li> | ||
{{ end }} | ||
</ul> | ||
</body> | ||
</html> | ||
` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(` | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<!-- TODO: seed these - maybe like the starter ex or the webui? --> | ||
<link rel="stylesheet" href="/ipfs/QmXB7PLRWH6bCiwrGh2MrBBjNkLv3mY3JdYXCikYZSwLED/bootstrap.min.css"/> | ||
<!-- helper to construct this is here: https://github.com/cryptix/exp/blob/master/imgesToCSSData/convert.go --> | ||
<link rel="stylesheet" href="/ipfs/QmXB7PLRWH6bCiwrGh2MrBBjNkLv3mY3JdYXCikYZSwLED/icons.css"> | ||
<style> | ||
.narrow {width: 0px;} | ||
.padding { margin: 100px;} | ||
#header { | ||
background: #000; | ||
} | ||
#logo { | ||
height: 25px; | ||
margin: 10px; | ||
} | ||
.ipfs-icon { | ||
width:16px; | ||
} | ||
</style> | ||
<title>{{ .Path }}</title> | ||
</head> | ||
<body> | ||
<div id="header" class="row"> | ||
<div class="col-xs-2"> | ||
<div id="logo" class="ipfs-logo"> </div> | ||
</div> | ||
</div> | ||
<br/> | ||
<div class="col-xs-12"> | ||
<div class="panel panel-default"> | ||
<div class="panel-heading"> | ||
<strong>Index of {{ .Path }}</strong> | ||
</div> | ||
<table class="table table-striped"> | ||
<tr> | ||
<td class="narrow"> | ||
<div class="ipfs-icon ipfs-_blank"> </div> | ||
</td> | ||
<td class="padding"> | ||
<a href="{{.BackLink}}">..</a> | ||
</td> | ||
<td></td> | ||
</tr> | ||
{{ range .Listing }} | ||
<tr> | ||
<td> | ||
<div class="ipfs-icon {{iconFromExt .Name}}"> </div> | ||
</td> | ||
<td> | ||
<a href="{{ .Path }}">{{ .Name }}</a> | ||
</td> | ||
<td>{{ .Size }} bytes</td> | ||
</tr> | ||
{{ end }} | ||
</table> | ||
</div> | ||
</div> | ||
</body> | ||
</html> | ||
`)) | ||
|
||
// 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{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sure there is a nicer way to do this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. go generate too maybe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are the icons coming from a font? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we should use font-awesome here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh you already put them all in the css 👍 wfm. |
||
".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, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only the default case here is doing something. It could be turned into a large
if !(caseA) && !(caseB) {}
but I disliked the readability.