Skip to content

Commit 2f614a3

Browse files
committed
Resolve git lfs pointers
1 parent b9f8f4a commit 2f614a3

File tree

4 files changed

+72
-38
lines changed

4 files changed

+72
-38
lines changed
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/// <reference lib="deno.ns" />
2+
3+
import type { Config } from "https://edge.netlify.com"
4+
5+
export default async (request: Request) => {
6+
switch (request.method) {
7+
case "GET":
8+
return await get(request)
9+
default:
10+
return new Response("Method not allowed", { status: 405 })
11+
}
12+
}
13+
14+
/** Resolve a Git LFS pointer to a file URL */
15+
async function get(request: Request) {
16+
try {
17+
const url = new URL(request.url)
18+
const repo = url.searchParams.get("repo")
19+
const pointer = url.searchParams.get("pointer")
20+
const authorization = request.headers.get("Authorization")
21+
22+
if (!repo || !pointer || !authorization) {
23+
throw new Error("Invalid request")
24+
}
25+
26+
const oid = pointer.match(/oid sha256:(?<oid>[a-f0-9]{64})/)?.groups?.oid
27+
const size = parseInt(pointer.match(/size (?<size>\d+)/)?.groups?.size ?? "0")
28+
29+
const response = await fetch(`https://github.com/${repo}.git/info/lfs/objects/batch`, {
30+
method: "POST",
31+
headers: {
32+
"Content-Type": "application/json",
33+
Accept: "application/vnd.git-lfs+json",
34+
Authorization: authorization,
35+
},
36+
body: JSON.stringify({
37+
operation: "download",
38+
transfers: ["basic"],
39+
objects: [{ oid, size }],
40+
}),
41+
})
42+
43+
const json = await response.json()
44+
const href = json.objects[0].actions.download.href
45+
46+
return new Response(href, { status: 200 })
47+
} catch (error) {
48+
return new Response(`Error: ${error.message}`, { status: 500 })
49+
}
50+
}
51+
52+
export const config: Config = {
53+
path: "/git-lfs-file",
54+
}

netlify/edge-functions/github-auth.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import type { Config } from "https://edge.netlify.com"
55
// Reference: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
66
export default async (request: Request) => {
77
try {
8-
const code = new URL(request.url).searchParams.get("code")
9-
const state = new URL(request.url).searchParams.get("state")
8+
const url = new URL(request.url)
9+
const code = url.searchParams.get("code")
10+
const state = url.searchParams.get("state")
1011

1112
const response = await fetch("https://github.com/login/oauth/access_token", {
1213
method: "POST",

src/components/file-preview.tsx

+5-14
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ export function FilePreview({ path, alt = "" }: FilePreviewProps) {
2424
// If file is already cached, don't fetch it again
2525
if (file) return
2626

27-
// Use ignore flag to avoid race conditions
28-
// Reference: https://react.dev/reference/react/useEffect#fetching-data-with-effects
29-
let ignore = false
30-
3127
async function loadFile() {
3228
if (!githubUser || !githubRepo) return
3329
console.log(githubUser, githubRepo)
@@ -46,12 +42,11 @@ export function FilePreview({ path, alt = "" }: FilePreviewProps) {
4642
url = URL.createObjectURL(file)
4743
}
4844

49-
if (!ignore) {
50-
setFile(file)
51-
setUrl(url)
52-
// Cache the file and its URL
53-
fileCache.set(path, { file, url })
54-
}
45+
setFile(file)
46+
setUrl(url)
47+
48+
// Cache the file and its URL
49+
fileCache.set(path, { file, url })
5550
} catch (error) {
5651
console.error(error)
5752
} finally {
@@ -60,10 +55,6 @@ export function FilePreview({ path, alt = "" }: FilePreviewProps) {
6055
}
6156

6257
loadFile()
63-
64-
return () => {
65-
ignore = true
66-
}
6758
}, [file, githubUser, githubRepo, path])
6859

6960
if (!file) {

src/utils/git-lfs.ts

+10-22
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,24 @@ export async function resolveGitLfsPointer({
2222
}) {
2323
const text = await file.text()
2424

25-
// Parse the Git LFS pointer
26-
const oid = text.match(/oid sha256:(?<oid>[a-f0-9]{64})/)?.groups?.oid
27-
const size = text.match(/size (?<size>\d+)/)?.groups?.size
28-
29-
if (!oid || !size) {
30-
throw new Error("Invalid Git LFS pointer")
31-
}
32-
33-
// Fetch the file URL from GitHub
34-
// TODO: Use proxy to avoid CORS issues
3525
const response = await fetch(
36-
`https://github.com/${githubRepo.owner}/${githubRepo.name}.git/info/lfs/objects/batch`,
26+
`/git-lfs-file?repo=${githubRepo.owner}/${githubRepo.name}&pointer=${text}`,
3727
{
38-
method: "POST",
3928
headers: {
40-
"Content-Type": "application/json",
41-
Accept: "application/vnd.git-lfs+json",
4229
Authorization: `Bearer ${githubUser.token}`,
4330
},
44-
body: JSON.stringify({
45-
operation: "download",
46-
transfers: ["basic"],
47-
objects: [{ oid, size }],
48-
}),
4931
},
5032
)
5133

52-
const json = await response.json()
34+
if (!response.ok) {
35+
throw new Error("Unable to resolve Git LFS pointer")
36+
}
37+
38+
const url = await response.text()
5339

54-
console.log(json)
40+
if (!url) {
41+
throw new Error("Unable to resolve Git LFS pointer")
42+
}
5543

56-
return ""
44+
return url
5745
}

0 commit comments

Comments
 (0)