Administrateurs système

Parqueo — documentation d'installation

Installation, configuration et exploitation. Public visé : la personne qui déploie et maintient l'application.


1. Prérequis#

Chemin recommandé (§2.1) — Docker avec le plugin Compose, et rien d'autre. Ni Node.js, ni PostgreSQL, ni reverse proxy à installer : tout est fourni par les images, certificat TLS compris.

Installation manuelle (§2.4 et suivantes), si vous ne voulez pas de conteneurs :

Composant Version Remarque
Node.js 20 LTS minimum, 22 recommandé
PostgreSQL 14 minimum
Reverse proxy nginx, Caddy, Traefik… requis (voir §2.8)

Optionnels, activés seulement s'ils sont configurés : un serveur SMTP (notifications), une boîte IMAP (collecteur email), un tenant Microsoft Entra ID (SSO).

Ressources : l'API est un processus Node mono-thread léger. Pour quelques dizaines d'utilisateurs simultanés, 1 vCPU et 1 Go de RAM suffisent, PostgreSQL compris. L'espace disque est dicté par les pièces jointes (10 Mo par fichier au maximum).


2. Installation#

Deux chemins. Le premier convient à la très grande majorité des cas et ne demande que Docker ; le second s'adresse à qui ne veut pas de conteneurs.

