diff --git a/.env.example b/.env.example index 3667262..40c140e 100644 --- a/.env.example +++ b/.env.example @@ -17,3 +17,6 @@ DB_PASSWORD=CHANGE_ME_POSTGRES_PASSWORD # Опционально: дополнительные доверенные источники для CSRF (через запятую) # CSRF_TRUSTED_ORIGINS=https://erp.gen7x.ru,https://other.example.com + +# Опционально: требовать вход (по умолчанию false — доступ без авторизации для отладки) +# REQUIRE_LOGIN=true diff --git a/HISTORY.md b/HISTORY.md index 1ff72c2..445fc0c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,17 @@ # История изменений ERP WaterSurf +# История изменений ERP WaterSurf + +## 2025-02-25 22:25 UTC – Временное отключение авторизации для отладки + +**Проблема**: Для быстрой разработки и отладки нужно работать без входа. + +**Решение**: Добавлена настройка `REQUIRE_LOGIN` (по умолчанию `false`). При `REQUIRE_LOGIN=false` представления не требуют входа; мидлварь `OptionalAuthMiddleware` подставляет первого активного суперпользователя для анонимных запросов, чтобы меню и поле «Автор» работали. Миксин `config.mixins.LoginRequiredMixin` проверяет `REQUIRE_LOGIN` и при `false` не перенаправляет на логин. Чтобы вернуть авторизацию: в `.env` задать `REQUIRE_LOGIN=true` и перезапустить приложение. + +**Изменения**: config/settings.py (REQUIRE_LOGIN, OptionalAuthMiddleware), config/mixins.py, config/middleware.py, documents/views.py, references/views.py, users/views.py (импорт LoginRequiredMixin из config.mixins), .env.example (комментарий про REQUIRE_LOGIN). + +--- + ## 2025-02-25 22:15 UTC – Поле «Дата»: размер и кнопка календаря **Проблема**: Дата не помещалась в поле (обрезка по ширине), кнопка выбора календаря сливалась с тёмным фоном. diff --git a/README.md b/README.md index 93fec1b..1038918 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,12 @@ docker compose up -d app - `SECRET_KEY` — секрет Django (не менее 50 символов) - `ALLOWED_HOSTS` — через запятую (например `erp.gen7x.ru,localhost`) - `DEBUG` — true/false +- `REQUIRE_LOGIN` — по умолчанию `false`: доступ без входа (для отладки); `true` — включить авторизацию ## Использование - Веб-интерфейс: после настройки Nginx — https://erp.gen7x.ru/ -- Вход по логину/паролю (пользователи Django). +- Вход по логину/паролю (пользователи Django). При `REQUIRE_LOGIN=false` доступ без входа (для отладки). - Меню: Справочники (валюты, виды заказов, клиенты, организации, поставщики, сотрудники, счета, товары), Документы (заказы клиентов, заказы поставщику, поступления, перемещения, расходы). - Поле «Автор» в документах подставляется из профиля пользователя (связь User → Сотрудник в разделе «Пользователи» / админка). diff --git a/app/config/middleware.py b/app/config/middleware.py new file mode 100644 index 0000000..3507afb --- /dev/null +++ b/app/config/middleware.py @@ -0,0 +1,33 @@ +""" +Мидлварь: при отключённой авторизации (REQUIRE_LOGIN=False) подставляет +суперпользователя для анонимных запросов, чтобы меню и поле «Автор» работали. +""" +from django.conf import settings +from django.contrib.auth import get_user_model + +_user_model = get_user_model() +_cached_dev_user = None + + +def get_dev_user(): + global _cached_dev_user + if _cached_dev_user is None: + _cached_dev_user = _user_model.objects.filter(is_superuser=True, is_active=True).first() + return _cached_dev_user + + +class OptionalAuthMiddleware: + """ + Если REQUIRE_LOGIN=False и пользователь не аутентифицирован, + подставить первого активного суперпользователя (для отладки). + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if not getattr(settings, "REQUIRE_LOGIN", True) and not request.user.is_authenticated: + user = get_dev_user() + if user: + request.user = user + return self.get_response(request) diff --git a/app/config/mixins.py b/app/config/mixins.py new file mode 100644 index 0000000..d67b309 --- /dev/null +++ b/app/config/mixins.py @@ -0,0 +1,19 @@ +""" +Миксины для представлений. +LoginRequiredMixin при REQUIRE_LOGIN=False не требует входа (для отладки/разработки). +""" +from django.contrib.auth.mixins import LoginRequiredMixin as BaseLoginRequiredMixin +from django.conf import settings + + +class LoginRequiredMixin(BaseLoginRequiredMixin): + """ + Требует вход только если в настройках включено REQUIRE_LOGIN. + При REQUIRE_LOGIN=False доступ без авторизации (для отладки). + Чтобы вернуть авторизацию: REQUIRE_LOGIN=true в .env или в settings. + """ + + def dispatch(self, request, *args, **kwargs): + if getattr(settings, "REQUIRE_LOGIN", True): + return super().dispatch(request, *args, **kwargs) + return super(BaseLoginRequiredMixin, self).dispatch(request, *args, **kwargs) diff --git a/app/config/settings.py b/app/config/settings.py index ec08f33..03877a0 100644 --- a/app/config/settings.py +++ b/app/config/settings.py @@ -38,6 +38,7 @@ MIDDLEWARE = [ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "config.middleware.OptionalAuthMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.locale.LocaleMiddleware", @@ -95,6 +96,10 @@ LOGIN_URL = "users:login" LOGIN_REDIRECT_URL = "users:home" LOGOUT_REDIRECT_URL = "users:login" +# Отладка/разработка: REQUIRE_LOGIN=false — доступ без входа (мидлварь подставит суперпользователя). +# Чтобы вернуть авторизацию: REQUIRE_LOGIN=true в .env. +REQUIRE_LOGIN = os.environ.get("REQUIRE_LOGIN", "false").lower() in ("1", "true", "yes") + # Логирование LOGGING = { "version": 1, diff --git a/app/documents/views.py b/app/documents/views.py index 741118f..9f2f765 100644 --- a/app/documents/views.py +++ b/app/documents/views.py @@ -1,7 +1,7 @@ """Представления документов: списки и формы создания/редактирования.""" import logging from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView -from django.contrib.auth.mixins import LoginRequiredMixin +from config.mixins import LoginRequiredMixin from django.urls import reverse_lazy from django.shortcuts import redirect, get_object_or_404 from django.contrib import messages diff --git a/app/references/views.py b/app/references/views.py index 386b4eb..8f35a5f 100644 --- a/app/references/views.py +++ b/app/references/views.py @@ -3,7 +3,7 @@ CRUD для справочников (списки и формы). """ import logging from django.views.generic import ListView, CreateView, UpdateView, DeleteView -from django.contrib.auth.mixins import LoginRequiredMixin +from config.mixins import LoginRequiredMixin from django.urls import reverse_lazy from django.contrib import messages from django.utils.translation import gettext_lazy as _ diff --git a/app/users/views.py b/app/users/views.py index 5a5bef9..61b669d 100644 --- a/app/users/views.py +++ b/app/users/views.py @@ -4,7 +4,7 @@ import logging from django.contrib.auth.views import LoginView, LogoutView from django.views.generic import TemplateView -from django.contrib.auth.mixins import LoginRequiredMixin +from config.mixins import LoginRequiredMixin logger = logging.getLogger(__name__)