You've already forked WhereToGoRedux
mirror of
https://github.com/Llloooggg/WhereToGoRedux.git
synced 2026-03-06 04:56:23 +03:00
[backend + frontend]: Добавлена базовая регистрация
This commit is contained in:
@@ -1,10 +1,35 @@
|
|||||||
asgiref==3.5.2
|
asgiref==3.5.2
|
||||||
backports.zoneinfo==0.2.1
|
backports.zoneinfo==0.2.1
|
||||||
|
certifi==2022.6.15.1
|
||||||
|
cffi==1.15.1
|
||||||
|
charset-normalizer==2.1.1
|
||||||
|
coreapi==2.3.3
|
||||||
|
coreschema==0.0.4
|
||||||
|
cryptography==38.0.1
|
||||||
|
defusedxml==0.7.1
|
||||||
Django==4.1.1
|
Django==4.1.1
|
||||||
django-cors-headers==3.13.0
|
django-cors-headers==3.13.0
|
||||||
django-filter==22.1
|
django-filter==22.1
|
||||||
|
django-templated-mail==1.1.1
|
||||||
djangorestframework==3.13.1
|
djangorestframework==3.13.1
|
||||||
djangorestframework-gis==1.0
|
djangorestframework-gis==1.0
|
||||||
|
djangorestframework-simplejwt==4.8.0
|
||||||
|
djoser==2.1.0
|
||||||
|
idna==3.3
|
||||||
|
itypes==1.2.0
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.1
|
||||||
|
oauthlib==3.2.1
|
||||||
psycopg2==2.9.3
|
psycopg2==2.9.3
|
||||||
|
pycparser==2.21
|
||||||
|
PyJWT==2.4.0
|
||||||
|
python3-openid==3.2.0
|
||||||
pytz==2022.2.1
|
pytz==2022.2.1
|
||||||
|
requests==2.28.1
|
||||||
|
requests-oauthlib==1.3.1
|
||||||
|
six==1.16.0
|
||||||
|
social-auth-app-django==4.0.0
|
||||||
|
social-auth-core==4.3.0
|
||||||
sqlparse==0.4.2
|
sqlparse==0.4.2
|
||||||
|
uritemplate==4.1.1
|
||||||
|
urllib3==1.26.12
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ from django.utils.translation import gettext_lazy
|
|||||||
|
|
||||||
class AuthConfig(AppConfig):
|
class AuthConfig(AppConfig):
|
||||||
default_auto_field = "django.db.models.BigAutoField"
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
name = "authentication"
|
name = "accounts"
|
||||||
verbose_name = gettext_lazy("authentication")
|
verbose_name = gettext_lazy("accounts")
|
||||||
14
backend/wheretogo/accounts/urls.py
Normal file
14
backend/wheretogo/accounts/urls.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from django.urls import include
|
||||||
|
|
||||||
|
from accounts.views import CustomTokenObtainPairView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("", include("djoser.urls")),
|
||||||
|
path(
|
||||||
|
"auth/jwt/create",
|
||||||
|
CustomTokenObtainPairView.as_view(),
|
||||||
|
name="custom_token_obtain_pair",
|
||||||
|
),
|
||||||
|
path("auth/", include("djoser.urls.jwt")),
|
||||||
|
]
|
||||||
13
backend/wheretogo/accounts/views.py
Normal file
13
backend/wheretogo/accounts/views.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||||||
|
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||||
|
|
||||||
|
|
||||||
|
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
|
||||||
|
def validate(self, attrs):
|
||||||
|
data = super().validate(attrs)
|
||||||
|
data["username"] = self.user.username
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class CustomTokenObtainPairView(TokenObtainPairView):
|
||||||
|
serializer_class = CustomTokenObtainPairSerializer
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
from django.urls import path
|
|
||||||
|
|
||||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
|
||||||
from rest_framework_simplejwt.views import TokenRefreshView
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path("token", TokenObtainPairView.as_view(), name="token_obtain_pair"),
|
|
||||||
path("token/refresh", TokenRefreshView.as_view(), name="token_refresh"),
|
|
||||||
]
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-09-10 14:48+0300\n"
|
"POT-Creation-Date: 2022-09-12 23:55+0300\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -20,6 +20,10 @@ msgstr ""
|
|||||||
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
|
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
|
||||||
"%100>=11 && n%100<=14)? 2 : 3);\n"
|
"%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||||
|
|
||||||
|
#: accounts/apps.py:8
|
||||||
|
msgid "accounts"
|
||||||
|
msgstr "аккаунты"
|
||||||
|
|
||||||
#: facilities/apps.py:8 facilities/models.py:18
|
#: facilities/apps.py:8 facilities/models.py:18
|
||||||
msgid "facilities"
|
msgid "facilities"
|
||||||
msgstr "заведения"
|
msgstr "заведения"
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ INSTALLED_APPS = [
|
|||||||
"django.contrib.messages",
|
"django.contrib.messages",
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
"django.contrib.gis",
|
"django.contrib.gis",
|
||||||
|
"djoser",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"rest_framework_simplejwt",
|
|
||||||
"rest_framework_gis",
|
"rest_framework_gis",
|
||||||
"corsheaders",
|
"corsheaders",
|
||||||
"authentication",
|
"accounts",
|
||||||
"facilities",
|
"facilities",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -128,9 +128,12 @@ STATIC_URL = "static/"
|
|||||||
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
|
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||||
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
||||||
)
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
SIMPLE_JWT = {
|
||||||
|
"AUTH_HEADER_TYPES": ("JWT",),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,3 +11,7 @@ DATABASES = {
|
|||||||
"PORT": "5432",
|
"PORT": "5432",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CORS_ALLOWED_ORIGINS = [
|
||||||
|
"http://localhost:8080",
|
||||||
|
]
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ from django.urls import include
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path("api/auth/", include("authentication.urls")),
|
path("api/accounts/", include("accounts.urls")),
|
||||||
path("api/facilities/", include("facilities.urls")),
|
path("api/facilities/", include("facilities.urls")),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<div class="card card-container">
|
<div class="card card-container">
|
||||||
<Form @submit="handleLogin" :validation-schema="schema"
|
<Form @submit="handleLogin" :validation-schema="schema"
|
||||||
class="p-4 p-md-5 border rounded-3 bg-light">
|
class="p-4 p-md-5 border rounded-3 bg-light">
|
||||||
|
<h5 class="card-title">Login</h5>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="username">Username</label>
|
<label for="username">Username</label>
|
||||||
<Field name="username" type="text" class="form-control" />
|
<Field name="username" type="text" class="form-control" />
|
||||||
@@ -61,7 +62,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.loggedIn) {
|
if (this.loggedIn) {
|
||||||
this.$router.push("/profile");
|
this.$router.push("/");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -69,7 +70,7 @@ export default {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.$store.dispatch("auth/login", user).then(
|
this.$store.dispatch("auth/login", user).then(
|
||||||
() => {
|
() => {
|
||||||
this.$router.push("/profile");
|
this.$router.push("/");
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|||||||
112
frontend/wheretogo/src/components/RegistrationPage.vue
Normal file
112
frontend/wheretogo/src/components/RegistrationPage.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container col-xl-10 col-xxl-8 px-4 py-5">
|
||||||
|
<div class="row align-items-center g-lg-5 py-5">
|
||||||
|
<div class="col-md-10 mx-auto col-lg-5">
|
||||||
|
<div class="card card-container">
|
||||||
|
<Form @submit="handleRegister" :validation-schema="schema"
|
||||||
|
class="p-4 p-md-5 border rounded-3 bg-light">
|
||||||
|
<h5 class="card-title">Registration</h5>
|
||||||
|
<div v-if="!successful">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<Field name="username" type="text" class="form-control" />
|
||||||
|
<ErrorMessage name="username" class="error-feedback" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<Field name="email" type="email" class="form-control" />
|
||||||
|
<ErrorMessage name="email" class="error-feedback" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<Field name="password" type="password" class="form-control" />
|
||||||
|
<ErrorMessage name="password" class="error-feedback" />
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-primary btn-block" :disabled="loading">
|
||||||
|
<span v-show="loading" class="spinner-border spinner-border-sm"></span>
|
||||||
|
Sign Up
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
<div v-if="message" class="alert" :class="successful ? 'alert-success' : 'alert-danger'">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { Form, Field, ErrorMessage } from "vee-validate";
|
||||||
|
import * as yup from "yup";
|
||||||
|
export default {
|
||||||
|
name: "RegistrationPage",
|
||||||
|
components: {
|
||||||
|
Form,
|
||||||
|
Field,
|
||||||
|
ErrorMessage,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const schema = yup.object().shape({
|
||||||
|
username: yup
|
||||||
|
.string()
|
||||||
|
.required("Username is required!")
|
||||||
|
.min(3, "Must be at least 3 characters!")
|
||||||
|
.max(20, "Must be maximum 20 characters!"),
|
||||||
|
email: yup
|
||||||
|
.string()
|
||||||
|
.required("Email is required!")
|
||||||
|
.email("Email is invalid!")
|
||||||
|
.max(50, "Must be maximum 50 characters!"),
|
||||||
|
password: yup
|
||||||
|
.string()
|
||||||
|
.required("Password is required!")
|
||||||
|
.min(6, "Must be at least 6 characters!")
|
||||||
|
.max(40, "Must be maximum 40 characters!"),
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
successful: false,
|
||||||
|
loading: false,
|
||||||
|
message: "",
|
||||||
|
schema,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
loggedIn() {
|
||||||
|
return this.$store.state.auth.status.loggedIn;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.loggedIn) {
|
||||||
|
this.$router.push("/");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRegister(user) {
|
||||||
|
this.message = "";
|
||||||
|
this.successful = false;
|
||||||
|
this.loading = true;
|
||||||
|
this.$store.dispatch("auth/register", user).then(
|
||||||
|
(data) => {
|
||||||
|
this.message = data.message;
|
||||||
|
this.successful = true;
|
||||||
|
this.loading = false;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.message =
|
||||||
|
(error.response &&
|
||||||
|
error.response.data &&
|
||||||
|
error.response.data.message) ||
|
||||||
|
error.message ||
|
||||||
|
error.toString();
|
||||||
|
this.successful = false;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createWebHistory, createRouter } from "vue-router";
|
import { createWebHistory, createRouter } from "vue-router";
|
||||||
import MapPage from "./components/MapPage.vue";
|
import MapPage from "./components/MapPage.vue";
|
||||||
import LoginPage from "./components/LoginPage.vue";
|
import LoginPage from "./components/LoginPage.vue";
|
||||||
|
import RegistrationPage from "./components/RegistrationPage.vue";
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
@@ -11,6 +12,10 @@ const routes = [
|
|||||||
path: "/login",
|
path: "/login",
|
||||||
component: LoginPage,
|
component: LoginPage,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/register",
|
||||||
|
component: RegistrationPage,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default function authHeader() {
|
export default function authHeader() {
|
||||||
let user = JSON.parse(localStorage.getItem('user'));
|
let user = JSON.parse(localStorage.getItem('user'));
|
||||||
if (user && user.accessToken) {
|
if (user && user.access) {
|
||||||
return { Authorization: 'Bearer ' + user.accessToken };
|
return { Authorization: 'Bearer ' + user.access };
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
const API_URL = "http://localhost:8000/api/auth/";
|
const API_URL = "http://127.0.0.1:8000/api/accounts/";
|
||||||
class AuthService {
|
class AuthService {
|
||||||
login(user) {
|
login(user) {
|
||||||
return axios
|
return axios
|
||||||
.post(API_URL + "token", {
|
.post(API_URL + "auth/jwt/create", {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.data.accessToken) {
|
if (response.data.access) {
|
||||||
localStorage.setItem("user", JSON.stringify(response.data));
|
localStorage.setItem("user", JSON.stringify(response.data));
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -18,7 +18,7 @@ class AuthService {
|
|||||||
localStorage.removeItem("user");
|
localStorage.removeItem("user");
|
||||||
}
|
}
|
||||||
register(user) {
|
register(user) {
|
||||||
return axios.post(API_URL + "signup", {
|
return axios.post(API_URL + "users/", {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import authHeader from './auth-header';
|
import authHeader from './auth-header';
|
||||||
const API_URL = 'http://localhost:8000/api/auth/user/';
|
const API_URL = 'http://127.0.0.1:8000/api/accounts/users/me/';
|
||||||
class UserService {
|
class UserService {
|
||||||
getPublicContent() {
|
getPublicContent() {
|
||||||
return axios.get(API_URL + 'all');
|
return axios.get(API_URL + 'all');
|
||||||
|
|||||||
Reference in New Issue
Block a user