Skip to content

Commit

Permalink
Merge pull request #1406 from ipfs/nicerIndex
Browse files Browse the repository at this point in the history
Nicer index
  • Loading branch information
jbenet committed Jul 4, 2015
2 parents 8143d38 + 1b16712 commit 224a9eb
Show file tree
Hide file tree
Showing 11 changed files with 698 additions and 63 deletions.
29 changes: 22 additions & 7 deletions assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,54 @@ var initDocPaths = []string{
"init-doc/quick-start",
}

// SeedInitDocs adds the list of embedded init documentation to the passed node, pins it and returns the root key
func SeedInitDocs(nd *core.IpfsNode) (*key.Key, error) {
return addAssetList(nd, initDocPaths)
}

var initGwAssets = []string{
"gw-assets/icons.css",
"gw-assets/bootstrap.min.css",
}

// SeedGatewayAssets adds the list of embedded gateway inidex assets to the passed node, pins it and returns the root key
func SeedGatewayAssets(nd *core.IpfsNode) (*key.Key, error) {
return addAssetList(nd, initGwAssets)
}

func addAssetList(nd *core.IpfsNode, l []string) (*key.Key, error) {
dirb := uio.NewDirectory(nd.DAG)

for _, p := range initDocPaths {
for _, p := range l {
d, err := Asset(p)
if err != nil {
return nil, fmt.Errorf("assets.AddDocuDir: could load Asset '%s': %s", p, err)
return nil, fmt.Errorf("assets: could load Asset '%s': %s", p, err)
}

s, err := coreunix.Add(nd, bytes.NewBuffer(d))
if err != nil {
return nil, fmt.Errorf("assets.AddDocuDir: could not Add '%s': %s", p, err)
return nil, fmt.Errorf("assets: could not Add '%s': %s", p, err)
}

fname := filepath.Base(p)
k := key.B58KeyDecode(s)
if err := dirb.AddChild(fname, k); err != nil {
return nil, fmt.Errorf("assets.AddDocuDir: could not add '%s' as a child: %s", fname, err)
return nil, fmt.Errorf("assets: could not add '%s' as a child: %s", fname, err)
}
}

dir := dirb.GetNode()
dkey, err := nd.DAG.Add(dir)
if err != nil {
return nil, fmt.Errorf("assets.AddDocuDir: DAG.Add(dir) failed: %s", err)
return nil, fmt.Errorf("assets: DAG.Add(dir) failed: %s", err)
}

if err := nd.Pinning.Pin(nd.Context(), dir, true); err != nil {
return nil, fmt.Errorf("assets.AddDocuDir: Pinning on init-docu failed: %s", err)
return nil, fmt.Errorf("assets: Pinning on init-docu failed: %s", err)
}

if err := nd.Pinning.Flush(); err != nil {
return nil, fmt.Errorf("assets.AddDocuDir: Pinnig flush failed: %s", err)
return nil, fmt.Errorf("assets: Pinnig flush failed: %s", err)
}

return &dkey, nil
Expand Down
31 changes: 31 additions & 0 deletions assets/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,34 @@ func TestEmbeddedDocs(t *testing.T) {
}
wg.Wait()
}

func TestGatewayAssets(t *testing.T) {
const wantCnt = 2
if len(initGwAssets) < wantCnt {
t.Fatalf("expected %d assets. got %d", wantCnt, len(initDocPaths))
}

for _, f := range initGwAssets {
// load data from filesystem (git)
vcsData, err := ioutil.ReadFile(f)
if err != nil {
t.Errorf("asset %s: could not read vcs file: %s", f, err)
return
}

// load data from emdedded source
embdData, err := Asset(f)
if err != nil {
t.Errorf("asset %s: could not read vcs file: %s", f, err)
return
}

if !bytes.Equal(vcsData, embdData) {
t.Errorf("asset %s: vcs and embedded data isnt equal", f)
return
}

t.Logf("checked %s", f)
}

}
48 changes: 48 additions & 0 deletions assets/bindata.go

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions assets/gw-assets/bootstrap.min.css

Large diffs are not rendered by default.

384 changes: 384 additions & 0 deletions assets/gw-assets/icons.css

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions cmd/ipfs/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ func addDefaultAssets(out io.Writer, repoRoot string) error {
}
defer nd.Close()

gwAkey, err := assets.SeedGatewayAssets(nd)
if err != nil {
return fmt.Errorf("init: seeding init docs failed: %s", err)
}
log.Debugf("init: seeded gateway assets %s", gwAkey)

dkey, err := assets.SeedInitDocs(nd)
if err != nil {
return fmt.Errorf("init: seeding init docs failed: %s", err)
Expand Down
86 changes: 31 additions & 55 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package corehttp
import (
"errors"
"fmt"
"html/template"
"io"
"net/http"
gopath "path"
Expand All @@ -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
Expand Down Expand Up @@ -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 += "/.."
}
}

tplData := listingTemplateData{
Listing: dirListing,
Path: urlPath,
BackLink: backLink,
}
err := listingTemplate.Execute(w, tplData)
if err != nil {
internalWebError(w, err)
return
}
Expand Down Expand Up @@ -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>
`
160 changes: 160 additions & 0 deletions core/corehttp/gateway_indexPage.go
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">&nbsp;</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">&nbsp;</div>
</td>
<td class="padding">
<a href="{{.BackLink}}">..</a>
</td>
<td></td>
</tr>
{{ range .Listing }}
<tr>
<td>
<div class="ipfs-icon {{iconFromExt .Name}}">&nbsp;</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{
".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,
}
Loading

0 comments on commit 224a9eb

Please sign in to comment.