diff options
Diffstat (limited to 'gemini.go')
-rw-r--r-- | gemini.go | 116 |
1 files changed, 20 insertions, 96 deletions
@@ -1,24 +1,13 @@ package syw import ( - "bytes" "context" - "mime" - "os" - "path" - "path/filepath" - "strings" "text/template" "tildegit.org/tjp/sliderule" "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. // // The routes it defines are: @@ -52,7 +41,8 @@ const ( // Repo: a *syw.Repository object corresponding to <repodir>/:repository // 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 { tmpl, err := addTemplates(geminiTemplate, overrides) if err != nil { @@ -61,99 +51,33 @@ func GeminiRouter(repodir string, overrides *template.Template) *sliderule.Route repoRouter := &sliderule.Router{} repoRouter.Use(assignRepo(repodir)) - repoRouter.Route("/", gmiTemplate(tmpl, "repo_home.gmi")) - repoRouter.Route("/branches", gmiTemplate(tmpl, "branch_list.gmi")) - repoRouter.Route("/tags", gmiTemplate(tmpl, "tag_list.gmi")) - repoRouter.Route("/refs/:ref/", gmiTemplate(tmpl, "ref.gmi")) - repoRouter.Route("/refs/:ref/tree/*path", geminiTreePath(tmpl)) - repoRouter.Route("/diffstat/:fromref/:toref", runGemiTemplate(tmpl, "diffstat.gmi.txt", "text/plain")) - repoRouter.Route("/diff/:fromref/:toref", runGemiTemplate(tmpl, "diff.gmi.txt", "text/x-diff")) + repoRouter.Route("/", repoRouteHandler(geminiProto, tmpl, "repo_home.gmi")) + repoRouter.Route("/branches", repoRouteHandler(geminiProto, tmpl, "branch_list.gmi")) + repoRouter.Route("/tags", repoRouteHandler(geminiProto, tmpl, "tag_list.gmi")) + repoRouter.Route("/refs/:ref/", repoRouteHandler(geminiProto, tmpl, "ref.gmi")) + repoRouter.Route("/refs/:ref/tree/*path", treePathHandler(geminiProto, tmpl, "tree.gmi")) + repoRouter.Route("/diffstat/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diffstat.gmi.txt")) + repoRouter.Route("/diff/:fromref/:toref", repoRouteHandler(geminiProto, tmpl, "diff.gmi.txt")) router := &sliderule.Router{} - router.Route("/", geminiRoot(repodir, tmpl)) + router.Route("/", rootDirHandler(geminiProto, repodir, tmpl, "repo_root.gmi")) router.Mount("/:"+reponamekey, repoRouter) return router } -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) - }) - } -} - -func geminiRoot(repodir string, tmpl *template.Template) sliderule.Handler { - return sliderule.HandlerFunc(func(ctx context.Context, request *sliderule.Request) *sliderule.Response { - entries, err := os.ReadDir(repodir) - if err != nil { - return gemini.Failure(err) - } +type geminiProtocol struct{ sliderule.ServerProtocol } - 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 (geminiProtocol) TemplateBaseData(_ context.Context, _ *sliderule.Request) map[string]any { + return map[string]any{} } -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 (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 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) - }) -} +var geminiProto = geminiProtocol{gemini.ServerProtocol} |