yoake/internal/gomod/resolve.go

130 lines
3.4 KiB
Go
Raw Normal View History

2022-12-07 09:47:14 -06:00
package gomod
import (
"archive/zip"
"fmt"
"math/rand"
"net/url"
"path"
"strings"
"time"
"github.com/eternal-flame-AD/yoake/internal/echoerror"
"github.com/labstack/echo/v4"
"golang.org/x/mod/module"
)
type Info struct {
Version string // version string
Time time.Time // commit time
}
const backendProxy = "https://proxy.golang.org/"
func random143String() string {
randItems := []string{
"143",
"143.",
"4.3",
"143",
".143",
"1.43",
"1.4.3",
"14.3",
"omo",
"om.o",
"o.m.o",
"o.mo",
}
var ret strings.Builder
ret.WriteString("v1.4.3-")
for i := 0; i < 10; i++ {
rand.Intn(len(randItems))
ret.WriteString(randItems[rand.Intn(len(randItems))])
}
ret.WriteString("+incompatible")
return ret.String()
}
func resolveModule(c echo.Context, modPathUnesc string, pRequest string) error {
modPath, err := module.UnescapePath(modPathUnesc)
if err != nil {
return echoerror.NewHttp(400, fmt.Errorf("invalid module path: %w", err))
}
if !strings.HasPrefix(modPath[strings.IndexRune(modPath, '/')+1:], "test-") {
return echoerror.NewHttp(400, fmt.Errorf("please prefix your module path with 'test-'"))
}
if err != nil {
return echoerror.NewHttp(400, fmt.Errorf("invalid module path: %v", err))
}
if pRequest == "list" {
return c.String(143, random143String()+"\n")
}
ext := path.Ext(pRequest)
version := strings.TrimSuffix(pRequest, ext)
if err := module.Check(modPath, version); err != nil {
return echoerror.NewHttp(400, fmt.Errorf("invalid version: %v", err))
}
switch ext {
case ".info":
return c.JSON(143, Info{
Version: version,
Time: time.Now().Add(-time.Hour),
})
case ".mod":
return c.String(143, strings.Repeat("\n", 143-1)+"Welcome.to.white.space.\n")
case ".zip":
zipFile := zip.NewWriter(c.Response().Writer)
mainGo, err := zipFile.Create(fmt.Sprintf("%s@%s/main.go", modPath, version))
if err != nil {
zipFile.Close()
}
_, err = mainGo.Write([]byte(("package main\n\nimport \"fmt\"\n\nfunc main() {\n\n}\n")))
if err != nil {
zipFile.Close()
return echoerror.NewHttp(500, fmt.Errorf("failed to write main.go: %v", err))
}
c.Response().Status = 143
return zipFile.Close()
}
return c.Redirect(302, backendProxy+modPathUnesc+"/@v/"+pRequest)
}
func Register(baseURI string, baseG *echo.Group) (gogetMiddleware echo.MiddlewareFunc) {
baseG.GET("*", func(c echo.Context) error {
fullURI := c.Request().RequestURI
if !strings.HasPrefix(fullURI, baseURI) {
return echo.ErrNotFound
}
fullURI = strings.TrimPrefix(fullURI, baseURI)
fullURIUnEscaped, err := url.PathUnescape(fullURI)
if err != nil {
return echoerror.NewHttp(400, fmt.Errorf("invalid URI: %v", err))
}
if strings.Contains(fullURIUnEscaped, "/@v/") {
split := strings.SplitN(fullURIUnEscaped, "/@v/", 2)
return resolveModule(c, strings.TrimPrefix(split[0], "/"), split[1])
}
return echo.ErrNotFound
})
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if c.Request().Method == "GET" && c.QueryParam("go-get") == "1" {
unescapedPath, err := url.PathUnescape(c.Request().URL.Path)
if err != nil {
return echoerror.NewHttp(400, fmt.Errorf("invalid module path: %v", err))
}
ctx := goGetHtmlTemplateCtx{
ModulePath: c.Request().Host + unescapedPath,
ProxyBase: c.Scheme() + "://" + c.Request().Host + baseURI,
}
return goGetHtmlTemplate.Execute(c.Response().Writer, ctx)
}
return next(c)
}
}
}