Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
yanosea committed Feb 27, 2025
1 parent 821ac97 commit 9d13111
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 53 deletions.
55 changes: 29 additions & 26 deletions app/application/spotlike/auth_usecase.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package spotlike

import (
"net/http"
"net/url"

spotify "github.com/zmb3/spotify/v2"
spotifyauth "github.com/zmb3/spotify/v2/auth"

"github.com/yanosea/spotlike/app/config"
Expand All @@ -9,63 +13,62 @@ import (
// authUseCase is a struct that contains the use case of authenticating the Spotify client.
type authUseCase struct {
Authenticator *spotifyauth.Authenticator
State string
Config *config.SpotlikeConfig
}

// NewAuthUseCase returns a new instance of the AuthUseCase struct.
func NewAuthUseCase(authenticator *spotifyauth.Authenticator, conf *config.SpotlikeConfig) *authUseCase {
func NewAuthUseCase(authenticator *spotifyauth.Authenticator, state string, conf *config.SpotlikeConfig) *authUseCase {
return &authUseCase{
Authenticator: authenticator,
State: state,
Config: conf,
}
}

// Run returns the output of the AuthUseCase.
func (uc *authUseCase) Run() error {
func (uc *authUseCase) Run() (*spotify.Client, string, error) {
// get port from uri
port, err := getPortFromUri(uc.Config.SpotifyRedirectUri)
if err != nil {
return err
return nil, "", err
}

var (
client spotifyproxy.ClientInstanceInterface
channel = make(chan spotifyproxy.ClientInstanceInterface)
client *spotify.Client
clientChan = make(chan *spotify.Client)
errChan = make(chan error)
refreshTokenChan = make(chan string)
)

a.Http.HandleFunc("/callback", func(w httpproxy.ResponseWriterInstanceInterface, r *httpproxy.RequestInstance) {
tok, err := a.Authenticator.Token(r.Context(), a.State, r)
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
tok, err := uc.Authenticator.Token(r.Context(), uc.State, r)
if err != nil {
a.Http.Error(w, err, httpproxy.StatusForbidden)
http.Error(w, err.Error(), http.StatusForbidden)
return
}

if st := r.FormValue("state"); st != a.State {
a.Http.NotFound(w, r)
if st := r.FormValue("state"); st != uc.State {
http.NotFound(w, r)
return
}

client := a.Spotify.NewClient(a.Authenticator.Client(r.Context(), tok))
client := spotify.New(uc.Authenticator.Client(r.Context(), tok))

a.RefreshToken = tok.FieldToken.RefreshToken
channel <- client
clientChan <- client
refreshTokenChan <- tok.RefreshToken
})

a.Http.HandleFunc("/", func(w httpproxy.ResponseWriterInstanceInterface, r *httpproxy.RequestInstance) {})
go func() error {
err := a.Http.ListenAndServe(":"+port, nil)
if err != nil {
return err
}
return nil
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})
go func() {
err := http.ListenAndServe(":"+port, nil)
errChan <- err
}()
client = <-channel

if client == nil {
return nil, AuthenticateFailed, nil
}
client = <-clientChan
refreshToken := <-refreshTokenChan
err = <-errChan

return client, AuthenticatedSuccessfully, nil
return client, refreshToken, err
}

// getPortFromUri gets port from uri.
Expand Down
7 changes: 4 additions & 3 deletions app/application/spotlike/get_auth_url_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func NewGetAuthUrlUseCase(authenticator *spotifyauth.Authenticator) *getAuthUrlU
}
}

// Run returns the authentication URL.
func (uc *getAuthUrlUseCase) Run() string {
return uc.Authenticator.AuthURL(randstr.Hex(11))
// Run returns the authentication URL and state.
func (uc *getAuthUrlUseCase) Run() (string, string) {
state := randstr.Hex(11)
return uc.Authenticator.AuthURL(state), state
}
32 changes: 26 additions & 6 deletions app/application/spotlike/refresh_usecase.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
package spotlike

import ()
import (
"context"

"golang.org/x/oauth2"

spotify "github.com/zmb3/spotify/v2"
spotifyauth "github.com/zmb3/spotify/v2/auth"

"github.com/yanosea/spotlike/app/config"
)

// refreshUseCase is a struct that contains the use case of refreshenticating the Spotify client.
type refreshUseCase struct{}
type refreshUseCase struct {
Authenticator *spotifyauth.Authenticator
Config *config.SpotlikeConfig
}

// NewRefreshUseCase returns a new instance of the RefreshUseCase struct.
func NewRefreshUseCase() *refreshUseCase {
return &refreshUseCase{}
func NewRefreshUseCase(authenticator *spotifyauth.Authenticator, conf *config.SpotlikeConfig) *refreshUseCase {
return &refreshUseCase{
Authenticator: authenticator,
Config: conf,
}
}

// Run returns the output of the RefreshUseCase.
func (uc *refreshUseCase) Run() error {
return nil
func (uc *refreshUseCase) Run() *spotify.Client {
tok := &oauth2.Token{
TokenType: "bearer",
RefreshToken: uc.Config.SpotifyRefreshToken,
}

return spotify.New(uc.Authenticator.Client(context.Background(), tok))
}
5 changes: 5 additions & 0 deletions app/presentation/cli/spotlike/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ func NewRootCommand(
output,
)
cmd.AddCommand(
spotlike.NewAuthCommand(
cobra,
conf,
output,
),
completion.NewCompletionCommand(
cobra,
output,
Expand Down
37 changes: 23 additions & 14 deletions app/presentation/cli/spotlike/command/spotlike/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func NewAuthCommand(
cmd := cobra.NewCommand()
cmd.SetUse("auth")
cmd.SetAliases([]string{"au", "a"})
cmd.SetUsageTemplate(versionUsageTemplate)
cmd.SetHelpTemplate(versionHelpTemplate)
cmd.SetUsageTemplate(authUsageTemplate)
cmd.SetHelpTemplate(authHelpTemplate)
cmd.SetArgs(cobra.ExactArgs(0))
cmd.SetSilenceErrors(true)
cmd.Flags().StringVarP(
Expand Down Expand Up @@ -156,28 +156,37 @@ func runAuth(conf *config.SpotlikeCliConfig, output *string) error {

if conf.SpotifyRefreshToken == "" {
guuc := spotlikeApp.NewGetAuthUrlUseCase(authenticator)
presenter.Print(os.Stdout, guuc.Run())
authUrl, state := guuc.Run()

auc := spotlikeApp.NewAuthUseCase(authenticator, bc)
if err := auc.Run(); err != nil {
presenter.Print(os.Stdout, "🌐 Log in to Spotify by visiting the page below in your browser.")
presenter.Print(os.Stdout, authUrl)

auc := spotlikeApp.NewAuthUseCase(authenticator, state, bc)
_, refreshToken, err := auc.Run()
if err != nil {
o := formatter.Red("❌ Failed to authenticate... Please try again...")
*output = o
return err
}

o := formatter.Green("πŸŽ‰ Authentication succeeded!") + "\n"
o += formatter.Green("If you don't want spotlike to ask questions above again, execute commands below to set envs or set your profile to set those.") + "\n"
o += formatter.Green("export SPOTIFY_ID="+conf.SpotifyID) + "\n"
o += formatter.Green("export SPOTIFY_SECRET="+conf.SpotifySecret) + "\n"
o += formatter.Green("export SPOTIFY_REDIRECT_URI="+authUrl) + "\n"
o += formatter.Green("export SPOTIFY_REFRESH_TOKEN="+refreshToken) + "\n"
*output = o
} else {
ruc := spotlikeApp.NewRefreshUseCase(authenticator, bc)
if err := ruc.Refresh(); err != nil {
if err := ruc.Run(); err != nil {
o := formatter.Red("❌ Failed to refresh... Please try again...")
*output = o
return err
}
if client := ruc.Run(); client == nil {
o := formatter.Red("❌ Failed to refresh... Please try again...")
*output = o
return nil
}
o := formatter.Green("πŸŽ‰ Refresh succeeded!") + "\n"
*output = o
}

o := formatter.Green("πŸŽ‰ Authentication succeeded!")
*output = o

return nil
}

Expand Down
8 changes: 4 additions & 4 deletions app/presentation/cli/spotlike/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ type SpotlikeCliConfig struct {

// envConfig is a struct that contains the environment variables.
type envConfig struct {
SpotifyID string `envconfig:"SPOTLIKE_SPOTIFY_ID"`
SpotifySecret string `envconfig:"SPOTLIKE_SPOTIFY_SECRET"`
SpotifyRedirectUri string `envconfig:"SPOTLIKE_SPOTIFY_REDIRECT_URI"`
SpotifyRefreshToken string `envconfig:"SPOTLIKE_SPOTIFY_REFRESH_TOKEN"`
SpotifyID string `envconfig:"SPOTIFY_ID"`
SpotifySecret string `envconfig:"SPOTIFY_SECRET"`
SpotifyRedirectUri string `envconfig:"SPOTIFY_REDIRECT_URI"`
SpotifyRefreshToken string `envconfig:"SPOTIFY_REFRESH_TOKEN"`
}

// GetConfig gets the configuration of the Spotlike cli application.
Expand Down

0 comments on commit 9d13111

Please sign in to comment.