core: pre-hydrate config into templates to directly load correct assets
closes #3228 Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
d4af47f576
commit
cada292e00
|
@ -68,10 +68,9 @@ class ConfigView(APIView):
|
||||||
caps.append(Capabilities.CAN_IMPERSONATE)
|
caps.append(Capabilities.CAN_IMPERSONATE)
|
||||||
return caps
|
return caps
|
||||||
|
|
||||||
@extend_schema(responses={200: ConfigSerializer(many=False)})
|
def get_config(self) -> ConfigSerializer:
|
||||||
def get(self, request: Request) -> Response:
|
"""Get Config"""
|
||||||
"""Retrieve public configuration options"""
|
return ConfigSerializer(
|
||||||
config = ConfigSerializer(
|
|
||||||
{
|
{
|
||||||
"error_reporting": {
|
"error_reporting": {
|
||||||
"enabled": CONFIG.y("error_reporting.enabled"),
|
"enabled": CONFIG.y("error_reporting.enabled"),
|
||||||
|
@ -86,4 +85,8 @@ class ConfigView(APIView):
|
||||||
"cache_timeout_reputation": int(CONFIG.y("redis.cache_timeout_reputation")),
|
"cache_timeout_reputation": int(CONFIG.y("redis.cache_timeout_reputation")),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return Response(config.data)
|
|
||||||
|
@extend_schema(responses={200: ConfigSerializer(many=False)})
|
||||||
|
def get(self, request: Request) -> Response:
|
||||||
|
"""Retrieve public configuration options"""
|
||||||
|
return Response(self.get_config().data)
|
||||||
|
|
|
@ -7,6 +7,12 @@
|
||||||
<script src="{% static 'dist/admin/AdminInterface.js' %}" type="module"></script>
|
<script src="{% static 'dist/admin/AdminInterface.js' %}" type="module"></script>
|
||||||
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
|
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
|
||||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
|
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
|
||||||
|
<script>
|
||||||
|
window.authentik = {};
|
||||||
|
window.authentik.locale = "{{ tenant.default_locale }}";
|
||||||
|
window.authentik.config = JSON.parse('{{ config_json|safe }}');
|
||||||
|
window.authentik.tenant = JSON.parse('{{ tenant_json|safe }}');
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
|
@ -10,9 +10,10 @@
|
||||||
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
<script>ShadyDOM = { force: !navigator.webdriver };</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script>
|
<script>
|
||||||
window.authentik = {
|
window.authentik = {};
|
||||||
"locale": "{{ tenant.default_locale }}",
|
window.authentik.locale = "{{ tenant.default_locale }}";
|
||||||
};
|
window.authentik.config = JSON.parse( '{{ config_json|safe }}');
|
||||||
|
window.authentik.tenant = JSON.parse('{{ tenant_json|safe }}');
|
||||||
window.authentik.flow = {
|
window.authentik.flow = {
|
||||||
"layout": "{{ flow.layout }}",
|
"layout": "{{ flow.layout }}",
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,12 @@
|
||||||
<script src="{% static 'dist/user/UserInterface.js' %}" type="module"></script>
|
<script src="{% static 'dist/user/UserInterface.js' %}" type="module"></script>
|
||||||
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: light)">
|
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: light)">
|
||||||
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: dark)">
|
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: dark)">
|
||||||
|
<script>
|
||||||
|
window.authentik = {};
|
||||||
|
window.authentik.locale = "{{ tenant.default_locale }}";
|
||||||
|
window.authentik.config = JSON.parse('{{ config_json|safe }}');
|
||||||
|
window.authentik.tenant = JSON.parse('{{ tenant_json|safe }}');
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
|
@ -4,11 +4,10 @@ from django.contrib.auth.decorators import login_required
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
from django.views.generic.base import TemplateView
|
|
||||||
|
|
||||||
from authentik.core.views import apps, impersonate
|
from authentik.core.views import apps, impersonate
|
||||||
from authentik.core.views.debug import AccessDeniedView
|
from authentik.core.views.debug import AccessDeniedView
|
||||||
from authentik.core.views.interface import FlowInterfaceView
|
from authentik.core.views.interface import FlowInterfaceView, InterfaceView
|
||||||
from authentik.core.views.session import EndSessionView
|
from authentik.core.views.session import EndSessionView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -39,12 +38,12 @@ urlpatterns = [
|
||||||
# Interfaces
|
# Interfaces
|
||||||
path(
|
path(
|
||||||
"if/admin/",
|
"if/admin/",
|
||||||
ensure_csrf_cookie(TemplateView.as_view(template_name="if/admin.html")),
|
ensure_csrf_cookie(InterfaceView.as_view(template_name="if/admin.html")),
|
||||||
name="if-admin",
|
name="if-admin",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"if/user/",
|
"if/user/",
|
||||||
ensure_csrf_cookie(TemplateView.as_view(template_name="if/user.html")),
|
ensure_csrf_cookie(InterfaceView.as_view(template_name="if/user.html")),
|
||||||
name="if-user",
|
name="if-user",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
|
@ -58,10 +57,10 @@ urlpatterns = [
|
||||||
name="if-session-end",
|
name="if-session-end",
|
||||||
),
|
),
|
||||||
# Fallback for WS
|
# Fallback for WS
|
||||||
path("ws/outpost/<uuid:pk>/", TemplateView.as_view(template_name="if/admin.html")),
|
path("ws/outpost/<uuid:pk>/", InterfaceView.as_view(template_name="if/admin.html")),
|
||||||
path(
|
path(
|
||||||
"ws/client/",
|
"ws/client/",
|
||||||
TemplateView.as_view(template_name="if/admin.html"),
|
InterfaceView.as_view(template_name="if/admin.html"),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,26 @@
|
||||||
"""Interface views"""
|
"""Interface views"""
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
from rest_framework.request import Request
|
||||||
|
|
||||||
|
from authentik.api.v3.config import ConfigView
|
||||||
from authentik.flows.models import Flow
|
from authentik.flows.models import Flow
|
||||||
|
from authentik.tenants.api import CurrentTenantSerializer
|
||||||
|
|
||||||
|
|
||||||
class FlowInterfaceView(TemplateView):
|
class InterfaceView(TemplateView):
|
||||||
|
"""Base interface view"""
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||||
|
kwargs["config_json"] = dumps(ConfigView(request=Request(self.request)).get_config().data)
|
||||||
|
kwargs["tenant_json"] = dumps(CurrentTenantSerializer(self.request.tenant).data)
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FlowInterfaceView(InterfaceView):
|
||||||
"""Flow interface"""
|
"""Flow interface"""
|
||||||
|
|
||||||
template_name = "if/flow.html"
|
template_name = "if/flow.html"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { VERSION } from "@goauthentik/web/constants";
|
import { globalAK } from "@goauthentik/web/api/Global";
|
||||||
|
import { EVENT_REFRESH, VERSION } from "@goauthentik/web/constants";
|
||||||
import { MessageMiddleware } from "@goauthentik/web/elements/messages/Middleware";
|
import { MessageMiddleware } from "@goauthentik/web/elements/messages/Middleware";
|
||||||
import { APIMiddleware } from "@goauthentik/web/elements/notifications/APIDrawer";
|
import { APIMiddleware } from "@goauthentik/web/elements/notifications/APIDrawer";
|
||||||
import { activateLocale } from "@goauthentik/web/interfaces/locale";
|
import { activateLocale } from "@goauthentik/web/interfaces/locale";
|
||||||
|
@ -6,9 +7,11 @@ import { getCookie } from "@goauthentik/web/utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
|
ConfigFromJSON,
|
||||||
Configuration,
|
Configuration,
|
||||||
CoreApi,
|
CoreApi,
|
||||||
CurrentTenant,
|
CurrentTenant,
|
||||||
|
CurrentTenantFromJSON,
|
||||||
FetchParams,
|
FetchParams,
|
||||||
Middleware,
|
Middleware,
|
||||||
RequestContext,
|
RequestContext,
|
||||||
|
@ -27,7 +30,9 @@ export class LoggingMiddleware implements Middleware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let globalConfigPromise: Promise<Config>;
|
let globalConfigPromise: Promise<Config> | undefined = Promise.resolve(
|
||||||
|
ConfigFromJSON(globalAK()?.config),
|
||||||
|
);
|
||||||
export function config(): Promise<Config> {
|
export function config(): Promise<Config> {
|
||||||
if (!globalConfigPromise) {
|
if (!globalConfigPromise) {
|
||||||
globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigRetrieve();
|
globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigRetrieve();
|
||||||
|
@ -60,7 +65,9 @@ export function tenantSetLocale(tenant: CurrentTenant) {
|
||||||
activateLocale(tenant.defaultLocale);
|
activateLocale(tenant.defaultLocale);
|
||||||
}
|
}
|
||||||
|
|
||||||
let globalTenantPromise: Promise<CurrentTenant>;
|
let globalTenantPromise: Promise<CurrentTenant> | undefined = Promise.resolve(
|
||||||
|
CurrentTenantFromJSON(globalAK()?.tenant),
|
||||||
|
);
|
||||||
export function tenant(): Promise<CurrentTenant> {
|
export function tenant(): Promise<CurrentTenant> {
|
||||||
if (!globalTenantPromise) {
|
if (!globalTenantPromise) {
|
||||||
globalTenantPromise = new CoreApi(DEFAULT_CONFIG)
|
globalTenantPromise = new CoreApi(DEFAULT_CONFIG)
|
||||||
|
@ -108,4 +115,13 @@ export function AndNext(url: string): string {
|
||||||
return `?next=${encodeURIComponent(url)}`;
|
return `?next=${encodeURIComponent(url)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.addEventListener(EVENT_REFRESH, () => {
|
||||||
|
// Upon global refresh, disregard whatever was pre-hydrated and
|
||||||
|
// actually load info from API
|
||||||
|
globalConfigPromise = undefined;
|
||||||
|
globalTenantPromise = undefined;
|
||||||
|
config();
|
||||||
|
tenant();
|
||||||
|
});
|
||||||
|
|
||||||
console.debug(`authentik(early): version ${VERSION}, apiBase ${DEFAULT_CONFIG.basePath}`);
|
console.debug(`authentik(early): version ${VERSION}, apiBase ${DEFAULT_CONFIG.basePath}`);
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
import { Config, CurrentTenant } from "@goauthentik/api";
|
||||||
|
|
||||||
export interface GlobalAuthentik {
|
export interface GlobalAuthentik {
|
||||||
locale?: string;
|
locale?: string;
|
||||||
flow?: {
|
flow?: {
|
||||||
layout: string;
|
layout: string;
|
||||||
};
|
};
|
||||||
|
config: Config;
|
||||||
|
tenant: CurrentTenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthentikWindow {
|
export interface AuthentikWindow {
|
||||||
|
|
Reference in New Issue