From b34873f98052ac5fb4bf6731a25730075796d764 Mon Sep 17 00:00:00 2001 From: ertopogo Date: Fri, 13 Mar 2026 00:33:28 +0100 Subject: Initial commit medias platform --- viewer-bff/public/app.js | 122 +++++++++++++++++++++++++++++++++++++++++++ viewer-bff/public/index.html | 43 +++++++++++++++ viewer-bff/public/styles.css | 93 +++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 viewer-bff/public/app.js create mode 100644 viewer-bff/public/index.html create mode 100644 viewer-bff/public/styles.css (limited to 'viewer-bff/public') diff --git a/viewer-bff/public/app.js b/viewer-bff/public/app.js new file mode 100644 index 0000000..ecbe3fb --- /dev/null +++ b/viewer-bff/public/app.js @@ -0,0 +1,122 @@ +const tokenInput = document.getElementById("tokenInput"); +const objectKeysInput = document.getElementById("objectKeysInput"); +const permissionsOutput = document.getElementById("permissionsOutput"); +const gallery = document.getElementById("gallery"); + +const loadPermissionsBtn = document.getElementById("loadPermissionsBtn"); +const buildGalleryBtn = document.getElementById("buildGalleryBtn"); + +let currentPermissions = null; + +function getToken() { + return tokenInput.value.trim(); +} + +function parseObjectKeys() { + return objectKeysInput.value + .split("\n") + .map((line) => line.trim()) + .filter(Boolean); +} + +function isAllowedByPermissions(objectKey, permissions) { + if (!permissions) return false; + if (permissions.allowAll) return true; + const prefixes = permissions.allowedPrefixes || []; + return prefixes.some((prefix) => objectKey.startsWith(prefix)); +} + +async function callJson(url, options = {}) { + const token = getToken(); + const headers = { + "Content-Type": "application/json", + ...(options.headers || {}) + }; + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const response = await fetch(url, { ...options, headers }); + const payload = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(payload.message || `HTTP ${response.status}`); + } + return payload; +} + +loadPermissionsBtn.addEventListener("click", async () => { + try { + const perms = await callJson("/api/me/permissions"); + currentPermissions = perms; + permissionsOutput.textContent = JSON.stringify(perms, null, 2); + } catch (error) { + permissionsOutput.textContent = `Erreur: ${error.message}`; + } +}); + +buildGalleryBtn.addEventListener("click", async () => { + gallery.innerHTML = ""; + const keys = parseObjectKeys(); + + if (!currentPermissions) { + permissionsOutput.textContent = "Charger d'abord les permissions."; + return; + } + + if (!keys.length) { + permissionsOutput.textContent = "Ajouter au moins une objectKey."; + return; + } + + for (const objectKey of keys) { + const card = document.createElement("article"); + card.className = "card"; + + const img = document.createElement("img"); + img.className = "thumb"; + img.alt = objectKey; + + const keyP = document.createElement("p"); + keyP.className = "key"; + keyP.textContent = objectKey; + + const openBtn = document.createElement("button"); + openBtn.textContent = "Ouvrir"; + openBtn.disabled = !isAllowedByPermissions(objectKey, currentPermissions); + + openBtn.addEventListener("click", async () => { + try { + const presign = await callJson("/api/media/presign", { + method: "POST", + body: JSON.stringify({ objectKey }) + }); + const signedUrl = presign.url; + img.src = signedUrl; + window.open(signedUrl, "_blank", "noopener,noreferrer"); + } catch (error) { + alert(`Presign refuse: ${error.message}`); + } + }); + + if (!openBtn.disabled) { + // Previsualisation opportuniste pour les objets autorises. + callJson("/api/media/presign", { + method: "POST", + body: JSON.stringify({ objectKey }) + }) + .then((presign) => { + img.src = presign.url; + }) + .catch(() => { + img.alt = "Previsualisation indisponible"; + }); + } else { + img.alt = "Acces refuse (roles)"; + } + + card.appendChild(img); + card.appendChild(keyP); + card.appendChild(openBtn); + gallery.appendChild(card); + } +}); diff --git a/viewer-bff/public/index.html b/viewer-bff/public/index.html new file mode 100644 index 0000000..8112bfa --- /dev/null +++ b/viewer-bff/public/index.html @@ -0,0 +1,43 @@ + + + + + + Viewer Medias (POC) + + + +
+

Viewer medias securise (POC)

+ +
+

1) Token utilisateur

+ + +
Aucune permission chargee.
+
+ +
+

2) Cles objet a visualiser

+

+ Une cle par ligne, exemple: photos/equipeA/2026/03/img001.jpg +

+ +
+ +
+
+ +
+

3) Galerie

+ +
+
+ + + + diff --git a/viewer-bff/public/styles.css b/viewer-bff/public/styles.css new file mode 100644 index 0000000..5417318 --- /dev/null +++ b/viewer-bff/public/styles.css @@ -0,0 +1,93 @@ +* { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: Arial, sans-serif; + background: #0f172a; + color: #e2e8f0; +} + +.container { + max-width: 1100px; + margin: 0 auto; + padding: 24px; +} + +h1 { + margin: 0 0 20px; +} + +.panel { + background: #111827; + border: 1px solid #334155; + border-radius: 8px; + padding: 16px; + margin-bottom: 16px; +} + +textarea { + width: 100%; + background: #0b1220; + color: #e2e8f0; + border: 1px solid #334155; + border-radius: 6px; + padding: 10px; +} + +button { + margin-top: 10px; + background: #2563eb; + border: none; + color: white; + border-radius: 6px; + padding: 8px 12px; + cursor: pointer; +} + +button:hover { + background: #1d4ed8; +} + +.hint { + margin-top: 0; + color: #94a3b8; +} + +#permissionsOutput { + white-space: pre-wrap; + background: #0b1220; + border: 1px solid #334155; + border-radius: 6px; + padding: 10px; + min-height: 50px; +} + +.gallery { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 14px; +} + +.card { + background: #0b1220; + border: 1px solid #334155; + border-radius: 8px; + padding: 10px; +} + +.thumb { + width: 100%; + height: 140px; + object-fit: cover; + border-radius: 6px; + background: #1e293b; +} + +.key { + margin: 8px 0; + font-size: 12px; + word-break: break-all; + color: #cbd5e1; +} -- cgit v1.2.3