aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-26 10:18:16 -0700
committerAdam Soltys <[email protected]>2026-10-26 10:18:16 -0700
commit5360027b5f44b75afd14741f8bb54738a1fc2be5 (patch)
tree45964a3686c9b1da28ae812a6893e99d9919e677
parent5fe2341616b2d1abdd7a3560371aff2d648e2311 (diff)
downloadcoinosv3-5360027b5f44b75afd14741f8bb54738a1fc2be5.tar.gz
coinosv3-5360027b5f44b75afd14741f8bb54738a1fc2be5.zip
An upload that fails says why: the host and its answer (or the signer's error) in the toast, not just "check your connection", which hid hosts refusing the file and a signer that never answered
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
-rw-r--r--src/app.js6
-rw-r--r--src/media-upload.js10
2 files changed, 11 insertions, 5 deletions
diff --git a/src/app.js b/src/app.js
index 6d6933f..9789401 100644
--- a/src/app.js
+++ b/src/app.js
@@ -3939,7 +3939,11 @@ async function publicMediaUpload(file) {
if (error instanceof MediaUploadError && error.code === 'unauthorized') throw new Error(t('mediaUploadUnauthorized'));
if (error instanceof MediaUploadError && error.code === 'too-big') throw new Error(t('mediaUploadTooBig'));
if (error instanceof MediaUploadError && error.code === 'unsupported') throw new Error(t('mediaUploadUnsupported'));
- throw new Error(t('msgUploadFailed'));
+ // what went wrong, said: "check your connection" alone hid a signer
+ // that never answered and hosts that refused the file
+ const why = String((error instanceof MediaUploadError ? error.details : '') || error?.message || '').slice(0, 200);
+ console.warn('media upload failed', error, why);
+ throw new Error(why && why !== 'failed' ? `${t('msgUploadFailed')} (${why})` : t('msgUploadFailed'));
} finally {
ui.mediaProgress = null;
}
diff --git a/src/media-upload.js b/src/media-upload.js
index f851943..7f020cf 100644
--- a/src/media-upload.js
+++ b/src/media-upload.js
@@ -73,6 +73,8 @@ function putWithProgress(url, { body, headers, signal }, onProgress) {
});
}
+const hostOf = (server) => { try { return new URL(server).hostname.replace(/^blossom\./, ''); } catch { return server; } };
+
// BUD-02 upload. `signEvent` is deliberately injected: the app can use its
// signed-in Nostr identity, while tests need no secret key.
export async function uploadPublicMedia(file, signEvent, {
@@ -97,7 +99,7 @@ export async function uploadPublicMedia(file, signEvent, {
// upload timeout. Any HTTP answer, even an error, means it's there.
if (i < servers.length - 1) {
try { await fetcher(server.replace(/\/$/, '') + '/upload', { method: 'HEAD', signal: AbortSignal.timeout(5000) }); }
- catch (error) { failures.push({ status: 0, reason: 'unreachable' }); continue; }
+ catch (error) { failures.push({ host: hostOf(server), status: 0, reason: 'unreachable' }); continue; }
}
try {
const init = {
@@ -116,12 +118,12 @@ export async function uploadPublicMedia(file, signEvent, {
try { body = await response.json(); } catch {}
const url = response.ok && responseUrl(body);
if (url) return mediaUrl(url, server, file);
- failures.push({ status: response.status, reason: response.headers.get('x-reason') || body?.message || '' });
+ failures.push({ host: hostOf(server), status: response.status, reason: response.headers.get('x-reason') || body?.message || '' });
} catch (error) {
- failures.push({ status: 0, reason: error?.message || '' });
+ failures.push({ host: hostOf(server), status: 0, reason: error?.message || '' });
}
}
- const details = failures.map((x) => [x.status, x.reason].filter(Boolean).join(' ')).filter(Boolean).join('; ');
+ const details = failures.map((x) => x.host + ': ' + ([x.status, x.reason].filter(Boolean).join(' ') || '?')).join('; ');
if (failures.length && failures.every((x) => x.status === 401 || x.status === 403)) throw new MediaUploadError('unauthorized', details);
if (failures.length && failures.every((x) => x.status === 413)) throw new MediaUploadError('too-big', details);
if (failures.length && failures.every((x) => x.status === 415)) throw new MediaUploadError('unsupported', details);