I'm installing #nuxtjs/recaptcha on my nuxt project, which is google reCaptcha V3. It looks like it's working great it's always returning success result from the servers like below.
{success: true, challenge_ts: '2022-05-05T11:37:06Z', hostname: 'localhost', score: 0.9}
But I'm not sure this work properly because the challenge does not appear. Recaptcha as I know it, should have a challenge that must be met. But why doesn't the challenge appear here?
Maybe it needs to be triggered with some event handler? But in the documentation example I didn't saw any things related about it or maybe I just do not realize it.
May be im missing something important, so I need your help to figure it out.
My template code
<template>
<div class="container">
<h1 align="center">SEND EMAIL TO US!</h1>
<div class="layout">
<form #submit.prevent="onSubmit">
<div class="basic-info">
<div class="name">
<label for="">Name :</label>
<b-form-input v-model="data.name" placeholder="Name"></b-form-input>
<div v-if="validation.name" class="mt-2">
<b-alert show variant="danger">{{ validation.name[0] }}</b-alert>
</div>
</div>
<div class="email">
<label for="">Email :</label>
<b-form-input
v-model="data.email"
placeholder="Email"
></b-form-input>
<div v-if="validation.email" class="mt-2">
<b-alert show variant="danger">{{ validation.email[0] }}</b-alert>
</div>
</div>
<div class="messege">
<label for="">Messege :</label>
<b-form-textarea
id="textarea"
v-model="data.messege"
placeholder="Enter Messege..."
rows="8"
max-rows="8"
></b-form-textarea>
<div v-if="validation.messege" class="mt-2">
<b-alert show variant="danger">
{{ validation.messege[0] }}
</b-alert>
</div>
</div>
<hr />
<b-button type="submit" variant="outline-primary">
SEND EMAIL
</b-button>
<hr />
<b-alert v-model="alert" show :variant="variant">
{{ result_messege }}
</b-alert>
</div>
</form>
</div>
</div>
</template>
my script code
<script>
export default {
async mounted() {
try {
await this.$recaptcha.init()
} catch (e) {
console.log(e)
}
},
methods: {
async onSubmit() {
try {
this.loading = true
// Start the verification process
const response = await this.verifyCaptcha()
console.log(response)
// Display error message if verification was not successful
if (!response.success) {
this.$recaptcha.reset()
this.loading = false
this.errorStatus = true
this.notificationMessage =
'There was an error with your reCaptcha verification. Please try again.'
return
}
// If verification was successful, send the message
await this.sendMail()
this.errorStatus = false
this.notificationMessage =
'Thank you for reaching out. We will get back to you as soon as possible'
this.loading = false
this.$recaptcha.reset()
} catch (error) {
this.loading = false
console.log(error)
}
},
async verifyCaptcha() {
try {
const token = await this.$recaptcha.execute()
console.log(token)
const response = await this.$axios.$post(
`/captcha-api/siteverify?secret=${process.env.SECRET_KEY}&response=${token}`
)
return response
} catch (error) {
this.loading = false
return error
}
},
},
}
</script>
This is totally normal, this is the whole concept of the v3 as you can see in this video: https://youtu.be/tbvxFW4UJdU
More details are also here: https://developers.google.com/recaptcha/docs/v3
And here: https://www.google.com/recaptcha/about/
So far, the feature is exactly this: do not require any interaction from the user but rather use some mouse trackers/AI to know if it's potentially malicious or not.
Related
I am using Flask as a backend and Vue JS as front end for my development. Vuex for state store.
In the logout() I am clearing the authentication token from localStorage via store.dispatch('/logout') and then using router.push('/login') to navigate to Login.vue. I find that the form details entered before are not getting cleared. When the logout() is performed it navigates to the Login.vue with flash message stating 'You have been logged out successfully'.
Below is the code snippet for the same:
logout() {
axios.get(`${this.host}:5000/logout`)
.then((res) => {
this.$store.dispatch('logout')
.then(() => {
this.$router.push('/login');
});
this.flashMessage.success({
message: res.data.msg,
time: 5000,
flashMessageStyle: {
backgroundColor: 'linear-gradient(#e66465, #9198e5)',
},
});
})
.catch((error) => {
this.flashMessage.error({
message: error.toString(),
time: 5000,
flashMessageStyle: {
backgroundColor: 'linear-gradient(#e66465, #9198e5)',
},
});
});
}
logout() is written in App.vue. Here router is an instance of vue-router.
To avoid the issue of form data not being cleared I have used router.go() instead of router.push() in the above code. But because of this implementation, the flash message is not getting displayed as the reload (because of router.go()) is overwriting the displaying of flash message which I don't want.
Please let me if it is possible to erase the form data after logout without getting into the trouble of not showing flash message.
Not sure how you have implemented Login.vue, but here is a Login.vue that I have implemented, and it works. One of the takeaways should be that my form model data values are initialized to empty strings.
<template>
<div id="login">
<form class="form-horizontal">
<div class="form-group">
<label for="username" class="col-md-offset-3 col-md-2 align-right">User Name</label>
<div class="col-md-3">
<input type="input" ref="username" class="form-control" id="username" v-model="username">
</div>
</div>
<div class="form-group">
<label for="password" class="col-md-offset-3 col-md-2 align-right">Password</label>
<div class="col-md-3">
<input type="password" class="form-control" id="password" v-model="password" v-on:keyup.enter="login">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-5 col-md-4">
<button type="button" class="btn btn-default"
v-on:click="login">Login</button>
<span class="error-msg" v-if="errorMsg">{{ errorMsg }}</span>
</div>
</div>
</form>
</div>
</template>
<script>
import { loginUrl, axios, processAjaxLoginError } from '../globalvars.js'
export default {
name: 'Login',
data() {
return {
username: '',
password: '',
errorMsg: ''
}
},
methods: {
login() {
axios.post(loginUrl, {
username: this.username,
password: this.password
})
.then(response => {
// Commit the token to the store
this.$store.commit('updateToken', { token: response.data.message });
// Clear error message
this.errorMsg = '';
// Redirect to customer index view
this.$router.push("/customers")
})
.catch(error => {
this.errorMsg = processAjaxLoginError(error);
})
}
},
computed: {
token() {
return this.$store.state.token;
}
},
mounted() {
this.$refs.username.focus();
}
}
</script>
the problem is that on click I increments the new userChoice according to the user chosen but it pushes on the profile route before retrieving the new userChoice click.
what to do here is my template I will put everything in the same function change use the push at the end but it does not work either then I do 2 functions but it does not work either what is the solution ??
<template>
<section
class="stopPadMarg container-fluid d-md-flex justify-content-between"
>
<div class="py-5 stopPadMarg bg-primary col-md-1">
<img
src="../assets/image/icon.png"
width="60px"
class="rounded-circle"
alt="logo"
/>
</div>
<div class="largeur80">
<form class="justify-content-center form-inline py-3 my-2 my-lg-0">
<input
v-model="searchKey"
id="search"
class="form-control mr-sm-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
</form>
<div>
<h3
class="backPrimaire opacity mx-1 text-primary bordurePost bordureRond"
>
<b-icon-chevron-double-down
class="mr-5 my-1 pt-1 text-secondary"
animation="cylon-vertical"
font-scale="1"
></b-icon-chevron-double-down>
Vos collegues
<b-icon-chevron-double-down
class="ml-5 my-1 pt-1 text-secondary"
animation="cylon-vertical"
font-scale="1"
></b-icon-chevron-double-down>
</h3>
</div>
<div class="hauteur">
<div class="mt-5 d-flex flex-wrap">
<div
v-for="(user, id) in filteredList"
v-bind:key="id"
class="col-md-3 d-flex flex-column align-items-center align-content-center"
>
<div #click="changeUser(user)" class="cursor">
<img
#click="changeRoute"
v-if="user.image_url !== null || ''"
:src="user.image_url"
width="100px"
height="100px"
class=" justify-content-left bordureProfil
rounded-circle"
/>
<img
v-else
src="../assets/image/icon.png"
width="100px"
class=" justify-content-left bordureProfil rounded-circle"
/>
</div>
<div>
<h5 class="mt-2">
{{ user.nom.toUpperCase() }}
</h5>
<h6 class="mb-3">{{ user.prenom.toLowerCase() }}</h6>
</div>
</div>
</div>
</div>
</div>
<div class="py-5 stopPadMarg bg-primary col-md-1">
<img
src="../assets/image/icon.png"
width="60px"
class="rounded-circle"
alt="logo"
/>
</div>
</section>
</template>
<script>
import axios from "axios";
export default {
components: {},
data() {
return {
searchKey: "",
postes: [],
users: [],
user_id: localStorage.getItem("userId"),
userChoice: localStorage.getItem("userChoice"),
};
},
async created() {
this.postes = [];
this.users = [];
await axios
.get("http://localhost:3000/postes")
.then(
(response) => ((this.postes = response.data), console.log(response))
)
.catch((error) => console.log(error));
await axios
.get("http://localhost:3000/users")
.then(
(response) => ((this.users = response.data), console.log(this.users))
)
.catch((error) => console.log(error));
await axios
.get("http://localhost:3000/users")
.then(
(response) => (
(this.userDef = response.data.find((user) => {
return user.id;
})),
console.log(this.userDef)
)
)
.catch((error) => console.log(error));
await axios
.get(`http://localhost:3000/user/${this.user_id}`)
.then(
(response) => (
(this.userConnect = response.data), console.log(this.userConnect.id)
)
)
.catch((error) => console.log(error));
await axios
.get("http://localhost:3000/commentaires")
.then(
(response) => (
(this.comments = response.data), console.log(this.comments)
)
)
.catch((error) => console.log(error));
},
computed: {
filteredList() {
return this.users.filter((user) => {
return user.nom.toLowerCase().includes(this.searchKey.toLowerCase());
});
},
},
methods: {
async changeUser(user) {
await localStorage.removeItem("userChoice");
await localStorage.setItem("userChoice", user.id);
this.$router.push(`/profil/${this.userChoice}`);
},
async changeRoute() {
await this.$router.push(`/profil/${this.userChoice}`);
},
},
};
</script>
<style></style>
and the picture here
if I press a second time on the same profile it gives it to me if I return to the colleagues page but not if I change profile there is an empty page
here picture of the routes path
in fact the route does not change profile and remains on 58 here c the profile of that which is connected and if we change number on the route it launches a page page so this is the problem with the path of the route that the we see in the browser cache
Having looked at your code it's obvious why you'd get an empty page when changing routes. Let me explain:
Your routes say this:
Register a route /profil/${userChoice} (which is a value read from localStorage).
This route definition is only read once, at page intialisation. So, when your page loads only /profil/58 will be defined, /profil/59 wont.
What you are probably looking for is route parameters:
https://router.vuejs.org/guide/essentials/dynamic-matching.html
You'd want the number part of this url to be dynamic and respond to changes.
So, instead of reading the value from localStorage, you would write:
{
path: '/profil/:user_id',
name: 'ProfilUser',
...
}
Now when your Profil components is initialized instead of accessing localStorage you read the provided value as follows:
created() {
var userChoice = this.$route.params.user_id;
}
(note it is also possible to get this param as a prop, consult the vue-router docs on how to do this)
Another thing you need to keep in mind is that you need to respond when this parameter changes. Your component will not be refreshed/remounted.
To respond to parameter changes you can do the following:
watch: {
'$route.params.user_id'() {
this.reloadAllStuff();
}
}
I would recommend to not use localStorage for this use case, let the URL parameter be the main source of truth.
Further reading:
https://qvault.io/2020/07/07/how-to-rerender-a-vue-route-when-path-parameters-change/
I have a simple Form with configured Vuelidate validators. It shows an error messages if the input is in dirty state, so when the form is opened at the first time there are no errors.
But when I click submit I expect to get a "clear" form, as well as I just open it, but instead, I get all my validation errors within my input.
Is there any elegant way to reset all vuelidate fields to clear state again, and get clear form?
<script>
import {required, email} from 'vuelidate/lib/validators'
export default {
data() {
return {
email: ''
//there could be a lot of props
}
},
methods: {
onSubmit() {
if (!this.$v.$invalid) {
const user = {
email: this.email,
//there could be a lot of props
};
console.log(user);
this.email = '';
}
}
},
validations: {
email: {required, email}
}
}
</script>
<template>
<div>
<form class="form" #submit.prevent="onSubmit">
<div class="input">
<label for="email">Email</label>
<input
:class="{ error: $v.email.$error }"
type="email"
id="email"
v-model.trim="email"
#input="$v.email.$touch()">
<div v-if="$v.email.$dirty">
<p class="error-message" v-if="!$v.email.email">Please enter a valid email</p>
<p class="error-message" v-if="!$v.email.required">Email must not be empty</p>
</div>
</div>
<button :disabled="$v.$invalid" type="submit">Submit</button>
</form>
</div>
</template>
after some research I found that this.$v.$reset(); makes things
I have a login screen that briefly flashes after the user logs in. This doesn't happen all the time, but it's annoying. I am having trouble figuring out what is causing this and so I thought I'd ask the great people of stackoverflow to see if they have any tips on how to fix or troubleshoot. Thanks!
Here's my issue (as shown in screenshots with some code below):
User logs in:
User sees spinner as his credentials are authenticated:
User sees login screen flash briefly again before router redirects him to the requested content.
Here is code for the Login.vue page:
<template>
<div class="col-lg-6">
<template v-if="isLoading">
<spinner key="spinner"></spinner>
</template>
<template v-else>
<div key="form">
<h1>Login</h1>
<aside class="alert alert-danger" v-if="error">
{{ error }}
</aside>
<form #submit.prevent="handlerLogin">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" v-model="formData.email" />
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" v-model="formData.password" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</template>
</div>
</template>
<script>
export default {
name: 'Login',
data() {
return {
isLoading: false,
formData: {
email: null,
password: null
}
}
},
computed: {
error() {
return this.$store.state.error
}
},
methods: {
async handlerLogin() {
this.isLoading = true
try {
const payload = {
email: this.formData.email,
password: this.formData.password
}
await this.$store.dispatch('logInUser', payload)
console.log('ready to fetch user profile')
await this.$store.dispatch('fetchUserProfile')
this.$router.replace('photos')
} catch (error) {
console.log(error)
} finally {
this.isLoading = false
}
}
}
}
</script>
And here is the logInUser action code from Vuex store:
async logInUser({ commit }, payload) {
commit('CLEAR_ERROR')
try {
const user = await auth.signInWithEmailAndPassword(payload.email, payload.password)
commit('SET_CURRENT_USER', user.user)
} catch (error) {
commit('SET_ERROR', error)
}
}
I think the mistake you are making is that you expect this.$router.replace to be blocking, which it is not. (docs) As such, the this.isLoading = false from finally is called immediately after. Sometimes Vue manages to re-render your component before the router finishes transitioning to the new view, and sometimes it does not.
You can create a third state that simply checks if the user is logged in and displays an appropriate message. Keep in mind that if the user gets on the log-in page when logged in, this needs to do something sensible as well. An other option is to move this.isLoading = false to the catch, but keep in mind that if navigation fails, for example due to a (global) route guard, your component is loading eternally without clear feedback what happened. A third option would be to make this.$router.replace blocking by wrapping it in a Promise or something:
await new Promise((resolve, reject) => {
this.$router.replace('photos', resolve, reject)
})
I'm trying to validate a simple form that contains two fields:
A select box
A file field
If one of the fields aren't filled in, a div (containing an error label) should be rendered next to the corresponding input field.
The problem: My 'error divs' aren't rendered when pushing data to the errors object (if the form is invalid).
Please note my console.log statement, that tells me that my error object has a key 'file' and a key 'selectedSupplier'.
Side note: I'm following this example: https://v2.vuejs.org/v2/cookbook/form-validation.html
Differences are, that I'd like to show error labels next to the corresponding field and that I'm setting errors in my errors object, instead of a simple array. So what could I be doing wrong?
Thanks.
This is my Main.vue file:
<template>
<div>
<form #submit="upload">
<div class="mb-8">
<h1 class="mb-3 text-90 font-normal text-2xl">Import Order Csv</h1>
<div class="card">
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="supplier_id" class="inline-block text-80 pt-2 leading-tight">Supplier</label>
</div>
<div class="py-6 px-8 w-1/2">
<select v-model="selectedSupplier" id="supplier_id" name="supplier_id" ref="supplier_id" class="w-full form-control form-input form-input-bordered">
<option v-for="supplier in suppliers" v-bind:value="supplier.id">{{ supplier.name }}</option>
</select>
<div v-if="errors.hasOwnProperty('selectedSupplier')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="csv_file" class="inline-block text-80 pt-2 leading-tight">File</label>
</div>
<div class="py-6 px-8 w-1/2">
<input id="csv_file" type="file" name="file" ref="file" #change="handleFile">
<div v-if="errors.hasOwnProperty('file')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center">
<button type="submit" class="btn btn-default btn-primary inline-flex items-center relative">Import</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
this.listSuppliers();
},
data() {
return {
errors: [],
file: '',
suppliers: [],
};
},
methods: {
checkForm() {
if (!this.selectedSupplier) {
this.errors.selectedSupplier = 'Supplier required';
}
if (!this.file) {
this.errors.file = 'File required';
}
},
listSuppliers() {
const self = this;
Nova.request()
.get('/tool/import-order-csv/suppliers')
.then(function (response) {
self.suppliers = response.data.data;
})
.catch(function (e) {
self.$toasted.show(e, {type: "error"});
});
},
handleFile: function (event) {
this.file = this.$refs.file.files[0];
},
upload: function (event) {
this.checkForm();
if (this.errors.hasOwnProperty('selectedSupplier') || this.errors.hasOwnProperty('file')) {
console.log(this.errors); // this actually shows both errors!
event.preventDefault();
}
const formData = new FormData();
formData.append('file', this.file);
formData.append('supplier_id', this.$refs.supplier_id.value);
const self = this;
Nova.request()
.post('/tool/import-order-csv/upload',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function (response) {
self.$toasted.show(response.data.message, {type: "success"});
})
.catch(function (e) {
self.$toasted.show(e.response.data.message, {type: "error"});
});
}
}
}
</script>
Apparently I had to use v-show instead of v-if, because v-if would be 'lazy' and will not render my error-div when the errors var gets filled.
It's working now, but not 100% sure if this is the best way, as I found another tutorial where v-if is used for form validation.(https://medium.com/#mscherrenberg/laravel-5-6-vue-js-simple-form-submission-using-components-92b6d5fd4434)
I was getting the same error, this is how I solved the problem,
<div v-if="errors.field1.length > 0 ? true : false"> // true or false
If you fix the code like this it will work
The reason might because the way you reassign object is not reactive, which not trigger v-if to re-calculate
this.errors.selectedSupplier = 'Supplier required';
this.errors.file = 'File required';
If you still want to use v-if , try change to this approach
this.errors = {...this.errors, selectedSupplier: 'Supplier required' }
this.errors = {...this.errors, file: 'File required' }
The way I handle my errors with VueJS is through lists and their length attribute.
I have an errors object in my data that looks like this:
errors: {
field1: [],
field2: [],
}
Then, when I submit the form, I will:
Empty all the lists for the errors (ie clearing the previous errors)
.push() new errors in the right lists (and .push() makes the Vue reactive)
Finally, in my form, my respective errors divs are displayed based on the length of the list:
<div class="error" v-if="errors.field1.length > 0">
use a v-for to display all the errors from the list
</div>
Hope it helps