Compare commits

...

4 Commits

28 changed files with 269 additions and 183 deletions

View File

@ -3,13 +3,11 @@
* @import { StorybookConfig } from "@storybook/web-components-vite"; * @import { StorybookConfig } from "@storybook/web-components-vite";
* @import { InlineConfig, Plugin } from "vite"; * @import { InlineConfig, Plugin } from "vite";
*/ */
import { cwd } from "process"; import { createBundleDefinitions } from "@goauthentik/web/scripts/esbuild/environment";
import postcssLit from "rollup-plugin-postcss-lit"; import postcssLit from "rollup-plugin-postcss-lit";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";
const NODE_ENV = process.env.NODE_ENV || "development"; const CSSImportPattern = /import [\w$]+ from .+\.(css)/g;
const CSSImportPattern = /import [\w\$]+ from .+\.(css)/g;
const JavaScriptFilePattern = /\.m?(js|ts|tsx)$/; const JavaScriptFilePattern = /\.m?(js|ts|tsx)$/;
/** /**
@ -54,11 +52,7 @@ const config = {
*/ */
const mergedConfig = { const mergedConfig = {
...config, ...config,
define: { define: createBundleDefinitions(),
"process.env.NODE_ENV": JSON.stringify(NODE_ENV),
"process.env.CWD": JSON.stringify(cwd()),
"process.env.AK_API_BASE_PATH": JSON.stringify(process.env.AK_API_BASE_PATH || ""),
},
plugins: [inlineCSSPlugin, ...plugins, postcssLit(), tsconfigPaths()], plugins: [inlineCSSPlugin, ...plugins, postcssLit(), tsconfigPaths()],
}; };

View File

@ -21,7 +21,7 @@ const log = console.debug.bind(console, logPrefix);
* ESBuild may tree-shake it out of production builds. * ESBuild may tree-shake it out of production builds.
* *
* ```ts * ```ts
* if (process.env.NODE_ENV === "development") { * if (import.meta.env.NODE_ENV=== "development") {
* await import("@goauthentik/esbuild-plugin-live-reload/client") * await import("@goauthentik/esbuild-plugin-live-reload/client")
* .catch(() => console.warn("Failed to import watcher")) * .catch(() => console.warn("Failed to import watcher"))
* } * }

View File

@ -4,15 +4,20 @@
export {}; export {};
declare global { declare global {
/**
* Environment variables injected by ESBuild.
*/
interface ImportMetaEnv {
/**
* The injected watcher URL for ESBuild.
* This is used for live reloading in development mode.
*
* @format url
*/
readonly ESBUILD_WATCHER_URL?: string;
}
interface ImportMeta { interface ImportMeta {
readonly env: { readonly env: ImportMetaEnv;
/**
* The injected watcher URL for ESBuild.
* This is used for live reloading in development mode.
*
* @format url
*/
ESBUILD_WATCHER_URL: string;
};
} }
} }

View File

@ -1,16 +0,0 @@
/**
* @file Constants for JavaScript and TypeScript files.
*/
/// <reference types="../../types/global.js" />
/**
* The current Node.js environment, defaulting to "development" when not set.
*
* Note, this should only be used during the build process.
*
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
* ensure that module tree-shaking works correctly.
*
*/
export const NodeEnvironment = process.env.NODE_ENV || "development";

View File

