github stars project celery-rabbit

This commit is contained in:
2021-07-06 16:36:19 +03:00
commit 70bff65fc0
85 changed files with 6716 additions and 0 deletions

View File

View File

View File

@@ -0,0 +1,8 @@
from django.contrib import admin
from server.apps.main.models import BlogPost
@admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin[BlogPost]):
"""Admin panel example for ``BlogPost`` model."""

View File

@@ -0,0 +1,11 @@
"""
This package is a place for your business logic.
Please, do not create any other files inside your app package.
Place all files here, including: logic, forms, serializers.
Decoupling is a good thing. We need more of that.
Try using https://github.com/dry-python/
It is awesome for writing business logic!
"""

View File

@@ -0,0 +1,35 @@
# Generated by Django 2.2.7 on 2019-11-24 11:01
from django.db import migrations, models
class Migration(migrations.Migration):
"""Initial migration that creates the example BlogPost model."""
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='BlogPost',
fields=[
(
'id',
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
('title', models.CharField(max_length=80)),
('body', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'BlogPost',
'verbose_name_plural': 'BlogPosts',
},
),
]

View File

@@ -0,0 +1,33 @@
import textwrap
from typing import Final, final
from django.db import models
#: That's how constants should be defined.
_POST_TITLE_MAX_LENGTH: Final = 80
@final
class BlogPost(models.Model):
"""
This model is used just as an example.
With it we show how one can:
- Use fixtures and factories
- Use migrations testing
"""
title = models.CharField(max_length=_POST_TITLE_MAX_LENGTH)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
verbose_name = 'BlogPost' # You can probably use `gettext` for this
verbose_name_plural = 'BlogPosts'
def __str__(self) -> str:
"""All django models should have this method."""
return textwrap.wrap(self.title, _POST_TITLE_MAX_LENGTH // 4)[0]

View File

@@ -0,0 +1,29 @@
body {
text-align: center;
}
img {
max-width: 100%;
height: auto;
}
.wemake-services-body {
height: 95vh;
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
align-items: center;
}
.wemake-services-logo {
max-width: 250px;
margin: 0 auto;
}
.github-corner img {
position: absolute;
top: 0;
right: 0;
border: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
from django.urls import path
from server.apps.main.views import index
app_name = 'main'
urlpatterns = [
path('hello/', index, name='hello'),
]

View File

@@ -0,0 +1,12 @@
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
def index(request: HttpRequest) -> HttpResponse:
"""
Main (or index) view.
Returns rendered default page to the user.
Typed with the help of ``django-stubs`` project.
"""
return render(request, 'main/index.html')

View File

@@ -0,0 +1,39 @@
"""
This is a django-split-settings main file.
For more information read this:
https://github.com/sobolevn/django-split-settings
https://sobolevn.me/2017/04/managing-djangos-settings
To change settings file:
`DJANGO_ENV=production python manage.py runserver`
"""
from os import environ
import django_stubs_ext
from split_settings.tools import include, optional
# Monkeypatching Django, so stubs will work for all generics,
# see: https://github.com/typeddjango/django-stubs
django_stubs_ext.monkeypatch()
# Managing environment via `DJANGO_ENV` variable:
environ.setdefault('DJANGO_ENV', 'development')
_ENV = environ['DJANGO_ENV']
_base_settings = (
'components/common.py',
'components/logging.py',
'components/csp.py',
'components/caches.py',
# Select the right env:
'environments/{0}.py'.format(_ENV),
# Optionally override some settings:
optional('environments/local.py'),
)
# Include settings:
include(*_base_settings)

View File

@@ -0,0 +1,11 @@
from pathlib import Path
from decouple import AutoConfig
# Build paths inside the project like this: BASE_DIR.joinpath('some')
# `pathlib` is better than writing: dirname(dirname(dirname(__file__)))
BASE_DIR = Path.cwd().parent.parent.parent.parent
# Loading `.env` files
# See docs: https://gitlab.com/mkleehammer/autoconfig
config = AutoConfig(search_path=BASE_DIR.joinpath('config'))

View File

@@ -0,0 +1,16 @@
# Caching
# https://docs.djangoproject.com/en/2.2/topics/cache/
CACHES = {
'default': {
# TODO: use some other cache in production,
# like https://github.com/jazzband/django-redis
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
},
}
# django-axes
# https://django-axes.readthedocs.io/en/latest/4_configuration.html#configuring-caches
AXES_CACHE = 'default'

View File

@@ -0,0 +1,201 @@
"""
Django settings for server project.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their config, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
from typing import Dict, List, Tuple, Union
from django.utils.translation import ugettext_lazy as _
from server.settings.components import BASE_DIR, config
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
SECRET_KEY = config('DJANGO_SECRET_KEY')
# Application definition:
INSTALLED_APPS: Tuple[str, ...] = (
# Your apps go here:
'server.apps.main',
# Default django apps:
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# django-admin:
'django.contrib.admin',
'django.contrib.admindocs',
# Security:
'axes',
# Health checks:
# You may want to enable other checks as well,
# see: https://github.com/KristianOellegaard/django-health-check
'health_check',
'health_check.db',
'health_check.cache',
'health_check.storage',
)
MIDDLEWARE: Tuple[str, ...] = (
# Content Security Policy:
'csp.middleware.CSPMiddleware',
# Django:
'django.middleware.security.SecurityMiddleware',
# django-permissions-policy
'django_permissions_policy.PermissionsPolicyMiddleware',
'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.clickjacking.XFrameOptionsMiddleware',
# Axes:
'axes.middleware.AxesMiddleware',
# Django HTTP Referrer Policy:
'django_http_referrer_policy.middleware.ReferrerPolicyMiddleware',
)
ROOT_URLCONF = 'server.urls'
WSGI_APPLICATION = 'server.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': config('POSTGRES_DB'),
'USER': config('POSTGRES_USER'),
'PASSWORD': config('POSTGRES_PASSWORD'),
'HOST': config('DJANGO_DATABASE_HOST'),
'PORT': config('DJANGO_DATABASE_PORT', cast=int),
'CONN_MAX_AGE': config('CONN_MAX_AGE', cast=int, default=60),
'OPTIONS': {
'connect_timeout': 10,
},
},
}
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
USE_I18N = True
USE_L10N = True
LANGUAGES = (
('en', _('English')),
('ru', _('Russian')),
)
LOCALE_PATHS = (
'locale/',
)
USE_TZ = True
TIME_ZONE = 'UTC'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
# Templates
# https://docs.djangoproject.com/en/2.2/ref/templates/api
TEMPLATES = [{
'APP_DIRS': True,
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
# Contains plain text templates, like `robots.txt`:
BASE_DIR.joinpath('server', 'templates'),
],
'OPTIONS': {
'context_processors': [
# Default template context processors:
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.request',
],
},
}]
# Media files
# Media root dir is commonly changed in production
# (see development.py and production.py).
# https://docs.djangoproject.com/en/2.2/topics/files/
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR.joinpath('media')
# Django authentication system
# https://docs.djangoproject.com/en/2.2/topics/auth/
AUTHENTICATION_BACKENDS = (
'axes.backends.AxesBackend',
'django.contrib.auth.backends.ModelBackend',
)
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
]
# Security
# https://docs.djangoproject.com/en/2.2/topics/security/
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
# https://github.com/DmytroLitvinov/django-http-referrer-policy
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
REFERRER_POLICY = 'same-origin'
# https://github.com/adamchainz/django-permissions-policy#setting
PERMISSIONS_POLICY: Dict[str, Union[str, List[str]]] = {} # noqa: WPS234
# Timeouts
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-EMAIL_TIMEOUT
EMAIL_TIMEOUT = 5

View File

@@ -0,0 +1,15 @@
"""
This file contains a definition for Content-Security-Policy headers.
Read more about it:
https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Content-Security-Policy
We are using `django-csp` to provide these headers.
Docs: https://github.com/mozilla/django-csp
"""
CSP_SCRIPT_SRC = ("'self'",)
CSP_IMG_SRC = ("'self'",)
CSP_FONT_SRC = ("'self'",)
CSP_STYLE_SRC = ("'self'",)
CSP_DEFAULT_SRC = ("'none'",)

View File

@@ -0,0 +1,77 @@
# Logging
# https://docs.djangoproject.com/en/2.2/topics/logging/
# See also:
# 'Do not log' by Nikita Sobolev (@sobolevn)
# https://sobolevn.me/2020/03/do-not-log
import structlog
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
# We use these formatters in our `'handlers'` configuration.
# Probably, you won't need to modify these lines.
# Unless, you know what you are doing.
'formatters': {
'json_formatter': {
'()': structlog.stdlib.ProcessorFormatter,
'processor': structlog.processors.JSONRenderer(),
},
'console': {
'()': structlog.stdlib.ProcessorFormatter,
'processor': structlog.processors.KeyValueRenderer(
key_order=['timestamp', 'level', 'event', 'logger'],
),
},
},
# You can easily swap `key/value` (default) output and `json` ones.
# Use `'json_console'` if you need `json` logs.
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'console',
},
'json_console': {
'class': 'logging.StreamHandler',
'formatter': 'json_formatter',
},
},
# These loggers are required by our app:
# - django is required when using `logger.getLogger('django')`
# - security is required by `axes`
'loggers': {
'django': {
'handlers': ['console'],
'propagate': True,
'level': 'INFO',
},
'security': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': False,
},
},
}
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.processors.TimeStamper(fmt='iso'),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.ExceptionPrettyPrinter(),
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
context_class=structlog.threadlocal.wrap_dict(dict),
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)

View File

@@ -0,0 +1,2 @@
"""Overriding settings based on the environment."""

View File

@@ -0,0 +1,150 @@
"""
This file contains all the settings that defines the development server.
SECURITY WARNING: don't run with debug turned on in production!
"""
import logging
from typing import List
from server.settings.components import config
from server.settings.components.common import (
DATABASES,
INSTALLED_APPS,
MIDDLEWARE,
)
# Setting the development status:
DEBUG = True
ALLOWED_HOSTS = [
config('DOMAIN_NAME'),
'localhost',
'0.0.0.0', # noqa: S104
'127.0.0.1',
'[::1]',
]
# Installed apps for development only:
INSTALLED_APPS += (
# Better debug:
'debug_toolbar',
'nplusone.ext.django',
# Linting migrations:
'django_migration_linter',
# django-test-migrations:
'django_test_migrations.contrib.django_checks.AutoNames',
# This check might be useful in production as well,
# so it might be a good idea to move `django-test-migrations`
# to prod dependencies and use this check in the main `settings.py`.
# This will check that your database is configured properly,
# when you run `python manage.py check` before deploy.
'django_test_migrations.contrib.django_checks.DatabaseConfiguration',
# django-extra-checks:
'extra_checks',
)
# Static files:
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS: List[str] = []
# Django debug toolbar:
# https://django-debug-toolbar.readthedocs.io
MIDDLEWARE += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
# https://github.com/bradmontgomery/django-querycount
# Prints how many queries were executed, useful for the APIs.
'querycount.middleware.QueryCountMiddleware',
)
def _custom_show_toolbar(request):
"""Only show the debug toolbar to users with the superuser flag."""
return DEBUG and request.user.is_superuser
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK':
'server.settings.environments.development._custom_show_toolbar',
}
# This will make debug toolbar to work with django-csp,
# since `ddt` loads some scripts from `ajax.googleapis.com`:
CSP_SCRIPT_SRC = ("'self'", 'ajax.googleapis.com')
CSP_IMG_SRC = ("'self'", 'data:')
CSP_CONNECT_SRC = ("'self'",)
# nplusone
# https://github.com/jmcarp/nplusone
# Should be the first in line:
MIDDLEWARE = ( # noqa: WPS440
'nplusone.ext.django.NPlusOneMiddleware',
) + MIDDLEWARE
# Logging N+1 requests:
NPLUSONE_RAISE = True # comment out if you want to allow N+1 requests
NPLUSONE_LOGGER = logging.getLogger('django')
NPLUSONE_LOG_LEVEL = logging.WARN
NPLUSONE_WHITELIST = [
{'model': 'admin.*'},
]
# django-test-migrations
# https://github.com/wemake-services/django-test-migrations
# Set of badly named migrations to ignore:
DTM_IGNORED_MIGRATIONS = frozenset((
('axes', '*'),
))
# django-extra-checks
# https://github.com/kalekseev/django-extra-checks
EXTRA_CHECKS = {
'checks': [
# Forbid `unique_together`:
'no-unique-together',
# Require non empty `upload_to` argument:
'field-file-upload-to',
# Use the indexes option instead:
'no-index-together',
# Each model must be registered in admin:
'model-admin',
# FileField/ImageField must have non empty `upload_to` argument:
'field-file-upload-to',
# Text fields shouldn't use `null=True`:
'field-text-null',
# Prefer using BooleanField(null=True) instead of NullBooleanField:
'field-boolean-null',
# Don't pass `null=False` to model fields (this is django default)
'field-null',
# ForeignKey fields must specify db_index explicitly if used in
# other indexes:
{'id': 'field-foreign-key-db-index', 'when': 'indexes'},
# If field nullable `(null=True)`,
# then default=None argument is redundant and should be removed:
'field-default-null',
# Fields with choices must have companion CheckConstraint
# to enforce choices on database level
'field-choices-constraint',
],
}
# Disable persistent DB connections
# https://docs.djangoproject.com/en/2.2/ref/databases/#caveats
DATABASES['default']['CONN_MAX_AGE'] = 0

View File

@@ -0,0 +1 @@
"""Override any custom settings here."""

View File

@@ -0,0 +1,75 @@
"""
This file contains all the settings used in production.
This file is required and if development.py is present these
values are overridden.
"""
from server.settings.components import config
# Production flags:
# https://docs.djangoproject.com/en/2.2/howto/deployment/
DEBUG = False
ALLOWED_HOSTS = [
# TODO: check production hosts
config('DOMAIN_NAME'),
# We need this value for `healthcheck` to work:
'localhost',
]
# Staticfiles
# https://docs.djangoproject.com/en/2.2/ref/contrib/staticfiles/
# This is a hack to allow a special flag to be used with `--dry-run`
# to test things locally.
_COLLECTSTATIC_DRYRUN = config(
'DJANGO_COLLECTSTATIC_DRYRUN', cast=bool, default=False,
)
# Adding STATIC_ROOT to collect static files via 'collectstatic':
STATIC_ROOT = '.static' if _COLLECTSTATIC_DRYRUN else '/var/www/django/static'
STATICFILES_STORAGE = (
# This is a string, not a tuple,
# but it does not fit into 80 characters rule.
'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
)
# Media files
# https://docs.djangoproject.com/en/2.2/topics/files/
MEDIA_ROOT = '/var/www/django/media'
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
_PASS = 'django.contrib.auth.password_validation' # noqa: S105
AUTH_PASSWORD_VALIDATORS = [
{'NAME': '{0}.UserAttributeSimilarityValidator'.format(_PASS)},
{'NAME': '{0}.MinimumLengthValidator'.format(_PASS)},
{'NAME': '{0}.CommonPasswordValidator'.format(_PASS)},
{'NAME': '{0}.NumericPasswordValidator'.format(_PASS)},
]
# Security
# https://docs.djangoproject.com/en/2.2/topics/security/
SECURE_HSTS_SECONDS = 31536000 # the same as Caddy has
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SECURE_REDIRECT_EXEMPT = [
# This is required for healthcheck to work:
'^health/',
]
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

View File

@@ -0,0 +1,14 @@
# The humans responsible & technology colophon
# http://humanstxt.org/
## balsh
Team:
## Technologies
Language: English
Doctype: HTML5
Technologies: Python, Django

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

View File

@@ -0,0 +1,60 @@
"""
Main URL mapping configuration file.
Include other URLConfs from external apps using method `include()`.
It is also a good practice to keep a single URL to the root index page.
This examples uses Django's default media
files serving technique in development.
"""
from django.conf import settings
from django.contrib import admin
from django.contrib.admindocs import urls as admindocs_urls
from django.urls import include, path
from django.views.generic import TemplateView
from health_check import urls as health_urls
from server.apps.main import urls as main_urls
from server.apps.main.views import index
admin.autodiscover()
urlpatterns = [
# Apps:
path('main/', include(main_urls, namespace='main')),
# Health checks:
path('health/', include(health_urls)), # noqa: DJ05
# django-admin:
path('admin/doc/', include(admindocs_urls)), # noqa: DJ05
path('admin/', admin.site.urls),
# Text and xml static files:
path('robots.txt', TemplateView.as_view(
template_name='txt/robots.txt',
content_type='text/plain',
)),
path('humans.txt', TemplateView.as_view(
template_name='txt/humans.txt',
content_type='text/plain',
)),
# It is a good practice to have explicit index view:
path('', index, name='index'),
]
if settings.DEBUG: # pragma: no cover
import debug_toolbar # noqa: WPS433
from django.conf.urls.static import static # noqa: WPS433
urlpatterns = [
# URLs specific only to django-debug-toolbar:
path('__debug__/', include(debug_toolbar.urls)), # noqa: DJ05
] + urlpatterns + static( # type: ignore
# Serving media files in development only:
settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT,
)

View File

@@ -0,0 +1,15 @@
"""
WSGI config for server 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/2.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
application = get_wsgi_application()