pub 0.2.9

publish anything to the open web
Documentation
package pub

import (
	_ "embed"
	"expvar"
	"fmt"
	"log/slog"
	"net/http"
	"os"
	"strings"

	"github.com/btwiuse/pub/handler"
	"github.com/btwiuse/rng"
	"github.com/webteleport/utils"
	"github.com/webteleport/wtf"
)

type Rule struct {
	Resource string
	Pattern  string
	Prefix   string
}

func InferPrefix(pattern string) string {
	parts := strings.Split(pattern, " ")
	pattern = parts[len(parts)-1]
	switch {
	case strings.HasSuffix(pattern, "/"):
		return strings.TrimSuffix(pattern, "/")
	default:
		return ""
	}
}

func SplitPathPrefix(pp string) (pattern, pfx string) {
	pattern = pp
	if strings.Contains(pp, "#") {
		parts := strings.SplitN(pp, "#", 2)
		pattern = parts[0]
		pfx = parts[1]
	} else if pfx == "" {
		pfx = InferPrefix(pattern)
	}
	return
}

func NewRule(res, pattern_with_prefix string) Rule {
	pattern, pfx := SplitPathPrefix(pattern_with_prefix)
	return Rule{res, pattern, pfx}
}

type Rules []Rule

func (s *Rules) Push(r Rule) {
	*s = append(*s, r)
}

func Parse(s []string) (rules Rules) {
	for i := 0; i < len(s); i += 2 {
		res := s[i]
		pwf := "/"
		if i+1 < len(s) {
			pwf = s[i+1]
		}
		rules.Push(NewRule(res, pwf))
	}
	return
}

func ApplyRules(mux *http.ServeMux, rules Rules) {
	for _, rule := range rules {
		res := rule.Resource
		pattern := rule.Pattern
		pfx := rule.Prefix
		emoji := handler.ResourceEmoji(res)
		info := fmt.Sprintf("%s %s 🌐 %s", emoji, res, pattern)
		if pfx != "" {
			info = fmt.Sprintf("%s (stripping prefix: %s)", info, pfx)
		}
		slog.Info(info)
		handlr := http.StripPrefix(pfx, handler.ResourceHandler(res))
		mux.Handle(rule.Pattern, handlr)
	}
}

func Handler(rules Rules) http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /debug/vars", expvar.Handler().ServeHTTP)
	ApplyRules(mux, rules)
	return mux
}

//go:embed README.md
var Usage string

func RelayAddr() string {
	if relay := os.Getenv("RELAY"); relay != "" {
		return relay
	}

	name := rng.NewDockerSepDigits("-", 4)
	return fmt.Sprintf("https://pub.webtransport.fun/%s", name)
}

func Run(args []string) error {
	rules := Parse(args)

	handler := Handler(rules)
	handler = utils.GzipMiddleware(handler)
	handler = utils.GinLoggerMiddleware(handler)

	return wtf.Serve(RelayAddr(), handler)
}