fix: 25s backend timeout + 30s frontend timeout on Spotify resolve
Extracts resolve logic into _resolveSpotify() and wraps it in Promise.race with a 25s timer so the backend always sends a response even if undici's AbortSignal.timeout silently fails on blocked sockets. Also reduces frontend timeout from 90s to 30s. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d38fdf601
commit
76271c1e54
2 files changed
+24
-30
No files matched your search
+23
-29
@@ -223,24 +223,15 @@ app.get('/api/health', (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/spotify/resolve', async (req, res) => {
|
async function _resolveSpotify(type, id) {
|
||||||
const { url } = req.body;
|
|
||||||
if (!url) return res.status(400).json({ error: 'url required' });
|
|
||||||
try {
|
|
||||||
const { type, id } = parseSpotifyUrl(url);
|
|
||||||
const token = await fetchSpotifyToken();
|
const token = await fetchSpotifyToken();
|
||||||
|
|
||||||
if (type === 'track') {
|
if (type === 'track') {
|
||||||
const t = await _spApi(`/tracks/${id}`, token);
|
const t = await _spApi(`/tracks/${id}`, token);
|
||||||
const artist = _spArtistName(t.artists);
|
const artist = _spArtistName(t.artists);
|
||||||
const artUrl = _spArtwork(t);
|
const artUrl = _spArtwork(t);
|
||||||
return res.json({
|
return { type: 'track', name: t.name || '', artist, artworkUrl: artUrl,
|
||||||
type: 'track',
|
tracks: [_spTrackItem(t, t.album?.name, artUrl, artist)] };
|
||||||
name: t.name || '',
|
|
||||||
artist,
|
|
||||||
artworkUrl: artUrl,
|
|
||||||
tracks: [_spTrackItem(t, t.album?.name, artUrl, artist)],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'album') {
|
if (type === 'album') {
|
||||||
@@ -248,13 +239,8 @@ app.post('/api/spotify/resolve', async (req, res) => {
|
|||||||
const artist = _spArtistName(album.artists);
|
const artist = _spArtistName(album.artists);
|
||||||
const artUrl = _spArtwork(album);
|
const artUrl = _spArtwork(album);
|
||||||
const trackItems = await _spAllAlbumTracks(id, token);
|
const trackItems = await _spAllAlbumTracks(id, token);
|
||||||
return res.json({
|
return { type: 'album', name: album.name || '', artist, artworkUrl: artUrl,
|
||||||
type: 'album',
|
tracks: trackItems.map(t => _spTrackItem(t, album.name, artUrl, artist)) };
|
||||||
name: album.name || '',
|
|
||||||
artist,
|
|
||||||
artworkUrl: artUrl,
|
|
||||||
tracks: trackItems.map(t => _spTrackItem(t, album.name, artUrl, artist)),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'playlist') {
|
if (type === 'playlist') {
|
||||||
@@ -262,18 +248,26 @@ app.post('/api/spotify/resolve', async (req, res) => {
|
|||||||
const owner = pl.owner?.display_name || '';
|
const owner = pl.owner?.display_name || '';
|
||||||
const artUrl = _spArtwork(pl);
|
const artUrl = _spArtwork(pl);
|
||||||
const { tracks: trackItems, skippedLocal } = await _spAllPlaylistTracks(id, token);
|
const { tracks: trackItems, skippedLocal } = await _spAllPlaylistTracks(id, token);
|
||||||
return res.json({
|
return { type: 'playlist', name: pl.name || '', artist: owner, artworkUrl: artUrl,
|
||||||
type: 'playlist',
|
description: pl.description || '', skippedLocal,
|
||||||
name: pl.name || '',
|
tracks: trackItems.map(t => _spTrackItem(t, null, null, null)) };
|
||||||
artist: owner,
|
|
||||||
artworkUrl: artUrl,
|
|
||||||
description: pl.description || '',
|
|
||||||
skippedLocal,
|
|
||||||
tracks: trackItems.map(t => _spTrackItem(t, null, null, null)),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(400).json({ error: `Unbekannter Typ: ${type}` });
|
throw new Error(`Unbekannter Typ: ${type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post('/api/spotify/resolve', async (req, res) => {
|
||||||
|
const { url } = req.body;
|
||||||
|
if (!url) return res.status(400).json({ error: 'url required' });
|
||||||
|
try {
|
||||||
|
const { type, id } = parseSpotifyUrl(url);
|
||||||
|
console.log(`[spotify/resolve] ${type} ${id}`);
|
||||||
|
|
||||||
|
const timeout = new Promise((_, reject) =>
|
||||||
|
setTimeout(() => reject(new Error('Zeitüberschreitung — Spotify API antwortet nicht (>25s)')), 25000)
|
||||||
|
);
|
||||||
|
const data = await Promise.race([_resolveSpotify(type, id), timeout]);
|
||||||
|
res.json(data);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error('[spotify/resolve]', e.message);
|
console.error('[spotify/resolve]', e.message);
|
||||||
if (e.message.includes('403') && e.message.includes('/playlists/')) {
|
if (e.message.includes('403') && e.message.includes('/playlists/')) {
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ export async function resolveSpotifyLink() {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ url }),
|
body: JSON.stringify({ url }),
|
||||||
signal: AbortSignal.timeout(90000),
|
signal: AbortSignal.timeout(30000),
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const err = await res.json().catch(() => ({}));
|
const err = await res.json().catch(() => ({}));
|
||||||
|
|||||||
Reference in new issue
Block a user