Конфигурация приложения перенесена в библиотеку

This commit is contained in:
2026-07-18 20:05:17 +03:00
parent a95793c3df
commit a5a29b03b6
18 changed files with 596 additions and 294 deletions

93
docker-compose.env Normal file
View File

@@ -0,0 +1,93 @@
#
# URLs
#
RACK_API_URL='http://localhost:8000'
#
# Security
#
RACK_DEBUG=True
RACK_SECRET_KEY='<change-me>'
RACK_ALLOWED_HOSTS='localhost, 127.0.0.1'
RACK_CORS_ALLOWED_ORIGINS='http://localhost:4200, http://127.0.0.1:4200'
RACK_CSRF_TRUSTED_ORIGINS='http://localhost:4200, http://127.0.0.1:4200'
RACK_SESSION_TIMEOUT=1209600
#
# Main Database
#
RACK_DATABASE_HOST='localhost'
RACK_DATABASE_PORT=5432
RACK_DATABASE_NAME='rack'
RACK_DATABASE_USERNAME='rack'
RACK_DATABASE_PASSWORD='<change-me>'
#
# Test Database
#
RACK_TEST_DATABASE='/tmp/rack/rack.sqlite'
#
# KeyDb Database
#
USE_KEYDB=1
KEYDB_HOST='127.0.0.1'
KEYDB_PORT=6379
KEYDB_DBINDEX=0
KEYDB_PASSWORD='<change-me>'
KEYDB_POOL_SIZE=4
#
# S3 Object Storage
#
USE_S3=1
RACK_S3_ENDPOINT_URL='http://localhost:9000'
RACK_MINIO_API_PORT=9000
RACK_MINIO_CONSOLE_PORT=9001
RACK_S3_ACCESS_KEY='minioadmin'
RACK_S3_SECRET_KEY='<change-me>'
RACK_S3_BUCKET_PREFIX='<change-me>'
RACK_S3_POOL_SIZE=4
#
# I10N and L10N
#
RACK_LANGUAGE='ru'
RACK_TIME_ZONE='Europe/Moscow'
#
# Static & Media files
#
RACK_STATIC_URL='assets/'
RACK_STATIC_ROOT='/tmp/rack/static'
RACK_MEDIA_ROOT='/tmp/rack/media'
RACK_THUMBNAILS_ROOT='/tmp/rack/thumbnails'
#
# Email SMTP
#
RACK_EMAIL_HOST='localhost'
RACK_EMAIL_PORT=1025
RACK_EMAIL_HOST_USER=''
RACK_EMAIL_HOST_PASSWORD=''
RACK_EMAIL_USE_TLS=False
RACK_EMAIL_USE_SSL=False
RACK_EMAIL_TIMEOUT=20
RACK_EMAIL_SSL_KEYFILE=''
RACK_EMAIL_SSL_CERTFILE=''
#
# Email Sender
#
RACK_EMAIL_FROM_NAME='DRF Rack'
RACK_EMAIL_FROM_ADDR='rack@xeaf.net'
RACK_EMAIL_SUBJECT_PREFIX='[DRF Rack]'
#
# Logs
#
RACK_LOG_LEVEL='DEBUG'
RACK_LOG_FILE='/tmp/rack/rack.log'
RACK_DEBUG_LOG_FILE='/tmp/rack/rack_debug.log'

121
docker-compose.yml Normal file
View File

