1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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}`);
});
|