package server import ( "context" "encoding/json" "errors" "fmt" "html/template" "log/slog" "net/http" "path" "strconv" "github.com/angelbeltran/templater" "ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/authentication" etsy_platform "ruben/inventory2/internal/domains/platforms/etsy" "ruben/inventory2/internal/domains/raw_events" accounts_api "ruben/inventory2/internal/server/api/accounts" auth_api "ruben/inventory2/internal/server/api/auth" templates_api "ruben/inventory2/internal/server/api/templates" "ruben/inventory2/internal/server/api/webhooks" etsy_webhooks "ruben/inventory2/internal/server/api/webhooks/etsy" "ruben/inventory2/internal/server/middleware" "ruben/inventory2/internal/server/response" "ruben/inventory2/internal/server/router" ) func NewServer( ctx context.Context, logger *slog.Logger, contentDir string, rawEvents *raw_events.Store, accts *accounts.Store, etsy *etsy_platform.Platform, auth *authentication.Authenticator, ) http.Handler { mux := router.NewMux( logger, middleware.LogRequests(ctx, logger.WithGroup("request")), ) // TODO: shouldn't this ACTUALLY be a middleware? // - only try to make this an actual middleware AFTER all the routers are broken out, so that way how the middleware is supposed to work can be known authMiddleware := middleware.NewAuth( logger.WithGroup("auth-middleware"), auth, auth_api.NewLoginURL, accts, ) // webpage content // non-html content: scripts, styles, images, etc scfs := http.FileServer(http.Dir(contentDir + "/scripts")) mux.Mux.Handle("GET /scripts/", http.StripPrefix("/scripts", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/javascript") if path.Ext(r.URL.Path) == ".gz" { w.Header().Set("Content-Encoding", "gzip") } scfs.ServeHTTP(w, r) }))) mux.Mux.Handle("GET /styles/", http.StripPrefix("/styles", http.FileServer(http.Dir(contentDir+"/styles")))) mux.Mux.Handle("GET /favicon/", http.StripPrefix("/favicon", http.FileServer(http.Dir(contentDir+"/favicon")))) // html (must be before the api endpoints, because the webpage middleware has to be installed beforehand // kind of a dumb way to capture routes for webpages wpr := templates_api.NewWebpageRouter( logger.WithGroup("templates"), contentDir, templater.NewTemplater( contentDir+"/templates", func() template.FuncMap { return template.FuncMap{ // params "addPathParam": func(k string, v any, args map[string]any) (map[string]any, error) { pathParams, ok := args["PathParams"].(map[string]string) if !ok { return nil, fmt.Errorf("PathParams no set are args: %v", args) } pathParams[k] = fmt.Sprint(v) return args, nil }, // parsing "parseInt": func(s string) (int, error) { return strconv.Atoi(s) }, "parseInt64": func(s string) (int64, error) { return strconv.ParseInt(s, 10, 64) }, "parsePlatform": func(s string) (accounts.Platform, error) { return accounts.NewPlatform(s) }, // arithmetic "addInt": func(a, b int) int { return a + b }, "subInt": func(a, b int) int { return a - b }, "multInt": func(a, b int) int { return a * b }, // json "prettyPrintJSON": func(j json.RawMessage) string { b, err := json.MarshalIndent(j, " ", "") if err != nil { return string(j) } return string(b) }, } }, ), rawEvents, accts, etsy, authMiddleware, ) mux.AddMiddleware( func(fn response.HandlerFunc) response.HandlerFunc { return func(r *http.Request) (response.Response, error) { res, err := fn(r) if err == nil || !errors.Is(err, router.ErrHandlerNotFound) { return res, err } renderWebPage, pathParams, found := wpr.Handler(r) if !found { return nil, response.NotFound() } for k, v := range pathParams { r.SetPathValue(k, v) } return renderWebPage(r) } }, ) mux.Route("/", wpr) // api endpoints mux.Route("/auth", auth_api.NewLoginSubrouter(logger.WithGroup("/auth"), auth)) mux.Route("/accounts", accounts_api.NewAccountSubrouter( logger.WithGroup("/accounts"), accts, authMiddleware, )) // api webhooks (TODO: make a router for these) webhookHandler := http.StripPrefix("/webhooks", webhooks.New( logger.WithGroup("webhooks"), rawEvents, etsy, webhooks.Config{ Etsy: etsy_webhooks.Config{ OAuthRedirectURIWithAcctIDParam: "/oauth/account/{acctID}/auth_code", }, }, )) mux.Mux.Handle("GET /webhooks/", webhookHandler) mux.Mux.Handle("POST /webhooks/", webhookHandler) mux.Mux.Handle("PUT /webhooks/", webhookHandler) return mux }