@ -1,6 +1,20 @@
/** /**
* @file Utility functions for building and copying files. * @file Utility functions for working with environment variables.
*/ */
/// <reference types="./types/global.js" />
//#region Constants
/**
* The current Node.js environment, defaulting to "development" when not set.
*
* Note, this should only be used during the build process.
*
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
* ensure that module tree-shaking works correctly.
*
*/
export const NodeEnvironment = process.env.NODE_ENV || "development";
/** /**
* A source environment variable, which can be a string, number, boolean, null, or undefined. * A source environment variable, which can be a string, number, boolean, null, or undefined.
@ -14,19 +28,26 @@
* @typedef {T extends string ? `"${T}"` : T} JSONify * @typedef {T extends string ? `"${T}"` : T} JSONify
*/ */
//#endregion
//#region Utilities
/** /**
* Given an object of environment variables, returns a new object with the same keys and values, but * Given an object of environment variables, returns a new object with the same keys and values, but
* with the values serialized as strings. * with the values serialized as strings.
* *
* @template {Record<string, EnvironmentVariable>} EnvRecord * @template {Record<string, EnvironmentVariable>} EnvRecord
* @template {string} [Prefix='process.env.'] * @template {string} [Prefix='import.meta.env.']
* *
* @param {EnvRecord} input * @param {EnvRecord} input
* @param {Prefix} [prefix='process.env.'] * @param {Prefix} [prefix='import.meta.env.']
* *
* @returns {{[K in keyof EnvRecord as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}} * @returns {{[K in keyof EnvRecord as `${Prefix}${K}`]: JSONify<EnvRecord[K]>}}
*/ */
export function serializeEnvironmentVars(input, prefix = /** @type {Prefix} */ ("process.env.")) { export function serializeEnvironmentVars(
input,
prefix = /** @type {Prefix} */ ("import.meta.env."),
) {
/** /**
* @type {Record<string, string>} * @type {Record<string, string>}
*/ */
@ -40,3 +61,5 @@ export function serializeEnvironmentVars(input, prefix = /** @type {Prefix} */ (
return /** @type {any} */ (env); return /** @type {any} */ (env);
} }
//#endregion

View File

@ -1,7 +1,6 @@
/// <reference types="./types/global.js" /> /// <reference types="./types/global.js" />
export * from "./paths.js"; export * from "./paths.js";
export * from "./constants.js"; export * from "./environment.js";
export * from "./build.js";
export * from "./version.js"; export * from "./version.js";
export * from "./scripting.js"; export * from "./scripting.js";

View File

@ -1,17 +1,32 @@
import { spawnSync } from "child_process"; /**
import fs from "fs"; * @file Lit Localize build script.
import path from "path"; *
import process from "process"; * @remarks
* Determines if all the Xliff translation source files are present and if the Typescript source files generated from those sources are up-to-date.
*
* If they are not, it runs the locale building script,
* intercepting the long spew of "this string is not translated" and replacing it with a
* summary of how many strings are missing with respect to the source locale.
*
* @import { ConfigFile } from "@lit/localize-tools/lib/types/config"
*/
import { PackageRoot } from "@goauthentik/web/paths";
import { spawnSync } from "node:child_process";
import { readFileSync, statSync } from "node:fs";
import path from "node:path";
/** /**
* Determines if all the Xliff translation source files are present and if the Typescript source * @type {ConfigFile}
* files generated from those sources are up-to-date. If they are not, it runs the locale building
* script, intercepting the long spew of "this string is not translated" and replacing it with a
* summary of how many strings are missing with respect to the source locale.
*/ */
const localizeRules = JSON.parse(
readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8"),
);
const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8")); /**
*
* @param {string} loc
* @returns {boolean}
*/
function generatedFileIsUpToDateWithXliffSource(loc) { function generatedFileIsUpToDateWithXliffSource(loc) {
const xliff = path.join("./xliff", `${loc}.xlf`); const xliff = path.join("./xliff", `${loc}.xlf`);
const gened = path.join("./src/locales", `${loc}.ts`); const gened = path.join("./src/locales", `${loc}.ts`);
@ -22,7 +37,7 @@ function generatedFileIsUpToDateWithXliffSource(loc) {
// generates a unique error message and halts the build. // generates a unique error message and halts the build.
try { try {
var xlfStat = fs.statSync(xliff); var xlfStat = statSync(xliff);
} catch (_error) { } catch (_error) {
console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`); console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
process.exit(1); process.exit(1);
@ -30,7 +45,7 @@ function generatedFileIsUpToDateWithXliffSource(loc) {
// If the generated file doesn't exist, of course it's not up to date. // If the generated file doesn't exist, of course it's not up to date.
try { try {
var genedStat = fs.statSync(gened); var genedStat = statSync(gened);
} catch (_error) { } catch (_error) {
return false; return false;
} }

View File

@ -1,3 +1,4 @@
/// <reference types="../types/esbuild.js" />
/** /**
* @file ESBuild script for building the authentik web UI. * @file ESBuild script for building the authentik web UI.
* *
@ -9,7 +10,6 @@ import {
NodeEnvironment, NodeEnvironment,
readBuildIdentifier, readBuildIdentifier,
resolvePackage, resolvePackage,
serializeEnvironmentVars,
} from "@goauthentik/monorepo"; } from "@goauthentik/monorepo";
import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "@goauthentik/web/paths"; import { DistDirectory, DistDirectoryName, EntryPoint, PackageRoot } from "@goauthentik/web/paths";
import { deepmerge } from "deepmerge-ts"; import { deepmerge } from "deepmerge-ts";
@ -20,15 +20,10 @@ import * as fs from "node:fs/promises";
import * as path from "node:path"; import * as path from "node:path";
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs"; import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
import { createBundleDefinitions } from "./esbuild/environment.mjs";
const logPrefix = "[Build]"; const logPrefix = "[Build]";
const definitions = serializeEnvironmentVars({
NODE_ENV: NodeEnvironment,
CWD: process.cwd(),
AK_API_BASE_PATH: process.env.AK_API_BASE_PATH,
});
const patternflyPath = resolvePackage("@patternfly/patternfly"); const patternflyPath = resolvePackage("@patternfly/patternfly");
/** /**
@ -86,7 +81,7 @@ const BASE_ESBUILD_OPTIONS = {
root: MonoRepoRoot, root: MonoRepoRoot,
}), }),
], ],
define: definitions, define: createBundleDefinitions(),
format: "esm", format: "esm",
logOverride: { logOverride: {
/** /**

View File

@ -0,0 +1,29 @@
/**
* @file ESBuild environment utilities.
*/
import { AuthentikVersion, NodeEnvironment, serializeEnvironmentVars } from "@goauthentik/monorepo";
/**
* Creates a mapping of environment variables to their respective runtime constants.
*/
export function createBundleDefinitions() {
const SerializedNodeEnvironment = /** @type {`"development"` | `"production"`} */ (
JSON.stringify(NodeEnvironment)
);
/**
* @satisfies {Record<ESBuildImportEnvKey, string>}
*/
const envRecord = {
AK_VERSION: AuthentikVersion,
AK_API_BASE_PATH: process.env.AK_API_BASE_PATH ?? "",
};
return {
...serializeEnvironmentVars(envRecord),
// We need to explicitly set this for NPM packages that use `process`
// to determine their environment.
"process.env.NODE_ENV": SerializedNodeEnvironment,
"import.meta.env.NODE_ENV": SerializedNodeEnvironment,
};
}

View File

@ -35,6 +35,11 @@ const __dirname = fileURLToPath(new URL(".", import.meta.url));
const projectRoot = path.join(__dirname, ".."); const projectRoot = path.join(__dirname, "..");
process.chdir(projectRoot); process.chdir(projectRoot);
/**
*
* @param {string[]} flags
* @returns
*/
const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]); const hasFlag = (flags) => process.argv.length > 1 && flags.includes(process.argv[2]);
const [configFile, files] = hasFlag(["-n", "--nightmare"]) const [configFile, files] = hasFlag(["-n", "--nightmare"])

View File

@ -1,22 +1,36 @@
import { readFileSync } from "fs"; /**
import path from "path"; * @file Pseudo-localization script.
*
* @import { ConfigFile } from "@lit/localize-tools/lib/types/config.js"
* @import { Config } from '@lit/localize-tools/lib/types/config.js';
* @import { ProgramMessage } from "@lit/localize-tools/src/messages.js"
* @import { Locale } from "@lit/localize-tools/src/types/locale.js"
*/
import { PackageRoot } from "@goauthentik/web/paths";
import { readFileSync } from "node:fs";
import path from "node:path";
import pseudolocale from "pseudolocale"; import pseudolocale from "pseudolocale";
import { fileURLToPath } from "url";
import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js"; import { makeFormatter } from "@lit/localize-tools/lib/formatters/index.js";
import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js"; import { sortProgramMessages } from "@lit/localize-tools/lib/messages.js";
import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js"; import { TransformLitLocalizer } from "@lit/localize-tools/lib/modes/transform.js";
const __dirname = fileURLToPath(new URL(".", import.meta.url)); const pseudoLocale = /** @type {Locale} */ ("pseudo-LOCALE");
const pseudoLocale = "pseudo-LOCALE";
const targetLocales = [pseudoLocale]; const targetLocales = [pseudoLocale];
const baseConfig = JSON.parse(readFileSync(path.join(__dirname, "../lit-localize.json"), "utf-8"));
/**
* @type {ConfigFile}
*/
const baseConfig = JSON.parse(readFileSync(path.join(PackageRoot, "lit-localize.json"), "utf-8"));
// Need to make some internal specifications to satisfy the transformer. It doesn't actually matter // Need to make some internal specifications to satisfy the transformer. It doesn't actually matter
// which Localizer we use (transformer or runtime), because all of the functionality we care about // which Localizer we use (transformer or runtime), because all of the functionality we care about
// is in their common parent class, but I had to pick one. Everything else here is just pure // is in their common parent class, but I had to pick one. Everything else here is just pure
// exploitation of the lit/localize-tools internals. // exploitation of the lit/localize-tools internals.
/**
* @satisfies {Config}
*/
const config = { const config = {
...baseConfig, ...baseConfig,
baseDir: path.join(__dirname, ".."), baseDir: path.join(__dirname, ".."),
@ -28,6 +42,11 @@ const config = {
resolve: (path) => path, resolve: (path) => path,
}; };
/**
*
* @param {ProgramMessage} message
* @returns
*/
const pseudoMessagify = (message) => ({ const pseudoMessagify = (message) => ({
name: message.name, name: message.name,
contents: message.contents.map((content) => contents: message.contents.map((content) =>
@ -36,7 +55,7 @@ const pseudoMessagify = (message) => ({
}); });
const localizer = new TransformLitLocalizer(config); const localizer = new TransformLitLocalizer(config);
const messages = localizer.extractSourceMessages().messages; const { messages } = localizer.extractSourceMessages();
const translations = messages.map(pseudoMessagify); const translations = messages.map(pseudoMessagify);
const sorted = sortProgramMessages([...messages]); const sorted = sortProgramMessages([...messages]);
const formatter = makeFormatter(config); const formatter = makeFormatter(config);

View File

@ -1,5 +1,4 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global"; import { globalAK } from "@goauthentik/common/global";
import { DefaultBrand } from "@goauthentik/common/ui/config"; import { DefaultBrand } from "@goauthentik/common/ui/config";
import "@goauthentik/elements/EmptyState"; import "@goauthentik/elements/EmptyState";
@ -45,7 +44,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
} }
return [ return [
[msg("Version"), version.versionCurrent], [msg("Version"), version.versionCurrent],
[msg("UI Version"), VERSION], [msg("UI Version"), import.meta.env.AK_VERSION],
[msg("Build"), build], [msg("Build"), build],
[msg("Python version"), status.runtime.pythonVersion], [msg("Python version"), status.runtime.pythonVersion],
[msg("Platform"), status.runtime.platform], [msg("Platform"), status.runtime.platform],

View File

@ -43,7 +43,7 @@ import {
renderSidebarItems, renderSidebarItems,
} from "./AdminSidebar.js"; } from "./AdminSidebar.js";
if (process.env.NODE_ENV === "development") { if (import.meta.env.NODE_ENV === "development") {
await import("@goauthentik/esbuild-plugin-live-reload/client"); await import("@goauthentik/esbuild-plugin-live-reload/client");
} }

View File

@ -8,7 +8,6 @@ import "@goauthentik/admin/admin-overview/cards/WorkerStatusCard";
import "@goauthentik/admin/admin-overview/charts/AdminLoginAuthorizeChart"; import "@goauthentik/admin/admin-overview/charts/AdminLoginAuthorizeChart";
import "@goauthentik/admin/admin-overview/charts/OutpostStatusChart"; import "@goauthentik/admin/admin-overview/charts/OutpostStatusChart";
import "@goauthentik/admin/admin-overview/charts/SyncStatusChart"; import "@goauthentik/admin/admin-overview/charts/SyncStatusChart";
import { VERSION } from "@goauthentik/common/constants";
import { me } from "@goauthentik/common/users"; import { me } from "@goauthentik/common/users";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js"; import { WithLicenseSummary } from "@goauthentik/elements/Interface/licenseSummaryProvider.js";
@ -22,8 +21,6 @@ import { msg, str } from "@lit/localize";
import { CSSResult, TemplateResult, css, html, nothing } from "lit"; import { CSSResult, TemplateResult, css, html, nothing } from "lit";
import { customElement, state } from "lit/decorators.js"; import { customElement, state } from "lit/decorators.js";
import { classMap } from "lit/directives/class-map.js"; import { classMap } from "lit/directives/class-map.js";
import { map } from "lit/directives/map.js";
import { when } from "lit/directives/when.js";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFDivider from "@patternfly/patternfly/components/Divider/divider.css"; import PFDivider from "@patternfly/patternfly/components/Divider/divider.css";
@ -33,21 +30,17 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { SessionUser } from "@goauthentik/api"; import { SessionUser } from "@goauthentik/api";
export function versionFamily(): string { function createReleaseNotesURL(semver: string): URL {
const parts = VERSION.split("."); const segments = semver.split(".");
parts.pop(); const versionFamily = segments.slice(0, -1).join(".");
return parts.join(".");
const release = `${versionFamily}#fixed-in-${segments.join("")}`;
return new URL(`/docs/releases/${release}`, "https://goauthentik.io");
} }
const RELEASE = `${VERSION.split(".").slice(0, -1).join(".")}#fixed-in-${VERSION.replaceAll(
".",
"",
)}`;
const AdminOverviewBase = WithLicenseSummary(AKElement); const AdminOverviewBase = WithLicenseSummary(AKElement);
type Renderer = () => TemplateResult | typeof nothing;
@customElement("ak-admin-overview") @customElement("ak-admin-overview")
export class AdminOverviewPage extends AdminOverviewBase { export class AdminOverviewPage extends AdminOverviewBase {
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
@ -83,7 +76,11 @@ export class AdminOverviewPage extends AdminOverviewBase {
[msg("Check the logs"), paramURL("/events/log")], [msg("Check the logs"), paramURL("/events/log")],
[msg("Explore integrations"), "https://goauthentik.io/integrations/", true], [msg("Explore integrations"), "https://goauthentik.io/integrations/", true],
[msg("Manage users"), paramURL("/identity/users")], [msg("Manage users"), paramURL("/identity/users")],
[msg("Check the release notes"), `https://goauthentik.io/docs/releases/${RELEASE}`, true], [
msg("Check the release notes"),
createReleaseNotesURL(import.meta.env.AK_VERSION).href,
true,
],
]; ];
@state() @state()
@ -193,45 +190,6 @@ export class AdminOverviewPage extends AdminOverviewBase {
</div>` </div>`
: nothing} `; : nothing} `;
} }
renderActions() {
const release = `${versionFamily()}#fixed-in-${VERSION.replaceAll(".", "")}`;
const quickActions: [string, string][] = [
[msg("Create a new application"), paramURL("/core/applications", { createForm: true })],
[msg("Check the logs"), paramURL("/events/log")],
[msg("Explore integrations"), "https://goauthentik.io/integrations/"],
[msg("Manage users"), paramURL("/identity/users")],
[msg("Check the release notes"), `https://goauthentik.io/docs/releases/${release}`],
];
const action = ([label, url]: [string, string]) => {
const isExternal = url.startsWith("https://");
const ex = (truecase: Renderer, falsecase: Renderer) =>
when(isExternal, truecase, falsecase);
const content = html`${label}${ex(
() => html`<i class="fas fa-external-link-alt ak-external-link"></i>`,
() => nothing,
)}`;
return html`<li>
${ex(
() =>
html`<a
href="${url}"
class="pf-u-mb-xl"
rel="noopener noreferrer"
target="_blank"
>${content}</a
>`,
() => html`<a href="${url}" class="pf-u-mb-xl" )>${content}</a>`,
)}
</li>`;
};
return html`${map(quickActions, action)}`;
}
} }
declare global { declare global {

View File

@ -3,7 +3,7 @@ import {
EventMiddleware, EventMiddleware,
LoggingMiddleware, LoggingMiddleware,
} from "@goauthentik/common/api/middleware.js"; } from "@goauthentik/common/api/middleware.js";
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants.js"; import { EVENT_LOCALE_REQUEST } from "@goauthentik/common/constants.js";
import { globalAK } from "@goauthentik/common/global.js"; import { globalAK } from "@goauthentik/common/global.js";
import { SentryMiddleware } from "@goauthentik/common/sentry"; import { SentryMiddleware } from "@goauthentik/common/sentry";
@ -79,4 +79,6 @@ export function AndNext(url: string): string {
return `?next=${encodeURIComponent(url)}`; return `?next=${encodeURIComponent(url)}`;
} }
console.debug(`authentik(early): version ${VERSION}, apiBase ${DEFAULT_CONFIG.basePath}`); console.debug(
`authentik(early): version ${import.meta.env.AK_VERSION}, apiBase ${DEFAULT_CONFIG.basePath}`,
);

View File

@ -1,12 +1,35 @@
/**
* @file Global constants used throughout the application.
*
* @todo Much of this content can be moved to a specific file, element, or component.
*/
/// <reference types="../../types/esbuild.js" />
//#region Patternfly
export const SECONDARY_CLASS = "pf-m-secondary"; export const SECONDARY_CLASS = "pf-m-secondary";
export const SUCCESS_CLASS = "pf-m-success"; export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger"; export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress"; export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current"; export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2025.4.1";
//#endregion
//#region Application
export const TITLE_DEFAULT = "authentik"; export const TITLE_DEFAULT = "authentik";
/**
* The delimiter used to parse the URL for the current route.
*
* @todo Move this to the ak-router.
*/
export const ROUTE_SEPARATOR = ";"; export const ROUTE_SEPARATOR = ";";
//#endregion
//#region Events
export const EVENT_REFRESH = "ak-refresh"; export const EVENT_REFRESH = "ak-refresh";
export const EVENT_NOTIFICATION_DRAWER_TOGGLE = "ak-notification-toggle"; export const EVENT_NOTIFICATION_DRAWER_TOGGLE = "ak-notification-toggle";
export const EVENT_API_DRAWER_TOGGLE = "ak-api-drawer-toggle"; export const EVENT_API_DRAWER_TOGGLE = "ak-api-drawer-toggle";
@ -20,7 +43,17 @@ export const EVENT_MESSAGE = "ak-message";
export const EVENT_THEME_CHANGE = "ak-theme-change"; export const EVENT_THEME_CHANGE = "ak-theme-change";
export const EVENT_REFRESH_ENTERPRISE = "ak-refresh-enterprise"; export const EVENT_REFRESH_ENTERPRISE = "ak-refresh-enterprise";
//#endregion
//#region WebSocket
export const WS_MSG_TYPE_MESSAGE = "message"; export const WS_MSG_TYPE_MESSAGE = "message";
export const WS_MSG_TYPE_REFRESH = "refresh"; export const WS_MSG_TYPE_REFRESH = "refresh";
//#endregion
//#region LocalStorage
export const LOCALSTORAGE_AUTHENTIK_KEY = "authentik-local-settings"; export const LOCALSTORAGE_AUTHENTIK_KEY = "authentik-local-settings";
//#endregion

View File

@ -1,4 +1,3 @@
import { VERSION } from "@goauthentik/common/constants";
import { SentryIgnoredError } from "@goauthentik/common/sentry"; import { SentryIgnoredError } from "@goauthentik/common/sentry";
export interface PlexPinResponse { export interface PlexPinResponse {
@ -19,7 +18,7 @@ export const DEFAULT_HEADERS = {
"Accept": "application/json", "Accept": "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
"X-Plex-Product": "authentik", "X-Plex-Product": "authentik",
"X-Plex-Version": VERSION, "X-Plex-Version": import.meta.env.AK_VERSION,
"X-Plex-Device-Vendor": "goauthentik.io", "X-Plex-Device-Vendor": "goauthentik.io",
}; };

View File

@ -1,4 +1,3 @@
import { VERSION } from "@goauthentik/common/constants";
import { globalAK } from "@goauthentik/common/global"; import { globalAK } from "@goauthentik/common/global";
import { me } from "@goauthentik/common/users"; import { me } from "@goauthentik/common/users";
import { readInterfaceRouteParam } from "@goauthentik/elements/router/utils"; import { readInterfaceRouteParam } from "@goauthentik/elements/router/utils";
@ -50,7 +49,7 @@ export function configureSentry(canDoPpi = false) {
/MutationObserver.observe/gi, /MutationObserver.observe/gi,
/NS_ERROR_FAILURE/gi, /NS_ERROR_FAILURE/gi,
], ],
release: `authentik@${VERSION}`, release: `authentik@${import.meta.env.AK_VERSION}`,
integrations: [ integrations: [
browserTracingIntegration({ browserTracingIntegration({
// https://docs.sentry.io/platforms/javascript/tracing/instrumentation/automatic-instrumentation/#custom-routing // https://docs.sentry.io/platforms/javascript/tracing/instrumentation/automatic-instrumentation/#custom-routing

View File

@ -223,7 +223,7 @@ export function inspectStyleSheetTree(element: ReactiveElement): InspectedStyleS
}; };
} }
if (process.env.NODE_ENV === "development") { if (import.meta.env.NODE_ENV === "development") {
Object.assign(window, { Object.assign(window, {
inspectStyleSheetTree, inspectStyleSheetTree,
serializeStyleSheet, serializeStyleSheet,

View File

@ -5,11 +5,11 @@ import {
type StyleRoot, type StyleRoot,
createStyleSheetUnsafe, createStyleSheetUnsafe,
setAdoptedStyleSheets, setAdoptedStyleSheets,
} from "@goauthentik/common/stylesheets.js"; } from "@goauthentik/web/common/stylesheets.js";
import { UIConfig } from "@goauthentik/common/ui/config.js"; import { UIConfig } from "@goauthentik/web/common/ui/config.js";
import AKBase from "@goauthentik/common/styles/authentik.css"; import AKBase from "@goauthentik/web/common/styles/authentik.css";
import AKBaseDark from "@goauthentik/common/styles/theme-dark.css"; import AKBaseDark from "@goauthentik/web/common/styles/theme-dark.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { Config, CurrentBrand, UiThemeEnum } from "@goauthentik/api"; import { Config, CurrentBrand, UiThemeEnum } from "@goauthentik/api";

View File

@ -1,5 +1,4 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { VERSION } from "@goauthentik/common/constants";
import { PFSize } from "@goauthentik/common/enums.js"; import { PFSize } from "@goauthentik/common/enums.js";
import { import {
EventContext, EventContext,
@ -76,7 +75,7 @@ ${context.message as string}
**Version and Deployment (please complete the following information):** **Version and Deployment (please complete the following information):**
- authentik version: ${VERSION} - authentik version: ${import.meta.env.AK_VERSION}
- Deployment: [e.g. docker-compose, helm] - Deployment: [e.g. docker-compose, helm]
**Additional context** **Additional context**

View File

@ -1,4 +1,3 @@
import { VERSION } from "@goauthentik/common/constants";
import { AKElement } from "@goauthentik/elements/Base"; import { AKElement } from "@goauthentik/elements/Base";
import { WithVersion } from "@goauthentik/elements/Interface/versionProvider"; import { WithVersion } from "@goauthentik/elements/Interface/versionProvider";
@ -10,20 +9,19 @@ import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
@customElement("ak-version-banner") @customElement("ak-version-banner")
export class VersionBanner extends WithVersion(AKElement) { export class VersionBanner extends WithVersion(AKElement) {
static get styles() { static styles = [PFBanner];
return [PFBanner];
}
render() { render() {
return this.version && this.version.versionCurrent !== VERSION if (!this.version?.versionCurrent) return nothing;
? html` if (this.version.versionCurrent === import.meta.env.AK_VERSION) return nothing;
<div class="pf-c-banner pf-m-sticky pf-m-gold">
${msg( return html`
str`A newer version (${this.version.versionCurrent}) of the UI is available.`, <div class="pf-c-banner pf-m-sticky pf-m-gold">
)} ${msg(
</div> str`A newer version (${this.version.versionCurrent}) of the UI is available.`,
` )}
: nothing; </div>
`;
} }
} }

View File

@ -14,6 +14,6 @@ import "@goauthentik/flow/stages/password/PasswordStage";
// end of stage import // end of stage import
if (process.env.NODE_ENV === "development") { if (import.meta.env.NODE_ENV === "development") {
await import("@goauthentik/esbuild-plugin-live-reload/client"); await import("@goauthentik/esbuild-plugin-live-reload/client");
} }

View File

@ -43,7 +43,7 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import { CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api"; import { CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api";
if (process.env.NODE_ENV === "development") { if (import.meta.env.NODE_ENV === "development") {
await import("@goauthentik/esbuild-plugin-live-reload/client"); await import("@goauthentik/esbuild-plugin-live-reload/client");
} }

View File

@ -2,6 +2,9 @@
{ {
"extends": "@goauthentik/tsconfig", "extends": "@goauthentik/tsconfig",
"compilerOptions": { "compilerOptions": {
"checkJs": true,
"allowJs": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"experimentalDecorators": true, "experimentalDecorators": true,

39
web/types/esbuild.d.ts vendored Normal file
View File

@ -0,0 +1,39 @@
/**
* @file Import meta environment variables available via ESBuild.
*/
export {};
declare global {
interface ESBuildImportEnv {
/**
* The authentik version injected by ESBuild during build time.
*
* @format semver
*/
readonly AK_VERSION: string;
/**
* @todo Determine where this is used and if it is needed,
* give it a better name.
* @deprecated
*/
readonly AK_API_BASE_PATH: string;
}
type ESBuildImportEnvKey = keyof ESBuildImportEnv;
/**
* Environment variables injected by ESBuild.
*/
interface ImportMetaEnv extends ESBuildImportEnv {
/**
* An environment variable used to determine
* whether Node.js is running in production mode.
*/
readonly NODE_ENV: "development" | "production";
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
}

View File

@ -1,5 +1,5 @@
/** /**
* @file Environment variables available via ESBuild. * @file Global variables provided by Node.js
*/ */
declare module "module" { declare module "module" {
@ -14,8 +14,8 @@ declare module "module" {
* const relativeDirname = dirname(fileURLToPath(import.meta.url)); * const relativeDirname = dirname(fileURLToPath(import.meta.url));
* ``` * ```
*/ */
// eslint-disable-next-line no-var
var __dirname: string; const __dirname: string;
} }
} }
@ -23,13 +23,16 @@ declare module "process" {
global { global {
namespace NodeJS { namespace NodeJS {
interface ProcessEnv { interface ProcessEnv {
CWD: string; /**
* Node environment, if any.
*/
readonly NODE_ENV?: "development" | "production";
/** /**
* @todo Determine where this is used and if it is needed, * @todo Determine where this is used and if it is needed,
* give it a better name. * give it a better name.
* @deprecated * @deprecated
*/ */
AK_API_BASE_PATH: string; readonly AK_API_BASE_PATH?: string;
} }
} }
} }

View File

@ -1,17 +1,15 @@
import replace from "@rollup/plugin-replace"; /// <reference types="@wdio/browser-runner" />
import { browser } from "@wdio/globals"; import { browser } from "@wdio/globals";
import type { Options } from "@wdio/types"; import type { Options } from "@wdio/types";
import path from "path"; import path from "node:path";
import { cwd } from "process"; import { fileURLToPath } from "node:url";
import { fileURLToPath } from "url"; import { createBundleDefinitions } from "scripts/esbuild/environment.mjs";
import type { UserConfig } from "vite"; import type { InlineConfig } from "vite";
import litCss from "vite-plugin-lit-css"; import litCSS from "vite-plugin-lit-css";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";
const __dirname = fileURLToPath(new URL(".", import.meta.url)); const __dirname = fileURLToPath(new URL(".", import.meta.url));
const isProdBuild = process.env.NODE_ENV === "production";
const apiBasePath = process.env.AK_API_BASE_PATH || "";
const runHeadless = process.env.CI !== undefined; const runHeadless = process.env.CI !== undefined;
const testSafari = process.env.WDIO_TEST_SAFARI !== undefined; const testSafari = process.env.WDIO_TEST_SAFARI !== undefined;
@ -72,21 +70,9 @@ export const config: Options.Testrunner = {
runner: [ runner: [
"browser", "browser",
{ {
viteConfig: (userConfig: UserConfig = { plugins: [] }) => ({ viteConfig: {
...userConfig, define: createBundleDefinitions(),
plugins: [ plugins: [litCSS(), tsconfigPaths()],
litCss(),
replace({
"process.env.NODE_ENV": JSON.stringify(
isProdBuild ? "production" : "development",
),
"process.env.CWD": JSON.stringify(cwd()),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true,
}),
...(userConfig?.plugins ?? []),
tsconfigPaths(),
],
resolve: { resolve: {
alias: { alias: {
"@goauthentik/admin": path.resolve(__dirname, "src/admin"), "@goauthentik/admin": path.resolve(__dirname, "src/admin"),
@ -101,7 +87,7 @@ export const config: Options.Testrunner = {
"@goauthentik/user": path.resolve(__dirname, "src/user"), "@goauthentik/user": path.resolve(__dirname, "src/user"),
}, },
}, },
}), } satisfies InlineConfig,
}, },
], ],