Browse Source
feat: login with pair code (#171)
feat: login with pair code (#171)
* feat: wip pair code login feat(app): add LoginWithCode method to IAppService interface for handling phone number login feat(app): implement LoginWithCode method in App struct to handle phone number login logic feat(app): implement LoginWithCode method in serviceApp to pair phone number with WhatsApp client * feat: add api login with code * feat: add ui getting pair code * feat: add pair code * chore: update docs & text * feat: upgrade version * chore: update docs * feat: update image gallery * fix: show error message * chore: update repo * fix: some code improvementpull/181/head v4.16.0
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 327 additions and 38 deletions
-
4docker/golang.Dockerfile
-
43docs/openapi.yaml
-
52readme.md
-
2src/config/settings.go
-
1src/domains/app/app.go
-
8src/go.mod
-
8src/go.sum
-
15src/internal/rest/app.go
-
2src/pkg/error/app_error.go
-
23src/services/app.go
-
21src/validations/app_validation.go
-
61src/validations/app_validation_test.go
-
7src/views/components/AppLogin.js
-
109src/views/components/AppLoginWithCode.js
-
3src/views/components/AppReconnect.js
-
6src/views/index.html
@ -0,0 +1,21 @@ |
|||||
|
package validations |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"fmt" |
||||
|
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error" |
||||
|
validation "github.com/go-ozzo/ozzo-validation/v4" |
||||
|
"regexp" |
||||
|
) |
||||
|
|
||||
|
func ValidateLoginWithCode(ctx context.Context, phoneNumber string) error { |
||||
|
// Combine validations using a single ValidateWithContext call
|
||||
|
err := validation.ValidateWithContext(ctx, &phoneNumber, |
||||
|
validation.Required, |
||||
|
validation.Match(regexp.MustCompile(`^\+?[0-9]{1,15}$`)), |
||||
|
) |
||||
|
if err != nil { |
||||
|
return pkgError.ValidationError(fmt.Sprintf("phone_number(%s): %s", phoneNumber, err.Error())) |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
package validations |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
func TestValidateLoginWithCode(t *testing.T) { |
||||
|
type args struct { |
||||
|
phoneNumber string |
||||
|
} |
||||
|
tests := []struct { |
||||
|
name string |
||||
|
args args |
||||
|
wantErr bool |
||||
|
}{ |
||||
|
{ |
||||
|
name: "Phone with +", |
||||
|
args: args{phoneNumber: "+6281234567890"}, |
||||
|
wantErr: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Phone without +", |
||||
|
args: args{phoneNumber: "621234567890"}, |
||||
|
wantErr: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Phone with 0", |
||||
|
args: args{phoneNumber: "081234567890"}, |
||||
|
wantErr: false, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Phone contains alphabet", |
||||
|
args: args{phoneNumber: "+6281234567890a"}, |
||||
|
wantErr: true, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Empty phone number", |
||||
|
args: args{phoneNumber: ""}, |
||||
|
wantErr: true, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Phone with special characters", |
||||
|
args: args{phoneNumber: "+6281234567890!@#"}, |
||||
|
wantErr: true, |
||||
|
}, |
||||
|
{ |
||||
|
name: "Extremely long phone number", |
||||
|
args: args{phoneNumber: "+62812345678901234567890"}, |
||||
|
wantErr: true, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
for _, tt := range tests { |
||||
|
t.Run(tt.name, func(t *testing.T) { |
||||
|
if err := ValidateLoginWithCode(context.Background(), tt.args.phoneNumber); (err != nil) != tt.wantErr { |
||||
|
t.Errorf("ValidateLoginWithCode() error = %v, wantErr %v", err, tt.wantErr) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
export default { |
||||
|
name: 'AppLoginWithCode', |
||||
|
props: { |
||||
|
connected: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
connected: function(val) { |
||||
|
if (val) { |
||||
|
// reset form
|
||||
|
this.phone = ''; |
||||
|
this.pair_code = null; |
||||
|
|
||||
|
$('#modalLoginWithCode').modal('hide'); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
data: () => { |
||||
|
return { |
||||
|
phone: '', |
||||
|
submitting: false, |
||||
|
pair_code: null, |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
async openModal() { |
||||
|
try { |
||||
|
if (this.connected) throw Error('You are already logged in.'); |
||||
|
|
||||
|
$('#modalLoginWithCode').modal({ |
||||
|
onApprove: function() { |
||||
|
return false; |
||||
|
}, |
||||
|
}).modal('show'); |
||||
|
} catch (err) { |
||||
|
showErrorInfo(err); |
||||
|
} |
||||
|
}, |
||||
|
async handleSubmit() { |
||||
|
if (this.submitting) return; |
||||
|
try { |
||||
|
this.submitting = true; |
||||
|
const { data } = await http.get(`/app/login-with-code`, { |
||||
|
params: { |
||||
|
phone: this.phone, |
||||
|
}, |
||||
|
}); |
||||
|
this.pair_code = data.results.pair_code; |
||||
|
} catch (err) { |
||||
|
if (err.response) { |
||||
|
showErrorInfo(err.response.data.message); |
||||
|
}else{ |
||||
|
showErrorInfo(err.message); |
||||
|
} |
||||
|
} finally { |
||||
|
this.submitting = false; |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
template: `
|
||||
|
<div class="green card" @click="openModal" style="cursor: pointer"> |
||||
|
<div class="content"> |
||||
|
<div class="header">Login with Code</div> |
||||
|
<div class="description"> |
||||
|
Enter your pairing code to log in and access your devices. |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Modal Login --> |
||||
|
<div class="ui small modal" id="modalLoginWithCode"> |
||||
|
<i class="close icon"></i> |
||||
|
<div class="header"> |
||||
|
Getting Pair Code |
||||
|
</div> |
||||
|
<div class="content"> |
||||
|
<div class="ui message info"> |
||||
|
<div class="header">How to pair?</div> |
||||
|
<ol> |
||||
|
<li>Open your Whatsapp</li> |
||||
|
<li>Link a device</li> |
||||
|
<li>Link with pair code</li> |
||||
|
</ol> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ui form"> |
||||
|
<div class="field"> |
||||
|
<label>Phone</label> |
||||
|
<input type="text" v-model="phone" placeholder="Type your phone number" |
||||
|
@keyup.enter="handleSubmit" :disabled="submitting"> |
||||
|
<small>Enter to submit</small> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="ui grid" v-if="pair_code"> |
||||
|
<div class="ui two column centered grid"> |
||||
|
<div class="column center aligned"> |
||||
|
<div class="header">Pair Code</div> |
||||
|
<p style="font-size: 32px">{{ pair_code }}</p> |
||||
|
|
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
`,
|
||||
|
}; |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue