File CodeView

This is a code view which proxies my personal git server. On the clearnet the following file is available at:

personal-gemini-capsule/src/branch/main/internal/codeview/codeview.go

----------------------------

package codeview

import (
	"encoding/json"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/url"
	"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:
 * - [x] Individual file view with line numbers?
 * - [x] Dir view with links to files
 * - [x] Project root view with markdown to gemtxt conversion and file tree
 */

func HandleRequest(w gemini.ResponseWriter, req *gemini.Request) {
	path := req.URL.Path

	switch {
	case strings.HasPrefix(path, "/codeview/dir"):
		serveDir(w, req, path)
	case strings.HasPrefix(path, "/codeview/raw/"):
		serveFile(w, req, path)
	default:
		w.WriteStatusMsg(gemini.StatusNotFound, "Page not found")
	}
}

func serveDir(w gemini.ResponseWriter, req *gemini.Request, path string) {
	repo := req.URL.Query().Get("repo")
	filepath := url.QueryEscape(req.URL.Query().Get("filepath"))
	branch := req.URL.Query().Get("branch")
	commit := req.URL.Query().Get("commit")
	slog.Info("Serving directory", "repo", repo, "filepath", filepath, "branch", branch, "commit", commit)
	var gitURL string
	ref := ""
	if branch != "" {
		ref = branch
	} else if commit != "" {
		ref = commit
	}

	if filepath == "." {
		gitURL = "https://git.travisshears.com/api/v1/repos/travisshears/" + repo + "/contents?ref=" + ref
	} else {
		gitURL = "https://git.travisshears.com/api/v1/repos/travisshears/" + repo + "/contents/" + filepath + "?ref=" + ref
	}

	slog.Info("Fetching directory contents", "url", gitURL)
	httpReq, err := http.NewRequest("GET", gitURL, 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 dirRes []struct {
		Name   string `json:"name"`
		Path   string `json:"path"`
		URL    string `json:"html_url"`
		Type   string `json:"type"`
		RawURL string `json:"download_url"`
	}

	if err := json.Unmarshal(body, &dirRes); err != nil {
		slog.Error("failed to parse response", "error", err)
		w.WriteStatusMsg(gemini.StatusGeneralPermFail, "Problem parsing response from git server")
		return
	}
	slog.Info("Successfully parsed response from git server", "res", dirRes)

	var content strings.Builder
	w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini")
	content.WriteString("# Directory CodeView\n")
	httpURL := fmt.Sprintf("https://git.travisshears.com/travisshears/personal-gemini-capsule/src/branch/main/%s", filepath)
	if branch != "" {
		httpURL = fmt.Sprintf("https://git.travisshears.com/travisshears/personal-gemini-capsule/src/branch/%s/%s", branch, filepath)
	} else if commit != "" {
		httpURL = fmt.Sprintf("https://git.travisshears.com/travisshears/personal-gemini-capsule/src/commit/%s/%s", commit, filepath)
	}
	content.WriteString(fmt.Sprintf("This is a code view which proxies my personal git server. On the clearnet the following dir is available:\n=> %s here\n\n", httpURL))

	// print readme page
	// var readmeURL string
	for _, file := range dirRes {
		if file.Type == "file" && file.Name == "README.md" {
			slog.Info("Found README.md", "file", file)
			body, err := getRemoteFile(file.RawURL)
			if err != nil {
				slog.Error("Failed to fetch README.md", "url", file.RawURL, "error", err)
				continue
			}
			content.WriteString("README.md found, directory listing below.\n")
			content.WriteString(renderCodeFile(body))
			content.WriteString("\n\n-----------------------------\n\n")
		}
	}

	for _, file := range dirRes {
		switch file.Type {
		case "dir":
			url := fmt.Sprintf("/codeview/dir?repo=%s&filepath=%s&commit=%s&branch=%s", repo, file.Path, commit, branch)
			content.WriteString(fmt.Sprintf("=> %s %s/\n", url, file.Path))
		case "file":
			p := strings.TrimPrefix(file.URL, "https://git.travisshears.com/travisshears/")
			url := fmt.Sprintf("/codeview/raw/%s", p)
			content.WriteString(fmt.Sprintf("=> %s %s\n", url, file.Path))
		}
	}
	content.WriteString("\n\n-----------------------------\n\n=> / Back to home\n")
	w.WriteBody([]byte(content.String()))
}

func renderCodeFile(code string) string {
	// Escape any triple backticks in the code to avoid prematurely ending Gemini preformatted blocks
	code = strings.ReplaceAll(code, "\`\`\`", "\\`\\`\\`")
	return fmt.Sprintf("\`\`\`code\n%s\n\`\`\`", code)
}

func getRemoteFile(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		slog.Error("Failed to fetch remote file", "url", url, "error", err)
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		slog.Error("Failed to fetch remote file", "url", url, "status", resp.Status)
		return "", fmt.Errorf("failed to fetch remote file")
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		slog.Error("Failed to read remote file", "url", url, "error", err)
		return "", err
	}

	return string(body), nil
}

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
	body, err := getRemoteFile(rawURL)
	if err != nil {
		slog.Error("Failed to create request to git server", "error", err)
		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(renderCodeFile(body))
	content.WriteString("\n\n-----------------------------\n\n=> / Back to home\n")
	w.WriteBody([]byte(content.String()))
}

-----------------------------

Back to home