Skip to content

Commit

Permalink
feat: change startup message
Browse files Browse the repository at this point in the history
  • Loading branch information
upvest-mike committed Sep 25, 2024
1 parent 6677217 commit 380bc91
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 59 deletions.
73 changes: 59 additions & 14 deletions service/runtime/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
"github.com/valyala/fastjson"
)

var errTunnelNotAvailable = errors.New("events tunnel is not available")

type ApiClient interface {
Authorise(context.Context, string) error
CreateWebhook(context.Context, WebhookRequest) (string, error)
Expand All @@ -39,6 +41,16 @@ type ApiClient interface {
OpenEndpoint(context.Context) (string, string, error)
CloseEndpoint(context.Context, string) error
GetEvents(context.Context, string) ([]PullItem, int, error)
TunnelIsReady(context.Context) error
}

func serviceIsNotAccessible(code int) bool {
return code == http.StatusNotFound || code == http.StatusServiceUnavailable || code == http.StatusBadGateway
}

var anonUserCredentials = UserCredentials{
ClientID: "00000000-0000-0000-0000-000000000000",
ClientSecret: "",
}

func NewClient(proxyAddress string, usersCredentials UserCredentials, timeout time.Duration) ApiClient {
Expand Down Expand Up @@ -120,15 +132,24 @@ type PullItem struct {
CreatedAt time.Time `json:"created_at"`
}

func (e *apiClient) GetEvents(ctx context.Context, tunnelID string) ([]PullItem, int, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e.proxyAddress+"/events-acceptor-service/endpoints/"+tunnelID, nil)
func (e *apiClient) TunnelIsReady(ctx context.Context) error {
_, code, err := e.get(ctx, "/events-acceptor-service/health")
if err != nil {
return nil, 0, errors.Wrap(err, "NewRequestWithContext")
return errors.Wrap(err, "io")
}
e.addHeaders(req)
code, body, err := e.io(req)
if serviceIsNotAccessible(code) {
return errTunnelNotAvailable
}
if code != http.StatusOK {
return errors.New("wrong http code: " + strconv.Itoa(code))
}
return nil
}

func (e *apiClient) GetEvents(ctx context.Context, tunnelID string) ([]PullItem, int, error) {
body, code, err := e.get(ctx, "/events-acceptor-service/endpoints/"+tunnelID)
if err != nil {
return nil, 0, errors.Wrap(err, "io")
return nil, 0, errors.Wrap(err, "get")
}
var res []PullItem
if code == http.StatusOK {
Expand All @@ -140,10 +161,13 @@ func (e *apiClient) GetEvents(ctx context.Context, tunnelID string) ([]PullItem,
}

func (e *apiClient) OpenEndpoint(ctx context.Context) (string, string, error) {
body, err := e.create(ctx, "", "/events-acceptor-service/endpoints")
body, code, err := e.post(ctx, "", "/events-acceptor-service/endpoints")
if err != nil {
return "", "", errors.Wrap(err, "create")
}
if code != http.StatusCreated {
return "", "", errors.Wrap(err, "Wrong HTTP code: "+strconv.Itoa(code))
}
url := fastjson.GetString(body, "url")
if len(url) == 0 {
return "", "", errors.New("no tunnel url")
Expand Down Expand Up @@ -180,23 +204,44 @@ func (e *apiClient) delete(ctx context.Context, uri string) error {
}

func (e *apiClient) create(ctx context.Context, request interface{}, uri string) ([]byte, error) {
body, code, err := e.post(ctx, request, uri)
if err != nil {
return nil, errors.Wrap(err, "createWithCode")
}
if code != http.StatusCreated {
return nil, errors.New("Wrong http code: " + strconv.Itoa(code))
}
return body, err
}

func (e *apiClient) get(ctx context.Context, uri string) ([]byte, int, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e.proxyAddress+uri, nil)
if err != nil {
return nil, 0, errors.Wrap(err, "NewRequestWithContext")
}
e.addHeaders(req)
code, body, err := e.io(req)
if err != nil {
return nil, 0, errors.Wrap(err, "io")
}
return body, code, err
}

func (e *apiClient) post(ctx context.Context, request interface{}, uri string) ([]byte, int, error) {
out, err := json.Marshal(request)
if err != nil {
return nil, errors.Wrap(err, "Marshal")
return nil, 0, errors.Wrap(err, "Marshal")
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, e.proxyAddress+uri, bytes.NewBuffer(out))
if err != nil {
return nil, errors.Wrap(err, "NewRequestWithContext")
return nil, 0, errors.Wrap(err, "NewRequestWithContext")
}
e.addHeaders(req)
code, body, err := e.io(req)
if err != nil {
return nil, errors.Wrap(err, "io")
}
if code != http.StatusCreated {
return nil, errors.New("Wrong http code: " + strconv.Itoa(code))
return nil, 0, errors.Wrap(err, "io")
}
return body, err
return body, code, err
}

func (e *apiClient) patch(ctx context.Context, request interface{}, uri string) error {
Expand Down
30 changes: 24 additions & 6 deletions service/runtime/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/pkg/errors"
"github.com/upvestco/httpsignature-proxy/service/logger"
"golang.org/x/exp/maps"
"golang.org/x/exp/rand"
)

type tunnel struct {
Expand Down Expand Up @@ -84,9 +85,6 @@ func (e *tunnel) doPulling(ctx context.Context, endpointID string) error {
if errors.Is(err, context.Canceled) {
return nil
}
if errors.Is(err, syscall.ECONNREFUSED) {
return nil
}
return errors.Wrap(err, "pullEvents")
}
}
Expand All @@ -107,6 +105,9 @@ func (e *tunnel) pullEvents(ctx context.Context, endpointID string) error {
return errors.Wrap(err, "doPull")
}
}
if serviceIsNotAccessible(code) {
return errTunnelNotAvailable
}
if code != http.StatusOK {
return errors.New("unexpected http response code: " + strconv.Itoa(code))
}
Expand Down Expand Up @@ -194,8 +195,16 @@ func (e *tunnel) start() error {
ctx, cancel := context.WithCancel(context.Background())
e.cancel = cancel

if err := e.apiClient.TunnelIsReady(ctx); err != nil {
if errors.Is(err, errTunnelNotAvailable) {
return errors.Wrap(errTunnelNotAvailable, "Webhook events listening is not available")
}
return errors.Wrap(err, "Could not create tunnel.")
}

if err := e.apiClient.Authorise(ctx, requiredScopes); err != nil {
return errors.Wrap(err, "Could not open the Webhook events tunnel. You client must have '"+requiredScopes+"' scope(s)")
e.logger.PrintLn(lightRed("Could not open the Webhook events tunnel. You client must have '" + requiredScopes + "' scope(s)"))
return nil
}
e.logger.LogF("client is authorised with '" + requiredScopes + "' scope(s)")

Expand All @@ -206,7 +215,7 @@ func (e *tunnel) start() error {
e.logger.LogF("backend endpoint (%s) for the client is created", endpoint)

request := WebhookRequest{
Title: "http signature temporary webhook",
Title: "http signature webhook " + randomString(8),
Url: endpoint,
Type: []string{"ALL"},
Config: &WebhookConfig{
Expand Down Expand Up @@ -244,7 +253,7 @@ func (e *tunnel) start() error {
}
}

return errors.Wrap(poolErr, "doPulling")
return errors.Wrap(poolErr, "doPulling on webhook "+webhookID)
}

func (e *tunnel) destroy() {
Expand All @@ -260,3 +269,12 @@ type Payload struct {
WebhookId string `json:"webhook_id"`
} `json:"payload"`
}

func randomString(n int) string {
const letters = "abcdefghijklmnopqrstuvwxyz"
b := make([]byte, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
53 changes: 14 additions & 39 deletions service/runtime/tunnels.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ package runtime

import (
"context"
"encoding/json"
"io"
"net/http"
"sync"
"time"

"github.com/pkg/errors"
"github.com/upvestco/httpsignature-proxy/service/logger"
Expand Down Expand Up @@ -56,44 +52,23 @@ func (e *Tunnels) Stop() {
e.closeGroup.Wait()
}

func AskForUserCredentials(proxyAddress string) (UserCredentials, int, error) {
client := http.Client{
Timeout: time.Second,
}
ctx := context.Background()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, proxyAddress+"/proxy-pass", nil)
if err != nil {
return UserCredentials{}, 0, errors.Wrap(err, "NewRequestWithContext")
}
resp, err := client.Do(req)
if err != nil {
return UserCredentials{}, 0, errors.Wrap(err, "DefaultClient.Do")
}
defer func() {
_ = resp.Body.Close()
}()
func (e *Tunnels) Start(userCredentialsCh chan UserCredentials) {
ctx, cancel := context.WithCancel(context.Background())
e.cancel = cancel

if resp.StatusCode != http.StatusAccepted {
return UserCredentials{}, resp.StatusCode, nil
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return UserCredentials{}, 0, errors.Wrap(err, "ReadAll")
if err := e.createApiClient(anonUserCredentials).TunnelIsReady(ctx); err != nil {
if errors.Is(err, errTunnelNotAvailable) {
e.logger.PrintLn(cyan("Webhook events listening is not available"))
} else {
e.logger.Log(err.Error())
}
return
}
var uc UserCredentials

if err := json.Unmarshal(body, &uc); err != nil {
return UserCredentials{}, http.StatusBadRequest, nil
}
return uc, resp.StatusCode, nil
}
e.logger.PrintLn(cyan("############################################################"))
e.logger.PrintLn(cyan("To start listening webhook events - send /auth/token request"))
e.logger.PrintLn(cyan("############################################################"))

func (e *Tunnels) Start(userCredentialsCh chan UserCredentials) {
e.logger.Print(cyan("############################################################\n"))
e.logger.Print(cyan("To start listening webhook events - send /auth/token request\n"))
e.logger.Print(cyan("############################################################\n"))
ctx, cancel := context.WithCancel(context.Background())
e.cancel = cancel
for {
select {
case <-ctx.Done():
Expand All @@ -111,7 +86,7 @@ func (e *Tunnels) Start(userCredentialsCh chan UserCredentials) {
e.closeGroup.Add(1)
go func(t *tunnel, uc UserCredentials) {
if err := t.start(); err != nil {
e.logger.Log(err.Error() + "\nListener closed")
e.logger.PrintLn(lightRed(err.Error()))
}
e.tunnels.remove(uc.ClientID)
e.closeGroup.Done()
Expand Down

0 comments on commit 380bc91

Please sign in to comment.