From a235e3881a107b3b761f0f4250bab82daff61ca2 Mon Sep 17 00:00:00 2001 From: shidron Date: Tue, 9 Jun 2026 09:42:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9=20=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20"=D0=9D=D0=B0?= =?UTF-8?q?=D1=80=D1=83=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=D0=BC=20=D0=BD=D0=B5?= =?UTF-8?q?=D1=82"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Database/Функции/application_get.sql | 42 ++++ Database/Функции/application_patch.sql | 19 ++ Database/Функции/application_post.sql | 22 ++ Database/Функции/image_post.sql | 12 + Database/Функции/status_get.sql | 12 + Database/Функции/status_post.sql | 11 + Database/Функции/user_activate.sql | 16 ++ Database/Функции/user_get.sql | 32 +++ Database/Функции/user_post.sql | 35 +++ Project/nn_project/api/__init__.py | 0 .../api/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 262 bytes .../api/__pycache__/admin.cpython-314.pyc | Bin 0 -> 306 bytes .../api/__pycache__/apps.cpython-314.pyc | Bin 0 -> 556 bytes .../api/__pycache__/models.cpython-314.pyc | Bin 0 -> 303 bytes .../api/__pycache__/views.cpython-314.pyc | Bin 0 -> 12633 bytes Project/nn_project/api/admin.py | 3 + Project/nn_project/api/apps.py | 5 + Project/nn_project/api/migrations/__init__.py | 0 .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 273 bytes Project/nn_project/api/models.py | 3 + Project/nn_project/api/tests.py | 3 + Project/nn_project/api/views.py | 219 ++++++++++++++++++ Project/nn_project/manage.py | 22 ++ Project/nn_project/nn_project/__init__.py | 0 .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 269 bytes .../__pycache__/settings.cpython-314.pyc | Bin 0 -> 2641 bytes .../__pycache__/urls.cpython-314.pyc | Bin 0 -> 1392 bytes .../__pycache__/wsgi.cpython-314.pyc | Bin 0 -> 758 bytes Project/nn_project/nn_project/asgi.py | 16 ++ Project/nn_project/nn_project/settings.py | 121 ++++++++++ Project/nn_project/nn_project/urls.py | 26 +++ Project/nn_project/nn_project/wsgi.py | 16 ++ 32 files changed, 635 insertions(+) create mode 100644 Database/Функции/application_get.sql create mode 100644 Database/Функции/application_patch.sql create mode 100644 Database/Функции/application_post.sql create mode 100644 Database/Функции/image_post.sql create mode 100644 Database/Функции/status_get.sql create mode 100644 Database/Функции/status_post.sql create mode 100644 Database/Функции/user_activate.sql create mode 100644 Database/Функции/user_get.sql create mode 100644 Database/Функции/user_post.sql create mode 100644 Project/nn_project/api/__init__.py create mode 100644 Project/nn_project/api/__pycache__/__init__.cpython-314.pyc create mode 100644 Project/nn_project/api/__pycache__/admin.cpython-314.pyc create mode 100644 Project/nn_project/api/__pycache__/apps.cpython-314.pyc create mode 100644 Project/nn_project/api/__pycache__/models.cpython-314.pyc create mode 100644 Project/nn_project/api/__pycache__/views.cpython-314.pyc create mode 100644 Project/nn_project/api/admin.py create mode 100644 Project/nn_project/api/apps.py create mode 100644 Project/nn_project/api/migrations/__init__.py create mode 100644 Project/nn_project/api/migrations/__pycache__/__init__.cpython-314.pyc create mode 100644 Project/nn_project/api/models.py create mode 100644 Project/nn_project/api/tests.py create mode 100644 Project/nn_project/api/views.py create mode 100644 Project/nn_project/manage.py create mode 100644 Project/nn_project/nn_project/__init__.py create mode 100644 Project/nn_project/nn_project/__pycache__/__init__.cpython-314.pyc create mode 100644 Project/nn_project/nn_project/__pycache__/settings.cpython-314.pyc create mode 100644 Project/nn_project/nn_project/__pycache__/urls.cpython-314.pyc create mode 100644 Project/nn_project/nn_project/__pycache__/wsgi.cpython-314.pyc create mode 100644 Project/nn_project/nn_project/asgi.py create mode 100644 Project/nn_project/nn_project/settings.py create mode 100644 Project/nn_project/nn_project/urls.py create mode 100644 Project/nn_project/nn_project/wsgi.py diff --git a/Database/Функции/application_get.sql b/Database/Функции/application_get.sql new file mode 100644 index 0000000..7702b59 --- /dev/null +++ b/Database/Функции/application_get.sql @@ -0,0 +1,42 @@ +CREATE OR REPLACE FUNCTION nn_db.application_get( + in_pk_user nn_db.user.pk_user%TYPE) +RETURNS TABLE( + pk_application nn_db.application.pk_application%TYPE + , car_number nn_db.application.car_number%TYPE + , description nn_db.application.description%TYPE + , fk_status nn_db.application.fk_status%TYPE + , title nn_db.status.title%TYPE + , fk_user nn_db.application.fk_user%TYPE + , username nn_db.user.username%TYPE + , pk_image nn_db.image.pk_image%TYPE + , file_name nn_db.image.file_name%TYPE + , file_ext nn_db.image.file_ext%TYPE) +AS $$ +BEGIN + RETURN QUERY SELECT + nn_db.application.pk_application + , nn_db.application.car_number + , nn_db.application.description + , nn_db.application.fk_status + , nn_db.status.title + , nn_db.application.fk_user + , nn_db.user.username + , nn_db.image.pk_image + , nn_db.image.file_name + , nn_db.image.file_ext + FROM nn_db.application + INNER JOIN nn_db.status + ON nn_db.status.pk_status = nn_db.application.fk_status + INNER JOIN nn_db.user + ON nn_db.user.pk_user = nn_db.application.fk_user + LEFT JOIN nn_db.image + ON nn_db.image.fk_application = nn_db.application.pk_application + WHERE + CASE WHEN in_pk_user IS NULL + THEN nn_db.application.fk_user IN (SELECT nn_db.user.pk_user FROM nn_db.user) + ELSE + nn_db.application.fk_user = in_pk_user + END + ORDER BY nn_db.application.pk_application; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/application_patch.sql b/Database/Функции/application_patch.sql new file mode 100644 index 0000000..a31406d --- /dev/null +++ b/Database/Функции/application_patch.sql @@ -0,0 +1,19 @@ +CREATE OR REPLACE FUNCTION nn_db.application_patch( + in_pk_application nn_db.application.pk_application%TYPE + , in_fk_status nn_db.application.fk_status%TYPE) +RETURNS TABLE(confirm boolean) +AS $$ +BEGIN + UPDATE nn_db.application + SET nn_db.application.fk_status = in_fk_status + WHERE + nn_db.application.pk_application = in_pk_application; + + RETURN QUERY SELECT ( + CASE WHEN + (SELECT fk_status FROM nn_db.application WHERE pk_application = in_pk_application LIMIT 1) = in_fk_status + THEN True + ELSE False + END); +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/application_post.sql b/Database/Функции/application_post.sql new file mode 100644 index 0000000..6665b06 --- /dev/null +++ b/Database/Функции/application_post.sql @@ -0,0 +1,22 @@ +CREATE OR REPLACE FUNCTION nn_db.application_post( + in_car_number nn_db.application.car_number%TYPE + , in_description nn_db.application.description%TYPE + , in_fk_user nn_db.application.fk_user%TYPE) +RETURNS TABLE(pk_application nn_db.application.pk_application%TYPE) +AS $$ +BEGIN + INSERT INTO nn_db.application ( + car_number + , description + , fk_status + , fk_user) + VALUES ( + in_car_number + , in_description + , (SELECT MIN(nn_db.status.pk_status) FROM nn_db.status) + , in_fk_user); + RETURN QUERY SELECT + MAX(nn_db.application.pk_application) + FROM nn_db.application; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/Database/Функции/image_post.sql b/Database/Функции/image_post.sql new file mode 100644 index 0000000..7ea7752 --- /dev/null +++ b/Database/Функции/image_post.sql @@ -0,0 +1,12 @@ +CREATE OR REPLACE FUNCTION nn_db.image_post( + in_file_name nn_db.image.file_name%TYPE + , in_file_ext nn_db.image.file_ext%TYPE + , in_fk_application nn_db.image.fk_application%TYPE) +RETURNS TABLE(pk_image nn_db.image.pk_image%TYPE) +AS $$ +BEGIN + INSERT INTO nn_db.image (file_name, file_ext, fk_application) + VALUES (in_file_name, in_file_ext, in_fk_application); + RETURN QUERY SELECT MAX(nn_db.image.pk_image) FROM nn_db.image; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/status_get.sql b/Database/Функции/status_get.sql new file mode 100644 index 0000000..11e93f5 --- /dev/null +++ b/Database/Функции/status_get.sql @@ -0,0 +1,12 @@ +CREATE OR REPLACE FUNCTION nn_db.status_get() +RETURNS TABLE( + pk_status nn_db.status.pk_status%TYPE + , title nn_db.status.title%TYPE) +AS $$ +BEGIN + RETURN QUERY SELECT + nn_db.status.pk_status, nn_db.status.title + FROM nn_db.status + ORDER BY nn_db.status.pk_status; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/status_post.sql b/Database/Функции/status_post.sql new file mode 100644 index 0000000..9696c7d --- /dev/null +++ b/Database/Функции/status_post.sql @@ -0,0 +1,11 @@ +CREATE OR REPLACE FUNCTION nn_db.status_post( + in_title nn_db.status.title%TYPE) +RETURNS TABLE(pk_status nn_db.status.pk_status%TYPE) +AS $$ +BEGIN + INSERT INTO nn_db.status (title) + VALUES (in_title); + + RETURN QUERY SELECT MAX(nn_db.status.pk_status) FROM nn_db.status; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/user_activate.sql b/Database/Функции/user_activate.sql new file mode 100644 index 0000000..c240959 --- /dev/null +++ b/Database/Функции/user_activate.sql @@ -0,0 +1,16 @@ +CREATE OR REPLACE FUNCTION nn_db.user_activate( + in_pk_user nn_db.user.pk_user%TYPE) +RETURNS TABLE(confirm boolean) +AS $$ +BEGIN + UPDATE nn_db.user + SET is_active = True + WHERE + pk_user = in_pk_user; + RETURN QUERY SELECT + is_active = True + FROM nn_db.user + WHERE + nn_db.user.pk_user = in_pk_user; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/user_get.sql b/Database/Функции/user_get.sql new file mode 100644 index 0000000..f0b3cd2 --- /dev/null +++ b/Database/Функции/user_get.sql @@ -0,0 +1,32 @@ +CREATE OR REPLACE FUNCTION nn_db.user_get( + in_search_value nn_db.user.username%TYPE) +RETURNS TABLE( + pk_user nn_db.user.pk_user%TYPE + , lastname nn_db.user.lastname%TYPE + , firstname nn_db.user.firstname%TYPE + , middlename nn_db.user.middlename%TYPE + , username nn_db.user.username%TYPE + , password_hash nn_db.user.password_hash%TYPE + , email nn_db.user.email%TYPE + , phone nn_db.user.phone%TYPE + , is_admin nn_db.user.is_admin%TYPE + , is_active nn_db.user.is_active%TYPE) +AS $$ +BEGIN + RETURN QUERY SELECT + nn_db.user.pk_user + , nn_db.user.lastname + , nn_db.user.firstname + , nn_db.user.middlename + , nn_db.user.username + , nn_db.user.password_hash + , nn_db.user.email + , nn_db.user.phone + , nn_db.user.is_admin + , nn_db.user.is_active + FROM nn_db.user + WHERE + nn_db.user.username ILIKE in_search_value + OR nn_db.user.email ILIKE in_search_value; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Database/Функции/user_post.sql b/Database/Функции/user_post.sql new file mode 100644 index 0000000..43d24ac --- /dev/null +++ b/Database/Функции/user_post.sql @@ -0,0 +1,35 @@ +CREATE OR REPLACE FUNCTION nn_db.user_post( + in_lastname nn_db.user.lastname%TYPE + , in_firstname nn_db.user.firstname%TYPE + , in_middlename nn_db.user.middlename%TYPE + , in_username nn_db.user.username%TYPE + , in_password_hash nn_db.user.password_hash%TYPE + , in_email nn_db.user.email%TYPE + , in_phone nn_db.user.phone%TYPE) +RETURNS TABLE(pk_user nn_db.user.pk_user%TYPE) +AS $$ +BEGIN + INSERT INTO nn_db.user( + lastname + , firstname + , middlename + , username + , password_hash + , email + , phone + , is_admin + , is_active) + VALUES( + in_lastname + , in_firstname + , in_middlename + , in_username + , in_password_hash + , in_email + , in_phone + , False + , False); + + RETURN QUERY SELECT MAX(nn_db.user.pk_user) FROM nn_db.user; +END; +$$ LANGUAGE plpgsql \ No newline at end of file diff --git a/Project/nn_project/api/__init__.py b/Project/nn_project/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Project/nn_project/api/__pycache__/__init__.cpython-314.pyc b/Project/nn_project/api/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..879a5166ccaf467167ff627a5e2805268dd0e3f2 GIT binary patch literal 262 zcmdPq_I|p@<2{{|u7+vH(c9Sj8wB zC|sCxVZw#Q3KwQw*m1Gp!oCZeE;e4+6{C1z(S@xS8!tA&L@#W(u=m1_3tJU{#NLZ7 z7xn=a@42wyVjGaZ4=AxErFPF`MoK~a8IYH~?TVnJq1e0*kJW=VX!UP0w84x8Nkl+v73yCPPgb3wi; P1~EP{Gcqz3F#}lub2W41 literal 0 HcmV?d00001 diff --git a/Project/nn_project/api/__pycache__/admin.cpython-314.pyc b/Project/nn_project/api/__pycache__/admin.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6b7c08387fc74291f8e9547d0ecf1bf5d7ccae7 GIT binary patch literal 306 zcmdPqbO{QCXDOrhm>G^ue`FSNpnMp;=Km{uqK7-VKnF}ObtYQ=m6fVrUFyX>t zg$uJT?6}x)Vc&&K7aK3^ic!3<=)%^EjTf6>q8Bz?*n45eg{=xeV(-P43;TeI_gvU; zu?@)I2b9`-vHN1H0#NS`u&%8a_QYJ63zToT*nF|$!d9^6oj{ST7n@=(%m>@F^};S7 zCoeC)peR2pHMt}vu^=-B?0UU|%3B;Zx%nxjIjMF<96+}+0&y{j@qw9}5nOC>u?XVeBqE3%baBYwlzPP4mXw;r2|(L{L%_b*9$E8Yd2c?31G zOEkJgHYumGWQwGT7MmuTlB3H?l*eP5I!7|`Z%H#W7+iOp4cjW3rF@hSMcO5u83Z+w zBb;i4$97e&Xq2m3jK?(v>!`AT85IZ=!!e)8cBU^FV9YWO3XBCqj2+nf)r!Q!j2%{u zN>>?UtZ2F(ub5WBvQbv05KJ-Vaf6$AW^nGBdsSZW7<2ovkB)5W_Xb0DMNcoK;07A- zn1X9)iW;=xMV!E!o`whT#fdoWox(G;pb35oLra`P8;XtBBRWY62}k)1FO;Vt+`30axC5?h98Ae`bpBieUv5} KgMS3K(De_Fp1Z;T literal 0 HcmV?d00001 diff --git a/Project/nn_project/api/__pycache__/models.cpython-314.pyc b/Project/nn_project/api/__pycache__/models.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84cc166964fb7db48cecc7a215bf4dee1e2fba2b GIT binary patch literal 303 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 a(wtPgB37WwK^`myF+MRfGBOr116cs>7I=sN literal 0 HcmV?d00001 diff --git a/Project/nn_project/api/models.py b/Project/nn_project/api/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/Project/nn_project/api/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/Project/nn_project/api/tests.py b/Project/nn_project/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/Project/nn_project/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/Project/nn_project/api/views.py b/Project/nn_project/api/views.py new file mode 100644 index 0000000..2770b2b --- /dev/null +++ b/Project/nn_project/api/views.py @@ -0,0 +1,219 @@ +from django.shortcuts import render +from django.http import JsonResponse +from django.db import connection + +from django.middleware.csrf import get_token +from django.core.signing import TimestampSigner +from django.utils.encoding import force_str, force_bytes +from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode +from django.contrib.auth.hashers import PBKDF2PasswordHasher + +from django.views.decorators.csrf import csrf_exempt +import re + + +def call_func(name: str, parameters: dict): + with connection.cursor() as cursor: + if parameters: + this_params = [] + for param in list(parameters.values()): + if type(param) == str: + this_params.append(f"'{param}'") + elif param is None: + this_params.append("null") + else: + this_params.append(param) + + cursor.execute(f"SELECT * FROM {name}({', '.join(this_params)})") + else: + cursor.execute(f"SELECT * FROM {name}()") + columns = [col[0] for col in cursor.description] + return [dict(zip(columns, row)) for row in cursor.fetchall()] + +def api_get_xcsrf(request): + data = { 'X-CSFRToken': get_token(request) } + return JsonResponse(data, safe=False, status=200) + +def is_auth(request): + if not request.headers.get('Authorization'): + return False + token = request.headers.get('Authorization').split(' ')[-1] + signer = TimestampSigner(salt='django.core.signing') + try: + login = signer.unsign(force_str(urlsafe_base64_decode(token)), max_age=1000) + print(login) + except: + return False + else: + user = call_func('nn_db.user_get', {'search_value': login})[0] + return user + +def api_applications(request): + user = is_auth(request) + if not user: + return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403) + if request.method == 'GET': + params = {} + if user['is_admin']: + params['pk_user'] = None + else: + params['pk_user'] = str(user['pk_user']) + # params['pk_user'] = None + results = call_func('nn_db.application_get', params) + + applications = [] + keys = [] + for result in results: + if result['pk_application'] not in keys: + application = {} + application['pk_application'] = result['pk_application'] + application['car_number'] = result['car_number'] + application['description'] = result['description'] + application['fk_status'] = result['fk_status'] + application['title'] = result['title'] + application['fk_user'] = result['fk_user'] + application['username'] = result['username'] + application['images'] = [] + + if result['pk_image'] is not None: + image = {} + image['pk_image'] = result['pk_image'] + image['file_name'] = result['file_name'] + image['file_ext'] = result['file_ext'] + application['images'].append(image) + + applications.append(application) + keys.append(application['pk_application']) + else: + for application in applications: + if application['pk_application'] == result['pk_application']: + image = {} + image['pk_image'] = result['pk_image'] + image['file_name'] = result['file_name'] + image['file_ext'] = result['file_ext'] + application['images'].append(image) + return JsonResponse({"code": 200, "data": applications}, safe=False, status=200) + if request.method == 'POST': + params = {} + params['car_number'] = request.POST.get('car_number') + params['description'] = request.POST.get('description') + params['fk_user'] = user['pk_user'] + confirm = call_func('nn_db.application_post', params) + return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201) + +def api_users(request): + if is_auth(request): + return JsonResponse({ "code": 403, "message":"Доступ запрещен" }, safe=False, status=403) + if request.method == 'POST': + params = [] + + + lastname = request.POST.get('lastname') + firstname = request.POST.get('firstname') + middlename = request.POST.get('middlename') + username = request.POST.get('username') + password = request.POST.get('password') + email = request.POST.get('email') + phone = request.POST.get('phone') + + errors = [] + + if lastname is None: + errors.append({ "lastname": "Параметр обязателен для заполнения" }) + if firstname is None: + errors.append({ "firstname": "Параметр обязателен для заполнения" }) + if username is None: + errors.append({ "username": "Параметр обязателен для заполнения" }) + if password is None: + errors.append({ "password": "Параметр обязателен для заполнения" }) + if email is None: + errors.append({ "email": "Параметр обязателен для заполнения" }) + if phone is None: + errors.append({ "phone": "Параметр обязателен для заполнения" }) + + if errors: + return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409) + + if len(lastname) < 6: + errors.append({ "lastname": "Фамилия должна быть длинее 6 символов" }) + elif len(lastname) > 60: + errors.append({ "lastname": "Фамилия должна быть короче 60 символов" }) + if len(firstname) < 6: + errors.append({ "firstname": "Имя должна быть длинее 6 символов" }) + elif len(firstname) > 60: + errors.append({ "firstname": "Имя должна быть короче 60 символов" }) + if middlename is not None: + if len(middlename) != 0: + if len(middlename) < 6: + errors.append({ "middlename": "Отчество должна быть длинее 6 символов" }) + elif len(middlename) > 60: + errors.append({ "middlename": "Отчество должна быть короче 60 символов" }) + + if len(username) < 6: + errors.append({ "username": "Имя пользователя должно быть длинее 6 символов" }) + elif len(username) > 60: + errors.append({ "username": "Имя пользователя должно быть короче 60 символов" }) + if len(password) < 8: + errors.append({ "password": "Пароль должен быть длинее 8 символов" }) + + + if len(email) < 5: + errors.append({ "email": "Адрес электронной почты должен быть длинее 5 символов" }) + elif re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', email) is None: + errors.append({ "email": "Адрес электронной почты имеет некорректный формат" }) + if len(phone) < 11: + errors.append({ "phone": "Номер телефона должен быть длинее 10 символов" }) + elif len(phone) > 12: + errors.append({ "phone": "Номер телефона должен быть короче 12 символов" }) + elif re.match(r'^(?:\+79|89)\d{9}$', phone) is None: + errors.append({ "phone": "Номер телефона имеет некорректный формат" }) + + if errors: + return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409) + + + + user_username = call_func('nn_db.user_get', {"search_value": username}) + user_email = call_func('nn_db.user_get', {"search_value": email}) + if not(user_username) and not(user_email): + hasher = PBKDF2PasswordHasher() + password_hash = hasher.encode(password, salt='extra') + pk_user = call_func("nn_db.user_post", {"lastname": lastname, "firstname": firstname, "middlename": middlename, "username": username, "password_hash": password_hash, "email": email, "phone": phone})[0]['pk_user'] + user = call_func("nn_db.user_get", {"search_value": username})[0] + return JsonResponse(user, safe=False, status=201) + else: + return JsonResponse({"code": 409, "message":"Пользователь c таким именем или адресом электронной почты существует"}, safe=False, status=409) + +def api_auth(request): + if is_auth(request): + return JsonResponse({ "code": 403, "message":"Доступ запрещен" }, safe=False, status=403) + if request.method == 'POST': + response = {} + login = request.POST.get('login') + password = request.POST.get('password') + + errors = [] + + if len(login) < 6: + errors.append({ "login": "Имя пользователя должно быть длинее 6 символов" }) + elif len(login) > 60: + errors.append({ "login": "Имя пользователя должно быть короче 60 символов" }) + if len(password) < 8: + errors.append({ "password": "Пароль должен быть длинее 8 символов" }) + + if errors: + return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409) + + user = call_func('nn_db.user_get', {'search_value': login}) + if user: + user = user[0] + hasher = PBKDF2PasswordHasher() + if not hasher.verify(password, user['password_hash']): + return JsonResponse({ "code": 401, "message":"Неверное имя пользователя или пароль" }, safe=False, status=401) + response = user + signer = TimestampSigner(salt='django.core.signing') + token = urlsafe_base64_encode(force_bytes(signer.sign(user['username']))) + response['token'] = token + return JsonResponse(response, safe=False, status=200) + else: + return JsonResponse({ "code": 401, "message":"Неверное имя пользователя или пароль" }, safe=False, status=401) \ No newline at end of file diff --git a/Project/nn_project/manage.py b/Project/nn_project/manage.py new file mode 100644 index 0000000..1a56756 --- /dev/null +++ b/Project/nn_project/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'nn_project.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/Project/nn_project/nn_project/__init__.py b/Project/nn_project/nn_project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Project/nn_project/nn_project/__pycache__/__init__.cpython-314.pyc b/Project/nn_project/nn_project/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..322b2a4291bbeb37e05c216a8a2e7e1d006e4777 GIT binary patch literal 269 zcmdPqyztYQ=m z6fVrUFyX>tg$uJT?6}x)Vc&&K7aK3^ic!3<=)%^EjTf6>q8Bz?*n45eg{=xeV(-P4 z3;TeI_gvU;u?@)I2b9`-vHN1H0#NS`u&%8a_QYJ63zToT*nF|$!d9^6oj{ST7n@=( z%m>@F^};S7CoeC)peR2pHMs2KczG$)vkyY=xUJXib0G| M%#4hTMa)1J0NY-6(f|Me literal 0 HcmV?d00001 diff --git a/Project/nn_project/nn_project/__pycache__/settings.cpython-314.pyc b/Project/nn_project/nn_project/__pycache__/settings.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9476eac8959e14f8551ece9b53bf1e87c56d4dda GIT binary patch literal 2641 zcmb7GOOMl56tH>6ENc{r=+Fcht&Vxi@q|zukKKGpSo!7nhIQwJK z5CfkbcPFkrh%n5b?xOdJc9}Q-0MlR)Lzu4^#Aev@9^%=>T=h&dSG`Bsj#VMpgY}A~B}?eU=w(ncstK$oz+r+IHLYYNdO0UL?#T+~m+F-Y zU(qbW8%zC~RJ{y=ux9c%)Jh#&|0kA-mj(jOveZ{l$%=ip+W<|saH($MgVWbPE-N-Z zawwU+wOF627$=e+9$VVKe6lvPYTdBvt5tpR%*qjC&uZB|^U)1eT`St7{YDFQvtrS} zSuu4%%BAf@$Gt#swN`sI^)}E44R;2FiFztyc*3OS!pBPO^7)b15(Ov#1#s zSvIk?TA{w2I4{u=a9LiYz6DuAl^nH~z7BHrDmJy!yBb>_Ynp4giOX==E!`i1|(r2T;d1 zC;~3u^=oj$VQ>mxoHNxb&ez-tY4vflUVC2``F1G0d$k1ZV}fG z`h$It?Y$_4+8X6PsT$>a1)nw#K-TUb9cv5FmX~GOSDp~-`+JIEx4!Z-k@$V?-s$Jz z>1KHPkMPX%@V;hv-}7*?8BR91R*Z=uwh?OinP9Xr@-jZ&h`tVShxP%9W7@wdFusHYKZeZWj69VXt2 zFe9-ST-+H?y1;--LN59qAP_^GpgSS{k~!!GM2QH+D2yRsGzy*cvClAK7}qTXqsMu|>aN=+0dv~xe%vLw zTtFfh$L{^c=WRv-(Qst~q9vAxAwZE0Lrk3@J0#eRg}uSs09tq=P?V;9$; zu=Z5cf6IdK9;Px8M`fI+vkI~PPd1Y8_{@_*7VUpz)QE%rpdH3tXoAv{rO89i`_oX3 zL68a@kL2P-8+FF5tdH6?Iu1uRJWq1WT~bZ?&)Kf^N!Nu2=1MhbP;qOvZP|KO^4n1u z12AN>r9ri3Fj_Hsg74 zt2{5OjthbMIStpSd~;c4syYfUUCWBFn;H@w2P6;ZwQP1wT3)MWQpi)H*{tG%W>%TH zHa}^v_+ES`9wYHc?4_IHK>VC;iKndv@uL`~Tj_Rg6;H&z*b_qp zzy0)UaRB6J;z{}~tPjAopFT*xL9lmE?HY<_E%5{RH`A}u`(mgz{{og_y4@1HN@*yb z!ea8Cr=dJsC%Ko+UW-V3(8nLp6OgeEA8WzysHp4u%fg&){Dm}q;e~eYg?8qpsg=s2 zd~OFnzp#>CSUEJ_``xre}E9tZ&Z z4uk26O~6p+pbbiZf+wIP6a*@8A1OrcMrrgScr0u#Oer(JnV8`zDn(1N)yO#Fyl4un zPEz!2Nw^HsXZ8c}Q8{0L8o@QA4r|1L7%R3znn6PI;vyH)27+UbYMSa( z6G7F5Dzu2cZKCTYY9SN0RL2;luxcS4R;#LQn}()RgWzhF*IVa}EaD(&VBXUjB_P4P zxZ%Q@VWMl$MM!K=YP)N)tP{