func ParseSearchConfig(c *caddy.Controller, cnf *httpserver.SiteConfig) (*Config, error) {
	hosthash := md5.New()
	hosthash.Write([]byte(cnf.Host()))
	conf := &Config{
		HostName:       hex.EncodeToString(hosthash.Sum(nil)),
		Engine:         `bleve`,
		IndexDirectory: `/tmp/caddyIndex`,
		IncludePaths:   []*regexp.Regexp{},
		ExcludePaths:   []*regexp.Regexp{},
		Endpoint:       `/search`,
		SiteRoot:       cnf.Root,
		Expire:         60 * time.Second,
		Template:       nil,
	}
	_, err := os.Stat(conf.SiteRoot)
	if err != nil {
		return nil, c.Err("[search]: `invalid root directory`")
	}
	incPaths := []string{}
	excPaths := []string{}
	for c.Next() {
		args := c.RemainingArgs()
		switch len(args) {
		case 2:
			conf.Endpoint = args[1]
			fallthrough
		case 1:
			incPaths = append(incPaths, args[0])
		}
		for c.NextBlock() {
			switch c.Val() {
			case "engine":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.Engine = c.Val()
			case "+path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				incPaths = append(incPaths, c.Val())
				incPaths = append(incPaths, c.RemainingArgs()...)
			case "-path":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				excPaths = append(excPaths, c.Val())
				excPaths = append(excPaths, c.RemainingArgs()...)
			case "endpoint":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.Endpoint = c.Val()
			case "expire":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				exp, err := strconv.Atoi(c.Val())
				if err != nil {
					return nil, err
				}
				conf.Expire = time.Duration(exp) * time.Second
			case "datadir":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}
				conf.IndexDirectory = c.Val()
			case "template":
				var err error
				if c.NextArg() {
					conf.Template, err = template.ParseFiles(filepath.Join(conf.SiteRoot, c.Val()))
					if err != nil {
						return nil, err
					}
				}
			}
		}
	}
	if len(incPaths) == 0 {
		incPaths = append(incPaths, "^/")
	}
	conf.IncludePaths = ConvertToRegExp(incPaths)
	conf.ExcludePaths = ConvertToRegExp(excPaths)
	dir := conf.IndexDirectory
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		if err := os.MkdirAll(dir, os.ModePerm); err != nil {
			return nil, c.Err("[search] Given 'datadir' not a valid path.")
		}
	}
	if conf.Template == nil {
		var err error
		conf.Template, err = template.New("search-results").Parse(defaultTemplate)
		if err != nil {
			return nil, err
		}
	}
	return conf, nil
}
ParseSearchConfig controller information to create a IndexSearch config
package search
import (
	"crypto/md5"
	"encoding/hex"
	"html/template"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"time"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	"github.com/pedronasser/caddy-search/indexer"
	"github.com/pedronasser/caddy-search/indexer/bleve"
)
func init() {
	caddy.RegisterPlugin("search", caddy.Plugin{
		ServerType: "http",
		Action:     Setup,
	})
}
func Setup(c *caddy.Controller) (err error) {
	cfg := httpserver.GetConfig(c)
	var config *Config
	config, err = ParseSearchConfig(c, cfg)
	if err != nil {
		return err
	}
	index, err := NewIndexer(config.Engine, indexer.Config{
		HostName:       config.HostName,
		IndexDirectory: config.IndexDirectory,
	})
	if err != nil {
		return err
	}
	ppl, err := NewPipeline(config, index)
	if err != nil {
		return err
	}
	expire := time.NewTicker(config.Expire)
	go func() {
		var lastScanned indexer.Record
		lastScanned = ScanToPipe(config.SiteRoot, ppl, index)
		for {
			select {
			case <-expire.C:
				if lastScanned != nil && (!lastScanned.Indexed().IsZero() || lastScanned.Ignored()) {
					lastScanned = ScanToPipe(config.SiteRoot, ppl, index)
				}
			}
		}
	}()
	search := &Search{
		Config:   config,
		Indexer:  index,
		Pipeline: ppl,
	}
	cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
		search.Next = next
		return search
	})
	return
}
func ScanToPipe(fp string, pipeline *Pipeline, index indexer.Handler) indexer.Record {
	var last indexer.Record
	absPath, _ := filepath.Abs(fp)
	filepath.Walk(absPath, func(path string, info os.FileInfo, err error) error {
		if info.Name() == "." {
			return nil
		}
		if info.Name() == "" || info.Name()[0] == '.' {
			if info.IsDir() {
				return filepath.SkipDir
			}
			return nil
		}
		if !info.IsDir() {
			reqPath, err := filepath.Rel(absPath, path)
			if err != nil {
				return nil
			}
			reqPath = "/" + reqPath
			if pipeline.ValidatePath(reqPath) {
				record := index.Record(reqPath)
				record.SetFullPath(path)
				record.SetModified(info.ModTime())
				pipeline.Pipe(record)
				last = record
			}
		}
		return nil
	})
	return last
}
func NewIndexer(engine string, config indexer.Config) (index indexer.Handler, err error) {
	name := filepath.Clean(config.IndexDirectory + string(filepath.Separator) + config.HostName)
	switch engine {
	default:
		index, err = bleve.New(name)
	}
	return
}
type Config struct {
	HostName       string
	Engine         string
	Path           string
	IncludePaths   []*regexp.Regexp
	ExcludePaths   []*regexp.Regexp
	Endpoint       string
	IndexDirectory string
	Template       *template.Template
	Expire         time.Duration
	SiteRoot       string
}