From 3a601b2bdd69c8ced3709409989f7f59aa2e11b4 Mon Sep 17 00:00:00 2001 From: Travis Shears Date: Tue, 7 Oct 2025 22:38:31 +0200 Subject: [PATCH] code view --- internal/codeview/codeview.go | 82 +++++++++++++++++++++++++++++++++++ main.go | 4 ++ 2 files changed, 86 insertions(+) create mode 100644 internal/codeview/codeview.go diff --git a/internal/codeview/codeview.go b/internal/codeview/codeview.go new file mode 100644 index 0000000..93629ee --- /dev/null +++ b/internal/codeview/codeview.go @@ -0,0 +1,82 @@ +package codeview + +import ( + "fmt" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/kulak/gemini" +) + +/* + * Codeview is a feature that proxies my personal git server (forgejo) + * Allowing me to render code natively in gemini + * + * Features: + * - [ ] Individual file view with line numbers? + * - [ ] Dir view with links to files + * - [ ] Project root view with markdown to gemtxt conversion and file tree + */ + +// https://git.travisshears.com/travisshears/personal-gemini-capsule/raw/branch/main/internal/guestbook/guestbook.go +// /codeview/raw/personal-gemini-capsule/src/branch/main/internal/guestbook/guestbook.go +func HandleRequest(w gemini.ResponseWriter, req *gemini.Request) { + path := req.URL.Path + + switch { + // case path == "/gemlog" || path == "/gemlog/": + // g.serveIndex(w, req) + case strings.HasPrefix(path, "/codeview/raw/"): + serveFile(w, req, path) + default: + w.WriteStatusMsg(gemini.StatusNotFound, "Page not found") + } +} + +func serveFile(w gemini.ResponseWriter, req *gemini.Request, path string) { + path = strings.TrimPrefix(path, "/codeview/raw/") + rawURL := strings.Replace(path, "src", "raw", 1) + rawURL = "https://git.travisshears.com/travisshears/" + rawURL + remoteURL := "https://git.travisshears.com/travisshears/" + path + httpReq, err := http.NewRequest("GET", rawURL, nil) + if err != nil { + slog.Error("Failed to create request to git server", "error", err) + w.WriteStatusMsg(gemini.StatusGeneralPermFail, "Problem connecting to git server") + return + } + res, err := http.DefaultClient.Do(httpReq) + if err != nil { + slog.Error("Failed to send request to git server", "error", err) + w.WriteStatusMsg(gemini.StatusGeneralPermFail, "Problem connecting to git server") + return + } + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + slog.Error("Failed to read res body", "error", err) + w.WriteStatusMsg(gemini.StatusGeneralPermFail, "Problem connecting to git server") + return + } + + if res.StatusCode < 200 || res.StatusCode >= 300 { + slog.Error("unexpected status code", "statusCode", res.StatusCode, "body", string(body)) + w.WriteStatusMsg(gemini.StatusGeneralPermFail, "Problem connecting to git server") + return + } + + var content strings.Builder + w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini") + // filename := strings.TrimPrefix(remoteURL, "https://git.travisshears.com/travisshears/") + // content.WriteString(fmt.Sprintf("# File: %s\n", filename)) + content.WriteString("# File CodeView\n") + content.WriteString(fmt.Sprintf("This is a code view which proxies my personal git server. On the clearnet the following file is available at:\n=> %s %s\n\n", remoteURL, path)) + content.WriteString("----------------------------\n\n") + content.WriteString("```code\n") + content.Write(body) + content.WriteString("```\n") + content.WriteString("\n\n-----------------------------\n\n=> / Back to home\n") + w.WriteBody([]byte(content.String())) +} diff --git a/main.go b/main.go index 315d397..b19d8eb 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( _ "github.com/mattn/go-sqlite3" + codeview "gemini_site/internal/codeview" gemlog "gemini_site/internal/gemlog" "gemini_site/internal/guestbook" microblog "gemini_site/internal/microblog" @@ -44,6 +45,9 @@ func (h MainHandler) ServeGemini(w gemini.ResponseWriter, req *gemini.Request) { case strings.HasPrefix(req.URL.Path, "/guestbook"): h.guestbook.HandleRequest(w, req) return + case strings.HasPrefix(req.URL.Path, "/codeview"): + codeview.HandleRequest(w, req) + return } switch req.URL.Path {