mirror of
https://github.com/direct-img/direct-img.link.git
synced 2026-10-29 15:34:51 +00:00
Feat: Move from Pages to a Worker and serve popular images as static files
Static assets don't count toward the free plan's 100,000 requests a day, so a daily GitHub Action promotes the most viewed images (by Cloudflare analytics) from R2 into public/ before deploying, and reports to ntfy. Zone rules fold the site and ?i= into the path, since static files ignore query strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// Uppercase so it can't collide with query cache keys, which are always lowercased
|
||||
const TOKEN_KEY = "SURREAL_TOKEN";
|
||||
// SurrealDB re-checks a password against its deliberately slow hash on every request (~47ms), but a token in ~1ms. A
|
||||
// token lasts 60 min, so it's kept in this isolate's memory and in KV until 10 min before it expires.
|
||||
let cached = null;
|
||||
|
||||
// A token from memory, then KV, then a new /signin (fresh skips memory and KV). null if signing in fails.
|
||||
async function getToken(env, fresh) {
|
||||
if (!fresh && cached?.until > Date.now()) return cached.token;
|
||||
let token = !fresh && await env.DIRECT_IMG_CACHE.get(TOKEN_KEY), signedIn = !token;
|
||||
if (signedIn) {
|
||||
const res = await fetch(`${env.SURREAL_URL}/signin`, {
|
||||
method: "POST",
|
||||
headers: { "Accept": "application/json" },
|
||||
body: JSON.stringify({ user: env.SURREAL_USER, pass: env.SURREAL_PASS }),
|
||||
});
|
||||
token = res.ok && (await res.json()).token;
|
||||
if (!token) return null;
|
||||
}
|
||||
const ttl = Math.floor(JSON.parse(atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))).exp - Date.now() / 1000) - 600;
|
||||
if (signedIn && ttl >= 60) await env.DIRECT_IMG_CACHE.put(TOKEN_KEY, token, { expirationTtl: ttl }).catch(() => {});
|
||||
cached = ttl > 0 ? { token, until: Date.now() + ttl * 1000 } : null;
|
||||
return token;
|
||||
}
|
||||
|
||||
// Runs SurrealQL in direct_img/rate_limit and resolves to the Response, or null if SurrealDB isn't configured. Uses the
|
||||
// password only if no token can be had, so it still works, just slower.
|
||||
export function surreal(env) {
|
||||
if (!env.SURREAL_URL || !env.SURREAL_USER || !env.SURREAL_PASS) return null;
|
||||
const send = (token, body) => fetch(`${env.SURREAL_URL}/sql`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Authorization": token ? `Bearer ${token}` : `Basic ${btoa(`${env.SURREAL_USER}:${env.SURREAL_PASS}`)}`,
|
||||
"surreal-ns": "direct_img",
|
||||
"surreal-db": "rate_limit",
|
||||
},
|
||||
body
|
||||
});
|
||||
return async body => {
|
||||
const token = await getToken(env).catch(() => null), res = await send(token, body);
|
||||
// A refused token (expired early or revoked) means the query never ran, so it's safe to retry with a new one
|
||||
return res.status === 401 && token ? send(await getToken(env, true).catch(() => null), body) : res;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user