57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
import "server-only";
|
||
|
||
export function normalizeFalCredentials(apiKey: string) {
|
||
const normalized = String(apiKey || "")
|
||
.replace(/\u00a0/g, " ")
|
||
.replace(/\u3000/g, " ")
|
||
.replace(/:/g, ":")
|
||
.trim();
|
||
const stripped = normalized.replace(/^bearer\s+/i, "").replace(/^key\s+/i, "");
|
||
const kvMatch = stripped.match(/^(?:fal[_-]?key|api[_-]?key|token)\s*[::]\s*(.+)$/i);
|
||
if (kvMatch?.[1]) {
|
||
return kvMatch[1].trim();
|
||
}
|
||
return stripped;
|
||
}
|
||
|
||
export function collectFalImageUrls(payload: any): string[] {
|
||
const urls = new Set<string>();
|
||
|
||
const isLikelyImageUrl = (value: string) =>
|
||
/^https?:\/\//i.test(value) &&
|
||
(/\.(png|jpg|jpeg|webp|gif)(\?|$)/i.test(value) ||
|
||
value.includes("image") ||
|
||
value.includes("img") ||
|
||
value.includes("fal.media"));
|
||
|
||
const walk = (value: any, parentKey = "") => {
|
||
if (typeof value === "string") {
|
||
if (isLikelyImageUrl(value)) {
|
||
urls.add(value);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (Array.isArray(value)) {
|
||
for (const item of value) {
|
||
walk(item, parentKey);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (value && typeof value === "object") {
|
||
for (const [key, nestedValue] of Object.entries(value)) {
|
||
if (key === "url" && typeof nestedValue === "string" && /^https?:\/\//i.test(nestedValue)) {
|
||
if (/image|img|file|output|result|artifact/i.test(parentKey) || isLikelyImageUrl(nestedValue)) {
|
||
urls.add(nestedValue);
|
||
}
|
||
}
|
||
walk(nestedValue, key);
|
||
}
|
||
}
|
||
};
|
||
|
||
walk(payload);
|
||
return Array.from(urls);
|
||
}
|