diff --git a/checker/__init__.py b/checker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/checker/admin.py b/checker/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/checker/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/checker/apps.py b/checker/apps.py new file mode 100644 index 0000000..8e67ea3 --- /dev/null +++ b/checker/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CheckerConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "checker" diff --git a/checker/migrations/__init__.py b/checker/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/checker/models.py b/checker/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/checker/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/checker/tests.py b/checker/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/checker/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/checker/views.py b/checker/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/checker/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/conf/__init__.py b/conf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/conf/api_router.py b/conf/api_router.py new file mode 100644 index 0000000..2012ebe --- /dev/null +++ b/conf/api_router.py @@ -0,0 +1 @@ +urlpatterns = [] \ No newline at end of file diff --git a/conf/asgi.py b/conf/asgi.py new file mode 100644 index 0000000..9eb89b5 --- /dev/null +++ b/conf/asgi.py @@ -0,0 +1,18 @@ +""" +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/conf/settings/__init__.py b/conf/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/conf/settings/base.py b/conf/settings/base.py new file mode 100644 index 0000000..c743296 --- /dev/null +++ b/conf/settings/base.py @@ -0,0 +1,195 @@ +import environ + +from pathlib import Path + +ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent + +APPS_DIR = ROOT_DIR +env = environ.Env() + +env.read_env(str(APPS_DIR / "conf" / ".env" / ".django")) +env.read_env(str(APPS_DIR / "conf" / ".env" / ".postgresql")) + +# https://docs.djangoproject.com/en/dev/ref/settings/#debug +DEBUG = env.bool("DJANGO_DEBUG", True) + +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-tz +USE_TZ = True +# https://docs.djangoproject.com/en/dev/ref/settings/#locale-paths +LOCALE_PATHS = [str(APPS_DIR / "locale")] + +# DATABASES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#databases +import dj_database_url + +DATABASES = { + "default": dj_database_url.config( + conn_max_age=600, default="postgres://postgres:debug@127.0.0.1:5432/db" + ) +} + +# 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 = "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.admin", +] +THIRD_PARTY_APPS = [ + "rest_framework", + "drf_yasg", + "corsheaders", +] + +HEALTH_CHECKS = [ + "health_check", # required + "health_check.db", # stock Django health checkers + "health_check.cache", + "health_check.storage", + # 'health_check.contrib.celery', + # 'health_check.contrib.celery_ping', + "health_check.contrib.migrations", + "health_check.contrib.psutil", # disk and memory utilization +] + +LOCAL_APPS = ["checker"] +# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps +INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + HEALTH_CHECKS + LOCAL_APPS + +# 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.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.common.BrokenLinkEmailsMiddleware", +] + +# 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": [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", + ], + }, + } +] + +# 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/" + +# LOGGING +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#logging +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"]}, +} + +# django-rest-framework +# ------------------------------------------------------------------------------- +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", + ), + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), + "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", +} + +# django-cors-headers +CORS_ALLOW_ALL_ORIGINS = True diff --git a/conf/settings/local.py b/conf/settings/local.py new file mode 100644 index 0000000..4ebf6cf --- /dev/null +++ b/conf/settings/local.py @@ -0,0 +1,74 @@ +from datetime import timedelta + +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 = ["*"] + +# CACHES +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#caches +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "", + } +} + +# django-debug-toolbar +# ------------------------------------------------------------------------------ +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites +INSTALLED_APPS += ["debug_toolbar"] +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware +MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa F405 +# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config +DEBUG_TOOLBAR_CONFIG = { + "DISABLE_PANELS": ["debug_toolbar.panels.redirects.RedirectsPanel"], + "SHOW_TEMPLATE_CONTEXT": True, +} + +# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips +INTERNAL_IPS = ["127.0.0.1"] + +# DRF +# ------------------------------------------------------------------------------ +SIMPLE_JWT = { + "ACCESS_TOKEN_LIFETIME": timedelta(minutes=10000), +} + +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_simplejwt.authentication.JWTAuthentication", + ), + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.AllowAny",), + "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", +} + +# STATIC +# ------------------------------------------------------------------------------ +# https://docs.djangoproject.com/en/dev/ref/settings/#static-root +STATIC_ROOT = str(ROOT_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(ROOT_DIR / "media") +# https://docs.djangoproject.com/en/dev/ref/settings/#media-url +MEDIA_URL = "/media/" diff --git a/conf/settings/production.py b/conf/settings/production.py new file mode 100644 index 0000000..e69de29 diff --git a/conf/urls.py b/conf/urls.py new file mode 100644 index 0000000..4429ec8 --- /dev/null +++ b/conf/urls.py @@ -0,0 +1,46 @@ +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 import permissions + +from django.conf import settings + +schema_view = get_schema_view( + openapi.Info( + title="API", + default_version="v1", + description="openapi schema", + contact=openapi.Contact(email="alexander.d.karpov@gmail.com"), + ), + validators=["ssv"], + public=True, + permission_classes=[permissions.AllowAny], +) + +urlpatterns = [ + # Django Admin, use {% url 'admin:index' %} + path("admin/", admin.site.urls), + path("api/", include("conf.api_router")), + 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", + ), + re_path( + r"^redoc/$", + schema_view.with_ui("redoc", cache_timeout=0), + name="schema-redoc", + ), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + +if settings.DEBUG: + import debug_toolbar + + urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns diff --git a/conf/wsgi.py b/conf/wsgi.py new file mode 100644 index 0000000..ab077e8 --- /dev/null +++ b/conf/wsgi.py @@ -0,0 +1,18 @@ +""" +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/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cd3c52f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3" +services: + postgres: + image: "postgres" + container_name: "postgres" + environment: + - POSTGRES_HOST=postgres + - POSTGRES_PORT=5432 + - POSTGRES_DB=db + - POSTGRES_USER=debug + - POSTGRES_PASSWORD=debug + ports: + - "5432:5432" + + network_mode: "host" \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..0ba5810 --- /dev/null +++ b/manage.py @@ -0,0 +1,24 @@ +#!/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.local" + ) + 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..b401351 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,11 @@ +Django==4.1 +django-environ==0.9.0 +environ==1.0 +Pillow==9.2.0 +djangorestframework==3.13.1 +djangorestframework-simplejwt==5.2.0 +django-health-check==3.16.5 +django-cors-headers==3.13.0 +drf-yasg==1.21.3 +psutil +dj-database-url \ No newline at end of file diff --git a/requirements/local.txt b/requirements/local.txt new file mode 100644 index 0000000..176563c --- /dev/null +++ b/requirements/local.txt @@ -0,0 +1,3 @@ +-r base.txt +psycopg2-binary==2.9.3 +django-debug-toolbar==3.6.0 diff --git a/requirements/production.txt b/requirements/production.txt new file mode 100644 index 0000000..cd9258f --- /dev/null +++ b/requirements/production.txt @@ -0,0 +1,4 @@ +-r base.txt + +gunicorn==20.1.0 +psycopg2==2.9.3