@cryptotaxi247 / kubo / commits / 32823d208

Static Website serving and listing

adds trailing slash redirection index.html return templated directory listing Signed off : Simon Kirkby <tigger@interthingy.com>

Simon Kirkby committed Nov 20, 2014 at 21:20 UTC 32823d2085a53820cf1995a2075dd478d3e46371
2 files changed +95 -4
cmd/ipfs/daemon.go
+3 -1
@@ -165,7 +165,9 @@ func listenAndServeAPI(node *core.IpfsNode, req cmds.Request, addr ma.Multiaddr)
165 cmdHandler := cmdsHttp.NewHandler(*req.Context(), commands.Root, origin)
166 mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)
167
168 - ifpsHandler := &ipfsHandler{node}
168 + ifpsHandler := &ipfsHandler{node: node}
169 + ifpsHandler.LoadTemplate()
170 +
171 mux.Handle("/ipfs/", ifpsHandler)
172
173 // if the server exits beforehand
cmd/ipfs/ipfsHandler.go
+92 -3
@@ -1,6 +1,7 @@
1 package main
2
3 import (
4 + "html/template"
5 "io"
6 "net/http"
7
@@ -23,10 +24,29 @@ type ipfs interface {
24 NewDagReader(nd *dag.Node) (io.Reader, error)
25 }
26
27 +// shortcut for templating
28 +type H map[string]interface{}
29 +
30 +// struct for directory listing
31 +type directoryItem struct {
32 + Size uint64
33 + Name string
34 +}
35 +
36 // ipfsHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
37 // (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
38 type ipfsHandler struct {
29 - node *core.IpfsNode
39 + node *core.IpfsNode
40 + dirList *template.Template
41 +}
42 +
43 +// Load the directroy list template
44 +func (i *ipfsHandler) LoadTemplate() {
45 + t, err := template.New("dir").Parse(listingTemplate)
46 + if err != nil {
47 + log.Error(err)
48 + }
49 + i.dirList = t
50 }
51
52 func (i *ipfsHandler) ResolvePath(path string) (*dag.Node, error) {
@@ -65,14 +85,63 @@ func (i *ipfsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
85 }
86
87 dr, err := i.NewDagReader(nd)
88 +
89 if err != nil {
69 - // TODO: return json object containing the tree data if it's a directory (err == ErrIsDir)
90 + if err == uio.ErrIsDir {
91 + log.Debug("is directory %s", path)
92 +
93 + if path[len(path)-1:] != "/" {
94 + log.Debug("missing trailing slash redirect")
95 + http.Redirect(w, r, "/ipfs/"+path+"/", 307)
96 + return
97 + }
98 +
99 + // storage for directory listing
100 + var dirListing []directoryItem
101 + // loop through files
102 + for _, link := range nd.Links {
103 + if link.Name == "index.html" {
104 + log.Debug("found index")
105 + // return index page
106 + nd, err := i.ResolvePath(path + "/index.html")
107 + if err != nil {
108 + w.WriteHeader(http.StatusInternalServerError)
109 + w.Write([]byte(err.Error()))
110 + log.Error("%s", err)
111 + return
112 + }
113 + dr, err := i.NewDagReader(nd)
114 + if err != nil {
115 + w.WriteHeader(http.StatusInternalServerError)
116 + w.Write([]byte(err.Error()))
117 + log.Error("%s", err)
118 + return
119 + }
120 + // write to request
121 + io.Copy(w, dr)
122 + return
123 + }
124 + dirListing = append(dirListing, directoryItem{link.Size, link.Name})
125 + }
126 + // template and return directory listing
127 + //for i, j := range dirListing {
128 + // log.Debug(i, ":", j.Size, " ", j.Name)
129 + //}
130 + err := i.dirList.Execute(w, H{"listing": dirListing, "path": path})
131 + if err != nil {
132 + w.WriteHeader(http.StatusInternalServerError)
133 + w.Write([]byte(err.Error()))
134 + log.Error("%s", err)
135 + return
136 + }
137 + return
138 + }
139 w.WriteHeader(http.StatusInternalServerError)
140 log.Error(err)
141 w.Write([]byte(err.Error()))
142 return
143 }
75 -
144 + // data file
145 io.Copy(w, dr)
146 }
147
@@ -97,3 +166,23 @@ func (i *ipfsHandler) postHandler(w http.ResponseWriter, r *http.Request) {
166 w.WriteHeader(http.StatusCreated)
167 w.Write([]byte(mh.Multihash(k).B58String()))
168 }
169 +
170 +// Directory listing template
171 +var listingTemplate = `
172 +<!DOCTYPE html>
173 +<html>
174 + <head>
175 + <meta charset="utf-8" />
176 + <title>{{ .path }}</title>
177 + </head>
178 + <body>
179 + <h2>Index of {{ .path }}</h2>
180 + <ul>
181 + <li> <a href="./..">Parent</a></li>
182 + {{ range $item := .listing }}
183 + <li> <a href="./{{ $item.Name }}">{{ $item.Name }}</a> - {{ $item.Size }} bytes</li>
184 + {{ end }}
185 + </ul>
186 + </body>
187 +</html>
188 +`