diff --git a/.gitignore b/.gitignore index b9191f2..2b7f95a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ output/ __pycache__/ src/config.json +src/config.json.bak diff --git a/Makefile b/Makefile index 371def6..5045938 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,13 @@ include .env PORT ?= 8000 -.PHONY: help install config serve sync fetch export deploy +.PHONY: help install config demo serve sync fetch export deploy help: @echo "Cibles disponibles :" @echo " make install - installe les dependances necessaires aux scripts (sshpass)" @echo " make config - cree .env et src/config.json si absents" + @echo " make demo - installe la config de demo (src/config.demo.json) et lance l'apercu" @echo " make serve - lance l'apercu + l'editeur en local (PORT=$(PORT))" @echo " make sync - recupere dans src/config.json les apps YunoHost manquantes" @echo " make fetch - recupere src/config.json depuis le serveur (config actuellement deployee)" @@ -33,6 +34,15 @@ config: echo "Remplace-le par tes propres apps, ou copie ton config.json personnel ici."; \ fi +demo: + @if [ -f src/config.json ] && ! cmp -s src/config.json src/config.demo.json; then \ + cp src/config.json src/config.json.bak; \ + echo "src/config.json existant sauvegarde dans src/config.json.bak"; \ + fi + @cp src/config.demo.json src/config.json + @echo "src/config.json remplace par la config de demo (src/config.demo.json)." + HOMEPAGE_DEMO=1 $(MAKE) serve + serve: scripts/start.sh $(PORT) diff --git a/README.md b/README.md index cf5780c..8ef037c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ src/ sources de la page (index.html, style.css, app.js) et de l'éditeur visuel (editor.html/css/js) + config.json (tes données, ignoré par git — voir ci-dessous) + config.example.json (exemple versionné, point de départ) + + config.demo.json / catalog.demo.json (données de démo, + utilisées par "make demo" — voir ci-dessous) scripts/ scripts d'outillage (preview, export, sync, déploiement) output/ généré par "make export" — c'est ce dossier qui est envoyé sur le serveur YunoHost (ignoré par git) @@ -47,6 +49,34 @@ Puis : make serve # lance l'apercu + l'editeur en local ``` +## Faire une démo + +```bash +make demo +``` + +Remplace `src/config.json` par un jeu de données d'exemple +(`src/config.demo.json`) — une vingtaine d'applications réalistes réparties +dans plusieurs groupes (Cloud & Fichiers, Média, Communication, +Productivité, Domotique, Administration), avec vraies icônes, couleurs et +initiales — puis lance `make serve`. + +- Si `src/config.json` existait déjà et différait de la démo, il est + d'abord sauvegardé dans `src/config.json.bak` (ignoré par git) pour ne + rien perdre. +- Le panneau **Catalogue YunoHost** de l'éditeur (colonnes latérales, qui + normalement liste via SSH les apps installées sur un vrai serveur — voir + `make sync` plus bas) est lui aussi rempli avec des données de démo + (`src/catalog.demo.json`, une dizaine d'apps supplémentaires) au lieu de + tenter une connexion SSH réelle : c'est le rôle de la variable + `HOMEPAGE_DEMO=1`, positionnée automatiquement par `make demo` pour + `scripts/preview.py`. En dehors de `make demo` (donc avec `make serve` + normal), la vraie synchronisation SSH est utilisée. + +Pour revenir à ta config perso après la démo : restaure ta sauvegarde +(`cp src/config.json.bak src/config.json`) ou relance `make sync` / +`make fetch` selon d'où elle vient. + ## Aperçu et édition en local ```bash diff --git a/scripts/preview.py b/scripts/preview.py index ec9aa14..54dcd53 100755 --- a/scripts/preview.py +++ b/scripts/preview.py @@ -6,6 +6,7 @@ Usage: python3 scripts/preview.py [port] """ import http.server import json +import os import shutil import sys from pathlib import Path @@ -19,6 +20,7 @@ SCRIPT_DIR = Path(__file__).resolve().parent PROJECT_ROOT = SCRIPT_DIR.parent SRC_DIR = PROJECT_ROOT / "src" OUTPUT_DIR = PROJECT_ROOT / "output" +DEMO_CATALOG_PATH = SRC_DIR / "catalog.demo.json" WATCHED_FILES = ["index.html", "style.css", "app.js", "config.json"] EXPORTED_FILES = ["index.html", "style.css", "app.js", "config.json"] @@ -122,6 +124,16 @@ class ReloadingHandler(http.server.SimpleHTTPRequestHandler): self.send_error(404) def _send_catalog(self): + if os.environ.get("HOMEPAGE_DEMO"): + try: + with open(DEMO_CATALOG_PATH, encoding="utf-8") as f: + apps = json.load(f) + except OSError as exc: + self._send_json({"error": str(exc)}) + return + self._send_json({"apps": apps}) + return + env = load_env() ssh_target, domain, self_app_id = sync_target(env) if not ssh_target or not domain: diff --git a/src/catalog.demo.json b/src/catalog.demo.json new file mode 100644 index 0000000..d170656 --- /dev/null +++ b/src/catalog.demo.json @@ -0,0 +1,82 @@ +[ + { + "id": "immich", + "name": "Immich", + "url": "https://immich.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/immich.png", + "color": "#4250af", + "initials": "IM" + }, + { + "id": "paperless-ngx", + "name": "Paperless-ngx", + "url": "https://docs.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/paperless-ngx.png", + "color": "#17541f", + "initials": "PN" + }, + { + "id": "miniflux", + "name": "Miniflux", + "url": "https://flux.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/miniflux.png", + "color": "#6a70d1", + "initials": "MF" + }, + { + "id": "forgejo", + "name": "Forgejo", + "url": "https://forge.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/forgejo.png", + "color": "#fb923c", + "initials": "FG" + }, + { + "id": "uptime-kuma", + "name": "Uptime Kuma", + "url": "https://status.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/uptime-kuma.png", + "color": "#5cdd8b", + "initials": "UK" + }, + { + "id": "ntfy", + "name": "ntfy", + "url": "https://notif.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/ntfy.png", + "color": "#3498db", + "initials": "NT" + }, + { + "id": "audiobookshelf", + "name": "Audiobookshelf", + "url": "https://livres-audio.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/audiobookshelf.png", + "color": "#82612d", + "initials": "AB" + }, + { + "id": "calibre-web", + "name": "Calibre-Web", + "url": "https://livres.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/calibre-web.png", + "color": "#6c4f9c", + "initials": "CW" + }, + { + "id": "navidrome", + "name": "Navidrome", + "url": "https://musique.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/navidrome.png", + "color": "#ff6a12", + "initials": "ND" + }, + { + "id": "firefly-iii", + "name": "Firefly III", + "url": "https://budget.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/firefly-iii.png", + "color": "#a5325b", + "initials": "FF" + } +] diff --git a/src/config.demo.json b/src/config.demo.json new file mode 100644 index 0000000..ff06d73 --- /dev/null +++ b/src/config.demo.json @@ -0,0 +1,238 @@ +{ + "portal": { + "logoText": "YUNO\nHOST", + "adminUrl": "/yunohost/admin/", + "editProfileUrl": "/yunohost/sso/edit", + "logoutUrl": "/yunohost/portalapi/logout", + "documentationUrl": "https://yunohost.org/docs", + "supportUrl": "https://yunohost.org/help" + }, + "user": { + "fullname": "Alex Dupont", + "username": "alex", + "domain": "maison.exemple" + }, + "pinned": [ + { + "id": "nextcloud", + "name": "Nextcloud", + "url": "https://cloud.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nextcloud.png", + "color": "#0082c9", + "initials": "NC" + }, + { + "id": "jellyfin", + "name": "Jellyfin", + "url": "https://films.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jellyfin.png", + "color": "#00a4dc", + "initials": "JF" + }, + { + "id": "vaultwarden", + "name": "Vaultwarden", + "url": "https://coffre.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/vaultwarden.png", + "color": "#175dde", + "initials": "VW" + } + ], + "groups": [ + { + "id": "cloud", + "name": "Cloud & Fichiers", + "collapsed": false, + "apps": [ + { + "id": "nextcloud", + "name": "Nextcloud", + "url": "https://cloud.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nextcloud.png", + "color": "#0082c9", + "initials": "NC" + }, + { + "id": "syncthing", + "name": "Syncthing", + "url": "https://sync.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/syncthing.png", + "color": "#0891d1", + "initials": "ST" + }, + { + "id": "vaultwarden", + "name": "Vaultwarden", + "url": "https://coffre.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/vaultwarden.png", + "color": "#175dde", + "initials": "VW" + } + ] + }, + { + "id": "media", + "name": "Média", + "collapsed": false, + "apps": [ + { + "id": "jellyfin", + "name": "Jellyfin", + "url": "https://films.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jellyfin.png", + "color": "#00a4dc", + "initials": "JF" + }, + { + "id": "photoprism", + "name": "PhotoPrism", + "url": "https://photos.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/photoprism.png", + "color": "#82b510", + "initials": "PP" + }, + { + "id": "transmission", + "name": "Transmission", + "url": "https://torrent.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/transmission.png", + "color": "#c1121f", + "initials": "TR" + } + ] + }, + { + "id": "communication", + "name": "Communication", + "collapsed": false, + "apps": [ + { + "id": "mattermost", + "name": "Mattermost", + "url": "https://chat.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/mattermost.png", + "color": "#0058cc", + "initials": "MM" + }, + { + "id": "matrix", + "name": "Matrix / Element", + "url": "https://matrix.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/matrix.png", + "color": "#0dbd8b", + "initials": "MX" + }, + { + "id": "jitsi", + "name": "Jitsi Meet", + "url": "https://visio.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/jitsi.png", + "color": "#97979c", + "initials": "JM" + } + ] + }, + { + "id": "productivite", + "name": "Productivité", + "collapsed": false, + "apps": [ + { + "id": "wordpress", + "name": "Blog (WordPress)", + "url": "https://blog.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/wordpress.png", + "color": "#21759b", + "initials": "WP" + }, + { + "id": "bookstack", + "name": "BookStack", + "url": "https://wiki.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/bookstack.png", + "color": "#0288d1", + "initials": "BS" + }, + { + "id": "freshrss", + "name": "FreshRSS", + "url": "https://rss.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/freshrss.png", + "color": "#f3a53c", + "initials": "RSS" + }, + { + "id": "wallabag", + "name": "Wallabag", + "url": "https://lire.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/wallabag.png", + "color": "#3f6184", + "initials": "WB" + } + ] + }, + { + "id": "domotique", + "name": "Domotique", + "collapsed": false, + "apps": [ + { + "id": "home-assistant", + "name": "Home Assistant", + "url": "https://domotique.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/home-assistant.png", + "color": "#41bdf5", + "initials": "HA" + } + ] + }, + { + "id": "admin", + "name": "Administration", + "collapsed": true, + "apps": [ + { + "id": "portainer", + "name": "Portainer", + "url": "https://docker.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/portainer.png", + "color": "#13bef9", + "initials": "PT" + }, + { + "id": "grafana", + "name": "Grafana", + "url": "https://stats.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/grafana.png", + "color": "#f46800", + "initials": "GF" + }, + { + "id": "pihole", + "name": "Pi-hole", + "url": "https://dns.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/pi-hole.png", + "color": "#96060c", + "initials": "PH" + }, + { + "id": "gitea", + "name": "Gitea", + "url": "https://git.maison.exemple/", + "icon": "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/gitea.png", + "color": "#609926", + "initials": "GT" + } + ] + } + ], + "ungrouped": [ + { + "id": "pas-encore-classe", + "name": "Nouvelle App", + "url": "https://exemple.maison.exemple/", + "color": "#8e8e8e", + "initials": "NA" + } + ] +}