Created
April 17, 2025 08:31
-
-
Save nbolton/82d71943207d10a5f4b07d031e2b0fa0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Lazy multipurpose mirror handler for AWS Lambda@Edge or API Gateway | |
import { S3Client, HeadObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3'; | |
import fetch from 'node-fetch'; | |
const s3 = new S3Client({ region: 'your-region' }); | |
const BUCKET = 'qt-mirror-cache'; | |
// Prefix -> upstream URL | |
const routeTable: Record<string, string> = { | |
'/qt': 'https://download.qt.io', | |
'/npm': 'https://registry.npmjs.org', | |
'/foo': 'https://example.com/foo', | |
}; | |
export async function handler(event: any) { | |
const path = decodeURIComponent(event.path); | |
const prefix = Object.keys(routeTable).find(p => path.startsWith(p)); | |
if (!prefix) { | |
console.warn(`[404] Unknown prefix for path: ${path}`); | |
return { statusCode: 404, body: 'Unknown mirror path' }; | |
} | |
const upstream = routeTable[prefix]; | |
const upstreamPath = path.slice(prefix.length); | |
const upstreamUrl = `${upstream}${upstreamPath}`; | |
const s3Key = path.startsWith('/') ? path.slice(1) : path; | |
try { | |
await s3.send(new HeadObjectCommand({ Bucket: BUCKET, Key: s3Key })); | |
console.info(`[HIT] Cache hit: s3://${BUCKET}/${s3Key}`); | |
return redirectToS3(s3Key); | |
} catch { | |
console.info(`[MISS] Cache miss: s3://${BUCKET}/${s3Key}`); | |
} | |
try { | |
const res = await fetch(upstreamUrl); | |
if (!res.ok) { | |
console.error(`[UPSTREAM FAIL] ${res.status} ${res.statusText} - ${upstreamUrl}`); | |
return { statusCode: res.status, body: 'Upstream error' }; | |
} | |
const body = await res.buffer(); | |
await s3.send(new PutObjectCommand({ | |
Bucket: BUCKET, | |
Key: s3Key, | |
Body: body, | |
ContentType: res.headers.get('content-type') || 'application/octet-stream', | |
})); | |
console.info(`[FETCHED] ${upstreamUrl} → s3://${BUCKET}/${s3Key}`); | |
return redirectToS3(s3Key); | |
} catch (err) { | |
console.error(`[ERROR] Failed to fetch and store ${upstreamUrl}:`, err); | |
return { statusCode: 500, body: 'Mirror error' }; | |
} | |
} | |
function redirectToS3(key: string) { | |
return { | |
statusCode: 302, | |
headers: { | |
Location: `https://${BUCKET}.s3.amazonaws.com/${key}`, | |
}, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment