Skip to content

Commit

Permalink
Update retry handler used by Pro Client (#1238)
Browse files Browse the repository at this point in the history
* move code to create user with backoff to pro client

* move code to create user with backoff to pro client

* move create user code to pro client

* clean-ups

* Update desktop to use same code to retry creating user

* Update desktop to use same code to retry creating user

* revert changes

* Add client session mock

* clean-ups

* clean-ups

* clean-ups

* clean-ups

* update flashlight and run go mod tidy

* update flashlight and run go mod tidy

* Remove old tests

* Stop polling if user becomes pro and some other clean-ups

* Fix user create api on android. (#1246)

* Fix user create api on android.

* Fix desktop build issue.

* Add back auth methods for now

* move plans and payment methods calls to pro client

* move plans and payment methods calls to pro client

* Fixed issue on desktop while creating user and other small fixes.

* remove commented code.

* clean-ups

---------

Co-authored-by: jigar-f <[email protected]>
Co-authored-by: Jigar-f <[email protected]>
  • Loading branch information
3 people authored Dec 5, 2024
1 parent c5a06ad commit c63862b
Show file tree
Hide file tree
Showing 22 changed files with 1,522 additions and 409 deletions.
4 changes: 2 additions & 2 deletions android/.project
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
</natures>
<filteredResources>
<filter>
<id>1620924294143</id>
<id>1732230225439</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
Expand Down
192 changes: 44 additions & 148 deletions desktop/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ type App struct {
ws ws.UIChannel

cachedUserData sync.Map
plansCache sync.Map

onUserData []func(current *protos.User, new *protos.User)

Expand Down Expand Up @@ -384,7 +383,7 @@ func (app *App) OnStatsChange(fn func(stats.Stats)) {
func (app *App) afterStart(cl *flashlightClient.Client) {
ctx := context.Background()
go app.fetchOrCreateUser(ctx)
go app.GetPaymentMethods(ctx)
go app.proClient.DesktopPaymentMethods(ctx)
go app.fetchDeviceLinkingCode(ctx)

app.OnSettingChange(settings.SNSystemProxy, func(val interface{}) {
Expand Down Expand Up @@ -570,17 +569,18 @@ func (app *App) IsPro() bool {
return isPro
}

func (app *App) fetchOrCreateUser(ctx context.Context) (*protos.User, error) {
settings := app.Settings()
lang := settings.GetLanguage()
func (app *App) fetchOrCreateUser(ctx context.Context) {
ss := app.Settings()
lang := ss.GetLanguage()
if lang == "" {
// set default language
settings.SetLanguage("en_us")
ss.SetLanguage("en_us")
}
if userID := settings.GetUserID(); userID == 0 {
return app.CreateUser(ctx)
if userID := ss.GetUserID(); userID == 0 {
ss.SetUserFirstVisit(true)
app.proClient.RetryCreateUser(ctx, app, 5*time.Minute)
} else {
return app.UserData(ctx)
app.proClient.UpdateUserData(ctx, app)
}
}

Expand All @@ -600,145 +600,6 @@ func (app *App) fetchDeviceLinkingCode(ctx context.Context) (string, error) {
return resp.Code, nil
}

// CreateUser is used when Lantern is run for the first time and creates a new user with the pro server
func (app *App) CreateUser(ctx context.Context) (*protos.User, error) {
log.Debug("New user, calling user create")
settings := app.Settings()
settings.SetUserFirstVisit(true)
resp, err := app.proClient.UserCreate(ctx)
if err != nil {
return nil, errors.New("Could not create new Pro user: %v", err)
}
user := resp.User
log.Debugf("DEBUG: User created: %v", user)
if resp.BaseResponse != nil && resp.BaseResponse.Error != "" {
return nil, errors.New("Could not create new Pro user: %v", err)
}
app.SetUserData(ctx, user.UserId, user)
settings.SetReferralCode(user.Referral)
settings.SetUserIDAndToken(user.UserId, user.Token)
go app.UserData(ctx)
return resp.User, nil
}

// Plans returns the plans available to a user
func (app *App) Plans(ctx context.Context) ([]protos.Plan, error) {
if v, ok := app.plansCache.Load("plans"); ok {
resp := v.([]protos.Plan)
log.Debugf("Returning plans from cache %s", v)
return resp, nil
}
resp, err := app.FetchPaymentMethods(ctx)
if err != nil {
return nil, err
}
return resp.Plans, nil
}

// GetPaymentMethods returns the plans and payment from cache if available
// if not then call FetchPaymentMethods
func (app *App) GetPaymentMethods(ctx context.Context) ([]protos.PaymentMethod, error) {
if v, ok := app.plansCache.Load("paymentMethods"); ok {
resp := v.([]protos.PaymentMethod)
log.Debugf("Returning payment methods from cache %s", v)
return resp, nil
}
resp, err := app.FetchPaymentMethods(ctx)
if err != nil {
return nil, err
}
desktopProviders, ok := resp.Providers["desktop"]
if !ok {
return nil, errors.New("No desktop payment providers found")
}
return desktopProviders, nil
}

// FetchPaymentMethods returns the plans and payment plans available to a user
func (app *App) FetchPaymentMethods(ctx context.Context) (*proclient.PaymentMethodsResponse, error) {
resp, err := app.proClient.PaymentMethodsV4(context.Background())
if err != nil {
return nil, errors.New("Could not get payment methods: %v", err)
}
desktopPaymentMethods, ok := resp.Providers["desktop"]
if !ok {
return nil, errors.New("No desktop payment providers found")
}
for i := range desktopPaymentMethods {
paymentMethod := &desktopPaymentMethods[i]
for j, provider := range paymentMethod.Providers {
if resp.Logo[provider.Name] != nil {
logos := resp.Logo[provider.Name].([]interface{})
for _, logo := range logos {
paymentMethod.Providers[j].LogoUrls = append(paymentMethod.Providers[j].LogoUrls, logo.(string))
}
}
}
}
//clear previous store cache
app.plansCache.Delete("plans")
app.plansCache.Delete("paymentMethods")
log.Debugf("DEBUG: Payment methods plans: %+v", resp.Plans)
log.Debugf("DEBUG: Payment methods providers: %+v", desktopPaymentMethods)
app.plansCache.Store("plans", resp.Plans)
app.plansCache.Store("paymentMethods", desktopPaymentMethods)
app.sendConfigOptions()
return resp, nil
}

// UserData looks up user data that is associated with the given UserConfig
func (app *App) UserData(ctx context.Context) (*protos.User, error) {
log.Debug("Refreshing user data")
resp, err := app.proClient.UserData(context.Background())
if err != nil {
return nil, errors.New("error fetching user data: %v", err)
} else if resp.User == nil {
return nil, errors.New("error fetching user data")
}
userDetail := resp.User
settings := app.Settings()

setProUser := func(isPro bool) {
app.Settings().SetProUser(isPro)
}

currentDevice := app.Settings().GetDeviceID()

// Check if device id is connect to same device if not create new user
// this is for the case when user removed device from other device
deviceFound := false
if userDetail.Devices != nil {
for _, device := range userDetail.Devices {
if device.Id == currentDevice {
deviceFound = true
break
}
}
}

/// Check if user has installed app first time
firstTime := settings.GetUserFirstVisit()
log.Debugf("First time visit %v", firstTime)
if userDetail.UserLevel == "pro" && firstTime {
log.Debugf("User is pro and first time")
setProUser(true)
} else if userDetail.UserLevel == "pro" && !firstTime && deviceFound {
log.Debugf("User is pro and not first time")
setProUser(true)
} else {
log.Debugf("User is not pro")
setProUser(false)
}
settings.SetUserIDAndToken(userDetail.UserId, userDetail.Token)
settings.SetExpiration(userDetail.Expiration)
settings.SetReferralCode(resp.User.Referral)
log.Debugf("User caching successful: %+v", userDetail)
// Save data in userData cache
app.SetUserData(ctx, userDetail.UserId, userDetail)
app.SendUpdateUserDataToUI()
return resp.User, nil
}

func (app *App) devices() protos.Devices {
user, found := app.GetUserData(app.Settings().GetUserID())

Expand Down Expand Up @@ -810,3 +671,38 @@ func (app *App) ProClient() proclient.ProClient {
defer app.mu.RUnlock()
return app.proClient
}

// Client session methods
func (app *App) FetchUserData() error {
go app.proClient.UserData(context.Background())
go app.proClient.FetchPaymentMethodsAndCache(context.Background())
return nil
}

func (app *App) GetDeviceID() (string, error) {
return app.Settings().GetDeviceID(), nil
}

func (app *App) GetUserFirstVisit() (bool, error) {
return app.Settings().GetUserFirstVisit(), nil
}

func (app *App) SetUserIDAndToken(id int64, token string) error {
app.Settings().SetUserIDAndToken(id, token)
return nil
}

func (app *App) SetProUser(pro bool) error {
app.Settings().SetProUser(pro)
return nil
}

func (app *App) SetReferralCode(referral string) error {
app.Settings().SetReferralCode(referral)
return nil
}

func (app *App) SetExpiration(exp int64) error {
app.Settings().SetExpiration(exp)
return nil
}
4 changes: 2 additions & 2 deletions desktop/app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ func (app *App) sendConfigOptions() {
return authEnabled
}
ctx := context.Background()
plans, _ := app.Plans(ctx)
paymentMethods, _ := app.GetPaymentMethods(ctx)
plans, _ := app.proClient.Plans(ctx)
paymentMethods, _ := app.proClient.DesktopPaymentMethods(ctx)
devices, _ := json.Marshal(app.devices())
log.Debugf("DEBUG: Devices: %s", string(devices))
log.Debugf("Expiration date: %s", app.settings.GetExpirationDate())
Expand Down
51 changes: 6 additions & 45 deletions desktop/app/pro.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import (
"context"
"encoding/json"
"reflect"
"time"

"github.com/getlantern/errors"
"github.com/getlantern/flashlight/v7/common"
"github.com/getlantern/lantern-client/desktop/ws"
"github.com/getlantern/lantern-client/internalsdk/pro"
"github.com/getlantern/lantern-client/internalsdk/protos"
)

// onProStatusChange allows registering an event handler to learn when the
// user's pro status or "yinbi enabled" status has changed.
// user's pro status changes
func (app *App) onProStatusChange(cb func(isPro bool)) {
app.setOnUserData(func(current *protos.User, new *protos.User) {
if current == nil || isActive(current) != isActive(new) {
Expand Down Expand Up @@ -109,53 +107,16 @@ func (app *App) IsProUserFast(uc common.UserConfig) (isPro bool, statusKnown boo
// It loops forever in 10 seconds interval until the user is fetched or
// created, as it's fundamental for the UI to work.
func (app *App) servePro(channel ws.UIChannel) error {
chFetch := make(chan bool)
ctx := context.Background()
go func() {
fetchOrCreate := func() error {
userID := app.settings.GetUserID()
if userID == 0 {
resp, err := app.proClient.UserCreate(ctx)
if err != nil {
return errors.New("Could not create new Pro user: %v", err)
}
app.settings.SetUserIDAndToken(resp.User.UserId, resp.User.Token)
} else {
_, err := app.proClient.UserData(ctx)
if err != nil {
return errors.New("Could not get user data for %v: %v", userID, err)
}
}
return nil
}

retry := time.NewTimer(0)
retryOnFail := func(drainChannel bool) {
if err := fetchOrCreate(); err != nil {
if drainChannel && !retry.Stop() {
<-retry.C
}
retry.Reset(10 * time.Second)
}
}
for {
select {
case <-chFetch:
retryOnFail(true)
case <-retry.C:
retryOnFail(false)
}
}
}()

service, err := channel.Register("pro", nil)
if err != nil {
return err
}
app.setOnUserData(func(current *protos.User, new *protos.User) {
b, _ := json.Marshal(new)
log.Debugf("Sending updated user data to all clients: %s", string(b))
service.Out <- new
if new != nil {
b, _ := json.Marshal(new)
log.Debugf("Sending updated user data to all clients: %s", string(b))
service.Out <- new
}
})
return err
}
11 changes: 5 additions & 6 deletions desktop/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func signup(email *C.char, password *C.char) *C.char {
saveUserSalt(salt)
setting.SetEmailAddress(C.GoString(email))
a.SetUserLoggedIn(true)
a.FetchPaymentMethods(context.Background())
a.ProClient().FetchPaymentMethodsAndCache(context.Background())
return C.CString("true")
}

Expand Down Expand Up @@ -129,7 +129,7 @@ func logout() *C.char {

clearLocalUserData()
// Create new user
if _, err := a.CreateUser(ctx); err != nil {
if _, err := a.ProClient().UserCreate(ctx); err != nil {
return sendError(err)
}
return C.CString("true")
Expand Down Expand Up @@ -253,7 +253,7 @@ func deleteAccount(password *C.char) *C.char {
A: A.Bytes(),
}
log.Debugf("Delete Account request email %v A %v", lowerCaseEmail, A.Bytes())
srpB, err := a.AuthClient().LoginPrepare(context.Background(), prepareRequestBody)
srpB, err := a.AuthClient().LoginPrepare(ctx, prepareRequestBody)
if err != nil {
return sendError(err)
}
Expand Down Expand Up @@ -299,7 +299,7 @@ func deleteAccount(password *C.char) *C.char {
if err != nil {
return sendError(err)
}
log.Debugf("Account Delted response %v", isAccountDeleted)
log.Debugf("Account deleted response %v", isAccountDeleted)

if !isAccountDeleted {
return sendError(log.Errorf("user_not_found error while deleting account %v", err))
Expand All @@ -310,8 +310,7 @@ func deleteAccount(password *C.char) *C.char {
// Set user id and token to nil
a.Settings().SetUserIDAndToken(0, "")
// Create new user
// Create new user
if _, err := a.CreateUser(ctx); err != nil {
if _, err := a.ProClient().UserCreate(ctx); err != nil {
return sendError(err)
}
return C.CString("true")
Expand Down
Loading

0 comments on commit c63862b

Please sign in to comment.