Files
inventory-plus-plus/internal/server/server.go
T
2026-01-12 23:21:52 -07:00

179 lines
4.7 KiB
Go

package server
import (
"context"
"encoding/json"
"errors"
"fmt"
"html/template"
"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"
"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"
"ruben/inventory2/internal/server/response"
"ruben/inventory2/internal/server/router"
)
func NewServer(
ctx context.Context,
logger *logging.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
}