web/admin: rework markdown, correctly render Admonitions, fix links

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2022-12-19 12:48:02 +01:00
parent 39e0ed2962
commit 9d5b9204fc
6 changed files with 132 additions and 24 deletions

View File

@ -3,6 +3,7 @@ import babel from "@rollup/plugin-babel";
import commonjs from "@rollup/plugin-commonjs"; import commonjs from "@rollup/plugin-commonjs";
import { nodeResolve } from "@rollup/plugin-node-resolve"; import { nodeResolve } from "@rollup/plugin-node-resolve";
import replace from "@rollup/plugin-replace"; import replace from "@rollup/plugin-replace";
import { cwd } from "process";
import copy from "rollup-plugin-copy"; import copy from "rollup-plugin-copy";
import cssimport from "rollup-plugin-cssimport"; import cssimport from "rollup-plugin-cssimport";
import { terser } from "rollup-plugin-terser"; import { terser } from "rollup-plugin-terser";
@ -82,6 +83,7 @@ export const defaultOptions = {
}), }),
replace({ replace({
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"), "process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
"process.env.CWD": JSON.stringify(cwd()),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath), "process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true, "preventAssignment": true,
}), }),

View File

@ -259,7 +259,20 @@ export class OAuth2ProviderViewPage extends AKElement {
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl" class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl"
> >
<div class="pf-c-card__body"> <div class="pf-c-card__body">
<ak-markdown .md=${MDProviderOAuth2}></ak-markdown> <ak-markdown
.replacers=${[
(input: string) => {
if (!this.provider) {
return input;
}
return input.replaceAll(
"&lt;application slug&gt;",
this.provider.assignedApplicationSlug,
);
},
]}
.md=${MDProviderOAuth2}
></ak-markdown>
</div> </div>
</div> </div>
</div>`; </div>`;

View File

@ -13,7 +13,6 @@ import MDTraefikStandalone from "@goauthentik/docs/providers/proxy/_traefik_stan
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror"; import "@goauthentik/elements/CodeMirror";
import { PFColor } from "@goauthentik/elements/Label"; import { PFColor } from "@goauthentik/elements/Label";
import { MarkdownDocument } from "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Markdown"; import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ModalButton"; import "@goauthentik/elements/buttons/ModalButton";
@ -104,25 +103,6 @@ export class ProxyProviderViewPage extends AKElement {
}); });
} }
renderConfigTemplate(markdown: MarkdownDocument): MarkdownDocument {
const extHost = new URL(this.provider?.externalHost || "http://a");
// See website/docs/providers/proxy/forward_auth.mdx
if (this.provider?.mode === ProxyMode.ForwardSingle) {
markdown.html = markdown.html
.replaceAll("authentik.company", window.location.hostname)
.replaceAll("outpost.company:9000", window.location.hostname)
.replaceAll("https://app.company", extHost.toString())
.replaceAll("app.company", extHost.hostname);
} else if (this.provider?.mode == ProxyMode.ForwardDomain) {
markdown.html = markdown.html
.replaceAll("authentik.company", window.location.hostname)
.replaceAll("outpost.company:9000", extHost.toString())
.replaceAll("https://app.company", extHost.toString())
.replaceAll("app.company", extHost.hostname);
}
return markdown;
}
renderConfig(): TemplateResult { renderConfig(): TemplateResult {
const serves = [ const serves = [
{ {
@ -154,6 +134,29 @@ export class ProxyProviderViewPage extends AKElement {
md: MDCaddyStandalone, md: MDCaddyStandalone,
}, },
]; ];
const replacers = [
(input: string): string => {
if (!this.provider) {
return input;
}
const extHost = new URL(this.provider.externalHost);
// See website/docs/providers/proxy/forward_auth.mdx
if (this.provider?.mode === ProxyMode.ForwardSingle) {
return input
.replaceAll("authentik.company", window.location.hostname)
.replaceAll("outpost.company:9000", window.location.hostname)
.replaceAll("https://app.company", extHost.toString())
.replaceAll("app.company", extHost.hostname);
} else if (this.provider?.mode == ProxyMode.ForwardDomain) {
return input
.replaceAll("authentik.company", window.location.hostname)
.replaceAll("outpost.company:9000", extHost.toString())
.replaceAll("https://app.company", extHost.toString())
.replaceAll("app.company", extHost.hostname);
}
return input;
},
];
return html`<ak-tabs pageIdentifier="proxy-setup"> return html`<ak-tabs pageIdentifier="proxy-setup">
${serves.map((server) => { ${serves.map((server) => {
return html`<section return html`<section
@ -161,7 +164,7 @@ export class ProxyProviderViewPage extends AKElement {
data-tab-title="${server.label}" data-tab-title="${server.label}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
<ak-markdown .md=${this.renderConfigTemplate(server.md)}></ak-markdown> <ak-markdown .replacers=${replacers} .md=${server.md}></ak-markdown>
</section>`; </section>`;
})}</ak-tabs })}</ak-tabs
>`; >`;

41
web/src/elements/Alert.ts Normal file
View File

@ -0,0 +1,41 @@
import { AKElement } from "@goauthentik/elements/Base";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import AKGlobal from "@goauthentik/common/styles/authentik.css";
import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
export enum Level {
Warning = "pf-m-warning",
Info = "pf-m-info",
Success = "pf-m-success",
Danger = "pf-m-danger",
}
@customElement("ak-alert")
export class Alert extends AKElement {
@property({ type: Boolean })
inline = false;
@property()
level: Level = Level.Warning;
static get styles(): CSSResult[] {
return [PFBase, PFAlert, AKGlobal];
}
render(): TemplateResult {
return html`<div
class="pf-c-alert ${this.inline ? html`pf-m-inline` : html``} ${this.level}"
>
<div class="pf-c-alert__icon">
<i class="fas fa-exclamation-circle"></i>
</div>
<h4 class="pf-c-alert__title">
<slot></slot>
</h4>
</div>`;
}
}

View File

@ -1,3 +1,4 @@
import "@goauthentik/elements/Alert";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { CSSResult, TemplateResult, html } from "lit"; import { CSSResult, TemplateResult, html } from "lit";
@ -12,22 +13,66 @@ export interface MarkdownDocument {
html: string; html: string;
metadata: { [key: string]: string }; metadata: { [key: string]: string };
filename: string; filename: string;
path: string;
} }
export type Replacer = (input: string, md: MarkdownDocument) => string;
@customElement("ak-markdown") @customElement("ak-markdown")
export class Markdown extends AKElement { export class Markdown extends AKElement {
@property({ attribute: false }) @property({ attribute: false })
md?: MarkdownDocument; md?: MarkdownDocument;
@property({ attribute: false })
replacers: Replacer[] = [];
defaultReplacers: Replacer[] = [
this.replaceAdmonitions,
this.replaceList,
this.replaceRelativeLinks,
];
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFList, PFContent, AKGlobal]; return [PFList, PFContent, AKGlobal];
} }
replaceAdmonitions(input: string): string {
const admonitionStart = /:::(\w+)<br\s\/>/gm;
const admonitionEnd = /:::/gm;
return input
.replaceAll(admonitionStart, "<ak-alert level=\"$1\">")
.replaceAll(admonitionEnd, "</ak-alert>");
}
replaceList(input: string): string {
return input.replace("<ul>", "<ul class='pf-c-list'>");
}
replaceRelativeLinks(input: string, md: MarkdownDocument): string {
const relativeLink = /href=".(.*)"/gm;
const cwd = process.env.CWD as string;
// cwd will point to $root/web, but the docs are in $root/website/docs
let relPath = md.path.replace(cwd + "site", "");
if (md.filename === "index.md") {
relPath = relPath.replace("index.md", "");
}
const baseURL = "https://goauthentik.io";
const fullURL = `${baseURL}${relPath}.$1`;
return input.replace(relativeLink, `href="${fullURL}" target="_blank"`);
}
render(): TemplateResult { render(): TemplateResult {
if (!this.md) { if (!this.md) {
return html``; return html``;
} }
const finalHTML = this.md?.html.replace("<ul>", "<ul class='pf-c-list'>"); let finalHTML = this.md.html;
const replacers = [...this.defaultReplacers, ...this.replacers];
replacers.forEach((r) => {
if (!this.md) {
return;
}
finalHTML = r(finalHTML, this.md);
});
return html`${this.md?.metadata.title ? html`<h2>${this.md.metadata.title}</h2>` : html``} return html`${this.md?.metadata.title ? html`<h2>${this.md.metadata.title}</h2>` : html``}
${unsafeHTML(finalHTML)}`; ${unsafeHTML(finalHTML)}`;
} }

View File

@ -47,6 +47,10 @@ To hide applications without modifying policy settings and without removing it,
Keep in mind, the users still have access, so they can still authorize access when the login process is started from the application. Keep in mind, the users still have access, so they can still authorize access when the login process is started from the application.
### Launch URLs (2022.3+) ### Launch URLs
:::info
Requires authentik 2022.3
:::
To give users direct links to applications, you can now use an URL like `https://authentik.company/application/launch/<slug>/`. This will redirect the user directly if they're already logged in, and otherwise authenticate the user, and then forward them. To give users direct links to applications, you can now use an URL like `https://authentik.company/application/launch/<slug>/`. This will redirect the user directly if they're already logged in, and otherwise authenticate the user, and then forward them.