@@ -0,0 +1,121 @@
#
# Службы
#
services:
# 1. База данных (Источник правды)
db :
image : postgres:15-alpine
container_name: rack_db
restart : always
env_file :
- .env
environment :
POSTGRES_DB : ${rack_DATABASE_NAME}
POSTGRES_USER : ${rack_DATABASE_USERNAME}
POSTGRES_PASSWORD: ${rack_DATABASE_PASSWORD}
ports :
- "${rack_DATABASE_PORT:-5432}:5432"
volumes :
- postgres_data:/var/lib/postgresql/data
networks :
- rack-network
# 2. KeyDB (Сессии, Сокеты, Очереди, Кэш чанков)
keydb :
image : eqalpha/keydb:latest
container_name: rack_keydb
restart : always
env_file :
- .env
ports :
- "${rack_KEYDB_PORT:-6379}:6379"
command : keydb-server --protected-mode no --save ""
volumes :
- keydb_data:/data
networks :
- rack-network
# 3. MinIO (S3 Хранилище)
minio :
image : minio/minio:latest # Всегда доступный и свежий образ
container_name: rack_minio
restart : always
env_file :
- .env
environment :
MINIO_ROOT_USER : ${rack_S3_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${rack_S3_SECRET_KEY}
ports :
- "${rack_MINIO_API_PORT:-9000}:9000"
- "${rack_MINIO_CONSOLE_PORT:-9001}:9001"
volumes :
- minio_data:/data
command : server /data --console-address ":9001"
healthcheck :
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks :
- rack-network
# 4. Авто-создатель бакетов
minio-init:
image : minio/mc:latest
container_name: rack_minio_mc
depends_on :
minio:
condition: service_healthy
env_file :
- .env
entrypoint : >
/bin/sh -c "
echo 'Настройка подключения к MinIO...';
/usr/bin/mc alias set local http://minio:9000 ${rack_S3_ACCESS_KEY} ${rack_S3_SECRET_KEY};
echo 'Ожидание готовности сервера...';
while ! /usr/bin/mc ping local --count 1 > /dev/null 2>&1; do
sleep 1;
done;
echo 'Сервер готов. Создание бакетов...';
/usr/bin/mc mb local/${rack_S3_BUCKET_PREFIX}-avatar --ignore-existing || true;
/usr/bin/mc mb local/${rack_S3_BUCKET_PREFIX}-media --ignore-existing || true;
echo 'Бакеты успешно проверены/созданы!';
exit 0;
"
networks :
- rack-network
# 5. Почтовый сервер (Ловушка для писем)
mailpit :
image : axllent/mailpit:latest
container_name: rack_mail
restart : always
env_file :
- .env
ports :
- "${rack_MAILPIT_UI_PORT:-8025}:8025"
- "${rack_MAILPIT_SMTP_PORT:-1025}:1025"
networks :
- rack-network
#
# Тома
#
volumes :
postgres_data:
keydb_data :
minio_data :
#
# Сеть
#
networks:
rack-network:
driver: bridge
name : rack-network

View File

@@ -8,5 +8,5 @@
Пакет классов работы с параметрами конфигурации Пакет классов работы с параметрами конфигурации
""" """
from .log_debug_filter import LogDebugFilter from .environment import Environment
from .rack_config import RackConfig from .setting_utils import SettingUtils

View File

@@ -8,12 +8,13 @@
Параметры конфигурации базы данных Параметры конфигурации базы данных
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import Environment
from net.xeaf.rack.utils.settings import SettingUtils
# #
# Проверка режима запуска тестов # Проверка режима запуска тестов
# #
if not RackConfig.is_test_mode(): if not SettingUtils.is_test_mode():
# #
# Основная БД # Основная БД
@@ -21,11 +22,11 @@ if not RackConfig.is_test_mode():
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.postgresql", "ENGINE": "django.db.backends.postgresql",
"HOST": RackConfig.env_str("DATABASE_HOST"), "HOST": Environment.get_str("DATABASE_HOST"),
"PORT": RackConfig.env_str("DATABASE_PORT"), "PORT": Environment.get_str("DATABASE_PORT"),
"NAME": RackConfig.env_str("DATABASE_NAME"), "NAME": Environment.get_str("DATABASE_NAME"),
"USER": RackConfig.env_str("DATABASE_USERNAME"), "USER": Environment.get_str("DATABASE_USERNAME"),
"PASSWORD": RackConfig.env_str("DATABASE_PASSWORD"), "PASSWORD": Environment.get_str("DATABASE_PASSWORD"),
} }
} }
@@ -37,6 +38,6 @@ else:
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.sqlite3", "ENGINE": "django.db.backends.sqlite3",
"NAME": RackConfig.env_str("TEST_DATABASE", ":memory:"), "NAME": Environment.get_str("TEST_DATABASE", ":memory:"),
} }
} }

View File

@@ -8,9 +8,9 @@
Параметры конфигурации режима отладки Параметры конфигурации режима отладки
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import SettingUtils
# #
# Признак режима отладки # Признак режима отладки
# #
DEBUG = RackConfig.is_debug_mode() DEBUG = SettingUtils.is_debug_mode()

View File

