78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
import { supabase } from "@/integrations/supabase/client";
|
|
|
|
const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL;
|
|
|
|
// Anti-spam: debounce tracking
|
|
let lastSearchTime = 0;
|
|
const SEARCH_COOLDOWN = 500; // ms between searches
|
|
|
|
async function callFetchMods(params: Record<string, string>) {
|
|
const now = Date.now();
|
|
if (now - lastSearchTime < SEARCH_COOLDOWN) {
|
|
await new Promise((r) => setTimeout(r, SEARCH_COOLDOWN - (now - lastSearchTime)));
|
|
}
|
|
lastSearchTime = Date.now();
|
|
|
|
const searchParams = new URLSearchParams(params);
|
|
const url = `${SUPABASE_URL}/functions/v1/fetch-mods?${searchParams.toString()}`;
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
Authorization: `Bearer ${import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY}`,
|
|
},
|
|
});
|
|
|
|
if (response.status === 429) {
|
|
throw new Error("تم تجاوز الحد المسموح، حاول مرة أخرى بعد قليل");
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`API error: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export async function getUserProjects(username = "fxfelixzero") {
|
|
return callFetchMods({ action: "user_projects", username });
|
|
}
|
|
|
|
export async function searchMods(query: string, offset = 0, limit = 20) {
|
|
// Sanitize client-side too
|
|
const cleanQuery = query.slice(0, 200).replace(/[<>{}]/g, "").trim();
|
|
if (!cleanQuery) return { hits: [], total_hits: 0 };
|
|
return callFetchMods({ action: "search", query: cleanQuery, offset: String(offset), limit: String(limit) });
|
|
}
|
|
|
|
export async function getProject(id: string) {
|
|
if (!/^[a-zA-Z0-9_-]+$/.test(id)) throw new Error("Invalid project id");
|
|
return callFetchMods({ action: "project", id });
|
|
}
|
|
|
|
export async function getProjectVersions(id: string) {
|
|
if (!/^[a-zA-Z0-9_-]+$/.test(id)) throw new Error("Invalid project id");
|
|
return callFetchMods({ action: "versions", id });
|
|
}
|
|
|
|
export async function translateQuery(query: string): Promise<{ translated: string; original: string }> {
|
|
const cleanQuery = query.slice(0, 200).trim();
|
|
if (!cleanQuery) return { translated: query, original: query };
|
|
|
|
const { data, error } = await supabase.functions.invoke("translate-search", {
|
|
body: { query: cleanQuery },
|
|
});
|
|
|
|
if (error) {
|
|
console.error("Translation error:", error);
|
|
return { translated: query, original: query };
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Random search keywords for homepage
|
|
const RANDOM_KEYWORDS = ["world", "mod", "skin", "map", "adventure", "survival", "building", "tools", "armor", "biome"];
|
|
|
|
export function getRandomKeyword(): string {
|
|
return RANDOM_KEYWORDS[Math.floor(Math.random() * RANDOM_KEYWORDS.length)];
|
|
}
|