account
This commit is contained in:
@@ -53,7 +53,8 @@ export default defineNuxtConfig({
|
||||
devProxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8000/api",
|
||||
prependPath: true
|
||||
prependPath: true,
|
||||
changeOrigin: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
66
pages/account/onboarding.vue
Normal file
66
pages/account/onboarding.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
layout: 'vuetify-app',
|
||||
middleware: ['auth']
|
||||
})
|
||||
const email = ref('')
|
||||
const sending = ref(false)
|
||||
const resendEmail = async () => {
|
||||
sending.value = true
|
||||
const { data, error } = await useFetch('/api/account/resend-verification-email/', {
|
||||
method: 'POST'
|
||||
})
|
||||
if (error.value) {
|
||||
console.log(error.value)
|
||||
}
|
||||
sending.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
class="h-100vh"
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col
|
||||
sm="9"
|
||||
offset-sm="1"
|
||||
md="8"
|
||||
offset-md="2"
|
||||
>
|
||||
<v-card
|
||||
class="mt-20vh"
|
||||
elevation="0"
|
||||
>
|
||||
<div class="text-center">
|
||||
<h2 class="text-h4">Verify your email</h2>
|
||||
<p class="text-body-2 mt-5">
|
||||
We've sent a verification email to <strong>{{ email }}</strong>. <br>
|
||||
Please check your inbox and click the link to verify your email address.
|
||||
</p>
|
||||
<v-btn
|
||||
variant="text"
|
||||
class="mt-5"
|
||||
color="primary"
|
||||
:loading="sending"
|
||||
@click="resendEmail"
|
||||
>
|
||||
Resend email
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.h-100vh {
|
||||
height: 100vh;
|
||||
}
|
||||
.mt-20vh {
|
||||
margin-top: 20vh;
|
||||
}
|
||||
</style>
|
||||
98
pages/account/signin.vue
Normal file
98
pages/account/signin.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<v-card
|
||||
style="height: 100vh"
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col
|
||||
sm="9"
|
||||
offset-sm="1"
|
||||
md="6"
|
||||
offset-md="3"
|
||||
>
|
||||
<v-card
|
||||
class="mt-15"
|
||||
elevation="0"
|
||||
>
|
||||
<div class="text-center text-h4">Sign in</div>
|
||||
<v-card-text>
|
||||
<v-form ref="signInForm">
|
||||
<v-text-field
|
||||
v-model="formData.username"
|
||||
:rules="formRules.username"
|
||||
label="User name"
|
||||
variant="underlined"
|
||||
></v-text-field>
|
||||
<v-text-field
|
||||
v-model="formData.password"
|
||||
:rules="formRules.password"
|
||||
label="Password"
|
||||
variant="underlined"
|
||||
></v-text-field>
|
||||
|
||||
</v-form>
|
||||
|
||||
<div v-if="errorMsg" class="text-red">{{ errorMsg }}</div>
|
||||
|
||||
<v-btn
|
||||
block
|
||||
color="primary"
|
||||
:loading="submitting"
|
||||
@click="submit"
|
||||
class="mt-5"
|
||||
>Submit</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
layout: 'vuetify-app'
|
||||
})
|
||||
const formData = ref({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
const formRules = ref({
|
||||
username: [
|
||||
v => !!v || 'Username is required'
|
||||
],
|
||||
password: [
|
||||
v => !!v || 'Password is required'
|
||||
]
|
||||
})
|
||||
const { $auth } = useNuxtApp()
|
||||
const errorMsg = ref(null)
|
||||
const signInForm = ref(null)
|
||||
const valid = ref(true)
|
||||
const submitting = ref(false)
|
||||
const submit = async () => {
|
||||
errorMsg.value = null
|
||||
const { valid } = await signInForm.value.validate()
|
||||
if (valid) {
|
||||
submitting.value = true
|
||||
const { data, error } = await useFetch('/api/account/login/', {
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
body: JSON.stringify(formData.value)
|
||||
})
|
||||
if (error.value) {
|
||||
if (error.value.status === 400) {
|
||||
if (error.value.data.non_field_errors) {
|
||||
errorMsg.value = error.value.data.non_field_errors[0]
|
||||
}
|
||||
} else {
|
||||
errorMsg.value = 'Something went wrong. Please try again.'
|
||||
}
|
||||
} else {
|
||||
}
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
158
pages/account/signup.vue
Normal file
158
pages/account/signup.vue
Normal file
@@ -0,0 +1,158 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
layout: 'vuetify-app'
|
||||
})
|
||||
|
||||
const formData = ref({
|
||||
username: '',
|
||||
email: '',
|
||||
password1: '',
|
||||
password2: '',
|
||||
})
|
||||
|
||||
const fieldErrors = ref({
|
||||
username: '',
|
||||
email: '',
|
||||
password1: '',
|
||||
password2: '',
|
||||
})
|
||||
|
||||
const formRules = ref({
|
||||
username: [
|
||||
v => !!v || 'Please enter your username',
|
||||
v => v.length >= 4 || 'Username must be at least 4 characters'
|
||||
],
|
||||
email: [
|
||||
v => !!v || 'Please enter your e-mail address',
|
||||
v => /.+@.+\..+/.test(v) || 'E-mail address must be valid'
|
||||
],
|
||||
password1: [
|
||||
v => !!v || 'Please enter your password',
|
||||
v => v.length >= 8 || 'Password must be at least 8 characters'
|
||||
],
|
||||
password2: [
|
||||
v => !!v || 'Please confirm your password',
|
||||
v => v.length >= 8 || 'Password must be at least 8 characters',
|
||||
v => v === formData.value.password1 || 'Confirm password must match password'
|
||||
]
|
||||
})
|
||||
|
||||
const submitting = ref(false)
|
||||
const errorMsg = ref(null)
|
||||
const signUpForm = ref(null)
|
||||
|
||||
const submit = async () => {
|
||||
errorMsg.value = null
|
||||
const { valid } = await signUpForm.value.validate()
|
||||
if (valid) {
|
||||
submitting.value = true
|
||||
|
||||
const { data, error } = await useFetch('/api/account/registration/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(formData.value)
|
||||
})
|
||||
|
||||
if (error.value) {
|
||||
if (error.value.status === 400) {
|
||||
for (const key in formData.value) {
|
||||
if (error.value.data[key]) {
|
||||
fieldErrors.value[key] = error.value.data[key][0]
|
||||
}
|
||||
}
|
||||
if (error.value.data.non_field_errors) {
|
||||
errorMsg.value = error.value.data.non_field_errors[0]
|
||||
}
|
||||
} else {
|
||||
errorMsg.value = 'Something went wrong. Please try again.'
|
||||
}
|
||||
} else {
|
||||
navigateTo('/account/onboarding')
|
||||
}
|
||||
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleFieldUpdate = (field) => {
|
||||
// fieldErrors.value[field] = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
style="height: 100vh"
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col
|
||||
sm="9"
|
||||
offset-sm="1"
|
||||
md="6"
|
||||
offset-md="3"
|
||||
>
|
||||
<v-card
|
||||
class="mt-15"
|
||||
elevation="0"
|
||||
>
|
||||
<div class="text-center text-h4">Create your account</div>
|
||||
<v-card-text>
|
||||
<v-form ref="signUpForm" class="mt-5">
|
||||
<v-text-field
|
||||
v-model="formData.username"
|
||||
:rules="formRules.username"
|
||||
:error-messages="fieldErrors.username"
|
||||
label="User name"
|
||||
variant="underlined"
|
||||
@update:modelValue="handleFieldUpdate('username')"
|
||||
clearable
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="formData.email"
|
||||
:rules="formRules.email"
|
||||
:error-messages="fieldErrors.email"
|
||||
label="Email"
|
||||
variant="underlined"
|
||||
@@update:modelValue="handleFieldUpdate('email')"
|
||||
clearable
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="formData.password1"
|
||||
:rules="formRules.password1"
|
||||
:error-messages="fieldErrors.password1"
|
||||
label="Password"
|
||||
variant="underlined"
|
||||
@update:modelValue="handleFieldUpdate('password1')"
|
||||
clearable
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field
|
||||
v-model="formData.password2"
|
||||
:rules="formRules.password2"
|
||||
:error-messages="fieldErrors.password2"
|
||||
label="Confirm password"
|
||||
variant="underlined"
|
||||
@update:modelValue="handleFieldUpdate('password2')"
|
||||
clearable
|
||||
></v-text-field>
|
||||
|
||||
</v-form>
|
||||
|
||||
<div v-if="errorMsg" class="text-red">{{ errorMsg }}</div>
|
||||
|
||||
<v-btn
|
||||
block
|
||||
size="large"
|
||||
color="primary"
|
||||
:loading="submitting"
|
||||
@click="submit"
|
||||
class="mt-5"
|
||||
>Submit</v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
const AUTH_ROUTE = {
|
||||
home: '/',
|
||||
login: '/login'
|
||||
login: '/account/signin',
|
||||
}
|
||||
|
||||
const COOKIE_OPTIONS = {
|
||||
@@ -13,13 +13,13 @@ const COOKIE_OPTIONS = {
|
||||
|
||||
const ENDPOINTS = {
|
||||
login: {
|
||||
url: '/api/auth/signin'
|
||||
url: '/api/account/login/'
|
||||
},
|
||||
refresh: {
|
||||
url: '/api/auth/token/refresh'
|
||||
},
|
||||
user: {
|
||||
url: '/api/auth/session'
|
||||
url: '/api/account/user/'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,9 @@ export default defineNuxtPlugin(() => {
|
||||
}
|
||||
|
||||
async fetchUser () {
|
||||
const { data, error } = await useAuthFetch(ENDPOINTS.user.url)
|
||||
const { data, error } = await useFetch(ENDPOINTS.user.url, {
|
||||
// withCredentials: true
|
||||
})
|
||||
if (!error.value) {
|
||||
this.user = data.value
|
||||
this.loginIn.value = true
|
||||
@@ -117,10 +119,10 @@ export default defineNuxtPlugin(() => {
|
||||
|
||||
addRouteMiddleware('auth', async (to, from) => {
|
||||
if (!auth.loginIn.value) {
|
||||
const token = await auth.retrieveToken()
|
||||
if (!token) {
|
||||
return await auth.redirectToLogin()
|
||||
}
|
||||
// const token = await auth.retrieveToken()
|
||||
// if (!token) {
|
||||
// return await auth.redirectToLogin()
|
||||
// }
|
||||
const error = await auth.fetchUser()
|
||||
if (error) {
|
||||
return await auth.redirectToLogin()
|
||||
|
||||
Reference in New Issue
Block a user