@@ -8,7 +8,7 @@
Параметры конфигурации Django REST Framework Параметры конфигурации Django REST Framework
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import SettingUtils
# #
# Настройка представления результатов вызова API # Настройка представления результатов вызова API
@@ -20,7 +20,7 @@ REST_FRAMEWORK_RENDERER = (
# #
# Только для режима отладки # Только для режима отладки
# #
if RackConfig.is_debug_mode(): if SettingUtils.is_debug_mode():
REST_FRAMEWORK_RENDERER += ('djangorestframework_camel_case.render.CamelCaseBrowsableAPIRenderer',) REST_FRAMEWORK_RENDERER += ('djangorestframework_camel_case.render.CamelCaseBrowsableAPIRenderer',)
# #

View File

@@ -8,7 +8,7 @@
Параметры конфигурации отправки электронной почты Параметры конфигурации отправки электронной почты
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import Environment
# #
# Класс методов отправки электронной почты # Класс методов отправки электронной почты
@@ -18,19 +18,19 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# #
# Информация об отправителе # Информация об отправителе
# #
EMAIL_FROM_NAME = RackConfig.env_str('EMAIL_FROM_NAME') EMAIL_FROM_NAME = Environment.get_str('EMAIL_FROM_NAME')
EMAIL_FROM_ADDR = RackConfig.env_str('EMAIL_FROM_ADDR') EMAIL_FROM_ADDR = Environment.get_str('EMAIL_FROM_ADDR')
EMAIL_SUBJECT_PREFIX = RackConfig.env_str('EMAIL_SUBJECT_PREFIX', '') EMAIL_SUBJECT_PREFIX = Environment.get_str('EMAIL_SUBJECT_PREFIX', '')
# #
# Параметры SMTP сервера # Параметры SMTP сервера
# #
EMAIL_HOST = RackConfig.env_str('EMAIL_HOST', 'localhost') EMAIL_HOST = Environment.get_str('EMAIL_HOST', 'localhost')
EMAIL_PORT = RackConfig.env_int('EMAIL_PORT', 25) EMAIL_PORT = Environment.get_int('EMAIL_PORT', 25)
EMAIL_HOST_USER = RackConfig.env_str('EMAIL_HOST_USER') EMAIL_HOST_USER = Environment.get_str('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = RackConfig.env_str('EMAIL_HOST_PASSWORD') EMAIL_HOST_PASSWORD = Environment.get_str('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = RackConfig.env_bool('EMAIL_USE_TLS', False) EMAIL_USE_TLS = Environment.get_bool('EMAIL_USE_TLS', False)
EMAIL_USE_SSL = RackConfig.env_bool('EMAIL_USE_SSL', False) EMAIL_USE_SSL = Environment.get_bool('EMAIL_USE_SSL', False)
EMAIL_TIMEOUT = RackConfig.env_int('EMAIL_TIMEOUT', 20) EMAIL_TIMEOUT = Environment.get_int('EMAIL_TIMEOUT', 20)
EMAIL_SSL_KEYFILE = RackConfig.env_str('EMAIL_SSL_KEYFILE', "") EMAIL_SSL_KEYFILE = Environment.get_str('EMAIL_SSL_KEYFILE', "")
EMAIL_SSL_CERTFILE = RackConfig.env_str('EMAIL_SSL_CERTFILE', "") EMAIL_SSL_CERTFILE = Environment.get_str('EMAIL_SSL_CERTFILE', "")

View File

@@ -8,25 +8,25 @@
Параметры конфигурации файловой системы Параметры конфигурации файловой системы
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import Environment
# #
# Путь к статическим файлам # Путь к статическим файлам
# #
STATIC_URL = RackConfig.env_str('STATIC_URL', 'static/') STATIC_URL = Environment.get_str('STATIC_URL', 'static/')
# #
# Путь для сборки статических файлов # Путь для сборки статических файлов
# #
STATIC_ROOT = RackConfig.env_str_as_dir('STATIC_ROOT') STATIC_ROOT = Environment.get_str_as_dir('STATIC_ROOT')
# #
# Путь к медиафайлам # Путь к медиафайлам
# #
MEDIA_ROOT = RackConfig.env_str_as_dir('MEDIA_ROOT') MEDIA_ROOT = Environment.get_str_as_dir('MEDIA_ROOT')
THUMBNAILS_ROOT = RackConfig.env_str_as_dir('THUMBNAILS_ROOT') THUMBNAILS_ROOT = Environment.get_str_as_dir('THUMBNAILS_ROOT')
# #
# Путь к временным файлам # Путь к временным файлам
# #
FILE_UPLOAD_TEMP_DIR = RackConfig.env_str_as_dir('FILE_UPLOADS_TEMP', '/tmp') FILE_UPLOAD_TEMP_DIR = Environment.get_str_as_dir('FILE_UPLOADS_TEMP', '/tmp')

View File

@@ -8,7 +8,7 @@
Параметры конфигурации интернационализации и локализации Параметры конфигурации интернационализации и локализации
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import Environment
# #
# Идентификаторы поддерживаемых языков # Идентификаторы поддерживаемых языков
@@ -27,12 +27,12 @@ LANGUAGES = (
# #
# Язык по умолчанию # Язык по умолчанию
# #
LANGUAGE_CODE = RackConfig.env_str('LANGUAGE') LANGUAGE_CODE = Environment.get_str('LANGUAGE')
# #
# Временная зона # Временная зона
# #
TIME_ZONE = RackConfig.env_str('TIME_ZONE') TIME_ZONE = Environment.get_str('TIME_ZONE')
USE_TZ = True USE_TZ = True
# #

View File

@@ -8,22 +8,22 @@
Параметры конфигурации подключения к KeyDb (Redis) Параметры конфигурации подключения к KeyDb (Redis)
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import Environment
# #
# Проверяем необходимость настройки # Проверяем необходимость настройки
# #
USE_KEYDB = RackConfig.env_bool('USE_KEYDB', True) USE_KEYDB = Environment.get_bool('USE_KEYDB', True)
if USE_KEYDB: if USE_KEYDB:
# #
# Читаем параметры подключения к KeyDb (Redis) # Читаем параметры подключения к KeyDb (Redis)
# #
KEYDB_HOST = RackConfig.env_str("KEYDB_HOST", "127.0.0.1") KEYDB_HOST = Environment.get_str("KEYDB_HOST", "127.0.0.1")
KEYDB_PORT = RackConfig.env_int("KEYDB_PORT", 6379) KEYDB_PORT = Environment.get_int("KEYDB_PORT", 6379)
KEYDB_DBINDEX = RackConfig.env_int("KEYDB_DBINDEX", 1) KEYDB_DBINDEX = Environment.get_int("KEYDB_DBINDEX", 1)
KEYDB_PASSWORD = RackConfig.env_str('KEYDB_PASSWORD', "") KEYDB_PASSWORD = Environment.get_str('KEYDB_PASSWORD', "")
KEYDB_POOL_SIZE = RackConfig.env_int('KEYDB_POOL_SIZE', 5) KEYDB_POOL_SIZE = Environment.get_int('KEYDB_POOL_SIZE', 5)
KEYDB_LOCATION = f"redis://:{KEYDB_PASSWORD}@{KEYDB_HOST}:{KEYDB_PORT}/{KEYDB_DBINDEX}" KEYDB_LOCATION = f"redis://:{KEYDB_PASSWORD}@{KEYDB_HOST}:{KEYDB_PORT}/{KEYDB_DBINDEX}"
# #

View File

@@ -8,19 +8,20 @@
Параметры конфигурации системы журналирования Параметры конфигурации системы журналирования
""" """
from net.xeaf.rack.utils.settings import LogDebugFilter from net.xeaf.rack.utils.settings import Environment
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import SettingUtils
from net.xeaf.rack.utils.settings.logger import LogDebugFilter
# #
# Имя журнала # Имя журнала
# #
LOGGER_NAME = RackConfig.env_str("LOGGER_NAME", "rack") LOGGER_NAME = Environment.get_str("LOGGER_NAME", "rack")
# #
# Уровень журналирования # Уровень журналирования
# #
LOG_LEVEL = RackConfig.env_str("LOG_LEVEL") LOG_LEVEL = Environment.get_str("LOG_LEVEL")
if RackConfig.is_debug_mode(): if SettingUtils.is_debug_mode():
LOG_LEVEL = "DEBUG" LOG_LEVEL = "DEBUG"
else: else:
if LOG_LEVEL == "DEBUG": if LOG_LEVEL == "DEBUG":
@@ -29,8 +30,8 @@ else:
# #
# Файлы журналов # Файлы журналов
# #
LOG_FILE = RackConfig.env_str_as_file("LOG_FILE") LOG_FILE = Environment.get_str_as_file("LOG_FILE")
DEBUG_LOG_FILE = RackConfig.env_str_as_file("DEBUG_LOG_FILE") DEBUG_LOG_FILE = Environment.get_str_as_file("DEBUG_LOG_FILE")
# #
# Настройка журнала # Настройка журнала
@@ -78,7 +79,7 @@ LOGGING = {
"maxBytes": 1024 * 1024 * 10, "maxBytes": 1024 * 1024 * 10,
"backupCount": 5, "backupCount": 5,
"formatter": "custom_format", "formatter": "custom_format",
"filters": [] if RackConfig.is_debug_mode() else ["strict_debug_only"], "filters": [] if SettingUtils.is_debug_mode() else ["strict_debug_only"],
"encoding": "utf-8", "encoding": "utf-8",
}, },
}, },

