package main import ( "flag" "log" "net/http" "strings" "github.com/tgckpg/resolver-go/internal/closure" "github.com/tgckpg/resolver-go/internal/generated" "github.com/tgckpg/resolver-go/internal/resolver" ) func main() { addr := flag.String("addr", ":8080", "listen address") src := flag.String("src", "./src", "BotanJS source root") flag.Parse() r, err := resolver.New(*src, generated.ClassMap) if err != nil { log.Fatal(err) } h := handler{ r: r, closure: closure.NewCompileCache(2), } http.HandleFunc("/", h.index) log.Printf("botan-api listening on %s", *addr) log.Fatal(http.ListenAndServe(*addr, nil)) } type handler struct { r *resolver.Resolver closure *closure.CompileCache } func (h handler) index(w http.ResponseWriter, req *http.Request) { path := strings.Trim(req.URL.Path, "/") if path == "" { _, _ = w.Write([]byte("BotanJS Go resolver API\n")) return } parts := strings.SplitN(path, "/", 2) modeText := parts[0] code := "" if len(parts) == 2 { code = parts[1] } else { code = req.URL.Query().Get("p") } if code == "" { http.Error(w, "missing request payload", http.StatusBadRequest) return } outMode := resolver.ModeJS if strings.HasSuffix(modeText, "css") { outMode = resolver.ModeCSS } else if !strings.HasSuffix(modeText, "js") { http.Error(w, "invalid mode", http.StatusBadRequest) return } res, err := h.r.ResolveRequest(code, outMode, nil) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } if outMode == resolver.ModeJS { if compiled, ok := h.closure.Get(res.Hash); ok { w.Header().Set("Content-Type", "application/javascript") w.Header().Set("X-Botan-Compiled", "hit") w.Write(compiled) return } jsExterns, err := h.r.GetExterns(generated.Externs) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } h.closure.Enqueue(closure.CompileJob{ Hash: res.Hash, Mode: "js", ExternSources: jsExterns, JSSources: []closure.SourceInput{ { Name: "botanjs-" + res.Hash + ".js", Source: string(res.Content), }, }, Defines: map[string]any{ "DEBUG": false, }, }) } // Compatibility flags: // rjs/rcss/ojs/ocss => content // hjs/hcss => hash filename only // js/css => currently content; cluster compiler can switch this later. if strings.HasPrefix(modeText, "h") { w.Header().Set("Content-Type", "text/plain; charset=utf-8") _, _ = w.Write([]byte(res.Hash)) return } w.Header().Set("Content-Type", res.ContentType) w.Header().Set("X-Botan-Compiled", "miss") _, _ = w.Write(res.Content) }