Skip to content

Commit c7cb144

Browse files
committed
feat: enhance sitemap generation with API integration for blog posts, hackathons, and crowdfunding projects
1 parent 7dc8f24 commit c7cb144

1 file changed

Lines changed: 141 additions & 24 deletions

File tree

app/sitemap.ts

Lines changed: 141 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,174 @@
11
import { MetadataRoute } from 'next';
22
import { getBlogPosts } from '@/lib/api/blog';
3+
import { getHackathons } from '@/lib/api/hackathons';
4+
import { getCrowdfundingProjects } from '@/lib/api/project';
35
import type { BlogPost } from '@/types/blog';
6+
import type { Hackathon } from '@/types/hackathon/core';
7+
import type { Crowdfunding } from '@/types/project';
48

9+
// Constants
10+
const SITE_URL = 'https://boundlessfi.xyz';
11+
const MAX_ITEMS = 100;
12+
13+
/**
14+
* Generates the sitemap for the website
15+
* Includes static pages and dynamically fetched content
16+
*/
517
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
6-
// Fetch real blog posts from API
7-
let blogPosts: MetadataRoute.Sitemap = [];
8-
try {
9-
const response = await getBlogPosts({
10-
status: 'PUBLISHED',
11-
limit: 1000, // Fetch all published posts
12-
});
13-
blogPosts = response.data.map((post: BlogPost) => ({
14-
url: `https://boundlessfi.xyz/blog/${post.slug}`,
15-
lastModified: new Date(post.updatedAt || post.publishedAt),
16-
changeFrequency: 'monthly' as const,
17-
priority: 0.7,
18-
}));
19-
} catch {
20-
// Silently fail if fetch fails, return empty array
21-
}
18+
const [blogPosts, hackathons, crowdfundingProjects] = await Promise.all([
19+
fetchBlogPostsSitemap(),
20+
fetchHackathonsSitemap(),
21+
fetchCrowdfundingProjectsSitemap(),
22+
]);
2223

23-
const baseUrl = 'https://boundlessfi.xyz';
24-
return [
24+
const staticPages: MetadataRoute.Sitemap = [
2525
{
26-
url: baseUrl,
26+
url: SITE_URL,
2727
lastModified: new Date(),
2828
changeFrequency: 'weekly',
2929
priority: 1.0,
3030
},
3131
{
32-
url: `${baseUrl}/about`,
32+
url: `${SITE_URL}/about`,
3333
lastModified: new Date(),
3434
changeFrequency: 'monthly',
3535
priority: 0.8,
3636
},
3737
{
38-
url: `${baseUrl}/blog`,
38+
url: `${SITE_URL}/blog`,
3939
lastModified: new Date(),
4040
changeFrequency: 'weekly',
4141
priority: 0.9,
4242
},
4343
{
44-
url: `${baseUrl}/projects`,
44+
url: `${SITE_URL}/projects`,
4545
lastModified: new Date(),
4646
changeFrequency: 'weekly',
4747
priority: 0.9,
4848
},
4949
{
50-
url: `${baseUrl}/me`,
50+
url: `${SITE_URL}/hackathons`,
51+
lastModified: new Date(),
52+
changeFrequency: 'weekly',
53+
priority: 0.9,
54+
},
55+
{
56+
url: `${SITE_URL}/me`,
5157
lastModified: new Date(),
5258
changeFrequency: 'monthly',
5359
priority: 0.6,
5460
},
55-
...blogPosts,
5661
];
62+
63+
return [...staticPages, ...blogPosts, ...hackathons, ...crowdfundingProjects];
64+
}
65+
66+
/**
67+
* Fetches published blog posts and formats them for sitemap
68+
* Returns empty array if fetch fails
69+
*/
70+
async function fetchBlogPostsSitemap(): Promise<MetadataRoute.Sitemap> {
71+
try {
72+
const response = await getBlogPosts({
73+
status: 'PUBLISHED',
74+
limit: MAX_ITEMS,
75+
});
76+
77+
// Validate response
78+
if (!response?.data || !Array.isArray(response.data)) {
79+
return [];
80+
}
81+
82+
return response.data
83+
.filter((post: BlogPost) => {
84+
// Validate required fields
85+
if (!post.slug) {
86+
return false;
87+
}
88+
return true;
89+
})
90+
.map((post: BlogPost) => ({
91+
url: `${SITE_URL}/blog/${post.slug}`,
92+
lastModified: new Date(
93+
post.updatedAt || post.publishedAt || new Date()
94+
),
95+
changeFrequency: 'monthly' as const,
96+
priority: 0.7,
97+
}));
98+
} catch {
99+
// Silently fail if fetch fails, return empty array
100+
return [];
101+
}
102+
}
103+
104+
/**
105+
* Fetches published hackathons and formats them for sitemap
106+
* Returns empty array if fetch fails
107+
*/
108+
async function fetchHackathonsSitemap(): Promise<MetadataRoute.Sitemap> {
109+
try {
110+
const response = await getHackathons(1, MAX_ITEMS, {});
111+
112+
// Validate response
113+
if (
114+
!response?.data ||
115+
!response.data.hackathons ||
116+
!Array.isArray(response.data.hackathons)
117+
) {
118+
return [];
119+
}
120+
121+
return response.data.hackathons
122+
.filter((hackathon: Hackathon) => {
123+
// Validate required fields
124+
if (!hackathon.slug) {
125+
return false;
126+
}
127+
return true;
128+
})
129+
.map((hackathon: Hackathon) => ({
130+
url: `${SITE_URL}/hackathons/${hackathon.slug}`,
131+
lastModified: new Date(
132+
hackathon.updatedAt || hackathon.publishedAt || new Date()
133+
),
134+
changeFrequency: 'weekly' as const,
135+
priority: 0.8,
136+
}));
137+
} catch {
138+
// Silently fail if fetch fails, return empty array
139+
return [];
140+
}
141+
}
142+
143+
/**
144+
* Fetches crowdfunding projects and formats them for sitemap
145+
* Returns empty array if fetch fails
146+
*/
147+
async function fetchCrowdfundingProjectsSitemap(): Promise<MetadataRoute.Sitemap> {
148+
try {
149+
const response = await getCrowdfundingProjects(1, MAX_ITEMS);
150+
151+
// Validate response
152+
if (!response?.data.campaigns || !Array.isArray(response.data.campaigns)) {
153+
return [];
154+
}
155+
156+
return response.data.campaigns
157+
.filter((project: Crowdfunding) => {
158+
// Validate required fields
159+
if (!project.id) {
160+
return false;
161+
}
162+
return true;
163+
})
164+
.map((project: Crowdfunding) => ({
165+
url: `${SITE_URL}/projects/${project.id}`,
166+
lastModified: new Date(project.updatedAt || new Date()),
167+
changeFrequency: 'weekly' as const,
168+
priority: 0.8,
169+
}));
170+
} catch {
171+
// Silently fail if fetch fails, return empty array
172+
return [];
173+
}
57174
}

0 commit comments

Comments
 (0)