merge with last commit
This commit is contained in:
commit
ecc8762c56
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
import factory
|
import factory
|
||||||
import random
|
import random
|
||||||
import time, datetime
|
import time, datetime, uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
@ -8,6 +8,10 @@ from pairent_app.models import Apartament, User
|
||||||
|
|
||||||
factory.Faker.override_default_locale('ru_RU');
|
factory.Faker.override_default_locale('ru_RU');
|
||||||
|
|
||||||
|
class UUID(factory.declarations.BaseDeclaration):
|
||||||
|
def evaluate(self, instance, step, extra):
|
||||||
|
return str(uuid.uuid4()).upper();
|
||||||
|
|
||||||
class OpenID_Address(factory.declarations.BaseDeclaration):
|
class OpenID_Address(factory.declarations.BaseDeclaration):
|
||||||
def evaluate(self, instance, step, extra):
|
def evaluate(self, instance, step, extra):
|
||||||
return ''.join(random.choices(list('abcdef12345678990'), k=6)) + "@vvsu.ru";
|
return ''.join(random.choices(list('abcdef12345678990'), k=6)) + "@vvsu.ru";
|
||||||
|
@ -115,7 +119,6 @@ class UserFactory(factory.django.DjangoModelFactory):
|
||||||
|
|
||||||
favorites_apartments = CSV(1, 100, 1, 16);
|
favorites_apartments = CSV(1, 100, 1, 16);
|
||||||
comparison_apartments = CSV(1, 100, 1, 5);
|
comparison_apartments = CSV(1, 100, 1, 5);
|
||||||
openid_addr = OpenID_Address();
|
|
||||||
name = factory.faker.Faker('name');
|
name = factory.faker.Faker('name');
|
||||||
date_of_birth = Date(1980, 2006);
|
date_of_birth = Date(1980, 2006);
|
||||||
about_me = factory.faker.Faker('sentence');
|
about_me = factory.faker.Faker('sentence');
|
||||||
|
@ -128,3 +131,7 @@ class UserFactory(factory.django.DjangoModelFactory):
|
||||||
|
|
||||||
city = Random(0,0,0, ['Владивосток', 'Хабаровск', 'Урюпинск', 'Мухосранск', 'Нью-Йорк'])
|
city = Random(0,0,0, ['Владивосток', 'Хабаровск', 'Урюпинск', 'Мухосранск', 'Нью-Йорк'])
|
||||||
role = 's'
|
role = 's'
|
||||||
|
|
||||||
|
openid_addr = OpenID_Address();
|
||||||
|
openid_id = UUID();
|
||||||
|
photo_provider = Random(0,0,0, ['VVSU', 'GRAVATAR']);
|
|
@ -19,7 +19,6 @@ class Migration(migrations.Migration):
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('favorites_apartments', models.CharField(max_length=100, help_text="Избранные квартиры (CSV)")),
|
('favorites_apartments', models.CharField(max_length=100, help_text="Избранные квартиры (CSV)")),
|
||||||
('comparison_apartments', models.CharField(max_length=100, help_text="Квартиры для сравнения (CSV)")),
|
('comparison_apartments', models.CharField(max_length=100, help_text="Квартиры для сравнения (CSV)")),
|
||||||
('openid_addr', models.CharField(max_length=1000, null=False, help_text='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')),
|
|
||||||
('name', models.CharField(max_length=256, help_text='ФИО Пользователя')),
|
('name', models.CharField(max_length=256, help_text='ФИО Пользователя')),
|
||||||
('date_of_birth', models.DateField(help_text='Дата рождения пользователя')),
|
('date_of_birth', models.DateField(help_text='Дата рождения пользователя')),
|
||||||
('about_me', models.CharField(max_length=1000, help_text='Поле "О Себе"')),
|
('about_me', models.CharField(max_length=1000, help_text='Поле "О Себе"')),
|
||||||
|
@ -29,7 +28,11 @@ class Migration(migrations.Migration):
|
||||||
('telegram', models.CharField(max_length=1000, help_text='Телеграм пользователя', null=True)),
|
('telegram', models.CharField(max_length=1000, help_text='Телеграм пользователя', null=True)),
|
||||||
('discord', models.CharField(max_length=1000, help_text='Дискорд ник пользователя', null=True)),
|
('discord', models.CharField(max_length=1000, help_text='Дискорд ник пользователя', null=True)),
|
||||||
('city', models.CharField(max_length=1000, help_text='Город пользователя', null=True)),
|
('city', models.CharField(max_length=1000, help_text='Город пользователя', null=True)),
|
||||||
('role', models.CharField(max_length=1, help_text='Роль пользователя (s - student, a - admin, m - moderator)', null=False))
|
('role', models.CharField(max_length=1, help_text='Роль пользователя (s - student, a - admin, m - moderator)', null=False)),
|
||||||
|
('photo_provider', models.CharField(max_length=100, verbose_name='Сервис, из которого загружается фотография пользователя (VVSU, GRAVATAR)')),
|
||||||
|
|
||||||
|
('openid_addr', models.CharField(max_length=1000, null=False, help_text='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')),
|
||||||
|
('openid_id', models.CharField(max_length=5000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)'))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
# Generated by Django 4.2.1 on 2023-05-15 14:41
|
|
||||||
|
|
||||||
import django.core.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pairent_app', '0005_user'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='user',
|
|
||||||
options={'verbose_name': 'Пользователь', 'verbose_name_plural': 'Пользователи'},
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='about_me',
|
|
||||||
field=models.CharField(max_length=1000, verbose_name='Поле "О Себе"'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='city',
|
|
||||||
field=models.CharField(max_length=1000, null=True, verbose_name='Город пользователя'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='comparison_apartments',
|
|
||||||
field=models.CharField(max_length=100, verbose_name='Квартиры для сравнения (CSV)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='date_of_birth',
|
|
||||||
field=models.DateField(verbose_name='Дата рождения пользователя'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='discord',
|
|
||||||
field=models.CharField(max_length=1000, null=True, verbose_name='Дискорд ник пользователя'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='email',
|
|
||||||
field=models.CharField(max_length=1000, null=True, verbose_name='Почтовый ящик пользователя в формате user@example.com'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='favorites_apartments',
|
|
||||||
field=models.CharField(max_length=100, verbose_name='Избранные квартиры (CSV)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='gender',
|
|
||||||
field=models.CharField(max_length=1, verbose_name='Пол пользователя (f,m,n,?)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=256, verbose_name='ФИО Пользователя'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='openid_addr',
|
|
||||||
field=models.CharField(max_length=1000, verbose_name='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='phone',
|
|
||||||
field=models.CharField(max_length=30, null=True, verbose_name='Телефон пользователя в международном формате (+00000000)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='role',
|
|
||||||
field=models.CharField(max_length=1, verbose_name='Роль пользователя (s - student, a - admin, m - moderator)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='user',
|
|
||||||
name='telegram',
|
|
||||||
field=models.CharField(max_length=1000, null=True, verbose_name='Телеграм пользователя'),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='PsychTestAnswers',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('first_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на первый вопрос')),
|
|
||||||
('second_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на второй вопрос')),
|
|
||||||
('third_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на третий вопрос')),
|
|
||||||
('fourth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на четвертый вопрос')),
|
|
||||||
('fifth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на пятый вопрос')),
|
|
||||||
('sixth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на шестой вопрос')),
|
|
||||||
('seventh_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на седьмой вопрос')),
|
|
||||||
('eighth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на восьмой вопрос')),
|
|
||||||
('nineth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на девятый вопрос')),
|
|
||||||
('tenth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на десятый вопрос')),
|
|
||||||
('eleventh_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на одиннадцатый вопрос')),
|
|
||||||
('twelfth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на двенадцатый вопрос')),
|
|
||||||
('users', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pairent_app.user', verbose_name='Пользователь')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import django.core.validators
|
||||||
|
from django.core.validators import RegexValidator, MaxValueValidator
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
from pairent_app.models import User
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pairent_app', '0005_user'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PsychTestAnswers',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('first_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на первый вопрос')),
|
||||||
|
('second_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на второй вопрос')),
|
||||||
|
('third_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на третий вопрос')),
|
||||||
|
('fourth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на четвертый вопрос')),
|
||||||
|
('fifth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на пятый вопрос')),
|
||||||
|
('sixth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на шестой вопрос')),
|
||||||
|
('seventh_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на седьмой вопрос')),
|
||||||
|
('eighth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на восьмой вопрос')),
|
||||||
|
('nineth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на девятый вопрос')),
|
||||||
|
('tenth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на десятый вопрос')),
|
||||||
|
('eleventh_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на одиннадцатый вопрос')),
|
||||||
|
('twelfth_question', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5)], verbose_name='Ответ на двенадцатый вопрос')),
|
||||||
|
('users', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pairent_app.user', verbose_name='Пользователь')),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]
|
|
@ -1,22 +0,0 @@
|
||||||
# Generated by Django 4.2.1 on 2023-05-15 15:23
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('pairent_app', '0006_alter_user_options_alter_user_about_me_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='psychtestanswers',
|
|
||||||
options={'verbose_name': 'Ответ на психологический тест', 'verbose_name_plural': 'Ответы на психологический тест'},
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='psychtestanswers',
|
|
||||||
old_name='users',
|
|
||||||
new_name='user',
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -86,7 +86,6 @@ class User(models.Model):
|
||||||
favorites_apartments = models.CharField(max_length=100, verbose_name="Избранные квартиры (CSV)")
|
favorites_apartments = models.CharField(max_length=100, verbose_name="Избранные квартиры (CSV)")
|
||||||
comparison_apartments = models.CharField(max_length=100, verbose_name="Квартиры для сравнения (CSV)")
|
comparison_apartments = models.CharField(max_length=100, verbose_name="Квартиры для сравнения (CSV)")
|
||||||
|
|
||||||
openid_addr = models.CharField(max_length=1000, null=False, verbose_name='Адрес Open ID Connect (login@provider.com, для ВВГУ - login@vvsu.ru)')
|
|
||||||
name = models.CharField(max_length=256, verbose_name='ФИО Пользователя')
|
name = models.CharField(max_length=256, verbose_name='ФИО Пользователя')
|
||||||
date_of_birth = models.DateField(verbose_name='Дата рождения пользователя')
|
date_of_birth = models.DateField(verbose_name='Дата рождения пользователя')
|
||||||
about_me = models.CharField(max_length=1000, verbose_name='Поле "О Себе"')
|
about_me = models.CharField(max_length=1000, verbose_name='Поле "О Себе"')
|
||||||
|
@ -102,6 +101,11 @@ class User(models.Model):
|
||||||
|
|
||||||
role = models.CharField(max_length=1, verbose_name='Роль пользователя (s - student, a - admin, m - moderator)', null=False)
|
role = models.CharField(max_length=1, verbose_name='Роль пользователя (s - student, a - admin, m - moderator)', null=False)
|
||||||
|
|
||||||
|
photo_provider = models.CharField(max_length=100, verbose_name='Сервис, из которого загружается фотография пользователя (VVSU, GRAVATAR)')
|
||||||
|
|
||||||
|
openid_addr = models.CharField(max_length=1000, null=False, verbose_name='Адрес Open ID (login@provider.com, для ВВГУ - login@vvsu.ru)')
|
||||||
|
openid_id = models.CharField(max_length=10000, verbose_name='ID Пользователя в системе провайдера авторизации (скорее всего ВВГУ)')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Пользователь"
|
verbose_name = "Пользователь"
|
||||||
verbose_name_plural = "Пользователи"
|
verbose_name_plural = "Пользователи"
|
||||||
|
|
|
@ -10,7 +10,14 @@ urlpatterns = format_suffix_patterns([
|
||||||
path("apartaments/comparison/", views.ApartamentGetManyViewSet.as_view({'get': 'retrieve'})), # пример: apartaments/comparison/?user_id=1 user_id - id пользователя
|
path("apartaments/comparison/", views.ApartamentGetManyViewSet.as_view({'get': 'retrieve'})), # пример: apartaments/comparison/?user_id=1 user_id - id пользователя
|
||||||
path("apartaments/favorite/", views.ApartamentGetManyViewSet.as_view({'get': 'list'})), # пример: apartaments/favorite/?user_id=1 user_id - id пользователя
|
path("apartaments/favorite/", views.ApartamentGetManyViewSet.as_view({'get': 'list'})), # пример: apartaments/favorite/?user_id=1 user_id - id пользователя
|
||||||
path("apartaments/filters/", views.ApartmentFilter.as_view({'post': 'list'})),
|
path("apartaments/filters/", views.ApartmentFilter.as_view({'post': 'list'})),
|
||||||
path("psych_test/add_result/<int:pk>", views.PsychTestAddResultViewSet.as_view({'post': 'create'})), # пример: psych_test/add_result/1/?result=50 result - результат псих теста пользователя
|
|
||||||
path("users/get_compatible", views.CompatibleUsersView.as_view({'post': 'list'})),
|
# user
|
||||||
|
path("user/get_compatible", views.CompatibleUsersView.as_view({'post': 'list'})),
|
||||||
|
# пример: psych_test/add_result/1/?result=50 result - результат псих теста пользователя
|
||||||
|
path("psych_test/add_result/<int:pk>", views.PsychTestAddResultViewSet.as_view({'post': 'create'})),
|
||||||
|
path('user/get', views.UserGet.as_view()),
|
||||||
|
|
||||||
|
# auth
|
||||||
re_path(r'^auth/vvsu/', views.VVSUAuthProxy),
|
re_path(r'^auth/vvsu/', views.VVSUAuthProxy),
|
||||||
|
path('auth/user/login', views.UserLogin.as_view())
|
||||||
])
|
])
|
||||||
|
|
|
@ -2,8 +2,9 @@ from rest_framework import viewsets
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView, View
|
from rest_framework.views import APIView, View
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from django.http import HttpResponseBadRequest, HttpResponse
|
from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse, HttpRequest
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
|
@ -15,7 +16,7 @@ from .serializer import (ApartamentListSerializer,
|
||||||
PsychTestAddResultSerializer,
|
PsychTestAddResultSerializer,
|
||||||
PublicUserSerializer)
|
PublicUserSerializer)
|
||||||
|
|
||||||
import json, math, random, re, requests
|
import json, math, random, re, requests, oidc_client, base64, hashlib
|
||||||
|
|
||||||
class ApartamentViewSet(viewsets.ReadOnlyModelViewSet):
|
class ApartamentViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
"""Вывод списка квартир или отдельной квартиры"""
|
"""Вывод списка квартир или отдельной квартиры"""
|
||||||
|
@ -169,3 +170,120 @@ def VVSUAuthProxy(req: Request):
|
||||||
resp.headers['Content-Type'] = preq.headers['Content-Type'];
|
resp.headers['Content-Type'] = preq.headers['Content-Type'];
|
||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
|
|
||||||
|
def regiserUser(oid, provider_id, name, date_of_birth):
|
||||||
|
user = User(
|
||||||
|
favorites_apartments='',
|
||||||
|
comparison_apartments='',
|
||||||
|
name=name,
|
||||||
|
date_of_birth=date_of_birth,
|
||||||
|
about_me='',
|
||||||
|
gender='?',
|
||||||
|
phone='+00000',
|
||||||
|
# email=,
|
||||||
|
# telegram=,
|
||||||
|
# discord=,
|
||||||
|
# city=,
|
||||||
|
role='s',
|
||||||
|
# photo_provider=,
|
||||||
|
openid_addr=oid,
|
||||||
|
openid_id=provider_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_oauth_token(remote, data):
|
||||||
|
return requests.post(remote + '/oauth2/token', data,
|
||||||
|
headers={
|
||||||
|
'Origin': 'https://pairent.vvsu.ru',
|
||||||
|
'Referer': 'https://pairent.vvsu.ru'
|
||||||
|
}).json();
|
||||||
|
|
||||||
|
def get_oauth_data(remote, key):
|
||||||
|
return requests.get(remote + '/userinfo', headers={
|
||||||
|
'Origin': 'https://pairent.vvsu.ru',
|
||||||
|
'Authorization': 'Bearer ' + key,
|
||||||
|
'User-Agent': 'curl/8.1'
|
||||||
|
}).json();
|
||||||
|
|
||||||
|
class UserLogin(APIView):
|
||||||
|
# TODO: Remove csrf exempt when index.html is loaded through django
|
||||||
|
@csrf_exempt
|
||||||
|
def post(self, req: HttpRequest):
|
||||||
|
|
||||||
|
if (req.session.has_key('auth_data')):
|
||||||
|
# TODO: Return user object instead of error
|
||||||
|
return JsonResponse({'error': 'already authenticated'})
|
||||||
|
|
||||||
|
if (req.content_type != 'application/json'):
|
||||||
|
res = HttpResponse({'error': 'bad content type'});
|
||||||
|
res.status_code = 400;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
data = json.loads(req.body.decode('utf8'));
|
||||||
|
|
||||||
|
if not ('code' in data and 'code_verifier' in data):
|
||||||
|
res = JsonResponse({'error': 'no code'});
|
||||||
|
res.status_code = 400;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
# auth_data = get_oauth_token('https://vvsu.ru/connect', {
|
||||||
|
# 'grant_type': 'authorization_code',
|
||||||
|
# 'redirect_uri': 'https://pairent.vvsu.ru/sign-in/',
|
||||||
|
# 'code': data['code'],
|
||||||
|
# 'code_verifier': data['code_verifier'],
|
||||||
|
# 'client_id': 'it-hub-client',
|
||||||
|
# 'client_secret': 'U8y@uPVee6Q^*729esHTo4Vd'
|
||||||
|
# });
|
||||||
|
|
||||||
|
auth_data = {'access_token': '5kHvrjy91LJgJLKitejBBG24c7JiX45tEstKVHRpfHc._WQDwQ2F13aytbGFjlGnjXJeUWcDD1V3om3cRW0IujM', 'expires_in': 3600, 'id_token': 'eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpoeWRyYS5vcGVuaWQuaWQtdG9rZW4iLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiIwIiwiYXRfaGFzaCI6IjRMR1dRekxVaXFodUVTYjU0QWFIM0EiLCJhdWQiOlsiaXQtaHViLWNsaWVudCJdLCJhdXRoX3RpbWUiOjE2ODQyMzc4MDksImNhbGxiYWNrX3VybCI6IiIsImV4cCI6MTY4NDI0MTQ1NSwiZmFtaWx5X25hbWUiOiLQn9GD0YHRgtC-0LLQsNC70L7QsiIsImdpdmVuX25hbWUiOiLQndC40LrQuNGC0LAiLCJpYXQiOjE2ODQyMzc4NTUsImlkIjoiMDk2Qzc4Q0QtNDk0My00RDU3LUJDNkQtNUNERTEyRjY4NkUzIiwiaXNzIjoiaHR0cHM6Ly93d3cudnZzdS5ydS9jb25uZWN0LyIsImp0aSI6IjEzMTBhNzcwLWFhZWUtNGExYS1hMTc1LWM3MzY3ZWM0ZjVhNyIsImxvZ2luIjoiaHR0cHM6Ly9vcGVuaWQudnZzdS5ydS9ibGVrX18iLCJvcGVuaWQiOiJodHRwczovL29wZW5pZC52dnN1LnJ1L2JsZWtfXyIsInBpY3R1cmUiOiJodHRwczovL3d3dy52dnN1LnJ1L29pc2twL3Bob3RvL3B0aC5hc3A_SUQ9MDk2Qzc4Q0QtNDk0My00RDU3LUJDNkQtNUNERTEyRjY4NkUzXHUwMDI2IiwicHJvZmlsZV91cmwiOm51bGwsInJhdCI6MTY4NDIzNzc5Nywic2lkIjoiOTYyYzg0OGYtZThkNS00ZDJjLWEwZmEtYjI5YmU3YjBlODAxIiwic3ViIjoiaHR0cHM6Ly9vcGVuaWQudnZzdS5ydS9ibGVrX18iLCJzdXJuYW1lIjoi0J_Rg9GB0YLQvtCy0LDQu9C-0LIiLCJ0aXRsZSI6ItCh0YLRg9C00LXQvdGCIiwidnZzdV9JZEVtcGwiOm51bGwsInZ2c3VfSWRTdHVkIjoiMTk3MDgwIiwidnZzdV9JZFVzZXIiOjE5MDQ4OSwidnZzdV9sb2dpbiI6ImJsZWtfXyJ9.A4BiOxpOqnesSiTGRdcTsC-lGhSABswivpUovD9EOdYmqKW753VlLcXQxfBPcfmq8Fdf7RmVvXTXPXYqkX7AKxQT-yUUm7XtJHCb85g2YfL64cjTP2sFYD6wPIU9nzXbCrsgKqKubY3p16Dn9VyrBCXE9N6jdbuNOFbWMLPLPlp7U5fx2SzVGaBMUONlTf8KiLkcisQoN4c_rPGqdi38gzhLf7WGEiKLOldXH1q-s_kPeObFvcdbsFrrnDPnJtdqBx8SF02wqJsrZlBiB9Hl-d6sSJYLZZWumFhS-qscfwRlTEZKqC-hWF5c9R8CUYewk89JxRvCcKrHZvPMip9j9vJF1_OjkSrC5EkGaprl765FgVPEBJqXj9LjGRkTOYfYUFAAMia_HhjtinQFp6XJ-Rh3JrmIfLAQ7DEUSOldMQ1xUw9GeHo_0sIsnjaM6lVx6M_SiDTWihxNu58DiI8tmvkdw7in95OJRoJZ30EhR3SGYsK3b51qdYK1aieufJHX40bN_S1gc84pisTg58z-zC5kGsjsZNv6gRSTO4oOpZMK1FMjv7HyasSMWEu-J052X4Qxquj4pWglpiGQNt3-E0jZUUjqmZ0-7AYiyEC_3IItBqWrve-LTXRF5faIZB5v3F3urY6Qjgn93m_AoK1oujfNAPk8WOLTv419CuC2fAc', 'scope': 'openid vvsu_IdUser vvsu_IdEmpl vvsu_IdStud vvsu_login given_name family_name', 'token_type': 'bearer'}
|
||||||
|
|
||||||
|
user = None;
|
||||||
|
new_user = False;
|
||||||
|
|
||||||
|
print(auth_data);
|
||||||
|
|
||||||
|
return JsonResponse(get_oauth_data('https://vvsu.ru/connect', auth_data['access_token']));
|
||||||
|
|
||||||
|
req.session['auth_data'] = vvsu_data;
|
||||||
|
|
||||||
|
if ('error' in vvsu_data):
|
||||||
|
res = JsonResponse(vvsu_data);
|
||||||
|
res.status_code = cb.status_code;
|
||||||
|
return res
|
||||||
|
|
||||||
|
vvsu_data['vvsu_login'] += '@vvsu.ru';
|
||||||
|
try:
|
||||||
|
user = User.objects.get(openid_addr=vvsu_data['vvsu_login']);
|
||||||
|
except User.DoesNotExist:
|
||||||
|
registerUser(vvsu_data['vvsu_login'], cb.id, f'{cb.given_name} {cb.family_name}');
|
||||||
|
user = User.objects.get(openid_addr=vvsu_data['vvsu_login']);
|
||||||
|
new_user = True;
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'user_data': user,
|
||||||
|
'new_user': new_user
|
||||||
|
});
|
||||||
|
|
||||||
|
class UserGet(APIView):
|
||||||
|
def get(self, req: HttpRequest):
|
||||||
|
if not ('id' in req.GET.keys() or 'login' in req.GET.keys()):
|
||||||
|
res = JsonResponse({'error': 'no id or login'});
|
||||||
|
res.status_code = 400;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
id_type = 'id' if 'id' in req.GET.keys() else 'login';
|
||||||
|
id = req.GET.get(id_type);
|
||||||
|
|
||||||
|
if (id_type == 'login'):
|
||||||
|
if not id.endswith('@vvsu.ru'):
|
||||||
|
id += '@vvsu.ru';
|
||||||
|
id_type = 'openid_addr';
|
||||||
|
|
||||||
|
user = None;
|
||||||
|
try:
|
||||||
|
user = User.objects.get(**{id_type: id});
|
||||||
|
except User.DoesNotExist:
|
||||||
|
res = JsonResponse({'error': 'not found'});
|
||||||
|
res.status_code = 404;
|
||||||
|
return res;
|
||||||
|
|
||||||
|
return JsonResponse(PublicUserSerializer(user).data);
|
|
@ -65,7 +65,7 @@ REST_FRAMEWORK = {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Настройка отвечающая, что все могут отправлять запрос на бекенд. УБРАТЬ ПРИ ПРОДАКШЕНЕ!
|
# Настройка отвечающая, что все могут отправлять запрос на бекенд. УБРАТЬ ПРИ ПРОДАКШЕНЕ!
|
||||||
CORS_ORIGIN_ALLOW_ALL = True
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'pairent_backend.urls'
|
ROOT_URLCONF = 'pairent_backend.urls'
|
||||||
|
|
||||||
|
@ -146,3 +146,17 @@ STATIC_URL = 'src/'
|
||||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
# VVSU Auth
|
||||||
|
OIDC_CONF = {
|
||||||
|
'client-id': 'it-hub-client',
|
||||||
|
'client-secret': 'U8y@uPVee6Q^*729esHTo4Vd',
|
||||||
|
'authority': "https://vvsu.ru/connect"
|
||||||
|
}
|
||||||
|
|
||||||
|
# CSRF
|
||||||
|
CORS_ORIGIN_ALLOW_ALL = DEBUG
|
||||||
|
CSRF_COOKIE_SECURE = not DEBUG
|
||||||
|
CSRF_COOKIE_HTTPONLY = not DEBUG
|
||||||
|
CSRF_TRUSTED_ORIGINS = ['http://pairent.vvsu.ru', 'http://localhost']
|
||||||
|
CORS_ORIGIN_WHITELIST = ('http://pairent.vvsu.ru', 'http://localhost')
|
|
@ -4,3 +4,4 @@ djangorestframework
|
||||||
django-cors-headers
|
django-cors-headers
|
||||||
Pillow
|
Pillow
|
||||||
requests
|
requests
|
||||||
|
oidc-client
|
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M18 4V14.2857C18 15.2321 17.2441 16 16.3125 16H1.6875C0.755859 16 0 15.2321 0 14.2857V4C0 3.05357 0.755859 2.28571 1.6875 2.28571H4.78125L5.21367 1.11071C5.45977 0.442857 6.08906 0 6.79219 0H11.2043C11.9074 0 12.5367 0.442857 12.7828 1.11071L13.2188 2.28571H16.3125C17.2441 2.28571 18 3.05357 18 4ZM13.2188 9.14286C13.2188 6.77857 11.3273 4.85714 9 4.85714C6.67266 4.85714 4.78125 6.77857 4.78125 9.14286C4.78125 11.5071 6.67266 13.4286 9 13.4286C11.3273 13.4286 13.2188 11.5071 13.2188 9.14286ZM12.0938 9.14286C12.0938 10.875 10.7051 12.2857 9 12.2857C7.29492 12.2857 5.90625 10.875 5.90625 9.14286C5.90625 7.41071 7.29492 6 9 6C10.7051 6 12.0938 7.41071 12.0938 9.14286Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 801 B |
|
@ -1,17 +1,51 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import constants from '../constants';
|
import constants from '../constants';
|
||||||
|
|
||||||
const { API_ROOT } = constants;
|
const { API_ROOT, api_path } = constants;
|
||||||
|
|
||||||
|
class UserLoginResponse {
|
||||||
|
/** @type {string} */
|
||||||
|
session_key;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
openid_login;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
id;
|
||||||
|
}
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
this = { ...data, ...this };
|
for (const key in data) {
|
||||||
|
this[key] = data[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static restoreFromLocalStorage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @returns {User}
|
||||||
|
*/
|
||||||
static async getById(id) {
|
static async getById(id) {
|
||||||
const data = await axios.post(API_ROOT + '/users/get', { id });
|
const data = await axios.get(API_ROOT + '/api/user/get', { params: { id } });
|
||||||
if (data.data['error'])
|
if (data.data['error'])
|
||||||
throw new Error(data.data['error']);
|
throw new Error(data.data['error']);
|
||||||
return new User(data.data);
|
return new User(data.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {import('oidc-client-ts').SigninResponse} response */
|
||||||
|
static async login(response) {
|
||||||
|
if (response.error !== null) {
|
||||||
|
throw new Error(response.error + ': ' + response.error_description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await axios.post(api_path('/api/auth/user/login'), response);
|
||||||
|
return new User(data.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { User, UserLoginResponse }
|
|
@ -6,6 +6,7 @@ import SVGIcon from '../UI/Icon/SVGIcon';
|
||||||
// import './styles/Header.css';
|
// import './styles/Header.css';
|
||||||
|
|
||||||
const HeaderElement = styled.header`
|
const HeaderElement = styled.header`
|
||||||
|
min-width: 950px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from "react";
|
||||||
|
import { styled } from "styled-components";
|
||||||
|
|
||||||
|
const FloatingBox = styled.div`
|
||||||
|
position: fixed;
|
||||||
|
top: 45%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
border: 1px solid #c2c4c2;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px 36px;
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 2px 1px #00000010;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& hr {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
height: 0px;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid #c2c4c2;
|
||||||
|
box-shadow: 0 2px 1px #c2c4c280;
|
||||||
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default FloatingBox;
|
|
@ -1,5 +1,8 @@
|
||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
|
|
||||||
|
/** @returns {string} */
|
||||||
|
const api_path = path => API_ROOT + path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Api root path
|
* Api root path
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -7,32 +10,14 @@ import { nanoid } from 'nanoid'
|
||||||
const API_ROOT = window.location.protocol + '//127.0.0.1:8000';
|
const API_ROOT = window.location.protocol + '//127.0.0.1:8000';
|
||||||
// ДЛЯ ПРОДА ПОСТАВИТЬ ЭТО: '//pairent.vvsu.ru'
|
// ДЛЯ ПРОДА ПОСТАВИТЬ ЭТО: '//pairent.vvsu.ru'
|
||||||
|
|
||||||
if (window.localStorage.getItem('oidc_client_key') == undefined) {
|
|
||||||
window.localStorage.setItem('oidc_client_key', nanoid(32));
|
|
||||||
}
|
|
||||||
|
|
||||||
const OIDC_CLIENT_KEY = window.localStorage.getItem('oidc_client_key');
|
|
||||||
|
|
||||||
|
|
||||||
/** OpenID Connect Client Config
|
/** OpenID Connect Client Config
|
||||||
* @type {import('oidc-client-ts').OidcClientSettings}
|
* @type {import('oidc-client-ts').OidcClientSettings}
|
||||||
*/
|
*/
|
||||||
const OIDCConfig = {
|
const OIDCConfig = {
|
||||||
onSignIn: () => {},
|
authority: api_path('/api/auth/vvsu/'),
|
||||||
authority: API_ROOT + '/api/auth/vvsu/',
|
|
||||||
client_id: 'it-hub-client',
|
client_id: 'it-hub-client',
|
||||||
redirect_uri: 'https://pairent.vvsu.ru/sign-in/',
|
redirect_uri: 'https://pairent.vvsu.ru/sign-in/',
|
||||||
scope: [
|
scope: 'openid vvsu_IdUser vvsu_IdEmpl vvsu_IdStud vvsu_login given_name family_name'
|
||||||
'openid',
|
|
||||||
'vvsu_IdUser',
|
|
||||||
'vvsu_IdEmpl',
|
|
||||||
'vvsu_IdStud',
|
|
||||||
'vvsu_login',
|
|
||||||
'given_name',
|
|
||||||
'family_name'
|
|
||||||
],
|
|
||||||
client_secret: OIDC_CLIENT_KEY
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default Object.freeze({ API_ROOT, OIDCConfig, api_path });
|
||||||
export default Object.freeze({ API_ROOT, OIDCConfig, OIDC_CLIENT_KEY });
|
|
|
@ -1,7 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
import { HashLoader } from "react-spinners";
|
import { HashLoader } from "react-spinners";
|
||||||
import { SigninResponse, SigninState } from 'oidc-client-ts';
|
import { SigninResponse, UserManager } from 'oidc-client-ts';
|
||||||
|
import { User } from "../../API/User";
|
||||||
|
import FloatingBox from "../../components/UI/FloatingBox";
|
||||||
|
|
||||||
|
import constants from "../../constants";
|
||||||
|
|
||||||
const CenterContainer = styled.div`
|
const CenterContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -18,27 +22,59 @@ const CenterContainer = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ErrorText = styled.p`
|
||||||
|
font-family: monospace;
|
||||||
|
margin: 10px 0;
|
||||||
|
width: 780px;
|
||||||
|
`;
|
||||||
|
|
||||||
export default class LoggedIn extends React.Component {
|
export default class LoggedIn extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.response = new SigninResponse(new URL(window.location.href).searchParams);
|
this.response = new SigninResponse(new URL(window.location.href).searchParams);
|
||||||
this.signin_state = SigninState.fromStorageString(window.localStorage.getItem('oidc_signin_state'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
console.log(this.response)
|
if (this.response.error) return;
|
||||||
|
|
||||||
|
let code_verifier = '?';
|
||||||
|
// get code verifier
|
||||||
|
for (const key in localStorage) {
|
||||||
|
if (key.startsWith('oidc.')) {
|
||||||
|
code_verifier = JSON.parse(localStorage[key]).code_verifier;
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(await User.login({...this.response, code_verifier}));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div style={{height: '65vh'}}>
|
<>
|
||||||
<CenterContainer>
|
<div style={{height: '65vh'}}>
|
||||||
<h2>Подождите пожалуйста</h2>
|
<CenterContainer>
|
||||||
<div style={{margin: '36px auto', width:'fit-content'}}>
|
<h2>Подождите пожалуйста</h2>
|
||||||
<HashLoader size='80' color='#0077aa' />
|
<div style={{margin: '36px auto', width:'fit-content'}}>
|
||||||
|
<HashLoader size='80px' color='#0077aa' />
|
||||||
|
</div>
|
||||||
|
</CenterContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{
|
||||||
|
this.response.error ?
|
||||||
|
<div>
|
||||||
|
<FloatingBox>
|
||||||
|
<h2>Ошибка авторизации</h2>
|
||||||
|
<p>{this.response.error}</p>
|
||||||
|
<ErrorText>{this.response.error_description}</ErrorText>
|
||||||
|
</FloatingBox>
|
||||||
</div>
|
</div>
|
||||||
</CenterContainer>
|
: null
|
||||||
</div>
|
}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,45 +1,15 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { styled } from 'styled-components';
|
import { styled } from 'styled-components';
|
||||||
import BlueButton from '../../components/UI/BlueButton';
|
|
||||||
import { HashLoader } from 'react-spinners';
|
import { HashLoader } from 'react-spinners';
|
||||||
|
import BlueButton from '../../components/UI/BlueButton';
|
||||||
|
import FloatingBox from '../../components/UI/FloatingBox';
|
||||||
|
|
||||||
import * as OpenID from 'oidc-client-ts';
|
import * as OpenID from 'oidc-client-ts';
|
||||||
import constants from '../../constants';
|
import constants from '../../constants';
|
||||||
|
|
||||||
const { OIDCConfig } = constants;
|
const { OIDCConfig } = constants;
|
||||||
|
|
||||||
const LoginBox = styled.div`
|
|
||||||
position: fixed;
|
|
||||||
top: 45%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
border: 1px solid #c2c4c2;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 24px 36px;
|
|
||||||
background: white;
|
|
||||||
box-shadow: 0 2px 1px #00000010;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
& h2 {
|
|
||||||
margin: 0px 0;
|
|
||||||
}
|
|
||||||
& hr {
|
|
||||||
margin: 16px 0;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
height: 0px;
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid #c2c4c2;
|
|
||||||
box-shadow: 0 2px 1px #c2c4c280;
|
|
||||||
}
|
|
||||||
& p {
|
|
||||||
font-size: 8pt;
|
|
||||||
color: gray;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const LoginButton = styled(BlueButton)`
|
const LoginButton = styled(BlueButton)`
|
||||||
font-size: 11pt;
|
font-size: 11pt;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
@ -60,6 +30,12 @@ const LoginButton = styled(BlueButton)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const SmallText = styled.p`
|
||||||
|
font-size: 8pt;
|
||||||
|
color: gray;
|
||||||
|
margin-top: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
const VVSULogoSVG = () => {
|
const VVSULogoSVG = () => {
|
||||||
return <svg xmlns="http://www.w3.org/2000/svg" xmlSpace="preserve" viewBox="0 0 451.5 155.9"><path fillRule="evenodd" d="M408.7 0h42.8L416 102.5l-2.8 8.1c-8.2 23.9-33.9 43.6-58 45.2h-.6c-.8.1-1.6.1-2.5.1H348c-.8 0-1.6 0-2.4-.1h-43l15.7-45.2.3-.8h35.7c9.1 0 19.2-7.8 22.5-17.2l.4-1 1.2-3.5h-32.7c-4.4 0-19.5 0-13.1-18.5l4.4-12.9 3.9-11.2L356.5 0H393l-21.7 62.8H387c2.6-7.5 15.8-45.6 21.7-62.8zm-91.2 0h25.9l-15.7 45.3h-7c-9.6 0-20.1 7.9-23.5 17.4L265 155.9c-.5 0-1-.1-1.5-.1h-43l15.8-45.2 7.8-22.6 14.8-42.5c8.3-24.1 34.4-44 58.6-45.2V0zM208 0h48.7l-20.1 58.1c-3.2 9.1-9.4 15.2-19.4 17.2l16.8.1-12.3 35.2c-8.2 23.9-33.9 43.6-58 45.2h-.7c-.8.1-1.6.1-2.5.1h-4.1c-.8 0-1.6 0-2.4-.1h-43l38.2-110.4c8.4-24.1 34.4-44 58.7-45.2l.1-.2zm-12.7 40.8h15.4l-8.1 23h-23.9l4.4-12.5c1.7-4.6 7.3-10.5 12.2-10.5zm-25.6 48.8H194l-5.5 16.1c-1.1 3.4-6.1 10.8-13.6 10.8h-14.1l8.9-26.9zM96.9 0h48.7l-20.2 58.1c-3.2 9.1-9.4 15.2-19.4 17.2l16.8.1-9.3 27.1h-.2l-2.8 8.1c-8.2 23.9-33.9 43.6-58 45.2h-.6c-.8.1-1.7.1-2.5.1h-4.1c-.8 0-1.6 0-2.4-.1H0l15.7-45.2L23.6 88l14.7-42.5C46.7 21.4 72.7 1.5 97 .3V0zM84.2 40.8h15.4l-8.1 23H67.6L72 51.3c1.7-4.6 7.3-10.5 12.2-10.5zM58.6 89.6h24.3l-5.5 16.1c-1.1 3.4-6.1 10.8-13.5 10.8H49.7l8.9-26.9z" clipRule="evenodd"/></svg>;
|
return <svg xmlns="http://www.w3.org/2000/svg" xmlSpace="preserve" viewBox="0 0 451.5 155.9"><path fillRule="evenodd" d="M408.7 0h42.8L416 102.5l-2.8 8.1c-8.2 23.9-33.9 43.6-58 45.2h-.6c-.8.1-1.6.1-2.5.1H348c-.8 0-1.6 0-2.4-.1h-43l15.7-45.2.3-.8h35.7c9.1 0 19.2-7.8 22.5-17.2l.4-1 1.2-3.5h-32.7c-4.4 0-19.5 0-13.1-18.5l4.4-12.9 3.9-11.2L356.5 0H393l-21.7 62.8H387c2.6-7.5 15.8-45.6 21.7-62.8zm-91.2 0h25.9l-15.7 45.3h-7c-9.6 0-20.1 7.9-23.5 17.4L265 155.9c-.5 0-1-.1-1.5-.1h-43l15.8-45.2 7.8-22.6 14.8-42.5c8.3-24.1 34.4-44 58.6-45.2V0zM208 0h48.7l-20.1 58.1c-3.2 9.1-9.4 15.2-19.4 17.2l16.8.1-12.3 35.2c-8.2 23.9-33.9 43.6-58 45.2h-.7c-.8.1-1.6.1-2.5.1h-4.1c-.8 0-1.6 0-2.4-.1h-43l38.2-110.4c8.4-24.1 34.4-44 58.7-45.2l.1-.2zm-12.7 40.8h15.4l-8.1 23h-23.9l4.4-12.5c1.7-4.6 7.3-10.5 12.2-10.5zm-25.6 48.8H194l-5.5 16.1c-1.1 3.4-6.1 10.8-13.6 10.8h-14.1l8.9-26.9zM96.9 0h48.7l-20.2 58.1c-3.2 9.1-9.4 15.2-19.4 17.2l16.8.1-9.3 27.1h-.2l-2.8 8.1c-8.2 23.9-33.9 43.6-58 45.2h-.6c-.8.1-1.7.1-2.5.1h-4.1c-.8 0-1.6 0-2.4-.1H0l15.7-45.2L23.6 88l14.7-42.5C46.7 21.4 72.7 1.5 97 .3V0zM84.2 40.8h15.4l-8.1 23H67.6L72 51.3c1.7-4.6 7.3-10.5 12.2-10.5zM58.6 89.6h24.3l-5.5 16.1c-1.1 3.4-6.1 10.8-13.5 10.8H49.7l8.9-26.9z" clipRule="evenodd"/></svg>;
|
||||||
}
|
}
|
||||||
|
@ -82,17 +58,16 @@ export default class LoginPage extends React.Component {
|
||||||
OpenID.Log.setLogger(console);
|
OpenID.Log.setLogger(console);
|
||||||
OpenID.Log.setLevel(OpenID.Log.DEBUG);
|
OpenID.Log.setLevel(OpenID.Log.DEBUG);
|
||||||
|
|
||||||
let client = new OpenID.OidcClient(OIDCConfig);
|
let client = new OpenID.UserManager(OIDCConfig);
|
||||||
|
|
||||||
|
client.signinRedirect();
|
||||||
|
|
||||||
const req = await client.createSigninRequest({});
|
|
||||||
window.localStorage.setItem('oidc_signin_state', req.state.toStorageString());
|
|
||||||
window.location.href = req.url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div style={{height: '65vh'}}>
|
<div style={{height: '65vh'}}>
|
||||||
<LoginBox>
|
<FloatingBox>
|
||||||
<h2>Вход</h2>
|
<h2>Вход</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
<LoginButton onClick={this.openid} disabled={this.state.loading}>
|
<LoginButton onClick={this.openid} disabled={this.state.loading}>
|
||||||
|
@ -105,11 +80,11 @@ export default class LoginPage extends React.Component {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</LoginButton>
|
</LoginButton>
|
||||||
<p>
|
<SmallText>
|
||||||
Вход осуществляется только через<br/>
|
Вход осуществляется только через<br/>
|
||||||
Систему Единого Входа ВВГУ
|
Систему Единого Входа ВВГУ
|
||||||
</p>
|
</SmallText>
|
||||||
</LoginBox>
|
</FloatingBox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
import React, { useEffect, useState }from 'react';
|
||||||
|
import styled, { keyframes } from 'styled-components';
|
||||||
|
import SVGIcon from "../../components/UI/Icon/SVGIcon";
|
||||||
|
import { User } from '../../API/User';
|
||||||
|
|
||||||
|
import { useFetching } from '../../hooks/useFetching';
|
||||||
|
|
||||||
|
import { Row, Col, Stack } from 'react-bootstrap';
|
||||||
|
|
||||||
|
const BackButton = styled.button`
|
||||||
|
border: 1px solid #c2c4c2;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 14px;
|
||||||
|
|
||||||
|
background: #ffffff;
|
||||||
|
color: gray;
|
||||||
|
box-shadow: 0 2px 1px #00000010;
|
||||||
|
|
||||||
|
font-size: 12pt;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
& ${SVGIcon} {
|
||||||
|
transform: translate(-4px, 2px)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = styled.div`
|
||||||
|
height: 100px;
|
||||||
|
padding-top: 32px;
|
||||||
|
z-index: 1;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
margin-left: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
|
transform: translateY(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
margin-left: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 19px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CabinetSection = styled.div`
|
||||||
|
width: 1270px;
|
||||||
|
height: 650px;
|
||||||
|
|
||||||
|
background: #FFFFFF;
|
||||||
|
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 20px;
|
||||||
|
margin: 27px auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CabinetContainer = styled(Row)`
|
||||||
|
padding: 30px 26px 30px 36px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Avatar = styled.img`
|
||||||
|
width: 256px;
|
||||||
|
height: 256px;
|
||||||
|
|
||||||
|
margin: 25px 0px 0px 65px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0px 0px 12px 2px rgba(0, 0, 0, 0.34);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const UploadPhoto = styled.button`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 192px;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
margin: 16px auto 0px;
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 19px;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
background: #007EFF;
|
||||||
|
border-radius: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const WelcomeText = styled.p`
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MainText = styled.p`
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InputBlock = styled.input`
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
width: ${props => props.width}px;
|
||||||
|
height: ${props => props.height}px;
|
||||||
|
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: #000000;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TextAreaBlock = styled.textarea`
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
margin: 16px 0px 0px 55px;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 18px;
|
||||||
|
color: #000000;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #CCCCCC;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CharacterTraitBlock = styled.div`
|
||||||
|
width: calc(100% + 15px);
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 28px;
|
||||||
|
display: inline-block;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
background: ${props => props.background};
|
||||||
|
border-radius: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonCircleChangeTrait = styled.button`
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
padding: 0px;
|
||||||
|
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 70%;
|
||||||
|
|
||||||
|
color: #FFFFFF;
|
||||||
|
background: #D9D9D9;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ButtonChangeTrait = styled.button`
|
||||||
|
margin-top: 14px;
|
||||||
|
|
||||||
|
width: auto;
|
||||||
|
height: 27px;
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 17px;
|
||||||
|
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
color: #007EFF;
|
||||||
|
background: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InformationBlock = (props) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row style={{marginTop: props.marginTop, marginLeft: 19}}>
|
||||||
|
<Col xs={5}>
|
||||||
|
<MainText>{props.title}</MainText>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<MainText>{props.text}</MainText>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const InformationBlockInput = (props) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row style={{marginTop: props.marginTop, marginLeft: 19}}>
|
||||||
|
<Col xs={5}>
|
||||||
|
<MainText>{props.title}</MainText>
|
||||||
|
</Col>
|
||||||
|
<Col xs={5}>
|
||||||
|
<InputBlock width={props.width} height={props.height} placeholder={props.placeholderInput} value={props.valueInput}/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TraitsBlock = (props) => {
|
||||||
|
const list = props.list;
|
||||||
|
|
||||||
|
if (list.length == 0) return (
|
||||||
|
<>
|
||||||
|
<h1>Вы ничего не указали</h1>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row style={{marginLeft: 19}}>
|
||||||
|
{
|
||||||
|
list.map((el, i) => {
|
||||||
|
return (
|
||||||
|
<Col style={{paddingLeft: 0, marginRight: 9, marginTop: 10}} key={i}>
|
||||||
|
<CharacterTraitBlock background={el.color}>
|
||||||
|
{el.text}
|
||||||
|
</CharacterTraitBlock>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{props.button
|
||||||
|
?
|
||||||
|
<>
|
||||||
|
<Col style={{paddingLeft: 0, marginRight: 9, marginTop: 10}}>
|
||||||
|
<ButtonCircleChangeTrait>
|
||||||
|
+
|
||||||
|
</ButtonCircleChangeTrait>
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<ButtonChangeTrait>Изменить..</ButtonChangeTrait>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const PersonalCabinet = function () {
|
||||||
|
|
||||||
|
const [user, setUser] = useState()
|
||||||
|
const userID = 1;
|
||||||
|
|
||||||
|
const [fetchUser, isUserLoading, userError] = useFetching(async (userID) => {
|
||||||
|
const response = await User.getById(userID);
|
||||||
|
setUser(response)
|
||||||
|
console.log(user.name)
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchUser(userID)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const TraitsListOne = [{text: 'Честность', color: '#3F51B5'}, {text: 'Аккуратность', color: '#03BCD6'}, {text: 'Музыкальность', color: '#E91D65'},
|
||||||
|
{text: 'Общительность', color: '#03A9F4'}, {text: 'Дружелюбность', color: '#8CC34D'}]
|
||||||
|
|
||||||
|
const TraitsListTwo = [{text: 'Честность', color: '#3F51B5'}, {text: 'Аккуратность', color: '#03BCD6'}, {text: 'Музыкальность', color: '#E91D65'},
|
||||||
|
{text: 'Общительность', color: '#03A9F4'}, {text: 'Дружелюбность', color: '#8CC34D'}, {text: 'Мудрость', color: '#FF5923'},
|
||||||
|
{text: 'Адекватность', color: '#2196F4'}, {text: 'Щедрость', color: '#FFC308'}, {text: 'Вежливость', color: '#9D28B2'}]
|
||||||
|
|
||||||
|
const TraitsListThree = [{text: 'Курит', color: '#94740B'}, {text: 'Равнодушие', color: '#D9B8B0'}, {text: 'Эгоист', color: '#6E3F58'}, {text: 'Лень', color: '#9F6844'},
|
||||||
|
{text: 'Лживость', color: '#AD9029'}, {text: 'Диструктивность', color: '#9A150C'}, {text: 'Токсичность', color: '#608426'}]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Title>
|
||||||
|
<BackButton>
|
||||||
|
<SVGIcon src='/images/icons/left-arrow-light.svg' width={10} height={16}/>
|
||||||
|
Вернуться назад
|
||||||
|
</BackButton>
|
||||||
|
<h2>Личный кабинет<span>/ Мои данные</span></h2>
|
||||||
|
</Title>
|
||||||
|
<CabinetSection>
|
||||||
|
<CabinetContainer>
|
||||||
|
<Col>
|
||||||
|
<Stack>
|
||||||
|
<WelcomeText>Добрый день, Александр!</WelcomeText>
|
||||||
|
<MainText>Ваш статус - Студент</MainText>
|
||||||
|
<Avatar src='/images/avatar-test.jpg'/>
|
||||||
|
<UploadPhoto>
|
||||||
|
<SVGIcon src='/images/icons/camera.svg' width={18} height={16}/>
|
||||||
|
Загрузить фото
|
||||||
|
</UploadPhoto>
|
||||||
|
<TextAreaBlock rows={3} cols={30} placeholder='Опиши себя парой предложений..'/>
|
||||||
|
<TextAreaBlock style={{marginTop: 9}} rows={3} cols={30} placeholder='Напишите сюда свои хобби..'/>
|
||||||
|
</Stack>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Stack style={{borderLeft: '1px solid #CCCCCC', height: '100%'}}>
|
||||||
|
<InformationBlock marginTop={13} title={'Имя'} text={'Александр'}/>
|
||||||
|
<InformationBlock marginTop={13} title={'Фамилия'} text={'Манаенков'}/>
|
||||||
|
<InformationBlock marginTop={13} title={'Отчество'} text={'Викторович'}/>
|
||||||
|
<InformationBlock marginTop={13} title={'Дата рождения'} text={'14.09.2004'}/>
|
||||||
|
<InformationBlock marginTop={33} title={'Факультет'} text={'Информационные системы (по отраслям)'}/>
|
||||||
|
<InformationBlock marginTop={44} title={'Группа'} text={'СО-ИС-20'}/>
|
||||||
|
<InformationBlockInput marginTop={33} title={'Телефон'} width={190} height={32} placeholderInput={'Введите номер телефона'}/>
|
||||||
|
<InformationBlockInput marginTop={10} title={'E-mail'} width={190} height={32} placeholderInput={'Введите E-mail'}/>
|
||||||
|
<InformationBlockInput marginTop={10} title={'Telegram'} width={190} height={32} placeholderInput={'Введите Ваш телеграмм'}/>
|
||||||
|
<InformationBlockInput marginTop={10} title={'Discord'} width={190} height={32} placeholderInput={'Введите Ваш дискорд'}/>
|
||||||
|
<InformationBlockInput marginTop={55} title={'Город'} width={190} height={32} placeholderInput={'Введите город'}/>
|
||||||
|
</Stack>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Stack style={{borderLeft: '1px solid #CCCCCC', height: '100%'}}>
|
||||||
|
<MainText style={{marginLeft: 19, marginTop: 13}}>Ваши личностные характеристики</MainText>
|
||||||
|
<TraitsBlock list={TraitsListOne} button={true}/>
|
||||||
|
<MainText style={{marginLeft: 19, marginTop: 25}}>Желаемые черты соседа</MainText>
|
||||||
|
<TraitsBlock list={TraitsListTwo}/>
|
||||||
|
<MainText style={{marginLeft: 19, marginTop: 19}}>Нежелаемые черты соседа</MainText>
|
||||||
|
<TraitsBlock list={TraitsListThree}/>
|
||||||
|
</Stack>
|
||||||
|
</Col>
|
||||||
|
</CabinetContainer>
|
||||||
|
</CabinetSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PersonalCabinet;
|
|
@ -6,6 +6,7 @@ import PsychTest from "../pages/PsychTest";
|
||||||
import Tinder from "../pages/Tinder";
|
import Tinder from "../pages/Tinder";
|
||||||
import LoginPage from "../pages/LoginPage";
|
import LoginPage from "../pages/LoginPage";
|
||||||
import LoggedIn from "../pages/LoggedIn";
|
import LoggedIn from "../pages/LoggedIn";
|
||||||
|
import PersonalCabinet from "../pages/PersonalCabinet";
|
||||||
|
|
||||||
// НА ПРОДАШКЕНЕ СДЕЛАТЬ ПРИВАТНЫЕ МАРШРУТЫ
|
// НА ПРОДАШКЕНЕ СДЕЛАТЬ ПРИВАТНЫЕ МАРШРУТЫ
|
||||||
// export const privateRoutes = [
|
// export const privateRoutes = [
|
||||||
|
@ -23,8 +24,9 @@ export default Object.freeze({
|
||||||
{ path: "/comparisons", component: <Comparisons />, exact: true },
|
{ path: "/comparisons", component: <Comparisons />, exact: true },
|
||||||
{ path: "/tinder", component: <Tinder />, exact: true },
|
{ path: "/tinder", component: <Tinder />, exact: true },
|
||||||
{ path: "/login", component: <LoginPage />, exact: true },
|
{ path: "/login", component: <LoginPage />, exact: true },
|
||||||
{ path: "/sign-in", component: <LoginPage />, exact: true },
|
{ path: "/sign-in", component: <LoggedIn />, exact: true },
|
||||||
{ path: "/psych_test", component: <PsychTest />, exact: true },
|
{ path: "/psych_test", component: <PsychTest />, exact: true },
|
||||||
|
{ path: "/personal_cabinet", component: <PersonalCabinet />, exact: true },
|
||||||
],
|
],
|
||||||
privateRoutes: [],
|
privateRoutes: [],
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue