discovery-media-player 0.1.26 → 0.1.27
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/context/standalone.js +14 -0
- package/package.json +1 -1
- package/server/handler.js +43 -9
package/context/standalone.js
CHANGED
|
@@ -457,6 +457,20 @@ function createStandaloneContext(env = process.env) {
|
|
|
457
457
|
supabasePublishableKey: env.SUPABASE_PUBLISHABLE_KEY || "",
|
|
458
458
|
mapsKey: env.GOOGLE_MAPS_API_KEY || "",
|
|
459
459
|
extraFrameAncestors: String(env.DOC_FRAME_ANCESTORS || "").split(/\s+/).filter(Boolean),
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Clé de `localStorage` sous laquelle VOTRE application range la session de ses membres.
|
|
463
|
+
*
|
|
464
|
+
* ⚠️ Vide par défaut, donc la détection des membres est inactive : aucun membre reconnu,
|
|
465
|
+
* aucune usurpation possible. Ce paquet portait `3dd-supabase-auth` en dur — la clé d'un seul
|
|
466
|
+
* hôte, qui ne trouvait rien chez tous les autres, et qui depuis 0.1.25 porte une propriété
|
|
467
|
+
* de sécurité : c'est par elle que l'appartenance se prouve.
|
|
468
|
+
*
|
|
469
|
+
* ⚠️ Et ça ne marche QUE si votre application et le player partagent la même origine. Deux
|
|
470
|
+
* sous-domaines, ce sont deux `localStorage` : aucune valeur ici n'y changera rien. C'est une
|
|
471
|
+
* transition — le bon mécanisme est que l'hôte injecte son membre au rendu de la page.
|
|
472
|
+
*/
|
|
473
|
+
hostAuthStorageKey: String(env.PLAYER_HOST_AUTH_STORAGE_KEY || "").trim(),
|
|
460
474
|
// ⚠️ Un BOOLÉEN, jamais l'URL. La carte d'identité doit pouvoir dire « un émetteur distinct
|
|
461
475
|
// est configuré » sans dire lequel : c'est le dernier endroit où un hôte devait deviner.
|
|
462
476
|
// `host-auth` dit que le code SAIT faire la séparation ; ceci dit qu'elle est POSÉE. Sans
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
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",
|
package/server/handler.js
CHANGED
|
@@ -415,7 +415,13 @@ var Live=(function(){
|
|
|
415
415
|
var pb=document.getElementById('presBtn');if(pb)pb.style.display='inline-flex';
|
|
416
416
|
var cb=document.getElementById('chatBtn');if(cb)cb.style.display='inline-flex';var _fb=document.getElementById('chatFab');if(_fb)_fb.classList.add('on');
|
|
417
417
|
wire();history();
|
|
418
|
-
try{sb=window.supabase.createClient(LIVECFG.supaUrl,LIVECFG.supaKey,{realtime:{params:{eventsPerSecond:10}}
|
|
418
|
+
try{sb=window.supabase.createClient(LIVECFG.supaUrl,LIVECFG.supaKey,{realtime:{params:{eventsPerSecond:10}},
|
|
419
|
+
// ⚠️ UNE CLÉ DÉCLARÉE, PAS CELLE PAR DÉFAUT. Ce client vivra un jour une session anonyme
|
|
420
|
+
// (canal Realtime prive). S'il ecrit sous la cle par defaut et que l'application de l'hote
|
|
421
|
+
// l'utilise aussi sur la meme origine, la session anonyme ECRASE celle du membre connecte.
|
|
422
|
+
// Chez nous les deux cles different deja, mais par heureux hasard : le declarer rend
|
|
423
|
+
// intentionnel ce qui n'etait qu'une consequence, et une topologie peut changer.
|
|
424
|
+
auth:{storageKey:LIVECFG.liveAuthKey||'dmp-live-auth',persistSession:true,autoRefreshToken:true}});
|
|
419
425
|
ch=sb.channel('plive-'+slug,{config:{presence:{key:(me.email||me.name||'x')+':'+MYID}}});
|
|
420
426
|
ch.on('presence',{event:'sync'},function(){renderPres(ch.presenceState());});
|
|
421
427
|
// Plus d'abonnement à la table des messages : elle n'est plus publiée ni lisible
|
|
@@ -503,9 +509,9 @@ var Live=(function(){
|
|
|
503
509
|
// Membre de l'équipe reconnu via la session app (MÊME ORIGINE, localStorage) → avatar + nom auto.
|
|
504
510
|
// Le jeton d'acces de la session locale, quand il y en a une. C'est la SEULE chose qui prouve au
|
|
505
511
|
// serveur qu'on est un membre ; 'member:true' dans la page ne prouve rien, il ne sert qu'a l'affichage.
|
|
506
|
-
function accessToken(){try{var raw=localStorage.getItem('
|
|
512
|
+
function accessToken(){try{var raw=localStorage.getItem(LIVECFG.hostAuthKey||'');if(!raw)return '';var s=JSON.parse(raw);
|
|
507
513
|
return String((s&&(s.access_token||(s.currentSession&&s.currentSession.access_token)||(s.session&&s.session.access_token)))||'');}catch(e){return '';}}
|
|
508
|
-
function detectMember(){try{var raw=localStorage.getItem('
|
|
514
|
+
function detectMember(){try{var raw=localStorage.getItem(LIVECFG.hostAuthKey||'');if(!raw)return null;var s=JSON.parse(raw);var u=s&&(s.user||(s.currentSession&&s.currentSession.user)||(s.session&&s.session.user));if(u&&u.email){var m=u.user_metadata||{};return{name:m.name||u.email,email:u.email,avatar:m.avatarUrl||'',member:true,role:'viewer'};}}catch(e){}return null;}
|
|
509
515
|
return {connect:connect,disconnect:disconnect,detectMember:detectMember,sendMap:sendMap,onMap:onMap,sendState:sendState,onState:onState};
|
|
510
516
|
})();`;
|
|
511
517
|
|
|
@@ -1243,6 +1249,34 @@ function adresseAppelant(req) {
|
|
|
1243
1249
|
* Le jeton d'accès, lui, est vérifié par l'hôte. Absent ou invalide : pas membre. Jamais de repli
|
|
1244
1250
|
* sur ce que l'appelant affirme, sinon la vérification ne sert qu'aux honnêtes.
|
|
1245
1251
|
*/
|
|
1252
|
+
// ── Clés de `localStorage`, et pourquoi ce ne sont plus des constantes ─────────────────────────
|
|
1253
|
+
//
|
|
1254
|
+
// ⚠️ `3dd-supabase-auth` — la clé de session du studio 3D Discovery — était écrite EN DUR à cinq
|
|
1255
|
+
// endroits de ce paquet open source. Conséquence pour tout autre hôte : `detectMember()` et
|
|
1256
|
+
// `accessToken()` ne trouvent jamais rien, donc AUCUN de ses membres n'est reconnu comme tel. La
|
|
1257
|
+
// séparation des populations interne/externe, que ce produit vend, ne fonctionnait que chez nous.
|
|
1258
|
+
//
|
|
1259
|
+
// ⚠️ Et depuis 0.1.25 cette clé porte une propriété de sécurité : c'est par elle que l'appartenance
|
|
1260
|
+
// se prouve. Une constante d'un hôte devenue porteuse pour tous les autres.
|
|
1261
|
+
//
|
|
1262
|
+
// Défaut VIDE, et c'est délibéré : sans clé déclarée, la détection est simplement inactive — aucun
|
|
1263
|
+
// membre, donc aucune usurpation. Un défaut à `3dd-supabase-auth` aurait gardé notre instance en
|
|
1264
|
+
// marche en laissant le défaut de conception intact, et le prochain hôte l'aurait découvert comme
|
|
1265
|
+
// ADV : en constatant que ses statistiques ne séparent rien.
|
|
1266
|
+
//
|
|
1267
|
+
// ⚠️ CE N'EST QU'UNE TRANSITION. Lire le `localStorage` d'une autre application ne peut PAS marcher
|
|
1268
|
+
// quand les origines diffèrent — l'instance ADV est sur `doc.adnfamily.com` et son application sur
|
|
1269
|
+
// `app.adnfamily.com` : deux `localStorage`, aucune configuration n'y changera rien. Le bon
|
|
1270
|
+
// mécanisme est que l'hôte INJECTE son membre au rendu de la page, comme il injecte déjà sa marque.
|
|
1271
|
+
// Tracé dans docs/AUDIT-2026-08-14-SUIVI.md.
|
|
1272
|
+
function cleSessionHote() {
|
|
1273
|
+
return String((PLAYER.config && PLAYER.config.hostAuthStorageKey) || "");
|
|
1274
|
+
}
|
|
1275
|
+
// La session du client Realtime du player, et l'identité d'un invité : à lui, sous son nom. Elles
|
|
1276
|
+
// ne dépendent d'aucun hôte, donc elles restent des constantes — mais plus des constantes d'AUTRUI.
|
|
1277
|
+
const CLE_SESSION_PLAYER = "dmp-live-auth";
|
|
1278
|
+
const CLE_INVITE = "dmp-present-me";
|
|
1279
|
+
|
|
1246
1280
|
async function membreVerifie(req) {
|
|
1247
1281
|
try {
|
|
1248
1282
|
const u = await PLAYER.identity.verifyToken((req.headers && req.headers.authorization) || "");
|
|
@@ -1796,7 +1830,7 @@ ${LEGAL_CSS}
|
|
|
1796
1830
|
<script nonce="${nonce}">${PLAYER_BROWSER_JS}</script>
|
|
1797
1831
|
<script nonce="${nonce}" src="${PDFJS}/pdf.min.js"></script>
|
|
1798
1832
|
${preview ? `<script nonce="${nonce}" src="${SUPAJS}"></script>
|
|
1799
|
-
<script nonce="${nonce}">var LIVECFG={supaUrl:${JSON.stringify(share.supa_url || "")},supaKey:${JSON.stringify(share.supa_key || "")}};var GMAPS_KEY=${JSON.stringify((PLAYER.config && PLAYER.config.mapsKey) || "" || "")};${LIVE_JS}
|
|
1833
|
+
<script nonce="${nonce}">var LIVECFG={supaUrl:${JSON.stringify(share.supa_url || "")},supaKey:${JSON.stringify(share.supa_key || "")},hostAuthKey:${JSON.stringify(cleSessionHote())},liveAuthKey:${JSON.stringify(CLE_SESSION_PLAYER)},guestKey:${JSON.stringify(CLE_INVITE)}};var GMAPS_KEY=${JSON.stringify((PLAYER.config && PLAYER.config.mapsKey) || "" || "")};${LIVE_JS}
|
|
1800
1834
|
${MAP_JS}</script>` : ""}
|
|
1801
1835
|
<script nonce="${nonce}">
|
|
1802
1836
|
(function(){
|
|
@@ -1871,7 +1905,7 @@ ${LEGAL_CSS}
|
|
|
1871
1905
|
var PRES=null, _pushT=null, _hbIv=0;
|
|
1872
1906
|
// JWT de la session app (MÊME ORIGINE, localStorage) → autorise le rattachement de la présentation au membre
|
|
1873
1907
|
// (reprise / liste / transfert) et les actions authentifiées (reclaim).
|
|
1874
|
-
function appToken(){ try{ var raw=localStorage.getItem('
|
|
1908
|
+
function appToken(){ try{ var raw=localStorage.getItem(LIVECFG.hostAuthKey||''); if(!raw)return''; var s=JSON.parse(raw); var t=(s&&(s.access_token||(s.currentSession&&s.currentSession.access_token)||(s.session&&s.session.access_token)))||''; return t; }catch(e){ return ''; } }
|
|
1875
1909
|
function ctlKey(slug){ return '3dd-pres-ctl-'+slug; }
|
|
1876
1910
|
function saveCtl(slug,control){ try{ localStorage.setItem(ctlKey(slug),control); }catch(e){} }
|
|
1877
1911
|
function clearCtl(slug){ try{ localStorage.removeItem(ctlKey(slug)); }catch(e){} }
|
|
@@ -2314,7 +2348,7 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
2314
2348
|
})();
|
|
2315
2349
|
</script>
|
|
2316
2350
|
<script nonce="${nonce}">
|
|
2317
|
-
var LIVECFG={supaUrl:${JSON.stringify(supaUrl || "")},supaKey:${JSON.stringify(supaKey || "")}};var GMAPS_KEY=${JSON.stringify((PLAYER.config && PLAYER.config.mapsKey) || "" || "")};
|
|
2351
|
+
var LIVECFG={supaUrl:${JSON.stringify(supaUrl || "")},supaKey:${JSON.stringify(supaKey || "")},hostAuthKey:${JSON.stringify(cleSessionHote())},liveAuthKey:${JSON.stringify(CLE_SESSION_PLAYER)},guestKey:${JSON.stringify(CLE_INVITE)}};var GMAPS_KEY=${JSON.stringify((PLAYER.config && PLAYER.config.mapsKey) || "" || "")};
|
|
2318
2352
|
${LIVE_JS}
|
|
2319
2353
|
${MAP_JS}
|
|
2320
2354
|
(function(){
|
|
@@ -2337,19 +2371,19 @@ function presentHtml(pres, nonce, logoUrl, supaUrl, supaKey) {
|
|
|
2337
2371
|
// « Reprendre la main » : si le membre connecté (même origine) devient propriétaire de CETTE présentation
|
|
2338
2372
|
// (après un transfert), on affiche un bouton pour ouvrir la visionneuse en pilotage — sinon on ne peut pas
|
|
2339
2373
|
// piloter depuis la page audience.
|
|
2340
|
-
function appTok(){ try{ var raw=localStorage.getItem('
|
|
2374
|
+
function appTok(){ try{ var raw=localStorage.getItem(LIVECFG.hostAuthKey||''); if(!raw)return''; var s=JSON.parse(raw); return (s&&(s.access_token||(s.currentSession&&s.currentSession.access_token)||(s.session&&s.session.access_token)))||''; }catch(e){return'';} }
|
|
2341
2375
|
function checkOwner(){ var tk=appTok(); if(!tk)return; fetch('/api/doc',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tk},body:JSON.stringify({action:'present-list'})}).then(function(r){return r.json();}).then(function(d){ var mine=false; if(d&&d.presentations){ for(var i=0;i<d.presentations.length;i++){ if(d.presentations[i].slug===slug&&d.presentations[i].mine){mine=true;break;} } } var b=document.getElementById('takeOver'); if(b)b.style.display=mine?'inline-flex':'none'; }).catch(function(){}); }
|
|
2342
2376
|
function startOwnerWatch(){ var b=document.getElementById('takeOver'); if(b&&!b._w){b._w=1;b.addEventListener('click',function(){ location.href='/app/documents?resume='+encodeURIComponent(slug); });} checkOwner(); setInterval(checkOwner,12000); }
|
|
2343
2377
|
var me=Live.detectMember();
|
|
2344
2378
|
if(me){ Live.connect(slug, me); startOwnerWatch(); return; }
|
|
2345
|
-
var saved=null; try{ saved=JSON.parse(localStorage.getItem('
|
|
2379
|
+
var saved=null; try{ saved=JSON.parse(localStorage.getItem(LIVECFG.guestKey||'dmp-present-me')||'null'); }catch(e){}
|
|
2346
2380
|
if(saved&&saved.name){ Live.connect(slug, saved); return; }
|
|
2347
2381
|
// Externe : on demande le nom pour participer.
|
|
2348
2382
|
var o=document.createElement('div'); o.className='join';
|
|
2349
2383
|
o.innerHTML='<div class=join-card><h4>Rejoindre la présentation</h4><p>Votre nom pour participer à la discussion.</p><input id=jName placeholder="Votre nom" maxlength=60 autocomplete=name><input id=jMail placeholder="Email (facultatif)" maxlength=120 autocomplete=email><button id=jGo>Rejoindre</button></div>';
|
|
2350
2384
|
document.body.appendChild(o);
|
|
2351
2385
|
var n=o.querySelector('#jName'); try{ n.focus(); }catch(e){}
|
|
2352
|
-
function go(){ var name=(n.value||'').trim()||'Invité'; var email=(o.querySelector('#jMail').value||'').trim(); var me2={name:name,email:email,avatar:'',member:false,role:'viewer'}; try{ localStorage.setItem('
|
|
2386
|
+
function go(){ var name=(n.value||'').trim()||'Invité'; var email=(o.querySelector('#jMail').value||'').trim(); var me2={name:name,email:email,avatar:'',member:false,role:'viewer'}; try{ localStorage.setItem(LIVECFG.guestKey||'dmp-present-me',JSON.stringify(me2)); }catch(e){} o.parentNode&&o.parentNode.removeChild(o); Live.connect(slug, me2); }
|
|
2353
2387
|
o.querySelector('#jGo').addEventListener('click',go);
|
|
2354
2388
|
n.addEventListener('keydown',function(e){ if(e.key==='Enter') go(); });
|
|
2355
2389
|
})();
|