package main
import (
"cmp"
"iter"
"log/slog"
"net/http"
"os"
"strings"
)
var log = slog.New(slog.NewJSONHandler(os.Stdout, nil))
type repoLinkArgs struct {
pkgHostname string
storageHostname string
storageUser string
repo string
mainBranch string
docHostname string
}
const redirectTemplate = `<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="PKG_HOSTNAME/REPO git STORAGE_HOSTNAME/STORAGE_USER/REPO">
<meta name="go-source" content="PKG_HOSTNAME/REPO _ STORAGE_HOSTNAME/STORAGE_USER/REPO/blob/MAIN_BRANCH{/dir} STORAGE_HOSTNAME/STORAGE_USER/REPO/blob/MAIN_BRANCH{/dir}/{file}#L{line}">
<meta http-equiv="refresh" content="0; url=DOC_HOSTNAME/PKG_HOSTNAME/REPO">
</head>
<body>
Redirecting to docs at <a href="DOC_HOSTNAME/PKG_HOSTNAME/REPO">DOC_HOSTNAME/PKG_HOSTNAME/REPO</a>...
</body>
</html>
`
func mkRepoPage(args repoLinkArgs) string {
out := redirectTemplate
out = strings.ReplaceAll(out, "PKG_HOSTNAME", args.pkgHostname)
out = strings.ReplaceAll(out, "STORAGE_HOSTNAME", args.storageHostname)
out = strings.ReplaceAll(out, "STORAGE_USER", args.storageUser)
out = strings.ReplaceAll(out, "REPO", args.repo)
out = strings.ReplaceAll(out, "MAIN_BRANCH", args.mainBranch)
out = strings.ReplaceAll(out, "DOC_HOSTNAME", args.docHostname)
return out
}
var (
pkgHostname = cmp.Or(os.Getenv("GOPKG_PKG_HOSTNAME"), "gopkg.awl.red")
storageHostname = cmp.Or(os.Getenv("GOPKG_STORAGE_HOSTNAME"), "https://codeberg.org")
storageUser = cmp.Or(os.Getenv("GOPKG_STORAGE_USERNAME"), "neallred")
docHostname = cmp.Or(os.Getenv("GOPKG_DOC_HOSTNAME"), "https://godoc.org")
defaultMainBranch = cmp.Or(os.Getenv("GOPKG_MAIN_BRANCH"), "master")
port = cmp.Or(os.Getenv("GOPKG_PORT"), "8080")
addr = cmp.Or(os.Getenv("GOPKG_ADDR"), "0.0.0.0")
repoWhitelist = listToSet(strings.SplitSeq(cmp.Or(os.Getenv("GOPKG_REPO_WHITELIST"), ""), ","))
)
func listToSet[T comparable](xs iter.Seq[T]) map[T]struct{} {
out := map[T]struct{}{}
var zeroVal T
for x := range xs {
out[x] = struct{}{}
}
delete(out, zeroVal)
return out
}
func getMainBranch(repo string) string {
switch repo {
case "phb":
return "master"
default:
return defaultMainBranch
}
}
func main() {
http.Handle("GET /{repo}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
repoValue := r.PathValue("repo")
if _, found := repoWhitelist[repoValue]; len(repoWhitelist) > 0 != found {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("repo " + repoValue + " not found"))
log.Info("404", "repo", repoValue, "remote_addr", r.RemoteAddr, "user_agent", r.UserAgent())
return
}
log.Info("get repo", "repo", repoValue, "remote_addr", r.RemoteAddr, "user_agent", r.UserAgent())
r.Header.Set("Content-Type", "text/html; charset=utf-8")
r.Header.Set("Cache-Control", "public, max-age=300")
w.Write([]byte(mkRepoPage(repoLinkArgs{
pkgHostname: pkgHostname,
storageHostname: storageHostname,
storageUser: storageUser,
repo: repoValue,
mainBranch: getMainBranch(repoValue),
docHostname: docHostname,
})))
}))
log.Info("listening on", "addr", addr, "port", port)
err := http.ListenAndServe(addr+":"+port, nil)
if err != nil {
log.Info("server end", "err", err)
}
}