mirror of
https://github.com/direct-img/direct-img.link.git
synced 2026-10-29 07:24:52 +00:00
Fix: Serve promoted images with a + without redirecting
Rule conditions see the original path, so chained rules can't encode +s. One rule per number of +s does it instead, which only fits the free plan's 10 rules if they're shared by all sites, so static files now live under their hostname (/free.direct-img.link/1/orange%2Bcat). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-3
@@ -13,9 +13,8 @@ export const MAX_INDEX = { "": 40, free: 20, ai: 1000 };
|
||||
// "free", "ai" or "" (main site), from the hostname's first label
|
||||
export const siteOf = hostname => ["free", "ai"].find(s => hostname.startsWith(`${s}.`)) || "";
|
||||
|
||||
// Path prefix each site's ?i= is folded into by the zone's URL Rewrite and Redirect Rules (scripts/rules.js), e.g.
|
||||
// /orange+cat?i=2 → /web/2/orange+cat, so promoted images can be static files, which ignore query strings
|
||||
export const folder = site => site || "web";
|
||||
// Most +s in a query the zone's URL Rewrite Rules (scripts/rules.js) can turn into a static file's path, one rule each
|
||||
export const MAX_PLUSES = 7;
|
||||
|
||||
export function normalizeQuery(path) {
|
||||
const clean = s => s.toLowerCase().trim().replace(/[\x00-\x1f]/g, "").replace(/\/+$/, "").replace(/\s+/g, " ");
|
||||
|
||||
+8
-7
@@ -4,7 +4,7 @@ import { freeImageUrls, parseFreeSource, FREE_UA } from "./_utils/free.js";
|
||||
import { isFlickrPlaceholder } from "./_utils/flickr.js";
|
||||
import { aiImage, parseAiSource, DEFAULT_AI_SOURCE } from "./_utils/ai.js";
|
||||
import { surreal } from "./_utils/surreal.js";
|
||||
import { TTL_SECONDS, MAX_INDEX, siteOf, folder, normalizeQuery, cacheKey as keyFor, r2Key as r2KeyFor, sha256 } from "./_utils/keys.js";
|
||||
import { TTL_SECONDS, MAX_INDEX, siteOf, normalizeQuery, cacheKey as keyFor, r2Key as r2KeyFor, sha256 } from "./_utils/keys.js";
|
||||
import * as LIMITS from "../public/assets/rate-limit.js";
|
||||
|
||||
// How long a failed search keeps serving bad.webp before the query is searched again
|
||||
@@ -365,13 +365,14 @@ async function countHit(env, request, query, site, params) {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// The zone's URL Rewrite (scripts/rules.js) turns a bare query into its site's static file path (/orange+cat →
|
||||
// /web/1/orange%2Bcat) so a promoted image is served without running the worker. Unfolded back here, so the rest of the
|
||||
// worker sees the original request: the rule's %2B were +s, since it leaves a query's own %2B unfolded. Other ?i= in the
|
||||
// path are still accepted (links from when ?i= was folded too). A folded path for another site is rejected like any slash.
|
||||
// The zone's URL Rewrite Rules (scripts/rules.js) turn a bare query into its static file's path, under its hostname
|
||||
// (free.direct-img.link/orange+cat → /free.direct-img.link/1/orange%2Bcat), so a promoted image is served without
|
||||
// running the worker. Unfolded back here, so the rest of the worker sees the original request: the rules' %2B were +s,
|
||||
// since they leave a query's own %2B alone. Links from when ?i= was folded too (/free/2/orange+cat) still work. A folded
|
||||
// path for another site is rejected like any slash.
|
||||
function unfold(url, site) {
|
||||
const m = url.pathname.match(/^\/(web|free|ai)\/([^/]*)\/([^/]+)$/);
|
||||
if (!m || m[1] !== folder(site)) return url;
|
||||
const m = url.pathname.match(/^\/([^/]+)\/([^/]*)\/([^/]+)$/);
|
||||
if (!m || ![url.hostname, site || "web"].includes(m[1])) return url;
|
||||
const u = new URL(url);
|
||||
u.pathname = `/${m[3].replace(/%2B/gi, "+")}`;
|
||||
if (m[2] && m[2] !== "1") u.searchParams.set("i", m[2]);
|
||||
|
||||
Reference in New Issue
Block a user