**Что сделано**
* **API**
* Добавлен `POST /api/v1/auth/register/` — создание пользователя, генерация кода и письмо (повторная отправка, если email есть, но не подтверждён).
* `POST /api/v1/auth/verify-email/` возвращает `user_id` при успехе.
* `POST /api/v1/auth/resend-email/` — без изменений по контракту.
* **Фронт (views\_front)**
* `RegisterView.form_valid` теперь вызывает API `auth/register` (без прямой работы с ORM).
* `EmailVerifyView.post` переводён на API `auth/verify-email` + автологин при успехе.
* Повторная отправка кода (`resend_email`) уже через API, добавлена защита от частых запросов по сессии.
* Исправлена сериализация телефона (E.164/строка) при отправке в API.
* **Клиент API**
* Новый модуль `employees/api_client.py`: безопасная сериализация payload, удобные ошибки (`api_bad_json status=… body=…`) и абсолютные URL через `request.build_absolute_uri`.
* **Middleware**
* `AuthRequiredMiddleware` теперь **пропускает**: `/api/v1/**`, статику/медиа, страницы логина/регистрации/сброса пароля, админ-логин и `OPTIONS`. Остальное для анонима — редирект на `login`.
* **Аутентификация/выход**
* Страница подтверждения выхода (в стиле iOS bottom sheet) + `ConfirmLogoutView`. Пункт «Выйти» ведёт на GET `/auth/logout/`, далее POST подтверждения.
* Шаблон выхода принимает `next` из контекста (без обращений к `request.GET.next` в шаблоне).
* **UX/мелочи**
* Устранены 500 при отсутствии `password_reset` — добавлены/учтены URL’ы потока сброса (или безопасные ссылки).
* Уточнены сообщения об ошибках при регистрации/верификации.
**Как проверить**
1. Аноним может вызвать:
* `POST /api/v1/auth/register/` → `201 {"ok":true}` / `200 {"ok":true,"resent":true}` / `400 {"error":"email_taken"}`.
* `POST /api/v1/auth/verify-email/` с корректным кодом → `200 {"ok":true,"user_id":...}`.
* `POST /api/v1/auth/resend-email/` → `200 {"ok":true}`.
2. UI:
* Регистрация в форме → обращение к API, письмо с кодом, переход на ввод кода.
* Ввод кода → API, автологин, редирект в ленту.
* «Выйти» в меню → страница подтверждения → POST → сообщение «Вы вышли…» и редирект на логин/`next`.
3. Проверить, что `/api/v1/**` не редиректится на логин (middleware whitelist работает).
4. Телефон в форме не ломает JSON (нет `TypeError: PhoneNumber is not JSON serializable`).
**Миграции**: нет.
44 lines
2.3 KiB
Python
44 lines
2.3 KiB
Python
from django.conf import settings
|
|
from django.conf.urls.static import static
|
|
from django.contrib import admin
|
|
from django.urls import include, path, reverse_lazy
|
|
from django.views.generic.edit import CreateView
|
|
from employees.forms import RegistrationForm
|
|
from employees.views_front import RegisterView
|
|
|
|
from . import views_auth
|
|
|
|
urlpatterns = [
|
|
path("admin/", admin.site.urls),
|
|
path("auth/login/", views_auth.EmailOrPhoneLoginView.as_view(), name="login"),
|
|
path("auth/logout/", views_auth.ConfirmLogoutView.as_view(), name="logout"),
|
|
path("auth/password-reset/", views_auth.PasswordResetView.as_view(
|
|
template_name="registration/password_reset_form.html",
|
|
email_template_name="registration/password_reset_email.txt",
|
|
subject_template_name="registration/password_reset_subject.txt",
|
|
success_url="/auth/password-reset/done/",
|
|
), name="password_reset"),
|
|
path("auth/password-reset/done/", views_auth.PasswordResetDoneView.as_view(
|
|
template_name="registration/password_reset_done.html",
|
|
), name="password_reset_done"),
|
|
path("auth/reset/<uidb64>/<token>/", views_auth.PasswordResetConfirmView.as_view(
|
|
template_name="registration/password_reset_confirm.html",
|
|
success_url="/auth/reset/done/",
|
|
), name="password_reset_confirm"),
|
|
path("auth/reset/done/", views_auth.PasswordResetCompleteView.as_view(
|
|
template_name="registration/password_reset_complete.html",
|
|
), name="password_reset_complete"),
|
|
# path("auth/", include("django.contrib.auth.urls")),
|
|
# path("auth/register/", RegisterView.as_view(), name="register"),
|
|
# path("sms-verify/", SMSVerifyView.as_view(), name="sms_verify"),
|
|
# path("resend-sms/", resend_sms, name="resend_sms"),
|
|
path("calendar/", include("calendar_app.urls", namespace="calendar")),
|
|
path("documents/", include("documents.urls", namespace="documents")),
|
|
path("requests/", include("requests_app.urls", namespace="requests_app")),
|
|
path("employees/", include("employees.urls_front", namespace="employees")),
|
|
path("communications/", include("communications.urls", namespace="communications")),
|
|
path("search/", include("search.urls", namespace="search")),
|
|
path("api/", include("api.urls")),
|
|
path("", include("feed.urls", namespace="feed")),
|
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|