Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions client/src/CatchallApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { Route, Routes } from "react-router";
import LazyAdminPage from "./features/admin/LazyAdminPage";
import LegacyDatasetAddToProject from "./features/legacy/LegacyDatasetAddToProject";
import LegacyDatasets from "./features/legacy/LegacyDatasets";
import LegacyProjectView from "./features/legacy/LegacyProjectView";
import LegacyRoot from "./features/legacy/LegacyRoot";
import LegacyShowDataset from "./features/legacy/LegacyShowDataset";
import LazyRootV2 from "./features/rootV2/LazyRootV2";
Expand All @@ -39,7 +38,6 @@ export default function CatchallApp() {
const { data: user } = useGetUserQueryState();
return (
<Routes>
<Route path="/projects/*" element={<LegacyProjectView />} />
<Route
path="/datasets/:identifier/add"
element={<LegacyDatasetAddToProject />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,25 @@ import { useEffect, useMemo } from "react";
import { useLocation, useNavigate } from "react-router";

import ContainerWrap from "~/components/container/ContainerWrap";
import { Loader } from "~/components/Loader";
import { useGetPlatformRedirectsBySourceUrlQuery } from "~/features/platform/api/platform.api";
import { locationPathnameToSourceUrl } from "~/features/platform/api/platform.utils";
import NoLegacySupportForProjects from "./NoLegacySupportForProjects";

function CheckingForRedirect() {
return (
<ContainerWrap>
<div className={cx("d-flex")}>
<div className={cx("m-auto", "d-flex", "flex-column")}>
<h3
data-cy="not-found-title"
className={cx(
"fw-bold",
"mt-0",
"mb-3",
"d-flex",
"align-items-center",
"gap-3",
"text-primary"
)}
>
Checking for redirect...
</h3>
</div>
</div>
</ContainerWrap>
);
interface ClientSideCheckForRedirectsProps {
projectSlug: string;
}

export default function CheckForRedirect() {
export default function ClientSideCheckForRedirects({
projectSlug,
}: ClientSideCheckForRedirectsProps) {
const location = useLocation();
const searchParams = useMemo(
() => new URLSearchParams(location.search),
[location.search]
);
const sourceUrl = locationPathnameToSourceUrl(location.pathname);

const sourceUrl = locationPathnameToSourceUrl(projectSlug);
const { data: redirectPlan, isFetching: isFetchingRedirects } =
useGetPlatformRedirectsBySourceUrlQuery(
sourceUrl ? { sourceUrl } : skipToken
Expand All @@ -69,7 +52,11 @@ export default function CheckForRedirect() {
navigate(
{
pathname: redirectPlan.target_url,
search: autostart ? `?autostartRedirect=true` : undefined,
search: autostart
? new URLSearchParams({
autostartRedirect: "true",
}).toString()
: undefined,
},
{
replace: true,
Expand All @@ -80,6 +67,23 @@ export default function CheckForRedirect() {
if (isFetchingRedirects || redirectPlan?.target_url != null) {
return <CheckingForRedirect />;
}

return <NoLegacySupportForProjects />;
}

function CheckingForRedirect() {
return (
<ContainerWrap>
<div className={cx("d-flex")}>
<div className={cx("m-auto", "d-flex", "flex-column")}>
<h3
data-cy="not-found-title"
className={cx("fw-bold", "mt-0", "mb-3", "text-primary")}
>
<Loader className={cx("bi", "me-2")} inline size={20} />
Checking for redirect...
</h3>
</div>
</div>
</ContainerWrap>
);
}
23 changes: 0 additions & 23 deletions client/src/features/legacy/LegacyProjectView.tsx

This file was deleted.

8 changes: 7 additions & 1 deletion client/src/features/platform/api/platform-empty.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

import { API_BASE_URL } from "~/utils/api/api.constants";
import { prepareHeaders } from "~/utils/api/api.utils";

// initialize an empty api service that we'll inject endpoints into later as needed
export const platformEmptyApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: "/api/data" }),
baseQuery: fetchBaseQuery({
baseUrl: API_BASE_URL,
prepareHeaders,
}),
endpoints: () => ({}),
reducerPath: "platformApi",
});
2 changes: 2 additions & 0 deletions client/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export default [
// Not found page for /u/*
route("*", "routes/users/catchall.tsx"),
]),
// Legacy projects (may redirect)
route(RELATIVE_ROUTES.projects.splat, "routes/legacy/projects.tsx"),
// * matches all URLs, the ? makes it optional so it will match / as well
route("*?", "routes/catchall.tsx"),
] satisfies RouteConfig;
101 changes: 101 additions & 0 deletions client/src/routes/legacy/projects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { data, redirect } from "react-router";

import ClientSideCheckForRedirects from "~/features/legacy/ClientSideCheckForRedirects";
import NoLegacySupportForProjects from "~/features/legacy/NoLegacySupportForProjects";
import { platformApi } from "~/features/platform/api/platform.api";
import { locationPathnameToSourceUrl } from "~/features/platform/api/platform.utils";
import { storeContext } from "~/store/store.utils.server";
import type { Route } from "./+types/projects";

export async function loader({ context, params, request }: Route.LoaderArgs) {
const store = context.get(storeContext);
const clientSideFetch = store == null || process.env.CYPRESS === "1";
if (clientSideFetch) {
//? In testing, we load the redirects data client-side
return data({
clientSideFetch,
redirectPlan: undefined,
error: undefined,
});
}

//? Otherwise, we load the redirects data to send a redirect if there is one
const splat = params["*"];
const sourceUrl = locationPathnameToSourceUrl(splat);
if (!sourceUrl) {
// The is no project slug to try to redirect to
return data({ clientSideFetch, redirectPlan: undefined, error: undefined });
}

const url = new URL(request.url);
const autostart = !!url.searchParams.get("autostart");

const endpoint = platformApi.endpoints.getPlatformRedirectsBySourceUrl;
const apiArgs = { sourceUrl };
store.dispatch(endpoint.initiate(apiArgs));
await Promise.all(store.dispatch(platformApi.util.getRunningQueriesThunk()));
const selector = endpoint.select(apiArgs);
const { data: redirectPlan, error } = selector(store.getState());
store.dispatch(platformApi.util.resetApiState());
if (error && "status" in error && typeof error.status === "number") {
if (error.status == 404) {
// Ignore 404s: there is no redirect
return data({
clientSideFetch,
redirectPlan: undefined,
error: undefined,
});
}
return data({ clientSideFetch, redirectPlan, error }, error.status);
}

// Send redirect response if we found a match
if (redirectPlan?.target_url != null) {
const redirectUrl = makeRedirectUrl(
redirectPlan.target_url,
autostart,
request.url
);
throw redirect(redirectUrl, 301);
}

return data({ clientSideFetch, redirectPlan, error });
}

//? NOTE: we do not provide a client-side loader since there is no
//? navigation link to legacy pages in the UI.

export default function LegacyProjectPage({
loaderData,
params,
}: Route.ComponentProps) {
if (loaderData.clientSideFetch) {
return <ClientSideCheckForRedirects projectSlug={params["*"]} />;
}
return <NoLegacySupportForProjects />;
}

function makeRedirectUrl(
targetUrl: string,
autostart: boolean,
requestUrl: string
) {
// Local redirect to a Renku v2 project
if (targetUrl.startsWith("/") && autostart) {
const search = new URLSearchParams({
autostartRedirect: "true",
}).toString();
return `${targetUrl}?${search}`;
}
if (targetUrl.startsWith("/")) {
return targetUrl;
}
// Redirect to an external URL
const redirectUrl = new URL(targetUrl, requestUrl);
if (autostart) {
redirectUrl.search = new URLSearchParams({
autostartRedirect: "true",
}).toString();
}
return redirectUrl.toString();
}
13 changes: 8 additions & 5 deletions client/src/routing/routes.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@

export const ABSOLUTE_ROUTES = {
root: "/",
/** @deprecated Renku Legacy route */
datasets: {
root: "/datasets",
splat: "/datasets/*",
},
projects: {
root: "/projects",
splat: "/projects/*",
},
/** @deprecated Renku Legacy route */
projects: { splat: "/projects/*" },
/** @deprecated Renku Legacy routes */
v1: { splat: "/v1/*" },
v2: {
index: "/",
Expand Down Expand Up @@ -74,8 +74,11 @@ export const ABSOLUTE_ROUTES = {

export const RELATIVE_ROUTES = {
root: "/",
/** @deprecated Renku Legacy route */
datasets: "/datasets",
projects: "/projects",
/** @deprecated Renku Legacy route */
projects: { splat: "projects/*" },
/** @deprecated Renku Legacy routes */
v1: { splat: "v1/*" },
v2: {
index: "/",
Expand Down
7 changes: 6 additions & 1 deletion client/src/store/store.utils.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { configureStore } from "@reduxjs/toolkit";
import { parseCookie } from "cookie";
import { createContext, type MiddlewareFunction } from "react-router";

import { platformApi } from "~/features/platform/api/platform.api";
import { projectV2Api } from "~/features/projectsV2/api/projectV2.enhanced-api";
import { usersApi } from "~/features/usersV2/api/users.api";
import cookieSlice from "./cookie.slice.server";
Expand All @@ -36,11 +37,15 @@ function makeStore() {
// Slices
[cookieSlice.reducerPath]: cookieSlice.reducer,
// APIs
[platformApi.reducerPath]: platformApi.reducer,
[projectV2Api.reducerPath]: projectV2Api.reducer,
[usersApi.reducerPath]: usersApi.reducer,
},
middleware: (gDM) =>
gDM().concat(projectV2Api.middleware).concat(usersApi.middleware),
gDM()
.concat(platformApi.middleware)
.concat(projectV2Api.middleware)
.concat(usersApi.middleware),
});
}

Expand Down
Loading