View File

@@ -8,14 +8,14 @@
Параметры конфигурации безопасности Параметры конфигурации безопасности
""" """
from net.xeaf.rack.utils.settings import RackConfig from net.xeaf.rack.utils.settings import Environment
# #
# Допустимые хосты, параметры CORS и CSRF # Допустимые хосты, параметры CORS и CSRF
# #
ALLOWED_HOSTS = RackConfig.env_str_as_list('ALLOWED_HOSTS') ALLOWED_HOSTS = Environment.get_str_as_list('ALLOWED_HOSTS')
CORS_ALLOWED_ORIGINS = RackConfig.env_str_as_list('CORS_ALLOWED_ORIGINS') CORS_ALLOWED_ORIGINS = Environment.get_str_as_list('CORS_ALLOWED_ORIGINS')
CSRF_TRUSTED_ORIGINS = RackConfig.env_str_as_list('CSRF_TRUSTED_ORIGINS') CSRF_TRUSTED_ORIGINS = Environment.get_str_as_list('CSRF_TRUSTED_ORIGINS')
# #
# Допустимые заголовки CORS # Допустимые заголовки CORS
@@ -34,7 +34,7 @@ CORS_ALLOW_HEADERS = (
# #
# Ключ # Ключ
# #
SECRET_KEY = RackConfig.env_str('SECRET_KEY') SECRET_KEY = Environment.get_str('SECRET_KEY')
# #
# Валидаторы паролей # Валидаторы паролей
@@ -49,10 +49,10 @@ AUTH_PASSWORD_VALIDATORS = [
# #
# Тайм-аут сессии # Тайм-аут сессии
# #
SESSION_TIMEOUT = RackConfig.env_int('SESSION_TIMEOUT', 3600) SESSION_TIMEOUT = Environment.get_int('SESSION_TIMEOUT', 3600)
# #
# Модели данных аутентификации # Модели данных аутентификации
# #
AUTH_USER_MODEL = 'net_xeaf_rack.Account' AUTH_USER_MODEL = Environment.get_str("AUTH_USER_MODEL", 'net_xeaf_rack.AccountModel')
AUTH_SESSION_MODEL = 'net_xeaf_rack.Session' AUTH_SESSION_MODEL = Environment.get_str("AUTH_SESSION_MODEL", 'net_xeaf_rack.SessionModel')

View File

@@ -0,0 +1,26 @@
# DRF Rack
# Библиотека классов расширений для Django REST Framework
#
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
# Все права защищены. Лицензия: MIT
"""
Параметры конфигурации WSGI
"""
from net.xeaf.rack.utils.settings import SettingUtils
#
# Пути
#
ROOT_URLCONF = SettingUtils.get_url_root()
#
# Класс приложения проекта
#
WSGI_APPLICATION = SettingUtils.get_wsgi_application()
#
# Тип автоинкрементного поля по умолчанию
#
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View File

@@ -5,12 +5,10 @@
# Все права защищены. Лицензия: MIT # Все права защищены. Лицензия: MIT
""" """
Описание класса RackConfig Описание класса EnvUtils
""" """
import os import os
import sys
from pathlib import Path
import environ import environ
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@@ -18,17 +16,14 @@ from environ import Env
from environ import NoValue from environ import NoValue
class RackConfig: class Environment:
""" """
Содержит базовые параметры для настройки приложения Реализация доступа к параметрам и переменным окружения
""" """
__base_dir: str __base_dir: str
""" Путь к корневой директории приложения """ """ Путь к корневой директории приложения """
__debug_mode: bool
""" Признак режима отладки приложения """
__is_initialized: bool __is_initialized: bool
""" Признак инициализации конфигурации """ """ Признак инициализации конфигурации """
@@ -38,9 +33,6 @@ class RackConfig:
__env_prefix: str __env_prefix: str
""" Префикс переменных окружения """ """ Префикс переменных окружения """
__config_files: dict[str, str | None] = {}
""" Словарь файлов параметров конфигурации """
@classmethod @classmethod
def initialize(cls, base_dir: str, env_prefix: str = ""): def initialize(cls, base_dir: str, env_prefix: str = ""):
""" """
@@ -54,56 +46,8 @@ class RackConfig:
cls.__env = environ.Env() cls.__env = environ.Env()
cls.__env_prefix = env_prefix cls.__env_prefix = env_prefix
environ.Env.read_env(os.path.join(cls.__base_dir, '.env')) environ.Env.read_env(os.path.join(cls.__base_dir, '.env'))
cls._init_configuration_files()
cls.__is_initialized = True cls.__is_initialized = True
# Сразу загружаем признак режима отладки
cls.__debug_mode = cls.env_bool('DEBUG', False)
@classmethod
def get_base_dir(cls) -> str:
"""
Возвращает путь к корневой директории приложения
:return: Путь к корневой директории приложения
"""
cls._check_initialization()
return cls.__base_dir
@classmethod
def get_debug_mode(cls) -> bool:
"""
Возвращает признак режима отладки приложения
:return: Признак режима отладки
"""
cls._check_initialization()
return cls.__debug_mode
@classmethod
def is_test_mode(cls) -> bool:
"""
Возвращает признак режима тестирования приложения
:return: Признак режима тестирования приложения
"""
cls._check_initialization()
return 'test' in sys.argv
@classmethod
def is_debug_mode(cls) -> bool:
"""
Возвращает признак режима отладки приложения
:return: Признак режима отладки
"""
cls._check_initialization()
return cls.__debug_mode
@classmethod @classmethod
def env(cls) -> Env: def env(cls) -> Env:
""" """
@@ -116,7 +60,7 @@ class RackConfig:
return cls.__env return cls.__env
@classmethod @classmethod
def env_str(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str: def get_str(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str:
""" """
Возвращает строковое значение переменной окружения Возвращает строковое значение переменной окружения
@@ -126,10 +70,10 @@ class RackConfig:
:return: Значение переменной окружения :return: Значение переменной окружения
""" """
return cls.env().str(cls.env_name(name), default_value) return cls.env().str(cls._env_name(name), default_value)
@classmethod @classmethod
def env_int(cls, name: str, default_value: int | NoValue = Env.NOTSET) -> int: def get_int(cls, name: str, default_value: int | NoValue = Env.NOTSET) -> int:
""" """
Возвращает целочисленное значение переменной окружения Возвращает целочисленное значение переменной окружения
@@ -139,10 +83,10 @@ class RackConfig:
:return: Значение переменной окружения :return: Значение переменной окружения
""" """
return cls.env().int(cls.env_name(name), default_value) return cls.env().int(cls._env_name(name), default_value)
@classmethod @classmethod
def env_bool(cls, name: str, default_value: bool | NoValue = Env.NOTSET) -> bool: def get_bool(cls, name: str, default_value: bool | NoValue = Env.NOTSET) -> bool:
""" """
Возвращает логическое значение переменной окружения Возвращает логическое значение переменной окружения
@@ -152,10 +96,10 @@ class RackConfig:
:return: Значение переменной окружения :return: Значение переменной окружения
""" """
return cls.env().bool(cls.env_name(name), default_value) return cls.env().bool(cls._env_name(name), default_value)
@classmethod @classmethod
def env_str_as_dir(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str: def get_str_as_dir(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str:
""" """
Возвращает значение переменной окружения в виде проверенного имени директории Возвращает значение переменной окружения в виде проверенного имени директории
@@ -165,13 +109,13 @@ class RackConfig:
:return: Значение переменной окружения :return: Значение переменной окружения
""" """
dir_path = cls.env_str(name, default_value) dir_path = cls.get_str(name, default_value)
os.makedirs(dir_path, exist_ok=True) os.makedirs(dir_path, exist_ok=True)
return dir_path return dir_path
@classmethod @classmethod
def env_str_as_file(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str: def get_str_as_file(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> str:
""" """
Возвращает значение переменной окружения в виде проверенного имени фйала Возвращает значение переменной окружения в виде проверенного имени фйала
@@ -181,14 +125,14 @@ class RackConfig:
:return: Значение переменной окружения :return: Значение переменной окружения
""" """
file_path = cls.env_str(name, default_value) file_path = cls.get_str(name, default_value)
dir_path = os.path.dirname(file_path) dir_path = os.path.dirname(file_path)
os.makedirs(dir_path, exist_ok=True) os.makedirs(dir_path, exist_ok=True)
return file_path return file_path
@classmethod @classmethod
def env_str_as_list(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> list[str]: def get_str_as_list(cls, name: str, default_value: str | NoValue = Env.NOTSET) -> list[str]:
""" """
Возвращает значение переменной окружения в виде списка строк Возвращает значение переменной окружения в виде списка строк
@@ -198,14 +142,14 @@ class RackConfig:
:return: Значение переменной окружения :return: Значение переменной окружения
""" """
values = cls.env_str(name, default_value) values = cls.get_str(name, default_value)
if not values: if not values:
return [] return []
return [item.strip() for item in values.split(",") if item.strip()] return [item.strip() for item in values.split(",") if item.strip()]
@classmethod @classmethod
def env_name(cls, name: str) -> str: def _env_name(cls, name: str) -> str:
""" """
Возвращает полное имя переменной окружения Возвращает полное имя переменной окружения
@@ -216,27 +160,6 @@ class RackConfig:
return f"{cls.__env_prefix}{name}" return f"{cls.__env_prefix}{name}"
@classmethod
def get_config_files(cls) -> list[str]:
"""
Возвращает список файлов конфигурации
:return: Список файлов конфигурации
"""
return [file for file in cls.__config_files.values() if file is not None]
@classmethod
def set_config_file(cls, name: str, full_path: str | None) -> None:
"""
Устанавливает полный путь к файлу конфигурации
:param name: Название файла конфигурации
:param full_path: Полный путь к файлу конфигурации
"""
cls.__config_files[name] = full_path
@classmethod @classmethod
def _check_initialization(cls) -> None: def _check_initialization(cls) -> None:
""" """
@@ -244,16 +167,4 @@ class RackConfig:
""" """
if not cls.__is_initialized: if not cls.__is_initialized:
raise ImproperlyConfigured("DRF Rack configuration is not initialized") raise ImproperlyConfigured(f"Class {__class__.__name__} is not initialized.")
@classmethod
def _init_configuration_files(cls) -> None:
"""
Инициализирует словарь файлов параметров конфигурации
"""
root = f"{os.path.dirname(__file__)}/configs"
for file in os.listdir(root):
if file.endswith(".py") and file != "__init__.py":
name = Path(file).stem
cls.__config_files[name] = f"{root}/{file}"

View File

@@ -0,0 +1,11 @@
# DRF Rack
# Библиотека классов расширений для Django REST Framework
#
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
# Все права защищены. Лицензия: MIT
"""
Пакет классов логгера
"""
from .log_debug_filter import LogDebugFilter

View File

@@ -0,0 +1,238 @@
# DRF Rack
# Библиотека классов расширений для Django REST Framework
#
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
# Все права защищены. Лицензия: MIT
"""
Описание класса SettingUtils
"""
import os
import sys
import tomllib
from pathlib import Path
from django.core.exceptions import ImproperlyConfigured
from .environment import Environment
class SettingUtils:
"""
Содержит методы работа с параметрами настройки приложения
"""
__base_dir: str
""" Путь к корневой директории приложения """
__debug_mode: bool
""" Признак режима отладки приложения """
__app_name: str
""" Имя приложения """
__app_version: str
""" Имя приложения """
__url_root: str
""" Корневой URL приложения """
__wsgi_application: str
""" Имя WSGI-приложения """
__is_initialized: bool
""" Признак инициализации конфигурации """
__config_files: dict[str, str | None] = {}
""" Словарь файлов параметров конфигурации """
@classmethod
def initialize(cls, base_dir: Path, app_name: str, env_prefix: str, url_root: str, wsgi_application: str):
"""
Инициализирует конфигурацию приложения
:param base_dir: Базовая директория приложения
:param app_name: Имя приложения
:param env_prefix: Префикс для переменных окружения
:param url_root: Корневой URL приложения
:param wsgi_application: Имя WSGI-приложения
"""
cls.__base_dir = str(base_dir)
cls.__url_root = url_root
cls.__wsgi_application = wsgi_application
cls.__app_name = app_name
cls.__app_version = cls.version_from_toml()
Environment.initialize(cls.__base_dir, env_prefix)
cls.__debug_mode = Environment.get_bool('DEBUG', False)
cls._init_configuration_files()
cls.__is_initialized = True
@classmethod
def get_app_name(cls) -> str:
"""
Возвращает имя приложения
:return: Имя приложения
"""
cls._check_initialization()
return cls.__app_name
@classmethod
def get_app_version(cls) -> str:
"""
Возвращает версию приложения
:return: Версия приложения
"""
cls._check_initialization()
return cls.__app_version
@classmethod
def get_url_root(cls) -> str:
"""
Возвращает корневой URL приложения
:return: Корневой URL приложения
"""
cls._check_initialization()
return cls.__url_root
@classmethod
def get_wsgi_application(cls) -> str:
"""
Возвращает WSGI-приложение приложения
:return: WSGI-приложение приложения
"""
cls._check_initialization()
return cls.__wsgi_application
@classmethod
def get_base_dir(cls) -> str:
"""
Возвращает путь к корневой директории приложения
:return: Путь к корневой директории приложения
"""
cls._check_initialization()
return cls.__base_dir
@classmethod
def get_debug_mode(cls) -> bool:
"""
Возвращает признак режима отладки приложения
:return: Признак режима отладки
"""
cls._check_initialization()
return cls.__debug_mode
@classmethod
def is_test_mode(cls) -> bool:
"""
Возвращает признак режима тестирования приложения
:return: Признак режима тестирования приложения
"""
cls._check_initialization()
return 'test' in sys.argv
@classmethod
def is_debug_mode(cls) -> bool:
"""
Возвращает признак режима отладки приложения
:return: Признак режима отладки
"""
cls._check_initialization()
return cls.__debug_mode
@classmethod
def version_from_toml(cls) -> str:
"""
Чтение версии из файла pyproject.toml
:return: Номер версии
"""
pyproject_path = os.path.join(cls.__base_dir, "pyproject.toml")
with open(pyproject_path, 'rb') as f:
data = tomllib.load(f)
return data['project']['version']
@classmethod
def get_config_files(cls) -> list[str]:
"""
Возвращает список файлов конфигурации
:return: Список файлов конфигурации
"""
return [file for file in cls.__config_files.values() if file is not None]
@classmethod
def set_config_file(cls, name: str, full_path: str | None) -> None:
"""
Устанавливает полный путь к файлу конфигурации
:param name: Название файла конфигурации
:param full_path: Полный путь к файлу конфигурации
"""
cls.__config_files[name] = full_path
@classmethod
def get_installed_apps(cls) -> list[str]:
"""
Возвращает список установленных приложений
:return: Список установленных приложений
"""
return [
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'rest_framework',
'net.xeaf.rack',
]
@classmethod
def _check_initialization(cls) -> None:
"""
Проверяет завершение инициализации
"""
if not cls.__is_initialized:
raise ImproperlyConfigured(f"Class {__class__.__name__} is not initialized")
@classmethod
def _init_configuration_files(cls) -> None:
"""
Инициализирует словарь файлов параметров конфигурации
"""
root = f"{os.path.dirname(__file__)}/configs"
for file in os.listdir(root):
if file.endswith(".py") and file != "__init__.py":
name = Path(file).stem
cls.__config_files[name] = f"{root}/{file}"

View File

@@ -1,139 +1,39 @@
# DRF Rack
# Библиотека классов расширений для Django REST Framework
#
# Автор: Николай В. Анохин <n.anokhin@xeaf.net>
# Все права защищены. Лицензия: MIT
""" """
Django settings for wsgiapp project. Настройки приложения
Generated by 'django-admin startproject' using Django 6.0.7.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/6.0/ref/settings/
""" """
from pathlib import Path from pathlib import Path
import sys from split_settings.tools import include
# Build paths inside the project like this: BASE_DIR / 'subdir'. from net.xeaf.rack.utils.settings import SettingUtils
#
# Инициализация библиотеки DRF Rack
#
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
SettingUtils.initialize(
base_dir=BASE_DIR,
env_prefix="RACK_",
app_name="DRF Rack Demo",
url_root="wsgiapp.urls",
wsgi_application="wsgiapp.wsgi.application",
)
config_files = SettingUtils.get_config_files()
# Quick-start development settings - unsuitable for production #
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ # Установленные приложения
#
INSTALLED_APPS = SettingUtils.get_installed_apps()
# SECURITY WARNING: keep the secret key used in production secret! #
SECRET_KEY = 'django-insecure-%vi5$0i9hq*ov#p&8mj5^fdfx#1y!k_awxgvgus-no=9dg1=9b' # Подключение модулей конфигурации
#
# SECURITY WARNING: don't run with debug turned on in production! include(*config_files, )
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',
'rest_framework',
'net.xeaf.rack',
]
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 = 'wsgiapp.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'wsgiapp.wsgi.application'
# Database
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
if 'test' in sys.argv:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
}
else:
# noinspection PyUnresolvedReferences
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': "/tmp/rack/rack.sqlite",
}
}
# Password validation
# https://docs.djangoproject.com/en/6.0/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',
},
]
AUTH_USER_MODEL = 'net_xeaf_rack.AccountModel'
# Internationalization
# https://docs.djangoproject.com/en/6.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = 'static/'
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
],
}