Initial DC CMS extraction

This commit is contained in:
DCCONSTRUCTIONS
2026-07-06 00:33:27 +03:00
commit 43a96c9f7f
23 changed files with 13844 additions and 0 deletions
@@ -0,0 +1 @@
@@ -0,0 +1,948 @@
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<script data-id="nodedc-auth-favicon">
"use strict";
(function () {
const faviconHref = "data:image/svg+xml,%3Csvg%20width%3D%2232%22%20height%3D%2232%22%20viewBox%3D%220%200%2032%2032%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Cstyle%3E%0A%20%20%20%20.icon-fav-01%20%7B%20fill%3A%20%23333334%3B%20%7D%0A%20%20%20%20%40media%20(prefers-color-scheme%3A%20dark)%20%7B%20.icon-fav-01%20%7B%20fill%3A%20%23FEFEFE%3B%20%7D%20%7D%0A%20%20%20%20%40media%20(prefers-color-scheme%3A%20light)%20%7B%20.icon-fav-01%20%7B%20fill%3A%20%23000000%3B%20%7D%20%7D%0A%20%20%3C%2Fstyle%3E%0A%20%20%3Cpath%20class%3D%22icon-fav-01%22%20d%3D%22M17.8738%2015.9159L16.0002%2018.9718L14.1267%2015.9159H17.8738ZM23.6307%2012.7864H8.36914L15.9999%2025.2308L23.6307%2012.7864Z%22%2F%3E%0A%20%20%3Cpath%20class%3D%22icon-fav-01%22%20d%3D%22M10.9793%2019.4872L6.67269%2011.5918H25.5344L21.2281%2019.4872H25.0581L31.2614%208H0.738281L7.16244%2019.4872H10.9793Z%22%2F%3E%0A%3C%2Fsvg%3E";
const applyFavicon = () => {
if (!document.head) return;
document.querySelectorAll('link[rel~="icon"]').forEach((node) => node.remove());
const link = document.createElement("link");
link.rel = "icon";
link.type = "image/svg+xml";
link.sizes = "any";
link.href = faviconHref;
document.head.appendChild(link);
};
applyFavicon();
window.addEventListener("DOMContentLoaded", applyFavicon, { once: true });
})();
</script>
{% with request_host=request.get_host %}
{% if request_host|slice:":11" != "auth-admin." and request_host|slice:":15" != "cms-auth-admin." and request_host|slice:":9" != "id-admin." and request_host|slice:":12" != "172.22.0.222" %}
{% if request.path|slice:":9" == "/if/flow/" or request.path|slice:":7" == "/flows/" or request.path|slice:":7" == "/login/" %}
<script data-id="nodedc-auth-flow-scope">
"use strict";
(function () {
const isAuthentikAdminHost =
window.location.hostname.startsWith("auth-admin.") ||
window.location.hostname.startsWith("cms-auth-admin.") ||
window.location.hostname.startsWith("id-admin.") ||
window.location.hostname === "172.22.0.222";
const isAuthExperiencePage =
!isAuthentikAdminHost &&
(
window.location.pathname.includes("/if/flow/") ||
window.location.pathname.includes("/flows/") ||
window.location.pathname.includes("/login/")
);
document.documentElement.classList.toggle("nodedc-auth-flow-page", isAuthExperiencePage);
window.addEventListener("DOMContentLoaded", function () {
document.body?.classList.toggle("nodedc-auth-flow-page", isAuthExperiencePage);
}, { once: true });
})();
</script>
<style data-id="nodedc-auth-login-css">
{% include "branding/nodedc-login.css" %}
</style>
<style data-id="nodedc-auth-critical-loader">
html.nodedc-auth-flow-page {
--ak-global--primary: #f4f4f5 !important;
--ak-global--background: #0e0f10 !important;
--ak-global--background-image: none !important;
--pf-global--primary-color--100: #f4f4f5 !important;
--pf-v5-global--primary-color--100: #f4f4f5 !important;
--pf-v6-global--primary-color--100: #f4f4f5 !important;
--pf-v4-global--palette--blue-300: #f4f4f5 !important;
--ak-global--palette--blue-300: #f4f4f5 !important;
--pf-global--active-color--100: #f4f4f5 !important;
--pf-global--link--Color: #f4f4f5 !important;
--nodedc-auth-primary: #f4f4f5 !important;
--nodedc-auth-bg: #0e0f10 !important;
}
html.nodedc-auth-flow-page,
html.nodedc-auth-flow-page body,
html.nodedc-auth-flow-page body .pf-c-login {
background: #0e0f10 !important;
color-scheme: dark !important;
}
html.nodedc-auth-flow-page body ak-flow-executor::part(main),
html.nodedc-auth-flow-page body .pf-c-login__main,
html.nodedc-auth-flow-page body .pf-c-empty-state,
html.nodedc-auth-flow-page body .pf-v5-c-empty-state,
html.nodedc-auth-flow-page body .pf-v6-c-empty-state,
html.nodedc-auth-flow-page body .pf-c-card,
html.nodedc-auth-flow-page body .pf-v5-c-card,
html.nodedc-auth-flow-page body .pf-v6-c-card,
html.nodedc-auth-flow-page body [class*="empty-state"] {
background: transparent !important;
box-shadow: none !important;
border: 0 !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
}
html.nodedc-auth-flow-page body .pf-c-spinner,
html.nodedc-auth-flow-page body .pf-v5-c-spinner,
html.nodedc-auth-flow-page body .pf-v6-c-spinner,
html.nodedc-auth-flow-page body [role="progressbar"],
html.nodedc-auth-flow-page body ak-spinner {
--pf-c-spinner--Color: #f4f4f5 !important;
--pf-v5-c-spinner--Color: #f4f4f5 !important;
--pf-v6-c-spinner--Color: #f4f4f5 !important;
--pf-c-spinner__clipper--after--BoxShadowColor: #f4f4f5 !important;
--pf-c-spinner__lead-ball--after--BackgroundColor: #f4f4f5 !important;
--pf-c-spinner__tail-ball--after--BackgroundColor: #f4f4f5 !important;
color: #f4f4f5 !important;
}
html.nodedc-auth-flow-page body #ak-placeholder.ak-c-placeholder,
html.nodedc-auth-flow-page body .ak-c-placeholder[slot="placeholder"] {
position: fixed !important;
top: 50% !important;
left: 50% !important;
display: block !important;
width: 2.5rem !important;
height: 2.5rem !important;
min-width: 2.5rem !important;
min-height: 2.5rem !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
border-radius: 999px !important;
background: transparent !important;
box-shadow: none !important;
opacity: 1 !important;
visibility: visible !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
transform: translate(-50%, -50%) !important;
overflow: visible !important;
}
html.nodedc-auth-flow-page body #ak-placeholder.ak-c-placeholder .pf-c-spinner,
html.nodedc-auth-flow-page body .ak-c-placeholder[slot="placeholder"] .pf-c-spinner,
html.nodedc-auth-flow-page body #ak-placeholder.ak-c-placeholder [role="progressbar"],
html.nodedc-auth-flow-page body .ak-c-placeholder[slot="placeholder"] [role="progressbar"] {
opacity: 0 !important;
visibility: hidden !important;
}
html.nodedc-auth-flow-page body #ak-placeholder.ak-c-placeholder::before,
html.nodedc-auth-flow-page body .ak-c-placeholder[slot="placeholder"]::before {
content: "";
position: absolute;
inset: 0;
border: 2px solid rgba(255, 255, 255, 0.22);
border-top-color: #f4f4f5;
border-radius: 999px;
animation: nodedc-auth-placeholder-spin 0.72s linear infinite;
pointer-events: none;
}
html.nodedc-auth-flow-page body.nodedc-auth-loading ak-flow-executor::part(main),
html.nodedc-auth-flow-page body.nodedc-auth-loading .pf-c-login__main {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
html.nodedc-auth-flow-page body.nodedc-auth-loading::before {
content: "";
position: fixed;
top: 50%;
left: 50%;
z-index: 1200;
width: 2.5rem;
height: 2.5rem;
border: 2px solid rgba(255, 255, 255, 0.22);
border-top-color: #f4f4f5;
border-radius: 999px;
transform: translate(-50%, -50%);
animation: nodedc-auth-loader-spin 0.72s linear infinite;
pointer-events: none;
}
html.nodedc-auth-flow-page body.nodedc-auth-card-ready::before {
display: none !important;
}
@keyframes nodedc-auth-loader-spin {
from {
transform: translate(-50%, -50%) rotate(0deg);
}
to {
transform: translate(-50%, -50%) rotate(360deg);
}
}
@keyframes nodedc-auth-placeholder-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>
{% endif %}
{% endif %}
{% endwith %}
<script data-id="authentik-config">
"use strict";
const isAuthentikAdminHost =
window.location.hostname.startsWith("auth-admin.") ||
window.location.hostname.startsWith("id-admin.") ||
window.location.hostname === "172.22.0.222";
const isAuthExperiencePage =
!isAuthentikAdminHost &&
(
window.location.pathname.includes("/if/flow/") ||
window.location.pathname.includes("/flows/") ||
window.location.pathname.includes("/login/")
);
const authentikBrand = JSON.parse('{{ brand_json|escapejs }}' || "{}");
if (isAuthExperiencePage) {
const nodedcAuthLoginCss = document.querySelector('style[data-id="nodedc-auth-login-css"]')?.textContent || "";
authentikBrand.branding_custom_css = nodedcAuthLoginCss;
authentikBrand.brandingCustomCss = nodedcAuthLoginCss;
} else {
authentikBrand.branding_custom_css = "";
authentikBrand.brandingCustomCss = "";
}
window.authentik = {
locale: "ru",
config: JSON.parse('{{ config_json|escapejs }}' || "{}"),
brand: authentikBrand,
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",
api: {
base: "{{ base_url }}",
relBase: "{{ base_url_rel }}",
},
};
window.addEventListener("DOMContentLoaded", function () {
{% for message in messages %}
window.dispatchEvent(
new CustomEvent("ak-message", {
bubbles: true,
composed: true,
detail: {
level: "{{ message.tags|escapejs }}",
message: "{{ message.message|escapejs }}",
},
}),
);
{% endfor %}
});
</script>
{% with request_host=request.get_host %}
{% if request_host|slice:":11" != "auth-admin." and request_host|slice:":15" != "cms-auth-admin." and request_host|slice:":9" != "id-admin." and request_host|slice:":12" != "172.22.0.222" %}
{% if request.path|slice:":9" == "/if/flow/" or request.path|slice:":7" == "/flows/" or request.path|slice:":7" == "/login/" %}
<script data-id="nodedc-auth-field-enhancements">
"use strict";
(function () {
const isAuthentikAdminHost =
window.location.hostname.startsWith("auth-admin.") ||
window.location.hostname.startsWith("cms-auth-admin.") ||
window.location.hostname.startsWith("id-admin.") ||
window.location.hostname === "172.22.0.222";
const isAuthExperiencePage =
!isAuthentikAdminHost &&
(
window.location.pathname.includes("/if/flow/") ||
window.location.pathname.includes("/flows/") ||
window.location.pathname.includes("/login/")
);
if (!isAuthExperiencePage) return;
document.documentElement.classList.add("nodedc-auth-flow-page");
window.addEventListener("DOMContentLoaded", function () {
document.body?.classList.add("nodedc-auth-flow-page");
}, { once: true });
const logoSvg = `
<svg id="nodedc-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220.82 54.55" aria-hidden="true">
<defs>
<style>
.cls-1{fill:#e2e1e1;}
.cls-2{fill:#dbdbdb;stroke:#dbdbdb;stroke-miterlimit:10;stroke-width:0.75px;}
</style>
</defs>
<path class="cls-1" d="M52.8,23.61,46.92,33.76,41.05,23.61H52.8m18-10.39H23.06L46.92,54.55Z"></path>
<polygon class="cls-1" points="31.28 33.13 18.11 10.34 75.73 10.34 62.59 33.13 74.28 33.13 93.22 0 0 0 19.61 33.13 31.28 33.13"></polygon>
<path class="cls-2" d="M116.35,18.49V1h1.27l10.34,15V1h1.33V18.49H128l-10.34-15v15Z"></path>
<path class="cls-2" d="M140.43,18.64c-4.79,0-8.16-3.72-8.16-8.89S135.64.86,140.43.86s8.17,3.72,8.17,8.89S145.25,18.64,140.43,18.64Zm0-1.25c4,0,6.79-3.17,6.79-7.64s-2.77-7.64-6.79-7.64-6.77,3.17-6.77,7.64S136.44,17.39,140.43,17.39Z"></path>
<path class="cls-2" d="M151.6,18.49V1h5.1c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74ZM153,17.24h3.75c4.77,0,7.42-2.92,7.42-7.49s-2.65-7.49-7.42-7.49H153Z"></path>
<path class="cls-2" d="M168.49,1h10.77V2.26h-9.42V8.93h7.89v1.25h-7.89v7.06h9.74v1.25H168.49Z"></path>
<path class="cls-2" d="M188.88,18.49V1H194c5.54,0,8.79,3.42,8.79,8.74s-3.25,8.74-8.79,8.74Zm1.35-1.25H194c4.77,0,7.41-2.92,7.41-7.49S198.75,2.26,194,2.26h-3.75Z"></path>
<path class="cls-2" d="M205.15,9.75c0-5.24,3.19-8.89,8.11-8.89a6.8,6.8,0,0,1,7.1,5.52h-1.43a5.54,5.54,0,0,0-5.74-4.27c-4.05,0-6.64,3.17-6.64,7.64s2.54,7.64,6.59,7.64a5.46,5.46,0,0,0,5.74-4.29h1.43c-.75,3.52-3.4,5.54-7.15,5.54C208.27,18.64,205.15,15.05,205.15,9.75Z"></path>
</svg>
`;
const clearIcon = `
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
<circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2"></circle>
<path d="M9 9l6 6M15 9l-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>
</svg>
`;
const genericLoginError = "Не удалось выполнить вход. Проверьте email и пароль или запросите доступ.";
const revokedLoginError = "Аккаунт больше не активен. Запросите доступ, если хотите подключиться снова.";
const genericFlowErrorMessages = [
"Response returned an error code",
"Response returned an error",
"Не удалось завершить операцию.",
];
const accountStatusCache = new Map();
const authTranslations = new Map([
["Failed to authenticate.", genericLoginError],
["Invalid credentials.", genericLoginError],
["Invalid password.", genericLoginError],
["Incorrect password.", genericLoginError],
["Authentication failed.", genericLoginError],
["This field is required.", "Заполните это поле."],
["Please enter your password", "Введите пароль"],
["Please enter your password.", "Введите пароль."],
["Please enter a valid email address.", "Введите корректную электронную почту."],
["Caps Lock is enabled.", "Включён Caps Lock."],
["Forgot username or password?", "Забыли пароль?"],
["Forgot password?", "Забыли пароль?"],
["Remember me on this device", "Запомнить на этом устройстве"],
["Email", "Эл. почта"],
["Email or Username", "Эл. почта"],
["Password", "Пароль"],
["Log in", "Войти"],
["Continue", "Продолжить"],
["Show password", "Показать пароль"],
["Hide password", "Скрыть пароль"],
["Permission denied", "Доступ запрещён"],
["Not you?", "Сменить пользователя"],
["Request has been denied.", "Доступ запрещён."],
["The request has been denied.", "Доступ запрещён."],
["В произведении запроса было отказано.", "Доступ запрещён."],
["Go home", "Вернуться назад"],
["Response returned an error code", "Не удалось завершить операцию."],
]);
function visitRoots(root, callback) {
callback(root);
root.querySelectorAll("*").forEach((element) => {
if (element.shadowRoot) {
visitRoots(element.shadowRoot, callback);
}
});
}
function ensureLogo() {
if (!document.body || document.querySelector("[data-nodedc-auth-logo='true']")) return;
document.body.classList.add("nodedc-auth-enhanced");
const logo = document.createElement("a");
logo.className = "nodedc-auth-logo";
logo.dataset.nodedcAuthLogo = "true";
logo.href = getLauncherBaseUrl();
logo.setAttribute("aria-label", "DC CMS");
logo.innerHTML = logoSvg;
document.body.appendChild(logo);
updateLogoLink(logo);
}
function getLauncherBaseUrl() {
const hostname = window.location.hostname;
const cmsHostnames = {
"cms-auth.local.nodedc": "cms.local.nodedc",
"cms-auth-admin.local.nodedc": "cms.local.nodedc",
"cms-id.local.nodedc": "cms.local.nodedc",
"auth.dcserve.ru": "cms.dcserve.ru",
"auth-admin.dcserve.ru": "cms.dcserve.ru",
};
const cmsHostname =
cmsHostnames[hostname] ||
(hostname.startsWith("cms-auth.")
? `cms.${hostname.slice(9)}`
: hostname.startsWith("auth.")
? `cms.${hostname.slice(5)}`
: hostname.startsWith("cms-id.")
? `cms.${hostname.slice(7)}`
: "cms.local.nodedc");
const port = window.location.port ? `:${window.location.port}` : "";
return `${window.location.protocol}//${cmsHostname}${port}/`;
}
function updateLogoLink(logo) {
fetch(new URL("/api/public/brand", getLauncherBaseUrl()).toString(), { cache: "no-store" })
.then((response) => (response.ok ? response.json() : null))
.then((payload) => {
if (payload?.logoLinkUrl) {
logo.href = payload.logoLinkUrl;
}
})
.catch(() => {});
}
function translateTextValue(value) {
if (!value) return value;
const trimmed = value.trim();
const translated = authTranslations.get(trimmed);
if (!translated) return value;
return value.replace(trimmed, translated);
}
function translateAuthText(root) {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const parent = node.parentElement;
if (!parent || !node.nodeValue?.trim()) return NodeFilter.FILTER_REJECT;
if (["SCRIPT", "STYLE", "SVG", "PATH", "POLYGON"].includes(parent.tagName)) {
return NodeFilter.FILTER_REJECT;
}
return NodeFilter.FILTER_ACCEPT;
},
});
const textNodes = [];
while (walker.nextNode()) {
textNodes.push(walker.currentNode);
}
textNodes.forEach((node) => {
const translated = translateTextValue(node.nodeValue);
if (translated !== node.nodeValue) {
node.nodeValue = translated;
}
});
root.querySelectorAll("[aria-label], [title], [placeholder], input[value], button").forEach((element) => {
["aria-label", "title", "placeholder"].forEach((attribute) => {
const value = element.getAttribute(attribute);
const translated = translateTextValue(value);
if (translated !== value) element.setAttribute(attribute, translated);
});
});
}
function hasPermissionDeniedScreen() {
let denied = false;
visitRoots(document, (root) => {
if (denied) return;
const text = root.textContent || "";
denied = [
"Permission denied",
"Доступ ограничен",
"Доступ запрещен",
"Доступ запрещён",
"Request has been denied.",
"The request has been denied.",
"В произведении запроса было отказано.",
"Доступ к модулю ограничен.",
].some((message) => text.includes(message));
});
return denied;
}
function syncPermissionDeniedState() {
document.body?.classList.toggle("nodedc-auth-permission-denied", hasPermissionDeniedScreen());
}
function findAncestor(element, predicate, maxDepth = 8) {
let current = element;
for (let depth = 0; current && depth < maxDepth; depth += 1) {
if (predicate(current)) return current;
current = current.parentElement;
}
return null;
}
function applyPermissionDeniedLayout(root) {
const deniedTitleMessages = ["Permission denied", "Доступ ограничен", "Доступ запрещен", "Доступ запрещён"];
const deniedSubtitle = "Доступ запрещён.";
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const text = node.nodeValue?.trim() || "";
return deniedTitleMessages.some((message) => text === message)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT;
},
});
const textNodes = [];
while (walker.nextNode()) {
textNodes.push(walker.currentNode);
}
textNodes.forEach((node) => {
const titleElement = findAncestor(
node.parentElement,
(element) =>
element.matches?.(".pf-c-title, .pf-v5-c-title, h1, h2") ||
element.getAttribute?.("slot") === "title",
);
const header =
titleElement?.closest?.(".pf-c-login__main-header") ||
findAncestor(titleElement || node.parentElement, (element) => element.matches?.(".pf-c-login__main-header"), 10) ||
root.querySelector?.(".pf-c-login__main-header");
if (titleElement) {
titleElement.style.display = "none";
}
if (header) {
header.setAttribute("data-nodedc-permission-denied", "true");
header.setAttribute("data-nodedc-denied-subtitle", deniedSubtitle);
}
});
}
function hidePermissionDeniedReason(root) {
const deniedMessages = [
"Request has been denied.",
"The request has been denied.",
"В произведении запроса было отказано.",
"Доступ к модулю ограничен.",
"Объяснение:",
"Reason:",
"Привязка политики",
"Policy binding",
"returned result",
"returned a result",
"'None' вернула результат",
];
root.querySelectorAll(".pf-c-alert, .pf-v5-c-alert, .pf-c-empty-state__body, [class*='alert']").forEach((element) => {
const text = element.textContent || "";
if (deniedMessages.some((message) => text.includes(message))) {
element.style.display = "none";
}
});
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const text = node.nodeValue?.trim() || "";
return deniedMessages.some((message) => text.includes(message))
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT;
},
});
const textNodes = [];
while (walker.nextNode()) {
textNodes.push(walker.currentNode);
}
textNodes.forEach((node) => {
let element = node.parentElement;
for (let depth = 0; element && depth < 6; depth += 1) {
const text = (element.textContent || "").trim();
if (deniedMessages.some((message) => text === message || text.endsWith(message)) && text.length <= 96) {
element.style.display = "none";
return;
}
element = element.parentElement;
}
if (node.parentElement) {
node.parentElement.style.display = "none";
}
});
}
function enhanceIdentifierField(root) {
const input = root.querySelector("#ak-identifier-input");
if (!input || input.dataset.nodedcClearBound === "true") return;
const fieldGroup = input.closest(".pf-c-form__group") || input.parentElement;
if (!fieldGroup) return;
input.dataset.nodedcClearBound = "true";
fieldGroup.style.position = "relative";
const button = document.createElement("button");
button.type = "button";
button.tabIndex = -1;
button.className = "nodedc-auth-clear-input";
button.dataset.nodedcClearInput = "true";
button.setAttribute("aria-label", "Очистить электронную почту");
button.innerHTML = clearIcon;
const update = () => {
button.dataset.visible = input.value ? "true" : "false";
};
button.addEventListener("click", (event) => {
event.preventDefault();
input.value = "";
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
input.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
input.focus();
update();
});
input.addEventListener("input", update);
input.addEventListener("change", update);
fieldGroup.appendChild(button);
update();
}
function maskPasswordInput(input) {
if (!input || input.type !== "text") return;
input.type = "password";
const button = input.closest(".pf-c-input-group")?.querySelector(".pf-c-button.pf-m-control");
const icon = button?.querySelector("i");
button?.setAttribute("aria-label", "Показать пароль");
icon?.classList.remove("fa-eye-slash");
icon?.classList.add("fa-eye");
}
function maskVisiblePasswords(target) {
visitRoots(document, (root) => {
root.querySelectorAll("#ak-stage-identification-password, #ak-stage-password-input").forEach((input) => {
const inputGroup = input.closest(".pf-c-input-group");
if (inputGroup?.contains(target)) return;
maskPasswordInput(input);
});
});
}
function enhancePasswordFields(root) {
root.querySelectorAll("#ak-stage-identification-password, #ak-stage-password-input").forEach((input) => {
if (input.placeholder !== "Введите пароль") {
input.placeholder = "Введите пароль";
}
if (input.dataset.nodedcBlurBound === "true") return;
input.dataset.nodedcBlurBound = "true";
input.addEventListener("blur", () => {
window.setTimeout(() => {
const inputGroup = input.closest(".pf-c-input-group");
if (inputGroup?.contains(document.activeElement)) return;
maskPasswordInput(input);
}, 0);
});
});
}
function normalizePasswordFields(root) {
enhancePasswordFields(root);
}
function dispatchSyntheticInput(input) {
if (!input) return;
try {
input.dispatchEvent(new InputEvent("input", {
bubbles: true,
composed: true,
inputType: "insertReplacementText",
data: input.value || "",
}));
} catch {
input.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
}
input.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
}
function syncFormFieldsBeforeSubmit(form) {
form.querySelectorAll("#ak-identifier-input, #ak-stage-identification-password, #ak-stage-password-input").forEach((input) => {
dispatchSyntheticInput(input);
});
}
function enhanceSubmitHandoff(root) {
root.querySelectorAll("form").forEach((form) => {
if (form.dataset.nodedcSubmitBound === "true") return;
form.dataset.nodedcSubmitBound = "true";
form.addEventListener("submit", () => {
syncFormFieldsBeforeSubmit(form);
document.body?.classList.add("nodedc-auth-submitting");
}, true);
});
}
function ensureAccessRequestLink(root) {
const input = root.querySelector("#ak-identifier-input, #ak-stage-identification-password, #ak-stage-password-input");
const form = input?.closest("form") || root.querySelector("form");
if (!input || !form || form.querySelector("[data-nodedc-access-request='true']")) return;
const link = document.createElement("a");
link.className = "nodedc-auth-request-access";
link.dataset.nodedcAccessRequest = "true";
link.href = "mailto:dcctouch@gmail.com";
link.textContent = "Запросить доступ";
form.appendChild(link);
}
function normalizeEmail(value) {
return typeof value === "string" ? value.trim().toLowerCase() : "";
}
function getLoginEmail(root) {
return normalizeEmail(root.querySelector("#ak-identifier-input")?.value);
}
function getLoginErrorMessages() {
return [
"Failed to authenticate.",
"Invalid credentials.",
"Invalid password.",
"Incorrect password.",
"Authentication failed.",
"Не удалось выполнить вход.",
"Неверная почта или пароль.",
"Неверный пароль.",
genericLoginError,
revokedLoginError,
];
}
function replaceLoginErrorText(root, message) {
const errorMessages = getLoginErrorMessages();
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const text = node.nodeValue?.trim() || "";
return errorMessages.some((errorMessage) => text.includes(errorMessage))
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT;
},
});
const textNodes = [];
while (walker.nextNode()) {
textNodes.push(walker.currentNode);
}
textNodes.forEach((node) => {
const currentValue = node.nodeValue || "";
const trimmed = currentValue.trim();
if (!trimmed) return;
node.nodeValue = currentValue.replace(trimmed, message);
});
}
function syncLoginErrorReason(root) {
if (!hasAuthError(root)) return;
replaceLoginErrorText(root, genericLoginError);
const email = getLoginEmail(root);
if (!email) return;
const cachedStatus = accountStatusCache.get(email);
if (cachedStatus === "revoked") {
replaceLoginErrorText(root, revokedLoginError);
return;
}
if (cachedStatus === "unknown" || cachedStatus === "loading") return;
accountStatusCache.set(email, "loading");
const statusUrl = new URL("/api/public/login-account-status", getLauncherBaseUrl());
statusUrl.searchParams.set("email", email);
fetch(statusUrl.toString(), { cache: "no-store" })
.then((response) => (response.ok ? response.json() : null))
.then((payload) => {
const status = payload?.status === "revoked" ? "revoked" : "unknown";
accountStatusCache.set(email, status);
if (status !== "revoked") return;
visitRoots(document, (candidateRoot) => {
replaceLoginErrorText(candidateRoot, revokedLoginError);
});
})
.catch(() => {
accountStatusCache.set(email, "unknown");
});
}
function hasAuthError(root) {
const hasErrorElement = root.querySelector(
".pf-c-alert, .pf-m-error, .pf-c-helper-text__item.pf-m-error, [aria-invalid='true']"
);
if (hasErrorElement) return true;
const text = root.textContent || "";
return getLoginErrorMessages().some((message) => text.includes(message));
}
function hasGenericFlowError(root) {
const text = root.textContent || "";
return genericFlowErrorMessages.some((message) => text.includes(message));
}
function hasLoadingOnlyScreen(root) {
const isFlowExecutorRoot = root.host?.tagName?.toLowerCase() === "ak-flow-executor";
const hasProgress =
root.querySelector?.(".pf-c-spinner, .pf-v5-c-spinner, [role='progressbar'], ak-spinner");
const isEmptyFlowExecutor = isFlowExecutorRoot && (root.textContent || "").trim() === "";
const hasInteractiveAuth =
root.querySelector?.("input, textarea, select, button[type='submit'], .pf-c-alert, .pf-v5-c-alert") ||
hasGenericFlowError(root);
return Boolean((hasProgress || isEmptyFlowExecutor) && !hasInteractiveAuth);
}
function syncLoadingState() {
let isLoadingOnly = false;
let hasAuthContent = false;
visitRoots(document, (root) => {
if (hasLoadingOnlyScreen(root)) {
isLoadingOnly = true;
}
if (
root.querySelector?.("input, textarea, select, button[type='submit'], .pf-c-alert, .pf-v5-c-alert") ||
hasGenericFlowError(root)
) {
hasAuthContent = true;
}
});
const shouldShowLoader = isLoadingOnly && !hasAuthContent;
document.body?.classList.toggle("nodedc-auth-loading", shouldShowLoader);
document.body?.classList.toggle("nodedc-auth-card-ready", hasAuthContent);
}
function restoreCardOnErrors(root) {
if (hasAuthError(root)) {
document.body?.classList.remove("nodedc-auth-submitting");
}
}
function isOidcLogoutFlow() {
const path = window.location.pathname;
const params = new URLSearchParams(window.location.search);
return (
path.includes("/end-session/") ||
path.includes("/if/flow/default-invalidation-flow") ||
params.has("post_logout_redirect_uri")
);
}
function getSafePostLogoutRedirect() {
const rawUrl = new URLSearchParams(window.location.search).get("post_logout_redirect_uri");
if (!rawUrl) return null;
try {
const url = new URL(rawUrl);
const allowedHosts = new Set([
"cms.local.nodedc",
"cms.nodedc.ru",
"dc-cms.local.nodedc",
"cms.dcserve.ru",
]);
if (!["http:", "https:"].includes(url.protocol)) return null;
if (!allowedHosts.has(url.hostname) && url.hostname !== new URL(getLauncherBaseUrl()).hostname) return null;
if (!url.pathname.startsWith("/auth/logged-out")) return null;
return url.toString();
} catch {
return null;
}
}
function buildLoggedOutRedirectUrl() {
return new URL("/auth/logged-out", getLauncherBaseUrl()).toString();
}
function redirectGenericFlowError(root) {
if (document.body?.dataset.nodedcFlowErrorRedirected === "true") return;
if (!window.location.pathname.includes("/if/flow/")) return;
if (!hasGenericFlowError(root)) return;
document.body.dataset.nodedcFlowErrorRedirected = "true";
document.body.classList.add("nodedc-auth-submitting");
window.setTimeout(() => {
window.location.replace(getSafePostLogoutRedirect() || buildLoggedOutRedirectUrl());
}, 120);
}
function redirectCompletedLogout(root) {
if (!isOidcLogoutFlow() || document.body?.dataset.nodedcLogoutRedirected === "true") return;
const redirectUrl = getSafePostLogoutRedirect();
if (!redirectUrl) return;
const isInvalidationFlow = window.location.pathname.includes("/if/flow/default-invalidation-flow");
const text = root.textContent || "";
const logoutComplete = [
"Logout successful",
"You've logged out",
"You have been logged out",
"Logged out",
"Вы вышли",
"Выход выполнен",
].some((message) => text.includes(message));
if (!logoutComplete && !isInvalidationFlow) return;
document.body.dataset.nodedcLogoutRedirected = "true";
document.body.classList.add("nodedc-auth-submitting");
window.setTimeout(() => {
window.location.replace(redirectUrl);
}, isInvalidationFlow ? 1600 : 150);
}
let scheduled = false;
function enhanceAuthFields() {
scheduled = false;
ensureLogo();
visitRoots(document, (root) => {
enhanceIdentifierField(root);
normalizePasswordFields(root);
enhanceSubmitHandoff(root);
ensureAccessRequestLink(root);
translateAuthText(root);
syncLoginErrorReason(root);
applyPermissionDeniedLayout(root);
hidePermissionDeniedReason(root);
restoreCardOnErrors(root);
redirectCompletedLogout(root);
redirectGenericFlowError(root);
});
syncPermissionDeniedState();
syncLoadingState();
}
function scheduleEnhancement() {
if (scheduled) return;
scheduled = true;
window.requestAnimationFrame(enhanceAuthFields);
}
window.addEventListener("DOMContentLoaded", scheduleEnhancement);
window.addEventListener("load", scheduleEnhancement);
document.addEventListener("pointerdown", (event) => {
const target = event.composedPath?.()[0] || event.target;
maskVisiblePasswords(target);
}, true);
window.addEventListener("blur", () => maskVisiblePasswords(null));
new MutationObserver(scheduleEnhancement).observe(document.documentElement, {
childList: true,
subtree: true,
});
})();
</script>
{% endif %}
{% endif %}
{% endwith %}
@@ -0,0 +1,50 @@
{% load static %}
{% load i18n %}
{% load authentik_core %}
{% get_current_language as LANGUAGE_CODE %}
<!DOCTYPE html>
<html
lang="{{ LANGUAGE_CODE }}"
data-theme="{% if ui_theme == "dark" %}dark{% else %}light{% endif %}"
data-theme-choice="{% if ui_theme == "dark" %}dark{% elif ui_theme == "light" %}light{% else %}auto{% endif %}"
>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
{# Darkreader breaks the site regardless of theme as its not compatible with webcomponents, and we default to a dark theme based on preferred colour-scheme #}
<meta name="darkreader-lock">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
{% block head_before %}
{% endblock %}
{% include "base/theme.html" %}
{% with request_host=request.get_host %}
{% if request_host|slice:":11" != "auth-admin." and request_host|slice:":15" != "cms-auth-admin." and request_host|slice:":9" != "id-admin." and request_host|slice:":12" != "172.22.0.222" %}
{% if request.path|slice:":9" == "/if/flow/" or request.path|slice:":7" == "/flows/" or request.path|slice:":7" == "/login/" %}
<style data-id="brand-css">{{ brand_css }}</style>
{% else %}
<style data-id="brand-css"></style>
{% endif %}
{% else %}
<style data-id="brand-css"></style>
{% endif %}
{% endwith %}
<script src="{% versioned_script 'dist/poly-%v.js' %}" type="module"></script>
{% block head %}
{% endblock %}
{% for key, value in html_meta.items %}
<meta name="{{key}}" content="{{ value }}" />
{% endfor %}
</head>
<body>
{% block body %}
{% endblock %}
{% block scripts %}
{% endblock %}
</body>
</html>
@@ -0,0 +1,737 @@
:root,
:host {
--ak-global--primary: #f4f4f5;
--ak-global--primary-hover: #ffffff;
--ak-global--background: #0e0f10;
--ak-global--background-image: none !important;
--ak-c-login--MaxWidth: 28rem;
--ak-c-login--spacer: 2.2rem;
--ak-c-login__main--BackgroundColor: transparent;
--ak-c-login__main--Color: #f3f3f3;
--ak-c-login__main--BoxShadow: none;
--ak-c-login__footer--PaddingBlock: 0;
--pf-global--primary-color--100: #f4f4f5;
--pf-global--primary-color--200: #ffffff;
--pf-v4-global--palette--blue-300: #f4f4f5;
--ak-global--palette--blue-300: #f4f4f5;
--pf-global--active-color--100: #f4f4f5;
--pf-global--link--Color: #f4f4f5;
--pf-global--link--Color--hover: #ffffff;
--pf-global--BackgroundColor--100: #0e0f10;
--pf-global--BackgroundColor--light-100: transparent;
--pf-global--Color--100: #f3f3f3;
--pf-global--Color--200: #9d9da3;
--pf-global--BorderRadius--lg: 1.9rem;
--pf-global--BorderRadius--sm: 1.15rem;
--pf-v5-global--BorderRadius--lg: 1.9rem;
--pf-v5-global--BorderRadius--sm: 1.15rem;
--pf-v6-global--BorderRadius--large: 1.9rem;
--pf-c-form-control--BackgroundColor: rgba(255, 255, 255, 0.04);
--pf-c-form-control--BorderColor: transparent;
--pf-c-form-control--BorderBottomColor: transparent;
--pf-v5-c-form-control--BackgroundColor: rgba(255, 255, 255, 0.04);
--pf-v5-c-form-control--BorderColor: transparent;
--pf-v5-c-form-control--BorderBottomColor: transparent;
--nodedc-auth-bg: #0e0f10;
--nodedc-auth-card-bg: rgba(9, 9, 12, 0.84);
--nodedc-auth-primary: #f4f4f5;
--nodedc-auth-primary-hover: #ffffff;
--nodedc-auth-on-primary: rgb(11, 17, 23);
--nodedc-auth-text-primary: #e4e6e7;
--nodedc-auth-text-secondary: #cacdce;
--nodedc-auth-text-placeholder: #959a9d;
color-scheme: dark;
}
html,
body,
.pf-c-login {
background: var(--nodedc-auth-bg) !important;
color: var(--nodedc-auth-text-primary) !important;
font-family: Inter, "Inter var", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
font-synthesis: none;
-webkit-font-smoothing: antialiased;
text-rendering: geometricPrecision;
}
:host {
background: transparent !important;
color: inherit !important;
font-family: Inter, "Inter var", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
font-synthesis: none;
-webkit-font-smoothing: antialiased;
text-rendering: geometricPrecision;
}
:host(.pf-c-login) {
display: flex !important;
min-width: 100vw !important;
min-height: 100vh !important;
align-items: center !important;
justify-content: center !important;
background: var(--nodedc-auth-bg) !important;
color: var(--nodedc-auth-text-primary) !important;
}
body::before,
.pf-c-login::before {
background: none !important;
}
body:not(.nodedc-auth-enhanced)::after {
content: "";
display: block;
position: fixed;
top: 1.7875rem;
left: calc(1.9375rem - 2px);
width: 7.25rem;
height: 1.79rem;
background: center / contain no-repeat url("data:image/svg+xml,%3Csvg id='nodedc-logo' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220.82 54.55'%3E%3Cpath fill='%23e2e1e1' d='M52.8 23.61 46.92 33.76 41.05 23.61H52.8m18-10.39H23.06l23.86 41.33Z'/%3E%3Cpolygon fill='%23e2e1e1' points='31.28 33.13 18.11 10.34 75.73 10.34 62.59 33.13 74.28 33.13 93.22 0 0 0 19.61 33.13 31.28 33.13'/%3E%3Cpath fill='%23dbdbdb' d='M116.35 18.49V1h1.27l10.34 15V1h1.33v17.49H128l-10.34-15v15Zm24.08.15c-4.79 0-8.16-3.72-8.16-8.89S135.64.86 140.43.86s8.17 3.72 8.17 8.89-3.35 8.89-8.17 8.89Zm0-1.25c4 0 6.79-3.17 6.79-7.64s-2.77-7.64-6.79-7.64-6.77 3.17-6.77 7.64 2.78 7.64 6.77 7.64ZM151.6 18.49V1h5.1c5.54 0 8.79 3.42 8.79 8.74s-3.25 8.74-8.79 8.74Zm1.4-1.25h3.75c4.77 0 7.42-2.92 7.42-7.49s-2.65-7.49-7.42-7.49H153ZM168.49 1h10.77v1.26h-9.42v6.67h7.89v1.25h-7.89v7.06h9.74v1.25h-11.09Zm20.39 17.49V1H194c5.54 0 8.79 3.42 8.79 8.74s-3.25 8.74-8.79 8.74Zm1.35-1.25H194c4.77 0 7.41-2.92 7.41-7.49S198.75 2.26 194 2.26h-3.75Zm14.92-7.49c0-5.24 3.19-8.89 8.11-8.89a6.8 6.8 0 0 1 7.1 5.52h-1.43a5.54 5.54 0 0 0-5.74-4.27c-4.05 0-6.64 3.17-6.64 7.64s2.54 7.64 6.59 7.64a5.46 5.46 0 0 0 5.74-4.29h1.43c-.75 3.52-3.4 5.54-7.15 5.54-4.89 0-8.01-3.59-8.01-8.89Z'/%3E%3C/svg%3E");
image-rendering: auto;
z-index: 20;
pointer-events: none;
}
.nodedc-auth-logo {
position: fixed;
display: block;
top: 1.7875rem;
left: calc(1.9375rem - 2px);
width: 7.25rem;
height: 1.79rem;
z-index: 20;
line-height: 0;
text-decoration: none;
pointer-events: auto;
color: var(--nodedc-auth-text-primary);
}
.nodedc-auth-logo svg {
display: block;
width: 100%;
height: 100%;
overflow: visible;
shape-rendering: geometricPrecision;
text-rendering: geometricPrecision;
}
.pf-c-page__drawer,
.pf-c-drawer,
.pf-c-drawer__main,
.pf-c-drawer__content,
.pf-c-drawer__body {
background: transparent !important;
}
.pf-c-drawer__body {
display: flex !important;
min-height: 100vh !important;
align-items: center !important;
justify-content: center !important;
padding: 0 !important;
}
ak-flow-executor.pf-c-login::before,
:host(.pf-c-login)::before {
display: none !important;
}
ak-flow-executor.pf-c-login {
display: flex !important;
width: 100% !important;
min-height: 100vh !important;
position: relative !important;
align-items: center !important;
justify-content: center !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
ak-brand-links,
ak-flow-inspector,
ak-locale-context,
ak-locale-switcher,
ak-locale-select,
ak-flow-executor [slot="footer"],
.pf-c-login__footer,
.pf-c-login__header,
.pf-c-login__main-header.pf-c-brand,
.pf-c-form > p:first-child {
display: none !important;
}
ak-flow-executor::part(locale-select),
ak-flow-executor::part(footer),
ak-flow-executor::part(branding) {
display: none !important;
}
ak-flow-executor::part(main),
.pf-c-login__main {
position: fixed !important;
top: 50% !important;
left: 50% !important;
right: auto !important;
transform: translate(-50%, -50%) !important;
width: min(100%, 28rem) !important;
max-width: 28rem !important;
min-height: 0 !important;
margin: 0 !important;
padding: 2.2rem !important;
box-sizing: border-box !important;
overflow: hidden !important;
border: 0 !important;
outline: none !important;
box-shadow: none !important;
border-radius: 1.9rem !important;
background: transparent !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
justify-content: flex-start !important;
}
body.nodedc-auth-card-ready ak-flow-executor::part(main),
body.nodedc-auth-card-ready .pf-c-login__main {
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.048) 0%, rgba(255, 255, 255, 0.015) 100%),
var(--nodedc-auth-card-bg) !important;
-webkit-backdrop-filter: blur(40px) !important;
backdrop-filter: blur(40px) !important;
}
ak-flow-card,
ak-stage-identification,
ak-stage-password {
display: block !important;
width: 100% !important;
background: transparent !important;
}
.pf-c-login__main-body,
.pf-c-form,
.pf-c-form__group,
.pf-c-form__group-control,
.pf-c-input-group,
.pf-c-input-group__item,
.pf-c-card,
.pf-c-card__body,
.pf-c-empty-state,
.pf-c-empty-state__content,
.pf-c-empty-state__body,
.pf-v5-c-empty-state,
.pf-v5-c-empty-state__content,
.pf-v5-c-empty-state__body,
.pf-v6-c-empty-state,
.pf-v6-c-empty-state__content,
.pf-v6-c-empty-state__body {
background: transparent !important;
box-shadow: none !important;
border: 0 !important;
}
.pf-c-login__main-header {
display: block !important;
width: 100% !important;
min-width: 0 !important;
padding: 0 0 1.75rem !important;
}
.pf-c-login__main-header .pf-c-title,
.pf-c-title.pf-m-3xl,
h1.pf-c-title {
display: block !important;
width: 100% !important;
max-width: 22rem !important;
margin: 0 !important;
color: var(--nodedc-auth-text-primary) !important;
font-size: 2rem !important;
line-height: 2.5rem !important;
font-weight: 650 !important;
letter-spacing: -0.045em !important;
text-wrap: balance !important;
white-space: normal !important;
word-break: normal !important;
overflow-wrap: normal !important;
hyphens: none !important;
overflow: visible !important;
line-clamp: unset !important;
-webkit-line-clamp: unset !important;
-webkit-box-orient: initial !important;
text-transform: none !important;
}
.pf-c-login__main-header::after {
content: "Вход в панель управления сайтами.";
display: block;
max-width: 22rem;
margin-top: 0.75rem;
color: var(--nodedc-auth-text-placeholder);
font-size: 1.9rem;
line-height: 2.5rem;
font-weight: 650;
letter-spacing: -0.045em;
text-transform: none;
}
.pf-c-login__main-header[data-nodedc-permission-denied="true"]::after,
body.nodedc-auth-permission-denied .pf-c-login__main-header::after {
content: attr(data-nodedc-denied-subtitle) !important;
margin-top: 0;
}
body.nodedc-auth-permission-denied .pf-c-login__main-header::after {
content: "Доступ запрещён." !important;
}
.pf-c-login__main-header[data-nodedc-permission-denied="true"] .pf-c-title,
.pf-c-login__main-header[data-nodedc-permission-denied="true"] .pf-c-title.pf-m-3xl,
.pf-c-login__main-header[data-nodedc-permission-denied="true"] h1.pf-c-title,
body.nodedc-auth-permission-denied .pf-c-login__main-header .pf-c-title,
body.nodedc-auth-permission-denied .pf-c-title.pf-m-3xl,
body.nodedc-auth-permission-denied h1.pf-c-title {
display: none !important;
}
body.nodedc-auth-permission-denied ak-flow-executor::part(main),
body.nodedc-auth-permission-denied .pf-c-login__main {
background: transparent !important;
border: 0 !important;
outline: 0 !important;
box-shadow: none !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
}
.pf-c-login__main-body {
padding: 0 !important;
}
.pf-c-form {
display: flex !important;
flex-direction: column !important;
gap: 1.05rem !important;
}
.pf-c-form__group,
ak-flow-input-password.pf-c-form__group {
margin: 0 !important;
}
.pf-c-form__label,
.pf-c-form__label-text,
label {
color: var(--nodedc-auth-text-placeholder) !important;
font-size: 0.75rem !important;
line-height: 1rem !important;
font-weight: 400 !important;
letter-spacing: -0.01em !important;
text-transform: none !important;
}
.pf-c-form__label-required {
display: none !important;
}
label[for="ak-identifier-input"],
label[for="ak-stage-identification-password"] {
font-size: 0 !important;
}
label[for="ak-identifier-input"] .pf-c-form__label-text,
label[for="ak-identifier-input"] .pf-c-form__label-required,
label[for="ak-stage-identification-password"] .pf-c-form__label-text,
label[for="ak-stage-identification-password"] .pf-c-form__label-required {
display: none !important;
}
label[for="ak-identifier-input"]::after {
content: "Эл. почта";
font-size: 0.75rem;
color: var(--nodedc-auth-text-placeholder);
font-weight: 400;
}
label[for="ak-stage-identification-password"]::after {
content: "Пароль";
font-size: 0.75rem;
color: var(--nodedc-auth-text-placeholder);
font-weight: 400;
}
.pf-c-form-control,
.pf-c-input-group .pf-c-form-control,
input.pf-c-form-control {
width: 100% !important;
min-height: 3rem !important;
padding: 0 1rem !important;
border: 0 !important;
border-bottom: 0 !important;
outline: none !important;
box-shadow: none !important;
border-radius: 1.15rem !important;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%),
rgba(255, 255, 255, 0.03) !important;
-webkit-backdrop-filter: blur(18px);
backdrop-filter: blur(18px);
color: var(--nodedc-auth-text-primary) !important;
font-size: 0.875rem !important;
font-style: normal !important;
font-weight: 400 !important;
text-transform: none !important;
caret-color: var(--nodedc-auth-primary) !important;
}
.pf-c-form-control::placeholder,
input.pf-c-form-control::placeholder {
color: var(--nodedc-auth-text-placeholder) !important;
font-style: normal !important;
opacity: 1 !important;
}
.pf-c-form-control:focus,
input.pf-c-form-control:focus {
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%),
rgba(255, 255, 255, 0.03) !important;
border: 0 !important;
border-bottom: 0 !important;
outline: none !important;
box-shadow: none !important;
}
.pf-c-form-control:-webkit-autofill,
.pf-c-form-control:-webkit-autofill:hover,
.pf-c-form-control:-webkit-autofill:focus,
input.pf-c-form-control:-webkit-autofill,
input.pf-c-form-control:-webkit-autofill:hover,
input.pf-c-form-control:-webkit-autofill:focus {
border: 0 !important;
outline: none !important;
-webkit-text-fill-color: var(--nodedc-auth-text-primary) !important;
caret-color: var(--nodedc-auth-primary) !important;
transition: background-color 999999s ease-in-out 0s !important;
box-shadow: inset 0 0 0 1000px rgba(255, 255, 255, 0.03) !important;
-webkit-box-shadow: inset 0 0 0 1000px rgba(255, 255, 255, 0.03) !important;
}
.pf-c-input-group {
position: relative !important;
display: block !important;
border: 0 !important;
box-shadow: none !important;
}
.pf-c-form__group:has(#ak-identifier-input) {
position: relative !important;
}
.pf-c-input-group .pf-c-button.pf-m-control {
position: absolute !important;
top: 0.5rem !important;
right: 0.75rem !important;
display: grid !important;
width: 2rem !important;
min-width: 2rem !important;
height: 2rem !important;
place-items: center !important;
padding: 0 !important;
border: 0 !important;
border-radius: 999px !important;
background: transparent !important;
color: var(--nodedc-auth-text-placeholder) !important;
box-shadow: none !important;
text-decoration: none !important;
appearance: none !important;
-webkit-appearance: none !important;
}
.pf-c-input-group .pf-c-button.pf-m-control::before,
.pf-c-input-group .pf-c-button.pf-m-control::after,
.pf-c-input-group .pf-c-button.pf-m-control:hover::before,
.pf-c-input-group .pf-c-button.pf-m-control:hover::after,
.pf-c-input-group .pf-c-button.pf-m-control:focus::before,
.pf-c-input-group .pf-c-button.pf-m-control:focus::after {
display: none !important;
border: 0 !important;
box-shadow: none !important;
content: none !important;
}
.pf-c-input-group .pf-c-button.pf-m-control:hover,
.pf-c-input-group .pf-c-button.pf-m-control:focus,
.pf-c-input-group .pf-c-button.pf-m-control:active {
border: 0 !important;
outline: none !important;
box-shadow: none !important;
background: transparent !important;
text-decoration: none !important;
}
.pf-c-input-group .pf-c-button.pf-m-control i,
.nodedc-auth-clear-input svg {
width: 1.25rem !important;
height: 1.25rem !important;
color: var(--nodedc-auth-text-placeholder) !important;
opacity: 0.9 !important;
}
.pf-c-input-group input.pf-c-form-control {
padding-right: 3rem !important;
}
.nodedc-auth-clear-input {
position: absolute !important;
right: 0.75rem !important;
bottom: 0.5rem !important;
display: none !important;
width: 2rem !important;
height: 2rem !important;
padding: 0 !important;
place-items: center !important;
border: 0 !important;
border-radius: 999px !important;
background: transparent !important;
color: var(--nodedc-auth-text-placeholder) !important;
box-shadow: none !important;
cursor: pointer !important;
}
.nodedc-auth-clear-input[data-visible="true"] {
display: grid !important;
}
.pf-c-button.pf-m-primary,
button.pf-m-primary,
button[type="submit"],
.pf-c-form .pf-c-button.pf-m-primary {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 100% !important;
min-height: 3.25rem !important;
margin-top: 1.25rem !important;
border: 0 !important;
outline: none !important;
box-shadow: none !important;
border-radius: 1.25rem !important;
background: var(--nodedc-auth-primary) !important;
color: var(--nodedc-auth-on-primary) !important;
font-size: 0.95rem !important;
font-weight: 600 !important;
line-height: 1 !important;
letter-spacing: -0.02em !important;
text-align: center !important;
text-transform: none !important;
}
.pf-c-button.pf-m-primary:hover,
button.pf-m-primary:hover,
button[type="submit"]:hover {
background: var(--nodedc-auth-primary-hover) !important;
color: var(--nodedc-auth-on-primary) !important;
}
.nodedc-auth-request-access {
display: inline-flex !important;
justify-content: center !important;
width: 100% !important;
margin-top: -0.35rem !important;
color: var(--nodedc-auth-primary) !important;
font-size: 0.75rem !important;
line-height: 1rem !important;
font-weight: 650 !important;
letter-spacing: -0.01em !important;
text-decoration: none !important;
}
.nodedc-auth-request-access:hover,
.nodedc-auth-request-access:focus {
color: var(--nodedc-auth-primary-hover) !important;
text-decoration: none !important;
}
.pf-c-alert,
.pf-c-alert.pf-m-inline {
border: 0 !important;
border-radius: 1rem !important;
margin: 0 0 1.15rem !important;
padding: 0 !important;
background: transparent !important;
color: var(--nodedc-auth-primary) !important;
box-shadow: none !important;
}
.pf-c-alert__icon,
.pf-c-alert__title,
.pf-c-alert__description {
color: var(--nodedc-auth-primary) !important;
font-size: 0.75rem !important;
line-height: 1rem !important;
font-weight: 600 !important;
}
body.nodedc-auth-permission-denied .pf-c-alert,
body.nodedc-auth-permission-denied .pf-v5-c-alert,
body.nodedc-auth-permission-denied .pf-c-alert.pf-m-inline {
display: none !important;
}
a,
.pf-c-button.pf-m-link,
button.pf-m-link {
color: var(--nodedc-auth-primary) !important;
font-size: 0.75rem !important;
line-height: 1rem !important;
font-weight: 600 !important;
text-decoration: none !important;
}
body.nodedc-auth-submitting ak-flow-executor::part(main),
body.nodedc-auth-loading ak-flow-executor::part(main),
body.nodedc-auth-submitting .pf-c-login__main,
body.nodedc-auth-loading .pf-c-login__main,
body.nodedc-auth-submitting .pf-c-empty-state,
body.nodedc-auth-loading .pf-c-empty-state,
body.nodedc-auth-submitting .pf-c-spinner,
body.nodedc-auth-loading .pf-c-spinner,
body.nodedc-auth-submitting [role="progressbar"] {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
body.nodedc-auth-loading [role="progressbar"] {
opacity: 0 !important;
visibility: hidden !important;
pointer-events: none !important;
}
.pf-c-spinner,
.pf-v5-c-spinner,
[role="progressbar"],
ak-spinner {
--pf-c-spinner--Color: var(--nodedc-auth-primary) !important;
--pf-v5-c-spinner--Color: var(--nodedc-auth-primary) !important;
--pf-c-spinner__clipper--after--BoxShadowColor: var(--nodedc-auth-primary) !important;
--pf-c-spinner__lead-ball--after--BackgroundColor: var(--nodedc-auth-primary) !important;
--pf-c-spinner__tail-ball--after--BackgroundColor: var(--nodedc-auth-primary) !important;
color: var(--nodedc-auth-primary) !important;
}
#ak-placeholder.ak-c-placeholder,
.ak-c-placeholder[slot="placeholder"] {
position: fixed !important;
top: 50% !important;
left: 50% !important;
display: block !important;
width: 2.5rem !important;
height: 2.5rem !important;
min-width: 2.5rem !important;
min-height: 2.5rem !important;
margin: 0 !important;
padding: 0 !important;
border: 0 !important;
border-radius: 999px !important;
background: transparent !important;
box-shadow: none !important;
opacity: 1 !important;
visibility: visible !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
transform: translate(-50%, -50%) !important;
overflow: visible !important;
}
#ak-placeholder.ak-c-placeholder .pf-c-spinner,
.ak-c-placeholder[slot="placeholder"] .pf-c-spinner,
#ak-placeholder.ak-c-placeholder [role="progressbar"],
.ak-c-placeholder[slot="placeholder"] [role="progressbar"] {
opacity: 0 !important;
visibility: hidden !important;
}
#ak-placeholder.ak-c-placeholder::before,
.ak-c-placeholder[slot="placeholder"]::before {
content: "";
position: absolute;
inset: 0;
border: 2px solid rgba(255, 255, 255, 0.22);
border-top-color: var(--nodedc-auth-primary);
border-radius: 999px;
animation: nodedc-auth-placeholder-spin 0.72s linear infinite;
pointer-events: none;
}
body.nodedc-auth-submitting::before,
body.nodedc-auth-loading::before {
content: "";
position: fixed;
top: 50%;
left: 50%;
z-index: 1200;
width: 2.5rem;
height: 2.5rem;
border: 2px solid rgba(255, 255, 255, 0.22);
border-top-color: var(--nodedc-auth-primary);
border-radius: 999px;
transform: translate(-50%, -50%);
animation: nodedc-auth-loader-spin 0.72s linear infinite;
pointer-events: none;
}
@keyframes nodedc-auth-loader-spin {
from {
transform: translate(-50%, -50%) rotate(0deg);
}
to {
transform: translate(-50%, -50%) rotate(360deg);
}
}
@keyframes nodedc-auth-placeholder-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
ak-library-application,
ak-application-wizard,
ak-user-interface,
.pf-c-page__sidebar,
.pf-c-page__header,
.pf-c-nav,
.pf-c-brand {
color-scheme: dark;
}
@media (max-width: 640px) {
ak-flow-executor.pf-c-login::before,
:host(.pf-c-login)::before {
display: none !important;
}
body::after,
.nodedc-auth-logo {
top: 1.7875rem;
left: calc(1.9375rem - 2px);
width: 7.25rem;
height: 1.79rem;
}
ak-flow-executor::part(main),
.pf-c-login__main {
position: fixed !important;
top: 50% !important;
right: auto !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
width: calc(100vw - 2rem) !important;
padding: 1.5rem !important;
}
}
@@ -0,0 +1,16 @@
{% extends "base/skeleton.html" %}
{% load authentik_core %}
{% block head %}
<script src="{% versioned_script 'dist/admin/AdminInterface-%v.js' %}" type="module"></script>
{% include "base/header_js.html" %}
{% endblock %}
{% block body %}
<ak-skip-to-content></ak-skip-to-content>
<ak-message-container alignment="bottom"></ak-message-container>
<ak-interface-admin>
{% include "base/placeholder.html" %}
</ak-interface-admin>
{% endblock %}