Protocol refactor
This commit is contained in:
parent
e8122362a0
commit
105022b22d
|
@ -1,2 +0,0 @@
|
||||||
tmp/
|
|
||||||
TODO.md
|
|
120
gemini.go
120
gemini.go
|
@ -1,24 +1,13 @@
|
||||||
package syw
|
package syw
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"mime"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"tildegit.org/tjp/sliderule"
|
"tildegit.org/tjp/sliderule"
|
||||||
"tildegit.org/tjp/sliderule/gemini"
|
"tildegit.org/tjp/sliderule/gemini"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
repokey = "syw_repo"
|
|
||||||
reponamekey = "repository"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GeminiRouter builds a router that will handle requests into a directory of git repositories.
|
// GeminiRouter builds a router that will handle requests into a directory of git repositories.
|
||||||
//
|
//
|
||||||
// The routes it defines are:
|
// The routes it defines are:
|
||||||
|
@ -52,7 +41,8 @@ const (
|
||||||
// Repo: a *syw.Repository object corresponding to <repodir>/:repository
|
// Repo: a *syw.Repository object corresponding to <repodir>/:repository
|
||||||
// Params: a map[string]string of the route parameters
|
// Params: a map[string]string of the route parameters
|
||||||
//
|
//
|
||||||
// The only exception is repo_root.gmi, which is rendered with a slice of the repo names instead.
|
// The only exception is repo_root.gmi, which is rendered with an object containing a single name
|
||||||
|
// "Repos", which is a slice of the repository names.
|
||||||
func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
||||||
tmpl, err := addTemplates(geminiTemplate, overrides)
|
tmpl, err := addTemplates(geminiTemplate, overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,99 +51,33 @@ func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Route
|
||||||
|
|
||||||
repoRouter := &sliderule.Router{}
|
repoRouter := &sliderule.Router{}
|
||||||
repoRouter.Use(assignRepo(repodir))
|
repoRouter.Use(assignRepo(repodir))
|
||||||
repoRouter.Route("/", gmiTemplate(tmpl, "repo_home.gmi"))
|
repoRouter.Route("/", repoRouteHandler(geminiProto, tmpl, "repo_home.gmi"))
|
||||||
repoRouter.Route("/branches", gmiTemplate(tmpl, "branch_list.gmi"))
|
repoRouter.Route("/branches", repoRouteHandler(geminiProto, tmpl, "branch_list.gmi"))
|
||||||
repoRouter.Route("/tags", gmiTemplate(tmpl, "tag_list.gmi"))
|
repoRouter.Route("/tags", repoRouteHandler(geminiProto, tmpl, "tag_list.gmi"))
|
||||||
repoRouter.Route("/refs/:ref/", gmiTemplate(tmpl, "ref.gmi"))
|
repoRouter.Route("/refs/:ref/", repoRouteHandler(geminiProto, tmpl, "ref.gmi"))
|
||||||
repoRouter.Route("/refs/:ref/tree/*path", geminiTreePath(tmpl))
|
repoRouter.Route("/refs/:ref/tree/*path", treePathHandler(geminiProto, tmpl, "tree.gmi"))
|
||||||
repoRouter.Route("/diffstat/:fromref/:toref", runGemiTemplate(tmpl, "diffstat.gmi.txt", "text/plain"))
|
repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diffstat.gmi.txt"))
|
||||||
repoRouter.Route("/diff/:fromref/:toref", runGemiTemplate(tmpl, "diff.gmi.txt", "text/x-diff"))
|
repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diff.gmi.txt"))
|
||||||
|
|
||||||
router := &sliderule.Router{}
|
router := &sliderule.Router{}
|
||||||
router.Route("/", geminiRoot(repodir, tmpl))
|
router.Route("/", rootDirHandler(geminiProto, repodir, tmpl, "repo_root.gmi"))
|
||||||
router.Mount("/:"+reponamekey, repoRouter)
|
router.Mount("/:"+reponamekey, repoRouter)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignRepo(repodir string) sliderule.Middleware {
|
type geminiProtocol struct{ sliderule.ServerProtocol }
|
||||||
return func(h sliderule.Handler) sliderule.Handler {
|
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
func (geminiProtocol) TemplateBaseData(_ context.Context, _ *sliderule.Request) map[string]any {
|
||||||
repo := Open(filepath.Join(repodir, sliderule.RouteParams(ctx)[reponamekey]))
|
return map[string]any{}
|
||||||
return h.Handle(context.WithValue(ctx, repokey, repo), request)
|
}
|
||||||
})
|
|
||||||
|
func (geminiProtocol) TemplateRepoData(ctx context.Context, request *sliderule.Request) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"Ctx": ctx,
|
||||||
|
"Repo": ctx.Value(repokey),
|
||||||
|
"Params": sliderule.RouteParams(ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func geminiRoot(repodir string, tmpl *template.Template) sliderule.Handler {
|
var geminiProto = geminiProtocol{gemini.ServerProtocol}
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
entries, err := os.ReadDir(repodir)
|
|
||||||
if err != nil {
|
|
||||||
return gemini.Failure(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{}
|
|
||||||
for _, item := range entries {
|
|
||||||
if Open(filepath.Join(repodir, item.Name())) != nil {
|
|
||||||
names = append(names, item.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
if err := tmpl.ExecuteTemplate(buf, "repo_root.gmi", names); err != nil {
|
|
||||||
return gemini.Failure(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return gemini.Success("text/gemini; charset=utf-8", buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func geminiTreePath(tmpl *template.Template) sliderule.Handler {
|
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
params := sliderule.RouteParams(ctx)
|
|
||||||
if params["path"] == "" || strings.HasSuffix(params["path"], "/") {
|
|
||||||
return gmiTemplate(tmpl, "tree.gmi").Handle(ctx, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
repo := ctx.Value(repokey).(*Repository)
|
|
||||||
|
|
||||||
body, err := repo.Blob(ctx, params["ref"], params["path"])
|
|
||||||
if err != nil {
|
|
||||||
return gemini.Failure(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaType := ""
|
|
||||||
ext := path.Ext(params["path"])
|
|
||||||
if ext == ".gmi" {
|
|
||||||
mediaType = "text/gemini; charset=utf-8"
|
|
||||||
} else {
|
|
||||||
mediaType = mime.TypeByExtension(ext)
|
|
||||||
}
|
|
||||||
if mediaType == "" {
|
|
||||||
mediaType = "application/octet-stream"
|
|
||||||
}
|
|
||||||
|
|
||||||
return gemini.Success(mediaType, bytes.NewBuffer(body))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func gmiTemplate(tmpl *template.Template, name string) sliderule.Handler {
|
|
||||||
return runGemiTemplate(tmpl, name, "text/gemini; charset=utf-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
func runGemiTemplate(tmpl *template.Template, name, mimetype string) sliderule.Handler {
|
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
obj := map[string]any{
|
|
||||||
"Ctx": ctx,
|
|
||||||
"Repo": ctx.Value(repokey),
|
|
||||||
"Params": sliderule.RouteParams(ctx),
|
|
||||||
}
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(buf, name, obj); err != nil {
|
|
||||||
return gemini.Failure(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return gemini.Success(mimetype, buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
88
gopher.go
88
gopher.go
|
@ -3,9 +3,7 @@ package syw
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"tildegit.org/tjp/sliderule"
|
"tildegit.org/tjp/sliderule"
|
||||||
|
@ -65,55 +63,47 @@ func GopherRouter(repodir string, overrides *template.Template) *sliderule.Route
|
||||||
|
|
||||||
repoRouter := &sliderule.Router{}
|
repoRouter := &sliderule.Router{}
|
||||||
repoRouter.Use(assignRepo(repodir))
|
repoRouter.Use(assignRepo(repodir))
|
||||||
repoRouter.Route("/branches", runGopherTemplate(tmpl, "branch_list.gph", gopher.MenuType))
|
repoRouter.Route("/branches", repoRouteHandler(gopherProto, tmpl, "branch_list.gph"))
|
||||||
repoRouter.Route("/tags", runGopherTemplate(tmpl, "tag_list.gph", gopher.MenuType))
|
repoRouter.Route("/tags", repoRouteHandler(gopherProto, tmpl, "tag_list.gph"))
|
||||||
repoRouter.Route("/refs/:ref", runGopherTemplate(tmpl, "ref.gph", gopher.MenuType))
|
repoRouter.Route("/refs/:ref", repoRouteHandler(gopherProto, tmpl, "ref.gph"))
|
||||||
repoRouter.Route("/refs/:ref/tree", gopherTreePath(tmpl, false))
|
repoRouter.Route("/refs/:ref/tree", gopherTreePath(tmpl))
|
||||||
repoRouter.Route("/refs/:ref/tree/*path", gopherTreePath(tmpl, true))
|
repoRouter.Route("/refs/:ref/tree/*path", gopherTreePath(tmpl))
|
||||||
repoRouter.Route("/diffstat/:fromref/:toref", runGopherTemplate(tmpl, "diffstat.gph.txt", gopher.TextFileType))
|
repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(gopherProto, tmpl, "diffstat.gph.txt"))
|
||||||
repoRouter.Route("/diff/:fromref/:toref", runGopherTemplate(tmpl, "diff.gph.txt", gopher.TextFileType))
|
repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(gopherProto, tmpl, "diff.gph.txt"))
|
||||||
|
|
||||||
router := &sliderule.Router{}
|
router := &sliderule.Router{}
|
||||||
router.Route("/", gopherRoot(repodir, tmpl))
|
router.Route("/", rootDirHandler(gopherProto, repodir, tmpl, "repo_root.gph"))
|
||||||
router.Route("/:"+reponamekey, assignRepo(repodir)(runGopherTemplate(tmpl, "repo_home.gph", gopher.MenuType)))
|
router.Route("/:"+reponamekey, assignRepo(repodir)(repoRouteHandler(gopherProto, tmpl, "repo_home.gph")))
|
||||||
router.Mount("/:"+reponamekey, repoRouter)
|
router.Mount("/:"+reponamekey, repoRouter)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
func gopherRoot(repodir string, tmpl *template.Template) sliderule.Handler {
|
type gopherProtocol struct{ sliderule.ServerProtocol }
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
entries, err := os.ReadDir(repodir)
|
|
||||||
if err != nil {
|
|
||||||
return gopher.Error(err).Response()
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{}
|
func (gopherProtocol) TemplateBaseData(ctx context.Context, request *sliderule.Request) map[string]any {
|
||||||
for _, item := range entries {
|
return map[string]any{
|
||||||
if Open(filepath.Join(repodir, item.Name())) != nil {
|
"Host": request.Hostname(),
|
||||||
names = append(names, item.Name())
|
"Port": request.Port(),
|
||||||
}
|
"Selector": request.Path,
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
obj := map[string]any{
|
|
||||||
"Repos": names,
|
|
||||||
"Host": request.Hostname(),
|
|
||||||
"Port": request.Port(),
|
|
||||||
"Selector": request.Path,
|
|
||||||
}
|
|
||||||
if err := tmpl.ExecuteTemplate(buf, "repo_root.gph", obj); err != nil {
|
|
||||||
return gopher.Error(err).Response()
|
|
||||||
}
|
|
||||||
|
|
||||||
return gopher.File(gopher.MenuType, buf)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func gopherTreePath(tmpl *template.Template, haspath bool) sliderule.Handler {
|
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 {
|
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
||||||
repo := ctx.Value(repokey).(*Repository)
|
repo := ctx.Value(repokey).(*Repository)
|
||||||
params := sliderule.RouteParams(ctx)
|
params := sliderule.RouteParams(ctx)
|
||||||
|
_, haspath := params["path"]
|
||||||
|
|
||||||
t := "tree"
|
t := "tree"
|
||||||
if haspath {
|
if haspath {
|
||||||
|
@ -128,7 +118,7 @@ func gopherTreePath(tmpl *template.Template, haspath bool) sliderule.Handler {
|
||||||
if !haspath {
|
if !haspath {
|
||||||
params["path"] = ""
|
params["path"] = ""
|
||||||
}
|
}
|
||||||
return runGopherTemplate(tmpl, "tree.gph", gopher.MenuType).Handle(ctx, request)
|
return repoRouteHandler(gopherProto, tmpl, "tree.gph").Handle(ctx, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := repo.Blob(ctx, params["ref"], params["path"])
|
body, err := repo.Blob(ctx, params["ref"], params["path"])
|
||||||
|
@ -143,23 +133,3 @@ func gopherTreePath(tmpl *template.Template, haspath bool) sliderule.Handler {
|
||||||
return gopher.File(filetype, bytes.NewBuffer(body))
|
return gopher.File(filetype, bytes.NewBuffer(body))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGopherTemplate(tmpl *template.Template, name string, filetype sliderule.Status) sliderule.Handler {
|
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
obj := map[string]any{
|
|
||||||
"Ctx": ctx,
|
|
||||||
"Repo": ctx.Value(repokey),
|
|
||||||
"Params": sliderule.RouteParams(ctx),
|
|
||||||
"Host": request.Hostname(),
|
|
||||||
"Port": request.Port(),
|
|
||||||
"Selector": request.Path,
|
|
||||||
}
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(buf, name, obj); err != nil {
|
|
||||||
return gopher.Error(err).Response()
|
|
||||||
}
|
|
||||||
|
|
||||||
return gopher.File(filetype, buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package syw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"tildegit.org/tjp/sliderule"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
repokey = "syw_repo"
|
||||||
|
reponamekey = "repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
func rootDirHandler(proto protocol, repodir string, tmpl *template.Template, tmplname string) sliderule.Handler {
|
||||||
|
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
||||||
|
entries, err := os.ReadDir(repodir)
|
||||||
|
if err != nil {
|
||||||
|
return proto.TemporaryServerError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := []string{}
|
||||||
|
for _, item := range entries {
|
||||||
|
if Open(filepath.Join(repodir, item.Name())) != nil {
|
||||||
|
names = append(names, item.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := proto.TemplateBaseData(ctx, request)
|
||||||
|
data["Repos"] = names
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if err := tmpl.ExecuteTemplate(buf, tmplname, data); err != nil {
|
||||||
|
return proto.TemporaryServerError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto.Success(tmplname, buf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func repoRouteHandler(proto protocol, tmpl *template.Template, tmplname string) sliderule.Handler {
|
||||||
|
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
||||||
|
data := repoTmplData(proto, ctx, request)
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if err := tmpl.ExecuteTemplate(buf, tmplname, data); err != nil {
|
||||||
|
return proto.TemporaryServerError(err)
|
||||||
|
}
|
||||||
|
return proto.Success(tmplname, buf)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func treePathHandler(proto protocol, tmpl *template.Template, tmplname string) sliderule.Handler {
|
||||||
|
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
||||||
|
params := sliderule.RouteParams(ctx)
|
||||||
|
if params["path"] == "" || strings.HasSuffix(params["path"], "/") {
|
||||||
|
return repoRouteHandler(proto, tmpl, tmplname).Handle(ctx, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ctx.Value(repokey).(*Repository).Blob(ctx, params["ref"], params["path"])
|
||||||
|
if err != nil {
|
||||||
|
return proto.TemporaryServerError(err)
|
||||||
|
}
|
||||||
|
return proto.Success(path.Base(params["path"]), bytes.NewBuffer(body))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func repoTmplData(proto protocol, ctx context.Context, request *sliderule.Request) map[string]any {
|
||||||
|
data := proto.TemplateBaseData(ctx, request)
|
||||||
|
for k, v := range proto.TemplateRepoData(ctx, request) {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignRepo(repodir string) sliderule.Middleware {
|
||||||
|
return func(h sliderule.Handler) sliderule.Handler {
|
||||||
|
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
||||||
|
repo := Open(filepath.Join(repodir, sliderule.RouteParams(ctx)[reponamekey]))
|
||||||
|
return h.Handle(context.WithValue(ctx, repokey, repo), request) //nolint:staticcheck
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package syw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"tildegit.org/tjp/sliderule"
|
||||||
|
)
|
||||||
|
|
||||||
|
type protocol interface {
|
||||||
|
sliderule.ServerProtocol
|
||||||
|
|
||||||
|
TemplateBaseData(context.Context, *sliderule.Request) map[string]any
|
||||||
|
TemplateRepoData(context.Context, *sliderule.Request) map[string]any
|
||||||
|
}
|
102
spartan.go
102
spartan.go
|
@ -1,13 +1,7 @@
|
||||||
package syw
|
package syw
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"mime"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"tildegit.org/tjp/sliderule"
|
"tildegit.org/tjp/sliderule"
|
||||||
|
@ -47,7 +41,8 @@ import (
|
||||||
// Repo: a *syw.Repository object corresponding to <repodir>/:repository
|
// Repo: a *syw.Repository object corresponding to <repodir>/:repository
|
||||||
// Params: a map[string]string of the route parameters
|
// Params: a map[string]string of the route parameters
|
||||||
//
|
//
|
||||||
// The only exception is repo_root.gmi, which is rendered with a slice of the repo names instead.
|
// The only exception is repo_root.gmi, which is rendered with an object containing a single name
|
||||||
|
// "Repos", a slice of the string repository names.
|
||||||
func SpartanRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
func SpartanRouter(repodir string, overrides *template.Template) *sliderule.Router {
|
||||||
tmpl, err := addTemplates(geminiTemplate, overrides)
|
tmpl, err := addTemplates(geminiTemplate, overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -56,90 +51,33 @@ func SpartanRouter(repodir string, overrides *template.Template) *sliderule.Rout
|
||||||
|
|
||||||
repoRouter := &sliderule.Router{}
|
repoRouter := &sliderule.Router{}
|
||||||
repoRouter.Use(assignRepo(repodir))
|
repoRouter.Use(assignRepo(repodir))
|
||||||
repoRouter.Route("/", sgmiTemplate(tmpl, "repo_home.gmi"))
|
repoRouter.Route("/", repoRouteHandler(spartanProto, tmpl, "repo_home.gmi"))
|
||||||
repoRouter.Route("/branches", sgmiTemplate(tmpl, "branch_list.gmi"))
|
repoRouter.Route("/branches", repoRouteHandler(spartanProto, tmpl, "branch_list.gmi"))
|
||||||
repoRouter.Route("/tags", sgmiTemplate(tmpl, "tag_list.gmi"))
|
repoRouter.Route("/tags", repoRouteHandler(spartanProto, tmpl, "tag_list.gmi"))
|
||||||
repoRouter.Route("/refs/:ref/", sgmiTemplate(tmpl, "ref.gmi"))
|
repoRouter.Route("/refs/:ref/", repoRouteHandler(spartanProto, tmpl, "ref.gmi"))
|
||||||
repoRouter.Route("/refs/:ref/tree/*path", spartanTreePath(tmpl))
|
repoRouter.Route("/refs/:ref/tree/*path", treePathHandler(spartanProto, tmpl, "tree.gmi"))
|
||||||
repoRouter.Route("/diffstat/:fromref/:toref", runSpartanTemplate(tmpl, "diffstat.gmi.txt", "text/plain"))
|
repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(spartanProto, tmpl, "diffstat.gmi.txt"))
|
||||||
repoRouter.Route("/diff/:fromref/:toref", runSpartanTemplate(tmpl, "diff.gmi.txt", "text/x-diff"))
|
repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(spartanProto, tmpl, "diff.gmi.txt"))
|
||||||
|
|
||||||
router := &sliderule.Router{}
|
router := &sliderule.Router{}
|
||||||
router.Route("/", spartanRoot(repodir, tmpl))
|
router.Route("/", rootDirHandler(spartanProto, repodir, tmpl, "repo_root.gmi"))
|
||||||
router.Mount("/:"+reponamekey, repoRouter)
|
router.Mount("/:"+reponamekey, repoRouter)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
func spartanRoot(repodir string, tmpl *template.Template) sliderule.Handler {
|
type spartanProtocol struct{ sliderule.ServerProtocol }
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
entries, err := os.ReadDir(repodir)
|
|
||||||
if err != nil {
|
|
||||||
return spartan.ServerError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
names := []string{}
|
func (spartanProtocol) TemplateBaseData(_ context.Context, _ *sliderule.Request) map[string]any {
|
||||||
for _, item := range entries {
|
return map[string]any{}
|
||||||
if Open(filepath.Join(repodir, item.Name())) != nil {
|
|
||||||
names = append(names, item.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
if err := tmpl.ExecuteTemplate(buf, "repo_root.gmi", names); err != nil {
|
|
||||||
return spartan.ServerError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return spartan.Success("text/gemini; charset=utf-8", buf)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func spartanTreePath(tmpl *template.Template) sliderule.Handler {
|
func (spartanProtocol) TemplateRepoData(ctx context.Context, request *sliderule.Request) map[string]any {
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
return map[string]any{
|
||||||
params := sliderule.RouteParams(ctx)
|
"Ctx": ctx,
|
||||||
if params["path"] == "" || strings.HasSuffix(params["path"], "/") {
|
"Repo": ctx.Value(repokey),
|
||||||
return sgmiTemplate(tmpl, "tree.gmi").Handle(ctx, request)
|
"Params": sliderule.RouteParams(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := ctx.Value(repokey).(*Repository)
|
|
||||||
|
|
||||||
body, err := repo.Blob(ctx, params["ref"], params["path"])
|
|
||||||
if err != nil {
|
|
||||||
return spartan.ServerError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaType := ""
|
|
||||||
ext := path.Ext(params["path"])
|
|
||||||
if ext == ".gmi" {
|
|
||||||
mediaType = "text/gemini; charset=utf-8"
|
|
||||||
} else {
|
|
||||||
mediaType = mime.TypeByExtension(ext)
|
|
||||||
}
|
|
||||||
if mediaType == "" {
|
|
||||||
mediaType = "application/octet-stream"
|
|
||||||
}
|
|
||||||
|
|
||||||
return spartan.Success(mediaType, bytes.NewBuffer(body))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sgmiTemplate(tmpl *template.Template, name string) sliderule.Handler {
|
var spartanProto = spartanProtocol{spartan.ServerProtocol}
|
||||||
return runSpartanTemplate(tmpl, name, "text/gemini; charset=utf-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
func runSpartanTemplate(tmpl *template.Template, name, mimetype string) sliderule.Handler {
|
|
||||||
return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response {
|
|
||||||
obj := map[string]any{
|
|
||||||
"Ctx": ctx,
|
|
||||||
"Repo": ctx.Value(repokey),
|
|
||||||
"Params": sliderule.RouteParams(ctx),
|
|
||||||
}
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
if err := tmpl.ExecuteTemplate(buf, name, obj); err != nil {
|
|
||||||
return spartan.ServerError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return spartan.Success(mimetype, buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Repositories
|
# Repositories
|
||||||
|
|
||||||
{{ range . -}}
|
{{ range .Repos -}}
|
||||||
=> ./{{.}}/ {{.}}
|
=> ./{{.}}/ {{.}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
Loading…
Reference in New Issue