Fix CI build SSG timeouts

- Add backend request timeout to prevent hanging builds

- Make getStaticProps resilient to API failures

- Avoid build-time crawling for dynamic routes (blocking fallback)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
einstein
2026-05-26 18:47:04 +03:00
parent 1df9b22fa3
commit 6af5d7fa1f
8 changed files with 105 additions and 101 deletions
+3
View File
@@ -1,8 +1,11 @@
import axios, { AxiosInstance, AxiosRequestConfig } from "axios"; import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { getAccessTokenCookie } from "@utils/auth"; import { getAccessTokenCookie } from "@utils/auth";
const API_TIMEOUT_MS = 10000;
const axiosInstance: AxiosInstance = axios.create({ const axiosInstance: AxiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL, baseURL: process.env.NEXT_PUBLIC_API_URL,
timeout: API_TIMEOUT_MS,
}); });
export enum APIPath { export enum APIPath {
+26 -29
View File
@@ -14,14 +14,13 @@ interface InitialProps {
const EventPage: NextPage<InitialProps> = ({ event }) => { const EventPage: NextPage<InitialProps> = ({ event }) => {
const router = useRouter(); const router = useRouter();
const { id } = router.query;
if (router.isFallback) return <LoadingView />; if (router.isFallback) return <LoadingView />;
return ( return (
<> <>
<Head> <Head>
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/events/${id}`} /> <link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/events/${event.id}`} />
</Head> </Head>
<PageWrapper> <PageWrapper>
<EventPageView event={event} /> <EventPageView event={event} />
@@ -30,36 +29,34 @@ const EventPage: NextPage<InitialProps> = ({ event }) => {
); );
}; };
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => ({
const allEvents = await EventApi.getEvents(); paths: [],
const paths = allEvents.map((e: Event) => ({ fallback: "blocking",
params: { });
id: String(e.id),
},
}
));
return {
paths,
fallback: true,
};
};
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => { export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
const { id } = params; const id = Number(params?.id);
let notFound = false; if (!id) {
let event: Event; return {
try { notFound: true,
event = await EventApi.getEvent(Number(id)); revalidate: 10,
} catch (err) { };
notFound = true; }
try {
const event = await EventApi.getEvent(id);
return {
props: {
event,
},
revalidate: 10, // Required for deleting hidden pages
};
} catch {
return {
notFound: true,
revalidate: 10,
};
} }
return {
props: {
event,
},
revalidate: 10, // Required for deleting hidden pages
notFound,
};
}; };
export default EventPage; export default EventPage;
+25 -29
View File
@@ -14,14 +14,13 @@ interface InitialProps {
const FeedPage: NextPage<InitialProps> = ({ post }) => { const FeedPage: NextPage<InitialProps> = ({ post }) => {
const router = useRouter(); const router = useRouter();
const { id } = router.query;
if (router.isFallback) return <LoadingView />; if (router.isFallback) return <LoadingView />;
return ( return (
<> <>
<Head> <Head>
<link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/feed/${id}`} /> <link rel="canonical" href={`${process.env.NEXT_PUBLIC_SITE_URL}/feed/${post.id}`} />
</Head> </Head>
<PageWrapper> <PageWrapper>
<FeedPageView post={post} /> <FeedPageView post={post} />
@@ -30,37 +29,34 @@ const FeedPage: NextPage<InitialProps> = ({ post }) => {
); );
}; };
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => ({
const feed = await FeedApi.getFeed(); paths: [],
const paths = feed.map((post: Post) => ({ fallback: "blocking",
params: { });
id: String(post.id),
},
}
));
return {
paths,
fallback: true,
};
};
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => { export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
const { id } = params; const id = Number(params?.id);
let notFound = false; if (!id) {
let post: Post; return {
try { notFound: true,
post = await FeedApi.getPost(Number(id)); revalidate: 10,
} catch (err) { };
notFound = true;
} }
return { try {
props: { const post = await FeedApi.getPost(id);
post, return {
}, props: {
revalidate: 10, // Required for deleting hidden pages post,
notFound, },
}; revalidate: 10, // Required for deleting hidden pages
};
} catch {
return {
notFound: true,
revalidate: 10,
};
}
}; };
export default FeedPage; export default FeedPage;
+7 -4
View File
@@ -44,12 +44,15 @@ const InEnglishPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) =
}; };
export const getStaticProps: GetStaticProps<InitialProps> = async () => { export const getStaticProps: GetStaticProps<InitialProps> = async () => {
const initialEvents = await fetcher<Event[]>(eventApi); const [eventsResult, feedResult] = await Promise.allSettled([
const initialFeed = await fetcher<Post[]>(feedApi); fetcher<Event[]>(eventApi),
fetcher<Post[]>(feedApi),
]);
return { return {
props: { props: {
initialEvents, initialEvents: eventsResult.status === "fulfilled" ? eventsResult.value : [],
initialFeed, initialFeed: feedResult.status === "fulfilled" ? feedResult.value : [],
}, },
revalidate: 10, revalidate: 10,
}; };
+7 -4
View File
@@ -43,12 +43,15 @@ const FrontPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
}; };
export const getStaticProps: GetStaticProps<InitialProps> = async () => { export const getStaticProps: GetStaticProps<InitialProps> = async () => {
const initialEvents = fetcher<Event[]>(eventApi); const [eventsResult, feedResult] = await Promise.allSettled([
const initialFeed = fetcher<Post[]>(feedApi); fetcher<Event[]>(eventApi),
fetcher<Post[]>(feedApi),
]);
return { return {
props: { props: {
initialEvents: await initialEvents, initialEvents: eventsResult.status === "fulfilled" ? eventsResult.value : [],
initialFeed: await initialFeed, initialFeed: feedResult.status === "fulfilled" ? feedResult.value : [],
}, },
revalidate: 10, revalidate: 10,
}; };
+7 -6
View File
@@ -3,9 +3,7 @@ import { NextPage, GetStaticProps } from "next";
import Head from "next/head"; import Head from "next/head";
import useSWR from "swr"; import useSWR from "swr";
import Event from "@models/Event"; import Event from "@models/Event";
import EventApi from "@api/eventApi";
import Post from "@models/Feed"; import Post from "@models/Feed";
import FeedApi from "@api/feedApi";
import ActualPageView from "@views/ActualPage/ActualPageView"; import ActualPageView from "@views/ActualPage/ActualPageView";
import PageWrapper from "@views/common/PageWrapper"; import PageWrapper from "@views/common/PageWrapper";
import { fetcher, APIPath, API } from "@api/backend"; import { fetcher, APIPath, API } from "@api/backend";
@@ -40,12 +38,15 @@ const ActualPage: NextPage<InitialProps> = ({ initialEvents, initialFeed }) => {
}; };
export const getStaticProps: GetStaticProps<InitialProps> = async () => { export const getStaticProps: GetStaticProps<InitialProps> = async () => {
const initialEvents = await EventApi.getEvents(); const [eventsResult, feedResult] = await Promise.allSettled([
const initialFeed = await FeedApi.getFeed(); fetcher<Event[]>(eventApi),
fetcher<Post[]>(feedApi),
]);
return { return {
props: { props: {
initialEvents, initialEvents: eventsResult.status === "fulfilled" ? eventsResult.value : [],
initialFeed, initialFeed: feedResult.status === "fulfilled" ? feedResult.value : [],
}, },
revalidate: 10, revalidate: 10,
}; };
+25 -27
View File
@@ -112,36 +112,34 @@ const SignUpPage: NextPage<InitialProps> = ({ initialForm }) => {
); );
}; };
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => ({
const allForms = await SignupApi.getForms(); paths: [],
const paths = allForms.map((e: SignupForm) => ({ fallback: "blocking",
params: { });
id: String(e.id),
},
}
));
return {
paths,
fallback: true,
};
};
export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => { export const getStaticProps: GetStaticProps<InitialProps> = async ({ params }) => {
const { id } = params; const id = Number(params?.id);
let notFound = false; if (!id) {
let initialForm: SignupForm; return {
try { notFound: true,
initialForm = await SignupApi.getForm(Number(id)); revalidate: 10,
} catch { };
notFound = true; }
try {
const initialForm = await SignupApi.getForm(id);
return {
props: {
initialForm,
},
revalidate: 10, // Required for deleting hidden pages
};
} catch {
return {
notFound: true,
revalidate: 10,
};
} }
return {
props: {
initialForm,
},
revalidate: 10, // Required for deleting hidden pages
notFound,
};
}; };
export default SignUpPage; export default SignUpPage;
+5 -2
View File
@@ -30,10 +30,13 @@ const CorporatePage: NextPage<InitialProps> = ({ initialJobAds }) => {
}; };
export const getStaticProps: GetStaticProps<InitialProps> = async () => { export const getStaticProps: GetStaticProps<InitialProps> = async () => {
const initialJobAds = await fetcher<JobAd[]>(jobAdApi); const jobAdsResult = await Promise.allSettled([
fetcher<JobAd[]>(jobAdApi),
]);
return { return {
props: { props: {
initialJobAds, initialJobAds: jobAdsResult[0].status === "fulfilled" ? jobAdsResult[0].value : [],
}, },
revalidate: 10, revalidate: 10,
}; };