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/server.js | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 viewer-bff/server.js (limited to 'viewer-bff/server.js') diff --git a/viewer-bff/server.js b/viewer-bff/server.js new file mode 100644 index 0000000..30ef81a --- /dev/null +++ b/viewer-bff/server.js @@ -0,0 +1,115 @@ +const express = require("express"); +const path = require("path"); + +const app = express(); + +const PORT = process.env.PORT || 8082; +const MEDIA_API_BASE_URL = + process.env.MEDIA_API_BASE_URL || "http://media-access-api:8081"; +const CORS_ALLOWED_ORIGIN = process.env.CORS_ALLOWED_ORIGIN || "*"; + +app.use(express.json({ limit: "1mb" })); + +app.use((req, res, next) => { + res.setHeader("Access-Control-Allow-Origin", CORS_ALLOWED_ORIGIN); + res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Authorization,Content-Type"); + if (req.method === "OPTIONS") { + return res.sendStatus(204); + } + return next(); +}); + +app.use(express.static(path.join(__dirname, "public"))); + +app.get("/health", (_req, res) => { + res.json({ + status: "ok", + service: "viewer-bff", + mediaApiBaseUrl: MEDIA_API_BASE_URL + }); +}); + +function getBearerToken(req) { + const authHeader = req.headers.authorization || ""; + if (!authHeader.startsWith("Bearer ")) { + return null; + } + return authHeader.slice("Bearer ".length).trim(); +} + +async function proxyJson(req, res, targetPath, method = "GET", body) { + const token = getBearerToken(req); + if (!token) { + return res.status(401).json({ + error: "missing_token", + message: "Header Authorization Bearer requis" + }); + } + + try { + const response = await fetch(`${MEDIA_API_BASE_URL}${targetPath}`, { + method, + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json" + }, + body: body ? JSON.stringify(body) : undefined + }); + + const text = await response.text(); + let payload = {}; + if (text) { + try { + payload = JSON.parse(text); + } catch (_err) { + payload = { raw: text }; + } + } + + if (!response.ok) { + return res.status(response.status).json({ + error: "media_api_error", + status: response.status, + message: payload.message || "Erreur retour media-access-api", + details: payload + }); + } + + return res.status(response.status).json(payload); + } catch (error) { + return res.status(502).json({ + error: "media_api_unreachable", + message: "Impossible de joindre media-access-api", + details: error.message + }); + } +} + +app.get("/api/me/permissions", async (req, res) => { + return proxyJson(req, res, "/v1/permissions", "GET"); +}); + +app.post("/api/media/presign", async (req, res) => { + const objectKey = req.body?.objectKey; + if (!objectKey || typeof objectKey !== "string") { + return res.status(400).json({ + error: "invalid_payload", + message: "Champ objectKey requis" + }); + } + + return proxyJson(req, res, "/v1/presign", "POST", { objectKey }); +}); + +app.get("*", (req, res) => { + if (req.path.startsWith("/api/")) { + return res.status(404).json({ error: "not_found" }); + } + return res.sendFile(path.join(__dirname, "public", "index.html")); +}); + +app.listen(PORT, () => { + // eslint-disable-next-line no-console + console.log(`viewer-bff listening on port ${PORT}`); +}); -- cgit v1.2.3