136 lines
5.2 KiB
Go
136 lines
5.2 KiB
Go
package syw
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"path"
|
|
"text/template"
|
|
|
|
"tildegit.org/tjp/sliderule"
|
|
"tildegit.org/tjp/sliderule/gopher"
|
|
)
|
|
|
|
// GopherRouter builds a router that will handle gopher requests in a directory of git repositories.
|
|
//
|
|
// The routes it defines are:
|
|
//
|
|
// / .gph listing of the repositories in the directory
|
|
// /:repository .gph overview of the repository
|
|
// /:repository/branches .gph list of branches/head
|
|
// /:repository/tags .gph listing of tags
|
|
// /:repository/refs/:ref .gph overview of a ref
|
|
// /:repository/refs/:ref/tree .gph listing of a ref's root directory
|
|
// /:repository/refs/:ref/tree/*path for directories:.gph list of contents
|
|
// for files: raw files (guessed item type text/binary/image/etc)
|
|
// /:repository/diffstat/:fromref/:toref text diffstat between two refs
|
|
// /:repository/diff/:fromref/:toref text diff between two refs
|
|
//
|
|
// The overrides argument can provide templates to define the behavior of nearly all of the above routes.
|
|
// All of them have default implementations, so the argument can be nil, but otherwise the template names
|
|
// used are:
|
|
//
|
|
// repo_root.gph gophermap at /
|
|
// repo_home.gph gophermap at /:repository
|
|
// branch_list.gph gophermap at /:repository/branches
|
|
// tag_list.gph gophermap at /:repository/tags
|
|
// ref.gph gophermap at /:repository/refs/:ref
|
|
// tree.gph gophermap at direcotry paths under /:repository/refs/:ref/tree/*path
|
|
// (file paths return the raw files without any template involved)
|
|
// diffstat.gph.txt plain text diffstat at /:repository/diffstat/:fromref/:toref
|
|
// diff.gph.txt plain text diff at /:repository/diff/:fromref/:toref
|
|
//
|
|
// Most of the templates above are rendered with an object with 6 fields:
|
|
//
|
|
// Ctx: the context.Context from the request
|
|
// Repo: a *syw.Repository corresponding to <repodir>/:repository
|
|
// Params: the map[string]string of the route parameters
|
|
// Host: the hostname of the running server
|
|
// Port: the port number of the running server
|
|
// Selector: the selector in the current request
|
|
//
|
|
// The only exception is repo_root.gph, which is instead rendered with a slice of the repo names.
|
|
//
|
|
// All templates have 3 additional functions made available to them:
|
|
//
|
|
// combine: func(string, ...string) string - successively combines paths using url.URL.ResolveReference
|
|
// join: func(string, ...string) string - successively joins path segments
|
|
// rawtext: func(selector, host, port, text string) string renders text lines as gopher info-message lines.
|
|
func GopherRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
|
tmpl, err := addTemplates(gopherTemplate, overrides)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
repoRouter := &sliderule.Router{}
|
|
repoRouter.Use(assignRepo(repodir))
|
|
repoRouter.Route("/branches", repoRouteHandler(gopherProto, tmpl, "branch_list.gph"))
|
|
repoRouter.Route("/tags", repoRouteHandler(gopherProto, tmpl, "tag_list.gph"))
|
|
repoRouter.Route("/refs/:ref", repoRouteHandler(gopherProto, tmpl, "ref.gph"))
|
|
repoRouter.Route("/refs/:ref/tree", gopherTreePath(tmpl))
|
|
repoRouter.Route("/refs/:ref/tree/*path", gopherTreePath(tmpl))
|
|
repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(gopherProto, tmpl, "diffstat.gph.txt"))
|
|
repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(gopherProto, tmpl, "diff.gph.txt"))
|
|
|
|
router := &sliderule.Router{}
|
|
router.Route("/", rootDirHandler(gopherProto, repodir, tmpl, "repo_root.gph"))
|
|
router.Route("/:"+reponamekey, assignRepo(repodir)(repoRouteHandler(gopherProto, tmpl, "repo_home.gph")))
|
|
router.Mount("/:"+reponamekey, repoRouter)
|
|
|
|
return router
|
|
}
|
|
|
|
type gopherProtocol struct{ sliderule.ServerProtocol }
|
|
|
|
func (gopherProtocol) TemplateBaseData(ctx context.Context, request *sliderule.Request) map[string]any {
|
|
return map[string]any{
|
|
"Host": request.Hostname(),
|
|
"Port": request.Port(),
|
|
"Selector": request.Path,
|
|
}
|
|
}
|
|
|
|
func (gopherProtocol) TemplateRepoData(ctx context.Context, request *sliderule.Request) map[string]any {
|
|
return map[string]any{
|
|
"Ctx": ctx,
|
|
"Repo": ctx.Value(repokey),
|
|
"Params": sliderule.RouteParams(ctx),
|
|
}
|
|
}
|
|
|
|
var gopherProto = gopherProtocol{gopher.ServerProtocol}
|
|
|
|
func gopherTreePath(tmpl *template.Template) sliderule.Handler {
|
|
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
repo := ctx.Value(repokey).(*Repository)
|
|
params := sliderule.RouteParams(ctx)
|
|
_, haspath := params["path"]
|
|
|
|
t := "tree"
|
|
if haspath {
|
|
var err error
|
|
t, err = repo.Type(ctx, params["ref"]+":"+params["path"])
|
|
if err != nil {
|
|
return gopher.Error(err).Response()
|
|
}
|
|
}
|
|
|
|
if t != "blob" {
|
|
if !haspath {
|
|
params["path"] = ""
|
|
}
|
|
return repoRouteHandler(gopherProto, tmpl, "tree.gph").Handle(ctx, request)
|
|
}
|
|
|
|
body, err := repo.Blob(ctx, params["ref"], params["path"])
|
|
if err != nil {
|
|
return gopher.Error(err).Response()
|
|
}
|
|
|
|
filetype := gopher.MenuType
|
|
if path.Base(params["path"]) != "gophermap" {
|
|
filetype = gopher.GuessItemType(params["path"])
|
|
}
|
|
return gopher.File(filetype, bytes.NewBuffer(body))
|
|
})
|
|
}
|