mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			257 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			257 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package setup
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"text/template"
 | 
						|
 | 
						|
	"github.com/mholt/caddy/middleware"
 | 
						|
	"github.com/mholt/caddy/middleware/browse"
 | 
						|
)
 | 
						|
 | 
						|
// Browse configures a new Browse middleware instance.
 | 
						|
func Browse(c *Controller) (middleware.Middleware, error) {
 | 
						|
	configs, err := browseParse(c)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	browse := browse.Browse{
 | 
						|
		Root:          c.Root,
 | 
						|
		Configs:       configs,
 | 
						|
		IgnoreIndexes: false,
 | 
						|
	}
 | 
						|
 | 
						|
	return func(next middleware.Handler) middleware.Handler {
 | 
						|
		browse.Next = next
 | 
						|
		return browse
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func browseParse(c *Controller) ([]browse.Config, error) {
 | 
						|
	var configs []browse.Config
 | 
						|
 | 
						|
	appendCfg := func(bc browse.Config) error {
 | 
						|
		for _, c := range configs {
 | 
						|
			if c.PathScope == bc.PathScope {
 | 
						|
				return fmt.Errorf("duplicate browsing config for %s", c.PathScope)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		configs = append(configs, bc)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	for c.Next() {
 | 
						|
		var bc browse.Config
 | 
						|
 | 
						|
		// First argument is directory to allow browsing; default is site root
 | 
						|
		if c.NextArg() {
 | 
						|
			bc.PathScope = c.Val()
 | 
						|
		} else {
 | 
						|
			bc.PathScope = "/"
 | 
						|
		}
 | 
						|
 | 
						|
		// Second argument would be the template file to use
 | 
						|
		var tplText string
 | 
						|
		if c.NextArg() {
 | 
						|
			tplBytes, err := ioutil.ReadFile(c.Val())
 | 
						|
			if err != nil {
 | 
						|
				return configs, err
 | 
						|
			}
 | 
						|
			tplText = string(tplBytes)
 | 
						|
		} else {
 | 
						|
			tplText = defaultTemplate
 | 
						|
		}
 | 
						|
 | 
						|
		// Build the template
 | 
						|
		tpl, err := template.New("listing").Parse(tplText)
 | 
						|
		if err != nil {
 | 
						|
			return configs, err
 | 
						|
		}
 | 
						|
		bc.Template = tpl
 | 
						|
 | 
						|
		// Save configuration
 | 
						|
		err = appendCfg(bc)
 | 
						|
		if err != nil {
 | 
						|
			return configs, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return configs, nil
 | 
						|
}
 | 
						|
 | 
						|
// The default template to use when serving up directory listings
 | 
						|
const defaultTemplate = `<!DOCTYPE html>
 | 
						|
<html>
 | 
						|
	<head>
 | 
						|
		<title>{{.Name}}</title>
 | 
						|
		<meta charset="utf-8">
 | 
						|
<style>
 | 
						|
* { padding: 0; margin: 0; }
 | 
						|
 | 
						|
body {
 | 
						|
	padding: 1% 2%;
 | 
						|
	font: 16px Arial;
 | 
						|
}
 | 
						|
 | 
						|
header {
 | 
						|
	font-size: 45px;
 | 
						|
	padding: 25px;
 | 
						|
}
 | 
						|
 | 
						|
header a {
 | 
						|
	text-decoration: none;
 | 
						|
	color: inherit;
 | 
						|
}
 | 
						|
 | 
						|
header .up {
 | 
						|
	display: inline-block;
 | 
						|
	height: 50px;
 | 
						|
	width: 50px;
 | 
						|
	text-align: center;
 | 
						|
	margin-right: 20px;
 | 
						|
}
 | 
						|
 | 
						|
header a.up:hover {
 | 
						|
	background: #000;
 | 
						|
	color: #FFF;
 | 
						|
}
 | 
						|
 | 
						|
h1 {
 | 
						|
	font-size: 30px;
 | 
						|
	display: inline;
 | 
						|
}
 | 
						|
 | 
						|
table {
 | 
						|
	border: 0;
 | 
						|
	border-collapse: collapse;
 | 
						|
	max-width: 750px;
 | 
						|
	margin: 0 auto;
 | 
						|
}
 | 
						|
 | 
						|
th,
 | 
						|
td {
 | 
						|
	padding: 4px 20px;
 | 
						|
	vertical-align: middle;
 | 
						|
	line-height: 1.5em; /* emoji are kind of odd heights */
 | 
						|
}
 | 
						|
 | 
						|
th {
 | 
						|
	text-align: left;
 | 
						|
}
 | 
						|
 | 
						|
th a {
 | 
						|
	color: #000;
 | 
						|
	text-decoration: none;
 | 
						|
}
 | 
						|
 | 
						|
@media (max-width: 700px) {
 | 
						|
	.hideable {
 | 
						|
		display: none;
 | 
						|
	}
 | 
						|
 | 
						|
	body {
 | 
						|
		padding: 0;
 | 
						|
	}
 | 
						|
 | 
						|
	header,
 | 
						|
	header h1 {
 | 
						|
		font-size: 16px;
 | 
						|
	}
 | 
						|
 | 
						|
	header {
 | 
						|
		position: fixed;
 | 
						|
		top: 0;
 | 
						|
		width: 100%;
 | 
						|
		background: #333;
 | 
						|
		color: #FFF;
 | 
						|
		padding: 15px;
 | 
						|
		text-align: center;
 | 
						|
	}
 | 
						|
 | 
						|
	header .up {
 | 
						|
		height: auto;
 | 
						|
		width: auto;
 | 
						|
		display: none;
 | 
						|
	}
 | 
						|
 | 
						|
	header a.up {
 | 
						|
		display: inline-block;
 | 
						|
		position: absolute;
 | 
						|
		left: 0;
 | 
						|
		top: 0;
 | 
						|
		width: 40px;
 | 
						|
		height: 48px;
 | 
						|
		font-size: 35px;
 | 
						|
	}
 | 
						|
 | 
						|
	header h1 {
 | 
						|
		font-weight: normal;
 | 
						|
	}
 | 
						|
 | 
						|
	main {
 | 
						|
		margin-top: 70px;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
.name {
 | 
						|
	white-space: pre;
 | 
						|
}
 | 
						|
</style>
 | 
						|
	</head>
 | 
						|
	<body>
 | 
						|
		<header>
 | 
						|
			{{if .CanGoUp}}
 | 
						|
			<a href=".." class="up" title="Up one level">⬑</a>
 | 
						|
			{{else}}
 | 
						|
			<div class="up"> </div>
 | 
						|
			{{end}}
 | 
						|
 | 
						|
			<h1>{{.Path}}</h1>
 | 
						|
		</header>
 | 
						|
		<main>
 | 
						|
			<table>
 | 
						|
				<tr>
 | 
						|
					<th>
 | 
						|
						{{if and (eq .Sort "name") (ne .Order "desc")}}
 | 
						|
						<a href="?sort=name&order=desc">Name ▲</a>
 | 
						|
						{{else if and (eq .Sort "name") (ne .Order "asc")}}
 | 
						|
						<a href="?sort=name&order=asc">Name ▼</a>
 | 
						|
						{{else}}
 | 
						|
						<a href="?sort=name&order=asc">Name</a>
 | 
						|
						{{end}}
 | 
						|
					</th>
 | 
						|
					<th>
 | 
						|
						{{if and (eq .Sort "size") (ne .Order "desc")}}
 | 
						|
						<a href="?sort=size&order=desc">Size ▲</a>
 | 
						|
						{{else if and (eq .Sort "size") (ne .Order "asc")}}
 | 
						|
						<a href="?sort=size&order=asc">Size ▼</a>
 | 
						|
						{{else}}
 | 
						|
						<a href="?sort=size&order=asc">Size</a>
 | 
						|
						{{end}}
 | 
						|
					</th>
 | 
						|
					<th class="hideable">
 | 
						|
						{{if and (eq .Sort "time") (ne .Order "desc")}}
 | 
						|
						<a href="?sort=time&order=desc">Modified ▲</a>
 | 
						|
						{{else if and (eq .Sort "time") (ne .Order "asc")}}
 | 
						|
						<a href="?sort=time&order=asc">Modified ▼</a>
 | 
						|
						{{else}}
 | 
						|
						<a href="?sort=time&order=asc">Modified</a>
 | 
						|
						{{end}}
 | 
						|
					</th>
 | 
						|
				</tr>
 | 
						|
				{{range .Items}}
 | 
						|
				<tr>
 | 
						|
					<td>
 | 
						|
						{{if .IsDir}}📂{{else}}📄{{end}}
 | 
						|
						<a href="{{.URL}}" class="name">{{.Name}}</a>
 | 
						|
					</td>
 | 
						|
					<td>{{.HumanSize}}</td>
 | 
						|
					<td class="hideable">{{.HumanModTime "01/02/2006 3:04:05 PM -0700"}}</td>
 | 
						|
				</tr>
 | 
						|
				{{end}}
 | 
						|
			</table>
 | 
						|
		</main>
 | 
						|
	</body>
 | 
						|
</html>`
 |