worker.js
· 2.9 KiB · JavaScript
Raw
// 利用api.nsmao.net实现的bing接口
const API_KEY = "ajdlfklahweufbrffbhjefjfsmd,shj" // 自行申请API
const CACHE_TTL = 36000 // 10 小时
export default {
async fetch(request, env, ctx) {
const { pathname } = new URL(request.url)
// 首页说明
if (pathname === "/") {
return new Response(
"🚀 本服务用于代理并缓存 https://api.nsmao.net 的图片接口,保护密钥不泄露。\n" +
"当前支持:/api/360bz 与 /api/bing 接口,图片将自动缓存10小时以减少请求压力。",
{
status: 200,
headers: { "Content-Type": "text/plain; charset=utf-8" }
}
)
}
// 360bz 图片接口
if (pathname === "/api/360bz") {
return await cachedProxyImage(
"360bz",
"https://api.nsmao.net/api/360bz/query",
{ key: API_KEY },
"url",
ctx
)
}
// bing 图片接口
if (pathname === "/api/bing") {
return await cachedProxyImage(
"bing",
"https://api.nsmao.net/api/bing/query",
{ key: API_KEY, type: "json" },
"image_url",
ctx
)
}
return new Response("Not found", { status: 404 })
}
}
async function cachedProxyImage(
cacheKey,
apiUrl,
queryParams,
imageField,
ctx
) {
const cache = caches.default
const cacheRequest = new Request(`https://cache.nsmao/${cacheKey}`)
const cached = await cache.match(cacheRequest)
if (cached) {
const cachedWithHeader = new Response(cached.body, cached)
cachedWithHeader.headers.set("X-Cache-Status", "HIT")
return cachedWithHeader
}
// 构造真实请求 URL
const url = new URL(apiUrl)
Object.entries(queryParams).forEach(([k, v]) => url.searchParams.set(k, v))
const headers = {
"Accept": "application/json",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
"Referer": "https://www.google.com/",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
}
// 请求 nsmao 接口
const apiResp = await fetch(url.toString(), { method: "GET", headers })
if (!apiResp.ok) {
return new Response("Failed to fetch API", { status: 502 })
}
const json = await apiResp.json()
const imageUrl = json?.data?.[imageField]
if (!imageUrl) {
return new Response("Image URL not found in API response", { status: 500 })
}
// 请求图片
const imageResp = await fetch(imageUrl, {
headers: {
...headers,
"Accept": "image/*"
}
})
const contentType = imageResp.headers.get("content-type") || "image/jpeg"
const response = new Response(imageResp.body, {
headers: {
"Content-Type": contentType,
"Cache-Control": `public, max-age=${CACHE_TTL}`,
"X-Cache-Status": "MISS"
}
})
// 后台写入缓存
ctx.waitUntil(cache.put(cacheRequest, response.clone()))
return response
}
| 1 | // 利用api.nsmao.net实现的bing接口 |
| 2 | const API_KEY = "ajdlfklahweufbrffbhjefjfsmd,shj" // 自行申请API |
| 3 | const CACHE_TTL = 36000 // 10 小时 |
| 4 | |
| 5 | export default { |
| 6 | async fetch(request, env, ctx) { |
| 7 | const { pathname } = new URL(request.url) |
| 8 | |
| 9 | // 首页说明 |
| 10 | if (pathname === "/") { |
| 11 | return new Response( |
| 12 | "🚀 本服务用于代理并缓存 https://api.nsmao.net 的图片接口,保护密钥不泄露。\n" + |
| 13 | "当前支持:/api/360bz 与 /api/bing 接口,图片将自动缓存10小时以减少请求压力。", |
| 14 | { |
| 15 | status: 200, |
| 16 | headers: { "Content-Type": "text/plain; charset=utf-8" } |
| 17 | } |
| 18 | ) |
| 19 | } |
| 20 | |
| 21 | // 360bz 图片接口 |
| 22 | if (pathname === "/api/360bz") { |
| 23 | return await cachedProxyImage( |
| 24 | "360bz", |
| 25 | "https://api.nsmao.net/api/360bz/query", |
| 26 | { key: API_KEY }, |
| 27 | "url", |
| 28 | ctx |
| 29 | ) |
| 30 | } |
| 31 | |
| 32 | // bing 图片接口 |
| 33 | if (pathname === "/api/bing") { |
| 34 | return await cachedProxyImage( |
| 35 | "bing", |
| 36 | "https://api.nsmao.net/api/bing/query", |
| 37 | { key: API_KEY, type: "json" }, |
| 38 | "image_url", |
| 39 | ctx |
| 40 | ) |
| 41 | } |
| 42 | |
| 43 | return new Response("Not found", { status: 404 }) |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | async function cachedProxyImage( |
| 48 | cacheKey, |
| 49 | apiUrl, |
| 50 | queryParams, |
| 51 | imageField, |
| 52 | ctx |
| 53 | ) { |
| 54 | const cache = caches.default |
| 55 | const cacheRequest = new Request(`https://cache.nsmao/${cacheKey}`) |
| 56 | const cached = await cache.match(cacheRequest) |
| 57 | |
| 58 | if (cached) { |
| 59 | const cachedWithHeader = new Response(cached.body, cached) |
| 60 | cachedWithHeader.headers.set("X-Cache-Status", "HIT") |
| 61 | return cachedWithHeader |
| 62 | } |
| 63 | |
| 64 | // 构造真实请求 URL |
| 65 | const url = new URL(apiUrl) |
| 66 | Object.entries(queryParams).forEach(([k, v]) => url.searchParams.set(k, v)) |
| 67 | |
| 68 | const headers = { |
| 69 | "Accept": "application/json", |
| 70 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", |
| 71 | "Referer": "https://www.google.com/", |
| 72 | "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" |
| 73 | } |
| 74 | |
| 75 | // 请求 nsmao 接口 |
| 76 | const apiResp = await fetch(url.toString(), { method: "GET", headers }) |
| 77 | if (!apiResp.ok) { |
| 78 | return new Response("Failed to fetch API", { status: 502 }) |
| 79 | } |
| 80 | |
| 81 | const json = await apiResp.json() |
| 82 | const imageUrl = json?.data?.[imageField] |
| 83 | |
| 84 | if (!imageUrl) { |
| 85 | return new Response("Image URL not found in API response", { status: 500 }) |
| 86 | } |
| 87 | |
| 88 | // 请求图片 |
| 89 | const imageResp = await fetch(imageUrl, { |
| 90 | headers: { |
| 91 | ...headers, |
| 92 | "Accept": "image/*" |
| 93 | } |
| 94 | }) |
| 95 | |
| 96 | const contentType = imageResp.headers.get("content-type") || "image/jpeg" |
| 97 | const response = new Response(imageResp.body, { |
| 98 | headers: { |
| 99 | "Content-Type": contentType, |
| 100 | "Cache-Control": `public, max-age=${CACHE_TTL}`, |
| 101 | "X-Cache-Status": "MISS" |
| 102 | } |
| 103 | }) |
| 104 | |
| 105 | // 后台写入缓存 |
| 106 | ctx.waitUntil(cache.put(cacheRequest, response.clone())) |
| 107 | return response |
| 108 | } |
| 109 |