Tableau de bord interne pour la gestion des armoires connectées, des stocks, des rôles et de l'historique d'actions du Devinci Fablab.
Outil interne, multi-rôles, avec authentification SSO (Keycloak), modèle ACM en tiers (rôles système immuables + rôles personnalisés configurables via le dashboard), et historique d'actions append-only.
- SmartLock Dashboard
Le Devinci Fablab dispose d'armoires connectées dont l'accès est contrôlé par badge étudiant. Chaque armoire contient un inventaire d'items (filaments, composants électroniques, textiles, fournitures de bureau, etc.). Le besoin :
- Gérer un inventaire dimensionné jusqu'à 100 types d'items par armoire, avec photos, références d'achat externes (Amazon, RS, fournisseur direct) et seuils de stock bas.
- Tracer toutes les actions (accès NFC, mouvements de stock, modifications de catalogue, attributions de rôles, cycle de vie de comptes, génération de bons de commande) dans un audit log append-only au niveau DB.
- Contrôler l'accès via un modèle ACM en tiers (T0..T5) où chaque tier contient des rôles pairs (Zero Trust, aucun droit implicite). Le modèle distingue 6 rôles système immuables (Admin sys, Présidence, Codir, Trésorerie, Bureau, Membre) et des rôles personnalisés (Agents, Responsables, Createch, Ingénieur de recherche…) créés/édités/supprimés via le dashboard.
- Chaque rôle porte deux flags :
manager(peut attribuer/révoquer des rôles utilisateurs aux tiers strictement inférieurs) etrole_admin(peut gérer le catalogue de rôles). Permissions par armoire en enum 3 niveaux (can_view < can_open < can_edit). Effectif sur un utilisateur = max sur tous ses rôles. - Générer des bons de commande CSV pour la Trésorerie à partir des alertes de stock bas, stockés dans un object storage S3-compatible.
Le cahier des charges complet est décrit dans CDC.md. Le panneau tactile et l'affichage public vitrine sont hors scope de ce projet.
- Outil interne maintenu par l'équipe, pas un livrable client : les choix techniques privilégient la productivité de l'équipe et la maintenabilité long terme sur la transférabilité.
- Évolutif : démarrer sur la gestion d'inventaire et de rôles, puis étendre vers d'autres outils internes du fablab sur la même base technique. Le panneau tactile et l'éventuel affichage public sont traités dans des projets séparés qui consomment la même API d'auth.
- Explicite plutôt que magique : on privilégie les stacks où le code écrit est le code qui tourne (débogage facile, reprise par d'autres contributeurs étudiants).
- Possession du code UI : composants copiés dans le projet (style shadcn), zéro lock-in sur une bibliothèque tierce.
| Couche | Choix | Justification courte |
|---|---|---|
| Meta-framework | SvelteKit (Svelte 5 + runes) | SSR explicite, file-based routing, hooks server, déploiement Node simple |
| UI / composants | shadcn-svelte + bits-ui | Composants copiés, zéro dépendance UI tierce |
| Styles | Tailwind CSS v4 | Système de design utilitaire, aligné avec shadcn |
| Icônes | Lucide (@lucide/svelte) |
Set d'icônes cohérent, léger |
| Auth (SSO) | Keycloak (OIDC + PKCE, OTP pour T2+) | SSO interne. Le dashboard ne parle jamais à Keycloak directement, tout passe par l'API. |
| API d'auth / données | SmartLock-Authentication-Authorization (FastAPI) | Service externe (repo). Source de vérité pour identités, rôles, permissions, audit. Le dashboard est son client REST. |
| Base de données | PostgreSQL (côté API d'auth) | Transactions, JSONB, audit append-only via rôle DB séparé. |
| Object storage | rustfs (S3-compatible, drop-in MinIO) | Stockage des photos d'items et des CSV générés. Accès via URLs signées émises par l'API. |
| Validation | Zod (à intégrer) | Schémas partagés client / serveur côté SvelteKit |
| Build / dev | Vite 7 | HMR rapide, config minimale |
| Conteneurisation | Docker + Docker Compose | Dev reproductible, déploiement self-hosted. Le compose inclut SvelteKit + rustfs. |
Les décisions structurantes derrière ces choix sont résumées dans la section Décisions clés ci-dessous.
- SvelteKit plutôt que Next.js. Le projet est un outil interne maintenu par l'équipe sur la durée. L'argument "transférable à n'importe quel dev React" pèse moins que "moins de magie cachée, plus facile à reprendre pour un contributeur étudiant". SvelteKit a une séparation client / serveur explicite (
+page.sveltevs+page.server.ts) sans Server Components ni cache defaults qui changent entre versions. - shadcn-svelte accepté comme exception. Le doc de décision globale exclut les ports communautaires de shadcn ;
shadcn-svelte(basé surbits-ui) est l'exception documentée pour ce projet, car suffisamment mature et adopté. - API d'auth comme single source of truth. Le dashboard ne parle jamais à Keycloak ni à PostgreSQL directement — tout passe par l'API
SmartLock-Authentication-Authorization(FastAPI). Centralise auth, permissions, audit, contrôle d'accès. Le dashboard est un client REST + un BFF SvelteKit fin via+page.server.ts. Conséquence : une seule surface à sécuriser, une seule matrice ACM à maintenir, et le panneau tactile (autre projet) consomme la même API. - Modèle ACM en tiers, rôles système + custom. Hiérarchie linéaire de 6 tiers (T0..T5), rôles pairs au sein d'un tier. 6 rôles système codés en dur (Admin sys, Présidence, Codir, Trésorerie, Bureau, Membre) ; tout le reste est custom et configurable via dashboard. Chaque rôle porte les flags
manageretrole_admin, qui dictent qui peut attribuer/révoquer un rôle utilisateur et qui peut gérer le catalogue. Conséquence : créer un nouvel Agent ou Responsable = quelques clics côté dashboard, pas un déploiement de code. - Audit log append-only. La table d'audit est en append-only au niveau DB. Le rôle applicatif n'a ni
DELETEniUPDATEdessus. Seul un rôle DB séparé (Admin sys uniquement, hors API) peut intervenir pour des raisons techniques (corruption, migration, archivage). Rétention indéfinie par défaut. - rustfs pour les assets binaires. Service d'object storage S3-compatible (drop-in MinIO) déployé dans le compose de ce projet. Stocke les photos d'items et les CSV générés par le workflow Trésorerie. L'API détient les credentials du bucket ; le dashboard accède aux assets via des URLs signées émises par l'API, valables un temps court.
- Keycloak en SSO. L'auth n'est pas faite dans l'app : Keycloak est la source de vérité, accédée uniquement par l'API d'auth. Le dashboard reçoit un JWT, valide la session côté serveur dans
hooks.server.ts, et propage le token aux appels API. - Self-hosted via Docker. Pas de Vercel ni de cloud serverless : l'infra reste dans le fablab.
adapter-node+ image Docker Node sur un VPS / serveur interne, plus rustfs en service voisin.
┌────────────────────────┐
│ Utilisateur │
│ (navigateur) │
└───────────┬────────────┘
│ HTTPS (OIDC + PKCE login,
│ Bearer JWT pour API)
▼
┌────────────────────────────────────────┐
│ SmartLock Dashboard │
│ (SvelteKit + adapter-node) │ ← ce projet
│ │
│ hooks.server.ts → session JWT │
│ +page.server.ts → BFF / proxy REST │
│ +page.svelte → UI (shadcn) │
└─────────────────┬──────────────────────┘
│ REST + Bearer JWT
▼
┌────────────────────────────────────────┐
│ SmartLock-Authentication- │
│ Authorization (FastAPI) │ ← projet voisin
└────┬───────────────┬──────────────┬────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Keycloak │ │PostgreSQL│ │ rustfs │
│ (OIDC) │ │ (data + │ │ (S3 obj │
│ │ │ audit) │ │ store) │
└──────────┘ └──────────┘ └─────┬────┘
│
│ déployé dans
│ le compose
│ de ce projet
┌──────────────────────────┐
│ Panneau tactile NFC │ ← autre projet (hors scope) :
│ (Raspberry Pi) │ consomme la même API d'auth.
└──────────────────────────┘
Le code applicatif est isolé dans web/. La racine ne contient que des éléments transverses (docs, justfile, Docker, ignores). Cela permet d'ajouter facilement d'autres composants à côté plus tard (ex : firmware/, infra/, docs/) sans réorganiser.
.
├── docker/ Dockerfiles et compose (dev + prod)
│ ├── Dockerfile Image de production (multi-stage, adapter-node)
│ ├── Dockerfile.dev Image de dev (Vite HMR)
│ ├── compose.yaml Compose dev (context: ../web, bind mount + HMR)
│ └── compose.prod.yaml Compose prod (context: ../web, restart policy)
├── web/ Projet SvelteKit (tout le code applicatif)
│ ├── src/
│ │ ├── routes/ Routes SvelteKit (file-based)
│ │ │ ├── +layout.svelte
│ │ │ ├── +page.svelte
│ │ │ └── Main/ Section principale (à structurer)
│ │ ├── lib/
│ │ │ ├── components/ui/ Composants shadcn-svelte (copiés, modifiables)
│ │ │ ├── assets/ Logos, favicon
│ │ │ └── utils.ts Helpers UI (cn, clsx)
│ │ ├── app.html Template HTML racine
│ │ ├── app.css CSS global (Tailwind import)
│ │ └── app.d.ts Types globaux SvelteKit
│ ├── static/ Assets servis tels quels
│ ├── components.json Config shadcn-svelte
│ ├── svelte.config.js Config SvelteKit (adapter, runes)
│ ├── vite.config.ts Config Vite (HMR Docker-friendly)
│ ├── tsconfig.json Config TypeScript
│ ├── package.json
│ ├── .npmrc
│ └── .dockerignore Lu par Docker au build (contexte = web/)
├── CDC.md Cahier des charges (besoins métier)
├── README.md Ce fichier
├── justfile Commandes raccourcies (opère dans web/ via npm --prefix)
├── LICENSE
└── .gitignore
Note :
.dockerignorevit dansweb/car Docker lit son.dockerignoreà la racine du contexte de build (qui estweb/, pas la racine du repo). C'est la convention.
Construit incrémentalement, une couche à la fois :
- MVP UI navigable (en cours, branche
draft-layout) — layouts, composants shadcn-svelte, navigation entre pages "Armoires" et "Rôles", données statiques. - Auth Keycloak via API — flow OIDC + PKCE côté SvelteKit, sessions JWT serveur, OTP imposé pour les comptes T2+.
- Intégration API d'auth — appels REST depuis
+page.server.tsvers/users,/roles,/armoires,/items,/stock. Pas de DB locale au dashboard, tout passe par l'API. - Gates côté UI — affichage conditionné par tier et flags
manager/role_admindu compte connecté (cf. CDC). - CRUD inventaire — items, catégories, stock, seuils, via les endpoints API.
- Historique / audit log — vue consultable, filtres par armoire / utilisateur / date / type d'événement.
- Landing publique — page
/sans auth (présentation, lien GitHub, lien asso, bouton login). - Gestion des rôles personnalisés — vue dédiée pour comptes
role_admin = true: créer / éditer / supprimer un rôle custom, configurer tier et flags. - Workflow Trésorerie — vue stocks bas, sélection d'items, génération CSV (stocké dans rustfs), suivi binaire
draft → clos. - Intégration rustfs — upload des photos d'items, téléchargement des CSV via URLs signées émises par l'API.
- Outils additionnels — autres modules métier du fablab sur la même base.
Le panneau tactile sur armoire et un éventuel affichage public vitrine sont hors scope de ce dépôt — ils sont traités dans des projets séparés qui consomment la même API d'auth.
# 1. Cloner et entrer dans le dossier
git clone <url> SmartLock-Dashboard && cd SmartLock-Dashboard
# 2. Installer les dépendances
just install
# 3. Lancer en dev (http://localhost:5173)
just dev-localOu via Docker (recommandé pour reproduire l'environnement) :
just dev- Node.js 22+ et npm
- Docker + Docker Compose v2 (optionnel mais recommandé)
- just (installation —
brew install justsur macOS,cargo install justpartout sinon)
just install # npm install
just dev-local # Vite dev server sur :5173 (sans conteneur)
just check # svelte-check (types)Le développement en conteneur garantit que tous les contributeurs travaillent avec la même version de Node, sans toucher leur installation locale. Le code est monté en bind mount, le HMR fonctionne normalement.
just dev # build + up détaché sur :5173 (recipe idempotente)
just dev-logs # suivre les logs du conteneur
just stop # arrêter dev + prodLe docker/Dockerfile de production utilise @sveltejs/adapter-node et tourne sur Node 22 alpine. Le compose.prod.yaml charge automatiquement web/.env côté host, applique le hardening (read_only, cap_drop ALL, no-new-privileges, init, non-root, limites mem/cpu/pids, log rotation) et expose un healthcheck GET /health (30s interval, 3 retries).
just prod # build + up détaché sur 127.0.0.1:3000 (recipe idempotente)
just prod-logs # suivre les logs du conteneur
just stop # arrêter dev + prodLe conteneur expose /health qui retourne {"status":"ok"} — utilisé pour le healthcheck Docker et toute supervision externe.
just check # svelte-check (types)
just lint # eslint
just test # vitest unit
just test-e2e # playwright + axe-core a11y smoke
just ci # tout enchaîné, fail-fastLa suite E2E suppose le dev bypass (pas de KEYCLOAK_ISSUER dans web/.env). Sous Keycloak réel, les specs auth (armoires, items, stocks, roles, logs, home, palette, a11y des routes auth) sont skippées proprement avec un motif clair dans le rapport.
just (sans argument) affiche le menu complet. Résumé :
| Catégorie | Recipe | Effet |
|---|---|---|
| Repo | update |
git pull --rebase |
| Repo | install |
npm install dans web/ |
| Dev local | dev-local |
Vite dev server sur :5173 (sans conteneur) |
| Dev local | build |
Build SvelteKit prod dans web/build |
| Dev local | preview |
Sert le build prod localement |
| Qualité | check |
svelte-check |
| Qualité | lint |
eslint |
| Qualité | test |
vitest unit |
| Qualité | test-e2e |
playwright + axe |
| Qualité | ci |
check + lint + test + test-e2e (fail-fast) |
| Docker dev | dev |
Tear-down + rebuild + up détaché sur :5173 |
| Docker dev | dev-logs |
Logs du conteneur dev |
| Docker prod | prod |
Tear-down + rebuild + up détaché sur 127.0.0.1:3000 (image hardened) |
| Docker prod | prod-logs |
Logs du conteneur prod |
| Nettoyage | stop |
Arrête dev + prod (volumes conservés) |
| Nettoyage | clean |
Reset total — conteneurs, volumes, caches BuildKit, artefacts locaux |
dev et prod sont idempotents : chaque appel commence par docker compose down -v --remove-orphans avant le up --build, donc pas besoin de variantes restart-*.
Aucune n'est nécessaire pour démarrer en dev sur l'UI seule. Au fur et à mesure que les couches s'ajoutent, créer un web/.env (non commité — SvelteKit lit le .env depuis la racine du projet, qui est web/) :
Le fichier de référence est web/.env.example. À reporter dans web/.env (non commité) :
# Backend API
PUBLIC_SMARTLOCK_API_URL=https://api.smartlock.devinci-fablab.fr
# Keycloak (realm dev pour tests, prod pour production)
KEYCLOAK_ISSUER=https://auth.devinci-fablab.fr/realms/dev
KEYCLOAK_CLIENT_ID=smartlock-dashboard
KEYCLOAK_CLIENT_SECRET=... # depuis Keycloak admin → Credentials
KEYCLOAK_REDIRECT_URI=https://dashboard.smartlock.devinci-fablab.fr/login/callback
KEYCLOAK_POST_LOGOUT_URI=https://dashboard.smartlock.devinci-fablab.fr
# Cookie de session SvelteKit (32+ bytes random — `openssl rand -hex 32`)
SESSION_SECRET=...Pas de DATABASE_URL ni de credentials rustfs côté dashboard : le dashboard ne parle ni à PostgreSQL ni à rustfs directement, c'est l'API qui en a la responsabilité.
Mode dev bypass : si KEYCLOAK_ISSUER est vide en dev, le dashboard authentifie un faux user T0 avec toutes les capacités — utile pour travailler sur le chrome et les routes sans monter Keycloak.
CDC.md— cahier des charges complet : modèle ACM en tiers, rôles système et personnalisés, flagsmanager/role_admin, cycle de vie compte, capacités spécifiques, audit log, workflow Trésorerie, divergences avec l'API d'auth actuelle.SmartLock-Authentication-Authorization— API backend FastAPI, source de vérité pour identités, rôles, permissions, audit. Le dashboard est son client.docs/superpowers/plans/— historique des phases d'implémentation (P0 foundation → P4 audit + polish) avec, pour chaque phase, ce qui a été livré, ce qui a été explicitement coupé, et les dépendances backend.
Voir LICENSE.