discovery-media-player 0.1.74 → 0.1.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/server/appelant.js +59 -0
- package/server/gabarit-legal.js +45 -0
- package/server/handler.js +29 -2149
- package/server/page-audience.js +304 -0
- package/server/page-mur.js +123 -0
- package/server/page-visionneuse.js +800 -0
- package/server/routes-agent.js +148 -0
- package/server/routes-direct.js +232 -0
- package/server/routes-liens.js +451 -0
- package/server/routes-visiteur.js +63 -0
- package/server/session-cles.js +35 -0
- package/server/texte.js +23 -1
- package/server/tiers.js +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.76",
|
|
4
4
|
"description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pdf-viewer",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// EXTRAIT DE handler.js (refactor lot 3 — routes, 19/08/2026) — blocs déplacés À L'IDENTIQUE.
|
|
2
|
+
// Reste à PLAT dans server/ (les gardes de forge ciblent server/*.js).
|
|
3
|
+
|
|
4
|
+
let PLAYER = null;
|
|
5
|
+
const init = (ctx) => { PLAYER = ctx; };
|
|
6
|
+
|
|
7
|
+
// ⚠️ `X-Forwarded-For` EST UN EN-TÊTE, DONC UNE AFFIRMATION DU CLIENT.
|
|
8
|
+
//
|
|
9
|
+
// Onze endroits en prenaient la première valeur pour identifier l'appelant, et toutes les limites
|
|
10
|
+
// de débit s'appuyaient dessus. Un client qui atteint directement le serveur — c'est le cas du
|
|
11
|
+
// serveur autonome, et de toute instance dont le proxy ne réécrit pas cet en-tête — pouvait donc
|
|
12
|
+
// en changer à chaque requête et n'être jamais limité. La limite existait, elle ne limitait rien.
|
|
13
|
+
//
|
|
14
|
+
// Le caractère « par processus » des compteurs était documenté ; la confiance dans un en-tête non
|
|
15
|
+
// authentifié ne l'était pas. C'est la différence entre une limite approximative et une limite
|
|
16
|
+
// décorative.
|
|
17
|
+
//
|
|
18
|
+
// La décision revient à l'hôte, parce que lui seul sait s'il y a un proxy devant lui :
|
|
19
|
+
// `identity.clientIp(req)` s'il la fournit, sinon l'adresse de la socket — jamais l'en-tête. Un
|
|
20
|
+
// hôte derrière un proxy déclare sa politique dans son câblage ; un hôte sans proxy n'a rien à
|
|
21
|
+
// faire, et il est protégé par défaut.
|
|
22
|
+
function adresseAppelant(req) {
|
|
23
|
+
try {
|
|
24
|
+
if (PLAYER.identity && typeof PLAYER.identity.clientIp === "function") {
|
|
25
|
+
const v = PLAYER.identity.clientIp(req);
|
|
26
|
+
if (v) return String(v).slice(0, 60);
|
|
27
|
+
}
|
|
28
|
+
} catch { /* une IP indisponible ne doit pas empêcher de lire un document */ }
|
|
29
|
+
return String((req.socket && req.socket.remoteAddress) || "").slice(0, 60);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** La clé d'un membre : son adresse, normalisée — la recherche de ligne est exacte. */
|
|
33
|
+
function lcMembre(email) {
|
|
34
|
+
return String(email || "").trim().toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function cleAnonyme(brut) {
|
|
38
|
+
const v = String(brut == null ? "" : brut).trim().slice(0, 120);
|
|
39
|
+
return /^anon-[A-Za-z0-9_-]{4,}$/.test(v) ? v : "anon-inconnu";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function profilDuJeton(req) {
|
|
43
|
+
try {
|
|
44
|
+
const u = await PLAYER.identity.verifyToken((req.headers && req.headers.authorization) || "");
|
|
45
|
+
if (!u || !u.email) return null;
|
|
46
|
+
if (typeof PLAYER.identity.profileOf === "function") {
|
|
47
|
+
const p = PLAYER.identity.profileOf(u) || {};
|
|
48
|
+
return { email: String(p.email || u.email), name: String(p.name || ""), avatar: String(p.avatar || "") };
|
|
49
|
+
}
|
|
50
|
+
const meta = (u && u.user_metadata) || {};
|
|
51
|
+
return {
|
|
52
|
+
email: String(u.email),
|
|
53
|
+
name: String(u.name || meta.name || meta.full_name || ""),
|
|
54
|
+
avatar: String(u.avatar || meta.avatarUrl || meta.avatar_url || ""),
|
|
55
|
+
};
|
|
56
|
+
} catch { return null; }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = { init, adresseAppelant, lcMembre, cleAnonyme, profilDuJeton };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// EXTRAIT DE handler.js (refactor lot 2, 19/08/2026) — texte déplacé À L'IDENTIQUE. Reste à
|
|
2
|
+
// PLAT dans server/ (les gardes de forge ciblent server/*.js).
|
|
3
|
+
const { esc } = require("./texte");
|
|
4
|
+
|
|
5
|
+
let PLAYER = null;
|
|
6
|
+
const init = (ctx) => { PLAYER = ctx; };
|
|
7
|
+
|
|
8
|
+
// ⚠️ UNE MENTION DONT L'OBJET EST D'ÊTRE EXACTE NE DOIT PAS INVENTER UN EXPÉDITEUR.
|
|
9
|
+
//
|
|
10
|
+
// Le texte par défaut disait « transmise à son expéditeur ». Vrai pour un lien nominatif, faux
|
|
11
|
+
// pour un lien que PERSONNE n'a envoyé — la plaquette publique d'un programme, ouverte depuis une
|
|
12
|
+
// carte par un visiteur qui n'a reçu aucun message. Le défaut est antérieur au mode serveur à
|
|
13
|
+
// serveur ; c'est lui qui l'a rendu visible, puisqu'il crée précisément des liens sans
|
|
14
|
+
// destinataire NI créateur.
|
|
15
|
+
//
|
|
16
|
+
// La distinction ne demande aucune donnée nouvelle : c'est déjà la clé d'idempotence de ces
|
|
17
|
+
// liens-là. Signalé par le second hôte en regardant son propre écran — ce qu'aucun test ne fait.
|
|
18
|
+
function legalFooter({ tracked, sansExpediteur }) {
|
|
19
|
+
const L = PLAYER.legal;
|
|
20
|
+
const liens = [
|
|
21
|
+
L.legalUrl ? `<a href="${esc(L.legalUrl)}" target=_blank rel=noreferrer>Mentions légales</a>` : "",
|
|
22
|
+
L.privacyUrl ? `<a href="${esc(L.privacyUrl)}" target=_blank rel=noreferrer>Confidentialité</a>` : "",
|
|
23
|
+
// Obligation AGPL : l'accès au source se propose à qui UTILISE le logiciel, pas seulement à qui le distribue.
|
|
24
|
+
L.sourceUrl ? `<a href="${esc(L.sourceUrl)}" target=_blank rel=noreferrer>Code source</a>` : "",
|
|
25
|
+
].filter(Boolean).join("<span class=lgl-sep>·</span>");
|
|
26
|
+
// Un contexte qui ne fournit pas le second texte retombe sur le premier : rien ne casse chez un
|
|
27
|
+
// hôte qui n'a pas encore de liens sans expéditeur.
|
|
28
|
+
const texte = sansExpediteur ? (L.trackingNoticeAnonymous || L.trackingNotice) : L.trackingNotice;
|
|
29
|
+
const mesure = tracked ? `<span class=lgl-note>${esc(texte)}</span>` : "";
|
|
30
|
+
if (!liens && !mesure) return "";
|
|
31
|
+
return `<div class=lgl>${mesure}${liens ? `<span class=lgl-links>${liens}</span>` : ""}</div>`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const LEGAL_CSS = `
|
|
35
|
+
.lgl{position:fixed;left:0;right:0;bottom:0;z-index:5;display:flex;flex-wrap:wrap;gap:4px 10px;
|
|
36
|
+
align-items:center;justify-content:center;padding:5px 12px;font-size:10.5px;line-height:1.35;
|
|
37
|
+
color:rgba(255,255,255,.94);background:rgba(0,0,0,.66);backdrop-filter:blur(3px);pointer-events:none}
|
|
38
|
+
.lgl a{color:inherit;text-decoration:underline;text-underline-offset:2px;pointer-events:auto}
|
|
39
|
+
.lgl a:hover{color:#fff}
|
|
40
|
+
.lgl-sep{margin:0 6px;opacity:.8}
|
|
41
|
+
.lgl-note{opacity:1}
|
|
42
|
+
@media (max-width:640px){ .lgl{font-size:10px;padding:4px 10px} }
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
module.exports = { init, legalFooter, LEGAL_CSS };
|