finalisation home page

This commit is contained in:
2026-02-06 20:20:01 +01:00
parent 91c1b03a2f
commit d9ac2b4cc5
45 changed files with 1892 additions and 148 deletions

View File

@@ -0,0 +1,108 @@
function isValidUrl(u) {
try {
new URL(u);
return true;
} catch {
return false;
}
}
function isInstagramPostUrl(u) {
try {
const url = new URL(u);
const host = url.hostname.replace(/^www\./, "");
if (host !== "instagram.com") return false;
const p = url.pathname;
return p.startsWith("/p/") || p.startsWith("/reel/") || p.startsWith("/tv/");
} catch {
return false;
}
}
export default defineCachedEventHandler(
async (event) => {
const config = useRuntimeConfig();
const appId = config.instagramAppId;
const clientToken = config.instagramClientToken;
if (!appId || !clientToken) {
throw createError({
statusCode: 500,
statusMessage:
"Missing Instagram oEmbed credentials (instagramAppId / instagramClientToken).",
});
}
const body = await readBody(event).catch(() => null);
if (!body || !Array.isArray(body.urls)) {
throw createError({
statusCode: 400,
statusMessage: "Invalid payload. Expected { urls: string[] }",
});
}
// normalise + filtre
const urls = body.urls
.map((u) => String(u).trim())
.filter((u) => isValidUrl(u))
.filter((u) => isInstagramPostUrl(u))
.slice(0, 24);
if (urls.length === 0) {
return { results: [] };
}
// Token Meta oEmbed: APP_ID|CLIENT_TOKEN
const accessToken = `${appId}|${clientToken}`;
const results = await Promise.all(
urls.map(async (url) => {
const endpoint = new URL(
"https://graph.facebook.com/v19.0/instagram_oembed"
);
endpoint.searchParams.set("url", url);
endpoint.searchParams.set("omitscript", "true");
endpoint.searchParams.set("access_token", accessToken);
try {
const data = await $fetch(endpoint.toString(), { timeout: 10000 });
return {
url,
ok: true,
html: data?.html || "",
title: data?.title ?? null,
author_name: data?.author_name ?? null,
thumbnail_url: data?.thumbnail_url ?? null,
};
} catch (e) {
return {
url,
ok: false,
error:
e?.data?.error?.message ||
e?.message ||
"Unknown oEmbed error",
};
}
})
);
return { results };
},
{
// cache 6h
maxAge: 60 * 60 * 6,
// clé de cache basée sur le payload (urls)
getKey: async (event) => {
const body = await readBody(event).catch(() => null);
const urls = Array.isArray(body?.urls) ? body.urls.join("|") : "none";
return `instagram:oembed:${urls}`;
},
}
);