Files
inventory-plus-plus/internal/server/api/webhooks/etsy/webhooks.go
T
2026-01-24 12:47:54 -07:00

153 lines
3.4 KiB
Go

package etsy
import (
"encoding/json"
"fmt"
"strconv"
"time"
"ruben/inventory2/internal/domains/platforms/etsy"
"ruben/inventory2/internal/domains/raw_events"
"ruben/inventory2/internal/logging"
"ruben/inventory2/internal/server/response"
"github.com/gin-gonic/gin"
)
type (
Config struct {
OAuthRedirectURIWithAcctIDParam string
}
)
func Routes(
r *gin.RouterGroup,
logger *logging.Logger,
db *raw_events.Store,
platform *etsy.Platform,
cfg Config,
) {
h := webhooks{
log: logger,
cfg: cfg,
db: db,
etsy: platform,
}
r.POST("/test", response.Handler(h.test))
r.GET(h.cfg.OAuthRedirectURIWithAcctIDParam, response.Handler(h.redirectURI))
r.GET("/:acctID/new-account-link", response.Handler(h.newAccountLink))
}
type (
webhooks struct {
log *logging.Logger
cfg Config
db *raw_events.Store
etsy *etsy.Platform
}
)
// POST /test
func (h webhooks) test(c *gin.Context) (response.Response, error) {
r := c.Request
var body json.RawMessage
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
return nil, response.Errorf("failed to decode body as json: %w", err)
}
ts := time.Now().UTC()
storeID := "test-store-id"
var payloadObject struct {
StoreID string
}
if err := json.Unmarshal(body, &payloadObject); err == nil && payloadObject.StoreID != "" {
storeID = payloadObject.StoreID
}
err := h.db.Save(r.Context(), &raw_events.Event{
Platform: "etsy",
StoreID: storeID,
EventID: fmt.Sprint(ts.Unix()),
EventTimestamp: ts,
Payload: body,
})
if err != nil {
return nil, response.Errorf("error occurred saving the body as the event payload: %w", err)
}
return response.Status(201), nil
}
// GET h.cfg.OAuthRedirectURIWithAcctIDParam
func (h webhooks) redirectURI(c *gin.Context) (response.Response, error) {
r := c.Request
// get account id for the request
acctID, err := strconv.ParseInt(c.Param("acctID"), 10, 64)
if err != nil || acctID <= 0 {
return nil, response.NotFound()
}
ctx := r.Context()
q := r.URL.Query()
state := q.Get("state")
// handle failed, potentially non-consenting, request
if errCode := q.Get("error"); errCode != "" {
errDesc := q.Get("error_description")
errURI := q.Get("error_uri")
fmt.Printf(
"error in obtaining an OAuth Token: error=%s, error_desc=%s, error_uri=%s, account_id=%d\n",
errCode,
errDesc,
errURI,
acctID,
)
h.etsy.InvalidateState(ctx, state)
return response.Status(200), nil
}
// validate the state to prevent CSRF attacks
ok, err := h.etsy.HandleNewAuthCode(ctx, acctID, state, q.Get("code"))
if err != nil {
h.log.Error("failed to handle new auth code", "error", err)
return nil, response.Forbidden()
}
if !ok {
return nil, response.Forbidden()
}
// redirect to the user's account page
return response.SeeOther(fmt.Sprintf("/accounts/%d", acctID)), nil
}
// GET /:acctID/new-account-link
func (h webhooks) newAccountLink(c *gin.Context) (response.Response, error) {
r := c.Request
acctIDStr := c.Param("acctID")
acctID, err := strconv.ParseInt(acctIDStr, 10, 64)
if err != nil {
return nil, response.NotFound().Msgf("account %s not found", acctIDStr)
}
u, err := h.etsy.GenerateConnectionURLForNewAccount(r.Context(), acctID)
if err != nil {
return nil, fmt.Errorf("failed to generate url for account %d: %w", acctID, err)
}
return response.TemporaryRedirect(u.String()), nil
}