tangled
alpha
login
or
join now
flo-bit.dev
/
blento
your personal website on atproto - mirror
blento.app
20
fork
atom
overview
issues
pulls
pipelines
move to remote functions
Florian
1 day ago
579fb3c2
bfe09cf7
+169
-180
11 changed files
expand all
collapse all
unified
split
src
lib
cards
media
LastFMCard
LastFMProfileCard
index.ts
LastFMRecentTracksCard
index.ts
LastFMTopAlbumsCard
index.ts
LastFMTopTracksCard
index.ts
api.remote.ts
social
GitHubContributorsCard
api.remote.ts
index.ts
GitHubProfileCard
api.remote.ts
index.ts
NpmxLikesLeaderboardCard
api.remote.ts
index.ts
+6
-29
src/lib/cards/media/LastFMCard/LastFMProfileCard/index.ts
···
1
1
import type { CardDefinition } from '../../../types';
2
2
import CreateLastFMCardModal from '../CreateLastFMCardModal.svelte';
3
3
import LastFMProfileCard from './LastFMProfileCard.svelte';
4
4
+
import { fetchLastFM } from '../api.remote';
4
5
5
6
export const LastFMProfileCardDefinition = {
6
7
type: 'lastfmProfile',
···
18
19
const username = item.cardData.lastfmUsername;
19
20
if (!username) continue;
20
21
try {
21
21
-
const response = await fetch(
22
22
-
`https://blento.app/api/lastfm?method=user.getInfo&user=${encodeURIComponent(username)}`
23
23
-
);
24
24
-
if (!response.ok) continue;
25
25
-
const text = await response.text();
26
26
-
const result = JSON.parse(text);
27
27
-
allData[`lastfmProfile:${username}`] = result?.user;
22
22
+
const data = await fetchLastFM({ method: 'user.getInfo', user: username });
23
23
+
if (data) allData[`lastfmProfile:${username}`] = data?.user;
28
24
} catch (error) {
29
25
console.error('Failed to fetch Last.fm profile:', error);
30
26
}
31
27
}
32
28
return allData;
33
29
},
34
34
-
loadDataServer: async (items, { cache, env }) => {
35
35
-
const apiKey = env?.LASTFM_API_KEY;
36
36
-
if (!apiKey) return {};
30
30
+
loadDataServer: async (items) => {
37
31
const allData: Record<string, unknown> = {};
38
32
for (const item of items) {
39
33
const username = item.cardData.lastfmUsername;
40
34
if (!username) continue;
41
35
try {
42
42
-
const cacheKey = `user.getInfo:${username}:7day:50`;
43
43
-
const cached = await cache?.get('lastfm', cacheKey);
44
44
-
if (cached) {
45
45
-
allData[`lastfmProfile:${username}`] = JSON.parse(cached)?.user;
46
46
-
continue;
47
47
-
}
48
48
-
const params = new URLSearchParams({
49
49
-
method: 'user.getInfo',
50
50
-
user: username,
51
51
-
api_key: apiKey,
52
52
-
format: 'json',
53
53
-
limit: '50'
54
54
-
});
55
55
-
const response = await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`);
56
56
-
if (!response.ok) continue;
57
57
-
const data = await response.json();
58
58
-
if (data.error) continue;
59
59
-
await cache?.put('lastfm', cacheKey, JSON.stringify(data), 12 * 60 * 60);
60
60
-
allData[`lastfmProfile:${username}`] = data?.user;
36
36
+
const data = await fetchLastFM({ method: 'user.getInfo', user: username });
37
37
+
if (data) allData[`lastfmProfile:${username}`] = data?.user;
61
38
} catch (error) {
62
39
console.error('Failed to fetch Last.fm profile:', error);
63
40
}
+6
-29
src/lib/cards/media/LastFMCard/LastFMRecentTracksCard/index.ts
···
1
1
import type { CardDefinition } from '../../../types';
2
2
import CreateLastFMCardModal from '../CreateLastFMCardModal.svelte';
3
3
import LastFMRecentTracksCard from './LastFMRecentTracksCard.svelte';
4
4
+
import { fetchLastFM } from '../api.remote';
4
5
5
6
export const LastFMRecentTracksCardDefinition = {
6
7
type: 'lastfmRecentTracks',
···
18
19
const username = item.cardData.lastfmUsername;
19
20
if (!username) continue;
20
21
try {
21
21
-
const response = await fetch(
22
22
-
`https://blento.app/api/lastfm?method=user.getRecentTracks&user=${encodeURIComponent(username)}&limit=50`
23
23
-
);
24
24
-
if (!response.ok) continue;
25
25
-
const text = await response.text();
26
26
-
const result = JSON.parse(text);
27
27
-
allData[`lastfmRecentTracks:${username}`] = result?.recenttracks?.track ?? [];
22
22
+
const data = await fetchLastFM({ method: 'user.getRecentTracks', user: username });
23
23
+
if (data) allData[`lastfmRecentTracks:${username}`] = data?.recenttracks?.track ?? [];
28
24
} catch (error) {
29
25
console.error('Failed to fetch Last.fm recent tracks:', error);
30
26
}
31
27
}
32
28
return allData;
33
29
},
34
34
-
loadDataServer: async (items, { cache, env }) => {
35
35
-
const apiKey = env?.LASTFM_API_KEY;
36
36
-
if (!apiKey) return {};
30
30
+
loadDataServer: async (items) => {
37
31
const allData: Record<string, unknown> = {};
38
32
for (const item of items) {
39
33
const username = item.cardData.lastfmUsername;
40
34
if (!username) continue;
41
35
try {
42
42
-
const cacheKey = `user.getRecentTracks:${username}:7day:50`;
43
43
-
const cached = await cache?.get('lastfm', cacheKey);
44
44
-
if (cached) {
45
45
-
allData[`lastfmRecentTracks:${username}`] = JSON.parse(cached)?.recenttracks?.track ?? [];
46
46
-
continue;
47
47
-
}
48
48
-
const params = new URLSearchParams({
49
49
-
method: 'user.getRecentTracks',
50
50
-
user: username,
51
51
-
api_key: apiKey,
52
52
-
format: 'json',
53
53
-
limit: '50'
54
54
-
});
55
55
-
const response = await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`);
56
56
-
if (!response.ok) continue;
57
57
-
const data = await response.json();
58
58
-
if (data.error) continue;
59
59
-
await cache?.put('lastfm', cacheKey, JSON.stringify(data), 15 * 60);
60
60
-
allData[`lastfmRecentTracks:${username}`] = data?.recenttracks?.track ?? [];
36
36
+
const data = await fetchLastFM({ method: 'user.getRecentTracks', user: username });
37
37
+
if (data) allData[`lastfmRecentTracks:${username}`] = data?.recenttracks?.track ?? [];
61
38
} catch (error) {
62
39
console.error('Failed to fetch Last.fm recent tracks:', error);
63
40
}
+6
-31
src/lib/cards/media/LastFMCard/LastFMTopAlbumsCard/index.ts
···
2
2
import CreateLastFMCardModal from '../CreateLastFMCardModal.svelte';
3
3
import LastFMTopAlbumsCard from './LastFMTopAlbumsCard.svelte';
4
4
import LastFMTopAlbumsCardSettings from './LastFMTopAlbumsCardSettings.svelte';
5
5
+
import { fetchLastFM } from '../api.remote';
5
6
6
7
export const LastFMTopAlbumsCardDefinition = {
7
8
type: 'lastfmTopAlbums',
···
22
23
const period = item.cardData.period ?? '7day';
23
24
if (!username) continue;
24
25
try {
25
25
-
const response = await fetch(
26
26
-
`https://blento.app/api/lastfm?method=user.getTopAlbums&user=${encodeURIComponent(username)}&period=${period}&limit=50`
27
27
-
);
28
28
-
if (!response.ok) continue;
29
29
-
const text = await response.text();
30
30
-
const result = JSON.parse(text);
31
31
-
allData[`lastfmTopAlbums:${username}:${period}`] = result?.topalbums?.album ?? [];
26
26
+
const data = await fetchLastFM({ method: 'user.getTopAlbums', user: username, period });
27
27
+
if (data) allData[`lastfmTopAlbums:${username}:${period}`] = data?.topalbums?.album ?? [];
32
28
} catch (error) {
33
29
console.error('Failed to fetch Last.fm top albums:', error);
34
30
}
35
31
}
36
32
return allData;
37
33
},
38
38
-
loadDataServer: async (items, { cache, env }) => {
39
39
-
const apiKey = env?.LASTFM_API_KEY;
40
40
-
if (!apiKey) return {};
34
34
+
loadDataServer: async (items) => {
41
35
const allData: Record<string, unknown> = {};
42
36
for (const item of items) {
43
37
const username = item.cardData.lastfmUsername;
44
38
const period = item.cardData.period ?? '7day';
45
39
if (!username) continue;
46
40
try {
47
47
-
const cacheKey = `user.getTopAlbums:${username}:${period}:50`;
48
48
-
const cached = await cache?.get('lastfm', cacheKey);
49
49
-
if (cached) {
50
50
-
allData[`lastfmTopAlbums:${username}:${period}`] =
51
51
-
JSON.parse(cached)?.topalbums?.album ?? [];
52
52
-
continue;
53
53
-
}
54
54
-
const params = new URLSearchParams({
55
55
-
method: 'user.getTopAlbums',
56
56
-
user: username,
57
57
-
api_key: apiKey,
58
58
-
format: 'json',
59
59
-
limit: '50',
60
60
-
period
61
61
-
});
62
62
-
const response = await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`);
63
63
-
if (!response.ok) continue;
64
64
-
const data = await response.json();
65
65
-
if (data.error) continue;
66
66
-
await cache?.put('lastfm', cacheKey, JSON.stringify(data), 60 * 60);
67
67
-
allData[`lastfmTopAlbums:${username}:${period}`] = data?.topalbums?.album ?? [];
41
41
+
const data = await fetchLastFM({ method: 'user.getTopAlbums', user: username, period });
42
42
+
if (data) allData[`lastfmTopAlbums:${username}:${period}`] = data?.topalbums?.album ?? [];
68
43
} catch (error) {
69
44
console.error('Failed to fetch Last.fm top albums:', error);
70
45
}
+6
-31
src/lib/cards/media/LastFMCard/LastFMTopTracksCard/index.ts
···
2
2
import CreateLastFMCardModal from '../CreateLastFMCardModal.svelte';
3
3
import LastFMPeriodSettings from '../LastFMPeriodSettings.svelte';
4
4
import LastFMTopTracksCard from './LastFMTopTracksCard.svelte';
5
5
+
import { fetchLastFM } from '../api.remote';
5
6
6
7
export const LastFMTopTracksCardDefinition = {
7
8
type: 'lastfmTopTracks',
···
22
23
const period = item.cardData.period ?? '7day';
23
24
if (!username) continue;
24
25
try {
25
25
-
const response = await fetch(
26
26
-
`https://blento.app/api/lastfm?method=user.getTopTracks&user=${encodeURIComponent(username)}&period=${period}&limit=50`
27
27
-
);
28
28
-
if (!response.ok) continue;
29
29
-
const text = await response.text();
30
30
-
const result = JSON.parse(text);
31
31
-
allData[`lastfmTopTracks:${username}:${period}`] = result?.toptracks?.track ?? [];
26
26
+
const data = await fetchLastFM({ method: 'user.getTopTracks', user: username, period });
27
27
+
if (data) allData[`lastfmTopTracks:${username}:${period}`] = data?.toptracks?.track ?? [];
32
28
} catch (error) {
33
29
console.error('Failed to fetch Last.fm top tracks:', error);
34
30
}
35
31
}
36
32
return allData;
37
33
},
38
38
-
loadDataServer: async (items, { cache, env }) => {
39
39
-
const apiKey = env?.LASTFM_API_KEY;
40
40
-
if (!apiKey) return {};
34
34
+
loadDataServer: async (items) => {
41
35
const allData: Record<string, unknown> = {};
42
36
for (const item of items) {
43
37
const username = item.cardData.lastfmUsername;
44
38
const period = item.cardData.period ?? '7day';
45
39
if (!username) continue;
46
40
try {
47
47
-
const cacheKey = `user.getTopTracks:${username}:${period}:50`;
48
48
-
const cached = await cache?.get('lastfm', cacheKey);
49
49
-
if (cached) {
50
50
-
allData[`lastfmTopTracks:${username}:${period}`] =
51
51
-
JSON.parse(cached)?.toptracks?.track ?? [];
52
52
-
continue;
53
53
-
}
54
54
-
const params = new URLSearchParams({
55
55
-
method: 'user.getTopTracks',
56
56
-
user: username,
57
57
-
api_key: apiKey,
58
58
-
format: 'json',
59
59
-
limit: '50',
60
60
-
period
61
61
-
});
62
62
-
const response = await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`);
63
63
-
if (!response.ok) continue;
64
64
-
const data = await response.json();
65
65
-
if (data.error) continue;
66
66
-
await cache?.put('lastfm', cacheKey, JSON.stringify(data), 60 * 60);
67
67
-
allData[`lastfmTopTracks:${username}:${period}`] = data?.toptracks?.track ?? [];
41
41
+
const data = await fetchLastFM({ method: 'user.getTopTracks', user: username, period });
42
42
+
if (data) allData[`lastfmTopTracks:${username}:${period}`] = data?.toptracks?.track ?? [];
68
43
} catch (error) {
69
44
console.error('Failed to fetch Last.fm top tracks:', error);
70
45
}
+59
src/lib/cards/media/LastFMCard/api.remote.ts
···
1
1
+
import { query, getRequestEvent } from '$app/server';
2
2
+
import { env } from '$env/dynamic/private';
3
3
+
import { createCache } from '$lib/cache';
4
4
+
5
5
+
const LASTFM_API_URL = 'https://ws.audioscrobbler.com/2.0/';
6
6
+
7
7
+
const CACHE_TTL: Record<string, number> = {
8
8
+
'user.getRecentTracks': 15 * 60,
9
9
+
'user.getTopTracks': 60 * 60,
10
10
+
'user.getTopAlbums': 60 * 60,
11
11
+
'user.getInfo': 12 * 60 * 60
12
12
+
};
13
13
+
14
14
+
export const fetchLastFM = query(
15
15
+
'unchecked',
16
16
+
async ({
17
17
+
method,
18
18
+
user,
19
19
+
period = '7day',
20
20
+
limit = '50'
21
21
+
}: {
22
22
+
method: string;
23
23
+
user: string;
24
24
+
period?: string;
25
25
+
limit?: string;
26
26
+
}) => {
27
27
+
const apiKey = env?.LASTFM_API_KEY;
28
28
+
if (!apiKey) return undefined;
29
29
+
30
30
+
const { platform } = getRequestEvent();
31
31
+
const cache = createCache(platform);
32
32
+
33
33
+
const cacheKey = `${method}:${user}:${period}:${limit}`;
34
34
+
const cached = await cache?.get('lastfm', cacheKey);
35
35
+
if (cached) return JSON.parse(cached);
36
36
+
37
37
+
const params = new URLSearchParams({
38
38
+
method,
39
39
+
user,
40
40
+
api_key: apiKey,
41
41
+
format: 'json',
42
42
+
limit
43
43
+
});
44
44
+
45
45
+
if (method === 'user.getTopTracks' || method === 'user.getTopAlbums') {
46
46
+
params.set('period', period);
47
47
+
}
48
48
+
49
49
+
const response = await fetch(`${LASTFM_API_URL}?${params}`);
50
50
+
if (!response.ok) return undefined;
51
51
+
52
52
+
const data = await response.json();
53
53
+
if (data.error) return undefined;
54
54
+
55
55
+
const ttl = CACHE_TTL[method] || 60 * 60;
56
56
+
await cache?.put('lastfm', cacheKey, JSON.stringify(data), ttl);
57
57
+
return data;
58
58
+
}
59
59
+
);
+26
src/lib/cards/social/GitHubContributorsCard/api.remote.ts
···
1
1
+
import { query, getRequestEvent } from '$app/server';
2
2
+
import { createCache } from '$lib/cache';
3
3
+
4
4
+
const GITHUB_CONTRIBUTORS_API_URL =
5
5
+
'https://edge-function-github-contribution.vercel.app/api/github-contributors';
6
6
+
7
7
+
export const fetchGitHubContributors = query(
8
8
+
'unchecked',
9
9
+
async ({ owner, repo }: { owner: string; repo: string }) => {
10
10
+
const { platform } = getRequestEvent();
11
11
+
const cache = createCache(platform);
12
12
+
13
13
+
const key = `${owner}/${repo}`;
14
14
+
const cached = await cache?.get('gh-contrib', key);
15
15
+
if (cached) return JSON.parse(cached);
16
16
+
17
17
+
const response = await fetch(
18
18
+
`${GITHUB_CONTRIBUTORS_API_URL}?owner=${encodeURIComponent(owner)}&repo=${encodeURIComponent(repo)}`
19
19
+
);
20
20
+
if (!response.ok) return undefined;
21
21
+
22
22
+
const data = await response.json();
23
23
+
await cache?.put('gh-contrib', key, JSON.stringify(data));
24
24
+
return data;
25
25
+
}
26
26
+
);
+6
-19
src/lib/cards/social/GitHubContributorsCard/index.ts
···
2
2
import GitHubContributorsCard from './GitHubContributorsCard.svelte';
3
3
import CreateGitHubContributorsCardModal from './CreateGitHubContributorsCardModal.svelte';
4
4
import GitHubContributorsCardSettings from './GitHubContributorsCardSettings.svelte';
5
5
+
import { fetchGitHubContributors } from './api.remote';
5
6
6
7
export type GitHubContributor = {
7
8
username: string;
···
33
34
const key = `${owner}/${repo}`;
34
35
if (contributorsData[key]) continue;
35
36
try {
36
36
-
const response = await fetch(
37
37
-
`https://blento.app/api/github/contributors?owner=${encodeURIComponent(owner)}&repo=${encodeURIComponent(repo)}`
38
38
-
);
39
39
-
if (response.ok) {
40
40
-
contributorsData[key] = await response.json();
41
41
-
}
37
37
+
const data = await fetchGitHubContributors({ owner, repo });
38
38
+
if (data) contributorsData[key] = data;
42
39
} catch (error) {
43
40
console.error('Failed to fetch GitHub contributors:', error);
44
41
}
45
42
}
46
43
return contributorsData;
47
44
},
48
48
-
loadDataServer: async (items, { cache }) => {
45
45
+
loadDataServer: async (items) => {
49
46
const contributorsData: GitHubContributorsLoadedData = {};
50
47
for (const item of items) {
51
48
const { owner, repo } = item.cardData;
···
53
50
const key = `${owner}/${repo}`;
54
51
if (contributorsData[key]) continue;
55
52
try {
56
56
-
const cached = await cache?.get('gh-contrib', key);
57
57
-
if (cached) {
58
58
-
contributorsData[key] = JSON.parse(cached);
59
59
-
continue;
60
60
-
}
61
61
-
const response = await fetch(
62
62
-
`https://edge-function-github-contribution.vercel.app/api/github-contributors?owner=${encodeURIComponent(owner)}&repo=${encodeURIComponent(repo)}`
63
63
-
);
64
64
-
if (!response.ok) continue;
65
65
-
const data = await response.json();
66
66
-
await cache?.put('gh-contrib', key, JSON.stringify(data));
67
67
-
contributorsData[key] = data;
53
53
+
const data = await fetchGitHubContributors({ owner, repo });
54
54
+
if (data) contributorsData[key] = data;
68
55
} catch (error) {
69
56
console.error('Failed to fetch GitHub contributors:', error);
70
57
}
+21
src/lib/cards/social/GitHubProfileCard/api.remote.ts
···
1
1
+
import { query, getRequestEvent } from '$app/server';
2
2
+
import { createCache } from '$lib/cache';
3
3
+
4
4
+
const GITHUB_API_URL = 'https://edge-function-github-contribution.vercel.app/api/github-data?user=';
5
5
+
6
6
+
export const fetchGitHubContributions = query('unchecked', async (user: string) => {
7
7
+
const { platform } = getRequestEvent();
8
8
+
const cache = createCache(platform);
9
9
+
10
10
+
const cached = await cache?.get('github', user);
11
11
+
if (cached) return JSON.parse(cached);
12
12
+
13
13
+
const response = await fetch(GITHUB_API_URL + encodeURIComponent(user));
14
14
+
if (!response.ok) return undefined;
15
15
+
16
16
+
const data = await response.json();
17
17
+
if (!data?.user) return undefined;
18
18
+
19
19
+
await cache?.put('github', user, JSON.stringify(data.user));
20
20
+
return data.user;
21
21
+
});
+9
-23
src/lib/cards/social/GitHubProfileCard/index.ts
···
1
1
import type { CardDefinition } from '../../types';
2
2
import CreateGitHubProfileCardModal from './CreateGitHubProfileCardModal.svelte';
3
3
-
import type GithubContributionsGraph from './GithubContributionsGraph.svelte';
4
3
import GitHubProfileCard from './GitHubProfileCard.svelte';
5
4
import type { GitHubContributionsData } from './types';
5
5
+
import { fetchGitHubContributions } from './api.remote';
6
6
7
7
export type GithubProfileLoadedData = Record<string, GitHubContributionsData | undefined>;
8
8
···
12
12
creationModalComponent: CreateGitHubProfileCardModal,
13
13
14
14
loadData: async (items) => {
15
15
-
const githubData: Record<string, GithubContributionsGraph> = {};
15
15
+
const githubData: Record<string, GitHubContributionsData> = {};
16
16
for (const item of items) {
17
17
+
const user = item.cardData.user;
18
18
+
if (!user) continue;
17
19
try {
18
18
-
const response = await fetch(
19
19
-
`https://blento.app/api/github?user=${encodeURIComponent(item.cardData.user)}`
20
20
-
);
21
21
-
if (response.ok) {
22
22
-
githubData[item.cardData.user] = await response.json();
23
23
-
}
20
20
+
const data = await fetchGitHubContributions(user);
21
21
+
if (data) githubData[user] = data;
24
22
} catch (error) {
25
23
console.error('Failed to fetch GitHub contributions:', error);
26
24
}
27
25
}
28
26
return githubData;
29
27
},
30
30
-
loadDataServer: async (items, { cache }) => {
28
28
+
loadDataServer: async (items) => {
31
29
const githubData: Record<string, GitHubContributionsData> = {};
32
30
for (const item of items) {
33
31
const user = item.cardData.user;
34
32
if (!user) continue;
35
33
try {
36
36
-
const cached = await cache?.get('github', user);
37
37
-
if (cached) {
38
38
-
githubData[user] = JSON.parse(cached);
39
39
-
continue;
40
40
-
}
41
41
-
const response = await fetch(
42
42
-
`https://edge-function-github-contribution.vercel.app/api/github-data?user=${encodeURIComponent(user)}`
43
43
-
);
44
44
-
if (!response.ok) continue;
45
45
-
const data = await response.json();
46
46
-
if (!data?.user) continue;
47
47
-
const result = data.user as GitHubContributionsData;
48
48
-
await cache?.put('github', user, JSON.stringify(result));
49
49
-
githubData[user] = result;
34
34
+
const data = await fetchGitHubContributions(user);
35
35
+
if (data) githubData[user] = data;
50
36
} catch (error) {
51
37
console.error('Failed to fetch GitHub contributions:', error);
52
38
}
+20
src/lib/cards/social/NpmxLikesLeaderboardCard/api.remote.ts
···
1
1
+
import { query, getRequestEvent } from '$app/server';
2
2
+
import { createCache } from '$lib/cache';
3
3
+
4
4
+
const LEADERBOARD_API_URL =
5
5
+
'https://npmx-likes-leaderboard-api-production.up.railway.app/api/leaderboard/likes?limit=20';
6
6
+
7
7
+
export const fetchNpmxLeaderboard = query(async () => {
8
8
+
const { platform } = getRequestEvent();
9
9
+
const cache = createCache(platform);
10
10
+
11
11
+
const cached = await cache?.get('npmx', 'likes');
12
12
+
if (cached) return JSON.parse(cached);
13
13
+
14
14
+
const response = await fetch(LEADERBOARD_API_URL);
15
15
+
if (!response.ok) return undefined;
16
16
+
17
17
+
const data = await response.json();
18
18
+
await cache?.put('npmx', 'likes', JSON.stringify(data));
19
19
+
return data;
20
20
+
});
+4
-18
src/lib/cards/social/NpmxLikesLeaderboardCard/index.ts
···
1
1
import type { CardDefinition } from '../../types';
2
2
import NpmxLikesLeaderboardCard from './NpmxLikesLeaderboardCard.svelte';
3
3
+
import { fetchNpmxLeaderboard } from './api.remote';
3
4
4
5
export const NpmxLikesLeaderboardCardDefinition = {
5
6
type: 'npmxLikesLeaderboard',
···
11
12
card.mobileH = 6;
12
13
},
13
14
loadData: async () => {
14
14
-
const res = await fetch('https://blento.app/api/npmx-leaderboard');
15
15
-
const data = await res.json();
16
16
-
return data;
15
15
+
return await fetchNpmxLeaderboard();
17
16
},
18
18
-
loadDataServer: async (_items, { cache }) => {
19
19
-
try {
20
20
-
const cached = await cache?.get('npmx', 'likes');
21
21
-
if (cached) return JSON.parse(cached);
22
22
-
const response = await fetch(
23
23
-
'https://npmx-likes-leaderboard-api-production.up.railway.app/api/leaderboard/likes?limit=20'
24
24
-
);
25
25
-
if (!response.ok) return undefined;
26
26
-
const data = await response.json();
27
27
-
await cache?.put('npmx', 'likes', JSON.stringify(data));
28
28
-
return data;
29
29
-
} catch (error) {
30
30
-
console.error('Error fetching npmx leaderboard:', error);
31
31
-
return undefined;
32
32
-
}
17
17
+
loadDataServer: async () => {
18
18
+
return await fetchNpmxLeaderboard();
33
19
},
34
20
minW: 3,
35
21
canHaveLabel: true,