From e0a52614c34c9fcd4a1a5432c747d4ba2c708c4c Mon Sep 17 00:00:00 2001 From: ayemunhossain Date: Tue, 15 Feb 2022 19:23:12 +0600 Subject: [PATCH] Initial phrase --- core/__init__.py | 4 + core/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 313 bytes core/__pycache__/celery.cpython-38.pyc | Bin 0 -> 997 bytes core/__pycache__/settings.cpython-38.pyc | Bin 0 -> 4343 bytes core/__pycache__/urls.cpython-38.pyc | Bin 0 -> 982 bytes core/__pycache__/wsgi.cpython-38.pyc | Bin 0 -> 560 bytes core/asgi.py | 16 ++ core/celery.py | 24 ++ core/settings.py | 214 ++++++++++++++++++ core/tasks.py | 11 + core/urls.py | 22 ++ core/wsgi.py | 16 ++ manage.py | 22 ++ user/__init__.py | 0 user/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 163 bytes user/__pycache__/admin.cpython-38.pyc | Bin 0 -> 287 bytes user/__pycache__/apiView.cpython-38.pyc | Bin 0 -> 2073 bytes user/__pycache__/apps.cpython-38.pyc | Bin 0 -> 436 bytes user/__pycache__/choices.cpython-38.pyc | Bin 0 -> 251 bytes user/__pycache__/models.cpython-38.pyc | Bin 0 -> 2860 bytes user/__pycache__/serializers.cpython-38.pyc | Bin 0 -> 2523 bytes user/__pycache__/urls.cpython-38.pyc | Bin 0 -> 638 bytes user/admin.py | 5 + user/apiView.py | 47 ++++ user/apps.py | 6 + user/choices.py | 2 + user/migrations/0001_initial.py | 37 +++ user/migrations/0002_auto_20220214_2342.py | 24 ++ user/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-38.pyc | Bin 0 -> 1327 bytes .../0002_auto_20220214_2342.cpython-38.pyc | Bin 0 -> 981 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 174 bytes user/models.py | 79 +++++++ user/serializers.py | 64 ++++++ user/tests.py | 3 + user/urls.py | 13 ++ user/views.py | 3 + 37 files changed, 612 insertions(+) create mode 100644 core/__init__.py create mode 100644 core/__pycache__/__init__.cpython-38.pyc create mode 100644 core/__pycache__/celery.cpython-38.pyc create mode 100644 core/__pycache__/settings.cpython-38.pyc create mode 100644 core/__pycache__/urls.cpython-38.pyc create mode 100644 core/__pycache__/wsgi.cpython-38.pyc create mode 100644 core/asgi.py create mode 100644 core/celery.py create mode 100644 core/settings.py create mode 100644 core/tasks.py create mode 100644 core/urls.py create mode 100644 core/wsgi.py create mode 100755 manage.py create mode 100644 user/__init__.py create mode 100644 user/__pycache__/__init__.cpython-38.pyc create mode 100644 user/__pycache__/admin.cpython-38.pyc create mode 100644 user/__pycache__/apiView.cpython-38.pyc create mode 100644 user/__pycache__/apps.cpython-38.pyc create mode 100644 user/__pycache__/choices.cpython-38.pyc create mode 100644 user/__pycache__/models.cpython-38.pyc create mode 100644 user/__pycache__/serializers.cpython-38.pyc create mode 100644 user/__pycache__/urls.cpython-38.pyc create mode 100644 user/admin.py create mode 100644 user/apiView.py create mode 100644 user/apps.py create mode 100644 user/choices.py create mode 100644 user/migrations/0001_initial.py create mode 100644 user/migrations/0002_auto_20220214_2342.py create mode 100644 user/migrations/__init__.py create mode 100644 user/migrations/__pycache__/0001_initial.cpython-38.pyc create mode 100644 user/migrations/__pycache__/0002_auto_20220214_2342.cpython-38.pyc create mode 100644 user/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 user/models.py create mode 100644 user/serializers.py create mode 100644 user/tests.py create mode 100644 user/urls.py create mode 100644 user/views.py diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e0b971e --- /dev/null +++ b/core/__init__.py @@ -0,0 +1,4 @@ +from __future__ import absolute_import, unicode_literals +from .celery import app as celery_app + +__all__ = ('celery_app',) \ No newline at end of file diff --git a/core/__pycache__/__init__.cpython-38.pyc b/core/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b58889c23418f6360ed95aa0872d429861ea4537 GIT binary patch literal 313 zcmYk0Jx;_h5QS~$XIBv`_6R9x(vS-vgjNxvq5#pj8F}MbFzdvY;}m-fuECYkQtbg~ zs2Gcek>;CuqbJRK*=#CC_5D%pPc*-h_+Ns=9rbiY&`fj71}^y^q~J^oo!M;2WkGu` zOIF8zaU?bNxQI`y3qEzf$-@7+5;f8TrWqS>@Hkl&x}z&AAQhYFsH3Bg;?>^m5& zVV!ER?&-Sn4QhA>yi2qco1R&mnY0{Nyh`DhX*ISyOV^08tlitVf~yaPS1+Olws7si zfSif#8{KQ-I%zIj{)V#4P29lcJNOGkTeyi^c;impZQi%>Cf>T$9#rn}i*2~m!EL;K zr{fOZxz(WB4mPG*zxxv~YkggApde#37sQX^lyNbJv&s>ngkOVaWkiT5S!Bu=QB2k> zAzp1VbWf-L#bmVZa)P65&v7UV5xr!Y*n9Qz^&8oB-wnnmXa00Jo1KnNrvCdg_hK|$ zH$%qB?o(B1s_=Mi53&e3?j>5Gbq6m|vS7O*OXhn|7rn~Sa5S8J02s9d#Ux8nNJ2&7 zx#$oafo2sjLR;!UR3G;Zt|ld0r{n2tFd7YAe{g<2{ibm>-opGjb}m^=971WtgyWKI zC0OblvIzQ+QY1)k&Unu)koDZjaqkd?E0SPG;djEKUAyVEwDu{A1B?!~p!6?LjW@u| z0Jj-L2@Vy+CsBS0w1C_JN5Bq!0@z=Suf|QWM?kRsMLU zDrbbw|C8Z$C?#!7f_&i%l&!iDXRyGUX7D!HeplMQKhH(ZiSKijkp|0T<&g(hm}LFg z7>fRRW_m5wysVN{l3YccC9;;&HZs`s6lv<@0_?F%>S~_^9t^Wne+5@Tp literal 0 HcmV?d00001 diff --git a/core/__pycache__/settings.cpython-38.pyc b/core/__pycache__/settings.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fdedd62fb0eae514105aef9fc3aae04f8ad0a81 GIT binary patch literal 4343 zcma(UTXWOs)pq1-2!xP?Ko$tg!eVITaM^I_UXX1$7LhNNWJ11&YGj`;juP22l1zx6 zI@9NU>mv`_$NnX~wlf{3kDb|>c6PcwpKK?#11-^5I^Vf{=R3FWq%k*_iQwPcKV_PK zSc*jc$`<2)S8(wHf9_AwNCYAfwIZD;iAEV5vtpeYG84ix7T1ZBcr-$zH1WsiPsu+= z4pIjxk_L`sAWml65poU^kc1SZAp^5;4(8xIT!4AF2n+2wG6$Dn5td*XF2f33X(!2f zxJoX-Dw&5bXd14qM%qz`!*#d;Yj6|3Y;$H5ZoTK)QF0N!A`5UE?jYaSs}Z>SK4wPA zCCI`(3R^99aaOETLV?_`8h1708h*D7~>7Ay@JH0Kcp7kbD7;$TfIO zuERID`zAbrr|=A(!*AhR`0jm*+@Le?J^b)v6u&P(_%RCk@F~#vK?aJ$CjugrU<=A1 z!AsZ%87gPws_!q8HK;)yeg}0k${unP6v*TEOHj!z(8yQNAh+>%2VUW+zlI%h7k^o_ z{T>w6NcQyy7Q1XT;Od6AkKeRsb|_HGGjNe|sppy9w(IBN!05K^b+hZzR^OrP&fVt2 zJI&rr^S-s&+281_n~#pN@80d*`u)at$F%cw{_agVzGUFz^gujld&gbH?d&usfE4s|_!&oMgm$aW5S1FiDxL)s| zt1Ki4R8DCAfayoGG$?bF;9O)GmC)#Xo~s7yB`r4(Xw2S z%nOBWv05ZtQBu?(CDiMB2qkNEO{!H@e<^h2k=r&|RiW#{xCTj}dq&^#PKptlr(Sg5 zSf)oG`58nG;cP5Oh}A8rDh6CtsEE!ib}GL-Wp$WLJ;QaeN?->pjg3G^DYt?!PXQTeJNv6}57BWo~a3|Id8C)x6jedHQViwbq=ggraO?zZ-6$8fQX z9cuJUo&`z`aoH4EE{Y|gA!~YFP*hRT3$mc9qDroh@Ipf?i&af32&^_w=vPPdx~Nnn zRUN0ToX`X;zA-tHt_xeDu1Z8CCrnydscD)lo~S;dDA*)KWz49>kz%z_7AwL);zN8e zeI55L*0W@99gr+Dc(WCk;eER;2^uCv3{6p4g31~C(Y zBx~Hd34VFfi=WQVVN=pWL1W@3(w)9VF&XE|fI%ama>IBo`a?A``$w&~&+m5hQ6r-aFtq1of$U;bSc;e>%z_q&p$KKidL9&Mf zxMen-7#2W~!dr~8QQVnf%h}VbV$b1123=?4tF8x0%r4t{OM?V(bxUFj@X(_)vX5WDFxIRq)@poJ$PCjK=Ol)YLH+!jo@ZjB|53$FffY-V?)D~uW4wn zSd@ezJS3pSL3)5^;KHcwpdGbgQl7_J)AW*p`HDtulZ-0iSwx-nmU(qY*xJGrRz*$2 z*s27%i?zb(I)iIT4LwMPaJ{BzB$HRLsukw4V5uO=qQX){W&L?HG>y=#3)D3{cU8mo zOqPe-nxJmuK8l1+B3OYdlUl|o7e*4Sd!SqX5^)}4UWBiImvj4ehvukdnYNcJQuola zd%3)g3FCz0>)M{}tQEtNb*-qB*7AdCHpecR+~}V^zIPm?zQqv%+u--CExSMC>s%C{ znfW9)!^OBnEc%J(pWMX>%D=b_7v;EEG!>1-PAI>`evSRY{mRXANrp@O2jRI`1hL$i z_s`rV)G&CsR5TSsIF53Rmy6Ay)>tf#(BBickUFpncj8VhJl&T;><{@;e@om9?P0s( P!=>5ITynHmBHZ!6jJ1Z1 literal 0 HcmV?d00001 diff --git a/core/__pycache__/urls.cpython-38.pyc b/core/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..565c52cb0e721a69225ed9550f737c3fa0f3fc2e GIT binary patch literal 982 zcma)5O>Yx15cO_0O}lA<8&}`bmC9O_TLcK&P*DkSDD;v`>Z~V;gFjZb7ih2j0sIdB z(q1|71GsQN*zQJ(kdSI=SN3T9=Dj!L%Y%a)`St7DaQ?W5(2sD~UVVRfd!O9F~KE02lOHLolQ$;6kpB!Baj zN{?m7`B|f?!0xVW1btfK*NkDRFqgG9ZjAB5Xo=cy@O zN28-o*Gfw`!iq|GF$()pk)H}`?UT6|#~t9&yz{+vf+u&3hN6LR6Oz>Chx@ts|F?I?rc8 literal 0 HcmV?d00001 diff --git a/core/__pycache__/wsgi.cpython-38.pyc b/core/__pycache__/wsgi.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f18f06dce538e38124dfb3e02d1dc49a574dcde GIT binary patch literal 560 zcmYjP!EO^V5cN7~6BdFvAkMxA9 zBR{~GaPpNCzrcyvjgT^u#~#mk-kTYpk47m$`S~@y_?!^(yEXQukH%|U{wXR+7DOhJ z>SU4N-qW=1EqV!oWYhn4Ap7#*J4LJag)9ywos;q5?XUFXd@^OFH7ixIm2L3Y0CSDq zKpC<$odyP!xIf#&}=Ij)wztQL9*piIHy%9g0g10 z#C#_RQMVAvAX?oym^GMOdA3p-9y1S+u0wGC1?SS1K9d_^D*K;PX=@IKKhK`=wY?6O zOK{rW)L;U?_Lbu4AM6F0jg$DWf>5+~#U73LD1ZBAHaRWk$7g5L*<@b4Kh4kI9p8@H z(lZYsC{y|IAP$NG@rxo->*IqRi>wQ3b2b!F;sDH6HP*xwA4*t>D;?t9J-8pFX-C+p zzd2H_frHk{2A+ez4A$`@i*+|nivZmS@~&Q%e>h=BqPzqnd5hXrm${obe7WoSEBq0} O4^j2+4Cy_3pZ){qGp(%v literal 0 HcmV?d00001 diff --git a/core/asgi.py b/core/asgi.py new file mode 100644 index 0000000..9fbf1e3 --- /dev/null +++ b/core/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for core project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_asgi_application() diff --git a/core/celery.py b/core/celery.py new file mode 100644 index 0000000..e14349e --- /dev/null +++ b/core/celery.py @@ -0,0 +1,24 @@ +from __future__ import absolute_import +import os +from celery import Celery +from django.conf import settings +from pytz import timezone + + +BASE_REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379') + + +os.environ.setdefault('DJANGO_SETTINGS_MODULE','core.settings') +app = Celery('core') +app.conf.enable_utc = False +app.conf.update(timezone='Asia/Dhaka') + +app.config_from_object('django.conf:settings', namespace='CELERY') +app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) +app.conf.broker_url = BASE_REDIS_URL + + + +@app.task(bind=True) +def debug_task(self): + print(f'Request: {self.request!r}') \ No newline at end of file diff --git a/core/settings.py b/core/settings.py new file mode 100644 index 0000000..e8e966a --- /dev/null +++ b/core/settings.py @@ -0,0 +1,214 @@ +from pathlib import Path +from datetime import timedelta +from django.conf import settings + + +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-r(b2&bp!i+l6mh0m-i3w)xxf#^0=yem80*#hk6w9ukbvw0jiqj' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'user.apps.UserConfig', + 'rest_framework.authtoken', + 'rest_framework', + 'drf_yasg', + 'rest_framework_swagger', + 'rest_framework_simplejwt', + 'rest_framework_simplejwt.token_blacklist', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'core.urls' +AUTH_USER_MODEL = "user.UserAccount" + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [BASE_DIR / 'templates'], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'core.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' +USE_I18N = True +USE_L10N = True +USE_TZ = True +TIME_ZONE = 'Asia/Dhaka' + + + +STATIC_ROOT = BASE_DIR / 'static' +MEDIA_ROOT = BASE_DIR / 'images' + +STATIC_URL = '/static/' +MEDIA_URL = '/media/' + +# Default primary key field type +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + + + +REST_FRAMEWORK = { + 'DEFAULT_PARSER_CLASSES': [ + 'rest_framework.parsers.JSONParser', + ], + + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ), + + 'DEFAULT_PERMISSION_CLASSES': ( + 'rest_framework.permissions.IsAuthenticatedOrReadOnly',), + + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', + + 'PAGE_SIZE': 10, + + 'DEFAULT_THROTTLE_CLASSES': ( + 'rest_framework.throttling.AnonRateThrottle', + 'rest_framework.throttling.UserRateThrottle', + ), + + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.BrowsableAPIRenderer', + ), + 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' + + # 'DEFAULT_THROTTLE_RATES': { + # 'anon': '1/minute', + # 'user': '20/minute', + # }, +} + + +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ROTATE_REFRESH_TOKENS': False, + 'BLACKLIST_AFTER_ROTATION': False, + 'UPDATE_LAST_LOGIN': False, + + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': settings.SECRET_KEY, + 'VERIFYING_KEY': None, + 'AUDIENCE': None, + 'ISSUER': None, + 'JWK_URL': None, + 'LEEWAY': 0, + + 'AUTH_HEADER_TYPES': ('Bearer', "JWT"), + 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', + 'USER_ID_FIELD': 'id', + 'USER_ID_CLAIM': 'user_id', + 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', + + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'TOKEN_TYPE_CLAIM': 'token_type', + + 'JTI_CLAIM': 'jti', + + 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', + 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), + 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), +} + + +SWAGGER_SETTINGS = { + 'SECURITY_DEFINITIONS': { + 'basic': { + 'type': 'basic' + } + }, +} + +REDOC_SETTINGS = { + 'LAZY_RENDERING': False, +} + + + + +REDIS_HOST = "127.0.0.1" +REDIS_PORT = 6379 +BROKER_URL = 'redis://localhost:6379' +CELERY_RESULT_BACKEND = 'redis://localhost:6379' +CELERY_ACCEPT_CONTENT = ['application/json'] +CELERY_TASK_SERIALIZER = 'json' +CELERY_RESULT_SERIALIZER = 'json' +CELERY_TIMEZONE = 'Asia/Dhaka' \ No newline at end of file diff --git a/core/tasks.py b/core/tasks.py new file mode 100644 index 0000000..ffb8756 --- /dev/null +++ b/core/tasks.py @@ -0,0 +1,11 @@ +from celery import shared_task + +from celery.utils.log import get_task_logger +from time import sleep + +logger = get_task_logger(__name__) + +@shared_task(name='my_first_task') +def my_first_task(duration): + sleep(duration) + return('first_task_done') \ No newline at end of file diff --git a/core/urls.py b/core/urls.py new file mode 100644 index 0000000..b2f9a80 --- /dev/null +++ b/core/urls.py @@ -0,0 +1,22 @@ +"""core URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('user.urls')), +] diff --git a/core/wsgi.py b/core/wsgi.py new file mode 100644 index 0000000..135b7a6 --- /dev/null +++ b/core/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for core project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..f2a662c --- /dev/null +++ b/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', 'core.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/user/__init__.py b/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user/__pycache__/__init__.cpython-38.pyc b/user/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b53e5c4031ea85426c60bb1a058e67fe47398e7 GIT binary patch literal 163 zcmWIL<>g`k0BoO@=L?8o3AjbiSi&=m~3PUi1CZpd gA0MBYmst`YuUAlci^C>2KczG$)edCCXCP((0E`GGs{jB1 literal 0 HcmV?d00001 diff --git a/user/__pycache__/admin.cpython-38.pyc b/user/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10ff229598923c06d330a37453ccf39fbfcf98a0 GIT binary patch literal 287 zcmYjLJ5s|i5S45>1d?zDm$->8z%Y<8loT*&+&R`Jk)w}Ck_(Q(4WQ&oYN>JsDpsap zXZG#9{k^wct*!`0|GwA>tlx?Jj~vN8roTZDL{LREZ73xo6S>NC-Y`ml_Ql5x*k|gq zS5g&c+$I%`MJu#4XZpt$TMzD`>+ER4b9K>Ni@r4nyY8$BPVQWWYjLKn0OcJ^gr$qf zybKWM4i3@>a9_k-;&6hWM}D*#I4C7;;5GPDumj&%slW}Y4N&f_E9*(9tY4nWP1~Ko b2tMLxKG|CjZ*hK$vZI2#WYs6_C0*vfuk=c1 literal 0 HcmV?d00001 diff --git a/user/__pycache__/apiView.cpython-38.pyc b/user/__pycache__/apiView.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a4eb94a2ae2adae00d7a8968f047b5ac9adc49a GIT binary patch literal 2073 zcma)7&2k$>5T4n8tt49^sstB55e7oU!7?dM6h&E%O`xDmkYy6~vTQVCXXRb3GPAZ5 zm6S_-^@$fC-24>0$6Pt(6*$p7D@9Tgsu=7)Cn9twh}k>0uRPEbCOc(2Yy-(%9PwC z%w^s=VIFs$n?VKq67#|LwO<9l%qrklw76__t+N$N+tlpsGeJ}!h;pmf%vcFPh%e-n>f#$2jC5i2Lw(r`=#NC zk?BfU(KPoisj^s7_rVEGhk(I;ZtP|Gntp+NLdF%=!)S{ zgL>4W4y_wCx^9fFEMmNY>-U;e;GSkld0{w+QXYoN55qKLCkc)#VfgqYN{WauV5#!j zNs^tl2T#>IbA}h0=2DbkiC9JRAsYO~1@i&ANFxurgd!%Mexjc1fxFc3x-ICk0cOtP zz~aCrV6w|7GtLQc*aR+Es-nwfT2npTJ!?n}1YEQCa~LeK*BBdp1MA^}Qj(kC(cJ3W z%(_61*?Xb_JGxC8tcoa>JmfQWh!qGlo#K0uWuk^=POb8kTASfdKQv8o4Wrl5tf84F zN9P3-b_2T33jjzfbaZ9$lctcB1g;VdG#{bCWEOA0Zx-ulK1TDO5PSmB_n=Q9u&8GU zoO=TTOxzYO#G~dA6hiV!M4YLJ)VC90X95bPWvtCuJUG$>ThbE5{gsyd2F~afCIW(c;-RqsWkBr!-EcrR0?-Xx>jp;MH*~{(X3unk!7k7VRTYK6hB+#1 zft;tMLEw8->EZ0{bNs}o5D{0=U{L|ma|GYT+0VczyEqZ^`8G&z!MsGG5U58V=r!-V(G@(rCBS>(4oS43GbR!ar)KvPe@a3O!XIIu31 Y_jMsh-{8NZ&SVWr1AjHMcD3gJ3lY@FWdHyG literal 0 HcmV?d00001 diff --git a/user/__pycache__/apps.cpython-38.pyc b/user/__pycache__/apps.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36914f0b4fc78ddad2378ec0788acfdb6513dae2 GIT binary patch literal 436 zcmYjNy-ve05Vqq~DrvhQ#D_i+YfxdopUNaI&)MTA^-uZNJ%qF$sGX?*gF9x=^M*D z@E=Gt`au?nXm_?S=2lmEvERAKsZez0sP+Oul8geuYML?dG9MUr@w;GSWj)ltUcr;7 z_Bw`bTxyWY#>-;AXdAsMq=L?Gk-CmX-I()Al#=r<;2fE)a`u$-=T@j=<{V@$TGeoY z^*kRMxZD@1-5q-PO_N7m%0wzv=q5>}Jv7=ROI;{g8zmYU=DH5keylLPUxiDtJID$W zH%cOmv9Won8TND6vku18r+s7{v2JqQ$T4free1aY0}R##W8L_A*yP4t%{uOs{Q&_D BYf%6I literal 0 HcmV?d00001 diff --git a/user/__pycache__/choices.cpython-38.pyc b/user/__pycache__/choices.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cf9248cccf43e92eaea0b20964fc37772f2f325 GIT binary patch literal 251 zcmYk0L2kk@5JjEf1cU^Oo*>(7>I+m=i&AJeP=&&xo65vPaN{tMZG^Z6Yc9|$dCM)j zDfX^knm79Tny*=nMnmE?I}bNUzpugkSK`f+A9Lp+h=@cidV*f)ClSe#1x%f$8jBs9pj4nc#Vur8)LHS(_cdlkIn5@#YG5ze!GLDf4rK-AzB{?WLyO{| P;4wU5LN|?K693%*Y_vb| literal 0 HcmV?d00001 diff --git a/user/__pycache__/models.cpython-38.pyc b/user/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a2f010af29aef73da36dbba4752e32a25eb330f GIT binary patch literal 2860 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 Re@VDjRBhpY`Z?#${s*MQRK5TJ literal 0 HcmV?d00001 diff --git a/user/__pycache__/urls.cpython-38.pyc b/user/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a297a51dd2231e52b277858b1a4561d682dde937 GIT binary patch literal 638 zcmYk3!H&}~5Qgn0Y0|V=3M^ODYY)^ffDl_%D&ZqflEKN0?4iG!E!wVz!OL{Lc-<&+XJ z5P=-1fCnn%p^A9qazuo3sA3+ggeQ~$6%hpAM&F4Tiug8goIv7v)N=}v=W)*y2t8-W zWyn);_KwUS+_~GyoOWSTw7WZs5OkB*x~%Gpt5+W?IChgYl$CA4Tw5^R`BD}i_tGWa z>pj#LLR57*A9RmBzTLD%RbLgA@kggVxrVI)yTi;WEt-l+lxIBk-~8ZY(Lr4(I6cDu zEh;nf_ZZ`u=e+0BlU_m}TDG}2!@AMG{VBr=)4OK;;r%wR;pne>Gaq+jaZ}W#UL1_H z#!ZOu*d0TYCUj&X3`5L6b?)1IYn;EMHv8OGs*!MWY!{zgwKXwT;yk;!usPiHr+i3+1q^xwyR$%w7ZrD;+5=5}J4i-Uw0I53OC6M9ak!zuO8??2TMy<`9Y literal 0 HcmV?d00001 diff --git a/user/admin.py b/user/admin.py new file mode 100644 index 0000000..5daa41c --- /dev/null +++ b/user/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import UserAccount as User + + +admin.site.register(User) \ No newline at end of file diff --git a/user/apiView.py b/user/apiView.py new file mode 100644 index 0000000..9690b8d --- /dev/null +++ b/user/apiView.py @@ -0,0 +1,47 @@ +from rest_framework import permissions, status +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework_simplejwt.tokens import RefreshToken +from .serializers import LoginSerializer, RegisterUserSerializer + + +class RegisterUser(APIView): + permission_classes = [permissions.AllowAny] + serializer_class = RegisterUserSerializer + def post(self,request,format="json"): + serializer = self.serializer_class(data=request.data) + + if serializer.is_valid(): + try: + newUser = serializer.save() + if newUser: + response = serializer.data + return Response(response, status=status.HTTP_201_CREATED) + + except Exception as e: + return Response(status=status.HTTP_400_BAD_REQUEST) + + return Response(serializer.errors or None, status=status.HTTP_400_BAD_REQUEST) + + +class LoginAPIView(APIView): + serializer_class = LoginSerializer + permission_classes = [permissions.AllowAny] + + def post(self, request): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + +class BlacklistTokenAdding(APIView): + permission_classes = [permissions.AllowAny] + + def post(self, request, format='json'): + try: + refresh_token = request.data["refresh_token"] + token = RefreshToken(refresh_token) + token.blacklist() + return Response(status=status.HTTP_200_OK) + except Exception as e: + return Response(status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/user/apps.py b/user/apps.py new file mode 100644 index 0000000..36cce4c --- /dev/null +++ b/user/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UserConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'user' diff --git a/user/choices.py b/user/choices.py new file mode 100644 index 0000000..c6f3ace --- /dev/null +++ b/user/choices.py @@ -0,0 +1,2 @@ +AUTH_PROVIDERS = {'facebook': 'facebook', 'google': 'google', + 'twitter': 'twitter', 'email': 'email'} \ No newline at end of file diff --git a/user/migrations/0001_initial.py b/user/migrations/0001_initial.py new file mode 100644 index 0000000..58eb1ee --- /dev/null +++ b/user/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.8 on 2022-02-14 17:40 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='UserAccount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), + ('email', models.EmailField(max_length=60, unique=True, verbose_name='email')), + ('username', models.CharField(max_length=30, unique=True)), + ('first_name', models.CharField(blank=True, max_length=50, null=True)), + ('last_name', models.CharField(blank=True, max_length=50, null=True)), + ('date_joined', models.DateTimeField(auto_now_add=True, verbose_name='date joined')), + ('last_login', models.DateTimeField(auto_now=True, verbose_name='last login')), + ('auth_provider', models.CharField(default='email', max_length=255)), + ('is_verified', models.BooleanField(default=False)), + ('is_superuser', models.BooleanField(default=False)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/user/migrations/0002_auto_20220214_2342.py b/user/migrations/0002_auto_20220214_2342.py new file mode 100644 index 0000000..46cd3a2 --- /dev/null +++ b/user/migrations/0002_auto_20220214_2342.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.8 on 2022-02-14 17:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ('user', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='useraccount', + name='groups', + field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + ), + migrations.AddField( + model_name='useraccount', + name='user_permissions', + field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + ), + ] diff --git a/user/migrations/__init__.py b/user/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/user/migrations/__pycache__/0001_initial.cpython-38.pyc b/user/migrations/__pycache__/0001_initial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f0aa54a46298a9bb5a9d25a73f1fd17e1422eb7 GIT binary patch literal 1327 zcmZWp&2QT_6c;JkmSx#-(yZIMt@FO&OPmcmY(O!zu9u;Q_OLb>po>8J(GDF-R4FN4 zaw^bWecL~=e~H(feA!{gU9v~YMKX+n==XaceDD39sndQx@^Ib#J(^G3p7*B(w^xAR zA-?4iZr}ltUPcm<(S#C7Cl7oGe)AyUWF5TmlK|**Z`|5qj5nqx%EU^uN~pXv z?Mwle<+Fvg-ClPPJjAyg63^paLcmKXki=hkNx)m{Hc;?+XB`5;Cx8~TH^GuZ2X$Qt zp#$9wMI0fHcyAq}4}spsM_r7%KJ*dCFmReZ7d1r9zKc3=n7|#>96FD8{-?Q%ngQD1 zh4);{`#XFOK5+O$7ZJlpaNpsN5$|CYqYc3tKKT#&)FHC7eRkWn8o=ie2m)Vh{1r)t z@FjBjmFYfxOX3UDJ1x0>v{grRidX zU{YLh6JKyWS0zt#mT?mntSnzC4cphqzMB^NHI+PHRcE?~DihSTfU$2nz?ZC+l?geB zip?cA?K&4PYi?RRV?u5p*b{s`z#n~JHy0n)nA_&s-nVvoV>i)K=(2K!Y`<~ArZtx= zKX*PR$ZIK0SF)Xt=>c|@u9e6+Tsbgm0M^|!pQ~3X186cwE>fzM$gi9{axyxHjwy6* z9Qz2*(n6~X0bFmdn>vTK1>212i84jj#8Pk=6KxG9MqODKT-)|c7ZF=j;sP19hKenh zm}Op88r8^N;Mi{C2@*=vQH8q?&&K;EOdSDfYN9m7>rqRNa-60wYbI?JqK?Q#h4V8p z@Pg;S^M&B0rmnchZs1St#nQejZ7Noe@UL)UlbP=0Gp2Xq>FJZ16ZM{TY}wI%YoZ_R z_!^h~3@LsgGTy*=s+8m`Z&0ML-{vO&;py-22)F(7%j8UDe8Qy^s+!Drd0wevGF3uy zT}V7|v{ZUDTeEznMzfz!MpL#p$2KOGyUESxVRC$Y{B^qP^{BYi_WRSpw>-uzqyY_r zfKYl!hUAbO;C|rtAxeYh`J3)jk6zQif@>E&ZkaB)#78i9ggK5}|IXp2jYj$&Gnw!~ PGl(B7pQWv0NJ9T#`FVFB literal 0 HcmV?d00001 diff --git a/user/migrations/__pycache__/0002_auto_20220214_2342.cpython-38.pyc b/user/migrations/__pycache__/0002_auto_20220214_2342.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b9e7315dd016b99cd578d59ff48bfd9fc73e517 GIT binary patch literal 981 zcmY*Y&2AGh5Z=FRlaSJaI09M;fs{+qh8_`WtExS_%h{fLm7DG6TzsAFuC8t5R@f%}YyY1eVa+Ozq39t?dD!!hj;RMlt3}8&c8&NI@@&fO2A{F5G!-XA9#A zFS+4V*ayx=^IX=YvFSv~y3%KJdNze@p=4&tb>^b75eQjMbY^5P+gq4Eb0M+`n2h4P z0#$IW9Z&c%Ee$Y?&4A|9OpY-TaCMutXPX|X2BX%Hv3c9I(mQhXyQyuA1TD{PYYL(w z1}=^5C=e7A`nK%jrFKz+x?0#GBW>pssFBp*E!`Mz&fV6+pEyEnW6VJ?yXmX10G_`% ztsv)Pp8wazSgLyhdh-pB^4(pZbDwARi<554Hi@rDh;0*s2xg`k0BoO@=L?8o3AjbiSi&=m~3PUi1CZpd rpPQLplvt9PpI59OAD@|*SrQ+wS5SG2!zMRBr8Fni4rJM9AZ7pnb<`^& literal 0 HcmV?d00001 diff --git a/user/models.py b/user/models.py new file mode 100644 index 0000000..8f6ddd2 --- /dev/null +++ b/user/models.py @@ -0,0 +1,79 @@ +from statistics import mode +from rest_framework_simplejwt.tokens import RefreshToken +from django.db import models +import uuid +from .choices import AUTH_PROVIDERS +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin +import random + + +sr = random.SystemRandom() + + + +class UserManager(BaseUserManager): + def create_user(self, username, email, first_name=None, last_name=None, password=None): + if not email: + raise ValueError("User must have email") + if not username: + raise ValueError("User must have username") + + user_obj = self.model( + email=self.normalize_email(email), + username=username, + first_name=first_name, + last_name=last_name, + password=password, + ) + + user_obj.set_password(password) + user_obj.save(using=self._db) + return user_obj + + def create_superuser(self, username, email, password): + user = self.create_user( + email=self.normalize_email(email), + password=password, + username=username, + ) + user.is_staff = True + user.is_superuser = True + user.save(using=self._db) + return user + + + +class UserAccount(AbstractBaseUser, PermissionsMixin): + uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) + email = models.EmailField(verbose_name='email', unique=True, max_length=60) + username = models.CharField(max_length=30, unique=True) + first_name = models.CharField(max_length=50, blank=True, null=True) + last_name = models.CharField(max_length=50, blank=True, null=True) + date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True) + last_login = models.DateTimeField(verbose_name='last login', auto_now=True) + auth_provider = models.CharField(max_length=255, blank=False, null=False, default=AUTH_PROVIDERS.get('email')) + + is_verified = models.BooleanField(default=False) + is_superuser = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['username', ] + objects = UserManager() + + def __str__(self): + return self.username + + def has_perm(self, perm, obj=None): + return self.is_superuser + + def has_module_perms(self, app_lebel): + return True + + def tokens(self): + refresh = RefreshToken.for_user(self) + return { + 'refresh': str(refresh), + 'access': str(refresh.access_token) + } \ No newline at end of file diff --git a/user/serializers.py b/user/serializers.py new file mode 100644 index 0000000..46511f9 --- /dev/null +++ b/user/serializers.py @@ -0,0 +1,64 @@ +from rest_framework import serializers +from django.contrib import auth +from rest_framework.exceptions import AuthenticationFailed +from user.models import UserAccount as User + + +class RegisterUserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ('email', 'username', 'password') + extra_kwargs = {'password': {'write_only': True}} + + def create(self, validated_data): + password = validated_data.pop('password', None) + instance = self.Meta.model(**validated_data) + + if password is not None: + instance.set_password(password) + instance.save() + return instance + + +class LoginSerializer(serializers.ModelSerializer): + email = serializers.EmailField(max_length=255, min_length=3) + password = serializers.CharField(write_only=True) + username = serializers.CharField(max_length=255, min_length=3, read_only=True) + + tokens = serializers.SerializerMethodField() + + def get_tokens(self, obj): + user = User.objects.get(email=obj['email']) + + return { + 'refresh': user.tokens()['refresh'], + 'access': user.tokens()['access'] + } + + class Meta: + model = User + fields = ['email', 'password', 'username', 'tokens'] + + def validate(self, attrs): + email = attrs.get('email', '') + password = attrs.get('password', '') + + filtered_user_by_email = User.objects.filter(email=email) + user = auth.authenticate(email=email, password=password) + + if filtered_user_by_email.exists() and filtered_user_by_email[0].auth_provider != 'email': + raise AuthenticationFailed( + detail='Please continue your login using ' + filtered_user_by_email[0].auth_provider) + + if not user: + raise AuthenticationFailed('Invalid credentials, try again') + if not user.is_active: + raise AuthenticationFailed('Account disabled, contact admin') + if not user.is_verified: + raise AuthenticationFailed('Email is not verified') + + return { + 'email': user.email, + 'username': user.username, + 'tokens': user.tokens + } diff --git a/user/tests.py b/user/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/user/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/user/urls.py b/user/urls.py new file mode 100644 index 0000000..3b735db --- /dev/null +++ b/user/urls.py @@ -0,0 +1,13 @@ +from django.urls import path +from .apiView import LoginAPIView, RegisterUser, BlacklistTokenAdding +from rest_framework_simplejwt.views import ( + TokenObtainPairView, + TokenRefreshView, +) + +urlpatterns = [ + path('api/login/', LoginAPIView.as_view(), name='login_user'), + path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), + path('api/logout/blacklist/', BlacklistTokenAdding.as_view(), name='blacklist'), + path('api/register/', RegisterUser.as_view(), name="register_new_user"), +] \ No newline at end of file diff --git a/user/views.py b/user/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/user/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here.