Verified Commit 73c5f556 authored by Kourser's avatar Kourser
Browse files

Toile : les polices viennent de l'instance, plus d'un dépôt tiers

Trouvé en éprouvant la politique de contenu dans un navigateur, ce qu'aucune
des garanties ne peut voir — elles passent par « inject », qui ne rend aucune
page.

À défaut de « window.EXCALIDRAW_ASSET_PATH », Excalidraw demandait ses polices
à esm.sh au moment où l'écran s'ouvrait. Deux raisons de refuser, et la
première est la plus grave : une cellule repliée derrière un pare-feu qui
bloque tout, ou coupée de l'internet pendant qu'on chiffre son système
d'information, ouvrait une toile sans polices — c'est précisément la situation
pour laquelle le produit existe. Ensuite, chaque ouverture annonçait à un tiers
qu'une organisation s'en servait, avec son adresse IP.

Les polices sont livrées dans le paquet : il suffisait de les servir. Elles
sont rapatriées avant la construction, et la politique de contenu ne les
autorise plus que depuis notre propre origine.

Xiaolai est écartée : treize des quatorze mégaoctets à elle seule, pour une
police que le système fournit déjà. Le texte en idéogrammes s'affiche donc avec
celle du système. Ce choix a un coût visible — le navigateur consigne un refus
par sous-ensemble de cette famille à l'ouverture de la toile.

Co-Authored-By: Claude (RCA)
Signed-off-by: default avatarJordan Grossemy <jordan.grossemy@rca.fr>
parent 85ebfdbc
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -17,3 +17,7 @@ site/build/
site/.docusaurus/
public/
.ci/

# Polices d'Excalidraw, rapatriées depuis node_modules par
# outils/polices-excalidraw.mjs : construites, pas versionnées.
apps/web/public/
+4 −3
Original line number Diff line number Diff line
@@ -5,9 +5,10 @@
  "license": "AGPL-3.0-or-later",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "apercu": "vite preview"
    "dev": "pnpm polices && vite",
    "build": "pnpm polices && vite build",
    "apercu": "vite preview",
    "polices": "node ../../outils/polices-excalidraw.mjs"
  },
  "dependencies": {
    "@excalidraw/excalidraw": "^0.18.1",
+18 −0
Original line number Diff line number Diff line
@@ -9,6 +9,24 @@
 * fichiers : la licence demande qu'il soit joint au programme et lisible, pas
 * qu'il soit recopié jusqu'à noyer le code qu'il protège.
 */
/**
 * Où la toile va chercher ses polices : chez nous, et nulle part ailleurs.
 *
 * Sans ce réglage, Excalidraw les demande à esm.sh au moment où l'écran
 * s'ouvre. Une cellule coupée du réseau — ce pour quoi ce produit existe —
 * ouvrirait une toile sans polices, et chaque ouverture annoncerait à un tiers
 * qu'une organisation s'en sert. Les fichiers sont copiés dans l'interface par
 * outils/polices-excalidraw.mjs, avant la construction.
 *
 * Posé ici plutôt que dans l'écran de la toile : les imports d'un module
 * s'évaluent avant son corps, et cet écran importe Excalidraw. Ici, le réglage
 * précède le chargement paresseux de son morceau.
 */
declare global {
  interface Window { EXCALIDRAW_ASSET_PATH?: string }
}
window.EXCALIDRAW_ASSET_PATH = "/excalidraw/";

import { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+89 −0
Original line number Diff line number Diff line
#!/usr/bin/env node
/**
 * Rapatrie les polices d'Excalidraw dans l'interface — Kastell.
 *
 *   node outils/polices-excalidraw.mjs      (appelé avant « vite build » et « vite »)
 *
 * Pourquoi cet outil existe
 *   Excalidraw ne trouve pas ses polices dans le paquet qu'on installe : à
 *   défaut de « window.EXCALIDRAW_ASSET_PATH », il les demande à esm.sh, un
 *   dépôt public tiers, au moment où l'écran s'ouvre.
 *
 *   Deux raisons de refuser cela, et la première est la plus grave :
 *
 *     — Un outil de crise doit tenir quand le réseau ne tient plus. Une cellule
 *       repliée derrière un pare-feu qui bloque tout, ou coupée de l'internet
 *       pendant qu'on chiffre son système d'information, ouvrirait une toile
 *       sans polices. C'est précisément la situation pour laquelle le produit
 *       existe.
 *     — Chaque ouverture de la toile annonçait à un tiers qu'une organisation
 *       s'en servait, avec son adresse IP. Personne ne l'avait demandé.
 *
 *   Les polices sont livrées dans le paquet : il suffit de les servir. La
 *   politique de contenu de l'application ne les autorise donc que depuis notre
 *   propre origine — voir apps/api/src/serveur.ts.
 *
 * Sur l'absence du chinois
 *   Xiaolai pèse treize des quatorze mégaoctets à lui seul. Le reste — huit
 *   familles latines et cyrilliques — tient dans un demi-mégaoctet. Embarquer
 *   treize mégaoctets dans chaque image pour une police que le système fournit
 *   déjà serait un mauvais échange ; le texte en idéogrammes s'affiche donc avec
 *   la police du système, ce qu'aucun navigateur moderne n'a de mal à faire.
 *
 * Licence : CC0 1.0 Universal, comme les autres outils de ce dossier.
 */

import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";

const racine = fileURLToPath(new URL("..", import.meta.url));
const destination = join(racine, "apps/web/public/excalidraw/fonts");

/** Treize mégaoctets pour une police que le système fournit — voir en-tête. */
const ECARTEES = new Set(["Xiaolai"]);

const require = createRequire(join(racine, "apps/web/package.json"));
let source;
try {
  // On demande au résolveur où le paquet se trouve réellement : sous pnpm, il
  // n'est pas là où un chemin écrit à la main l'attendrait.
  source = join(dirname(require.resolve("@excalidraw/excalidraw")), "fonts");
} catch {
  console.error("Excalidraw n'est pas installé. Lancez « pnpm install » d'abord.");
  process.exit(1);
}

if (!existsSync(source)) {
  console.error(
    `Polices introuvables dans ${source}.\n` +
    "La disposition du paquet a changé : cet outil doit être relu avant que\n" +
    "l'interface reparte chercher ses polices chez un tiers.");
  process.exit(1);
}

rmSync(destination, { recursive: true, force: true });
mkdirSync(destination, { recursive: true });

let familles = 0;
let fichiers = 0;
for (const entree of readdirSync(source)) {
  if (ECARTEES.has(entree)) continue;
  const chemin = join(source, entree);
  if (!statSync(chemin).isDirectory()) {
    cpSync(chemin, join(destination, entree));
    fichiers += 1;
    continue;
  }
  cpSync(chemin, join(destination, entree), { recursive: true });
  familles += 1;
  fichiers += readdirSync(join(destination, entree)).length;
}

console.log(
  `  Polices Excalidraw : ${familles} famille(s), ${fichiers} fichier(s) `
  + `→ apps/web/public/excalidraw/fonts`);
if (ECARTEES.size > 0) {
  console.log(`  Écartée(s), servie(s) par le système : ${[...ECARTEES].join(", ")}`);
}