- Создан модуль calendarEventDragDrop.js с классами DragDropState и CalendarUtils - Реализовано перетаскивание событий между календарями через модальное окно - Добавлена кнопка 'Переместить все' для массового перемещения - Модальное окно отображает источник и цель перемещения - Поддержка legacy-календарей (company/department/personal) - Добавлены стили для подсветки drop-target (папки и календари) - Backend: исправлена обработка null значений в serializer - Backend: добавлен endpoint list_raw для получения немодерируемых событий - Использованы стандартные Bootstrap стили для модалов - Исправлено всплытие событий dragover/drop (stopPropagation) - Эмодзи иконки для типов календарей (����) - Toast уведомления об успехе/ошибках перемещения
87 lines
2.5 KiB
JavaScript
87 lines
2.5 KiB
JavaScript
/**
|
||
* @fileoverview Authentication utilities - работа с токенами и авторизацией
|
||
* @module utils/authUtils
|
||
*/
|
||
|
||
/**
|
||
* Получить токен доступа из meta-тега или localStorage
|
||
* @returns {string} Токен доступа или пустая строка
|
||
*/
|
||
export function getAccessToken() {
|
||
const meta = document.querySelector('meta[name="api-access"]');
|
||
const token = meta?.getAttribute("content")?.trim();
|
||
if (token) return token;
|
||
|
||
try {
|
||
return localStorage.getItem("api.access") || "";
|
||
} catch {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Получить CSRF токен из различных источников
|
||
* @returns {string} CSRF токен или пустая строка
|
||
*/
|
||
export function getCsrfToken() {
|
||
return (
|
||
document.querySelector("[name=csrfmiddlewaretoken]")?.value ||
|
||
document.querySelector('meta[name="csrf-token"]')?.content ||
|
||
getCookie("csrftoken") ||
|
||
""
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Получить значение cookie по имени
|
||
* @param {string} name - Имя cookie
|
||
* @returns {string} Значение cookie или пустая строка
|
||
*/
|
||
export function getCookie(name) {
|
||
let cookieValue = null;
|
||
if (document.cookie && document.cookie !== "") {
|
||
const cookies = document.cookie.split(";");
|
||
for (let i = 0; i < cookies.length; i++) {
|
||
const cookie = cookies[i].trim();
|
||
if (cookie.substring(0, name.length + 1) === name + "=") {
|
||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return cookieValue || "";
|
||
}
|
||
|
||
/**
|
||
* Создать заголовки для API запросов с авторизацией
|
||
* @param {boolean} [includeContentType=true] - Добавлять ли Content-Type
|
||
* @returns {Object} Объект с заголовками
|
||
*/
|
||
export function authHeaders(includeContentType = true) {
|
||
const token = getAccessToken();
|
||
const headers = {
|
||
Accept: "application/json",
|
||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||
};
|
||
|
||
const csrfToken = getCsrfToken();
|
||
if (csrfToken) {
|
||
headers["X-CSRFToken"] = csrfToken;
|
||
}
|
||
|
||
if (includeContentType) {
|
||
headers["Content-Type"] = "application/json";
|
||
}
|
||
|
||
return headers;
|
||
}
|
||
|
||
/**
|
||
* Проверить наличие валидного токена авторизации
|
||
* @returns {boolean} True если токен есть
|
||
*/
|
||
export function hasValidToken() {
|
||
const token = getAccessToken();
|
||
return Boolean(token && token.length > 0);
|
||
}
|