web: lazy load parts of interfaces (#2864)

This commit is contained in:
Jens L 2022-05-14 17:07:37 +02:00 committed by GitHub
parent 4da350ebfc
commit be06adcb59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 389 additions and 314 deletions

View file

@ -70,24 +70,32 @@ export function manualChunks(id) {
}
}
export const PLUGINS = [
cssimport(),
markdown(),
nodeResolve({ extensions, browser: true }),
commonjs(),
babel({
extensions,
babelHelpers: "runtime",
include: ["src/**/*"],
}),
replace({
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true,
}),
sourcemaps(),
isProdBuild && terser(),
].filter((p) => p);
export const defaultOptions = {
plugins: [
cssimport(),
markdown(),
nodeResolve({ extensions, browser: true }),
commonjs(),
babel({
extensions,
babelHelpers: "runtime",
include: ["src/**/*"],
}),
replace({
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true,
}),
sourcemaps(),
isProdBuild && terser(),
].filter((p) => p),
watch: {
clearScreen: false,
},
preserveEntrySignatures: false,
cache: true,
context: "window",
};
// Polyfills (imported first)
export const POLY = {
@ -110,9 +118,6 @@ export const POLY = {
copyOnce: false,
}),
].filter((p) => p),
watch: {
clearScreen: false,
},
};
export default [
@ -120,8 +125,6 @@ export default [
// Flow interface
{
input: "./src/interfaces/FlowInterface.ts",
context: "window",
cache: true,
output: [
{
format: "es",
@ -130,16 +133,11 @@ export default [
manualChunks: manualChunks,
},
],
plugins: PLUGINS,
watch: {
clearScreen: false,
},
...defaultOptions,
},
// Admin interface
{
input: "./src/interfaces/AdminInterface.ts",
context: "window",
cache: true,
output: [
{
format: "es",
@ -148,16 +146,11 @@ export default [
manualChunks: manualChunks,
},
],
plugins: PLUGINS,
watch: {
clearScreen: false,
},
...defaultOptions,
},
// User interface
{
input: "./src/interfaces/UserInterface.ts",
context: "window",
cache: true,
output: [
{
format: "es",
@ -166,9 +159,6 @@ export default [
manualChunks: manualChunks,
},
],
plugins: PLUGINS,
watch: {
clearScreen: false,
},
...defaultOptions,
},
];

View file

@ -95,6 +95,11 @@ html > form > input {
vertical-align: middle;
}
.pf-c-description-list__description .pf-c-button {
margin-right: 6px;
margin-bottom: 6px;
}
@media (prefers-color-scheme: dark) {
.ak-static-page h1 {
color: var(--ak-dark-foreground);

View file

@ -1,4 +1,5 @@
import { TemplateResult, html } from "lit";
import { until } from "lit/directives/until.js";
export const SLUG_REGEX = "[-a-zA-Z0-9_]+";
export const ID_REGEX = "\\d+";
@ -12,39 +13,41 @@ export class Route {
url: RegExp;
private element?: TemplateResult;
private callback?: (args: RouteArgs) => TemplateResult;
private callback?: (args: RouteArgs) => Promise<TemplateResult>;
constructor(url: RegExp, element?: TemplateResult) {
constructor(url: RegExp, callback?: (args: RouteArgs) => Promise<TemplateResult>) {
this.url = url;
this.element = element;
this.callback = callback;
}
redirect(to: string): Route {
this.callback = () => {
redirect(to: string, raw = false): Route {
this.callback = async () => {
console.debug(`authentik/router: redirecting ${to}`);
window.location.hash = `#${to}`;
return html``;
};
return this;
}
redirectRaw(to: string): Route {
this.callback = () => {
console.debug(`authentik/router: redirecting ${to}`);
window.location.hash = `${to}`;
if (!raw) {
window.location.hash = `#${to}`;
} else {
window.location.hash = to;
}
return html``;
};
return this;
}
then(render: (args: RouteArgs) => TemplateResult): Route {
this.callback = async (args) => {
return render(args);
};
return this;
}
thenAsync(render: (args: RouteArgs) => Promise<TemplateResult>): Route {
this.callback = render;
return this;
}
render(args: RouteArgs): TemplateResult {
if (this.callback) {
return this.callback(args);
return html`${until(this.callback(args))}`;
}
if (this.element) {
return this.element;

View file

@ -30,26 +30,13 @@ import { WebsocketClient } from "../common/ws";
import { EVENT_FLOW_ADVANCE, TITLE_DEFAULT } from "../constants";
import "../elements/LoadingOverlay";
import { first } from "../utils";
import "./FlowInspector";
import "./sources/apple/AppleLoginInit";
import "./sources/plex/PlexLoginInit";
import "./stages/RedirectStage";
import "./stages/access_denied/AccessDeniedStage";
import "./stages/authenticator_duo/AuthenticatorDuoStage";
import "./stages/authenticator_sms/AuthenticatorSMSStage";
import "./stages/authenticator_static/AuthenticatorStaticStage";
import "./stages/authenticator_totp/AuthenticatorTOTPStage";
import "./stages/authenticator_validate/AuthenticatorValidateStage";
import "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
import "./stages/autosubmit/AutosubmitStage";
import { StageHost } from "./stages/base";
import "./stages/captcha/CaptchaStage";
import "./stages/consent/ConsentStage";
import "./stages/dummy/DummyStage";
import "./stages/email/EmailStage";
import "./stages/identification/IdentificationStage";
import "./stages/password/PasswordStage";
import "./stages/prompt/PromptStage";
@customElement("ak-flow-executor")
export class FlowExecutor extends LitElement implements StageHost {
@ -229,7 +216,117 @@ export class FlowExecutor extends LitElement implements StageHost {
} as ChallengeTypes;
}
renderChallenge(): TemplateResult {
async renderChallengeNativeElement(): Promise<TemplateResult> {
switch (this.challenge?.component) {
case "ak-stage-access-denied":
// Statically imported for performance reasons
return html`<ak-stage-access-denied
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-access-denied>`;
case "ak-stage-identification":
// Statically imported for performance reasons
return html`<ak-stage-identification
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-identification>`;
case "ak-stage-password":
// Statically imported for performance reasons
return html`<ak-stage-password
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-password>`;
case "ak-stage-captcha":
// Statically imported to prevent browsers blocking urls
return html`<ak-stage-captcha
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-captcha>`;
case "ak-stage-consent":
await import("./stages/consent/ConsentStage");
return html`<ak-stage-consent
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-consent>`;
case "ak-stage-dummy":
await import("./stages/dummy/DummyStage");
return html`<ak-stage-dummy
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-dummy>`;
case "ak-stage-email":
await import("./stages/email/EmailStage");
return html`<ak-stage-email
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-email>`;
case "ak-stage-autosubmit":
// Statically imported for performance reasons
return html`<ak-stage-autosubmit
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-autosubmit>`;
case "ak-stage-prompt":
await import("./stages/prompt/PromptStage");
return html`<ak-stage-prompt
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-prompt>`;
case "ak-stage-authenticator-totp":
await import("./stages/authenticator_totp/AuthenticatorTOTPStage");
return html`<ak-stage-authenticator-totp
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-totp>`;
case "ak-stage-authenticator-duo":
await import("./stages/authenticator_duo/AuthenticatorDuoStage");
return html`<ak-stage-authenticator-duo
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-duo>`;
case "ak-stage-authenticator-static":
await import("./stages/authenticator_static/AuthenticatorStaticStage");
return html`<ak-stage-authenticator-static
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-static>`;
case "ak-stage-authenticator-webauthn":
await import("./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage");
return html`<ak-stage-authenticator-webauthn
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-webauthn>`;
case "ak-stage-authenticator-sms":
await import("./stages/authenticator_sms/AuthenticatorSMSStage");
return html`<ak-stage-authenticator-sms
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-sms>`;
case "ak-stage-authenticator-validate":
await import("./stages/authenticator_validate/AuthenticatorValidateStage");
return html`<ak-stage-authenticator-validate
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-validate>`;
case "ak-flow-sources-plex":
await import("./sources/plex/PlexLoginInit");
return html`<ak-flow-sources-plex
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-sources-plex>`;
case "ak-flow-sources-oauth-apple":
await import("./sources/apple/AppleLoginInit");
return html`<ak-flow-sources-oauth-apple
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-sources-oauth-apple>`;
default:
break;
}
return html`Invalid native challenge element`;
}
async renderChallenge(): Promise<TemplateResult> {
if (!this.challenge) {
return html``;
}
@ -247,96 +344,7 @@ export class FlowExecutor extends LitElement implements StageHost {
case ChallengeChoices.Shell:
return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;
case ChallengeChoices.Native:
switch (this.challenge.component) {
case "ak-stage-access-denied":
return html`<ak-stage-access-denied
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-access-denied>`;
case "ak-stage-identification":
return html`<ak-stage-identification
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-identification>`;
case "ak-stage-password":
return html`<ak-stage-password
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-password>`;
case "ak-stage-captcha":
return html`<ak-stage-captcha
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-captcha>`;
case "ak-stage-consent":
return html`<ak-stage-consent
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-consent>`;
case "ak-stage-dummy":
return html`<ak-stage-dummy
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-dummy>`;
case "ak-stage-email":
return html`<ak-stage-email
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-email>`;
case "ak-stage-autosubmit":
return html`<ak-stage-autosubmit
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-autosubmit>`;
case "ak-stage-prompt":
return html`<ak-stage-prompt
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-prompt>`;
case "ak-stage-authenticator-totp":
return html`<ak-stage-authenticator-totp
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-totp>`;
case "ak-stage-authenticator-duo":
return html`<ak-stage-authenticator-duo
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-duo>`;
case "ak-stage-authenticator-static":
return html`<ak-stage-authenticator-static
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-static>`;
case "ak-stage-authenticator-webauthn":
return html`<ak-stage-authenticator-webauthn
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-webauthn>`;
case "ak-stage-authenticator-validate":
return html`<ak-stage-authenticator-validate
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-validate>`;
case "ak-stage-authenticator-sms":
return html`<ak-stage-authenticator-sms
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-stage-authenticator-sms>`;
case "ak-flow-sources-plex":
return html`<ak-flow-sources-plex
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-sources-plex>`;
case "ak-flow-sources-oauth-apple":
return html`<ak-flow-sources-oauth-apple
.host=${this as StageHost}
.challenge=${this.challenge}
></ak-flow-sources-oauth-apple>`;
default:
break;
}
break;
return await this.renderChallengeNativeElement();
default:
console.debug(`authentik/flows: unexpected data type ${this.challenge.type}`);
break;
@ -350,10 +358,20 @@ export class FlowExecutor extends LitElement implements StageHost {
}
return html`
${this.loading ? html`<ak-loading-overlay></ak-loading-overlay>` : html``}
${this.renderChallenge()}
${until(this.renderChallenge())}
`;
}
async renderInspector(): Promise<TemplateResult> {
if (!this.inspectorOpen) {
return html``;
}
await import("./FlowInspector");
return html`<ak-flow-inspector
class="pf-c-drawer__panel pf-m-width-33"
></ak-flow-inspector>`;
}
render(): TemplateResult {
return html`<div class="pf-c-background-image">
<svg
@ -439,13 +457,7 @@ export class FlowExecutor extends LitElement implements StageHost {
</div>
</div>
</div>
<ak-flow-inspector
class="pf-c-drawer__panel pf-m-width-33 ${this.inspectorOpen
? ""
: "display-none"}"
?hidden=${!this.inspectorOpen}
></ak-flow-inspector>
${until(this.renderInspector())}
</div>
</div>
</div>`;

View file

@ -1,96 +1,122 @@
import { de, en, es, fr, pl, tr, zh } from "make-plural/plurals";
import { Messages, i18n } from "@lingui/core";
import { detect, fromNavigator, fromUrl } from "@lingui/detect-locale";
import { t } from "@lingui/macro";
import { LitElement } from "lit";
import { messages as localeDE } from "../locales/de";
import { messages as localeEN } from "../locales/en";
import { messages as localeES } from "../locales/es";
import { messages as localeFR_FR } from "../locales/fr_FR";
import { messages as localePL } from "../locales/pl";
import { messages as localeDEBUG } from "../locales/pseudo-LOCALE";
import { messages as localeTR } from "../locales/tr";
import { messages as localeZH_Hans } from "../locales/zh-Hans";
import { messages as localeZH_Hant } from "../locales/zh-Hant";
import { messages as localeZH_TW } from "../locales/zh_TW";
interface Locale {
locale: Messages;
// eslint-disable-next-line @typescript-eslint/ban-types
plurals: Function;
}
export const LOCALES: {
code: string;
label: string;
// eslint-disable-next-line @typescript-eslint/ban-types
plurals: Function;
locale: Messages;
locale: () => Promise<Locale>;
}[] = [
{
code: "en",
plurals: en,
label: t`English`,
locale: localeEN,
locale: async () => {
return {
locale: (await import("../locales/en")).messages,
plurals: (await import("make-plural/plurals")).en,
};
},
},
{
code: "debug",
plurals: en,
label: t`Debug`,
locale: localeDEBUG,
locale: async () => {
return {
locale: (await import("../locales/pseudo-LOCALE")).messages,
plurals: (await import("make-plural/plurals")).en,
};
},
},
{
code: "fr",
plurals: fr,
label: t`French`,
locale: localeFR_FR,
locale: async () => {
return {
locale: (await import("../locales/fr_FR")).messages,
plurals: (await import("make-plural/plurals")).fr,
};
},
},
{
code: "tr",
plurals: tr,
label: t`Turkish`,
locale: localeTR,
locale: async () => {
return {
locale: (await import("../locales/tr")).messages,
plurals: (await import("make-plural/plurals")).tr,
};
},
},
{
code: "es",
plurals: es,
label: t`Spanish`,
locale: localeES,
locale: async () => {
return {
locale: (await import("../locales/es")).messages,
plurals: (await import("make-plural/plurals")).es,
};
},
},
{
code: "pl",
plurals: pl,
label: t`Polish`,
locale: localePL,
locale: async () => {
return {
locale: (await import("../locales/pl")).messages,
plurals: (await import("make-plural/plurals")).pl,
};
},
},
{
code: "zh_TW",
plurals: zh,
label: t`Taiwanese Mandarin`,
locale: localeZH_TW,
locale: async () => {
return {
locale: (await import("../locales/zh_TW")).messages,
plurals: (await import("make-plural/plurals")).zh,
};
},
},
{
code: "zh-CN",
plurals: zh,
label: t`Chinese (simplified)`,
locale: localeZH_Hans,
locale: async () => {
return {
locale: (await import("../locales/zh-Hans")).messages,
plurals: (await import("make-plural/plurals")).zh,
};
},
},
{
code: "zh-HK",
plurals: zh,
label: t`Chinese (traditional)`,
locale: localeZH_Hant,
locale: async () => {
return {
locale: (await import("../locales/zh-Hant")).messages,
plurals: (await import("make-plural/plurals")).zh,
};
},
},
{
code: "de",
plurals: de,
label: t`German`,
locale: localeDE,
locale: async () => {
return {
locale: (await import("../locales/de")).messages,
plurals: (await import("make-plural/plurals")).de,
};
},
},
];
LOCALES.forEach((locale) => {
i18n.loadLocaleData(locale.code, { plurals: locale.plurals });
i18n.load(locale.code, locale.locale);
});
const DEFAULT_FALLBACK = () => "en";
export function autoDetectLanguage() {
@ -102,25 +128,33 @@ export function autoDetectLanguage() {
}
if (detected in i18n._messages) {
console.debug(`authentik/locale: Activating detected locale '${detected}'`);
i18n.activate(detected);
activateLocale(detected);
} else {
console.debug(`authentik/locale: No locale for '${detected}', falling back to en`);
i18n.activate(DEFAULT_FALLBACK());
activateLocale(DEFAULT_FALLBACK());
}
}
export function activateLocale(code: string) {
const urlLocale = fromUrl("locale");
if (urlLocale !== null && urlLocale !== "") {
i18n.activate(urlLocale);
} else {
i18n.activate(code);
code = urlLocale;
}
document.querySelectorAll("[data-refresh-on-locale=true]").forEach((el) => {
try {
(el as LitElement).requestUpdate();
} catch {
console.debug(`authentik/locale: failed to update element ${el}`);
}
const locale = LOCALES.find((locale) => locale.code == code);
if (!locale) {
console.warn(`authentik/locale: failed to find locale for code ${code}`);
return;
}
locale.locale().then((localeData) => {
i18n.loadLocaleData(locale.code, { plurals: localeData.plurals });
i18n.load(locale.code, localeData.locale);
i18n.activate(locale.code);
document.querySelectorAll("[data-refresh-on-locale=true]").forEach((el) => {
try {
(el as LitElement).requestUpdate();
} catch {
console.debug(`authentik/locale: failed to update element ${el}`);
}
});
});
}
autoDetectLanguage();

View file

@ -2824,6 +2824,7 @@ msgstr "Server laden"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2876,6 +2876,7 @@ msgstr "Load servers"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2817,6 +2817,7 @@ msgstr "Servidores de carga"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2848,6 +2848,7 @@ msgstr "Charger les serveurs"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2814,6 +2814,7 @@ msgstr "Załaduj serwery"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2858,6 +2858,7 @@ msgstr ""
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2818,6 +2818,7 @@ msgstr "Sunucuları yükle"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2802,6 +2802,7 @@ msgstr "加载服务器"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2804,6 +2804,7 @@ msgstr "加载服务器"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -2804,6 +2804,7 @@ msgstr "加载服务器"
#: src/elements/table/Table.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowExecutor.ts
#: src/flows/FlowInspector.ts
#: src/flows/stages/access_denied/AccessDeniedStage.ts
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts

View file

@ -163,6 +163,11 @@ export class FlowViewPage extends LitElement {
`inspector&next=/#${window.location.hash}`,
)}`;
window.open(finalURL, "_blank");
})
.catch((exc: Response) => {
// This request can return a HTTP 400 when a flow
// is not applicable.
window.open(exc.url, "_blank");
});
}}
>

View file

@ -2,116 +2,130 @@ import { html } from "lit";
import { ID_REGEX, Route, SLUG_REGEX, UUID_REGEX } from "./elements/router/Route";
import "./pages/admin-overview/AdminOverviewPage";
import "./pages/admin-overview/DashboardUserPage";
import "./pages/applications/ApplicationListPage";
import "./pages/applications/ApplicationViewPage";
import "./pages/crypto/CertificateKeyPairListPage";
import "./pages/events/EventInfoPage";
import "./pages/events/EventListPage";
import "./pages/events/RuleListPage";
import "./pages/events/TransportListPage";
import "./pages/flows/FlowListPage";
import "./pages/flows/FlowViewPage";
import "./pages/groups/GroupListPage";
import "./pages/groups/GroupViewPage";
import "./pages/outposts/OutpostListPage";
import "./pages/outposts/ServiceConnectionListPage";
import "./pages/policies/PolicyListPage";
import "./pages/policies/reputation/ReputationListPage";
import "./pages/property-mappings/PropertyMappingListPage";
import "./pages/providers/ProviderListPage";
import "./pages/providers/ProviderViewPage";
import "./pages/sources/SourceListPage";
import "./pages/sources/SourceViewPage";
import "./pages/stages/StageListPage";
import "./pages/stages/invitation/InvitationListPage";
import "./pages/stages/prompt/PromptListPage";
import "./pages/system-tasks/SystemTaskListPage";
import "./pages/tenants/TenantListPage";
import "./pages/tokens/TokenListPage";
import "./pages/users/UserListPage";
import "./pages/users/UserViewPage";
export const ROUTES: Route[] = [
// Prevent infinite Shell loops
new Route(new RegExp("^/$")).redirect("/administration/overview"),
new Route(new RegExp("^#.*")).redirect("/administration/overview"),
new Route(new RegExp("^/library$")).redirectRaw("/if/user/"),
new Route(
new RegExp("^/administration/overview$"),
html`<ak-admin-overview></ak-admin-overview>`,
),
new Route(
new RegExp("^/administration/dashboard/users$"),
html`<ak-admin-dashboard-users></ak-admin-dashboard-users>`,
),
new Route(
new RegExp("^/administration/system-tasks$"),
html`<ak-system-task-list></ak-system-task-list>`,
),
new Route(new RegExp("^/core/providers$"), html`<ak-provider-list></ak-provider-list>`),
new Route(new RegExp(`^/core/providers/(?<id>${ID_REGEX})$`)).then((args) => {
new Route(new RegExp("^/library$")).redirect("/if/user/", true),
// statically imported since this is the default route
new Route(new RegExp("^/administration/overview$"), async () => {
return html`<ak-admin-overview></ak-admin-overview>`;
}),
new Route(new RegExp("^/administration/dashboard/users$"), async () => {
await import("./pages/admin-overview/DashboardUserPage");
return html`<ak-admin-dashboard-users></ak-admin-dashboard-users>`;
}),
new Route(new RegExp("^/administration/system-tasks$"), async () => {
await import("./pages/system-tasks/SystemTaskListPage");
return html`<ak-system-task-list></ak-system-task-list>`;
}),
new Route(new RegExp("^/core/providers$"), async () => {
await import("./pages/providers/ProviderListPage");
return html`<ak-provider-list></ak-provider-list>`;
}),
new Route(new RegExp(`^/core/providers/(?<id>${ID_REGEX})$`), async (args) => {
await import("./pages/providers/ProviderViewPage");
return html`<ak-provider-view .providerID=${parseInt(args.id, 10)}></ak-provider-view>`;
}),
new Route(
new RegExp("^/core/applications$"),
html`<ak-application-list></ak-application-list>`,
),
new Route(new RegExp(`^/core/applications/(?<slug>${SLUG_REGEX})$`)).then((args) => {
new Route(new RegExp("^/core/applications$"), async () => {
await import("./pages/applications/ApplicationListPage");
return html`<ak-application-list></ak-application-list>`;
}),
new Route(new RegExp(`^/core/applications/(?<slug>${SLUG_REGEX})$`), async (args) => {
await import("./pages/applications/ApplicationViewPage");
return html`<ak-application-view .applicationSlug=${args.slug}></ak-application-view>`;
}),
new Route(new RegExp("^/core/sources$"), html`<ak-source-list></ak-source-list>`),
new Route(new RegExp(`^/core/sources/(?<slug>${SLUG_REGEX})$`)).then((args) => {
new Route(new RegExp("^/core/sources$"), async () => {
await import("./pages/sources/SourceListPage");
return html`<ak-source-list></ak-source-list>`;
}),
new Route(new RegExp(`^/core/sources/(?<slug>${SLUG_REGEX})$`), async (args) => {
await import("./pages/sources/SourceViewPage");
return html`<ak-source-view .sourceSlug=${args.slug}></ak-source-view>`;
}),
new Route(
new RegExp("^/core/property-mappings$"),
html`<ak-property-mapping-list></ak-property-mapping-list>`,
),
new Route(new RegExp("^/core/tokens$"), html`<ak-token-list></ak-token-list>`),
new Route(new RegExp("^/core/tenants$"), html`<ak-tenant-list></ak-tenant-list>`),
new Route(new RegExp("^/policy/policies$"), html`<ak-policy-list></ak-policy-list>`),
new Route(
new RegExp("^/policy/reputation$"),
html`<ak-policy-reputation-list></ak-policy-reputation-list>`,
),
new Route(new RegExp("^/identity/groups$"), html`<ak-group-list></ak-group-list>`),
new Route(new RegExp(`^/identity/groups/(?<uuid>${UUID_REGEX})$`)).then((args) => {
new Route(new RegExp("^/core/property-mappings$"), async () => {
await import("./pages/property-mappings/PropertyMappingListPage");
return html`<ak-property-mapping-list></ak-property-mapping-list>`;
}),
new Route(new RegExp("^/core/tokens$"), async () => {
await import("./pages/tokens/TokenListPage");
return html`<ak-token-list></ak-token-list>`;
}),
new Route(new RegExp("^/core/tenants$"), async () => {
await import("./pages/tenants/TenantListPage");
return html`<ak-tenant-list></ak-tenant-list>`;
}),
new Route(new RegExp("^/policy/policies$"), async () => {
await import("./pages/policies/PolicyListPage");
return html`<ak-policy-list></ak-policy-list>`;
}),
new Route(new RegExp("^/policy/reputation$"), async () => {
await import("./pages/policies/reputation/ReputationListPage");
return html`<ak-policy-reputation-list></ak-policy-reputation-list>`;
}),
new Route(new RegExp("^/identity/groups$"), async () => {
await import("./pages/groups/GroupListPage");
return html`<ak-group-list></ak-group-list>`;
}),
new Route(new RegExp(`^/identity/groups/(?<uuid>${UUID_REGEX})$`), async (args) => {
await import("./pages/groups/GroupViewPage");
return html`<ak-group-view .groupId=${args.uuid}></ak-group-view>`;
}),
new Route(new RegExp("^/identity/users$"), html`<ak-user-list></ak-user-list>`),
new Route(new RegExp(`^/identity/users/(?<id>${ID_REGEX})$`)).then((args) => {
new Route(new RegExp("^/identity/users$"), async () => {
await import("./pages/users/UserListPage");
return html`<ak-user-list></ak-user-list>`;
}),
new Route(new RegExp(`^/identity/users/(?<id>${ID_REGEX})$`), async (args) => {
await import("./pages/users/UserViewPage");
return html`<ak-user-view .userId=${parseInt(args.id, 10)}></ak-user-view>`;
}),
new Route(
new RegExp("^/flow/stages/invitations$"),
html`<ak-stage-invitation-list></ak-stage-invitation-list>`,
),
new Route(
new RegExp("^/flow/stages/prompts$"),
html`<ak-stage-prompt-list></ak-stage-prompt-list>`,
),
new Route(new RegExp("^/flow/stages$"), html`<ak-stage-list></ak-stage-list>`),
new Route(new RegExp("^/flow/flows$"), html`<ak-flow-list></ak-flow-list>`),
new Route(new RegExp(`^/flow/flows/(?<slug>${SLUG_REGEX})$`)).then((args) => {
new Route(new RegExp("^/flow/stages/invitations$"), async () => {
await import("./pages/stages/invitation/InvitationListPage");
return html`<ak-stage-invitation-list></ak-stage-invitation-list>`;
}),
new Route(new RegExp("^/flow/stages/prompts$"), async () => {
await import("./pages/stages/prompt/PromptListPage");
return html`<ak-stage-prompt-list></ak-stage-prompt-list>`;
}),
new Route(new RegExp("^/flow/stages$"), async () => {
await import("./pages/stages/StageListPage");
return html`<ak-stage-list></ak-stage-list>`;
}),
new Route(new RegExp("^/flow/flows$"), async () => {
await import("./pages/flows/FlowListPage");
return html`<ak-flow-list></ak-flow-list>`;
}),
new Route(new RegExp(`^/flow/flows/(?<slug>${SLUG_REGEX})$`), async (args) => {
await import("./pages/flows/FlowViewPage");
return html`<ak-flow-view .flowSlug=${args.slug}></ak-flow-view>`;
}),
new Route(new RegExp("^/events/log$"), html`<ak-event-list></ak-event-list>`),
new Route(new RegExp(`^/events/log/(?<id>${UUID_REGEX})$`)).then((args) => {
new Route(new RegExp("^/events/log$"), async () => {
await import("./pages/events/EventListPage");
return html`<ak-event-list></ak-event-list>`;
}),
new Route(new RegExp(`^/events/log/(?<id>${UUID_REGEX})$`), async (args) => {
await import("./pages/events/EventInfoPage");
return html`<ak-event-info-page .eventID=${args.id}></ak-event-info-page>`;
}),
new Route(
new RegExp("^/events/transports$"),
html`<ak-event-transport-list></ak-event-transport-list>`,
),
new Route(new RegExp("^/events/rules$"), html`<ak-event-rule-list></ak-event-rule-list>`),
new Route(new RegExp("^/outpost/outposts$"), html`<ak-outpost-list></ak-outpost-list>`),
new Route(
new RegExp("^/outpost/integrations$"),
html`<ak-outpost-service-connection-list></ak-outpost-service-connection-list>`,
),
new Route(
new RegExp("^/crypto/certificates$"),
html`<ak-crypto-certificate-list></ak-crypto-certificate-list>`,
),
new Route(new RegExp("^/events/transports$"), async () => {
await import("./pages/events/TransportListPage");
return html`<ak-event-transport-list></ak-event-transport-list>`;
}),
new Route(new RegExp("^/events/rules$"), async () => {
await import("./pages/events/RuleListPage");
return html`<ak-event-rule-list></ak-event-rule-list>`;
}),
new Route(new RegExp("^/outpost/outposts$"), async () => {
await import("./pages/outposts/OutpostListPage");
return html`<ak-outpost-list></ak-outpost-list>`;
}),
new Route(new RegExp("^/outpost/integrations$"), async () => {
await import("./pages/outposts/ServiceConnectionListPage");
return html`<ak-outpost-service-connection-list></ak-outpost-service-connection-list>`;
}),
new Route(new RegExp("^/crypto/certificates$"), async () => {
await import("./pages/crypto/CertificateKeyPairListPage");
return html`<ak-crypto-certificate-list></ak-crypto-certificate-list>`;
}),
];

View file

@ -2,12 +2,14 @@ import { html } from "lit";
import { Route } from "./elements/router/Route";
import "./user/LibraryPage";
import "./user/user-settings/UserSettingsPage";
export const ROUTES: Route[] = [
// Prevent infinite Shell loops
new Route(new RegExp("^/$")).redirect("/library"),
new Route(new RegExp("^#.*")).redirect("/library"),
new Route(new RegExp("^/library$"), html`<ak-library></ak-library>`),
new Route(new RegExp("^/settings$"), html`<ak-user-settings></ak-user-settings>`),
new Route(new RegExp("^/library$"), async () => html`<ak-library></ak-library>`),
new Route(new RegExp("^/settings$"), async () => {
await import("./user/user-settings/UserSettingsPage");
return html`<ak-user-settings></ak-user-settings>`;
}),
];