fix(platform): isolate authentik admin styling

This commit is contained in:
Codex
2026-05-25 01:08:50 +03:00
parent e9def6672c
commit c1ac8b1a10
16 changed files with 670 additions and 61 deletions
@@ -21,8 +21,38 @@
})();
</script>
{% with request_host=request.get_host %}
{% if request_host|slice:":11" != "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("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">
:root {
html.nodedc-auth-flow-page {
--ak-global--primary: rgb(195, 255, 102) !important;
--ak-global--background: #0e0f10 !important;
--ak-global--background-image: none !important;
@@ -37,22 +67,22 @@
--nodedc-auth-bg: #0e0f10 !important;
}
html,
body,
html body .pf-c-login {
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 body ak-flow-executor::part(main),
html body .pf-c-login__main,
html body .pf-c-empty-state,
html body .pf-v5-c-empty-state,
html body .pf-v6-c-empty-state,
html body .pf-c-card,
html body .pf-v5-c-card,
html body .pf-v6-c-card,
html body [class*="empty-state"] {
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;
@@ -60,11 +90,11 @@
backdrop-filter: none !important;
}
html body .pf-c-spinner,
html body .pf-v5-c-spinner,
html body .pf-v6-c-spinner,
html body [role="progressbar"],
html body ak-spinner {
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: rgb(195, 255, 102) !important;
--pf-v5-c-spinner--Color: rgb(195, 255, 102) !important;
--pf-v6-c-spinner--Color: rgb(195, 255, 102) !important;
@@ -74,8 +104,8 @@
color: rgb(195, 255, 102) !important;
}
html body #ak-placeholder.ak-c-placeholder,
html body .ak-c-placeholder[slot="placeholder"] {
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;
@@ -98,16 +128,16 @@
overflow: visible !important;
}
html body #ak-placeholder.ak-c-placeholder .pf-c-spinner,
html body .ak-c-placeholder[slot="placeholder"] .pf-c-spinner,
html body #ak-placeholder.ak-c-placeholder [role="progressbar"],
html body .ak-c-placeholder[slot="placeholder"] [role="progressbar"] {
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 body #ak-placeholder.ak-c-placeholder::before,
html body .ak-c-placeholder[slot="placeholder"]::before {
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;
@@ -118,14 +148,14 @@
pointer-events: none;
}
html body.nodedc-auth-loading ak-flow-executor::part(main),
html body.nodedc-auth-loading .pf-c-login__main {
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 body.nodedc-auth-loading::before {
html.nodedc-auth-flow-page body.nodedc-auth-loading::before {
content: "";
position: fixed;
top: 50%;
@@ -141,7 +171,7 @@
pointer-events: none;
}
html body.nodedc-auth-card-ready::before {
html.nodedc-auth-flow-page body.nodedc-auth-card-ready::before {
display: none !important;
}
@@ -165,14 +195,38 @@
}
}
</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: JSON.parse('{{ brand_json|escapejs }}' || "{}"),
brand: authentikBrand,
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",
@@ -197,10 +251,31 @@
});
</script>
{% with request_host=request.get_host %}
{% if request_host|slice:":11" != "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("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>
@@ -254,9 +329,11 @@
["Continue", "Продолжить"],
["Show password", "Показать пароль"],
["Hide password", "Скрыть пароль"],
["Permission denied", "Доступ ограничен"],
["Permission denied", "Доступ запрещён"],
["Not you?", "Сменить пользователя"],
["Request has been denied.", "Доступ к модулю ограничен."],
["Request has been denied.", "Доступ запрещён."],
["The request has been denied.", "Доступ запрещён."],
["В произведении запроса было отказано.", "Доступ запрещён."],
["Go home", "Вернуться назад"],
["Response returned an error code", "Не удалось завершить операцию."],
]);
@@ -366,7 +443,11 @@
denied = [
"Permission denied",
"Доступ ограничен",
"Доступ запрещен",
"Доступ запрещён",
"Request has been denied.",
"The request has been denied.",
"В произведении запроса было отказано.",
"Доступ к модулю ограничен.",
].some((message) => text.includes(message));
});
@@ -387,8 +468,8 @@
}
function applyPermissionDeniedLayout(root) {
const deniedTitleMessages = ["Permission denied", "Доступ ограничен"];
const deniedSubtitle = "Доступ к модулю NODE.DC ограничен.";
const deniedTitleMessages = ["Permission denied", "Доступ ограничен", "Доступ запрещен", "Доступ запрещён"];
const deniedSubtitle = "Доступ запрещён.";
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode(node) {
const text = node.nodeValue?.trim() || "";
@@ -426,7 +507,19 @@
}
function hidePermissionDeniedReason(root) {
const deniedMessages = ["Request has been denied.", "Доступ к модулю ограничен."];
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 || "";
@@ -547,12 +640,35 @@
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);
});
@@ -823,3 +939,6 @@
});
})();
</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:":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>
@@ -280,7 +280,7 @@ body.nodedc-auth-permission-denied .pf-c-login__main-header::after {
}
body.nodedc-auth-permission-denied .pf-c-login__main-header::after {
content: "Доступ к модулю NODE.DC ограничен." !important;
content: "Доступ запрещён." !important;
}
.pf-c-login__main-header[data-nodedc-permission-denied="true"] .pf-c-title,
@@ -292,6 +292,16 @@ 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;
}
@@ -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 %}