isogit-lfs/src/upload.ts

104 lines
2.8 KiB
TypeScript

import { Buffer } from "buffer";
import { HTTPRequest } from "./types";
import { buildPointerInfo, PointerInfo } from "./pointers";
import { getAuthHeader } from "./util";
interface LFSInfoResponse {
objects: {
actions?: {
upload: {
href: string;
header?: Record<string, string>;
};
verify?: {
href: string;
header?: Record<string, string>;
};
};
}[];
}
function isValidLFSInfoResponseData(
val: Record<string, any>
): val is LFSInfoResponse {
const obj = val.objects?.[0];
return obj && (!obj.actions || obj.actions.upload.href.trim !== undefined);
}
/**
* Given a blob, uploads the blob to LFS server and returns a PointerInfo,
* which the caller can then combine with object path into a Pointer
* and commit in place of the original Git blob.
*/
export default async function uploadBlobs(
{ headers = {}, url, auth }: HTTPRequest,
contents: Buffer[]
): Promise<PointerInfo[]> {
const infos = await Promise.all(contents.map((c) => buildPointerInfo(c)));
const authHeaders: Record<string, string> = auth ? getAuthHeader(auth) : {};
// Request LFS transfer
const lfsInfoRequestData = {
operation: "upload",
transfers: ["basic"],
objects: infos,
};
const lfsInfoRes = await fetch(`${url}/info/lfs/objects/batch`, {
method: "POST",
headers: {
...headers,
...authHeaders,
Accept: "application/vnd.git-lfs+json",
},
body: JSON.stringify(lfsInfoRequestData),
});
const lfsInfoResponseData = await lfsInfoRes.json();
if (!isValidLFSInfoResponseData(lfsInfoResponseData)) {
throw new Error(
"Unexpected JSON structure received for LFS upload request"
);
}
await Promise.all(
lfsInfoResponseData.objects.map(async (object, index) => {
// server already has file
if (!object.actions) return;
const { actions } = object;
const resp = await fetch(actions.upload.href, {
method: "PUT",
headers: {
...headers,
...authHeaders,
...(actions.upload.header ?? {}),
Accept: "application/vnd.git-lfs+json",
},
body: contents[index],
});
if (!resp.ok)
throw new Error(
`Upload might have been unsuccessful, upload action yielded HTTP ${resp.status}`
);
if (actions.verify) {
const verificationResp = await fetch(actions.verify.href, {
method: "POST",
headers: {
...(actions.verify.header ?? {}),
Accept: "application/vnd.git-lfs+json",
},
body: JSON.stringify(infos[index]),
});
if (!resp.ok)
throw new Error(
`Upload might have been unsuccessful, verification action yielded HTTP ${verificationResp.status}`
);
}
})
);
return infos;
}