discovery-media-player 0.1.23 → 0.1.24
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/storage.js +23 -14
- package/package.json +1 -1
- package/server/handler.js +7 -1
- package/server/shares.js +4 -0
package/context/storage.js
CHANGED
|
@@ -142,10 +142,24 @@ const TYPES = {
|
|
|
142
142
|
* des premières pages avant la fin du téléchargement.
|
|
143
143
|
*/
|
|
144
144
|
async function readLocal(cible, range) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
// ⚠️ ON OUVRE D'ABORD, ON INTERROGE ENSUITE. `stat(chemin)` puis `open(chemin)` sont deux
|
|
146
|
+
// résolutions du même NOM à deux instants : entre les deux, le fichier peut avoir été remplacé.
|
|
147
|
+
// On lisait alors les octets du NOUVEAU fichier avec la taille de l'ANCIEN — un `Content-Range`
|
|
148
|
+
// qui ne décrit pas ce qu'il transporte, donc un document faux sans qu'aucune erreur ne sorte.
|
|
149
|
+
// Un descripteur, lui, désigne un objet et non un nom : `fh.stat()` parle du même fichier que
|
|
150
|
+
// `fh.read()`, quoi qu'il advienne du chemin entre-temps.
|
|
151
|
+
let fh;
|
|
152
|
+
try { fh = await fsp.open(cible, "r"); } catch { return null; }
|
|
153
|
+
try {
|
|
154
|
+
const stat = await fh.stat();
|
|
155
|
+
if (!stat.isFile()) return null;
|
|
156
|
+
return await lireDepuis(fh, stat, cible, range);
|
|
157
|
+
} finally {
|
|
158
|
+
await fh.close();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
148
161
|
|
|
162
|
+
async function lireDepuis(fh, stat, cible, range) {
|
|
149
163
|
const type = TYPES[path.extname(cible).toLowerCase()] || "application/octet-stream";
|
|
150
164
|
const total = stat.size;
|
|
151
165
|
const m = /^bytes=(\d*)-(\d*)$/.exec(String(range || ""));
|
|
@@ -159,17 +173,12 @@ async function readLocal(cible, range) {
|
|
|
159
173
|
statut = 206;
|
|
160
174
|
}
|
|
161
175
|
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (statut === 206) entetes["content-range"] = `bytes ${debut}-${fin}/${total}`;
|
|
169
|
-
return reponse(statut, entetes, corps);
|
|
170
|
-
} finally {
|
|
171
|
-
await fh.close();
|
|
172
|
-
}
|
|
176
|
+
const buf = Buffer.alloc(fin - debut + 1);
|
|
177
|
+
const { bytesRead } = await fh.read(buf, 0, buf.length, debut);
|
|
178
|
+
const corps = bytesRead === buf.length ? buf : buf.subarray(0, bytesRead);
|
|
179
|
+
const entetes = { "content-type": type };
|
|
180
|
+
if (statut === 206) entetes["content-range"] = `bytes ${debut}-${fin}/${total}`;
|
|
181
|
+
return reponse(statut, entetes, corps);
|
|
173
182
|
}
|
|
174
183
|
|
|
175
184
|
function reponse(status, entetes, corps) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
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
|
@@ -2755,7 +2755,13 @@ async function handler(req, res) {
|
|
|
2755
2755
|
if (body.action === "reshare") {
|
|
2756
2756
|
const mail = String(body.email || "").trim().toLowerCase();
|
|
2757
2757
|
const j = (status, obj) => { res.statusCode = status; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify(obj)); };
|
|
2758
|
-
|
|
2758
|
+
// ⚠️ LA LONGUEUR AVANT LE MOTIF — et ici ce n'est pas de la prudence, c'est mesuré :
|
|
2759
|
+
// `.+@.+\..+` reprend à chaque position de départ, coût 49 ms sur 10 000 caractères,
|
|
2760
|
+
// 3 900 ms sur 100 000. Une seule requête, sur une route ouverte au lecteur, bloquait la
|
|
2761
|
+
// boucle d'événements quatre secondes — l'instance entière, pas seulement l'appelant.
|
|
2762
|
+
// Le débit (8/h par IP) ne protégeait rien : il est vérifié APRÈS, deux lignes plus bas.
|
|
2763
|
+
// 254 est le maximum d'une adresse (RFC 5321) : au-delà ce n'est pas « long », c'est faux.
|
|
2764
|
+
if (mail.length > 254 || !/.+@.+\..+/.test(mail)) return j(400, { ok: false, error: "email" });
|
|
2759
2765
|
const ip = adresseAppelant(req) || "anon";
|
|
2760
2766
|
const allowed = await PLAYER.limits.allow(`reshare:${ip}`, 8, 3600);
|
|
2761
2767
|
if (!allowed) return j(429, { ok: false, error: "rate", message: "Trop de partages, réessayez plus tard." });
|
package/server/shares.js
CHANGED
|
@@ -58,6 +58,10 @@ async function createReshare(parentSlug, { email, name }) {
|
|
|
58
58
|
// rapport qui a mené ici.
|
|
59
59
|
// • `allow_download` (défaut `true`) — le bouton Télécharger revenait sur un document où il
|
|
60
60
|
// avait été refusé.
|
|
61
|
+
// ⚠️ À ne pas prendre pour une protection : c'est une PRÉFÉRENCE D'AFFICHAGE. Le lecteur qui
|
|
62
|
+
// voit le document en a déjà les octets ; masquer le bouton retire une commodité, pas un
|
|
63
|
+
// accès. Un document qu'on ne veut pas voir sortir ne doit pas être partagé, ou doit l'être
|
|
64
|
+
// derrière `require_auth` — celui-là, lui, décide de qui obtient les octets.
|
|
61
65
|
// • `brand_key` — la marque se perdait à l'endroit exact où le document commence à circuler :
|
|
62
66
|
// le lecteur d'un document VALONEUF transmettait un lien qui s'ouvre sous une autre marque.
|
|
63
67
|
//
|