alby_http_service.go raw

   1  package http
   2  
   3  import (
   4  	"errors"
   5  	"fmt"
   6  	"net/http"
   7  
   8  	"github.com/getAlby/hub/alby"
   9  	"github.com/getAlby/hub/config"
  10  	"github.com/getAlby/hub/logger"
  11  	"github.com/getAlby/hub/service"
  12  	"github.com/labstack/echo/v4"
  13  )
  14  
  15  type AlbyHttpService struct {
  16  	albySvc      alby.AlbyService
  17  	albyOAuthSvc alby.AlbyOAuthService
  18  	appConfig    *config.AppConfig
  19  	svc          service.Service
  20  }
  21  
  22  func NewAlbyHttpService(svc service.Service, albySvc alby.AlbyService, albyOAuthSvc alby.AlbyOAuthService, appConfig *config.AppConfig) *AlbyHttpService {
  23  	return &AlbyHttpService{
  24  		albySvc:      albySvc,
  25  		albyOAuthSvc: albyOAuthSvc,
  26  		appConfig:    appConfig,
  27  		svc:          svc,
  28  	}
  29  }
  30  
  31  func (albyHttpSvc *AlbyHttpService) RegisterSharedRoutes(readOnlyApiGroup *echo.Group, fullAccessApiGroup *echo.Group, e *echo.Echo) {
  32  	e.GET("/api/alby/callback", albyHttpSvc.albyCallbackHandler)
  33  	e.GET("/api/alby/info", albyHttpSvc.albyInfoHandler)
  34  	e.GET("/api/alby/rates/:currency", albyHttpSvc.albyBitcoinRateHandler)
  35  	e.GET("/api/alby/currencies", albyHttpSvc.albyCurrenciesHandler)
  36  	e.GET("/api/alby/stories", albyHttpSvc.albyStoriesHandler)
  37  	readOnlyApiGroup.GET("/alby/me", albyHttpSvc.albyMeHandler)
  38  	fullAccessApiGroup.POST("/alby/link-account", albyHttpSvc.albyLinkAccountHandler)
  39  	fullAccessApiGroup.POST("/alby/auto-channel", albyHttpSvc.autoChannelHandler)
  40  	fullAccessApiGroup.POST("/alby/unlink-account", albyHttpSvc.unlinkHandler)
  41  }
  42  
  43  func (albyHttpSvc *AlbyHttpService) autoChannelHandler(c echo.Context) error {
  44  	ctx := c.Request().Context()
  45  
  46  	var autoChannelRequest alby.AutoChannelRequest
  47  	if err := c.Bind(&autoChannelRequest); err != nil {
  48  		return c.JSON(http.StatusBadRequest, ErrorResponse{
  49  			Message: fmt.Sprintf("Bad request: %s", err.Error()),
  50  		})
  51  	}
  52  
  53  	autoChannelResponseResponse, err := albyHttpSvc.albyOAuthSvc.RequestAutoChannel(ctx, albyHttpSvc.svc.GetLNClient(), autoChannelRequest.IsPublic)
  54  
  55  	if err != nil {
  56  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
  57  			Message: fmt.Sprintf("Failed to request auto channel: %s", err.Error()),
  58  		})
  59  	}
  60  
  61  	return c.JSON(http.StatusOK, autoChannelResponseResponse)
  62  }
  63  
  64  func (albyHttpSvc *AlbyHttpService) unlinkHandler(c echo.Context) error {
  65  	ctx := c.Request().Context()
  66  
  67  	err := albyHttpSvc.albyOAuthSvc.UnlinkAccount(ctx)
  68  
  69  	if err != nil {
  70  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
  71  			Message: fmt.Sprintf("Failed to unlink: %s", err.Error()),
  72  		})
  73  	}
  74  
  75  	return c.NoContent(http.StatusNoContent)
  76  }
  77  
  78  func (albyHttpSvc *AlbyHttpService) albyInfoHandler(c echo.Context) error {
  79  	info, err := albyHttpSvc.albySvc.GetInfo(c.Request().Context())
  80  	if err != nil {
  81  		logger.Logger.WithError(err).Error("Failed to request alby info endpoint")
  82  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
  83  			Message: fmt.Sprintf("Failed to request alby info endpoint: %s", err.Error()),
  84  		})
  85  	}
  86  
  87  	return c.JSON(http.StatusOK, info)
  88  }
  89  
  90  func (albyHttpSvc *AlbyHttpService) albyBitcoinRateHandler(c echo.Context) error {
  91  	rate, err := albyHttpSvc.albySvc.GetBitcoinRate(c.Request().Context(), c.Param("currency"))
  92  	if err != nil {
  93  		logger.Logger.WithError(err).Error("Failed to get Bitcoin rate")
  94  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
  95  			Message: fmt.Sprintf("Failed to get Bitcoin rate: %s", err.Error()),
  96  		})
  97  	}
  98  	return c.JSON(http.StatusOK, rate)
  99  }
 100  
 101  func (albyHttpSvc *AlbyHttpService) albyCurrenciesHandler(c echo.Context) error {
 102  	currencies, err := albyHttpSvc.albySvc.GetCurrencies(c.Request().Context())
 103  	if err != nil {
 104  		logger.Logger.WithError(err).Error("Failed to get currencies")
 105  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
 106  			Message: fmt.Sprintf("Failed to get currencies: %s", err.Error()),
 107  		})
 108  	}
 109  
 110  	return c.JSON(http.StatusOK, currencies)
 111  }
 112  
 113  func (albyHttpSvc *AlbyHttpService) albyStoriesHandler(c echo.Context) error {
 114  	stories, err := albyHttpSvc.albyOAuthSvc.GetStories(c.Request().Context())
 115  	if err != nil {
 116  		logger.Logger.WithError(err).Error("Failed to get stories")
 117  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
 118  			Message: fmt.Sprintf("Failed to get stories: %s", err.Error()),
 119  		})
 120  	}
 121  	return c.JSON(http.StatusOK, stories)
 122  }
 123  
 124  func (albyHttpSvc *AlbyHttpService) albyCallbackHandler(c echo.Context) error {
 125  	code := c.QueryParam("code")
 126  
 127  	err := albyHttpSvc.albyOAuthSvc.CallbackHandler(c.Request().Context(), code)
 128  	if err != nil {
 129  		logger.Logger.WithError(err).Error("Failed to handle Alby OAuth callback")
 130  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
 131  			Message: fmt.Sprintf("Failed to handle Alby OAuth callback: %s", err.Error()),
 132  		})
 133  	}
 134  
 135  	if albyHttpSvc.appConfig.IsDefaultClientId() {
 136  		// do not redirect if using default OAuth client
 137  		// redirect will be handled by the frontend instead
 138  		return c.NoContent(http.StatusNoContent)
 139  	}
 140  
 141  	redirectUrl := albyHttpSvc.appConfig.GetBaseFrontendUrl()
 142  
 143  	if redirectUrl == "" {
 144  		// OAuth using a custom client requires a base URL set for the callback
 145  		return errors.New("no BASE_URL set")
 146  	}
 147  
 148  	return c.Redirect(http.StatusFound, redirectUrl)
 149  }
 150  
 151  func (albyHttpSvc *AlbyHttpService) albyMeHandler(c echo.Context) error {
 152  	me, err := albyHttpSvc.albyOAuthSvc.GetMe(c.Request().Context())
 153  	if err != nil {
 154  		logger.Logger.WithError(err).Error("Failed to request alby me endpoint")
 155  		return c.JSON(http.StatusInternalServerError, ErrorResponse{
 156  			Message: fmt.Sprintf("Failed to request alby me endpoint: %s", err.Error()),
 157  		})
 158  	}
 159  
 160  	return c.JSON(http.StatusOK, me)
 161  }
 162  
 163  func (albyHttpSvc *AlbyHttpService) albyLinkAccountHandler(c echo.Context) error {
 164  	var linkAccountRequest alby.AlbyLinkAccountRequest
 165  	if err := c.Bind(&linkAccountRequest); err != nil {
 166  		return c.JSON(http.StatusBadRequest, ErrorResponse{
 167  			Message: fmt.Sprintf("Bad request: %s", err.Error()),
 168  		})
 169  	}
 170  
 171  	err := albyHttpSvc.albyOAuthSvc.LinkAccount(c.Request().Context(), albyHttpSvc.svc.GetLNClient(), linkAccountRequest.Budget, linkAccountRequest.Renewal)
 172  	if err != nil {
 173  		logger.Logger.WithError(err).Error("Failed to connect alby account")
 174  		return err
 175  	}
 176  
 177  	return c.NoContent(http.StatusNoContent)
 178  }
 179