Web: Controllers cleanup (#14616)
* web: Fix issues surrounding availability of controllers during init. web: Fix edgecase where flow does not have brand. * web: Fix import path. * web: Clean up mixin/controller paths. * web: Prepare for consistent import styling. - Prep for Storybook fixes. * web: Update MDX types. * web: Fix issues surrounding async imports, MDX typing, relative paths. * web: Format. Clarify. * web: Group module types.
This commit is contained in:
@ -31,8 +31,33 @@ export const AuthentikPrettierConfig = {
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
vueIndentScriptAndStyle: false,
|
||||
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
plugins: [
|
||||
// ---
|
||||
"prettier-plugin-packagejson",
|
||||
"@trivago/prettier-plugin-sort-imports",
|
||||
],
|
||||
importOrder: [
|
||||
// ---
|
||||
|
||||
"^(@goauthentik/|#)common.+",
|
||||
"^(@goauthentik/|#)elements.+",
|
||||
"^(@goauthentik/|#)components.+",
|
||||
"^(@goauthentik/|#)user.+",
|
||||
"^(@goauthentik/|#)admin.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
"^(@goauthentik/|#)flow.+",
|
||||
|
||||
"^#.+",
|
||||
"^@goauthentik.+",
|
||||
|
||||
"<THIRD_PARTY_MODULES>",
|
||||
|
||||
"^(@?)lit(.*)$",
|
||||
"\\.css$",
|
||||
"^@goauthentik/api$",
|
||||
"^[./]",
|
||||
],
|
||||
importOrderSideEffects: false,
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
||||
|
4
packages/prettier-config/package-lock.json
generated
4
packages/prettier-config/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.5",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.5",
|
||||
"version": "2.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "^1.0.1",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.5",
|
||||
"version": "2.0.0",
|
||||
"description": "authentik's Prettier config",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
@ -4,30 +4,27 @@
|
||||
* @import {
|
||||
* OnLoadArgs,
|
||||
* OnLoadResult,
|
||||
* OnResolveArgs,
|
||||
* OnResolveResult,
|
||||
* Plugin,
|
||||
* PluginBuild
|
||||
* } from "esbuild"
|
||||
*/
|
||||
import { MonoRepoRoot } from "@goauthentik/core/paths/node";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
|
||||
/**
|
||||
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData
|
||||
* Data passed to `onload`.
|
||||
* @typedef {Omit<OnLoadArgs, 'pluginData'> & LoadDataFields} LoadData Data passed to `onload`.
|
||||
*
|
||||
* @typedef LoadDataFields
|
||||
* Extra fields given in `data` to `onload`.
|
||||
* @property {PluginData | null | undefined} [pluginData]
|
||||
* Plugin data.
|
||||
* @typedef LoadDataFields Extra fields given in `data` to `onload`.
|
||||
* @property {PluginData | null | undefined} [pluginData] Plugin data.
|
||||
*
|
||||
*
|
||||
* @typedef PluginData
|
||||
* Extra data passed.
|
||||
* @property {Buffer | string | null | undefined} [contents]
|
||||
* File contents.
|
||||
* @typedef PluginData Extra data passed.
|
||||
* @property {Buffer | string | null | undefined} [contents] File contents.
|
||||
*/
|
||||
|
||||
const name = "mdx-plugin";
|
||||
const pluginName = "mdx-plugin";
|
||||
|
||||
/**
|
||||
* @typedef MDXPluginOptions
|
||||
@ -38,28 +35,35 @@ const name = "mdx-plugin";
|
||||
/**
|
||||
* Bundle MDX into JSON modules.
|
||||
*
|
||||
* @param {MDXPluginOptions} options Options.
|
||||
* @returns {Plugin} Plugin.
|
||||
* @param {MDXPluginOptions} options
|
||||
* @returns {Plugin}
|
||||
*/
|
||||
export function mdxPlugin({ root }) {
|
||||
return { name, setup };
|
||||
// TODO: Replace with `resolvePackage` after NPM Workspaces support is added.
|
||||
const docsPackageRoot = path.resolve(MonoRepoRoot, "website");
|
||||
|
||||
/**
|
||||
* @param {PluginBuild} build
|
||||
* Build.
|
||||
* @returns {undefined}
|
||||
* Nothing.
|
||||
*/
|
||||
function setup(build) {
|
||||
build.onLoad({ filter: /\.mdx?$/ }, onload);
|
||||
/**
|
||||
* @param {OnResolveArgs} args
|
||||
* @returns {Promise<OnResolveResult>}
|
||||
*/
|
||||
async function resolveListener(args) {
|
||||
if (!args.path.startsWith("~")) return args;
|
||||
|
||||
return {
|
||||
path: path.resolve(docsPackageRoot, args.path.slice(1)),
|
||||
pluginName,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LoadData} data
|
||||
* Data.
|
||||
* @returns {Promise<OnLoadResult>}
|
||||
* Result.
|
||||
*/
|
||||
async function onload(data) {
|
||||
async function loadListener(data) {
|
||||
const content = String(
|
||||
data.pluginData &&
|
||||
data.pluginData.contents !== null &&
|
||||
@ -77,7 +81,16 @@ export function mdxPlugin({ root }) {
|
||||
return {
|
||||
contents: JSON.stringify({ content, publicPath, publicDirectory }),
|
||||
loader: "file",
|
||||
pluginName,
|
||||
};
|
||||
}
|
||||
|
||||
build.onResolve({ filter: /\.mdx?$/ }, resolveListener);
|
||||
build.onLoad({ filter: /\.mdx?$/ }, loadListener);
|
||||
}
|
||||
|
||||
return {
|
||||
name: pluginName,
|
||||
setup,
|
||||
};
|
||||
}
|
||||
|
@ -67,6 +67,7 @@
|
||||
"#admin/*": "./src/admin/*.js",
|
||||
"#flow/*.css": "./src/flow/*.css",
|
||||
"#flow/*": "./src/flow/*.js",
|
||||
"#locales/*": "./src/locales/*.js",
|
||||
"#stories/*": "./src/stories/*.js",
|
||||
"#*/browser": {
|
||||
"types": "./out/*/browser.d.ts",
|
||||
|
19
web/paths/index.js
Normal file
19
web/paths/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @file Paths used by the web package.
|
||||
*
|
||||
* @runtime common
|
||||
*/
|
||||
|
||||
/**
|
||||
* The name of the distribution directory.
|
||||
*
|
||||
* @runtime common
|
||||
*/
|
||||
export const DistDirectoryName = "dist";
|
||||
|
||||
/**
|
||||
* The name of the static file directory.
|
||||
*
|
||||
* @runtime common
|
||||
*/
|
||||
export const StaticDirectoryName = "static";
|
@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @file Paths used by the web package.
|
||||
*
|
||||
* @runtime node
|
||||
*/
|
||||
import { DistDirectoryName } from "#paths";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
@ -11,18 +17,17 @@ const relativeDirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* The root of the web package.
|
||||
*
|
||||
* @runtime node
|
||||
*/
|
||||
export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(relativeDirname, ".."));
|
||||
|
||||
/**
|
||||
* The name of the distribution directory.
|
||||
*/
|
||||
export const DistDirectoryName = "dist";
|
||||
|
||||
/**
|
||||
* Path to the web package's distribution directory.
|
||||
*
|
||||
* This is where the built files are located after running the build process.
|
||||
*
|
||||
* @runtime node
|
||||
*/
|
||||
export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectoryName}`} */ (
|
||||
resolve(PackageRoot, DistDirectoryName)
|
||||
@ -43,6 +48,8 @@ export const DistDirectory = /** @type {`${WebPackageIdentifier}/${DistDirectory
|
||||
* Entry points available for building.
|
||||
*
|
||||
* @satisfies {Record<string, EntryPointTarget>}
|
||||
*
|
||||
* @runtime node
|
||||
*/
|
||||
export const EntryPoint = /** @type {const} */ ({
|
||||
Admin: {
|
||||
|
@ -6,7 +6,8 @@
|
||||
*/
|
||||
import { mdxPlugin } from "#bundler/mdx-plugin/node";
|
||||
import { createBundleDefinitions } from "#bundler/utils/node";
|
||||
import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "#paths/node";
|
||||
import { DistDirectoryName } from "#paths";
|
||||
import { DistDirectory, EntryPoint, PackageRoot } from "#paths/node";
|
||||
import { NodeEnvironment } from "@goauthentik/core/environment/node";
|
||||
import { MonoRepoRoot, resolvePackage } from "@goauthentik/core/paths/node";
|
||||
import { readBuildIdentifier } from "@goauthentik/core/version/node";
|
||||
@ -26,7 +27,7 @@ const patternflyPath = resolvePackage("@patternfly/patternfly", import.meta);
|
||||
*/
|
||||
const BASE_ESBUILD_OPTIONS = {
|
||||
entryNames: `[dir]/[name]-${readBuildIdentifier()}`,
|
||||
chunkNames: "[dir]/chunks/[name]-[hash]",
|
||||
chunkNames: "[dir]/chunks/[hash]",
|
||||
assetNames: "assets/[dir]/[name]-[hash]",
|
||||
publicPath: path.join("/static", DistDirectoryName),
|
||||
outdir: DistDirectory,
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { DefaultBrand } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||
import { ModalButton } from "@goauthentik/elements/buttons/ModalButton";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -57,7 +56,8 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
|
||||
}
|
||||
|
||||
renderModal() {
|
||||
let product = globalAK().brand.brandingTitle || DefaultBrand.brandingTitle;
|
||||
let product = this.brandingTitle;
|
||||
|
||||
if (this.licenseSummary.status !== LicenseSummaryStatusEnum.Unlicensed) {
|
||||
product += ` ${msg("Enterprise")}`;
|
||||
}
|
||||
@ -73,7 +73,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
|
||||
<div class="pf-c-about-modal-box__brand">
|
||||
<img
|
||||
class="pf-c-about-modal-box__brand-image"
|
||||
src=${this.brand?.brandingFavicon ?? DefaultBrand.brandingFavicon}
|
||||
src=${this.brandingFavicon}
|
||||
alt="${msg("authentik Logo")}"
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,9 +1,12 @@
|
||||
import "#admin/AdminInterface/AboutModal";
|
||||
import type { AboutModal } from "#admin/AdminInterface/AboutModal";
|
||||
import { ROUTES } from "#admin/Routes";
|
||||
import { EVENT_API_DRAWER_TOGGLE, EVENT_NOTIFICATION_DRAWER_TOGGLE } from "#common/constants";
|
||||
import { configureSentry } from "#common/sentry/index";
|
||||
import { me } from "#common/users";
|
||||
import { WebsocketClient } from "#common/ws";
|
||||
import { AuthenticatedInterface } from "#elements/Interface/Interface";
|
||||
import { WithLicenseSummary } from "#elements/Interface/licenseSummaryProvider";
|
||||
import { SidebarToggleEventDetail } from "#components/ak-page-header";
|
||||
import { AuthenticatedInterface } from "#elements/AuthenticatedInterface";
|
||||
import "#elements/ak-locale-context/ak-locale-context";
|
||||
import "#elements/banner/EnterpriseStatusBanner";
|
||||
import "#elements/banner/EnterpriseStatusBanner";
|
||||
@ -11,16 +14,13 @@ import "#elements/banner/VersionBanner";
|
||||
import "#elements/banner/VersionBanner";
|
||||
import "#elements/messages/MessageContainer";
|
||||
import "#elements/messages/MessageContainer";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "#elements/notifications/APIDrawer";
|
||||
import "#elements/notifications/NotificationDrawer";
|
||||
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
|
||||
import "#elements/router/RouterOutlet";
|
||||
import "#elements/sidebar/Sidebar";
|
||||
import "#elements/sidebar/SidebarItem";
|
||||
import "@goauthentik/admin/AdminInterface/AboutModal";
|
||||
import type { AboutModal } from "@goauthentik/admin/AdminInterface/AboutModal";
|
||||
import { ROUTES } from "@goauthentik/admin/Routes";
|
||||
import { SidebarToggleEventDetail } from "@goauthentik/components/ak-page-header.js";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement, eventOptions, property, query } from "lit/decorators.js";
|
||||
@ -45,7 +45,7 @@ if (process.env.NODE_ENV === "development") {
|
||||
}
|
||||
|
||||
@customElement("ak-interface-admin")
|
||||
export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
|
||||
export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterface) {
|
||||
//#region Properties
|
||||
|
||||
@property({ type: Boolean })
|
||||
@ -202,7 +202,7 @@ export class AdminInterface extends WithLicenseSummary(AuthenticatedInterface) {
|
||||
|
||||
<ak-sidebar class="${classMap(sidebarClasses)}">
|
||||
${renderSidebarItems(AdminSidebarEntries)}
|
||||
${this.config?.capabilities.includes(CapabilitiesEnum.IsEnterprise)
|
||||
${this.can(CapabilitiesEnum.IsEnterprise)
|
||||
? renderSidebarItems(AdminSidebarEnterpriseEntries)
|
||||
: nothing}
|
||||
</ak-sidebar>
|
||||
|
@ -11,10 +11,10 @@ import "#admin/admin-overview/charts/SyncStatusChart";
|
||||
import { me } from "#common/users";
|
||||
import "#components/ak-page-header";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { WithLicenseSummary } from "#elements/Interface/licenseSummaryProvider";
|
||||
import "#elements/cards/AggregatePromiseCard";
|
||||
import type { QuickAction } from "#elements/cards/QuickActionsCard";
|
||||
import "#elements/cards/QuickActionsCard";
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import { paramURL } from "#elements/router/RouterOutlet";
|
||||
import { createReleaseNotesURL } from "@goauthentik/core/version";
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/admin/applications/ProviderSelectModal";
|
||||
import { iconHelperText } from "@goauthentik/admin/helperText";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
@ -6,18 +7,14 @@ import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
import "@goauthentik/elements/Alert.js";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/Alert";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
import "@goauthentik/elements/forms/ProxyForm";
|
||||
import "@goauthentik/elements/forms/Radio";
|
||||
import "@goauthentik/elements/forms/SearchSelect";
|
||||
import "@goauthentik/elements/forms/SearchSelect/ak-search-select";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
|
@ -1,17 +1,17 @@
|
||||
import "@goauthentik/admin/applications/ApplicationForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.md";
|
||||
import "@goauthentik/elements/AppIcon.js";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import "@goauthentik/elements/ak-mdx";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
import "#admin/applications/ApplicationForm";
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import "#elements/AppIcon";
|
||||
import "#elements/ak-mdx/ak-mdx";
|
||||
import "#elements/buttons/SpinnerButton/ak-spinner-button";
|
||||
import "#elements/forms/DeleteBulkForm";
|
||||
import "#elements/forms/ModalForm";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { getURLParam } from "#elements/router/RouteMatch";
|
||||
import { PaginatedResponse } from "#elements/table/Table";
|
||||
import { TableColumn } from "#elements/table/Table";
|
||||
import { TablePage } from "#elements/table/TablePage";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
import MDApplication from "~docs/add-secure-apps/applications/index.md";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
@ -22,7 +22,7 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
|
||||
import { Application, CoreApi, PoliciesApi } from "@goauthentik/api";
|
||||
|
||||
import "./ApplicationWizardHint";
|
||||
import "./ApplicationWizardHint.js";
|
||||
|
||||
export const applicationListStyle = css`
|
||||
/* Fix alignment issues with images in tables */
|
||||
@ -50,7 +50,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
}
|
||||
pageDescription(): string {
|
||||
return msg(
|
||||
str`External applications that use ${this.brand?.brandingTitle ?? "authentik"} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
|
||||
str`External applications that use ${this.brandingTitle} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
@ -3,5 +3,5 @@ import { createContext } from "@lit/context";
|
||||
import { LocalTypeCreate } from "./steps/ProviderChoices.js";
|
||||
|
||||
export const applicationWizardProvidersContext = createContext<LocalTypeCreate[]>(
|
||||
Symbol("ak-application-wizard-providers-context"),
|
||||
Symbol.for("ak-application-wizard-providers-context"),
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import { ApplicationWizardStep } from "@goauthentik/admin/applications/wizard/ApplicationWizardStep.js";
|
||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
||||
import type { NavigableButton, WizardButton } from "@goauthentik/components/ak-wizard/types";
|
||||
import "@goauthentik/elements/EmptyState.js";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
|
||||
import { bound } from "@goauthentik/elements/decorators/bound.js";
|
||||
import "@goauthentik/elements/forms/FormGroup.js";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement.js";
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
||||
import { ValidationRecord } from "@goauthentik/admin/applications/wizard/types";
|
||||
import { renderForm } from "@goauthentik/admin/providers/ldap/LDAPProviderFormForm.js";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
|
||||
import { ValidationRecord } from "@goauthentik/admin/applications/wizard/types";
|
||||
import { renderForm } from "@goauthentik/admin/providers/radius/RadiusProviderFormForm.js";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { customElement } from "@lit/reactive-element/decorators.js";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import "@goauthentik/elements/Alert";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, nothing } from "lit";
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils";
|
||||
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/admin/users/ServiceAccountForm";
|
||||
import "@goauthentik/admin/users/UserActiveForm";
|
||||
import "@goauthentik/admin/users/UserForm";
|
||||
@ -11,11 +13,6 @@ import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { formatElapsedTime } from "@goauthentik/common/temporal";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/Dropdown";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
@ -295,7 +292,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
${msg("Set password")}
|
||||
</button>
|
||||
</ak-forms-modal>
|
||||
${this.brand?.flowRecovery
|
||||
${this.brand.flowRecovery
|
||||
? html`
|
||||
<ak-action-button
|
||||
class="pf-m-secondary"
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-branded-flow-search";
|
||||
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
|
@ -4,7 +4,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import renderDescriptionList from "@goauthentik/components/DescriptionList";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import MDProviderOAuth2 from "@goauthentik/docs/add-secure-apps/providers/oauth2/index.mdx";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
@ -12,6 +11,7 @@ import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/ak-mdx";
|
||||
import "@goauthentik/elements/buttons/ModalButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import MDProviderOAuth2 from "~docs/add-secure-apps/providers/oauth2/index.mdx";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
|
@ -5,14 +5,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import MDCaddyStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_caddy_standalone.md";
|
||||
import MDNginxIngress from "@goauthentik/docs/add-secure-apps/providers/proxy/_nginx_ingress.md";
|
||||
import MDNginxPM from "@goauthentik/docs/add-secure-apps/providers/proxy/_nginx_proxy_manager.md";
|
||||
import MDNginxStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_nginx_standalone.md";
|
||||
import MDTraefikCompose from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_compose.md";
|
||||
import MDTraefikIngress from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_ingress.md";
|
||||
import MDTraefikStandalone from "@goauthentik/docs/add-secure-apps/providers/proxy/_traefik_standalone.md";
|
||||
import MDHeaderAuthentication from "@goauthentik/docs/add-secure-apps/providers/proxy/header_authentication.mdx";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
@ -22,6 +14,14 @@ import "@goauthentik/elements/buttons/ModalButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
|
||||
import { formatSlug } from "@goauthentik/elements/router/utils.js";
|
||||
import MDCaddyStandalone from "~docs/add-secure-apps/providers/proxy/_caddy_standalone.md";
|
||||
import MDNginxIngress from "~docs/add-secure-apps/providers/proxy/_nginx_ingress.md";
|
||||
import MDNginxPM from "~docs/add-secure-apps/providers/proxy/_nginx_proxy_manager.md";
|
||||
import MDNginxStandalone from "~docs/add-secure-apps/providers/proxy/_nginx_standalone.md";
|
||||
import MDTraefikCompose from "~docs/add-secure-apps/providers/proxy/_traefik_compose.md";
|
||||
import MDTraefikIngress from "~docs/add-secure-apps/providers/proxy/_traefik_ingress.md";
|
||||
import MDTraefikStandalone from "~docs/add-secure-apps/providers/proxy/_traefik_standalone.md";
|
||||
import MDHeaderAuthentication from "~docs/add-secure-apps/providers/proxy/header_authentication.mdx";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { BaseProviderForm } from "@goauthentik/admin/providers/BaseProviderForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
|
@ -7,13 +7,13 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import MDSCIMProvider from "@goauthentik/docs/add-secure-apps/providers/scim/index.md";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/ak-mdx";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/ModalButton";
|
||||
import "@goauthentik/elements/sync/SyncStatusCard";
|
||||
import MDSCIMProvider from "~docs/add-secure-apps/providers/scim/index.md";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
||||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
@ -9,10 +10,6 @@ import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-switch-input";
|
||||
import "@goauthentik/components/ak-text-input";
|
||||
import "@goauthentik/components/ak-textarea-input";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
@ -4,7 +4,6 @@ import "@goauthentik/admin/sources/kerberos/KerberosSourceForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import MDSourceKerberosBrowser from "@goauthentik/docs/users-sources/sources/protocols/kerberos/browser.md";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
@ -13,6 +12,7 @@ import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import "@goauthentik/elements/sync/SyncStatusCard";
|
||||
import MDSourceKerberosBrowser from "~docs/users-sources/sources/protocols/kerberos/browser.md";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
||||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
@ -9,10 +10,6 @@ import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/components/ak-radio-input";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
||||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
import { BaseSourceForm } from "@goauthentik/admin/sources/BaseSourceForm";
|
||||
@ -8,10 +9,6 @@ import {
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { PlexAPIClient, PlexResource, popupCenterScreen } from "@goauthentik/common/helpers/plex";
|
||||
import { ascii_letters, digits, randomString } from "@goauthentik/common/utils";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-provider.js";
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/admin/common/ak-crypto-certificate-search";
|
||||
import "@goauthentik/admin/common/ak-flow-search/ak-source-flow-search";
|
||||
import { iconHelperText, placeholderHelperText } from "@goauthentik/admin/helperText";
|
||||
@ -7,10 +8,6 @@ import {
|
||||
UserMatchingModeToLabel,
|
||||
} from "@goauthentik/admin/sources/oauth/utils";
|
||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/ak-dual-select/ak-dual-select-dynamic-selected-provider.js";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint.js";
|
||||
import "@goauthentik/admin/users/ServiceAccountForm";
|
||||
import "@goauthentik/admin/users/UserActiveForm";
|
||||
@ -11,15 +13,10 @@ import { parseAPIResponseError } from "@goauthentik/common/errors/network";
|
||||
import { userTypeToLabel } from "@goauthentik/common/labels";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { formatElapsedTime } from "@goauthentik/common/temporal";
|
||||
import { rootInterface } from "@goauthentik/common/theme";
|
||||
import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import "@goauthentik/elements/TreeView";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
|
@ -22,11 +22,11 @@ import "#components/events/ObjectChangelog";
|
||||
import "#components/events/UserEvents";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import "#elements/CodeMirror";
|
||||
import { WithCapabilitiesConfig } from "#elements/Interface/capabilitiesProvider";
|
||||
import "#elements/Tabs";
|
||||
import "#elements/buttons/ActionButton/ak-action-button";
|
||||
import "#elements/buttons/SpinnerButton/ak-spinner-button";
|
||||
import "#elements/forms/ModalForm";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "#elements/oauth/UserAccessTokenList";
|
||||
import "#elements/oauth/UserRefreshTokenList";
|
||||
import "#elements/user/SessionList";
|
||||
|
@ -18,7 +18,6 @@ export const CURRENT_CLASS = "pf-m-current";
|
||||
|
||||
//#region Application
|
||||
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
/**
|
||||
* The delimiter used to parse the URL for the current route.
|
||||
*
|
||||
|
@ -26,8 +26,12 @@ export const HTTPStatusCodeTransformer: Record<number, HTTPErrorJSONTransformer>
|
||||
[HTTPStatusCode.Forbidden]: GenericErrorFromJSON,
|
||||
} as const;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Type Predicates
|
||||
|
||||
/**
|
||||
* Type guard to check if a response contains a JSON body.
|
||||
* Type predicate to check if a response contains a JSON body.
|
||||
*
|
||||
* This is useful to guard against parsing errors when attempting to read the response body.
|
||||
*/
|
||||
@ -35,6 +39,24 @@ export function isJSONResponse(response: Response): boolean {
|
||||
return Boolean(response.headers.get("content-type")?.includes("application/json"));
|
||||
}
|
||||
|
||||
/**
|
||||
* An error originating from an aborted request.
|
||||
*
|
||||
* @see {@linkcode isAbortError} to check if an error originates from an aborted request.
|
||||
*/
|
||||
export interface AbortErrorLike extends DOMException {
|
||||
name: "AbortError";
|
||||
}
|
||||
|
||||
/**
|
||||
* Type predicate to check if an error originates from an aborted request.
|
||||
*
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort | MDN}
|
||||
*/
|
||||
export function isAbortError(error: unknown): error is AbortErrorLike {
|
||||
return error instanceof DOMException && error.name === "AbortError";
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region API
|
||||
|
@ -2,13 +2,12 @@
|
||||
* @file Theme utilities.
|
||||
*/
|
||||
import { type StyleRoot, createStyleSheetUnsafe, setAdoptedStyleSheets } from "#common/stylesheets";
|
||||
import { UIConfig } from "#common/ui/config";
|
||||
|
||||
import AKBase from "#common/styles/authentik.css";
|
||||
import AKBaseDark from "#common/styles/theme-dark.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { Config, CurrentBrand, UiThemeEnum } from "@goauthentik/api";
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
//#region Stylesheet Exports
|
||||
|
||||
@ -259,6 +258,8 @@ export function applyUITheme(
|
||||
export function applyDocumentTheme(hint: CSSColorSchemeValue | UIThemeHint = "auto"): void {
|
||||
const preferredColorScheme = formatColorScheme(hint);
|
||||
|
||||
if (document.documentElement.dataset.theme === preferredColorScheme) return;
|
||||
|
||||
const applyStyleSheets: UIThemeListener = (currentUITheme) => {
|
||||
console.debug(`authentik/theme (document): switching to ${currentUITheme} theme`);
|
||||
|
||||
@ -285,36 +286,20 @@ export function applyDocumentTheme(hint: CSSColorSchemeValue | UIThemeHint = "au
|
||||
applyStyleSheets(preferredColorScheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* An element that can be themed.
|
||||
*/
|
||||
export interface ThemedElement extends HTMLElement {
|
||||
/**
|
||||
* The brand information for the current theme.
|
||||
*/
|
||||
readonly brand?: CurrentBrand;
|
||||
/**
|
||||
* The UI configuration for the current theme,
|
||||
* typically injected through a Lit Mixin.
|
||||
*
|
||||
* @see {@linkcode UIConfig} for details.
|
||||
*/
|
||||
readonly uiConfig?: UIConfig;
|
||||
/**
|
||||
* An authentik configuration initially provided by the server.
|
||||
*/
|
||||
readonly config?: Config;
|
||||
activeTheme: ResolvedUITheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root interface element of the page.
|
||||
*
|
||||
* @todo Can this be handled with a Lit Mixin?
|
||||
*/
|
||||
export function rootInterface<T extends ThemedElement = ThemedElement>(): T | null {
|
||||
export function rootInterface<T extends HTMLElement = HTMLElement>(): T {
|
||||
const element = document.body.querySelector<T>("[data-ak-interface-root]");
|
||||
|
||||
if (!element) {
|
||||
throw new Error(
|
||||
`Could not find root interface element. Was this element added before the parent interface element?`,
|
||||
);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { EVENT_WS_MESSAGE, TITLE_DEFAULT } from "#common/constants";
|
||||
import { EVENT_WS_MESSAGE } from "#common/constants";
|
||||
import { globalAK } from "#common/global";
|
||||
import { UIConfig, UserDisplay, getConfigForUser } from "#common/ui/config";
|
||||
import { DefaultBrand } from "#common/ui/config";
|
||||
import { me } from "#common/users";
|
||||
import "#components/ak-nav-buttons";
|
||||
import type { PageHeaderInit, SidebarToggleEventDetail } from "#components/ak-page-header";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { WithBrandConfig } from "#elements/Interface/brandProvider";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { isAdminRoute } from "#elements/router/utils";
|
||||
import { themeImage } from "#elements/utils/images";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
@ -290,7 +289,7 @@ export class AKPageNavbar
|
||||
//#region Private Methods
|
||||
|
||||
#setTitle(header?: string) {
|
||||
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
|
||||
let title = this.brandingTitle;
|
||||
|
||||
if (isAdminRoute()) {
|
||||
title = `${msg("Admin")} - ${title}`;
|
||||
@ -368,9 +367,7 @@ export class AKPageNavbar
|
||||
<a href="#/">
|
||||
<div class="logo">
|
||||
<img
|
||||
src=${themeImage(
|
||||
this.brand?.brandingLogo ?? DefaultBrand.brandingLogo,
|
||||
)}
|
||||
src=${themeImage(this.brandingLogo)}
|
||||
alt="${msg("authentik Logo")}"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
@ -3,5 +3,5 @@ import { createContext } from "@lit/context";
|
||||
import type { WizardStepState } from "./types";
|
||||
|
||||
export const wizardStepContext = createContext<WizardStepState>(
|
||||
Symbol("authentik-wizard-step-labels"),
|
||||
Symbol.for("authentik-wizard-step-labels"),
|
||||
);
|
||||
|
12
web/src/elements/AuthenticatedInterface.ts
Normal file
12
web/src/elements/AuthenticatedInterface.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Interface } from "#elements/Interface";
|
||||
import { LicenseContextController } from "#elements/controllers/EnterpriseContextController";
|
||||
import { VersionContextController } from "#elements/controllers/VersionContextController";
|
||||
|
||||
export class AuthenticatedInterface extends Interface {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addController(new LicenseContextController(this));
|
||||
this.addController(new VersionContextController(this));
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import { createContext } from "@lit/context";
|
||||
|
||||
import type { Config, CurrentBrand, LicenseSummary, SessionUser, Version } from "@goauthentik/api";
|
||||
|
||||
export const authentikConfigContext = createContext<Config>(Symbol("authentik-config-context"));
|
||||
|
||||
export const authentikUserContext = createContext<SessionUser>(Symbol("authentik-user-context"));
|
||||
|
||||
export const authentikEnterpriseContext = createContext<LicenseSummary>(
|
||||
Symbol("authentik-enterprise-context"),
|
||||
);
|
||||
|
||||
export const authentikBrandContext = createContext<CurrentBrand>(Symbol("authentik-brand-context"));
|
||||
|
||||
export const authentikVersionContext = createContext<Version>(Symbol("authentik-version-context"));
|
||||
|
||||
export default authentikConfigContext;
|
@ -1,19 +1,14 @@
|
||||
import { globalAK } from "@goauthentik/common/global.js";
|
||||
import {
|
||||
StyleRoot,
|
||||
createCSSResult,
|
||||
createStyleSheetUnsafe,
|
||||
} from "@goauthentik/common/stylesheets.js";
|
||||
import { globalAK } from "#common/global";
|
||||
import { StyleRoot, createCSSResult, createStyleSheetUnsafe } from "#common/stylesheets";
|
||||
import {
|
||||
$AKBase,
|
||||
CSSColorSchemeValue,
|
||||
ResolvedUITheme,
|
||||
ThemedElement,
|
||||
applyUITheme,
|
||||
createUIThemeEffect,
|
||||
formatColorScheme,
|
||||
resolveUITheme,
|
||||
} from "@goauthentik/common/theme.js";
|
||||
} from "#common/theme";
|
||||
|
||||
import { localized } from "@lit/localize";
|
||||
import { CSSResult, CSSResultGroup, CSSResultOrNative, LitElement } from "lit";
|
||||
@ -21,11 +16,8 @@ import { property } from "lit/decorators.js";
|
||||
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
// Re-export the theme helpers
|
||||
export { rootInterface } from "@goauthentik/common/theme";
|
||||
|
||||
@localized()
|
||||
export class AKElement extends LitElement implements ThemedElement {
|
||||
export class AKElement extends LitElement {
|
||||
//#region Static Properties
|
||||
|
||||
public static styles?: Array<CSSResult | CSSModule>;
|
||||
|
33
web/src/elements/Interface.ts
Normal file
33
web/src/elements/Interface.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { globalAK } from "#common/global";
|
||||
import { applyDocumentTheme } from "#common/theme";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { BrandingContextController } from "#elements/controllers/BrandContextController";
|
||||
import { ConfigContextController } from "#elements/controllers/ConfigContextController";
|
||||
import { ModalOrchestrationController } from "#elements/controllers/ModalOrchestrationController";
|
||||
import { WithAuthentikConfig } from "#elements/mixins/config";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
/**
|
||||
* The base interface element for the application.
|
||||
*/
|
||||
export abstract class Interface extends WithAuthentikConfig(AKElement) {
|
||||
static styles = [PFBase];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const { config, brand } = globalAK();
|
||||
|
||||
applyDocumentTheme(brand.uiTheme);
|
||||
|
||||
this.addController(new ConfigContextController(this, config));
|
||||
this.addController(new BrandingContextController(this, brand));
|
||||
this.addController(new ModalOrchestrationController());
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { ThemedElement } from "@goauthentik/common/theme";
|
||||
import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
|
||||
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import type { CurrentBrand } from "@goauthentik/api";
|
||||
import { CoreApi } from "@goauthentik/api";
|
||||
|
||||
export class BrandContextController implements ReactiveController {
|
||||
host!: ReactiveElementHost<ThemedElement>;
|
||||
|
||||
context!: ContextProvider<{ __context__: CurrentBrand | undefined }>;
|
||||
|
||||
constructor(host: ReactiveElementHost<ThemedElement>) {
|
||||
this.host = host;
|
||||
this.context = new ContextProvider(this.host, {
|
||||
context: authentikBrandContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
this.fetch = this.fetch.bind(this);
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
fetch() {
|
||||
new CoreApi(DEFAULT_CONFIG).coreBrandsCurrentRetrieve().then((brand) => {
|
||||
this.context.setValue(brand);
|
||||
this.host.brand = brand;
|
||||
});
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH, this.fetch);
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH, this.fetch);
|
||||
}
|
||||
|
||||
hostUpdate() {
|
||||
// If the Interface changes its brand information for some reason,
|
||||
// we should notify all users of the context of that change. doesn't
|
||||
if (this.host.brand !== this.context.value) {
|
||||
this.context.setValue(this.host.brand);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { ThemedElement } from "@goauthentik/common/theme";
|
||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
|
||||
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import type { Config } from "@goauthentik/api";
|
||||
import { RootApi } from "@goauthentik/api";
|
||||
|
||||
export class ConfigContextController implements ReactiveController {
|
||||
host!: ReactiveElementHost<ThemedElement>;
|
||||
|
||||
context!: ContextProvider<{ __context__: Config | undefined }>;
|
||||
|
||||
constructor(host: ReactiveElementHost<ThemedElement>) {
|
||||
this.host = host;
|
||||
this.context = new ContextProvider(this.host, {
|
||||
context: authentikConfigContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
// Pre-hydrate from template-embedded config
|
||||
this.context.setValue(globalAK().config);
|
||||
this.host.config = globalAK().config;
|
||||
this.fetch = this.fetch.bind(this);
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
fetch() {
|
||||
new RootApi(DEFAULT_CONFIG).rootConfigRetrieve().then((config) => {
|
||||
this.context.setValue(config);
|
||||
this.host.config = config;
|
||||
});
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH, this.fetch);
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH, this.fetch);
|
||||
}
|
||||
|
||||
hostUpdate() {
|
||||
// If the Interface changes its config information, we should notify all
|
||||
// users of the context of that change, without creating an infinite
|
||||
// loop of resets.
|
||||
if (this.host.config !== this.context.value) {
|
||||
this.context.setValue(this.host.config);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/common/constants";
|
||||
import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
|
||||
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import type { LicenseSummary } from "@goauthentik/api";
|
||||
import { EnterpriseApi } from "@goauthentik/api";
|
||||
|
||||
import type { AkAuthenticatedInterface } from "./Interface";
|
||||
|
||||
export class EnterpriseContextController implements ReactiveController {
|
||||
host!: ReactiveElementHost<AkAuthenticatedInterface>;
|
||||
|
||||
context!: ContextProvider<{ __context__: LicenseSummary | undefined }>;
|
||||
|
||||
constructor(host: ReactiveElementHost<AkAuthenticatedInterface>) {
|
||||
this.host = host;
|
||||
this.context = new ContextProvider(this.host, {
|
||||
context: authentikEnterpriseContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
this.fetch = this.fetch.bind(this);
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
fetch() {
|
||||
new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve().then((enterprise) => {
|
||||
this.context.setValue(enterprise);
|
||||
this.host.licenseSummary = enterprise;
|
||||
});
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH_ENTERPRISE, this.fetch);
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH_ENTERPRISE, this.fetch);
|
||||
}
|
||||
|
||||
hostUpdate() {
|
||||
// If the Interface changes its config information, we should notify all
|
||||
// users of the context of that change, without creating an infinite
|
||||
// loop of resets.
|
||||
if (this.host.licenseSummary !== this.context.value) {
|
||||
this.context.setValue(this.host.licenseSummary);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
import { globalAK } from "@goauthentik/common/global.js";
|
||||
import { ThemedElement, applyDocumentTheme } from "@goauthentik/common/theme.js";
|
||||
import { UIConfig } from "@goauthentik/common/ui/config.js";
|
||||
import { AKElement } from "@goauthentik/elements/Base.js";
|
||||
import { VersionContextController } from "@goauthentik/elements/Interface/VersionContextController.js";
|
||||
import { ModalOrchestrationController } from "@goauthentik/elements/controllers/ModalOrchestrationController.js";
|
||||
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import {
|
||||
type Config,
|
||||
type CurrentBrand,
|
||||
type LicenseSummary,
|
||||
type Version,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { BrandContextController } from "./BrandContextController.js";
|
||||
import { ConfigContextController } from "./ConfigContextController.js";
|
||||
import { EnterpriseContextController } from "./EnterpriseContextController.js";
|
||||
|
||||
const configContext = Symbol("configContext");
|
||||
const modalController = Symbol("modalController");
|
||||
const versionContext = Symbol("versionContext");
|
||||
|
||||
export abstract class LightInterface extends AKElement implements ThemedElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
|
||||
|
||||
if (!document.documentElement.dataset.theme) {
|
||||
applyDocumentTheme(globalAK().brand.uiTheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Interface extends LightInterface implements ThemedElement {
|
||||
static styles = [PFBase];
|
||||
protected [configContext]: ConfigContextController;
|
||||
|
||||
protected [modalController]: ModalOrchestrationController;
|
||||
|
||||
@state()
|
||||
public config?: Config;
|
||||
|
||||
@state()
|
||||
public brand?: CurrentBrand;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addController(new BrandContextController(this));
|
||||
this[configContext] = new ConfigContextController(this);
|
||||
this[modalController] = new ModalOrchestrationController(this);
|
||||
}
|
||||
}
|
||||
|
||||
export interface AkAuthenticatedInterface extends ThemedElement {
|
||||
licenseSummary?: LicenseSummary;
|
||||
version?: Version;
|
||||
}
|
||||
|
||||
const enterpriseContext = Symbol("enterpriseContext");
|
||||
|
||||
export class AuthenticatedInterface extends Interface implements AkAuthenticatedInterface {
|
||||
[enterpriseContext]!: EnterpriseContextController;
|
||||
[versionContext]!: VersionContextController;
|
||||
|
||||
@state()
|
||||
public uiConfig?: UIConfig;
|
||||
|
||||
@state()
|
||||
public licenseSummary?: LicenseSummary;
|
||||
|
||||
@state()
|
||||
public version?: Version;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this[enterpriseContext] = new EnterpriseContextController(this);
|
||||
this[versionContext] = new VersionContextController(this);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { authentikVersionContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import type { ReactiveElementHost } from "@goauthentik/elements/types.js";
|
||||
|
||||
import { ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import type { Version } from "@goauthentik/api";
|
||||
import { AdminApi } from "@goauthentik/api";
|
||||
|
||||
import type { AkAuthenticatedInterface } from "./Interface";
|
||||
|
||||
export class VersionContextController implements ReactiveController {
|
||||
host!: ReactiveElementHost<AkAuthenticatedInterface>;
|
||||
|
||||
context!: ContextProvider<{ __context__: Version | undefined }>;
|
||||
|
||||
constructor(host: ReactiveElementHost<AkAuthenticatedInterface>) {
|
||||
this.host = host;
|
||||
this.context = new ContextProvider(this.host, {
|
||||
context: authentikVersionContext,
|
||||
initialValue: undefined,
|
||||
});
|
||||
this.fetch = this.fetch.bind(this);
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
fetch() {
|
||||
new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve().then((version) => {
|
||||
this.context.setValue(version);
|
||||
this.host.version = version;
|
||||
});
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH, this.fetch);
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH, this.fetch);
|
||||
}
|
||||
|
||||
hostUpdate() {
|
||||
// If the Interface changes its version information for some reason,
|
||||
// we should notify all users of the context of that change. doesn't
|
||||
if (this.host.version !== this.context.value) {
|
||||
this.context.setValue(this.host.version);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
import { authentikBrandContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { createMixin } from "@goauthentik/elements/types";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import type { CurrentBrand } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* A mixin that provides the current brand to the element.
|
||||
*/
|
||||
export interface StyleBrandMixin {
|
||||
/**
|
||||
* The current style branding configuration.
|
||||
*/
|
||||
brand: CurrentBrand;
|
||||
}
|
||||
|
||||
/**
|
||||
* A mixin that provides the current brand to the element.
|
||||
*
|
||||
* @category Mixin
|
||||
*
|
||||
* @see {@link https://lit.dev/docs/composition/mixins/#mixins-in-typescript | Lit Mixins}
|
||||
*/
|
||||
export const WithBrandConfig = createMixin<StyleBrandMixin>(
|
||||
({
|
||||
/**
|
||||
* The superclass constructor to extend.
|
||||
*/
|
||||
SuperClass,
|
||||
/**
|
||||
* Whether or not to subscribe to the context.
|
||||
*/
|
||||
subscribe = true,
|
||||
}) => {
|
||||
abstract class StyleBrandProvider extends SuperClass implements StyleBrandMixin {
|
||||
@consume({
|
||||
context: authentikBrandContext,
|
||||
subscribe,
|
||||
})
|
||||
@state()
|
||||
public brand!: CurrentBrand;
|
||||
}
|
||||
|
||||
return StyleBrandProvider;
|
||||
},
|
||||
);
|
@ -1,4 +0,0 @@
|
||||
import { AuthenticatedInterface, Interface, LightInterface } from "./Interface";
|
||||
|
||||
export { Interface, AuthenticatedInterface, LightInterface };
|
||||
export default Interface;
|
@ -1,35 +0,0 @@
|
||||
import { authentikVersionContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
import { Constructor } from "@lit/reactive-element/decorators/base.js";
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
import type { Version } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* A consumer that provides version information to the element.
|
||||
*/
|
||||
export declare class VersionConsumer {
|
||||
/**
|
||||
* The current version of the application.
|
||||
*/
|
||||
public readonly version: Version;
|
||||
}
|
||||
|
||||
export function WithVersion<T extends Constructor<LitElement>>(
|
||||
/**
|
||||
* The superclass constructor to extend.
|
||||
*/
|
||||
SuperClass: T,
|
||||
/**
|
||||
* Whether or not to subscribe to the context.
|
||||
*/
|
||||
subscribe = true,
|
||||
) {
|
||||
class VersionProvider extends SuperClass {
|
||||
@consume({ context: authentikVersionContext, subscribe })
|
||||
public version!: Version;
|
||||
}
|
||||
|
||||
return VersionProvider as Constructor<VersionConsumer> & T;
|
||||
}
|
@ -5,7 +5,7 @@ import { customEvent } from "@goauthentik/elements/utils/customEvents";
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { WithBrandConfig } from "../Interface/brandProvider";
|
||||
import { WithBrandConfig } from "../mixins/branding";
|
||||
import { initializeLocalization } from "./configureLocale";
|
||||
import type { LocaleGetter, LocaleSetter } from "./configureLocale";
|
||||
import { DEFAULT_LOCALE, autoDetectLanguage, getBestMatchLocale } from "./helpers";
|
||||
@ -70,7 +70,7 @@ export class LocaleContext extends WithBrandConfig(AKElement) {
|
||||
}
|
||||
|
||||
updateLocale(requestedLocale: string | undefined = undefined) {
|
||||
const localeRequest = autoDetectLanguage(requestedLocale, this.brand?.defaultLocale);
|
||||
const localeRequest = autoDetectLanguage(requestedLocale, this.brand.defaultLocale);
|
||||
const locale = getBestMatchLocale(localeRequest);
|
||||
if (!locale) {
|
||||
console.warn(`authentik/locale: failed to find locale for code ${localeRequest}`);
|
||||
|
@ -1,22 +1,5 @@
|
||||
import { createContext, useContext } from "react";
|
||||
|
||||
/**
|
||||
* A parsed JSON module containing MDX content and metadata from ESBuild.
|
||||
*/
|
||||
export interface MDXModule {
|
||||
/**
|
||||
* The Markdown content of the module.
|
||||
*/
|
||||
content: string;
|
||||
/**
|
||||
* The public path of the module, typically identical to the docs page path.
|
||||
*/
|
||||
publicPath?: string;
|
||||
/**
|
||||
* The public directory of the module, used to resolve relative links.
|
||||
*/
|
||||
publicDirectory?: string;
|
||||
}
|
||||
import type { MDXModule } from "~docs/types";
|
||||
|
||||
/**
|
||||
* Fetches an MDX module from a URL or ESBuild static asset.
|
||||
|
@ -1,15 +1,14 @@
|
||||
import "@goauthentik/elements/Alert";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import {
|
||||
MDXModule,
|
||||
MDXModuleContext,
|
||||
fetchMDXModule,
|
||||
} from "@goauthentik/elements/ak-mdx/MDXModuleContext";
|
||||
import { MDXAnchor } from "@goauthentik/elements/ak-mdx/components/MDXAnchor";
|
||||
import { MDXWrapper } from "@goauthentik/elements/ak-mdx/components/MDXWrapper";
|
||||
import { remarkAdmonition } from "@goauthentik/elements/ak-mdx/remark/remark-admonition";
|
||||
import { remarkHeadings } from "@goauthentik/elements/ak-mdx/remark/remark-headings";
|
||||
import { remarkLists } from "@goauthentik/elements/ak-mdx/remark/remark-lists";
|
||||
import { globalAK } from "#common/global";
|
||||
import "#elements/Alert";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { MDXModuleContext, fetchMDXModule } from "#elements/ak-mdx/MDXModuleContext";
|
||||
import { MDXAnchor } from "#elements/ak-mdx/components/MDXAnchor";
|
||||
import { MDXWrapper } from "#elements/ak-mdx/components/MDXWrapper";
|
||||
import { remarkAdmonition } from "#elements/ak-mdx/remark/remark-admonition";
|
||||
import { remarkHeadings } from "#elements/ak-mdx/remark/remark-headings";
|
||||
import { remarkLists } from "#elements/ak-mdx/remark/remark-lists";
|
||||
import { WithAuthentikConfig } from "#elements/mixins/config";
|
||||
import { DistDirectoryName, StaticDirectoryName } from "#paths";
|
||||
import { compile as compileMDX, run as runMDX } from "@mdx-js/mdx";
|
||||
import apacheGrammar from "highlight.js/lib/languages/apache";
|
||||
import diffGrammar from "highlight.js/lib/languages/diff";
|
||||
@ -26,11 +25,12 @@ import remarkFrontmatter from "remark-frontmatter";
|
||||
import remarkGFM from "remark-gfm";
|
||||
import remarkMdxFrontmatter from "remark-mdx-frontmatter";
|
||||
import remarkParse from "remark-parse";
|
||||
import type { MDXModule } from "~docs/types";
|
||||
|
||||
import { css } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import OneDark from "@goauthentik/common/styles/one-dark.css";
|
||||
import OneDark from "#common/styles/one-dark.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||
import PFTable from "@patternfly/patternfly/components/Table/table.css";
|
||||
@ -54,7 +54,7 @@ const highlightThemeOptions: HighlightOptions = {
|
||||
export type Replacer = (input: string) => string;
|
||||
|
||||
@customElement("ak-mdx")
|
||||
export class AKMDX extends AKElement {
|
||||
export class AKMDX extends WithAuthentikConfig(AKElement) {
|
||||
@property({
|
||||
reflect: true,
|
||||
})
|
||||
@ -166,9 +166,17 @@ export class AKMDX extends AKElement {
|
||||
this.#reactRoot = createRoot(this.shadowRoot!);
|
||||
|
||||
let nextMDXModule: MDXModule | undefined;
|
||||
const { relBase } = globalAK().api;
|
||||
|
||||
if (this.url) {
|
||||
nextMDXModule = await fetchMDXModule(this.url);
|
||||
const pathname =
|
||||
relBase +
|
||||
StaticDirectoryName +
|
||||
"/" +
|
||||
DistDirectoryName +
|
||||
this.url.slice(this.url.indexOf("/assets"));
|
||||
|
||||
nextMDXModule = await fetchMDXModule(pathname);
|
||||
} else {
|
||||
nextMDXModule = {
|
||||
content: this.content,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, nothing } from "lit";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { WithVersion } from "#elements/mixins/version";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { html, nothing } from "lit";
|
||||
|
74
web/src/elements/controllers/BrandContextController.ts
Normal file
74
web/src/elements/controllers/BrandContextController.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
import { isAbortError } from "#common/errors/network";
|
||||
import { BrandingContext, BrandingMixin } from "#elements/mixins/branding";
|
||||
import type { ReactiveElementHost } from "#elements/types";
|
||||
|
||||
import { Context, ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import { CoreApi, CurrentBrand } from "@goauthentik/api";
|
||||
|
||||
export class BrandingContextController implements ReactiveController {
|
||||
#log = console.debug.bind(console, `authentik/controller/branding`);
|
||||
#abortController: null | AbortController = null;
|
||||
|
||||
#host: ReactiveElementHost<BrandingMixin>;
|
||||
#context: ContextProvider<Context<unknown, CurrentBrand>>;
|
||||
|
||||
constructor(host: ReactiveElementHost<BrandingMixin>, initialValue: CurrentBrand) {
|
||||
this.#host = host;
|
||||
this.#context = new ContextProvider(this.#host, {
|
||||
context: BrandingContext,
|
||||
initialValue,
|
||||
});
|
||||
this.#host.brand = initialValue;
|
||||
}
|
||||
|
||||
#fetch = () => {
|
||||
this.#log("Fetching configuration...");
|
||||
|
||||
this.#abortController?.abort();
|
||||
|
||||
this.#abortController = new AbortController();
|
||||
|
||||
return new CoreApi(DEFAULT_CONFIG)
|
||||
.coreBrandsCurrentRetrieve({
|
||||
signal: this.#abortController.signal,
|
||||
})
|
||||
.then((brand) => {
|
||||
this.#context.setValue(brand);
|
||||
this.#host.brand = brand;
|
||||
})
|
||||
|
||||
.catch((error: unknown) => {
|
||||
if (isAbortError(error)) {
|
||||
this.#log("Aborted fetching brand");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
this.#abortController = null;
|
||||
});
|
||||
};
|
||||
|
||||
public hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH, this.#fetch);
|
||||
this.#fetch();
|
||||
}
|
||||
|
||||
public hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH, this.#fetch);
|
||||
this.#abortController?.abort();
|
||||
}
|
||||
|
||||
public hostUpdate() {
|
||||
// If the Interface changes its brand information for some reason,
|
||||
// we should notify all users of the context of that change. doesn't
|
||||
if (this.#host.brand && this.#host.brand !== this.#context.value) {
|
||||
this.#context.setValue(this.#host.brand);
|
||||
}
|
||||
}
|
||||
}
|
79
web/src/elements/controllers/ConfigContextController.ts
Normal file
79
web/src/elements/controllers/ConfigContextController.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
import { isAbortError } from "#common/errors/network";
|
||||
import { AKConfigMixin, AuthentikConfigContext } from "#elements/mixins/config";
|
||||
import type { ReactiveElementHost } from "#elements/types";
|
||||
|
||||
import { Context, ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import { Config, RootApi } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* A controller that provides the application configuration to the element.
|
||||
*/
|
||||
export class ConfigContextController implements ReactiveController {
|
||||
#log = console.debug.bind(console, `authentik/controller/config`);
|
||||
#abortController: null | AbortController = null;
|
||||
|
||||
#host: ReactiveElementHost<AKConfigMixin>;
|
||||
#context: ContextProvider<Context<unknown, Config>>;
|
||||
|
||||
constructor(host: ReactiveElementHost<AKConfigMixin>, initialValue: Config) {
|
||||
this.#host = host;
|
||||
|
||||
this.#context = new ContextProvider(this.#host, {
|
||||
context: AuthentikConfigContext,
|
||||
initialValue,
|
||||
});
|
||||
|
||||
this.#host.authentikConfig = initialValue;
|
||||
}
|
||||
|
||||
#fetch = () => {
|
||||
this.#log("Fetching configuration...");
|
||||
|
||||
this.#abortController?.abort();
|
||||
|
||||
this.#abortController = new AbortController();
|
||||
|
||||
return new RootApi(DEFAULT_CONFIG)
|
||||
.rootConfigRetrieve({
|
||||
signal: this.#abortController.signal,
|
||||
})
|
||||
.then((authentikConfig) => {
|
||||
this.#context.setValue(authentikConfig);
|
||||
this.#host.authentikConfig = authentikConfig;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (isAbortError(error)) {
|
||||
this.#log("Aborted fetching configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
this.#abortController = null;
|
||||
});
|
||||
};
|
||||
|
||||
public hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH, this.#fetch);
|
||||
this.#fetch();
|
||||
}
|
||||
|
||||
public hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH, this.#fetch);
|
||||
this.#abortController?.abort();
|
||||
}
|
||||
|
||||
public hostUpdate() {
|
||||
// If the Interface changes its config information, we should notify all
|
||||
// users of the context of that change, without creating an infinite
|
||||
// loop of resets.
|
||||
if (this.#host.authentikConfig && this.#host.authentikConfig !== this.#context.value) {
|
||||
this.#context.setValue(this.#host.authentikConfig);
|
||||
}
|
||||
}
|
||||
}
|
77
web/src/elements/controllers/EnterpriseContextController.ts
Normal file
77
web/src/elements/controllers/EnterpriseContextController.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH_ENTERPRISE } from "#common/constants";
|
||||
import { isAbortError } from "#common/errors/network";
|
||||
import { LicenseContext, LicenseMixin } from "#elements/mixins/license";
|
||||
import type { ReactiveElementHost } from "#elements/types";
|
||||
|
||||
import { Context, ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import { EnterpriseApi, LicenseSummary } from "@goauthentik/api";
|
||||
|
||||
export class LicenseContextController implements ReactiveController {
|
||||
#log = console.debug.bind(console, `authentik/controller/license`);
|
||||
#abortController: null | AbortController = null;
|
||||
|
||||
#host: ReactiveElementHost<LicenseMixin>;
|
||||
#context: ContextProvider<Context<unknown, LicenseSummary>>;
|
||||
|
||||
constructor(host: ReactiveElementHost<LicenseMixin>, initialValue?: LicenseSummary) {
|
||||
this.#host = host;
|
||||
this.#context = new ContextProvider(this.#host, {
|
||||
context: LicenseContext,
|
||||
initialValue: initialValue,
|
||||
});
|
||||
}
|
||||
|
||||
#fetch = () => {
|
||||
this.#log("Fetching license summary...");
|
||||
|
||||
this.#abortController?.abort();
|
||||
|
||||
this.#abortController = new AbortController();
|
||||
|
||||
return new EnterpriseApi(DEFAULT_CONFIG)
|
||||
.enterpriseLicenseSummaryRetrieve(
|
||||
{},
|
||||
{
|
||||
signal: this.#abortController.signal,
|
||||
},
|
||||
)
|
||||
.then((enterprise) => {
|
||||
this.#context.setValue(enterprise);
|
||||
this.#host.licenseSummary = enterprise;
|
||||
})
|
||||
|
||||
.catch((error: unknown) => {
|
||||
if (isAbortError(error)) {
|
||||
this.#log("Aborted fetching license summary");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
this.#abortController = null;
|
||||
});
|
||||
};
|
||||
|
||||
public hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH_ENTERPRISE, this.#fetch);
|
||||
this.#fetch();
|
||||
}
|
||||
|
||||
public hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH_ENTERPRISE, this.#fetch);
|
||||
this.#abortController?.abort();
|
||||
}
|
||||
|
||||
public hostUpdate() {
|
||||
// If the Interface changes its config information, we should notify all
|
||||
// users of the context of that change, without creating an infinite
|
||||
// loop of resets.
|
||||
if (this.#host.licenseSummary && this.#host.licenseSummary !== this.#context.value) {
|
||||
this.#context.setValue(this.#host.licenseSummary);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
import { bound } from "@goauthentik/elements/decorators/bound.js";
|
||||
import { LitElement, ReactiveController } from "lit";
|
||||
|
||||
import { LitElement, ReactiveController, ReactiveControllerHost } from "lit";
|
||||
|
||||
type ReactiveElementHost = Partial<ReactiveControllerHost> & LitElement;
|
||||
|
||||
type ModalElement = LitElement & { closeModal(): void | boolean };
|
||||
interface ModalElement extends LitElement {
|
||||
closeModal(): void | boolean;
|
||||
}
|
||||
|
||||
export class ModalShowEvent extends Event {
|
||||
modal: ModalElement;
|
||||
@ -50,75 +48,70 @@ const modalIsLive = (modal: ModalElement) => modal.isConnected && modal.checkVis
|
||||
*/
|
||||
|
||||
export class ModalOrchestrationController implements ReactiveController {
|
||||
host!: ReactiveElementHost;
|
||||
#knownModals: ModalElement[] = [];
|
||||
|
||||
knownModals: ModalElement[] = [];
|
||||
|
||||
constructor(host: ReactiveElementHost) {
|
||||
this.host = host;
|
||||
host.addController(this);
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
public hostConnected() {
|
||||
window.addEventListener("keyup", this.handleKeyup);
|
||||
window.addEventListener("ak-modal-show", this.addModal);
|
||||
window.addEventListener("ak-modal-show", this.#addModal);
|
||||
window.addEventListener("ak-modal-hide", this.closeModal);
|
||||
}
|
||||
|
||||
hostDisconnected() {
|
||||
public hostDisconnected() {
|
||||
window.removeEventListener("keyup", this.handleKeyup);
|
||||
window.removeEventListener("ak-modal-show", this.addModal);
|
||||
window.removeEventListener("ak-modal-show", this.#addModal);
|
||||
window.removeEventListener("ak-modal-hide", this.closeModal);
|
||||
}
|
||||
|
||||
@bound
|
||||
addModal(e: ModalShowEvent) {
|
||||
this.knownModals = [...this.knownModals, e.modal];
|
||||
}
|
||||
#addModal = (e: ModalShowEvent) => {
|
||||
this.#knownModals = [...this.#knownModals, e.modal];
|
||||
};
|
||||
|
||||
scheduleCleanup(modal: ModalElement) {
|
||||
setTimeout(() => {
|
||||
this.knownModals = this.knownModals.filter((m) => modalIsLive(m) && modal !== m);
|
||||
}, 0);
|
||||
}
|
||||
#cleanupFrameID = -1;
|
||||
|
||||
@bound
|
||||
closeModal(e: ModalHideEvent) {
|
||||
#scheduleCleanup = (modal: ModalElement) => {
|
||||
cancelAnimationFrame(this.#cleanupFrameID);
|
||||
|
||||
this.#cleanupFrameID = requestAnimationFrame(() => {
|
||||
this.#knownModals = this.#knownModals.filter((m) => modalIsLive(m) && modal !== m);
|
||||
});
|
||||
};
|
||||
|
||||
closeModal = (e: ModalHideEvent) => {
|
||||
const modal = e.modal;
|
||||
if (!modalIsLive(modal)) {
|
||||
return;
|
||||
}
|
||||
if (modal.closeModal() !== false) {
|
||||
this.scheduleCleanup(modal);
|
||||
}
|
||||
}
|
||||
|
||||
removeTopmostModal() {
|
||||
const knownModals = [...this.knownModals];
|
||||
if (!modalIsLive(modal)) return;
|
||||
|
||||
if (modal.closeModal() !== false) {
|
||||
this.#scheduleCleanup(modal);
|
||||
}
|
||||
};
|
||||
|
||||
#removeTopmostModal = () => {
|
||||
const knownModals = [...this.#knownModals];
|
||||
|
||||
// Pop off modals until you find the first live one, schedule it to be closed, and make that
|
||||
// cleaned list the current state. Since this is our *only* state object, this has the
|
||||
// effect of creating a new "knownModals" collection with some semantics.
|
||||
while (true) {
|
||||
const modal = knownModals.pop();
|
||||
if (!modal) {
|
||||
break;
|
||||
}
|
||||
if (!modalIsLive(modal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modal) break;
|
||||
|
||||
if (!modalIsLive(modal)) continue;
|
||||
|
||||
if (modal.closeModal() !== false) {
|
||||
this.scheduleCleanup(modal);
|
||||
this.#scheduleCleanup(modal);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
this.knownModals = knownModals;
|
||||
}
|
||||
this.#knownModals = knownModals;
|
||||
};
|
||||
|
||||
@bound
|
||||
handleKeyup(e: KeyboardEvent) {
|
||||
handleKeyup = ({ key }: KeyboardEvent) => {
|
||||
// The latter handles Firefox 37 and earlier.
|
||||
if (e.key === "Escape" || e.key === "Esc") {
|
||||
this.removeTopmostModal();
|
||||
if (key === "Escape" || key === "Esc") {
|
||||
this.#removeTopmostModal();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
73
web/src/elements/controllers/VersionContextController.ts
Normal file
73
web/src/elements/controllers/VersionContextController.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
import { isAbortError } from "#common/errors/network";
|
||||
import { VersionContext, VersionMixin } from "#elements/mixins/version";
|
||||
import type { ReactiveElementHost } from "#elements/types";
|
||||
|
||||
import { Context, ContextProvider } from "@lit/context";
|
||||
import type { ReactiveController } from "lit";
|
||||
|
||||
import { AdminApi, Version } from "@goauthentik/api";
|
||||
|
||||
export class VersionContextController implements ReactiveController {
|
||||
#log = console.debug.bind(console, `authentik/controller/version`);
|
||||
#abortController: null | AbortController = null;
|
||||
|
||||
#host: ReactiveElementHost<VersionMixin>;
|
||||
#context: ContextProvider<Context<unknown, Version>>;
|
||||
|
||||
constructor(host: ReactiveElementHost<VersionMixin>, initialValue?: Version) {
|
||||
this.#host = host;
|
||||
this.#context = new ContextProvider(this.#host, {
|
||||
context: VersionContext,
|
||||
initialValue,
|
||||
});
|
||||
}
|
||||
|
||||
#fetch = () => {
|
||||
this.#log("Fetching latest version...");
|
||||
|
||||
this.#abortController?.abort();
|
||||
|
||||
this.#abortController = new AbortController();
|
||||
|
||||
return new AdminApi(DEFAULT_CONFIG)
|
||||
.adminVersionRetrieve({
|
||||
signal: this.#abortController.signal,
|
||||
})
|
||||
.then((version) => {
|
||||
this.#context.setValue(version);
|
||||
this.#host.version = version;
|
||||
})
|
||||
|
||||
.catch((error: unknown) => {
|
||||
if (isAbortError(error)) {
|
||||
this.#log("Aborted fetching license summary");
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
this.#abortController = null;
|
||||
});
|
||||
};
|
||||
|
||||
public hostConnected() {
|
||||
window.addEventListener(EVENT_REFRESH, this.#fetch);
|
||||
this.#fetch();
|
||||
}
|
||||
|
||||
public hostDisconnected() {
|
||||
window.removeEventListener(EVENT_REFRESH, this.#fetch);
|
||||
this.#abortController?.abort();
|
||||
}
|
||||
|
||||
public hostUpdate() {
|
||||
// If the Interface changes its version information for some reason,
|
||||
// we should notify all users of the context of that change. doesn't
|
||||
if (this.#host.version && this.#host.version !== this.#context.value) {
|
||||
this.#context.setValue(this.#host.version);
|
||||
}
|
||||
}
|
||||
}
|
80
web/src/elements/mixins/branding.ts
Normal file
80
web/src/elements/mixins/branding.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { DefaultBrand } from "#common/ui/config";
|
||||
import { createMixin } from "#elements/types";
|
||||
|
||||
import { consume, createContext } from "@lit/context";
|
||||
|
||||
import type { CurrentBrand, FooterLink } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* The Lit context for application branding.
|
||||
*
|
||||
* @category Context
|
||||
* @see {@linkcode BrandingMixin}
|
||||
* @see {@linkcode WithBrandConfig}
|
||||
*/
|
||||
export const BrandingContext = createContext<CurrentBrand>(
|
||||
Symbol.for("authentik-branding-context"),
|
||||
);
|
||||
|
||||
/**
|
||||
* A mixin that provides the current brand to the element.
|
||||
*
|
||||
* @see {@linkcode WithBrandConfig}
|
||||
*/
|
||||
export interface BrandingMixin {
|
||||
/**
|
||||
* The current style branding configuration.
|
||||
*/
|
||||
readonly brand: Readonly<CurrentBrand>;
|
||||
|
||||
readonly brandingTitle: string;
|
||||
readonly brandingLogo: string;
|
||||
readonly brandingFavicon: string;
|
||||
readonly brandingFooterLinks: FooterLink[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A mixin that provides the current brand to the element.
|
||||
*
|
||||
* @category Mixin
|
||||
*
|
||||
* @see {@link https://lit.dev/docs/composition/mixins/#mixins-in-typescript | Lit Mixins}
|
||||
*/
|
||||
export const WithBrandConfig = createMixin<BrandingMixin>(
|
||||
({
|
||||
/**
|
||||
* The superclass constructor to extend.
|
||||
*/
|
||||
SuperClass,
|
||||
/**
|
||||
* Whether or not to subscribe to the context.
|
||||
*/
|
||||
subscribe = true,
|
||||
}) => {
|
||||
abstract class BrandingProvider extends SuperClass implements BrandingMixin {
|
||||
@consume({
|
||||
context: BrandingContext,
|
||||
subscribe,
|
||||
})
|
||||
public brand!: CurrentBrand;
|
||||
|
||||
public get brandingTitle(): string {
|
||||
return this.brand.brandingTitle ?? DefaultBrand.brandingTitle;
|
||||
}
|
||||
|
||||
public get brandingLogo(): string {
|
||||
return this.brand.brandingLogo ?? DefaultBrand.brandingLogo;
|
||||
}
|
||||
|
||||
public get brandingFavicon(): string {
|
||||
return this.brand.brandingFavicon ?? DefaultBrand.brandingFavicon;
|
||||
}
|
||||
|
||||
public get brandingFooterLinks(): FooterLink[] {
|
||||
return this.brand.uiFooterLinks ?? DefaultBrand.uiFooterLinks;
|
||||
}
|
||||
}
|
||||
|
||||
return BrandingProvider;
|
||||
},
|
||||
);
|
@ -1,10 +1,7 @@
|
||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { AKConfigMixin } from "#elements/mixins/config";
|
||||
import { createMixin } from "@goauthentik/elements/types";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
|
||||
import { CapabilitiesEnum } from "@goauthentik/api";
|
||||
import { Config } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* A consumer that provides the capability methods to the element.
|
||||
@ -22,13 +19,6 @@ export interface CapabilitiesMixin {
|
||||
): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexically-scoped symbol for the capabilities configuration.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
const kCapabilitiesConfig: unique symbol = Symbol("capabilitiesConfig");
|
||||
|
||||
/**
|
||||
* A mixin that provides the capability methods to the element.
|
||||
*
|
||||
@ -37,14 +27,14 @@ const kCapabilitiesConfig: unique symbol = Symbol("capabilitiesConfig");
|
||||
* After importing, simply mixin this function:
|
||||
*
|
||||
* ```ts
|
||||
* export class AkMyNiftyNewFeature extends withCapabilitiesContext(AKElement) {
|
||||
* export class AkMyNiftyNewFeature extends WithCapabilitiesConfig(AKElement) {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* And then if you need to check on a capability:
|
||||
*
|
||||
* ```ts
|
||||
* if (this.can(CapabilitiesEnum.IsEnterprise) { ... }
|
||||
* if (this.can(CapabilitiesEnum.IsEnterprise)) { ... }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
@ -53,21 +43,15 @@ const kCapabilitiesConfig: unique symbol = Symbol("capabilitiesConfig");
|
||||
* @category Mixin
|
||||
*
|
||||
*/
|
||||
export const WithCapabilitiesConfig = createMixin<CapabilitiesMixin>(
|
||||
({ SuperClass, subscribe = true }) => {
|
||||
export const WithCapabilitiesConfig = createMixin<CapabilitiesMixin, AKConfigMixin>(
|
||||
({ SuperClass }) => {
|
||||
abstract class CapabilitiesProvider extends SuperClass implements CapabilitiesMixin {
|
||||
@consume({
|
||||
context: authentikConfigContext,
|
||||
subscribe,
|
||||
})
|
||||
private readonly [kCapabilitiesConfig]: Config | undefined;
|
||||
|
||||
public can(capability: CapabilitiesEnum) {
|
||||
const config = this[kCapabilitiesConfig];
|
||||
const config = this.authentikConfig;
|
||||
|
||||
if (!config) {
|
||||
throw new Error(
|
||||
"ConfigContext: Attempted to access site configuration before initialization.",
|
||||
`ConfigContext: Attempted to check capability "${capability}" before initialization. Does the element have the AuthentikConfigMixin applied?`,
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,23 @@
|
||||
import { authentikConfigContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { createMixin } from "@goauthentik/elements/types";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
import { consume, createContext } from "@lit/context";
|
||||
|
||||
import type { Config } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* The Lit context for the application configuration.
|
||||
*
|
||||
* @category Context
|
||||
* @see {@linkcode AKConfigMixin}
|
||||
* @see {@linkcode WithAuthentikConfig}
|
||||
*/
|
||||
export const AuthentikConfigContext = createContext<Config>(Symbol.for("authentik-config-context"));
|
||||
|
||||
/**
|
||||
* A consumer that provides the application configuration to the element.
|
||||
*
|
||||
* @category Mixin
|
||||
* @see {@linkcode WithAuthentikConfig}
|
||||
*/
|
||||
export interface AKConfigMixin {
|
||||
/**
|
||||
@ -33,7 +44,7 @@ export const WithAuthentikConfig = createMixin<AKConfigMixin>(
|
||||
}) => {
|
||||
abstract class AKConfigProvider extends SuperClass implements AKConfigMixin {
|
||||
@consume({
|
||||
context: authentikConfigContext,
|
||||
context: AuthentikConfigContext,
|
||||
subscribe,
|
||||
})
|
||||
public readonly authentikConfig!: Readonly<Config>;
|
@ -1,10 +1,13 @@
|
||||
import { authentikEnterpriseContext } from "@goauthentik/elements/AuthentikContexts";
|
||||
import { createMixin } from "@goauthentik/elements/types";
|
||||
import { createMixin } from "#elements/types";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
import { consume, createContext } from "@lit/context";
|
||||
|
||||
import { type LicenseSummary, LicenseSummaryStatusEnum } from "@goauthentik/api";
|
||||
|
||||
export const LicenseContext = createContext<LicenseSummary>(
|
||||
Symbol.for("authentik-license-context"),
|
||||
);
|
||||
|
||||
/**
|
||||
* A consumer that provides license information to the element.
|
||||
*/
|
||||
@ -26,7 +29,7 @@ export interface LicenseMixin {
|
||||
export const WithLicenseSummary = createMixin<LicenseMixin>(({ SuperClass, subscribe = true }) => {
|
||||
abstract class LicenseProvider extends SuperClass implements LicenseMixin {
|
||||
@consume({
|
||||
context: authentikEnterpriseContext,
|
||||
context: LicenseContext,
|
||||
subscribe,
|
||||
})
|
||||
public readonly licenseSummary!: LicenseSummary;
|
61
web/src/elements/mixins/version.ts
Normal file
61
web/src/elements/mixins/version.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { createMixin } from "#elements/types";
|
||||
|
||||
import { consume, createContext } from "@lit/context";
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import type { Version } from "@goauthentik/api";
|
||||
|
||||
/**
|
||||
* The Lit context for application branding.
|
||||
*
|
||||
* @category Context
|
||||
* @see {@linkcode VersionMixin}
|
||||
* @see {@linkcode WithVersion}
|
||||
*/
|
||||
|
||||
export const VersionContext = createContext<Version>(Symbol.for("authentik-version-context"));
|
||||
|
||||
/**
|
||||
* A mixin that provides the current version to the element.
|
||||
*
|
||||
* @see {@linkcode WithVersion}
|
||||
*/
|
||||
export interface VersionMixin {
|
||||
/**
|
||||
* The current version of the application.
|
||||
*
|
||||
* @format semver
|
||||
*/
|
||||
readonly version: Version;
|
||||
}
|
||||
|
||||
/**
|
||||
* A mixin that provides the current authentik version to the element.
|
||||
*
|
||||
* @category Mixin
|
||||
*
|
||||
* @see {@link https://lit.dev/docs/composition/mixins/#mixins-in-typescript | Lit Mixins}
|
||||
*/
|
||||
export const WithVersion = createMixin<VersionMixin>(
|
||||
({
|
||||
/**
|
||||
* The superclass constructor to extend.
|
||||
*/
|
||||
SuperClass,
|
||||
/**
|
||||
* Whether or not to subscribe to the context.
|
||||
*/
|
||||
subscribe = true,
|
||||
}) => {
|
||||
abstract class VersionProvider extends SuperClass implements VersionMixin {
|
||||
@consume({
|
||||
context: VersionContext,
|
||||
subscribe,
|
||||
})
|
||||
@state()
|
||||
public version!: Version;
|
||||
}
|
||||
|
||||
return VersionProvider;
|
||||
},
|
||||
);
|
@ -1,9 +1,10 @@
|
||||
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint.js";
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import { WithVersion } from "#elements/mixins/version";
|
||||
import type { AdminInterface } from "@goauthentik/admin/AdminInterface/index.entrypoint";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { rootInterface } from "@goauthentik/common/theme";
|
||||
import { DefaultBrand } from "@goauthentik/common/ui/config";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, css, html, nothing } from "lit";
|
||||
|
@ -15,10 +15,13 @@ export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
|
||||
*/
|
||||
export type ReactiveElementHost<T> = Partial<ReactiveControllerHost & Writeable<T>> & HTMLElement;
|
||||
|
||||
export type AbstractLitElementConstructor = abstract new (...args: never[]) => LitElement;
|
||||
export type AbstractLitElementConstructor<T = unknown> = abstract new (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
...args: any[]
|
||||
) => LitElement & T;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type LitElementConstructor = new (...args: any[]) => LitElement;
|
||||
export type LitElementConstructor<T = unknown> = new (...args: any[]) => LitElement & T;
|
||||
|
||||
/**
|
||||
* A constructor that has been extended with a mixin.
|
||||
@ -37,11 +40,11 @@ export type ConstructorWithMixin<SuperClass, Mixin> =
|
||||
/**
|
||||
* The init object passed to the `createMixin` callback.
|
||||
*/
|
||||
export interface CreateMixinInit<T extends LitElementConstructor = LitElementConstructor> {
|
||||
export interface CreateMixinInit<C = unknown> {
|
||||
/**
|
||||
* The superclass constructor to extend.
|
||||
*/
|
||||
SuperClass: T;
|
||||
SuperClass: LitElementConstructor<C>;
|
||||
/**
|
||||
* Whether or not to subscribe to the context.
|
||||
*
|
||||
@ -58,7 +61,9 @@ export interface CreateMixinInit<T extends LitElementConstructor = LitElementCon
|
||||
* @param mixinCallback The callback that will be called to create the mixin.
|
||||
* @template Mixin The mixin class to union with the superclass.
|
||||
*/
|
||||
export function createMixin<Mixin>(mixinCallback: (init: CreateMixinInit) => unknown) {
|
||||
export function createMixin<Mixin, C = unknown>(
|
||||
mixinCallback: (init: CreateMixinInit<C>) => unknown,
|
||||
) {
|
||||
return <T extends LitElementConstructor | AbstractLitElementConstructor>(
|
||||
/**
|
||||
* The superclass constructor to extend.
|
||||
@ -73,7 +78,7 @@ export function createMixin<Mixin>(mixinCallback: (init: CreateMixinInit) => unk
|
||||
subscribe?: boolean,
|
||||
) => {
|
||||
const MixinClass = mixinCallback({
|
||||
SuperClass: SuperClass as LitElementConstructor,
|
||||
SuperClass: SuperClass as LitElementConstructor<C>,
|
||||
subscribe,
|
||||
});
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { resolveUITheme } from "@goauthentik/common/theme";
|
||||
import { rootInterface } from "@goauthentik/elements/Base";
|
||||
import { resolveUITheme, rootInterface } from "#common/theme";
|
||||
import type { AKElement } from "#elements/Base";
|
||||
|
||||
export function themeImage(rawPath: string) {
|
||||
const enabledTheme = rootInterface()?.activeTheme || resolveUITheme();
|
||||
const enabledTheme = rootInterface<AKElement>()?.activeTheme || resolveUITheme();
|
||||
|
||||
return rawPath.replaceAll("%(theme)s", enabledTheme);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import "@goauthentik/admin/common/ak-license-notice";
|
||||
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider";
|
||||
import { WizardPage } from "@goauthentik/elements/wizard/WizardPage";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
|
@ -1,24 +1,21 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import {
|
||||
EVENT_FLOW_ADVANCE,
|
||||
EVENT_FLOW_INSPECTOR_TOGGLE,
|
||||
TITLE_DEFAULT,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { DefaultBrand } from "@goauthentik/common/ui/config";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
import "@goauthentik/flow/components/ak-brand-footer";
|
||||
import "@goauthentik/flow/sources/apple/AppleLoginInit";
|
||||
import "@goauthentik/flow/sources/plex/PlexLoginInit";
|
||||
import "@goauthentik/flow/stages/FlowErrorStage";
|
||||
import "@goauthentik/flow/stages/FlowFrameStage";
|
||||
import "@goauthentik/flow/stages/RedirectStage";
|
||||
import { StageHost, SubmitOptions } from "@goauthentik/flow/stages/base";
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_FLOW_ADVANCE, EVENT_FLOW_INSPECTOR_TOGGLE } from "#common/constants";
|
||||
import { globalAK } from "#common/global";
|
||||
import { configureSentry } from "#common/sentry/index";
|
||||
import { WebsocketClient } from "#common/ws";
|
||||
import { Interface } from "#elements/Interface";
|
||||
import "#elements/LoadingOverlay";
|
||||
import "#elements/ak-locale-context/ak-locale-context";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import { themeImage } from "#elements/utils/images";
|
||||
import "#flow/components/ak-brand-footer";
|
||||
import "#flow/sources/apple/AppleLoginInit";
|
||||
import "#flow/sources/plex/PlexLoginInit";
|
||||
import "#flow/stages/FlowErrorStage";
|
||||
import "#flow/stages/FlowFrameStage";
|
||||
import "#flow/stages/RedirectStage";
|
||||
import { StageHost, SubmitOptions } from "#flow/stages/base";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, css, html, nothing } from "lit";
|
||||
@ -48,7 +45,10 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor extends Interface implements StageHost {
|
||||
export class FlowExecutor
|
||||
extends WithCapabilitiesConfig(WithBrandConfig(Interface))
|
||||
implements StageHost
|
||||
{
|
||||
@property()
|
||||
flowSlug: string = window.location.pathname.split("/")[3];
|
||||
|
||||
@ -58,9 +58,9 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
set challenge(value: ChallengeTypes | undefined) {
|
||||
this._challenge = value;
|
||||
if (value?.flowInfo?.title) {
|
||||
document.title = `${value.flowInfo?.title} - ${this.brand?.brandingTitle}`;
|
||||
document.title = `${value.flowInfo?.title} - ${this.brandingTitle}`;
|
||||
} else {
|
||||
document.title = this.brand?.brandingTitle || TITLE_DEFAULT;
|
||||
document.title = this.brandingTitle;
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
@ -238,7 +238,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
if (this.config?.capabilities.includes(CapabilitiesEnum.CanDebug)) {
|
||||
if (this.can(CapabilitiesEnum.CanDebug)) {
|
||||
this.inspectorAvailable = true;
|
||||
}
|
||||
this.loading = true;
|
||||
@ -519,11 +519,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
class="pf-c-login__main-header pf-c-brand ak-brand"
|
||||
>
|
||||
<img
|
||||
src="${themeImage(
|
||||
this.brand?.brandingLogo ??
|
||||
globalAK()?.brand.brandingLogo ??
|
||||
DefaultBrand.brandingLogo,
|
||||
)}"
|
||||
src="${themeImage(this.brand.brandingLogo)}"
|
||||
alt="${msg("authentik Logo")}"
|
||||
/>
|
||||
</div>
|
||||
@ -531,7 +527,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
</div>
|
||||
<ak-brand-links
|
||||
class="pf-c-login__footer"
|
||||
.links=${this.brand?.uiFooterLinks ?? []}
|
||||
.links=${this.brandingFooterLinks}
|
||||
></ak-brand-links>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +1,6 @@
|
||||
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import "@goauthentik/elements/Divider";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
WithCapabilitiesConfig,
|
||||
} from "@goauthentik/elements/Interface/capabilitiesProvider";
|
||||
import { LOCALES } from "@goauthentik/elements/ak-locale-context/definitions";
|
||||
import "@goauthentik/elements/forms/FormElement";
|
||||
import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { TITLE_DEFAULT } from "@goauthentik/common/constants";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import { Interface } from "#elements/Interface";
|
||||
import "#elements/LoadingOverlay";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import Guacamole from "guacamole-common-js";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
import AKGlobal from "@goauthentik/common/styles/authentik.css";
|
||||
import AKGlobal from "#common/styles/authentik.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@ -43,7 +43,7 @@ const RECONNECT_ATTEMPTS_INITIAL = 5;
|
||||
const RECONNECT_ATTEMPTS = 5;
|
||||
|
||||
@customElement("ak-rac")
|
||||
export class RacInterface extends Interface {
|
||||
export class RacInterface extends WithBrandConfig(Interface) {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
@ -231,10 +231,12 @@ export class RacInterface extends Interface {
|
||||
}
|
||||
|
||||
updateTitle(): void {
|
||||
let title = this.brand?.brandingTitle || TITLE_DEFAULT;
|
||||
let title = this.brandingTitle;
|
||||
|
||||
if (this.endpointName) {
|
||||
title = `${this.endpointName} - ${title}`;
|
||||
}
|
||||
|
||||
document.title = `${title}`;
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
// sort-imports-ignore
|
||||
import "rapidoc";
|
||||
import "@goauthentik/elements/ak-locale-context/index.js";
|
||||
import "#elements/ak-locale-context/index";
|
||||
|
||||
import { CSRFHeaderName } from "@goauthentik/common/api/middleware.js";
|
||||
import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants.js";
|
||||
import { getCookie } from "@goauthentik/common/utils.js";
|
||||
import { Interface } from "@goauthentik/elements/Interface/Interface.js";
|
||||
import { DefaultBrand } from "@goauthentik/common/ui/config.js";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images.js";
|
||||
import { CSRFHeaderName } from "#common/api/middleware";
|
||||
import { EVENT_THEME_CHANGE } from "#common/constants";
|
||||
import { getCookie } from "#common/utils";
|
||||
import { Interface } from "#elements/Interface";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { themeImage } from "#elements/utils/images";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
@ -17,7 +17,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-api-browser")
|
||||
export class APIBrowser extends Interface {
|
||||
export class APIBrowser extends WithBrandConfig(Interface) {
|
||||
@property()
|
||||
schemaPath?: string;
|
||||
|
||||
@ -102,9 +102,7 @@ export class APIBrowser extends Interface {
|
||||
<img
|
||||
alt="${msg("authentik Logo")}"
|
||||
class="logo"
|
||||
src="${themeImage(
|
||||
this.brand?.brandingLogo ?? DefaultBrand.brandingLogo,
|
||||
)}"
|
||||
src="${themeImage(this.brandingLogo)}"
|
||||
/>
|
||||
</div>
|
||||
</rapi-doc>
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { LightInterface } from "@goauthentik/elements/Interface";
|
||||
import { globalAK } from "#common/global";
|
||||
import { applyDocumentTheme } from "#common/theme";
|
||||
import { AKElement } from "#elements/Base";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, css, html } from "lit";
|
||||
@ -10,7 +12,7 @@ import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-loading")
|
||||
export class Loading extends LightInterface {
|
||||
export class Loading extends AKElement {
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFPage,
|
||||
@ -23,6 +25,16 @@ export class Loading extends LightInterface {
|
||||
`,
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
applyDocumentTheme(globalAK().brand.uiTheme);
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
this.dataset.akInterfaceRoot = this.tagName.toLowerCase();
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <section
|
||||
class="ak-static-page pf-c-page__main-section pf-m-no-padding-mobile pf-m-xl"
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import { Interface } from "#elements/Interface";
|
||||
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { PFSize } from "@goauthentik/common/enums.js";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { rootInterface } from "@goauthentik/common/theme";
|
||||
import { truncateWords } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/AppIcon";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Expand";
|
||||
import "@goauthentik/user/LibraryApplication/RACLaunchEndpointModal";
|
||||
import type { RACLaunchEndpointModal } from "@goauthentik/user/LibraryApplication/RACLaunchEndpointModal";
|
||||
@ -71,14 +72,14 @@ export class LibraryApplication extends AKElement {
|
||||
}
|
||||
|
||||
renderExpansion(application: Application) {
|
||||
const me = rootInterface<UserInterface>()?.me;
|
||||
const { me, uiConfig } = rootInterface<UserInterface>();
|
||||
|
||||
return html`<ak-expand textOpen=${msg("Fewer details")} textClosed=${msg("More details")}>
|
||||
<div class="pf-c-content">
|
||||
<small>${application.metaPublisher}</small>
|
||||
</div>
|
||||
${truncateWords(application.metaDescription || "", 10)}
|
||||
${rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
|
||||
${uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser
|
||||
? html`
|
||||
<a
|
||||
class="pf-c-button pf-m-control pf-m-small pf-m-block"
|
||||
@ -148,9 +149,10 @@ export class LibraryApplication extends AKElement {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
|
||||
const me = rootInterface<UserInterface>()?.me;
|
||||
const { me, uiConfig } = rootInterface<UserInterface>();
|
||||
|
||||
const expandable =
|
||||
(rootInterface()?.uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser) ||
|
||||
(uiConfig?.enabledFeatures.applicationEdit && me?.user.isSuperuser) ||
|
||||
this.application.metaPublisher !== "" ||
|
||||
this.application.metaDescription !== "";
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { rootInterface } from "@goauthentik/common/theme";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import type { UserInterface } from "@goauthentik/user/index.entrypoint";
|
||||
|
||||
import { localized, msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
@ -47,7 +49,8 @@ export class LibraryPage extends AKElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const uiConfig = rootInterface()?.uiConfig;
|
||||
const { uiConfig } = rootInterface<UserInterface>();
|
||||
|
||||
if (!uiConfig) {
|
||||
throw new Error("Could not retrieve uiConfig. Reason: unknown. Check logs.");
|
||||
}
|
||||
|
@ -1,30 +1,31 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import {
|
||||
EVENT_API_DRAWER_TOGGLE,
|
||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||
EVENT_WS_MESSAGE,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { UIConfig, getConfigForUser } from "@goauthentik/common/ui/config";
|
||||
import { DefaultBrand } from "@goauthentik/common/ui/config";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import "@goauthentik/components/ak-nav-buttons";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { AuthenticatedInterface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/banner/EnterpriseStatusBanner";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/elements/notifications/APIDrawer";
|
||||
import "@goauthentik/elements/notifications/NotificationDrawer";
|
||||
import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch";
|
||||
import "@goauthentik/elements/router/RouterOutlet";
|
||||
import "@goauthentik/elements/sidebar/Sidebar";
|
||||
import "@goauthentik/elements/sidebar/SidebarItem";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
import { ROUTES } from "@goauthentik/user/Routes";
|
||||
} from "#common/constants";
|
||||
import { globalAK } from "#common/global";
|
||||
import { configureSentry } from "#common/sentry/index";
|
||||
import { UIConfig, getConfigForUser } from "#common/ui/config";
|
||||
import { DefaultBrand } from "#common/ui/config";
|
||||
import { me } from "#common/users";
|
||||
import { WebsocketClient } from "#common/ws";
|
||||
import "#components/ak-nav-buttons";
|
||||
import { AuthenticatedInterface } from "#elements/AuthenticatedInterface";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import "#elements/ak-locale-context/ak-locale-context";
|
||||
import "#elements/banner/EnterpriseStatusBanner";
|
||||
import "#elements/buttons/ActionButton/ak-action-button";
|
||||
import "#elements/messages/MessageContainer";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import "#elements/notifications/APIDrawer";
|
||||
import "#elements/notifications/NotificationDrawer";
|
||||
import { getURLParam, updateURLParams } from "#elements/router/RouteMatch";
|
||||
import "#elements/router/RouterOutlet";
|
||||
import "#elements/sidebar/Sidebar";
|
||||
import "#elements/sidebar/SidebarItem";
|
||||
import { themeImage } from "#elements/utils/images";
|
||||
import { ROUTES } from "#user/Routes";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
@ -41,7 +42,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 { CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api";
|
||||
import { EventsApi, SessionUser } from "@goauthentik/api";
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
await import("@goauthentik/esbuild-plugin-live-reload/client");
|
||||
@ -117,21 +118,19 @@ const customStyles = css`
|
||||
|
||||
@customElement("ak-interface-user-presentation")
|
||||
// @ts-ignore
|
||||
class UserInterfacePresentation extends AKElement {
|
||||
static get styles() {
|
||||
return [
|
||||
PFBase,
|
||||
PFDisplay,
|
||||
PFBrand,
|
||||
PFPage,
|
||||
PFAvatar,
|
||||
PFButton,
|
||||
PFDrawer,
|
||||
PFDropdown,
|
||||
PFNotificationBadge,
|
||||
customStyles,
|
||||
];
|
||||
}
|
||||
class UserInterfacePresentation extends WithBrandConfig(AKElement) {
|
||||
static styles = [
|
||||
PFBase,
|
||||
PFDisplay,
|
||||
PFBrand,
|
||||
PFPage,
|
||||
PFAvatar,
|
||||
PFButton,
|
||||
PFDrawer,
|
||||
PFDropdown,
|
||||
PFNotificationBadge,
|
||||
customStyles,
|
||||
];
|
||||
|
||||
@property({ type: Object })
|
||||
uiConfig!: UIConfig;
|
||||
@ -148,9 +147,6 @@ class UserInterfacePresentation extends AKElement {
|
||||
@property({ type: Number })
|
||||
notificationsCount = 0;
|
||||
|
||||
@property({ type: Object })
|
||||
brand!: CurrentBrand;
|
||||
|
||||
get canAccessAdmin() {
|
||||
return (
|
||||
this.me.user.isSuperuser ||
|
||||
@ -206,8 +202,8 @@ class UserInterfacePresentation extends AKElement {
|
||||
<a href="#/" class="pf-c-page__header-brand-link">
|
||||
<img
|
||||
class="pf-c-brand"
|
||||
src="${themeImage(this.brand.brandingLogo)}"
|
||||
alt="${this.brand.brandingTitle}"
|
||||
src="${themeImage(this.brandingLogo)}"
|
||||
alt="${this.brandingTitle}"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
@ -265,7 +261,7 @@ class UserInterfacePresentation extends AKElement {
|
||||
//
|
||||
//
|
||||
@customElement("ak-interface-user")
|
||||
export class UserInterface extends AuthenticatedInterface {
|
||||
export class UserInterface extends WithBrandConfig(AuthenticatedInterface) {
|
||||
@property({ type: Boolean })
|
||||
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
|
||||
|
||||
@ -278,7 +274,10 @@ export class UserInterface extends AuthenticatedInterface {
|
||||
notificationsCount = 0;
|
||||
|
||||
@state()
|
||||
me?: SessionUser;
|
||||
me: SessionUser | null = null;
|
||||
|
||||
@state()
|
||||
uiConfig: UIConfig | null = null;
|
||||
|
||||
constructor() {
|
||||
configureSentry(true);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
|
||||
import { rootInterface } from "@goauthentik/common/theme";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Tabs";
|
||||
import "@goauthentik/elements/user/SessionList";
|
||||
import "@goauthentik/elements/user/UserConsentList";
|
||||
|
@ -1,18 +1,14 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import {
|
||||
APIError,
|
||||
parseAPIResponseError,
|
||||
pluckErrorDetail,
|
||||
} from "@goauthentik/common/errors/network";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { refreshMe } from "@goauthentik/common/users";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
import { StageHost } from "@goauthentik/flow/stages/base";
|
||||
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
|
||||
import { DEFAULT_CONFIG } from "#common/api/config";
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
|
||||
import { globalAK } from "#common/global";
|
||||
import { MessageLevel } from "#common/messages";
|
||||
import { refreshMe } from "#common/users";
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { showMessage } from "#elements/messages/MessageContainer";
|
||||
import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { StageHost } from "#flow/stages/base";
|
||||
import "#user/user-settings/details/stages/prompt/PromptStage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
|
||||
@ -92,10 +88,10 @@ export class UserSettingsFlowExecutor
|
||||
|
||||
updated(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("brand") && this.brand) {
|
||||
this.flowSlug = this.brand?.flowUserSettings;
|
||||
if (!this.flowSlug) {
|
||||
return;
|
||||
}
|
||||
this.flowSlug = this.brand.flowUserSettings;
|
||||
|
||||
if (!this.flowSlug) return;
|
||||
|
||||
this.nextChallenge();
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@
|
||||
"@goauthentik/admin/*": ["./src/admin/*"],
|
||||
"@goauthentik/common/*": ["./src/common/*"],
|
||||
"@goauthentik/components/*": ["./src/components/*"],
|
||||
"@goauthentik/docs/*": ["../website/docs/*"],
|
||||
"@goauthentik/elements/*": ["./src/elements/*"],
|
||||
"@goauthentik/flow/*": ["./src/flow/*"],
|
||||
"@goauthentik/locales/*": ["./src/locales/*"],
|
||||
|
@ -24,7 +24,6 @@
|
||||
"@goauthentik/admin/*": ["./src/admin/*"],
|
||||
"@goauthentik/common/*": ["./src/common/*"],
|
||||
"@goauthentik/components/*": ["./src/components/*"],
|
||||
"@goauthentik/docs/*": ["../website/docs/*"],
|
||||
"@goauthentik/elements/*": ["./src/elements/*"],
|
||||
"@goauthentik/flow/*": ["./src/flow/*"],
|
||||
"@goauthentik/locales/*": ["./src/locales/*"],
|
||||
|
28
web/types/mdx.d.ts
vendored
28
web/types/mdx.d.ts
vendored
@ -1,4 +1,28 @@
|
||||
declare module "*.md" {
|
||||
/**
|
||||
* @file Provides types for ESBuild "virtual modules" generated from MDX files.
|
||||
*/
|
||||
|
||||
declare module "~docs/types" {
|
||||
/**
|
||||
* A parsed JSON module containing MDX content and metadata from ESBuild.
|
||||
*/
|
||||
export interface MDXModule {
|
||||
/**
|
||||
* The Markdown content of the module.
|
||||
*/
|
||||
content: string;
|
||||
/**
|
||||
* The public path of the module, typically identical to the docs page path.
|
||||
*/
|
||||
publicPath?: string;
|
||||
/**
|
||||
* The public directory of the module, used to resolve relative links.
|
||||
*/
|
||||
publicDirectory?: string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module "~docs/*.md" {
|
||||
/**
|
||||
* The serialized JSON content of an MD file.
|
||||
*/
|
||||
@ -6,7 +30,7 @@ declare module "*.md" {
|
||||
export default serializedJSON;
|
||||
}
|
||||
|
||||
declare module "*.mdx" {
|
||||
declare module "~docs/*.mdx" {
|
||||
/**
|
||||
* The serialized JSON content of an MDX file.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user