der tenant admin hat eine neue, mobil unterstützende UI, login redirect funktioniert, typescript fehler wurden bereinigt. Neue Blog Posts von ChatGPT eingebaut, übersetzt von Gemini 2.5

This commit is contained in:
Codex Agent
2025-11-05 19:27:10 +01:00
parent adb93b5f9d
commit c6ac04eb15
44 changed files with 1995 additions and 1949 deletions

View File

@@ -1,6 +1,7 @@
import { ADMIN_LOGIN_PATH, ADMIN_LOGIN_START_PATH } from '../constants';
import { ADMIN_BASE_PATH, ADMIN_DEFAULT_AFTER_LOGIN_PATH, ADMIN_LOGIN_PATH, ADMIN_LOGIN_START_PATH } from '../constants';
const LAST_DESTINATION_KEY = 'tenant.oauth.lastDestination';
const DASHBOARD_PREFIX = '/dashboard';
function ensureLeadingSlash(target: string): string {
if (!target) {
@@ -14,6 +15,28 @@ function ensureLeadingSlash(target: string): string {
return target.startsWith('/') ? target : `/${target}`;
}
function matchesDashboardScope(path: string): boolean {
return (
path === DASHBOARD_PREFIX ||
path.startsWith(`${DASHBOARD_PREFIX}?`) ||
path.startsWith(`${DASHBOARD_PREFIX}/`)
);
}
export function isPermittedReturnTarget(target: string): boolean {
if (!target) {
return false;
}
const sanitized = ensureLeadingSlash(target);
if (sanitized.startsWith(ADMIN_BASE_PATH)) {
return true;
}
return matchesDashboardScope(sanitized);
}
function base64UrlEncode(value: string): string {
const encoder = new TextEncoder();
const bytes = encoder.encode(value);
@@ -65,16 +88,19 @@ export interface ReturnTargetResolution {
export function resolveReturnTarget(raw: string | null, fallback: string): ReturnTargetResolution {
const normalizedFallback = ensureLeadingSlash(fallback);
const fallbackIsAllowed = isPermittedReturnTarget(normalizedFallback)
? normalizedFallback
: ADMIN_DEFAULT_AFTER_LOGIN_PATH;
if (!raw) {
const encoded = encodeReturnTo(normalizedFallback);
return { finalTarget: normalizedFallback, encodedFinal: encoded };
const encoded = encodeReturnTo(fallbackIsAllowed);
return { finalTarget: fallbackIsAllowed, encodedFinal: encoded };
}
const decodedPrimary = decodeReturnTo(raw);
if (!decodedPrimary) {
const encoded = encodeReturnTo(normalizedFallback);
return { finalTarget: normalizedFallback, encodedFinal: encoded };
const encoded = encodeReturnTo(fallbackIsAllowed);
return { finalTarget: fallbackIsAllowed, encodedFinal: encoded };
}
const normalizedPrimary = decodedPrimary.trim();
@@ -86,20 +112,25 @@ export function resolveReturnTarget(raw: string | null, fallback: string): Retur
const url = new URL(normalizedPrimary, window.location.origin);
const innerRaw = url.searchParams.get('return_to');
if (!innerRaw) {
const encoded = encodeReturnTo(normalizedFallback);
return { finalTarget: normalizedFallback, encodedFinal: encoded };
const encoded = encodeReturnTo(fallbackIsAllowed);
return { finalTarget: fallbackIsAllowed, encodedFinal: encoded };
}
return resolveReturnTarget(innerRaw, normalizedFallback);
return resolveReturnTarget(innerRaw, fallbackIsAllowed);
} catch (error) {
console.warn('[Auth] Failed to parse return_to chain', error);
const encoded = encodeReturnTo(normalizedFallback);
return { finalTarget: normalizedFallback, encodedFinal: encoded };
const encoded = encodeReturnTo(fallbackIsAllowed);
return { finalTarget: fallbackIsAllowed, encodedFinal: encoded };
}
}
}
const finalTarget = ensureLeadingSlash(normalizedPrimary);
if (!isPermittedReturnTarget(finalTarget)) {
const encoded = encodeReturnTo(fallbackIsAllowed);
return { finalTarget: fallbackIsAllowed, encodedFinal: encoded };
}
const encodedFinal = encodeReturnTo(finalTarget);
return { finalTarget, encodedFinal };