Files
excalicloud/README.md
T

121 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# excalidraw-go
Excalidraw auto-hébergé avec un **gestionnaire de fichiers intégré** : arborescence de dossiers, création, renommage, déplacement (glisser-déposer ou menu), duplication, suppression (corbeille), recherche, import/export de fichiers `.excalidraw`, sauvegarde automatique côté serveur. Interface web unique, responsive (l'explorateur devient un tiroir sur mobile).
- **Backend** : Go 1.24, bibliothèque standard uniquement (aucune dépendance externe), API REST/JSON, stockage en fichiers `.excalidraw` sur un volume.
- **Frontend** : React 19 + composant officiel [`@excalidraw/excalidraw`](https://www.npmjs.com/package/@excalidraw/excalidraw) 0.18.1 (licence MIT), build Vite **embarqué dans le binaire Go** (`embed`).
- **Image** : multi-stage, finale en `distroless/static:nonroot` (~53 Mo), un seul conteneur, aucun accès Internet requis à l'exécution (polices Excalidraw servies localement).
## Architecture
```
Navigateur ──HTTP──▶ conteneur excalidraw-go (:8080, uid 65532, rootfs read-only)
├─ / SPA React + Excalidraw (embarquée dans le binaire)
├─ /api/... API fichiers (Go net/http)
└─ /healthz sonde
│
▼
volume /data
├─ projets/réseau/sdn.excalidraw ← fichiers JSON Excalidraw standards
├─ .library.excalidrawlib ← bibliothèque partagée
└─ .trash/<horodatage>/... ← éléments supprimés
```
Les schémas restent des fichiers `.excalidraw` standards : ils s'ouvrent tels quels dans excalidraw.com, VS Code (extension Excalidraw) ou Obsidian, et se sauvegardent/versionnent comme n'importe quel répertoire (rsync, restic, git...).
## Démarrage
```bash
cp .env.example .env # optionnel : port, authentification Basic
docker compose up -d --build
# http://localhost:8080
```
| Variable | Défaut | Rôle |
|-----------------|----------|-------------------------------------------------------------------|
| `LISTEN_ADDR` | `:8080` | Adresse d'écoute |
| `DATA_DIR` | `/data` | Racine des schémas |
| `MAX_BODY_MB` | `50` | Taille max d'un schéma (images embarquées en base64 comprises) |
| `AUTH_USER` / `AUTH_PASSWORD` | vide | Active une authentification HTTP Basic si les deux sont définis |
Bind mount au lieu du volume nommé : `- ./data:/data` puis `chown -R 65532:65532 ./data`.
## Utilisation
- **+ Schéma / + Dossier** : création dans le dossier courant.
- **⋯** sur une ligne : renommer, déplacer, dupliquer, télécharger, supprimer.
- **Glisser-déposer** (souris) d'un élément sur un dossier, sur `..` ou sur le fil d'Ariane pour le déplacer. Sur écran tactile : menu ⋯ → *Déplacer…*
- **Importer** : envoie un ou plusieurs `.excalidraw` locaux dans le dossier courant.
- **Sauvegarde** : automatique 1,5 s après la dernière modification, immédiate avec `Ctrl/Cmd+S`. Le statut (Modifié / Enregistrement… / Enregistré) est affiché dans la barre du haut.
- **Lien direct** : l'URL `http://hôte:8080/#/dossier/schema.excalidraw` ouvre le schéma.
- **Bibliothèque** Excalidraw : persistée côté serveur, partagée entre tous les schémas.
### Concurrence
Chaque lecture renvoie un `ETag` (SHA-256 tronqué du contenu) ; chaque sauvegarde l'envoie en `If-Match`. Si le fichier a été modifié ailleurs (autre onglet, autre poste), le serveur répond `412` et l'interface propose d'écraser ou de recharger. Il n'y a pas d'édition collaborative temps réel (voir *Évolutions*).
## API
Tous les chemins sont relatifs à `DATA_DIR`, en notation `/`. Les composants commençant par `.` sont refusés, et les liens symboliques sortant de la racine sont bloqués.
| Méthode | Route | Corps / paramètres | Réponse |
|---|---|---|---|
| GET | `/api/list?path=dir` | | `[Entry]` (dossiers puis schémas) |
| GET | `/api/search?q=texte` | | `[Entry]` (200 max) |
| GET | `/api/file?path=x.excalidraw` | | JSON du schéma + `ETag` |
| PUT | `/api/file?path=x.excalidraw` | JSON ; `If-Match: <etag>` ou `If-None-Match: *` (création) | `Entry` + `ETag` ; `409` existe, `412` conflit |
| POST | `/api/folder` | `{"path":"a/b"}` | `Entry` |
| POST | `/api/move` | `{"from":"a.excalidraw","to":"d/b.excalidraw"}` | `Entry` (renommage / déplacement, fichier ou dossier) |
| POST | `/api/copy` | `{"from":"…","to":"…"}` | `Entry` |
| DELETE | `/api/entry?path=x[&recursive=true]` | | `204` (déplacé dans `.trash/`) |
| GET/PUT | `/api/library` | bibliothèque `excalidrawlib` | |
| GET | `/healthz` | | `ok` (hors authentification) |
`Entry` = `{"name","path","isDir","size","modTime"}`.
Exemple d'automatisation (génération de schémas par script) :
```bash
curl -X PUT 'http://localhost:8080/api/file?path=auto/inventaire.excalidraw' \
-H 'If-None-Match: *' --data-binary @inventaire.excalidraw
```
## Développement
```bash
# Terminal 1 : API
go run ./cmd/server # DATA_DIR=./data par défaut
# Terminal 2 : frontend avec rechargement à chaud (proxy /api -> :8080)
cd frontend && npm install && npm run dev
# Tests et build complet hors Docker
go test ./...
cd frontend && npm run build && cd .. && go build -o server ./cmd/server
```
Arborescence :
```
cmd/server/main.go serveur HTTP, SPA embarquée, auth Basic, healthcheck
internal/storage/ accès disque : sécurité des chemins, écriture atomique, ETag, corbeille
internal/api/ routes REST
web/embed.go embarque web/dist (produit par le build Vite)
frontend/src/App.tsx éditeur Excalidraw, sauvegarde auto, conflits, bibliothèque
frontend/src/Explorer.tsx gestionnaire de fichiers
```
## Sécurité
- Conteneur non-root, rootfs en lecture seule, `cap_drop: ALL`, `no-new-privileges`.
- L'authentification Basic intégrée suffit pour un usage local ; en exposition réseau, placer le service derrière un reverse proxy TLS (Traefik, Caddy, nginx) avec SSO (Authelia, oauth2-proxy, Authentik) et laisser `AUTH_*` vides.
- Écritures atomiques (fichier temporaire + `fsync` + `rename`) : pas de schéma tronqué en cas d'arrêt brutal.
- Aucune suppression physique : la purge de `.trash/` est à la charge de l'exploitant (ex. `find /data/.trash -mindepth 1 -maxdepth 1 -mtime +30 -exec rm -rf {} +`).
## Limites et évolutions possibles
- **Collaboration temps réel** : nécessiterait le serveur [excalidraw-room](https://github.com/excalidraw/excalidraw-room) (WebSocket) et la logique de réconciliation (`reconcileElements`) côté client.
- **Miniatures** dans l'explorateur : générables côté client via `exportToSvg` et stockées à côté du fichier.
- **Historique de versions** : commit git automatique dans `DATA_DIR` à chaque sauvegarde, ou snapshots du volume (ZFS/Btrfs).
- **Multi-utilisateurs avec espaces privés** : identité transmise par le reverse proxy (`X-Forwarded-User`) et racine par utilisateur.
- Les schémas avec beaucoup d'images embarquées sont volumineux (base64 dans le JSON) ; ajuster `MAX_BODY_MB`.