Skip to content

Commit b71b7ee

Browse files
committed
fix: add request timeouts and fix stale routes causing 500s
- Add 8s timeout wrappers to homepage and layout fetch calls - Remove stale /src/app.css link from app.html - Add /nodeinfo/2.0 redirect to ap.ewancroft.uk
1 parent 02bae4b commit b71b7ee

4 files changed

Lines changed: 68 additions & 20 deletions

File tree

src/app.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<!doctype html>
22
<html lang="en">
33
<head>
4-
<link rel="stylesheet" href="/src/app.css" />
54
<link rel="preload" href="/fonts/InterVariable.woff2" as="font" type="font/woff2" crossorigin />
65
<link rel="preload" href="/fonts/InterVariable-Italic.woff2" as="font" type="font/woff2" crossorigin />
76
<meta charset="utf-8" />

src/routes/+layout.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@ import type { LayoutLoad } from './$types';
22
import { createSiteMeta, type SiteMetadata, defaultSiteMeta } from '$lib/helper/siteMeta';
33
import { fetchProfile } from '$lib/services/atproto';
44

5+
/**
6+
* Wraps a promise with a timeout. Returns null on timeout or rejection.
7+
*/
8+
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T | null> {
9+
return new Promise((resolve) => {
10+
const timer = setTimeout(() => resolve(null), ms);
11+
promise.then(
12+
(value) => {
13+
clearTimeout(timer);
14+
resolve(value);
15+
},
16+
() => {
17+
clearTimeout(timer);
18+
resolve(null);
19+
}
20+
);
21+
});
22+
}
23+
24+
const REQUEST_TIMEOUT = 8_000;
25+
526
/**
627
* Layout load function - fetches profile data and provides base site metadata
728
*/
@@ -15,7 +36,7 @@ export const load: LayoutLoad = async ({ url, fetch }) => {
1536
// Fetch profile data (needed by Header and page components)
1637
let profile = null;
1738
try {
18-
profile = await fetchProfile(fetch);
39+
profile = await withTimeout(fetchProfile(fetch), REQUEST_TIMEOUT);
1940
} catch (error) {
2041
console.error('[Layout] Failed to load profile:', error);
2142
}

src/routes/+page.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,40 @@ import {
1010
fetchRecentPopfeedReviews
1111
} from '$lib/services/atproto';
1212

13+
/**
14+
* Wraps a promise with a timeout. Returns null on timeout or rejection,
15+
* so it's safe to use with Promise.allSettled.
16+
*/
17+
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T | null> {
18+
return new Promise((resolve) => {
19+
const timer = setTimeout(() => resolve(null), ms);
20+
promise.then(
21+
(value) => {
22+
clearTimeout(timer);
23+
resolve(value);
24+
},
25+
() => {
26+
clearTimeout(timer);
27+
resolve(null);
28+
}
29+
);
30+
});
31+
}
32+
33+
/** Per-request timeout — keeps Vercel serverless functions under the 10s limit. */
34+
const REQUEST_TIMEOUT = 8_000;
35+
1336
export const load: PageLoad = async ({ fetch, parent }) => {
1437
const { profile } = await parent();
1538

1639
const [musicStatus, kibunStatus, latestPost, documents, supporters, popfeedReview] =
1740
await Promise.allSettled([
18-
fetchMusicStatus(fetch),
19-
fetchKibunStatus(fetch),
20-
fetchLatestBlueskyPost(fetch),
21-
fetchRecentDocuments(5, fetch),
22-
fetchAllSupporters(),
23-
fetchRecentPopfeedReviews(fetch)
41+
withTimeout(fetchMusicStatus(fetch), REQUEST_TIMEOUT),
42+
withTimeout(fetchKibunStatus(fetch), REQUEST_TIMEOUT),
43+
withTimeout(fetchLatestBlueskyPost(fetch), REQUEST_TIMEOUT),
44+
withTimeout(fetchRecentDocuments(5, fetch), REQUEST_TIMEOUT),
45+
withTimeout(fetchAllSupporters(), REQUEST_TIMEOUT),
46+
withTimeout(fetchRecentPopfeedReviews(fetch), REQUEST_TIMEOUT)
2447
]);
2548

2649
// Create page metadata with dynamic OG

vercel.json

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,21 @@
5757
}
5858
],
5959
"rewrites": [],
60-
"redirects": [
61-
{
62-
"source": "/.well-known/nodeinfo",
63-
"destination": "https://ap.ewancroft.uk/.well-known/nodeinfo",
64-
"permanent": false
65-
},
66-
{
67-
"source": "/.well-known/host-meta",
68-
"destination": "https://ap.ewancroft.uk/.well-known/host-meta",
69-
"permanent": false
70-
}
71-
]
60+
"redirects": [
61+
{
62+
"source": "/.well-known/nodeinfo",
63+
"destination": "https://ap.ewancroft.uk/.well-known/nodeinfo",
64+
"permanent": false
65+
},
66+
{
67+
"source": "/.well-known/host-meta",
68+
"destination": "https://ap.ewancroft.uk/.well-known/host-meta",
69+
"permanent": false
70+
},
71+
{
72+
"source": "/nodeinfo/2.0",
73+
"destination": "https://ap.ewancroft.uk/nodeinfo/2.0",
74+
"permanent": false
75+
}
76+
]
7277
}

0 commit comments

Comments
 (0)