isogit-lfs/src/upload.ts

104 lines
2.8 KiB
TypeScript
Raw Permalink Normal View History

2022-12-07 23:39:09 +00:00
import { Buffer } from "buffer";
2022-12-07 23:39:09 +00:00
import { HTTPRequest } from "./types";
import { buildPointerInfo, PointerInfo } from "./pointers";
2023-01-17 14:09:58 +00:00
import { getAuthHeader } from "./util";
interface LFSInfoResponse {
objects: {
actions?: {
upload: {
href: string;
header?: Record<string, string>;
};
verify?: {
href: string;
header?: Record<string, string>;
};
};
}[];
}
2022-12-07 23:39:09 +00:00
function isValidLFSInfoResponseData(
val: Record<string, any>
): val is LFSInfoResponse {
const obj = val.objects?.[0];
2022-12-07 23:39:09 +00:00
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.
*/
2023-01-17 14:09:58 +00:00
export default async function uploadBlobs(
{ headers = {}, url, auth }: HTTPRequest,
contents: Buffer[]
): Promise<PointerInfo[]> {
const infos = await Promise.all(contents.map((c) => buildPointerInfo(c)));
2022-12-07 23:39:09 +00:00
const authHeaders: Record<string, string> = auth ? getAuthHeader(auth) : {};
// Request LFS transfer
const lfsInfoRequestData = {
2022-12-07 23:39:09 +00:00
operation: "upload",
transfers: ["basic"],
2023-01-17 14:09:58 +00:00
objects: infos,
};
2023-01-17 14:09:58 +00:00
const lfsInfoRes = await fetch(`${url}/info/lfs/objects/batch`, {
2022-12-07 23:39:09 +00:00
method: "POST",
headers: {
...headers,
...authHeaders,
2022-12-07 23:39:09 +00:00
Accept: "application/vnd.git-lfs+json",
},
2023-01-17 14:09:58 +00:00
body: JSON.stringify(lfsInfoRequestData),
});
2023-01-17 14:09:58 +00:00
const lfsInfoResponseData = await lfsInfoRes.json();
if (!isValidLFSInfoResponseData(lfsInfoResponseData)) {
2022-12-07 23:39:09 +00:00
throw new Error(
2023-01-17 14:09:58 +00:00
"Unexpected JSON structure received for LFS upload request"
2022-12-07 23:39:09 +00:00
);
}
2023-01-17 14:09:58 +00:00
await Promise.all(
lfsInfoResponseData.objects.map(async (object, index) => {
// server already has file
if (!object.actions) return;
const { actions } = object;
2023-01-17 14:09:58 +00:00
const resp = await fetch(actions.upload.href, {
2022-12-07 23:39:09 +00:00
method: "PUT",
2023-01-17 14:09:58 +00:00
headers: {
...headers,
...authHeaders,
...(actions.upload.header ?? {}),
Accept: "application/vnd.git-lfs+json",
},
body: contents[index],
});
2023-01-17 14:09:58 +00:00
if (!resp.ok)
throw new Error(
`Upload might have been unsuccessful, upload action yielded HTTP ${resp.status}`
);
2023-01-17 14:09:58 +00:00
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}`
);
}
2023-01-17 14:09:58 +00:00
})
);
return infos;
}