commit e69771b09f000031757fbf986b66bf0e09c20046 Author: setop Date: Thu Nov 28 02:45:01 2024 +0100 first implementation diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..27b4d7a --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +golang 1.21.0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..abb5193 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine + +RUN apk add curl + +COPY main . + +EXPOSE 5555 + +ENTRYPOINT ["/main", "5555"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..c6879a1 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ + +# Purpose + +This tool allows to serve static files of a git repo hosted on a gitea instance + +Files to be served must be published in a branch named "pages" and in a folder named "public" + +# Usage + +## standalone + +set GITDOMAIN environment variable, then run `go run main.go 8081` + +## Docker + +Build image using docker and run a container, setting GITDOMAIN environment variable + +# Limitations + +* serve only public repo +* only relative links are supported + +# Further + +* perform security audit +* find a way to serve private repo content +* propose a standard gitea action to publish content +* custom domains diff --git a/cmd.txt b/cmd.txt new file mode 100644 index 0000000..346329f --- /dev/null +++ b/cmd.txt @@ -0,0 +1,11 @@ + +CGO_ENABLED=0 go build main.go && strip main + +GITDOMAIN=git.zoocoop.com ./main 8081 + +export VERSION=0.1.0 +docker build -t giteapages:$VERSION . + +docker save giteapages:$VERSION | pv | ssh kmsf2 docker load + + diff --git a/main.go b/main.go new file mode 100644 index 0000000..2ef8078 --- /dev/null +++ b/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "fmt" + "io/ioutil" + "net/http" + "os" + "regexp" + "strings" +) + +var GITDOMAIN = os.Getenv("GITDOMAIN") + +func main() { + // Get the port from command-line arguments + if len(os.Args) < 2 { + fmt.Println("Usage: go run main.go ") + return + } + port := os.Args[1] + + http.HandleFunc("/", serveFile) + fmt.Printf("Server is running on :%s\n", port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + fmt.Println("Error starting server:", err) + } +} + +func serveFile(w http.ResponseWriter, r *http.Request) { + // Parse the URL path + parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/") + if len(parts) < 3 { + http.Error(w, "Invalid URL format", http.StatusBadRequest) + return + } + + user := parts[0] + repo := parts[1] + filePath := strings.Join(parts[2:], "/") + + // Construct the raw file URL + rawURL := fmt.Sprintf("https://%s/%s/%s/raw/branch/pages/public/%s", GITDOMAIN, user, repo, filePath) + + // Fetch the content from the raw URL + resp, err := http.Get(rawURL) + if err != nil || resp.StatusCode != http.StatusOK { + fmt.Println("Failed to fetch file:", err) + http.Error(w, "Failed to fetch file", http.StatusNotFound) + return + } + defer resp.Body.Close() + + // Read the content + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + http.Error(w, "Failed to read file content", http.StatusInternalServerError) + return + } + + // Determine the content type based on the file extension + contentType := http.DetectContentType(content) + if strings.HasSuffix(filePath, ".css") { + contentType = "text/css" + } else if strings.HasSuffix(filePath, ".html") { + contentType = "text/html" + } else if strings.HasSuffix(filePath, ".js") { + contentType = "application/javascript" + } else if strings.HasSuffix(filePath, ".png") { + contentType = "image/png" + } else if strings.HasSuffix(filePath, ".jpg") || strings.HasSuffix(filePath, ".jpeg") { + contentType = "image/jpeg" + } else if strings.HasSuffix(filePath, ".gif") { + contentType = "image/gif" + } + + // Rewrite links in the content + var rewrittenContent = content + if contentType == "text/html"{ + rewrittenContent = []byte(rewriteLinks(string(content), user, repo)) + } + + // Set the content type and write the response + w.Header().Set("Content-Type", contentType) + w.Write([]byte(rewrittenContent)) +} + +func rewriteLinks(content, user, repo string) string { + // Regular expressions to match URLs + linkRegex := regexp.MustCompile(`(?i)(href|src)="([^"]+)"`) + replacedContent := linkRegex.ReplaceAllStringFunc(content, func(match string) string { + // Extract the URL + submatches := linkRegex.FindStringSubmatch(match) + if len(submatches) < 3 { + return match // Return the original match if it doesn't match + } + url := submatches[2] + + // Rewrite the URL + rewrittenURL := rewriteURL(url, user, repo) + return fmt.Sprintf("%s=\"%s\"", submatches[1], rewrittenURL) + }) + + return replacedContent +} + +func rewriteURL(url, user, repo string) string { + // Check if the URL is relative or absolute + if strings.HasPrefix(url, "/") { + // absolute URL, rewrite it + return fmt.Sprintf("https://%s/%s/%s/raw/branch/pages/public%s", GITDOMAIN, user, repo, url) + } + // Return the original URL if it's not relative + return url +}