Masquer les derniers auteurs
| author | version | line-number | content |
|---|---|---|---|
![]() |
4.1 | 1 | Tableau de bord d'accueil : liens vers tous les services auto-hébergés, avec leur état en direct et des widgets, répartis sur une page d'accueil et trois pages secondaires. |
![]() |
1.1 | 2 | |
| 3 | == Fonctionnalités utilisées == | ||
| 4 | |||
![]() |
4.1 | 5 | * Sections et liens vers les services, avec icônes stockées en local ou issues des jeux d'icônes intégrés. |
![]() |
1.1 | 6 | * Indicateur d'état de chaque service (vérification de disponibilité). |
![]() |
4.1 | 7 | * Configuration multi-pages : une page d'accueil (##conf.yml##) et trois pages secondaires déclarées dans sa section ##pages:##. |
| 8 | * Widgets de contenu dynamique, dont certains interrogent des services locaux ou des API externes avec des identifiants (AdGuard Home, Nextcloud, API de vols et d'actualités). | ||
| 9 | * Cartes de statistiques GitHub fournies par une instance locale de github-readme-stats. | ||
| 10 | * Accès protégé par Authelia au niveau de SWAG : aucune page ni aucun fichier de configuration n'est servi sans connexion. | ||
| 11 | * Proxy CORS personnalisé dans SWAG, qui remplace celui de Dashy pour les widgets en ##useProxy: true##. | ||
| 12 | * Configuration modifiable depuis l'interface, enregistrée dans les fichiers YAML. | ||
![]() |
1.1 | 13 | |
| 14 | == Stack == | ||
| 15 | |||
| 16 | {{code language="yaml"}} | ||
| 17 | services: | ||
| 18 | dashy: | ||
| 19 | image: lissy93/dashy:latest | ||
| 20 | container_name: dashy | ||
| 21 | environment: | ||
| 22 | - NODE_ENV=production | ||
| 23 | - UID=1000 | ||
| 24 | - GID=1000 | ||
| 25 | - NODE_TLS_REJECT_UNAUTHORIZED=0 | ||
| 26 | ports: | ||
| 27 | - "4000:8080" | ||
| 28 | volumes: | ||
| 29 | - <APPDATA>/Dashy/user-data:/app/user-data | ||
| 30 | restart: unless-stopped | ||
| 31 | healthcheck: | ||
| 32 | test: ['CMD', 'node', '/app/services/healthcheck'] | ||
| 33 | interval: 90s | ||
| 34 | timeout: 10s | ||
| 35 | retries: 3 | ||
| 36 | start_period: 40s | ||
| 37 | networks: | ||
| 38 | - proxy | ||
| 39 | |||
| 40 | networks: | ||
| 41 | proxy: | ||
| 42 | external: true | ||
| 43 | {{/code}} | ||
| 44 | |||
![]() |
4.1 | 45 | * ##NODE_TLS_REJECT_UNAUTHORIZED=0## : le serveur de Dashy accepte les certificats non reconnus lors des vérifications d'état et dans son proxy CORS, nécessaire pour les services locaux en https avec un certificat auto-signé. |
| 46 | * ##UID## / ##GID## : propriétaire des fichiers écrits dans ##user-data## (sauvegarde depuis l'interface). | ||
| 47 | * Port ##4000## : accès direct depuis le LAN, sans SWAG ni Authelia. L'accès normal passe par SWAG, qui joint le conteneur par son nom sur le réseau ##proxy##, port interne ##8080##. Pour que tout accès passe par Authelia, supprimer cette publication de port : SWAG n'en a pas besoin. | ||
| 48 | * Volume : tout le dossier ##user-data## est monté dans ##/app/user-data##. Dashy sert ce dossier à la racine du site : ##conf.yml##, les pages secondaires et ##item-icons/## sont accessibles par leur nom. | ||
| 49 | * Réseau ##proxy## : réseau Docker externe partagé avec SWAG, à créer avant le premier déploiement (##docker network create proxy##). | ||
| 50 | * Mises à jour de l'image : [[Watchtower>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Watchtower.WebHome]]. | ||
![]() |
1.1 | 51 | |
![]() |
4.1 | 52 | === Exposition derrière SWAG === |
| 53 | |||
| 54 | Fichier ##<APPDATA>/swag/config/nginx/proxy-confs/30-dashy.subdomain.conf##. Chaque ##location## inclut ##authelia-location.conf##, selon le même schéma que les autres services protégés. Voir aussi [[SWAG>>Wiki Batcave John.Infrastructure.ServeurPrincipal.SWAG.WebHome]]. | ||
| 55 | |||
| 56 | {{code language="nginx"}} | ||
| 57 | server { | ||
| 58 | listen 443 ssl; | ||
| 59 | listen [::]:443 ssl; | ||
| 60 | server_name <DOMAINE_DASHY>; | ||
| 61 | |||
| 62 | include /config/nginx/ssl.conf; | ||
| 63 | include /config/nginx/snippets/robots-default.conf; | ||
| 64 | |||
| 65 | client_max_body_size 25m; | ||
| 66 | |||
| 67 | # Authelia : tout le domaine exige une connexion | ||
| 68 | include /config/nginx/authelia-server.conf; | ||
| 69 | |||
| 70 | set $upstream_app dashy; | ||
| 71 | set $upstream_port 8080; | ||
| 72 | set $upstream_proto http; | ||
| 73 | |||
| 74 | # Interface web du Pico W, relayée sous /pico/ | ||
| 75 | location /pico/ { | ||
| 76 | include /config/nginx/authelia-location.conf; | ||
| 77 | proxy_pass http://<IP_PICO>/; | ||
| 78 | include /config/nginx/proxy.conf; | ||
| 79 | proxy_set_header Connection close; | ||
| 80 | } | ||
| 81 | |||
| 82 | # API de Dashy (sauvegarde de la config, vérifications d'état...) | ||
| 83 | location ^~ /api/ { | ||
| 84 | include /config/nginx/authelia-location.conf; | ||
| 85 | include /config/nginx/proxy.conf; | ||
| 86 | include /config/nginx/resolver.conf; | ||
| 87 | proxy_pass $upstream_proto://$upstream_app:$upstream_port; | ||
| 88 | } | ||
| 89 | |||
| 90 | # Proxy CORS des widgets, traité par SWAG et non par Dashy | ||
| 91 | location = /cors-proxy { | ||
| 92 | include /config/nginx/authelia-location.conf; | ||
| 93 | set $target $http_target_url; | ||
| 94 | if ($target = "") { return 400; } | ||
| 95 | |||
| 96 | add_header Access-Control-Allow-Origin "*" always; | ||
| 97 | add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always; | ||
| 98 | add_header Access-Control-Allow-Headers "*" always; | ||
| 99 | if ($request_method = OPTIONS) { return 204; } | ||
| 100 | |||
| 101 | proxy_ssl_server_name on; | ||
| 102 | proxy_ssl_verify off; | ||
| 103 | |||
| 104 | set $auth_header ""; | ||
| 105 | set $accept_header ""; | ||
| 106 | set $ocs_header ""; | ||
| 107 | set $host_override ""; | ||
| 108 | |||
| 109 | access_by_lua_block { | ||
| 110 | local target = ngx.var.target | ||
| 111 | local host, port = target:match("^https?://([^/:]+):?(%d*)") | ||
| 112 | if not host then | ||
| 113 | ngx.status = 400 | ||
| 114 | ngx.header["Content-Type"] = "application/json" | ||
| 115 | ngx.say('{"error":"Target-URL invalide"}') | ||
| 116 | return ngx.exit(400) | ||
| 117 | end | ||
| 118 | |||
| 119 | -- Cibles LAN autorisées (widgets Dashy internes) | ||
| 120 | local allowed_internal = { ["<IP_SERVEUR>"] = true, ["<IP_ADGUARD>"] = true, ["<IP_PICO>"] = true } | ||
| 121 | local function is_private_ip(h) | ||
| 122 | if h == "localhost" then return true end | ||
| 123 | local a, b = h:match("^(%d+)%.(%d+)%.%d+%.%d+$") | ||
| 124 | if not a then return false end | ||
| 125 | a, b = tonumber(a), tonumber(b) | ||
| 126 | if a == 127 or a == 10 or a == 0 then return true end | ||
| 127 | if a == 172 and b >= 16 and b <= 31 then return true end | ||
| 128 | if a == 192 and b == 168 then return true end | ||
| 129 | if a == 169 and b == 254 then return true end | ||
| 130 | return false | ||
| 131 | end | ||
| 132 | |||
| 133 | -- Anti-SSRF : LAN, loopback et metadata bloqués sauf liste blanche | ||
| 134 | if is_private_ip(host) and not allowed_internal[host] then | ||
| 135 | ngx.status = 403 | ||
| 136 | ngx.header["Content-Type"] = "application/json" | ||
| 137 | ngx.say('{"error":"Cible interne non autorisée"}') | ||
| 138 | return ngx.exit(403) | ||
| 139 | end | ||
| 140 | |||
| 141 | ngx.var.host_override = (port ~= "" and port ~= nil) and (host .. ":" .. port) or host | ||
| 142 | |||
| 143 | -- En-têtes envoyés par Dashy en JSON dans CustomHeaders | ||
| 144 | local cjson = require "cjson.safe" | ||
| 145 | local ch = ngx.req.get_headers()["customheaders"] | ||
| 146 | if ch then | ||
| 147 | local obj = cjson.decode(ch) | ||
| 148 | if obj then | ||
| 149 | if obj["Authorization"] then ngx.var.auth_header = obj["Authorization"] end | ||
| 150 | if obj["Accept"] then ngx.var.accept_header = obj["Accept"] end | ||
| 151 | if obj["OCS-APIREQUEST"] ~= nil then ngx.var.ocs_header = tostring(obj["OCS-APIREQUEST"]) end | ||
| 152 | end | ||
| 153 | end | ||
| 154 | } | ||
| 155 | |||
| 156 | proxy_set_header Authorization $auth_header; | ||
| 157 | proxy_set_header Accept $accept_header; | ||
| 158 | proxy_set_header OCS-APIREQUEST $ocs_header; | ||
| 159 | proxy_set_header Host $host_override; | ||
| 160 | proxy_set_header X-Forwarded-Proto https; | ||
| 161 | |||
| 162 | proxy_pass $target; | ||
| 163 | } | ||
| 164 | |||
| 165 | # Application (SPA Vue en mode history) | ||
| 166 | location / { | ||
| 167 | include /config/nginx/authelia-location.conf; | ||
| 168 | include /config/nginx/proxy.conf; | ||
| 169 | include /config/nginx/resolver.conf; | ||
| 170 | proxy_pass $upstream_proto://$upstream_app:$upstream_port; | ||
| 171 | |||
| 172 | # Une 404 de l'upstream sur /login, /settings... renvoie l'index de l'app | ||
| 173 | proxy_intercept_errors on; | ||
| 174 | error_page 404 = @dashy_fallback; | ||
| 175 | } | ||
| 176 | |||
| 177 | location @dashy_fallback { | ||
| 178 | proxy_set_header X-Original-URI $request_uri; | ||
| 179 | proxy_pass $upstream_proto://$upstream_app:$upstream_port/; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | {{/code}} | ||
| 183 | |||
| 184 | Fonctionnement du proxy CORS : | ||
| 185 | |||
| 186 | * Un widget en ##useProxy: true## n'appelle pas l'API cible depuis le navigateur : il envoie la requête à ##/cors-proxy## sur le domaine de Dashy, avec l'adresse cible dans l'en-tête ##Target-URL## et ses propres en-têtes (authentification comprise) en JSON dans ##CustomHeaders##. | ||
| 187 | * Derrière SWAG, c'est ce bloc ##location = /cors-proxy## qui répond, pas le proxy intégré de Dashy. Il ne transmet que ##Authorization##, ##Accept## et ##OCS-APIREQUEST## : un widget qui a besoin d'un autre en-tête doit être ajouté dans le bloc Lua. | ||
| 188 | * Les adresses IP privées sont refusées (403), sauf celles de ##allowed_internal##. Tout nouveau widget qui vise une IP du LAN doit y être ajouté, puis SWAG rechargé. Un nom de domaine public passe sans liste blanche. | ||
| 189 | * Les appels à ##/cors-proxy## partent de la page Dashy, sur le même domaine : le navigateur y joint le cookie de session Authelia, et ##/cors-proxy## n'est pas utilisable sans connexion. | ||
| 190 | * Les requêtes ##OPTIONS## (pré-vol CORS) reçoivent leur 204 avant le contrôle Authelia, car les ##if## de nginx sont évalués avant ##auth_request##. | ||
| 191 | * En accès direct sur le port ##4000##, c'est le proxy intégré de Dashy qui répond, sans liste blanche ni Authelia. | ||
| 192 | * Nécessite le module Lua de nginx, présent dans l'image SWAG. | ||
| 193 | |||
![]() |
1.1 | 194 | === Accès === |
| 195 | |||
![]() |
4.1 | 196 | L'accès se fait en deux étapes : |
![]() |
1.1 | 197 | |
![]() |
4.1 | 198 | 1. **Authelia**, devant tout le domaine : sans session Authelia, SWAG redirige vers le portail Authelia. Cela couvre l'interface, ##conf.yml## et les pages secondaires, l'API de Dashy, ##/cors-proxy## et ##/pico/##. |
| 199 | 1. **Dashy**, une fois Authelia passé : son écran de connexion donne accès à l'éditeur de configuration au compte administrateur défini dans ##appConfig.auth.users## (##disableConfigurationForNonAdmin: true##). Voir [[Authentification>>||anchor="HAuthentification"]]. | ||
| 200 | |||
| 201 | Règle dans la configuration d'[[Authelia>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Authelia.WebHome]], section ##access_control## : | ||
| 202 | |||
| 203 | {{code language="yaml"}} | ||
| 204 | access_control: | ||
| 205 | default_policy: deny | ||
| 206 | rules: | ||
| 207 | - domain: "<DOMAINE_DASHY>" | ||
| 208 | policy: one_factor | ||
| 209 | {{/code}} | ||
| 210 | |||
| 211 | * La règle ne sert que si SWAG interroge Authelia : sans les ##include## d'Authelia dans le fichier SWAG, elle n'est jamais évaluée, quelle que soit sa ##policy##. Il faut les deux. | ||
| 212 | * Une règle ##bypass## pour ce domaine, placée avant, annulerait la protection : Authelia applique la première règle qui correspond. | ||
| 213 | * ##enableGuestAccess: true## ne concerne que les personnes déjà passées par Authelia : elles voient les pages sans se connecter à Dashy, mais ne peuvent pas modifier la configuration. | ||
| 214 | * Le fichier Authelia ouvre aussi le CORS de ##/.well-known/openid-configuration## et ##/jwks.json## au domaine de Dashy, en prévision d'une connexion OIDC. Celle-ci n'est pas activée dans ##conf.yml##. | ||
| 215 | |||
| 216 | {{warning}}C'est Authelia qui protège les fichiers, pas Dashy : l'authentification de Dashy se fait dans le navigateur et masque seulement l'éditeur. ##conf.yml## et les pages secondaires contiennent les identifiants des widgets, les clés d'API, les adresses internes et le hash du mot de passe administrateur. Toute voie d'accès qui contourne Authelia (port ##4000##, autre proxy, règle ##bypass##) les rend lisibles. Vérifier après chaque modification de SWAG ou d'Authelia, en navigation privée : ##/conf.yml## doit renvoyer vers le portail Authelia.{{/warning}} | ||
| 217 | |||
![]() |
1.1 | 218 | == Configuration == |
| 219 | |||
| 220 | Dans ##<APPDATA>/Dashy/user-data## : | ||
| 221 | |||
| 222 | |=Élément|=Rôle | ||
![]() |
4.1 | 223 | |##conf.yml##|Page d'accueil et configuration commune : ##appConfig##, ##pageInfo##, ##pages##, sections |
| 224 | |##bookmark.yml##|Page secondaire //bookmarks// : favoris web | ||
| 225 | |##cpu.yml##|Page secondaire //cpu// : supervision Glances | ||
| 226 | |##news.yml##|Page secondaire //news// : capteurs, flux, actualités, crypto | ||
| 227 | |##item-icons/##|Icônes des services et image de fond, stockées en local | ||
| 228 | |##conf.yml.bak##, ##bookmark.yml.bak##|Copies manuelles, non utilisées par Dashy | ||
| 229 | |##config-backups/##|Dossier de sauvegardes de Dashy (vide actuellement) | ||
![]() |
1.1 | 230 | |
![]() |
4.1 | 231 | === Fonctionnement multi-pages === |
![]() |
1.1 | 232 | |
![]() |
4.1 | 233 | La page d'accueil est toujours ##conf.yml##. Les autres pages sont déclarées dans sa section ##pages:## : chaque entrée donne le nom affiché dans la barre de navigation et le chemin du fichier, relatif à ##user-data## (une URL distante est aussi acceptée). |
| 234 | |||
| 235 | {{code language="yaml"}} | ||
| 236 | pages: | ||
| 237 | - name: bookmarks | ||
| 238 | path: bookmark.yml | ||
| 239 | - name: cpu | ||
| 240 | path: cpu.yml | ||
| 241 | - name: news | ||
| 242 | path: news.yml | ||
| 243 | {{/code}} | ||
| 244 | |||
| 245 | * Une page secondaire ne contient que ##pageInfo## et ##sections## : ##appConfig## (thème, authentification, recherche...) est lu uniquement dans ##conf.yml## et s'applique à toutes les pages. | ||
| 246 | * Chaque page secondaire répète la même liste ##pages:##, pour garder la navigation identique d'une page à l'autre. Aucune ne déclare de fichier supplémentaire. | ||
| 247 | * Dans ##pageInfo.navLinks## des pages secondaires, un lien ##Home## vers ##/## ramène à l'accueil. | ||
| 248 | * Ajouter une page : créer le fichier dans ##user-data##, l'ajouter dans ##pages:## de ##conf.yml## et des autres pages, puis recharger. | ||
| 249 | |||
| 250 | === Récapitulatif des pages === | ||
| 251 | |||
| 252 | |=Page Dashy|=Fichier|=Sous-page XWiki|=Widgets utilisant des identifiants | ||
| 253 | |Accueil|##conf.yml##|[[Accueil>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Accueil.WebHome]]|##adguard-stats##, ##adguard-filter-status##, ##adguard-top-domains## (compte AdGuard Home), ##nextcloud-system## (mot de passe d'application Nextcloud). Les widgets ##image## dépendent du jeton GitHub de github-readme-stats, stocké côté serveur. | ||
| 254 | |bookmarks|##bookmark.yml##|[[Bookmarks>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Bookmarks.WebHome]]|Aucun widget | ||
| 255 | |cpu|##cpu.yml##|[[CPU>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.CPU.WebHome]]|Aucun (Glances sans authentification) | ||
| 256 | |news|##news.yml##|[[News>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.News.WebHome]]|##flight-data## (clé RapidAPI), ##news-headlines## (clé Currents API) | ||
| 257 | |||
| 258 | === Fichiers non référencés === | ||
| 259 | |||
| 260 | * ##cpu2.yml## : ancienne copie de ##cpu.yml## avec une indentation à un espace qui casse la structure YAML. Déclarée nulle part, mais servie comme les autres fichiers. À supprimer. | ||
| 261 | * ##conf.yml.bak## et ##bookmark.yml.bak## : copies de sauvegarde, non déclarées. | ||
| 262 | |||
| 263 | === Structure d'un fichier === | ||
| 264 | |||
| 265 | Un fichier de page contient trois blocs de premier niveau, plus ##appConfig## et ##pages## pour ##conf.yml## : | ||
| 266 | |||
| 267 | {{code language="yaml"}} | ||
| 268 | pageInfo: | ||
| 269 | title: <TITRE> | ||
| 270 | description: <SOUS_TITRE> | ||
| 271 | navLinks: | ||
| 272 | - title: <LIBELLE> | ||
| 273 | target: newtab | ||
| 274 | path: https://<SITE_PERSO> | ||
| 275 | appConfig: | ||
| 276 | # conf.yml uniquement, voir ci-dessous | ||
| 277 | pages: | ||
| 278 | # conf.yml, recopié dans chaque page secondaire | ||
| 279 | sections: | ||
| 280 | - name: <NOM_SECTION> | ||
| 281 | icon: <ICONE> | ||
| 282 | displayData: | ||
| 283 | sortBy: default # default | alphabetical | random | ||
| 284 | rows: 1 | ||
| 285 | cols: 1 | ||
| 286 | collapsed: false | ||
| 287 | hideForGuests: false | ||
| 288 | items: | ||
| 289 | - title: <NOM_SERVICE> | ||
| 290 | description: <DESCRIPTION> | ||
| 291 | icon: <ICONE> | ||
| 292 | url: https://<DOMAINE_SERVICE>/ | ||
| 293 | target: newtab | ||
| 294 | statusCheck: true | ||
| 295 | statusCheckAllowInsecure: true | ||
| 296 | id: <ID> | ||
| 297 | widgets: | ||
| 298 | - type: <TYPE_WIDGET> | ||
| 299 | useProxy: true | ||
| 300 | options: | ||
| 301 | <OPTION>: <VALEUR> | ||
| 302 | id: <ID> | ||
| 303 | {{/code}} | ||
| 304 | |||
| 305 | * ##id## : identifiant unique généré par l'interface. Facultatif en écriture manuelle : Dashy en génère un s'il manque. | ||
| 306 | * ##icon## : formes utilisées dans cette configuration : | ||
| 307 | ** nom de fichier présent dans ##item-icons/## (##portainer.svg##) ; | ||
| 308 | ** ##hl-<nom>## : jeu d'icônes homelab intégré (##hl-jellyfin##) ; | ||
| 309 | ** classes Font Awesome (##fas fa-terminal##, ##fab fa-github##, ##fad fa-clock##), actif via ##enableFontAwesome: true## ; | ||
| 310 | ** ##favicon## : favicon du site récupéré par le service défini dans ##faviconApi## ; | ||
| 311 | ** emoji (##🛡️##) ou URL complète d'une image. | ||
| 312 | * ##useProxy## se place sur le widget, pas sur la section : placé au niveau de la section, il est ignoré. | ||
| 313 | |||
| 314 | === appConfig et thème === | ||
| 315 | |||
| 316 | {{code language="yaml"}} | ||
| 317 | appConfig: | ||
| 318 | theme: dracula | ||
| 319 | layout: auto | ||
| 320 | iconSize: small | ||
| 321 | backgroundImg: /item-icons/<IMAGE_FOND> | ||
| 322 | startingView: default | ||
| 323 | defaultOpeningMethod: newtab | ||
| 324 | statusCheck: false | ||
| 325 | statusCheckInterval: 0 | ||
| 326 | faviconApi: allesedv | ||
| 327 | enableFontAwesome: true | ||
| 328 | enableMaterialDesignIcons: false | ||
| 329 | widgetsAlwaysUseProxy: false | ||
| 330 | webSearch: | ||
| 331 | searchEngine: duckduckgo | ||
| 332 | openingMethod: newtab | ||
| 333 | searchBangs: {} | ||
| 334 | disableWebSearch: false | ||
| 335 | auth: | ||
| 336 | enableGuestAccess: true | ||
| 337 | users: | ||
| 338 | - user: <UTILISATEUR_DASHY> | ||
| 339 | hash: <HASH_SHA256_DU_MOT_DE_PASSE> | ||
| 340 | type: admin | ||
| 341 | enableHeaderAuth: false | ||
| 342 | headerAuth: | ||
| 343 | userHeader: REMOTE_USER | ||
| 344 | proxyWhitelist: [] | ||
| 345 | enableKeycloak: false | ||
| 346 | hideComponents: | ||
| 347 | hideHeading: false | ||
| 348 | hideNav: false | ||
| 349 | hideSearch: false | ||
| 350 | hideSettings: false | ||
| 351 | hideFooter: false | ||
| 352 | showSplashScreen: false | ||
| 353 | preventWriteToDisk: false | ||
| 354 | preventLocalSave: false | ||
| 355 | disableConfiguration: false | ||
| 356 | disableConfigurationForNonAdmin: true | ||
| 357 | allowConfigEdit: true | ||
| 358 | enableServiceWorker: false | ||
| 359 | disableContextMenu: false | ||
| 360 | disableUpdateChecks: false | ||
| 361 | disableSmartSort: false | ||
| 362 | enableErrorReporting: false | ||
| 363 | {{/code}} | ||
| 364 | |||
| 365 | |=Clé|=Effet | ||
| 366 | |##theme: dracula##|Thème sombre intégré, appliqué à toutes les pages | ||
| 367 | |##backgroundImg##|Image de fond, servie depuis ##item-icons/## | ||
| 368 | |##statusCheck: false##|Pas de vérification d'état globale : elle est activée item par item (##statusCheck: true##) | ||
| 369 | |##faviconApi: allesedv##|Les icônes ##favicon## sont récupérées via le service externe allesedv | ||
| 370 | |##widgetsAlwaysUseProxy: false##|Chaque widget choisit avec ##useProxy## s'il passe par ##/cors-proxy## | ||
| 371 | |##webSearch##|Recherche web DuckDuckGo depuis la barre de recherche, en nouvel onglet | ||
| 372 | |##preventWriteToDisk: false##|L'interface peut écrire dans les fichiers YAML du volume | ||
| 373 | |##preventLocalSave: false##|Les modifications peuvent aussi être gardées dans le stockage local du navigateur | ||
| 374 | |##disableConfigurationForNonAdmin: true##|Seul un compte ##type: admin## voit l'éditeur de configuration | ||
| 375 | |||
| 376 | === Authentification === | ||
| 377 | |||
| 378 | * Un seul compte, administrateur, défini par son nom et le hash SHA-256 de son mot de passe. Générer le hash : | ||
| 379 | |||
![]() |
1.1 | 380 | {{code language="bash"}} |
![]() |
4.1 | 381 | echo -n '<MOT_DE_PASSE_DASHY>' | sha256sum |
| 382 | {{/code}} | ||
| 383 | |||
| 384 | * ##enableGuestAccess: true## : les visiteurs authentifiés par Authelia mais pas par Dashy voient toutes les pages. ##hideForGuests: true## sur une section la masque aux invités, sans la retirer des fichiers servis. | ||
| 385 | * ##enableHeaderAuth## (utilisateur transmis par le reverse proxy dans ##REMOTE_USER##) et ##enableKeycloak## sont désactivés. | ||
| 386 | |||
| 387 | === Vérifier la syntaxe === | ||
| 388 | |||
| 389 | Après une modification à la main : | ||
| 390 | |||
| 391 | {{code language="bash"}} | ||
![]() |
1.1 | 392 | docker exec dashy yarn validate-config |
| 393 | {{/code}} | ||
![]() |
4.1 | 394 | |
![]() |
3.1 | 395 | == Statistiques GitHub : github-readme-stats == |
![]() |
1.1 | 396 | |
![]() |
3.1 | 397 | Instance locale de github-readme-stats, qui génère les cartes SVG de statistiques GitHub affichées par un widget de Dashy : statistiques du profil, langages les plus utilisés, dépôts épinglés. |
| 398 | |||
| 399 | {{code language="yaml"}} | ||
| 400 | services: | ||
| 401 | github-readme-stats: | ||
| 402 | image: github-readme-stats:local | ||
| 403 | container_name: github-readme-stats | ||
| 404 | restart: unless-stopped | ||
| 405 | ports: | ||
| 406 | - "9000:9000" | ||
| 407 | environment: | ||
| 408 | - PAT_1=${PAT_1} | ||
| 409 | - CACHE_SECONDS=14400 | ||
| 410 | {{/code}} | ||
| 411 | |||
| 412 | * ##PAT_1## : jeton d'accès personnel GitHub, défini dans les variables de la stack Portainer. Jeton classique avec les permissions ##repo## pour compter les dépôts privés. | ||
| 413 | * ##CACHE_SECONDS## : durée de mise en cache des cartes (4 heures), pour limiter les appels à l'API GitHub. | ||
| 414 | * Accessible uniquement en local, sur le port 9000. | ||
| 415 | |||
| 416 | Principales adresses : | ||
| 417 | |||
| 418 | |=Carte|=Adresse | ||
![]() |
4.1 | 419 | |Statistiques du profil|##/api?username=<UTILISATEUR_GITHUB>## |
| 420 | |Langages les plus utilisés|##/api/top-langs?username=<UTILISATEUR_GITHUB>## | ||
| 421 | |Dépôt épinglé|##/api/pin?username=<UTILISATEUR_GITHUB>&repo=<DEPOT>## | ||
![]() |
3.1 | 422 | |
![]() |
4.1 | 423 | Dans Dashy, les cartes s'affichent avec un widget de type ##image## qui pointe vers l'instance locale (voir la sous-page [[Accueil>>Wiki Batcave John.Infrastructure.ServeurPrincipal.Dashy.Accueil.WebHome]]). |
![]() |
3.1 | 424 | |
| 425 | === Image locale === | ||
| 426 | |||
| 427 | Portainer ne sait pas construire une image dans une stack : l'image ##github-readme-stats:local## est construite sur l'hôte, depuis le dépôt cloné. Elle n'est donc pas mise à jour par Watchtower. Pour la mettre à jour, depuis le dossier du dépôt : | ||
| 428 | |||
| 429 | {{code language="bash"}} | ||
| 430 | git pull | ||
| 431 | docker build -t github-readme-stats:local . | ||
| 432 | {{/code}} | ||
| 433 | |||
| 434 | Puis redéployer la stack dans Portainer. | ||
| 435 | |||
![]() |
4.1 | 436 | == Maintenance == |
| 437 | |||
| 438 | === Après une migration ou une réinstallation d'un service === | ||
| 439 | |||
| 440 | |=Service migré ou réinstallé|=À refaire dans Dashy | ||
| 441 | |Nextcloud|Créer un nouveau mot de passe d'application et le reporter dans ##nextcloud-system## : les mots de passe d'application ne suivent pas une migration (sauvegarde restaurée, passage à AIO, nouvelle instance). Vérifier que le compte est toujours administrateur et que le nom d'utilisateur a gardé la même casse. | ||
| 442 | |AdGuard Home|Reporter le nom d'utilisateur et le mot de passe de l'interface web dans les trois widgets ##adguard-*##. Si l'adresse IP change : mettre à jour ##hostname## et la liste ##allowed_internal## de SWAG. | ||
| 443 | |Glances|Mettre à jour ##hostname## dans tous les widgets ##gl-*## de ##cpu.yml## si le domaine change. | ||
| 444 | |github-readme-stats|Reconstruire l'image locale et recréer le jeton ##PAT_1## s'il a expiré. | ||
| 445 | |Pico W|Mettre à jour son IP dans ##news.yml## (item et script du widget ##embed##), dans ##location /pico/## et dans ##allowed_internal## de SWAG. | ||
| 446 | |SWAG|Recopier ##30-dashy.subdomain.conf## complet. Sans les ##include## d'Authelia, Dashy et ses fichiers de configuration redeviennent publics. Sans le bloc ##/cors-proxy##, les widgets en ##useProxy: true## passent par le proxy de Dashy, sans liste blanche. | ||
| 447 | |Authelia|Vérifier que la règle ##one_factor## du domaine de Dashy est toujours présente et qu'aucune règle ##bypass## ne la précède. | ||
| 448 | |Dashy|Restaurer ##user-data## (fichiers YAML et ##item-icons/##). Aucun identifiant n'est stocké ailleurs. | ||
| 449 | |API externes (RapidAPI, Currents)|Si la clé est régénérée ou le compte recréé, reporter la nouvelle clé dans ##news.yml##. | ||
| 450 | |||
| 451 | === Recharger la configuration === | ||
| 452 | |||
| 453 | * Modification depuis l'interface : l'enregistrement sur le disque, depuis l'éditeur de configuration, écrit le fichier YAML de la page affichée. | ||
| 454 | * Modification à la main : Dashy relit les fichiers YAML à chaque chargement, un rafraîchissement du navigateur suffit. Si l'ancienne configuration reste affichée, elle vient du stockage local du navigateur : réinitialiser les paramètres locaux depuis le menu de configuration, ou vider les données du site. | ||
| 455 | * Changement dans la stack (variables d'environnement, image) : redéployer la stack dans Portainer. | ||
| 456 | * Changement dans le fichier SWAG : ##docker exec swag nginx -t## puis ##docker exec swag nginx -s reload##. | ||
| 457 | * Changement dans la configuration d'Authelia : ##docker restart authelia##. | ||
| 458 | |||
| 459 | === Un widget reste vide === | ||
| 460 | |||
| 461 | Dans l'ordre : | ||
| 462 | |||
| 463 | 1. Outils de développement du navigateur (F12), onglets Console et Réseau : repérer la requête du widget et son code de retour. | ||
| 464 | 1. Requête vers ##/cors-proxy## : | ||
| 465 | 1*. 302 vers le portail Authelia, ou 401 : session Authelia expirée ; recharger la page et se reconnecter ; | ||
| 466 | 1*. 400 ##Target-URL invalide## : ##hostname## mal formé (schéma ##http:~/~/## ou ##https:~/~/## obligatoire) ; | ||
| 467 | 1*. 403 ##Cible interne non autorisée## : IP du LAN absente de ##allowed_internal## dans SWAG ; | ||
| 468 | 1*. 401 ou 403 renvoyé par la cible : identifiants faux, mot de passe d'application révoqué, compte sans droits suffisants, ou en-tête d'authentification non transmis par le bloc Lua ; | ||
| 469 | 1*. 502 ou 504 : service cible arrêté ou injoignable depuis le conteneur SWAG. | ||
| 470 | 1. Requête directe (sans ##/cors-proxy##) bloquée avec une erreur CORS dans la console : passer le widget en ##useProxy: true##. | ||
| 471 | 1. Journaux : ##docker logs swag## et ##<APPDATA>/swag/config/log/nginx/error.log## pour le proxy, ##docker logs dashy## pour le serveur de Dashy. | ||
| 472 | 1. Vérifier la syntaxe du fichier avec ##yarn validate-config## : une erreur d'indentation peut vider toute une section. | ||
| 473 | 1. Tester l'API cible à la main depuis l'hôte, avec les mêmes identifiants. Exemple pour Nextcloud : | ||
| 474 | |||
| 475 | {{code language="bash"}} | ||
| 476 | curl -u '<UTILISATEUR_NEXTCLOUD>:<MOT_DE_PASSE_APPLICATION>' -H 'OCS-APIREQUEST: true' \ | ||
| 477 | 'https://<DOMAINE_NEXTCLOUD>/ocs/v2.php/apps/serverinfo/api/v1/info?format=json' | ||
| 478 | {{/code}} | ||
| 479 | |||
![]() |
1.1 | 480 | == Sauvegarde == |
| 481 | |||
![]() |
4.1 | 482 | ##<APPDATA>/Dashy/user-data## est sauvegardé par le script de [[Sauvegarde>>Wiki Batcave John.Infrastructure.Sauvegarde.WebHome]]. Il contient toute la configuration, identifiants des widgets compris : la sauvegarde doit être protégée en conséquence. Le fichier SWAG ##30-dashy.subdomain.conf## est aussi dans ##<APPDATA>##, couvert par la même sauvegarde. |
| 483 |
