From 827c56871b7b11633b42f5995421a8df190950a1 Mon Sep 17 00:00:00 2001 From: Alexandr Karpov Date: Fri, 21 Oct 2022 20:25:30 +0300 Subject: [PATCH] initial commit --- .editorconfig | 27 +++++ .gitignore | 133 +++++++++++++++++++++ app/__init__.py | 0 app/conf/__init__.py | 0 app/conf/api.py | 1 + app/conf/asgi.py | 16 +++ app/conf/celery.py | 17 +++ app/conf/settings/__init__.py | 1 + app/conf/settings/base.py | 217 ++++++++++++++++++++++++++++++++++ app/conf/settings/local.py | 52 ++++++++ app/conf/urls.py | 40 +++++++ app/conf/wsgi.py | 16 +++ manage.py | 22 ++++ requirements/base.txt | 11 ++ 14 files changed, 553 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 app/__init__.py create mode 100644 app/conf/__init__.py create mode 100644 app/conf/api.py create mode 100644 app/conf/asgi.py create mode 100644 app/conf/celery.py create mode 100644 app/conf/settings/__init__.py create mode 100644 app/conf/settings/base.py create mode 100644 app/conf/settings/local.py create mode 100644 app/conf/urls.py create mode 100644 app/conf/wsgi.py create mode 100755 manage.py create mode 100644 requirements/base.txt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c27916e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,rst,ini}] +indent_style = space +indent_size = 4 + +[*.{html,css,scss,json,yml,xml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +[nginx.conf] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4de1b5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,133 @@ +app/media/ +app/static/ +.idea/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/conf/__init__.py b/app/conf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/conf/api.py b/app/conf/api.py new file mode 100644 index 0000000..637600f --- /dev/null +++ b/app/conf/api.py @@ -0,0 +1 @@ +urlpatterns = [] diff --git a/app/conf/asgi.py b/app/conf/asgi.py new file mode 100644 index 0000000..444f807 --- /dev/null +++ b/app/conf/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for conf 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/4.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conf.settings") + +application = get_asgi_application() diff --git a/app/conf/celery.py b/app/conf/celery.py new file mode 100644 index 0000000..ef00351 --- /dev/null +++ b/app/conf/celery.py @@ -0,0 +1,17 @@ +import os + +from celery import Celery + +# Set the default Django settings module for the 'celery' program. +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conf.settings") + +app = Celery("conf") + +# Using a string here means the worker doesn't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object("django.conf:settings", namespace="CELERY") + +# Load task modules from all registered Django apps. +app.autodiscover_tasks() diff --git a/app/conf/settings/__init__.py b/app/conf/settings/__init__.py new file mode 100644 index 0000000..8f607e4 --- /dev/null +++ b/app/conf/settings/__init__.py @@ -0,0 +1 @@ +from .local import * diff --git a/app/conf/settings/base.py b/app/conf/settings/base.py new file mode 100644 index 0000000..c6f31d0 --- /dev/null +++ b/app/conf/settings/base.py @@ -0,0 +1,217 @@ +from pathlib import Path + +import environ + +APPS_DIR = Path(__file__).resolve(strict=True).parent.parent.parent +ROOT_DIR = APPS_DIR.parent +env = environ.Env() + +READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=True) +if READ_DOT_ENV_FILE: + # OS environment variables take precedence over variables from .env + env.read_env(str(ROOT_DIR / ".env")) + +# GENERAL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +DEBUG = env.bool("DJANGO_DEBUG", False) +# Local time zone. Choices are +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# though not all of them may be available with every OS. +# In Windows, this must be set to your system time zone. +TIME_ZONE = "Europe/Moscow" +# https://docs.djangoproject.com/en/dev/ref/settings/#language-code +LANGUAGE_CODE = "en-us" +# https://docs.djangoproject.com/en/dev/ref/settings/#site-id +SITE_ID = 1 +# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n +USE_I18N = True +# https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n +USE_L10N = True +# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz +USE_TZ = True +# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths +LOCALE_PATHS = [str(ROOT_DIR / "locale")] + +# DATABASES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#databases +DATABASES = {"default": env.db("DATABASE_URL")} +DATABASES["default"]["ATOMIC_REQUESTS"] = True +# https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-DEFAULT_AUTO_FIELD +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +# URLS +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf +ROOT_URLCONF = "app.conf.urls" +# https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application +WSGI_APPLICATION = "conf.wsgi.application" + +# APPS +# ------------------------------------------------------------------------------ +DJANGO_APPS = [ + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.sites", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.humanize", + "django.contrib.admin", + "django.forms", +] +THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "drf_yasg"] + +LOCAL_APPS = [] +# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps +INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS + +# AUTHENTICATION +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends +AUTHENTICATION_BACKENDS = [ + "django.contrib.auth.backends.ModelBackend", +] + +# PASSWORDS +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/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"}, +] + +# MIDDLEWARE +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#middleware +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "corsheaders.middleware.CorsMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.common.BrokenLinkEmailsMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +# TEMPLATES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#templates +TEMPLATES = [ + { + # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND + "BACKEND": "django.template.backends.django.DjangoTemplates", + # https://docs.djangoproject.com/en/dev/ref/settings/#dirs + "DIRS": [str(APPS_DIR / "templates")], + # https://docs.djangoproject.com/en/dev/ref/settings/#app-dirs + "APP_DIRS": True, + "OPTIONS": { + # https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.contrib.messages.context_processors.messages", + ], + }, + } +] + +# https://docs.djangoproject.com/en/dev/ref/settings/#form-renderer +FORM_RENDERER = "django.forms.renderers.TemplatesSetting" + +# FIXTURES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs +FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),) + +# SECURITY +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly +SESSION_COOKIE_HTTPONLY = True +# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly +CSRF_COOKIE_HTTPONLY = True +# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter +SECURE_BROWSER_XSS_FILTER = True +# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options +X_FRAME_OPTIONS = "DENY" + +# ADMIN +# ------------------------------------------------------------------------------ +# Django Admin URL. +ADMIN_URL = "admin/" +# https://docs.djangoproject.com/en/dev/ref/settings/#admins +ADMINS = [("""sanspie""", "alexandr.d.karpov@gmail.com")] +# https://docs.djangoproject.com/en/dev/ref/settings/#managers +MANAGERS = ADMINS + +# LOGGING +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#logging +# See https://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "verbose": { + "format": "%(levelname)s %(asctime)s %(module)s " + "%(process)d %(thread)d %(message)s" + } + }, + "handlers": { + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "verbose", + } + }, + "root": {"level": "INFO", "handlers": ["console"]}, +} + +# Celery +# ------------------------------------------------------------------------------ +if USE_TZ: + # https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-timezone + CELERY_TIMEZONE = TIME_ZONE +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-broker_url +CELERY_BROKER_URL = env("CELERY_BROKER_URL") +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-result_backend +CELERY_RESULT_BACKEND = CELERY_BROKER_URL +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-accept_content +CELERY_ACCEPT_CONTENT = ["json"] +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-task_serializer +CELERY_TASK_SERIALIZER = "json" +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#std:setting-result_serializer +CELERY_RESULT_SERIALIZER = "json" +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-time-limit +CELERY_TASK_TIME_LIMIT = 5 * 60 +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-soft-time-limit +CELERY_TASK_SOFT_TIME_LIMIT = 60 +# https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler +CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" + + +# django-rest-framework +# ------------------------------------------------------------------------------- +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", + ), + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), +} + +# django-cors-headers +CORS_ALLOW_ALL_ORIGINS = True diff --git a/app/conf/settings/local.py b/app/conf/settings/local.py new file mode 100644 index 0000000..d3f3fce --- /dev/null +++ b/app/conf/settings/local.py @@ -0,0 +1,52 @@ +from .base import * +from .base import env + +# GENERAL +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +DEBUG = True +# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key +SECRET_KEY = env( + "DJANGO_SECRET_KEY", + default="7IFkV9gOD8gP3RbPfBuFmcUE4rPVvlnTlVScHr8OBCmHQrA1OIl2la2TJqKIBkTu", +) +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = ["", "127.0.0.1"] + +# CACHES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#caches +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "", + } +} + +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips +INTERNAL_IPS = ["127.0.0.1"] + +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": (), + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.AllowAny",), + "DATETIME_FORMAT": "%Y-%m-%dT%H:%M:%S.%fZ", +} + +# STATIC +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#static-root +STATIC_ROOT = str(APPS_DIR / "static") +# https://docs.djangoproject.com/en/dev/ref/settings/#static-url +STATIC_URL = "/static/" +# https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders +STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", +] + +# MEDIA +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#media-root +MEDIA_ROOT = str(APPS_DIR / "media") +# https://docs.djangoproject.com/en/dev/ref/settings/#media-url +MEDIA_URL = "/media/" diff --git a/app/conf/urls.py b/app/conf/urls.py new file mode 100644 index 0000000..049fdcc --- /dev/null +++ b/app/conf/urls.py @@ -0,0 +1,40 @@ +from django.conf import settings +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import path, include, re_path +from drf_yasg import openapi +from drf_yasg.views import get_schema_view +from rest_framework.permissions import AllowAny + +schema_view = get_schema_view( + openapi.Info( + title="API", + default_version="v1", + description="description", + terms_of_service="https://akarpov.ru/about", + contact=openapi.Contact(email="alexander.d.karpov@gmail.com"), + license=openapi.License(name="License"), + ), + validators=["ssv"], + public=True, + permission_classes=[AllowAny], +) + +urlpatterns = ( + [ + path("admin/", admin.site.urls), + path("api/", include("conf.api")), + re_path( + r"^swagger(?P\.json|\.yaml)$", + schema_view.without_ui(cache_timeout=0), + name="schema-json", + ), + re_path( + r"^swagger/$", + schema_view.with_ui("swagger", cache_timeout=0), + name="schema-swagger-ui", + ), + ] + + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +) diff --git a/app/conf/wsgi.py b/app/conf/wsgi.py new file mode 100644 index 0000000..720a139 --- /dev/null +++ b/app/conf/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for conf 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/4.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "conf.settings") + +application = get_wsgi_application() diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..39cfb26 --- /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", "conf.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/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..b5832ab --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,11 @@ +djangorestframework==3.14.0 +Django==4.0.8 +django-cors-headers==3.13.0 +django-environ==0.9.0 +"drf-yasg[validation]" +Pillow==9.2.0 + +redis==4.3.4 +psycopg2-binary==2.9.4 + +celery==5.2.7