fix(xworkmate): avoid preview route bailouts

This commit is contained in:
Haitao Pan 2026-03-18 16:23:50 +08:00
parent 68bf1e2c1e
commit 9569df6a27
3 changed files with 167 additions and 101 deletions

View File

@ -1,59 +1,10 @@
export const dynamic = "force-dynamic";
import { headers } from "next/headers";
import { connection } from "next/server";
import { redirect } from "next/navigation";
import { XWorkmateProfileEditor } from "@/components/xworkmate/XWorkmateProfileEditor";
import {
buildSharedXWorkmateUrl,
isLegacyConsoleXWorkmateHost,
isSharedXWorkmateHost,
normalizeXWorkmateHost,
} from "@/lib/xworkmate/host";
import { buildXWorkmateScopeKey } from "@/lib/xworkmate/types";
import { getXWorkmateSessionContext } from "@/server/xworkmate/profile";
import { XWorkmateProfileRoute } from "@/components/xworkmate/XWorkmateProfileRoute";
export const metadata = {
title: "XWorkmate Shared Integrations",
description: "Manage the shared XWorkmate integrations profile",
};
export default async function XWorkmateAdminPage() {
await connection();
const requestHeaders = await headers();
const requestHost = normalizeXWorkmateHost(
requestHeaders.get("x-forwarded-host") ?? requestHeaders.get("host"),
);
if (isLegacyConsoleXWorkmateHost(requestHost)) {
redirect(buildSharedXWorkmateUrl("/xworkmate/admin"));
}
const { user, profile } = await getXWorkmateSessionContext(requestHost);
if (!profile) {
redirect("/xworkmate");
}
if (!isSharedXWorkmateHost(requestHost)) {
redirect("/xworkmate/integrations");
}
if (
profile.profileScope !== "tenant-shared" ||
!profile.canEditIntegrations
) {
redirect("/xworkmate");
}
const scopeKey = buildXWorkmateScopeKey(profile, user?.id, requestHost);
return (
<div className="min-h-[calc(100vh-var(--app-shell-nav-offset))] bg-[linear-gradient(180deg,#f4f7fd_0%,#f6f8fb_32%,#f3f5f8_100%)] px-4 py-5 md:px-6">
<div className="mx-auto max-w-6xl">
<XWorkmateProfileEditor
payload={profile}
scopeKey={scopeKey}
workspaceHref="/xworkmate"
/>
</div>
</div>
);
export default function XWorkmateAdminPage() {
return <XWorkmateProfileRoute mode="admin" />;
}

View File

@ -1,56 +1,10 @@
export const dynamic = "force-dynamic";
import { headers } from "next/headers";
import { connection } from "next/server";
import { redirect } from "next/navigation";
import { XWorkmateProfileEditor } from "@/components/xworkmate/XWorkmateProfileEditor";
import {
buildSharedXWorkmateUrl,
isLegacyConsoleXWorkmateHost,
isSharedXWorkmateHost,
normalizeXWorkmateHost,
} from "@/lib/xworkmate/host";
import { buildXWorkmateScopeKey } from "@/lib/xworkmate/types";
import { getXWorkmateSessionContext } from "@/server/xworkmate/profile";
import { XWorkmateProfileRoute } from "@/components/xworkmate/XWorkmateProfileRoute";
export const metadata = {
title: "XWorkmate Personal Integrations",
description: "Manage the personal XWorkmate integrations profile",
};
export default async function XWorkmateIntegrationsPage() {
await connection();
const requestHeaders = await headers();
const requestHost = normalizeXWorkmateHost(
requestHeaders.get("x-forwarded-host") ?? requestHeaders.get("host"),
);
if (isLegacyConsoleXWorkmateHost(requestHost)) {
redirect(buildSharedXWorkmateUrl("/xworkmate/integrations"));
}
const { user, profile } = await getXWorkmateSessionContext(requestHost);
if (!profile) {
redirect("/xworkmate");
}
if (isSharedXWorkmateHost(requestHost)) {
redirect(profile.canEditIntegrations ? "/xworkmate/admin" : "/xworkmate");
}
if (profile.profileScope !== "user-private") {
redirect("/xworkmate");
}
const scopeKey = buildXWorkmateScopeKey(profile, user?.id, requestHost);
return (
<div className="min-h-[calc(100vh-var(--app-shell-nav-offset))] bg-[linear-gradient(180deg,#f4f7fd_0%,#f6f8fb_32%,#f3f5f8_100%)] px-4 py-5 md:px-6">
<div className="mx-auto max-w-6xl">
<XWorkmateProfileEditor
payload={profile}
scopeKey={scopeKey}
workspaceHref="/xworkmate"
/>
</div>
</div>
);
export default function XWorkmateIntegrationsPage() {
return <XWorkmateProfileRoute mode="integrations" />;
}

View File

@ -0,0 +1,161 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import { XWorkmateLoading } from "@/app/xworkmate/XWorkmateLoading";
import { useUserStore } from "@/lib/userStore";
import {
buildSharedXWorkmateUrl,
isLegacyConsoleXWorkmateHost,
isSharedXWorkmateHost,
normalizeXWorkmateHost,
} from "@/lib/xworkmate/host";
import {
buildXWorkmateScopeKey,
type XWorkmateProfileResponse,
} from "@/lib/xworkmate/types";
import { XWorkmateProfileEditor } from "@/components/xworkmate/XWorkmateProfileEditor";
type ProfileRouteMode = "admin" | "integrations";
type XWorkmateProfileRouteProps = {
mode: ProfileRouteMode;
};
async function fetchProfile(): Promise<XWorkmateProfileResponse | null> {
const response = await fetch("/api/xworkmate/profile", {
credentials: "include",
cache: "no-store",
headers: {
Accept: "application/json",
},
});
if (response.status === 401) {
return null;
}
if (!response.ok) {
throw new Error(`xworkmate_profile_failed:${response.status}`);
}
return (await response.json()) as XWorkmateProfileResponse;
}
export function XWorkmateProfileRoute({
mode,
}: XWorkmateProfileRouteProps): React.ReactNode {
const router = useRouter();
const sessionUser = useUserStore((state) => state.user);
const [profile, setProfile] = useState<XWorkmateProfileResponse | null>(null);
const [status, setStatus] = useState<"loading" | "ready" | "redirecting">(
"loading",
);
const requestHost = useMemo(() => {
if (typeof window === "undefined") {
return "";
}
return normalizeXWorkmateHost(window.location.host);
}, []);
useEffect(() => {
let cancelled = false;
async function load() {
try {
if (isLegacyConsoleXWorkmateHost(requestHost)) {
setStatus("redirecting");
window.location.replace(
buildSharedXWorkmateUrl(
mode === "admin" ? "/xworkmate/admin" : "/xworkmate/integrations",
),
);
return;
}
const nextProfile = await fetchProfile();
if (cancelled) {
return;
}
if (!nextProfile) {
setStatus("redirecting");
router.replace("/xworkmate");
return;
}
if (mode === "admin") {
if (!isSharedXWorkmateHost(requestHost)) {
setStatus("redirecting");
router.replace("/xworkmate/integrations");
return;
}
if (
nextProfile.profileScope !== "tenant-shared" ||
!nextProfile.canEditIntegrations
) {
setStatus("redirecting");
router.replace("/xworkmate");
return;
}
} else {
if (isSharedXWorkmateHost(requestHost)) {
setStatus("redirecting");
router.replace(
nextProfile.canEditIntegrations
? "/xworkmate/admin"
: "/xworkmate",
);
return;
}
if (nextProfile.profileScope !== "user-private") {
setStatus("redirecting");
router.replace("/xworkmate");
return;
}
}
setProfile(nextProfile);
setStatus("ready");
} catch {
if (cancelled) {
return;
}
setStatus("redirecting");
router.replace("/xworkmate");
}
}
void load();
return () => {
cancelled = true;
};
}, [mode, requestHost, router]);
if (status !== "ready" || !profile) {
return <XWorkmateLoading />;
}
const scopeKey = buildXWorkmateScopeKey(
profile,
sessionUser?.id ?? sessionUser?.uuid ?? null,
requestHost,
);
return (
<div className="min-h-[calc(100vh-var(--app-shell-nav-offset))] bg-[linear-gradient(180deg,#f4f7fd_0%,#f6f8fb_32%,#f3f5f8_100%)] px-4 py-5 md:px-6">
<div className="mx-auto max-w-6xl">
<XWorkmateProfileEditor
payload={profile}
scopeKey={scopeKey}
workspaceHref="/xworkmate"
/>
</div>
</div>
);
}