web: full web components part 1 (#4964)
* migrate loading Signed-off-by: Jens Langhammer <jens@goauthentik.io> * migrate api browser Signed-off-by: Jens Langhammer <jens@goauthentik.io> * migrate base css Signed-off-by: Jens Langhammer <jens@goauthentik.io> * move tenant fetching to base interface Signed-off-by: Jens Langhammer <jens@goauthentik.io> * import pre-loaded stages in flow interface and not executor to strip down executor size Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix redirect and such Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
bebf18f257
commit
e2d3a95c80
|
@ -7,82 +7,13 @@ API Browser - {{ tenant.branding_title }}
|
|||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<script type="module" src="{% static 'dist/rapidoc-min.js' %}"></script>
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
let cookieValue = "";
|
||||
if (document.cookie && document.cookie !== "") {
|
||||
const cookies = document.cookie.split(";");
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === name + "=") {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', (event) => {
|
||||
const rapidocEl = document.querySelector('rapi-doc');
|
||||
rapidocEl.addEventListener('before-try', (e) => {
|
||||
e.detail.request.headers.append('X-authentik-CSRF', getCookie("authentik_csrf"));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
img.logo {
|
||||
width: 100%;
|
||||
padding: 1rem 0.5rem 1.5rem 0.5rem;
|
||||
min-height: 48px;
|
||||
}
|
||||
</style>
|
||||
<script src="{% static 'dist/standalone/api-browser/index.js' %}?version={{ version }}" type="module"></script>
|
||||
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: light)">
|
||||
<meta name="theme-color" content="#151515" media="(prefers-color-scheme: dark)">
|
||||
<link rel="icon" href="{{ tenant.branding_favicon }}">
|
||||
<link rel="shortcut icon" href="{{ tenant.branding_favicon }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<rapi-doc
|
||||
spec-url="{{ path }}"
|
||||
heading-text=""
|
||||
theme="light"
|
||||
render-style="read"
|
||||
default-schema-tab="schema"
|
||||
primary-color="#fd4b2d"
|
||||
nav-bg-color="#212427"
|
||||
bg-color="#000000"
|
||||
text-color="#000000"
|
||||
nav-text-color="#ffffff"
|
||||
nav-hover-bg-color="#3c3f42"
|
||||
nav-accent-color="#4f5255"
|
||||
nav-hover-text-color="#ffffff"
|
||||
use-path-in-nav-bar="true"
|
||||
nav-item-spacing="relaxed"
|
||||
allow-server-selection="false"
|
||||
show-header="false"
|
||||
allow-spec-url-load="false"
|
||||
allow-spec-file-load="false">
|
||||
<div slot="nav-logo">
|
||||
<img alt="authentik Logo" class="logo" src="{% static 'dist/assets/icons/icon_left_brand.png' %}" />
|
||||
</div>
|
||||
</rapi-doc>
|
||||
<script>
|
||||
const rapidoc = document.querySelector("rapi-doc");
|
||||
const matcher = window.matchMedia("(prefers-color-scheme: light)");
|
||||
const changer = (ev) => {
|
||||
const style = getComputedStyle(document.documentElement);
|
||||
let bg, text = "";
|
||||
if (matcher.matches) {
|
||||
bg = style.getPropertyValue('--pf-global--BackgroundColor--light-300');
|
||||
text = style.getPropertyValue('--pf-global--Color--300');
|
||||
} else {
|
||||
bg = style.getPropertyValue('--ak-dark-background');
|
||||
text = style.getPropertyValue('--ak-dark-foreground');
|
||||
}
|
||||
rapidoc.attributes.getNamedItem("bg-color").value = bg.trim();
|
||||
rapidoc.attributes.getNamedItem("text-color").value = text.trim();
|
||||
rapidoc.requestUpdate();
|
||||
};
|
||||
matcher.addEventListener("change", changer);
|
||||
window.addEventListener("load", changer);
|
||||
</script>
|
||||
<ak-api-browser schemaPath="{{ path }}"></ak-api-browser>
|
||||
{% endblock %}
|
||||
|
|
|
@ -9,16 +9,13 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>{% block title %}{% trans title|default:tenant.branding_title %}{% endblock %}</title>
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'dist/assets/icons/icon.png' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/page.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/empty-state.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/spinner.css' %}">
|
||||
{% block head_before %}
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/custom.css' %}" data-inject>
|
||||
<script src="{% static 'dist/poly.js' %}" type="module"></script>
|
||||
<script src="{% static 'dist/poly.js' %}?version={{ version }}" type="module"></script>
|
||||
<script src="{% static 'dist/standalone/loading/index.js' %}?version={{ version }}" type="module"></script>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
<meta name="sentry-trace" content="{{ sentry_trace }}" />
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head %}
|
||||
<script src="{% static 'dist/admin/AdminInterface.js' %}?version={{ version }}" type="module"></script>
|
||||
|
@ -15,19 +14,6 @@
|
|||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-interface-admin>
|
||||
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
|
||||
<div class="pf-c-empty-state" style="height: 100vh;">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="{% trans 'Loading...' %}">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans "Loading..." %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ak-loading></ak-loading>
|
||||
</ak-interface-admin>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_before %}
|
||||
{{ block.super }}
|
||||
|
@ -31,19 +30,6 @@ window.authentik.flow = {
|
|||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-flow-executor>
|
||||
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
|
||||
<div class="pf-c-empty-state" style="height: 100vh;">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="{% trans 'Loading...' %}">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans "Loading..." %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ak-loading></ak-loading>
|
||||
</ak-flow-executor>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head %}
|
||||
<script src="{% static 'dist/user/UserInterface.js' %}?version={{ version }}" type="module"></script>
|
||||
|
@ -15,19 +14,6 @@
|
|||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<ak-interface-user>
|
||||
<section class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl">
|
||||
<div class="pf-c-empty-state" style="height: 100vh;">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span class="pf-c-spinner pf-m-xl pf-c-empty-state__icon" role="progressbar" aria-valuetext="{% trans 'Loading...' %}">
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">
|
||||
{% trans "Loading..." %}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<ak-loading></ak-loading>
|
||||
</ak-interface-user>
|
||||
{% endblock %}
|
||||
|
|
|
@ -14,28 +14,10 @@ const D3_WARNING = /Circular dependency.*d3-[interpolate|selection]/;
|
|||
const extensions = [".js", ".jsx", ".ts", ".tsx"];
|
||||
|
||||
export const resources = [
|
||||
{ src: "node_modules/rapidoc/dist/rapidoc-min.js", dest: "dist/" },
|
||||
|
||||
{
|
||||
src: "node_modules/@patternfly/patternfly/patternfly.min.css",
|
||||
dest: "dist/",
|
||||
},
|
||||
{
|
||||
src: "node_modules/@patternfly/patternfly/patternfly-base.css",
|
||||
dest: "dist/",
|
||||
},
|
||||
{
|
||||
src: "node_modules/@patternfly/patternfly/components/Page/page.css",
|
||||
dest: "dist/",
|
||||
},
|
||||
{
|
||||
src: "node_modules/@patternfly/patternfly/components/EmptyState/empty-state.css",
|
||||
dest: "dist/",
|
||||
},
|
||||
{
|
||||
src: "node_modules/@patternfly/patternfly/components/Spinner/spinner.css",
|
||||
dest: "dist/",
|
||||
},
|
||||
{ src: "src/common/styles/*", dest: "dist/" },
|
||||
{ src: "src/custom.css", dest: "dist/" },
|
||||
|
||||
|
@ -132,8 +114,25 @@ export const POLY = {
|
|||
].filter((p) => p),
|
||||
};
|
||||
|
||||
export const standalone = ["api-browser", "loading"].map((input) => {
|
||||
return {
|
||||
input: `./src/standalone/${input}`,
|
||||
output: [
|
||||
{
|
||||
format: "es",
|
||||
dir: `dist/standalone/${input}`,
|
||||
sourcemap: true,
|
||||
manualChunks: manualChunks,
|
||||
},
|
||||
],
|
||||
...defaultOptions,
|
||||
};
|
||||
});
|
||||
|
||||
export default [
|
||||
POLY,
|
||||
// Standalone
|
||||
...standalone,
|
||||
// Flow interface
|
||||
{
|
||||
input: "./src/flow/FlowInterface.ts",
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import { POLY } from "./rollup.config";
|
||||
import { POLY, standalone } from "./rollup.config";
|
||||
|
||||
export default [POLY];
|
||||
export default [POLY, ...standalone];
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
EVENT_SIDEBAR_TOGGLE,
|
||||
VERSION,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { autoDetectLanguage } from "@goauthentik/common/ui/locale";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
|
@ -105,6 +106,7 @@ export class AdminInterface extends Interface {
|
|||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
configureSentry(true);
|
||||
this.version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
this.user = await me();
|
||||
if (!this.user.user.isSuperuser && this.user.user.pk > 0) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
|
||||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
@ -10,7 +11,7 @@ import "@goauthentik/elements/forms/SearchSelect";
|
|||
import { t } from "@lingui/macro";
|
||||
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import {
|
||||
|
@ -19,7 +20,6 @@ import {
|
|||
CoreGroupsListRequest,
|
||||
CryptoApi,
|
||||
CryptoCertificatekeypairsListRequest,
|
||||
CurrentTenant,
|
||||
Flow,
|
||||
FlowsApi,
|
||||
FlowsInstancesListDesignationEnum,
|
||||
|
@ -32,14 +32,10 @@ import {
|
|||
|
||||
@customElement("ak-provider-ldap-form")
|
||||
export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
|
||||
@state()
|
||||
tenant?: CurrentTenant;
|
||||
async loadInstance(pk: number): Promise<LDAPProvider> {
|
||||
const provider = await new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersLdapRetrieve({
|
||||
id: pk,
|
||||
});
|
||||
this.tenant = await tenant();
|
||||
return provider;
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
|
@ -101,7 +97,7 @@ export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
|
|||
return flow?.pk;
|
||||
}}
|
||||
.selected=${(flow: Flow): boolean => {
|
||||
let selected = flow.pk === this.tenant?.flowAuthentication;
|
||||
let selected = flow.pk === rootInterface()?.tenant?.flowAuthentication;
|
||||
if (this.instance?.authorizationFlow === flow.pk) {
|
||||
selected = true;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
.ak-static-page h1 {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
body {
|
||||
background-color: var(--ak-dark-background) !important;
|
||||
}
|
||||
:root {
|
||||
--pf-global--Color--100: var(--ak-dark-foreground);
|
||||
--pf-global--Color--100: var(--ak-dark-foreground) !important;
|
||||
--pf-c-page__main-section--m-light--BackgroundColor: var(--ak-dark-background-darker);
|
||||
--pf-global--link--Color: var(--ak-dark-foreground-link);
|
||||
--pf-global--link--Color: var(--ak-dark-foreground-link) !important;
|
||||
}
|
||||
.pf-c-radio {
|
||||
--pf-c-radio__label--Color: var(--ak-dark-foreground);
|
||||
|
|
|
@ -41,7 +41,7 @@ export function me(): Promise<SessionUser> {
|
|||
settings: {},
|
||||
},
|
||||
};
|
||||
if (ex.response.status === 401 || ex.response.status === 403) {
|
||||
if (ex.response?.status === 401 || ex.response?.status === 403) {
|
||||
const relativeUrl = window.location
|
||||
.toString()
|
||||
.substring(window.location.origin.length);
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { tenant } from "@goauthentik/common/api/config";
|
||||
import { EVENT_LOCALE_CHANGE, EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
|
||||
import { LitElement } from "lit";
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import AKGlobal from "@goauthentik/common/styles/authentik.css";
|
||||
import ThemeDark from "@goauthentik/common/styles/theme-dark.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
import { CurrentTenant, UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
export function rootInterface(): Interface | undefined {
|
||||
const el = Array.from(document.body.querySelectorAll("*")).filter(
|
||||
|
@ -165,6 +168,15 @@ export class AKElement extends LitElement {
|
|||
}
|
||||
|
||||
export class Interface extends AKElement {
|
||||
@state()
|
||||
tenant?: CurrentTenant;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, PFBase];
|
||||
tenant().then((tenant) => (this.tenant = tenant));
|
||||
}
|
||||
|
||||
_activateTheme(root: AdoptedStyleSheetsElement, theme: UiThemeEnum): void {
|
||||
super._activateTheme(root, theme);
|
||||
super._activateTheme(document, theme);
|
||||
|
|
|
@ -34,7 +34,6 @@ export class AggregateCard extends AKElement {
|
|||
.center-value {
|
||||
font-size: var(--pf-global--icon--FontSize--lg);
|
||||
text-align: center;
|
||||
color: var(--pf-global--Color--100);
|
||||
}
|
||||
.subtext {
|
||||
font-size: var(--pf-global--FontSize--sm);
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { tenant } from "@goauthentik/common/api/config";
|
||||
import { EVENT_SIDEBAR_TOGGLE } from "@goauthentik/common/constants";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
|
@ -30,9 +28,6 @@ export const DefaultTenant: CurrentTenant = {
|
|||
|
||||
@customElement("ak-sidebar-brand")
|
||||
export class SidebarBrand extends AKElement {
|
||||
@property({ attribute: false })
|
||||
tenant: CurrentTenant = DefaultTenant;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
|
@ -69,11 +64,6 @@ export class SidebarBrand extends AKElement {
|
|||
});
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
configureSentry(true);
|
||||
tenant().then((tenant) => (this.tenant = tenant));
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` ${window.innerWidth <= MIN_WIDTH
|
||||
? html`
|
||||
|
@ -95,7 +85,10 @@ export class SidebarBrand extends AKElement {
|
|||
<a href="#/" class="pf-c-page__header-brand-link">
|
||||
<div class="pf-c-brand ak-brand">
|
||||
<img
|
||||
src="${first(this.tenant.brandingLogo, DefaultTenant.brandingLogo)}"
|
||||
src="${first(
|
||||
rootInterface()?.tenant?.brandingLogo,
|
||||
DefaultTenant.brandingLogo,
|
||||
)}"
|
||||
alt="authentik Logo"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
|
|
@ -12,17 +12,7 @@ import { Interface } from "@goauthentik/elements/Base";
|
|||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import "@goauthentik/flow/stages/FlowErrorStage";
|
||||
import "@goauthentik/flow/stages/RedirectStage";
|
||||
import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
|
||||
// Import webauthn-related stages to prevent issues on safari
|
||||
// Which is overly sensitive to allowing things only in the context of a
|
||||
// user interaction
|
||||
import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage";
|
||||
import "@goauthentik/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
import "@goauthentik/flow/stages/autosubmit/AutosubmitStage";
|
||||
import { StageHost } from "@goauthentik/flow/stages/base";
|
||||
import "@goauthentik/flow/stages/captcha/CaptchaStage";
|
||||
import "@goauthentik/flow/stages/identification/IdentificationStage";
|
||||
import "@goauthentik/flow/stages/password/PasswordStage";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
|
@ -43,7 +33,6 @@ import {
|
|||
ChallengeChoices,
|
||||
ChallengeTypes,
|
||||
ContextualFlowInfo,
|
||||
CurrentTenant,
|
||||
FlowChallengeResponseRequest,
|
||||
FlowErrorChallenge,
|
||||
FlowsApi,
|
||||
|
@ -92,9 +81,6 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||
@property({ type: Boolean })
|
||||
loading = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
tenant!: CurrentTenant;
|
||||
|
||||
@state()
|
||||
inspectorOpen = false;
|
||||
|
||||
|
@ -186,7 +172,6 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||
this.addEventListener(EVENT_FLOW_INSPECTOR_TOGGLE, () => {
|
||||
this.inspectorOpen = !this.inspectorOpen;
|
||||
});
|
||||
tenant().then((tenant) => (this.tenant = tenant));
|
||||
}
|
||||
|
||||
async getTheme(): Promise<UiThemeEnum> {
|
||||
|
@ -283,25 +268,25 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||
async renderChallengeNativeElement(): Promise<TemplateResult> {
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-access-denied":
|
||||
// Statically imported for performance reasons
|
||||
await import("@goauthentik/flow/stages/access_denied/AccessDeniedStage");
|
||||
return html`<ak-stage-access-denied
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-access-denied>`;
|
||||
case "ak-stage-identification":
|
||||
// Statically imported for performance reasons
|
||||
await import("@goauthentik/flow/stages/identification/IdentificationStage");
|
||||
return html`<ak-stage-identification
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-identification>`;
|
||||
case "ak-stage-password":
|
||||
// Statically imported for performance reasons
|
||||
await import("@goauthentik/flow/stages/password/PasswordStage");
|
||||
return html`<ak-stage-password
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-password>`;
|
||||
case "ak-stage-captcha":
|
||||
// Statically imported to prevent browsers blocking urls
|
||||
await import("@goauthentik/flow/stages/captcha/CaptchaStage");
|
||||
return html`<ak-stage-captcha
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
|
@ -325,7 +310,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||
.challenge=${this.challenge}
|
||||
></ak-stage-email>`;
|
||||
case "ak-stage-autosubmit":
|
||||
// Statically imported for performance reasons
|
||||
await import("@goauthentik/flow/stages/autosubmit/AutosubmitStage");
|
||||
return html`<ak-stage-autosubmit
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
|
@ -368,6 +353,9 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-sms>`;
|
||||
case "ak-stage-authenticator-validate":
|
||||
await import(
|
||||
"@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage"
|
||||
);
|
||||
return html`<ak-stage-authenticator-validate
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
|
@ -411,9 +399,8 @@ export class FlowExecutor extends Interface implements StageHost {
|
|||
.challenge=${this.challenge}
|
||||
></ak-stage-flow-error>`;
|
||||
default:
|
||||
break;
|
||||
return html`Invalid native challenge element`;
|
||||
}
|
||||
return html`Invalid native challenge element`;
|
||||
}
|
||||
|
||||
async renderChallenge(): Promise<TemplateResult> {
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
import { autoDetectLanguage } from "@goauthentik/common/ui/locale";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/flow/FlowExecutor";
|
||||
// Statically import some stages to speed up load speed
|
||||
import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
|
||||
// Import webauthn-related stages to prevent issues on safari
|
||||
// Which is overly sensitive to allowing things only in the context of a
|
||||
// user interaction
|
||||
import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage";
|
||||
import "@goauthentik/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
import "@goauthentik/flow/stages/autosubmit/AutosubmitStage";
|
||||
import "@goauthentik/flow/stages/captcha/CaptchaStage";
|
||||
import "@goauthentik/flow/stages/identification/IdentificationStage";
|
||||
import "@goauthentik/flow/stages/password/PasswordStage";
|
||||
|
||||
// end of stage import
|
||||
|
||||
autoDetectLanguage();
|
||||
|
|
|
@ -44,7 +44,7 @@ export class AuthenticatorValidateStage
|
|||
return this.host.loading;
|
||||
}
|
||||
|
||||
get tenant(): CurrentTenant {
|
||||
get tenant(): CurrentTenant | undefined {
|
||||
return this.host.tenant;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export interface StageHost {
|
|||
loading: boolean;
|
||||
submit(payload: unknown): Promise<boolean>;
|
||||
|
||||
readonly tenant: CurrentTenant;
|
||||
readonly tenant?: CurrentTenant;
|
||||
}
|
||||
|
||||
export function readFileAsync(file: Blob) {
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
|
||||
import { first, getCookie } from "@goauthentik/common/utils";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
import { DefaultTenant } from "@goauthentik/elements/sidebar/SidebarBrand";
|
||||
import "rapidoc";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-api-browser")
|
||||
export class APIBrowser extends Interface {
|
||||
@property()
|
||||
schemaPath?: string;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
||||
img.logo {
|
||||
width: 100%;
|
||||
padding: 1rem 0.5rem 1.5rem 0.5rem;
|
||||
min-height: 48px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@state()
|
||||
bgColor = "#000000";
|
||||
|
||||
@state()
|
||||
textColor = "#000000";
|
||||
|
||||
firstUpdated(): void {
|
||||
this.addEventListener(EVENT_THEME_CHANGE, ((ev: CustomEvent<UiThemeEnum>) => {
|
||||
const style = getComputedStyle(document.documentElement);
|
||||
if (ev.detail === UiThemeEnum.Light) {
|
||||
this.bgColor = style
|
||||
.getPropertyValue("--pf-global--BackgroundColor--light-300")
|
||||
.trim();
|
||||
this.textColor = style.getPropertyValue("--pf-global--Color--300").trim();
|
||||
} else {
|
||||
this.bgColor = style.getPropertyValue("--ak-dark-background").trim();
|
||||
this.textColor = style.getPropertyValue("--ak-dark-foreground").trim();
|
||||
}
|
||||
}) as EventListener);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_THEME_CHANGE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: UiThemeEnum.Automatic,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`
|
||||
<rapi-doc
|
||||
spec-url=${ifDefined(this.schemaPath)}
|
||||
heading-text=""
|
||||
theme="light"
|
||||
render-style="read"
|
||||
default-schema-tab="schema"
|
||||
primary-color="#fd4b2d"
|
||||
nav-bg-color="#212427"
|
||||
bg-color=${this.bgColor}
|
||||
text-color=${this.textColor}
|
||||
nav-text-color="#ffffff"
|
||||
nav-hover-bg-color="#3c3f42"
|
||||
nav-accent-color="#4f5255"
|
||||
nav-hover-text-color="#ffffff"
|
||||
use-path-in-nav-bar="true"
|
||||
nav-item-spacing="relaxed"
|
||||
allow-server-selection="false"
|
||||
show-header="false"
|
||||
allow-spec-url-load="false"
|
||||
allow-spec-file-load="false"
|
||||
@before-try=${(
|
||||
e: CustomEvent<{
|
||||
request: {
|
||||
headers: Headers;
|
||||
};
|
||||
}>,
|
||||
) => {
|
||||
e.detail.request.headers.append(
|
||||
"X-authentik-CSRF",
|
||||
getCookie("authentik_csrf"),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div slot="nav-logo">
|
||||
<img
|
||||
alt="authentik Logo"
|
||||
class="logo"
|
||||
src="${first(this.tenant?.brandingLogo, DefaultTenant.brandingLogo)}"
|
||||
/>
|
||||
</div>
|
||||
</rapi-doc>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { Interface } from "@goauthentik/elements/Base";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-loading")
|
||||
export class Loading extends Interface {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFSpinner,
|
||||
PFEmptyState,
|
||||
css`
|
||||
:host([theme="dark"]) h1 {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
async getTheme(): Promise<UiThemeEnum> {
|
||||
return globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <section
|
||||
class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"
|
||||
>
|
||||
<div class="pf-c-empty-state" style="height: 100vh;">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<span
|
||||
class="pf-c-spinner pf-m-xl"
|
||||
role="progressbar"
|
||||
aria-valuetext="${t`Loading...`}"
|
||||
>
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
<h1 class="pf-c-title pf-m-lg">${t`Loading...`}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</section>`;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { DEFAULT_CONFIG, tenant } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import {
|
||||
EVENT_API_DRAWER_TOGGLE,
|
||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||
|
@ -36,7 +36,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
|||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||
|
||||
import { CurrentTenant, EventsApi, SessionUser } from "@goauthentik/api";
|
||||
import { EventsApi, SessionUser } from "@goauthentik/api";
|
||||
|
||||
autoDetectLanguage();
|
||||
|
||||
|
@ -50,9 +50,6 @@ export class UserInterface extends Interface {
|
|||
|
||||
ws: WebsocketClient;
|
||||
|
||||
@property({ attribute: false })
|
||||
tenant: CurrentTenant = DefaultTenant;
|
||||
|
||||
@property({ type: Number })
|
||||
notificationsCount = 0;
|
||||
|
||||
|
@ -128,7 +125,6 @@ export class UserInterface extends Interface {
|
|||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
this.tenant = await tenant();
|
||||
this.me = await me();
|
||||
this.config = await uiConfig();
|
||||
const notifications = await new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({
|
||||
|
@ -165,8 +161,8 @@ export class UserInterface extends Interface {
|
|||
<a href="#/" class="pf-c-page__header-brand-link">
|
||||
<img
|
||||
class="pf-c-brand"
|
||||
src="${first(this.tenant.brandingLogo, DefaultTenant.brandingLogo)}"
|
||||
alt="${(this.tenant.brandingTitle, DefaultTenant.brandingTitle)}"
|
||||
src="${first(this.tenant?.brandingLogo, DefaultTenant.brandingLogo)}"
|
||||
alt="${(this.tenant?.brandingTitle, DefaultTenant.brandingTitle)}"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -62,7 +62,7 @@ export class UserSettingsPromptStage extends PromptStage {
|
|||
<div class="pf-c-form__horizontal-group">
|
||||
<div class="pf-c-form__actions">
|
||||
<button type="submit" class="pf-c-button pf-m-primary">${t`Save`}</button>
|
||||
${this.host.tenant.flowUnenrollment
|
||||
${this.host.tenant?.flowUnenrollment
|
||||
? html` <a
|
||||
class="pf-c-button pf-m-danger"
|
||||
href="/if/flow/${this.host.tenant.flowUnenrollment}/"
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
"paths": {
|
||||
"@goauthentik/admin/*": ["src/admin/*"],
|
||||
"@goauthentik/common/*": ["src/common/*"],
|
||||
"@goauthentik/docs/*": ["../website/docs/*"],
|
||||
"@goauthentik/elements/*": ["src/elements/*"],
|
||||
"@goauthentik/flow/*": ["src/flow/*"],
|
||||
"@goauthentik/polyfill/*": ["src/polyfill/*"],
|
||||
"@goauthentik/user/*": ["src/user/*"],
|
||||
"@goauthentik/locales/*": ["src/locales/*"],
|
||||
"@goauthentik/docs/*": ["../website/docs/*"]
|
||||
"@goauthentik/polyfill/*": ["src/polyfill/*"],
|
||||
"@goauthentik/standalone/*": ["src/standalone/*"],
|
||||
"@goauthentik/user/*": ["src/user/*"]
|
||||
},
|
||||
"baseUrl": ".",
|
||||
"esModuleInterop": true,
|
||||
|
|
Reference in New Issue