release: candidate downloads go through Cloudflare first (the direct route to the origin was throttled per connection: one AppImage took 20+ min there, 3 MB/s through Cloudflare), the origin only as a fallback; a heartbeat logs the bytes every 30 s
This commit is contained in:
parent
37cda2a39a
commit
9ba97b7e29
1 changed files with 20 additions and 3 deletions
|
|
@ -162,15 +162,32 @@ export async function api<T>(method: string, path: string, body?: unknown, raw =
|
|||
}
|
||||
}
|
||||
|
||||
/** Download one asset (drafts need the token) to a file; through the origin like uploads when it is set. */
|
||||
/** Download one asset (drafts need the token) to a file. Through Cloudflare first: its 100 MB limit is on request
|
||||
* bodies (uploads), and the direct route to the origin was throttled per connection to ~30 KB/s (an 0.8.11 release
|
||||
* sat 20 minutes on one AppImage while Cloudflare gave 3 MB/s). The origin is the fallback. A heartbeat logs the
|
||||
* bytes every 30 s; curl's own --max-time bounds each try. */
|
||||
export async function downloadAsset(url: string, dest: string): Promise<void> {
|
||||
const ip = origin();
|
||||
const dir = mkdtempSync(join(tmpdir(), 'forgejo-dl-'));
|
||||
try {
|
||||
writeFileSync(join(dir, 'h'), `Authorization: ${auth()}\n`, { mode: 0o600 });
|
||||
const host = new URL(BASE).host;
|
||||
const r = spawnSync('curl', ['-sS', '-L', '--fail', '--max-time', '1800', ...(ip ? ['--resolve', `${host}:443:${ip.includes(':') ? `[${ip}]` : ip}`] : []), '-H', `@${join(dir, 'h')}`, '-o', dest, url], { encoding: 'utf8' });
|
||||
if (r.status !== 0) fail(`download ${basename(dest)}: curl exit ${r.status}: ${r.stderr.slice(0, 300)}`);
|
||||
const routes: string[][] = [[], ...(ip ? [['--resolve', `${host}:443:${ip.includes(':') ? `[${ip}]` : ip}`]] : [])];
|
||||
let last = '';
|
||||
for (const [i, route] of routes.entries()) {
|
||||
const t0 = Date.now();
|
||||
const p = Bun.spawn(['curl', '-sS', '-L', '--fail', '--max-time', '1800', ...route, '-H', `@${join(dir, 'h')}`, '-o', dest, url], { stdout: 'ignore', stderr: 'pipe' });
|
||||
const beat = setInterval(() => {
|
||||
let n = 0; try { n = statSync(dest).size; } catch { /* not started */ }
|
||||
console.log(` download ${basename(dest)} (${i ? 'origin' : 'cloudflare'}): ${(n / 1e6).toFixed(1)} MB after ${Math.round((Date.now() - t0) / 1000)} s`);
|
||||
}, 30_000);
|
||||
const code = await p.exited;
|
||||
clearInterval(beat);
|
||||
if (code === 0) return;
|
||||
last = `curl exit ${code}: ${(await new Response(p.stderr).text()).slice(0, 300)}`;
|
||||
if (i + 1 < routes.length) console.log(` download ${basename(dest)}: ${last}; trying the origin`);
|
||||
}
|
||||
fail(`download ${basename(dest)}: ${last}`);
|
||||
} finally { rmSync(dir, { recursive: true, force: true }); }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue