Filtered out felix-fx-i mod
Co-authored-by: felix-fx-top <253056634+felix-fx-top@users.noreply.github.com>
This commit is contained in:
parent
445c56af0c
commit
006673e11c
@ -11,7 +11,10 @@ const Index = () => {
|
|||||||
|
|
||||||
const { data: userMods, isLoading: loadingUser } = useQuery({
|
const { data: userMods, isLoading: loadingUser } = useQuery({
|
||||||
queryKey: ["user-projects"],
|
queryKey: ["user-projects"],
|
||||||
queryFn: () => getUserProjects(),
|
queryFn: async () => {
|
||||||
|
const projects = await getUserProjects();
|
||||||
|
return (projects || []).filter((p: any) => p.slug !== "felix-fx-i" && p.id !== "felix-fx-i");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: discoverData, isLoading: loadingDiscover } = useQuery({
|
const { data: discoverData, isLoading: loadingDiscover } = useQuery({
|
||||||
|
|||||||
126
supabase/functions/fetch-curseforge/index.ts
Normal file
126
supabase/functions/fetch-curseforge/index.ts
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
|
||||||
|
};
|
||||||
|
|
||||||
|
const CF_BASE = "https://api.curseforge.com/v1";
|
||||||
|
const MINECRAFT_GAME_ID = 432;
|
||||||
|
|
||||||
|
// Rate limiting
|
||||||
|
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
|
||||||
|
const RATE_LIMIT = 30;
|
||||||
|
const RATE_WINDOW = 60_000;
|
||||||
|
|
||||||
|
function checkRateLimit(ip: string): boolean {
|
||||||
|
const now = Date.now();
|
||||||
|
const entry = rateLimitMap.get(ip);
|
||||||
|
if (!entry || now > entry.resetAt) {
|
||||||
|
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_WINDOW });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
entry.count++;
|
||||||
|
return entry.count <= RATE_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeString(str: string | null, maxLen = 100): string {
|
||||||
|
if (!str) return "";
|
||||||
|
return str.slice(0, maxLen).replace(/[<>{}]/g, "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
serve(async (req) => {
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return new Response(null, { headers: corsHeaders });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method !== "GET") {
|
||||||
|
return new Response(JSON.stringify({ error: "Method not allowed" }), {
|
||||||
|
status: 405,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientIp = req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "unknown";
|
||||||
|
if (!checkRateLimit(clientIp)) {
|
||||||
|
return new Response(JSON.stringify({ error: "Too many requests" }), {
|
||||||
|
status: 429,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json", "Retry-After": "60" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const apiKey = Deno.env.get("CURSEFORGE_API_KEY");
|
||||||
|
if (!apiKey) {
|
||||||
|
return new Response(JSON.stringify({ error: "CurseForge API key not configured" }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(req.url);
|
||||||
|
const action = sanitizeString(url.searchParams.get("action"), 20);
|
||||||
|
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"x-api-key": apiKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cfUrl = "";
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case "search": {
|
||||||
|
const query = sanitizeString(url.searchParams.get("query"), 200);
|
||||||
|
const offset = Math.max(0, Math.min(parseInt(url.searchParams.get("offset") || "0") || 0, 1000));
|
||||||
|
const limit = Math.max(1, Math.min(parseInt(url.searchParams.get("limit") || "20") || 20, 50));
|
||||||
|
const classId = sanitizeString(url.searchParams.get("classId"), 10);
|
||||||
|
|
||||||
|
cfUrl = `${CF_BASE}/mods/search?gameId=${MINECRAFT_GAME_ID}&searchFilter=${encodeURIComponent(query)}&index=${offset}&pageSize=${limit}&sortField=2&sortOrder=desc`;
|
||||||
|
if (classId) {
|
||||||
|
cfUrl += `&classId=${classId}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "mod": {
|
||||||
|
const id = sanitizeString(url.searchParams.get("id"), 20);
|
||||||
|
if (!id || !/^\d+$/.test(id)) {
|
||||||
|
return new Response(JSON.stringify({ error: "Invalid mod id" }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cfUrl = `${CF_BASE}/mods/${id}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "files": {
|
||||||
|
const id = sanitizeString(url.searchParams.get("id"), 20);
|
||||||
|
if (!id || !/^\d+$/.test(id)) {
|
||||||
|
return new Response(JSON.stringify({ error: "Invalid mod id" }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cfUrl = `${CF_BASE}/mods/${id}/files?pageSize=10`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return new Response(JSON.stringify({ error: "Invalid action" }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(cfUrl, { headers });
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(data), {
|
||||||
|
status: response.status,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return new Response(JSON.stringify({ error: "Internal server error" }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user