fetchProduction
uses node:fs
Fetch production's traces (GET /__tcm/traces.sqlite on its server, with its traces token) into the studio's copy,
which its traces pages read: what came, or why nothing did.
import { HttpError } from "#codebase/tcmts/server"; import { editableCodebase, production, productionFile, productionTraces } from "#codebase/tcmts/studio"; import fs from "node:fs"; import { Readable } from "node:stream"; import { pipeline } from "node:stream/promises"; export async function fetchProduction(): Promise<string> { const cb = editableCodebase(); const p = production(cb); if (!p) throw new HttpError(409, "Production isn't set up yet: its address and traces token."); let res: Response; try { res = await fetch(`${p.url}/__tcm/traces.sqlite`, { headers: { authorization: `Bearer ${p.token}`, "accept-encoding": "gzip" }, signal: AbortSignal.timeout(120_000), }); } catch (e) { throw new HttpError(502, `${p.url} didn't answer: ${(e as Error).message}`); } if (res.status === 401) throw new HttpError(401, `${p.url} didn't take the token: it's the server's TCM_TRACES_TOKEN.`); if (res.status === 404) throw new HttpError( 404, `${p.url} hands out no traces: set TCM_TRACES_TOKEN where it runs (tcm start), to the token given here.`, ); if (res.status === 204) { fs.rmSync(productionTraces(cb), { force: true }); return "Production has no traces yet."; } if (!res.ok || !res.body) throw new HttpError(502, `${p.url} answered ${res.status}.`); const tmp = `${productionTraces(cb)}.part`; await pipeline(Readable.fromWeb(res.body as never), fs.createWriteStream(tmp)); fs.renameSync(tmp, productionTraces(cb)); fs.writeFileSync( productionFile(cb), `${JSON.stringify({ ...p, fetched: Date.now() }, null, 2)}\n`, { mode: 0o600 }, ); const mb = fs.statSync(productionTraces(cb)).size / 2 ** 20; return `Fetched production's traces (${mb < 1 ? "under 1" : Math.round(mb)} MB).`; }
uses
- tcmts.server.HttpError
- tcmts.studio.editableCodebase
- tcmts.studio.production
- tcmts.studio.productionFile
- tcmts.studio.productionTraces