§2.1 à §2.3 — en une commande §2.4 à §2.9 — installation manuelle
Prérequis Docker + plugin Compose Node.js, PostgreSQL, reverse proxy
Certificat TLS obtenu et renouvelé tout seul à votre charge (Let's Encrypt)
Mise à jour docker compose pull && up -d git pull, rebuild, redémarrage
À construire rien, les images sont publiées client et dépendances

2.1 Installation en une commande#

curl -fsSL https://parqueo.fr/install.sh | sh -s -- --domaine parqueo.exemple.fr

Le script vérifie Docker, télécharge la configuration, génère les secrets, tire les images publiées et démarre la stack. Il ne compile rien : ni git, ni Node, ni les sources ne sont nécessaires sur le serveur.

Sans --domaine, Parqueo répond en HTTP sur le port 80 — pratique pour évaluer sur un réseau interne. Avec un nom de domaine, Caddy demande un certificat Let's Encrypt au premier démarrage et le renouvelle seul ; les ports 80 et 443 doivent alors être joignables depuis Internet, le 80 servant à la validation du domaine et à la redirection vers HTTPS.

Élément Détail
Répertoire ./parqueo (surchargeable par --repertoire)
Configuration .env, créé en 600, secrets générés
Données volumes parqueo-db, parqueo-uploads, parqueo-caddy (certificats)
Images ghcr.io/lcanet2/parqueo-api et -web

JWT_SECRET et POSTGRES_PASSWORD sont générés une fois et ne doivent plus changer : le premier invalide toutes les sessions ouvertes, le second rend la base inaccessible. Une réexécution du script conserve un .env existant.

Pour une installation hors ligne ou depuis un miroir interne, PARQUEO_SOURCE redirige le téléchargement de la configuration.

2.2 Premier démarrage#

Ouvrez l'adresse affichée par le script. Le premier écran vous fait créer le compte administrateur : Parqueo n'est livré avec aucun mot de passe par défaut.

La route qui crée ce compte ne répond que tant que la base ne contient aucun utilisateur, et se referme définitivement ensuite. Le critère est bien « aucun utilisateur » et non « aucun administrateur » : rouvrir l'installation sur une base en service laisserait n'importe qui s'y déclarer administrateur.

Ensuite : créez les équipes et les catégories, puis parcourez Paramètres.

L'amorçage automatique de la base (SEED_ON_START, actif par défaut) ne pose qu'une catégorie et une équipe, sans lesquelles aucun ticket n'est ouvrable. Il ne crée aucun compte, et reste sans effet sur une base déjà amorcée.

2.3 Exploitation courante#

Depuis le répertoire d'installation :

docker compose pull && docker compose up -d   # mettre à jour
docker compose down                           # arrêter, données conservées
docker compose down -v                        # tout effacer, volumes compris
docker compose logs -f api                    # suivre les journaux

Les migrations de base sont appliquées au démarrage du conteneur api : une mise à jour ne demande aucune commande supplémentaire. Pour les sauvegardes, voir §7.


Les sections suivantes décrivent l'installation manuelle, sans Docker. Exemple pour une machine Debian/Ubuntu, application déployée dans /opt/parqueo, servie par nginx sur https://parqueo.exemple.fr.

2.4 Utilisateur système et code#

sudo adduser --system --group --home /opt/parqueo parqueo
sudo -u parqueo git clone https://github.com/Lcanet2/parqueo.git /opt/parqueo

2.5 Base de données#

sudo -u postgres psql -c "CREATE USER parqueo WITH PASSWORD '<mot-de-passe-solide>';"
sudo -u postgres psql -c "CREATE DATABASE parqueo OWNER parqueo;"

2.6 API#

cd /opt/parqueo/server
sudo -u parqueo npm ci
sudo -u parqueo npx prisma generate
sudo -u parqueo cp .env.example .env
sudo -u parqueo nano .env               # voir §3
sudo chmod 600 .env
sudo -u parqueo npx prisma migrate deploy
sudo -u parqueo npm run seed

Installez toutes les dépendances (npm ci, sans --omit=dev) : la CLI Prisma qui applique les migrations est une dépendance de développement. migrate deploy applique les migrations existantes sans jamais réinitialiser la base — c'est la commande de production. npm run seed pose la catégorie et l'équipe par défaut ; il ne crée aucun compte, celui-ci se crée au premier accès (§2.2).

Générer un JWT_SECRET solide :

node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"

L'API refuse de démarrer sans JWT_SECRET, ou avec un secret de moins de 16 caractères, et le dit explicitement dans le journal. Elle démarrait autrefois sans, pour n'échouer qu'à la première tentative de connexion.

2.7 Client#

cd /opt/parqueo/client
sudo -u parqueo npm ci
sudo -u parqueo npm run build       # produit client/dist

dist/ est un ensemble de fichiers statiques : aucun processus Node ne tourne pour le front.

2.8 Reverse proxy#

Le client appelle /api en relatif : le proxy doit servir le front et l'API sur la même origine. C'est la seule contrainte de déploiement structurante.

server {
    listen 443 ssl http2;
    server_name parqueo.exemple.fr;

    ssl_certificate     /etc/letsencrypt/live/parqueo.exemple.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/parqueo.exemple.fr/privkey.pem;

    # Pièces jointes : 10 Mo côté application ; la limite nginx par défaut (1 Mo)
    # les rejetterait avant d'atteindre Node. 12M couvre aussi les rapports
    # d'inventaire (5 Mo sur /api/inventory). Si vous poussez des inventaires
    # GLPI très volumineux, l'application accepte jusqu'à 15 Mo sur
    # /api/inventory/glpi : montez alors cette valeur à 16M.
    client_max_body_size 12M;

    root /opt/parqueo/client/dist;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;   # SPA : repli sur index.html
    }

    location /api {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name parqueo.exemple.fr;
    return 301 https://$host$request_uri;
}

Le jeton SSO transite en query string (?sso_token=…) : si vous journalisez les URL complètes, filtrez ce paramètre.

2.9 Service systemd#

/etc/systemd/system/parqueo.service :

[Unit]
Description=Parqueo API
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=parqueo
Group=parqueo
# Impératif : uploads/ est résolu relativement au répertoire de travail.
WorkingDirectory=/opt/parqueo/server
ExecStart=/usr/bin/node src/index.js
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ReadWritePaths=/opt/parqueo/server/uploads

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now parqueo
sudo systemctl status parqueo

Le .env est lu par dotenv depuis le répertoire de travail (pas besoin de EnvironmentFile). WorkingDirectory est critique : les pièces jointes sont écrites dans uploads/ en chemin relatif ; démarrer ailleurs rend les fichiers existants introuvables.

Le premier démarrage se déroule ensuite comme au §2.2.


3. Référence des variables d'environnement#

Fichier server/.env. Seules les quatre premières sont nécessaires au démarrage.

3.1 Cœur#

Variable Défaut Rôle
DATABASE_URL chaîne de connexion PostgreSQL (requis)
JWT_SECRET clé de signature des jetons (requis) — la changer déconnecte tout le monde
PORT 4000 port d'écoute de l'API
CLIENT_ORIGIN origine autorisée par CORS ; en production, l'URL publique du site
APP_URL http://localhost:$PORT URL publique de l'API, utilisée dans les liens des emails

3.2 Email sortant#

Variable Défaut Rôle
SMTP_HOST vide si vide, aucun email n'est envoyé
SMTP_PORT 587
SMTP_USER / SMTP_PASS vide authentification, omise si SMTP_USER est vide
SMTP_FROM expéditeur, ex. Parqueo <no-reply@exemple.fr>

3.3 Clôture automatique#

Variable Défaut Rôle
AUTO_CLOSE_DAYS 7 valeur initiale seulement ; une fois enregistré dans Paramètres, c'est la base qui fait foi

3.4 SSO Microsoft Entra ID#

Variable Défaut Rôle
SSO_TENANT_ID vide identifiant d'annuaire — les trois premières activent le SSO
SSO_CLIENT_ID vide identifiant d'application
SSO_CLIENT_SECRET vide secret client
SSO_REDIRECT_URI APP_URL + /api/auth/sso/callback doit correspondre exactement à l'app registration
SSO_ALLOWED_DOMAINS vide garde-fou : domaines email acceptés, séparés par des virgules
SSO_POST_LOGIN_URL CLIENT_ORIGIN où renvoyer le navigateur après connexion

3.5 Collecteur email#

Variable Défaut Rôle
IMAP_HOST vide si vide, le collecteur est désactivé
IMAP_PORT 993
IMAP_USER / IMAP_PASS identifiants de la boîte
IMAP_TLS true false pour désactiver TLS
IMAP_POLL_SECONDS 60 intervalle de relève, plancher à 15 s
IMAP_CATEGORY_ID première catégorie catégorie des tickets créés par email

3.6 Inventaire automatique#

Variable Défaut Rôle
INVENTORY_TOKEN vide si vide, les endpoints /api/inventory sont désactivés ; sinon, secret présenté par les agents/scripts
INTUNE_ENABLED false connecteur Intune (permission Graph requise, voir §6)
INTUNE_SYNC_HOURS 6 cadence de la synchro Intune (0 = seulement à la demande)
SNMP_ENABLED false scan réseau SNMP
SNMP_RANGES vide plages CIDR à scanner, séparées par des virgules
SNMP_COMMUNITY public communauté SNMP v2c (lecture seule)
SNMP_SCAN_HOURS 0 cadence du scan (0 = seulement à la demande)

4. Configuration du SSO Microsoft Entra ID#

4.1 Côté Entra (portail Azure)#

  1. Inscriptions d'applications → Nouvelle inscription.
  2. Nom : Parqueo. Types de comptes : comptes de cet annuaire uniquement.
  3. URI de redirection, type Web : https://parqueo.exemple.fr/api/auth/sso/callback.
  4. Relever l'ID d'application (client) et l'ID d'annuaire (locataire).
  5. Certificats et secrets → Nouveau secret client. Copier la valeur (elle n'est plus affichée ensuite) et noter la date d'expiration.
  6. Autorisations d'API : openid, profile, email (déléguées Microsoft Graph, généralement déjà présentes).

4.2 Côté Parqueo#

SSO_TENANT_ID="<id-annuaire>"
SSO_CLIENT_ID="<id-application>"
SSO_CLIENT_SECRET="<valeur-du-secret>"
SSO_REDIRECT_URI="https://parqueo.exemple.fr/api/auth/sso/callback"
SSO_ALLOWED_DOMAINS="exemple.fr"

Redémarrer l'API : le bouton « Se connecter avec Microsoft » apparaît automatiquement sur la page de connexion.

4.3 Comportement#


5. Configuration du collecteur email#

Dédiez une boîte au support (ex. support@exemple.fr) — le collecteur marque les messages comme lus et relève toute la boîte de réception.

IMAP_HOST="imap.exemple.fr"
IMAP_PORT=993
IMAP_USER="support@exemple.fr"
IMAP_PASS="<mot-de-passe>"
IMAP_POLL_SECONDS=60
IMAP_CATEGORY_ID=1
SMTP_FROM="Support <support@exemple.fr>"

Fonctionnement :

Conseil : utilisez la même adresse pour SMTP_FROM et IMAP_USER, pour que les réponses aux notifications reviennent dans le collecteur. Au démarrage, les journaux affichent [collecteur] boîte support@exemple.fr relevée toutes les 60s.


6. Configuration de l'inventaire automatique#

L'inventaire peut se remplir seul via trois moyens, combinables. Tous convergent vers le même traitement (déduplication par uuid puis numéro de série ; les champs saisis à la main ne sont jamais écrasés). Guide d'usage côté interface : documentation fonctionnelle §9.

6.1 Push par agent ou script (token partagé)#

INVENTORY_TOKEN="<long-secret-aléatoire>"

Sans lui, POST /api/inventory et /api/inventory/glpi répondent 404. Les appelants le présentent en en-tête X-Parqueo-Token (ou Authorization: Bearer).

6.2 Connecteur Microsoft Intune (sans agent)#

Pour un parc enrôlé dans Intune, Parqueo interroge Microsoft Graph — rien à déployer sur les postes.

  1. Sur l'app registration Entra du SSO (§4), ajoutez la permission applicative Graph DeviceManagementManagedDevices.Read.All, puis accordez le consentement administrateur.

  2. Dans le .env (les SSO_* doivent déjà être renseignés) :

    INTUNE_ENABLED=true
    INTUNE_SYNC_HOURS=6
    
  3. Redémarrez l'API : une synchro part au démarrage, puis toutes les INTUNE_SYNC_HOURS. Un admin peut aussi la lancer depuis Inventaire → Synchroniser Intune.

6.3 Scan réseau SNMP (sans agent)#

Pour les équipements sans agent (imprimantes, switches, NAS, onduleurs) :

SNMP_ENABLED=true
SNMP_RANGES="192.168.1.0/24,10.0.0.0/28"
SNMP_COMMUNITY="public"
SNMP_SCAN_HOURS=0

Le serveur doit joindre les équipements en UDP 161 (pare-feux). Le scan se lance depuis Inventaire → Scanner le réseau, et en tâche de fond si SNMP_SCAN_HOURS > 0. Préférez un adressage fixe : l'identité de déduplication d'un équipement réseau dérive de son nom système.


7. Sauvegarde et restauration#

Deux choses à sauvegarder : la base et server/uploads/. Le reste se réinstalle depuis Git.

7.1 Sauvegarde#

#!/bin/sh
# /opt/parqueo/backup.sh
set -e
DEST=/var/backups/parqueo
STAMP=$(date +%F)
mkdir -p "$DEST"

sudo -u postgres pg_dump -Fc parqueo > "$DEST/parqueo-$STAMP.dump"
tar czf "$DEST/uploads-$STAMP.tar.gz" -C /opt/parqueo/server uploads

find "$DEST" -type f -mtime +30 -delete

Dans la crontab root : 15 2 * * * /opt/parqueo/backup.sh.

N'oubliez pas server/.env (il contient JWT_SECRET et les secrets SMTP/SSO) : sauvegardez-le séparément, dans un coffre — pas dans la même archive que la base.

7.2 Restauration#

sudo systemctl stop parqueo
sudo -u postgres dropdb parqueo
sudo -u postgres createdb parqueo -O parqueo
sudo -u postgres pg_restore -d parqueo /var/backups/parqueo/parqueo-2026-07-23.dump
sudo tar xzf /var/backups/parqueo/uploads-2026-07-23.tar.gz -C /opt/parqueo/server
sudo chown -R parqueo:parqueo /opt/parqueo/server/uploads
sudo systemctl start parqueo

Restaurer la base sans les uploads laisse des pièces jointes référencées mais absentes du disque : les deux vont ensemble.


8. Mise à jour#

Installation par images (§2.1) — deux commandes, depuis le répertoire d'installation :

docker compose pull
docker compose up -d

Les migrations de base sont appliquées au démarrage du conteneur api. Pour rester sur une version précise plutôt que sur la dernière, renseignez PARQUEO_VERSION dans le .env (par exemple PARQUEO_VERSION=0.1.0).

Installation manuelle (§2.4) :

sudo systemctl stop parqueo

cd /opt/parqueo
sudo -u parqueo git pull

cd server
sudo -u parqueo npm ci
sudo -u parqueo npx prisma generate
sudo -u parqueo npx prisma migrate deploy

cd ../client
sudo -u parqueo npm ci
sudo -u parqueo npm run build

sudo systemctl start parqueo

Sauvegardez avant toute mise à jour embarquant une migration : Prisma ne propose pas de retour arrière automatique.


9. Exploitation#

9.1 Journaux#

Tout part sur la sortie standard, donc dans journald :

sudo journalctl -u parqueo -f

Préfixes utiles :

Préfixe Origine
[collecteur] collecteur IMAP
[clôture auto] clôture automatique horaire
[intune] / [snmp] synchro Intune / scan réseau
[mail] échec d'envoi SMTP
[workflow « … »] action de workflow en échec

9.2 Supervision#

9.3 Tâches planifiées internes#

Aucune crontab à créer : la clôture automatique (horaire), le collecteur email (selon IMAP_POLL_SECONDS), et — si activées — la synchro Intune (INTUNE_SYNC_HOURS) et le scan SNMP (SNMP_SCAN_HOURS) tournent dans le processus de l'API et s'arrêtent avec lui.


10. Dépannage#

Symptôme Cause probable Correction
EADDRINUSE :::4000 une autre instance tourne déjà arrêter le processus ou changer PORT
Page blanche, 404 sur les routes internes nginx sans repli SPA ajouter try_files $uri $uri/ /index.html
Toutes les requêtes API en 401 JWT_SECRET modifié, ou jeton expiré (7 j) se reconnecter
403 « Accès refusé » sur une page admin rôle insuffisant, ou rôle changé sans reconnexion se déconnecter/reconnecter : le rôle est figé dans le jeton
413 à l'envoi d'une pièce jointe client_max_body_size nginx trop bas passer à 12M
413 sur /api/inventory depuis un agent rapport au-delà de 5 Mo (15 Mo pour /glpi), ou client_max_body_size nginx trop bas relever client_max_body_size ; réduire le périmètre remonté par l'agent
L'agent d'inventaire ne remonte rien, sans erreur visible l'agent poste sur une autre URL, ou INVENTORY_TOKEN absent (l'API répond alors 404) vérifier le token et les journaux de l'agent
« Fichier manquant ou type non autorisé » extension hors liste blanche (images, PDF, docs bureautiques, archives, logs) convertir ou compresser le fichier
Aucun email reçu SMTP_HOST vide les journaux affichent [mail non envoyé…]
Emails entrants ignorés expéditeur inconnu de Parqueo créer le compte, ou l'importer en masse
Boucle de tickets créés par email SMTP_FROMIMAP_USER et boîte auto-répondeuse aligner les deux adresses
Bouton SSO absent une des trois variables SSO_* manque vérifier GET /api/auth/config
AADSTS50011 (redirect URI) l'URI ne correspond pas à l'app registration aligner SSO_REDIRECT_URI au caractère près
Pièces jointes introuvables après un déplacement processus lancé hors de server/ corriger WorkingDirectory
Migration bloquée schéma divergent npx prisma migrate status ; ne jamais lancer migrate dev en production