package server import ( "context" "encoding/json" "fmt" "html/template" "net/http" "path" "strconv" "strings" "github.com/angelbeltran/templater" "github.com/gin-gonic/gin" "ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/authentication" etsy_platform "ruben/inventory2/internal/domains/platforms/etsy" "ruben/inventory2/internal/domains/raw_events" "ruben/inventory2/internal/logging" 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" ) // r gin.IRouter, // TODO: use this everywhere func Router( ctx context.Context, logger *logging.Logger, contentDir string, rawEvents *raw_events.Store, accts *accounts.Store, etsy *etsy_platform.Platform, auth *authentication.Authenticator, ) *gin.Engine { r := gin.Default() // 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 r.Use(fileServer("/scripts", contentDir+"/scripts", func(c *gin.Context) { w := c.Writer w.Header().Set("Content-Type", "text/javascript") if path.Ext(c.Request.URL.Path) == ".gz" { w.Header().Set("Content-Encoding", "gzip") } })) r.Static("/styles", "./styles") r.Static("/favicon", "./favicon") // html (must be before the api endpoints, because the webpage middleware has to be installed beforehand r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/ui") }) // kind of a dumb way to capture routes for webpages templates_api.SetupRoutes( logger.WithGroup("templates"), r.Group("/ui"), 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, ) // api endpoints api := r.Group("/api") auth_api.Routes( api.Group("/auth"), logger.WithGroup("/auth"), auth, ) accounts_api.Routes( api.Group("/accounts"), logger.WithGroup("/accounts"), accts, authMiddleware, ) // api webhooks (TODO: make a router for these) webhooks.Webhooks( api.Group("/webhooks"), logger.WithGroup("webhooks"), rawEvents, etsy, webhooks.Config{ Etsy: etsy_webhooks.Config{ OAuthRedirectURIWithAcctIDParam: "/oauth/account/{acctID}/auth_code", }, }, ) return r } func fileServer(urlPrefix, dir string, beforeServe func(c *gin.Context)) gin.HandlerFunc { scfs := http.StripPrefix(urlPrefix, http.FileServer(http.Dir(dir))) return func(c *gin.Context) { r := c.Request if r.URL.Path == urlPrefix || strings.HasPrefix(r.URL.Path, path.Join(urlPrefix, "/")) { if beforeServe != nil { beforeServe(c) } scfs.ServeHTTP(c.Writer, r) c.Abort() } // TODO: need a c.Next()? } }