web: re-organise frontend and cleanup common code (#3572)
* fix repo in api client Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: re-organise files to match their interface Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * core: include version in script tags Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * cleanup maybe broken Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * revert rename Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: get rid of Client.ts Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * move more to common Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * more moving Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * format Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * unfuck files that vscode fucked, thanks Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * move more Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * finish moving (maybe) Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * ok more moving Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix more stuff that vs code destroyed Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * get rid "web" prefix for virtual package Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix locales Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * use custom base element Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix css file Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * don't run autoDetectLanguage when importing locale Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * fix circular dependencies Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * web: fix build Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
105
web/src/common/api/config.ts
Normal file
105
web/src/common/api/config.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import {
|
||||
CSRFMiddleware,
|
||||
EventMiddleware,
|
||||
LoggingMiddleware,
|
||||
} from "@goauthentik/common/api/middleware";
|
||||
import { EVENT_REFRESH, VERSION } from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { activateLocale } from "@goauthentik/common/ui/locale";
|
||||
|
||||
import {
|
||||
Config,
|
||||
ConfigFromJSON,
|
||||
Configuration,
|
||||
CoreApi,
|
||||
CurrentTenant,
|
||||
CurrentTenantFromJSON,
|
||||
RootApi,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
let globalConfigPromise: Promise<Config> | undefined = Promise.resolve(
|
||||
ConfigFromJSON(globalAK()?.config),
|
||||
);
|
||||
export function config(): Promise<Config> {
|
||||
if (!globalConfigPromise) {
|
||||
globalConfigPromise = new RootApi(DEFAULT_CONFIG).rootConfigRetrieve();
|
||||
}
|
||||
return globalConfigPromise;
|
||||
}
|
||||
|
||||
export function tenantSetFavicon(tenant: CurrentTenant) {
|
||||
/**
|
||||
* <link rel="icon" href="/static/dist/assets/icons/icon.png">
|
||||
* <link rel="shortcut icon" href="/static/dist/assets/icons/icon.png">
|
||||
*/
|
||||
const rels = ["icon", "shortcut icon"];
|
||||
rels.forEach((rel) => {
|
||||
let relIcon = document.head.querySelector<HTMLLinkElement>(`link[rel='${rel}']`);
|
||||
if (!relIcon) {
|
||||
relIcon = document.createElement("link");
|
||||
relIcon.rel = rel;
|
||||
document.getElementsByTagName("head")[0].appendChild(relIcon);
|
||||
}
|
||||
relIcon.href = tenant.brandingFavicon;
|
||||
});
|
||||
}
|
||||
|
||||
export function tenantSetLocale(tenant: CurrentTenant) {
|
||||
if (tenant.defaultLocale === "") {
|
||||
return;
|
||||
}
|
||||
console.debug("authentik/locale: setting locale from tenant default");
|
||||
activateLocale(tenant.defaultLocale);
|
||||
}
|
||||
|
||||
let globalTenantPromise: Promise<CurrentTenant> | undefined = Promise.resolve(
|
||||
CurrentTenantFromJSON(globalAK()?.tenant),
|
||||
);
|
||||
export function tenant(): Promise<CurrentTenant> {
|
||||
if (!globalTenantPromise) {
|
||||
globalTenantPromise = new CoreApi(DEFAULT_CONFIG)
|
||||
.coreTenantsCurrentRetrieve()
|
||||
.then((tenant) => {
|
||||
tenantSetFavicon(tenant);
|
||||
tenantSetLocale(tenant);
|
||||
return tenant;
|
||||
});
|
||||
}
|
||||
return globalTenantPromise;
|
||||
}
|
||||
|
||||
export function getMetaContent(key: string): string {
|
||||
const metaEl = document.querySelector<HTMLMetaElement>(`meta[name=${key}]`);
|
||||
if (!metaEl) return "";
|
||||
return metaEl.content;
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG = new Configuration({
|
||||
basePath: process.env.AK_API_BASE_PATH + "/api/v3",
|
||||
headers: {
|
||||
"sentry-trace": getMetaContent("sentry-trace"),
|
||||
},
|
||||
middleware: [
|
||||
new CSRFMiddleware(),
|
||||
new EventMiddleware(),
|
||||
new LoggingMiddleware(CurrentTenantFromJSON(globalAK()?.tenant)),
|
||||
],
|
||||
});
|
||||
|
||||
// This is just a function so eslint doesn't complain about
|
||||
// missing-whitespace-between-attributes or
|
||||
// unexpected-character-in-attribute-name
|
||||
export function AndNext(url: string): string {
|
||||
return `?next=${encodeURIComponent(url)}`;
|
||||
}
|
||||
|
||||
window.addEventListener(EVENT_REFRESH, () => {
|
||||
// Upon global refresh, disregard whatever was pre-hydrated and
|
||||
// actually load info from API
|
||||
globalConfigPromise = undefined;
|
||||
globalTenantPromise = undefined;
|
||||
config();
|
||||
tenant();
|
||||
});
|
||||
|
||||
console.debug(`authentik(early): version ${VERSION}, apiBase ${DEFAULT_CONFIG.basePath}`);
|
||||
56
web/src/common/api/middleware.ts
Normal file
56
web/src/common/api/middleware.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { EVENT_REQUEST_POST } from "@goauthentik/common/constants";
|
||||
import { getCookie } from "@goauthentik/common/utils";
|
||||
|
||||
import {
|
||||
CurrentTenant,
|
||||
FetchParams,
|
||||
Middleware,
|
||||
RequestContext,
|
||||
ResponseContext,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export interface RequestInfo {
|
||||
method: string;
|
||||
path: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export class LoggingMiddleware implements Middleware {
|
||||
tenant: CurrentTenant;
|
||||
constructor(tenant: CurrentTenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
post(context: ResponseContext): Promise<Response | void> {
|
||||
let msg = `authentik/api[${this.tenant.matchedDomain}]: `;
|
||||
msg += `${context.response.status} ${context.init.method} ${context.url}`;
|
||||
console.debug(msg);
|
||||
return Promise.resolve(context.response);
|
||||
}
|
||||
}
|
||||
|
||||
export class CSRFMiddleware implements Middleware {
|
||||
pre?(context: RequestContext): Promise<FetchParams | void> {
|
||||
// @ts-ignore
|
||||
context.init.headers["X-authentik-CSRF"] = getCookie("authentik_csrf");
|
||||
return Promise.resolve(context);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMiddleware implements Middleware {
|
||||
post?(context: ResponseContext): Promise<Response | void> {
|
||||
const request: RequestInfo = {
|
||||
method: (context.init.method || "GET").toUpperCase(),
|
||||
path: context.url,
|
||||
status: context.response.status,
|
||||
};
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(EVENT_REQUEST_POST, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: request,
|
||||
}),
|
||||
);
|
||||
return Promise.resolve(context.response);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user