Skip to content

Commit 525e84f

Browse files
Merge pull request #2 from hookdeck/feat/unauthenticated-user
feat: logout command & creating guest access when calling listen
2 parents 45758dd + 9779230 commit 525e84f

File tree

8 files changed

+219
-30
lines changed

8 files changed

+219
-30
lines changed

README.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# Hookdeck CLI
22

3-
> Hookdeck CLI is in public beta. A **free** account is required, and we will allow for unauthenticated use cases in the short future!
4-
53
Using the Hookdeck CLI, you can forward your webhooks to your local webserver. We offer unlimited **free** and **permanent** webhook URLs. You webhook history is preserved between session and can be viewed, replayed or used for testing by you and your teammates.
64

75
Hookdeck CLI is compatible with most of Hookdeck features such as filtering and fan-out delivery. You can use Hookdeck CLI to develop or test your webhook integration code locally.
@@ -14,7 +12,6 @@ For a complete reference, see the [CLI reference](https://hookdeck.com/cli)
1412

1513
![demo](docs/cli-demo.gif)
1614

17-
1815
## Installation
1916

2017
Hookdeck CLI is available for macOS, Windows, and Linux for distros like Ubuntu, Debian, RedHat and CentOS.
@@ -36,7 +33,7 @@ scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
3633
scoop install hookdeck
3734
```
3835

39-
### Linux Or Without package managers
36+
### Linux Or Without package managers
4037

4138
To install the Hookdeck CLI on Linux without a package manager:
4239

@@ -74,6 +71,8 @@ Login with your Hookdeck account.
7471
hookdeck login
7572
```
7673

74+
> Login is optional, if you do not login a temporary guest account will be created for you when you run other commands.
75+
7776
### Listen
7877

7978
Start a session to forward your webhooks to a local HTTP server.
@@ -82,7 +81,7 @@ Start a session to forward your webhooks to a local HTTP server.
8281
hookdeck listen <port> <source-alias?> <connection-query?>
8382
```
8483

85-
Hookdeck works by routing webhooks receive for a given `source` (ie: Shopify, Github, etc.) to its defined `destination` by connecting them with a `connection` to a `destination`. The CLI allows you to receive webhooks for any given connection and forward them to your localhost at the specified port.
84+
Hookdeck works by routing webhooks receive for a given `source` (ie: Shopify, Github, etc.) to its defined `destination` by connecting them with a `connection` to a `destination`. The CLI allows you to receive webhooks for any given connection and forward them to your localhost at the specified port.
8685

8786
Each `source` is assigned a Webhook URL, which you can use to receive webhooks. When starting with a fresh account, the CLI will prompt you to create your first source. Each CLI process can listen to one source at a time.
8887

@@ -91,20 +90,21 @@ Contrarily to ngrok, **Hookdeck does not allow to append a path to your Webhook
9190
> The `port` param is mandatory, webhooks will be forwarded to http://localhost:$PORT/$DESTINATION_PATH
9291
9392
#### Listen to all your connections for a given source
93+
9494
The second param, `source-alias` is used to select a specific source to listen on. By default, the CLI will start listening on all eligible connections for that source.
9595

9696
```sh-session
9797
$ hookdeck listen 3000 shopify
9898

99-
╭ Shopify ───────────────────────────────────────────────────────────────╮
100-
│ │
101-
│ 🔌 Webhook URL: http://localhost:5000/e/src_dgRnekOhKKZe7KqyXK88Uajr │
102-
│ │
103-
╰────────────────────────────────────────────────────────────────────────╯
99+
👉 Inspect and replay webhooks: https://dashboard.hookdeck.com/cli/events
100+
101+
Shopify Source
102+
🔌 Webhook URL: https://events.hookdeck.com/e/src_DAjaFWyyZXsFdZrTOKpuHnOH
103+
104+
Connections
104105
Inventory Service forwarding to /webhooks/shopify/inventory
105106
Orders Service forwarding to /webhooks/shopify/orders
106107

107-
👉 Inspect and replay webhooks: https://dashboard.hookdeck.io/events/cli
108108

109109
⣾ Getting ready...
110110

@@ -117,23 +117,22 @@ The 3rd param, `connection-query` can be used to filter the list of connections
117117
```sh-session
118118
$ hookdeck listen 3000 shopify orders
119119

120-
╭ Shopify ───────────────────────────────────────────────────────────────╮
121-
│ │
122-
│ 🔌 Webhook URL: http://localhost:5000/e/src_dgRnekOhKKZe7KqyXK88Uajr │
123-
│ │
124-
╰────────────────────────────────────────────────────────────────────────╯
120+
👉 Inspect and replay webhooks: https://dashboard.hookdeck.com/cli/events
121+
122+
Shopify Source
123+
🔌 Webhook URL: https://events.hookdeck.com/e/src_DAjaFWyyZXsFdZrTOKpuHnOH
124+
125+
Connections
125126
Inventory Service forwarding to /webhooks/shopify/inventory
126127

127-
👉 Inspect and replay webhooks: https://dashboard.hookdeck.io/events/cli
128128

129129
⣾ Getting ready...
130130

131131
```
132132

133133
#### Viewing and interacting with your webhooks
134134

135-
Webhooks logs for your CLI can be found at https://dashboard.hookdeck.io/events/cli. Events can be replayed or saved at any time.
136-
135+
Webhooks logs for your CLI can be found at https://dashboard.hookdeck.com/cli/events. Events can be replayed or saved at any time.
137136

138137
### Version
139138

@@ -152,6 +151,7 @@ hookdeck completion
152151
```
153152

154153
## License
154+
155155
Copyright (c) Hookdeck. All rights reserved.
156156

157157
Licensed under the [Apache License 2.0 license](blob/master/LICENSE).

pkg/cmd/logout.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package cmd
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
6+
"github.com/hookdeck/hookdeck-cli/pkg/logout"
7+
"github.com/hookdeck/hookdeck-cli/pkg/validators"
8+
)
9+
10+
type logoutCmd struct {
11+
cmd *cobra.Command
12+
all bool
13+
}
14+
15+
func newLogoutCmd() *logoutCmd {
16+
lc := &logoutCmd{}
17+
18+
lc.cmd = &cobra.Command{
19+
Use: "logout",
20+
Args: validators.NoArgs,
21+
Short: "Logout of your Hookdeck account",
22+
Long: `Logout of your Hookdeck account to setup the CLI`,
23+
RunE: lc.runLogoutCmd,
24+
}
25+
lc.cmd.Flags().BoolVarP(&lc.all, "all", "a", false, "Clear credentials for all projects you are currently logged into.")
26+
27+
return lc
28+
}
29+
30+
func (lc *logoutCmd) runLogoutCmd(cmd *cobra.Command, args []string) error {
31+
if lc.all {
32+
return logout.All(&Config)
33+
}
34+
return logout.Logout(&Config)
35+
}

pkg/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func init() {
103103
rootCmd.Flags().BoolP("version", "v", false, "Get the version of the Hookdeck CLI")
104104

105105
rootCmd.AddCommand(newLoginCmd().cmd)
106+
rootCmd.AddCommand(newLogoutCmd().cmd)
106107
rootCmd.AddCommand(newListenCmd().cmd)
107108
rootCmd.AddCommand(newCompletionCmd().cmd)
108109
}

pkg/hookdeck/guest.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package hookdeck
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
type GuestUser struct {
11+
Id string `json:"id"`
12+
APIKey string `json:"key"`
13+
Url string `json:"link"`
14+
BrowserURL string `json:"browser_url"`
15+
PollURL string `json:"poll_url"`
16+
}
17+
18+
type CreateGuestUserInput struct {
19+
DeviceName string `json:"device_name"`
20+
}
21+
22+
func (c *Client) CreateGuestUser(input CreateGuestUserInput) (GuestUser, error) {
23+
input_bytes, err := json.Marshal(input)
24+
if err != nil {
25+
return GuestUser{}, err
26+
}
27+
res, err := c.Post(context.Background(), "/cli/guest", input_bytes, nil)
28+
if err != nil {
29+
return GuestUser{}, err
30+
}
31+
if res.StatusCode != http.StatusOK {
32+
return GuestUser{}, fmt.Errorf("Unexpected http status code: %d %s", res.StatusCode)
33+
}
34+
guest_user := GuestUser{}
35+
postprocessJsonResponse(res, &guest_user)
36+
return guest_user, nil
37+
}

pkg/listen/listen.go

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ import (
2121
"net/url"
2222
"regexp"
2323

24-
box "github.com/Delta456/box-cli-maker/v2"
24+
"github.com/hookdeck/hookdeck-cli/pkg/ansi"
2525
"github.com/hookdeck/hookdeck-cli/pkg/config"
2626
"github.com/hookdeck/hookdeck-cli/pkg/hookdeck"
27+
"github.com/hookdeck/hookdeck-cli/pkg/login"
2728
"github.com/hookdeck/hookdeck-cli/pkg/proxy"
29+
"github.com/hookdeck/hookdeck-cli/pkg/validators"
2830
log "github.com/sirupsen/logrus"
2931
)
3032

@@ -35,10 +37,26 @@ type Flags struct {
3537

3638
// listenCmd represents the listen command
3739
func Listen(port string, source_alias string, connection_query string, flags Flags, config *config.Config) error {
40+
var key string
41+
var err error
42+
var guest_url string
3843

39-
key, err := config.Profile.GetAPIKey()
44+
key, err = config.Profile.GetAPIKey()
4045
if err != nil {
41-
return err
46+
errString := err.Error()
47+
if errString == validators.ErrAPIKeyNotConfigured.Error() || errString == validators.ErrDeviceNameNotConfigured.Error() {
48+
guest_url, _ = login.GuestLogin(config)
49+
if guest_url == "" {
50+
return err
51+
}
52+
53+
key, err = config.Profile.GetAPIKey()
54+
if err != nil {
55+
return err
56+
}
57+
} else {
58+
return err
59+
}
4260
}
4361

4462
parsedBaseURL, err := url.Parse(config.APIBaseURL)
@@ -61,24 +79,31 @@ func Listen(port string, source_alias string, connection_query string, flags Fla
6179
return err
6280
}
6381

64-
// Print sources, connections and URLs
6582
fmt.Println()
66-
Box := box.New(box.Config{Px: 2, Py: 1, ContentAlign: "Left", Type: "Round", Color: "White", TitlePos: "Top"})
67-
Box.Print(source.Label, "🔌 Webhook URL: "+source.Url)
83+
fmt.Println(ansi.Bold("Dashboard"))
84+
if guest_url != "" {
85+
fmt.Println("👤 Login URL: " + guest_url)
86+
fmt.Println("Sign up in the dashboard to make your webhook URL permanent.")
87+
fmt.Println()
88+
}
89+
fmt.Println("👉 Inspect and replay webhooks: https://dashboard.hookdeck.com/cli/events")
90+
fmt.Println()
6891

69-
//var connection_ids []string
92+
fmt.Println(ansi.Bold(source.Label + " Source"))
93+
fmt.Println("🔌 Webhook URL: " + source.Url)
94+
fmt.Println()
95+
96+
fmt.Println(ansi.Bold("Connections"))
7097
for _, connection := range connections {
7198
fmt.Println(connection.Label + " forwarding to " + connection.Destination.CliPath)
72-
//connection_ids = append(connection_ids, connection.Id)
7399
}
100+
fmt.Println()
74101

75102
deviceName, err := config.Profile.GetDeviceName()
76103
if err != nil {
77104
return err
78105
}
79106

80-
fmt.Println("\n👉 Inspect and replay webhooks: https://dashboard.hookdeck.io/events/cli\n")
81-
82107
p := proxy.New(&proxy.Config{
83108
DeviceName: deviceName,
84109
Key: key,

pkg/login/client_login.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,49 @@ func Login(config *config.Config, input io.Reader) error {
8484
return nil
8585
}
8686

87+
func GuestLogin(config *config.Config) (string, error) {
88+
parsedBaseURL, err := url.Parse(config.APIBaseURL)
89+
if err != nil {
90+
return "", err
91+
}
92+
93+
client := &hookdeck.Client{
94+
BaseURL: parsedBaseURL,
95+
}
96+
97+
fmt.Println("🚩 Not connected with any account. Creating a guest account...")
98+
99+
guest_user, err := client.CreateGuestUser(hookdeck.CreateGuestUserInput{
100+
DeviceName: config.Profile.DeviceName,
101+
})
102+
if err != nil {
103+
return "", err
104+
}
105+
106+
// Call poll function
107+
response, err := PollForKey(guest_user.PollURL, 0, 0)
108+
if err != nil {
109+
return "", err
110+
}
111+
112+
validateErr := validators.APIKey(response.APIKey)
113+
if validateErr != nil {
114+
return "", validateErr
115+
}
116+
117+
config.Profile.APIKey = response.APIKey
118+
config.Profile.ClientID = response.ClientID
119+
config.Profile.DisplayName = response.UserName
120+
config.Profile.TeamName = response.TeamName
121+
122+
profileErr := config.Profile.CreateProfile()
123+
if profileErr != nil {
124+
return "", profileErr
125+
}
126+
127+
return guest_user.Url, nil
128+
}
129+
87130
func getLinks(baseURL string, deviceName string) (*Links, error) {
88131
parsedBaseURL, err := url.Parse(baseURL)
89132
if err != nil {

pkg/logout/logout.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package logout
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hookdeck/hookdeck-cli/pkg/config"
7+
)
8+
9+
// Logout function is used to clear the credentials set for the current Profile
10+
func Logout(config *config.Config) error {
11+
key, _ := config.Profile.GetAPIKey()
12+
13+
if key == "" {
14+
fmt.Println("You are already logged out.")
15+
return nil
16+
}
17+
18+
fmt.Println("Logging out...")
19+
20+
profileName := config.Profile.ProfileName
21+
22+
err := config.RemoveProfile(profileName)
23+
if err != nil {
24+
return err
25+
}
26+
27+
if profileName == "default" {
28+
fmt.Println("Credentials have been cleared for the default project.")
29+
} else {
30+
fmt.Printf("Credentials have been cleared for %s.\n", profileName)
31+
}
32+
33+
return nil
34+
}
35+
36+
// All function is used to clear the credentials on all profiles
37+
func All(cfg *config.Config) error {
38+
fmt.Println("Logging out...")
39+
40+
err := cfg.RemoveAllProfiles()
41+
if err != nil {
42+
return err
43+
}
44+
45+
fmt.Println("Credentials have been cleared for all projects.")
46+
47+
return nil
48+
}

pkg/proxy/proxy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func (p *Proxy) processEndpointResponse(webhookEvent *websocket.Attempt, resp *h
252252
localTime := time.Now().Format(timeLayout)
253253

254254
color := ansi.Color(os.Stdout)
255-
outputStr := fmt.Sprintf("%s [%d] %s %s | https://dashboard.hookdeck.io/events/%s",
255+
outputStr := fmt.Sprintf("%s [%d] %s %s | https://dashboard.hookdeck.com/events/%s",
256256
color.Faint(localTime),
257257
ansi.ColorizeStatus(resp.StatusCode),
258258
resp.Request.Method,

0 commit comments

Comments
 (0)