2026-03-16 16:52:55 +01:00

58 KiB
Raw Blame History

APIs Externes - Documentation Complète

Auteur : Équipe 4NK Date : 2026-01-27 Version : 2.0

Référence unique (checks de déploiement) : docs/DEPLOYMENT.md#cartographie-des-checks-de-déploiement-source-unique

Ce document consolide toute la documentation relative aux APIs externes utilisées par LeCoffre.io :

  • API d'ancrage Bitcoin Signet
  • API Annuaire
  • API agent IA notaire (ai_working_help)

API agent IA notaire (ai_working_help)

Vue d'ensemble

Les backends des applications métier appellent l'API ai_working_help (service agent IA notaire) pour les opérations ask, enqueue et getResponse. L'URL de base est configurée via NOTARY_AI_AGENT_URL sans segment projet dans le chemin (ex. http://192.168.1.173:3020/v1). Le projet (et lenv) sont identifiés par le token Bearer : lAPI cherche dans tous les projets et tous les envs le fichier projects/<id>/.secrets/<env>/ia_token dont le contenu correspond au token. Le filtrage IP 192.168.1.* est géré côté API.

Authentification

Contrat obligatoire : tous les appels à l'API agent doivent envoyer le header :

Authorization: Bearer <token>

Le token est de la forme base + env (ex. nicolecoffreiotest, nicolecoffreiopprod). env est le nom denvironnement (test, pprod, prod), à adapter selon les environnements. La clé en base est NOTARY_AI_AGENT_TOKEN ; sa valeur doit correspondre à celle côté ia_dev dans projects/<id>/.secrets/<env>/ia_token pour lenv concerné. Le backend envoie ce header sur :

  • POST {NOTARY_AI_AGENT_URL}/ask — question synchrone (legacy)
  • POST {NOTARY_AI_AGENT_URL}/enqueue — mise en file (spooler)
  • GET {NOTARY_AI_AGENT_URL}/response/:request_uid — récupération de la réponse (poll après enqueue)

Implémentation côté backend : lecoffre-back-main/src/services/notary/NotaryFolderAIService/NotaryFolderAIService.ts (méthodes ask, enqueue, getResponse). Le header n'est ajouté que si NOTARY_AI_AGENT_TOKEN est renseigné ; si l'API exige l'authentification, la clé doit être définie dans .secrets/<env>/env-full et injectée en base lors du déploiement.


Consolidation des évolutions backend récentes

Cette section consolide les éléments issus des documents de travail intégrés intégrés dans la documentation API pérenne.

Intégration Genapi / iNot

  • Endpoint backend ajouté pour l'envoi unitaire vers iNot : POST /api/v1/notary/documents/:uid/push-inot.
  • Flux implémenté : OAuth token, recherche dossier par numéro, création eDocument, upload binaire.
  • Contrôle métier côté backend : document validé requis avant push.
  • Gestion d'indisponibilité normalisée avec code métier GENAPI_API_UNAVAILABLE.

Standardisation des erreurs d'intégrations externes

  • Normalisation des mappings d'indisponibilité *_API_UNAVAILABLE sur plusieurs intégrations (GENAPI, ANNUAIRE, ANCHORAGE, IDNOT, OPENID, IPFS, MAILCHIMP, STRIPE).
  • Helpers partagés de propagation d'erreurs et de journalisation externe introduits pour homogénéiser les contrôleurs, helpers métier et services.
  • Conventions de nommage d'opérations externes unifiées (integration.operation) pour la traçabilité.

Correctifs de robustesse API

  • Correctif d'interop CJS/ESM sur l'hydratation des ressources ID.Not (User.hydrate is not a function) via normalisation des constructeurs hydratables.
  • Correctif de fusion PDF batch : normalisation des noms de fichiers multipart pour éviter les erreurs d'encodage WinAnsi pendant la génération de PDF fusionnés.

API d'Ancrage Bitcoin Signet

Vue d'ensemble

L'API d'ancrage Bitcoin Signet est un service autonome qui gère l'ancrage de hashes de documents sur la blockchain Bitcoin Signet. Elle est séparée du backend principal pour :

  • Isolation : Le nœud Bitcoin et l'API d'ancrage tournent sur un serveur dédié
  • Performance : Évite la surcharge du backend principal avec des opérations blockchain lentes
  • Sécurité : Accès restreint par API key
  • Scalabilité : Peut gérer une file d'attente d'ancrages indépendamment

Architecture

lecoffre-back-main (prod.lecoffreio.4nkweb.com:3009)
         │
         ├─── HTTP POST ───> lecoffre-anchor-api (anchorage.certificator.4nkweb.com:3004)
         │                           │
         │                           ├─── JSON-RPC ───> Bitcoin Node (localhost:38332)
         │                           └─── Callback ───> lecoffre-back-main
         │
         └─── Continue processing...

Infrastructure

Serveur et déploiement

  • Serveur : Serveur bitcoin (où le nœud Bitcoin Signet est déployé)
  • Répertoire : /home/ncantu/Bureau/code/bitcoin/api-anchorage
  • URL publique : https://anchorage.certificator.4nkweb.com
  • Port interne : 3004
  • Service systemd : anchor-api.service ou api-anchorage.service (user service)

Proxy Nginx

Le proxy 192.168.1.100 route toutes les requêtes vers anchorage.certificator.4nkweb.com vers le serveur bitcoin (port 3004).

Configuration : /etc/nginx/sites-enabled/certificator.4nkweb.com

Configuration

Variables d'environnement

Le fichier .env est situé dans /home/ncantu/Bureau/code/bitcoin/api-anchorage/.env :

PORT=3004
NODE_ENV=production
BITCOIN_RPC_HOST=127.0.0.1
BITCOIN_RPC_PORT=18443
BITCOIN_COOKIE_PATH=/home/ncantu/.bitcoin/signet/.cookie
# Note: Utiliser 127.0.0.1 au lieu de localhost pour forcer IPv4

Service systemd

Fichier : /home/ncantu/.config/systemd/user/anchor-api.service

[Unit]
Description=Anchor API Service - anchorage.certificator.4nkweb.com:3004
After=network-online.target signet.service
Wants=network-online.target

[Service]
Type=simple
WorkingDirectory=/home/ncantu/Bureau/code/bitcoin/api-anchorage
EnvironmentFile=/home/ncantu/Bureau/code/bitcoin/api-anchorage/.env
ExecStart=/bin/bash -c 'export PORT=3004; export NVM_DIR="/home/ncantu/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; cd /home/ncantu/Bureau/code/bitcoin/api-anchorage && npm run dev'
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=anchor-api

NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=default.target

Nœud Bitcoin

  • Service : signet.service (user service)
  • Port RPC : 18443 (localhost, Signet)
  • Port P2P : 38333 (exposé via NAT)
  • Cookie : /home/ncantu/.bitcoin/signet/.cookie
  • Configuration : /srv/4NK/signet.4nkweb.com/bitcoin/bitcoin.conf

Fonctionnement

L'API d'ancrage fonctionne de manière synchrone :

  • Le traitement Bitcoin se fait avant la réponse HTTP
  • L'API retourne directement le txid avec toutes les informations
  • Code HTTP 200 (OK) au lieu de 202 (Accepted)
  • Pas de UUID, pas de status "pending", pas besoin de status endpoint

Analyse du fonctionnement réel

Code réel de l'API :

// AnchorController.anchorDocument()
// Ancrer directement sur Bitcoin (synchrone)
const txid = await this.bitcoinService.anchorHash(hash);

// Récupérer le statut de la transaction
const txStatus = await this.bitcoinService.getTransactionStatus(txid);

const response: IAnchorResponse = {
  txid,
  confirmations: txStatus.confirmations,
  block_height: txStatus.block_height,
  status: 'confirmed',
};

res.status(200).json(response); // Retourne directement { txid: "...", status: "confirmed", ... }

Fonctionnement :

  • L'API attend que le traitement Bitcoin soit terminé (await)
  • Elle retourne directement le txid avec toutes les informations (confirmations, block_height)
  • Code HTTP 200 (OK) au lieu de 202 (Accepted)
  • Pas de UUID, pas de status "pending"

Problème initial résolu : Le backend s'attendait à recevoir un UUID avec status: "pending" et HTTP 202, mais l'API fonctionne de manière synchrone et retourne directement le txid avec HTTP 200. Le backend a été aligné sur le fonctionnement réel de l'API.

Endpoints API

Health Check

GET /health

Réponse :

{
  "ok": true,
  "service": "anchor-api",
  "bitcoin": {
    "connected": true,
    "blocks": 198769
  },
  "timestamp": "2026-01-23T11:03:33.668Z"
}

Ancrage de document

POST /api/anchor/document
Content-Type: application/json
x-api-key: <API_KEY>

{
  "documentUid": "uuid-du-document",
  "hash": "hash-sha256-du-fichier-filigrane-64-hex",
  "callback_url": "https://prod.lecoffreio.4nkweb.com/api/v1/public/anchors/callback/document" (optionnel, non utilisé)
}

Réponse (200 OK) :

{
  "txid": "56504e002d95301ebcfb4b30eaedc5d3fd9a448e121ffdce4f356b8d34169e85",
  "status": "confirmed",
  "confirmations": 0,
  "block_height": 142062
}

Important :

  • L'API fonctionne de manière synchrone : le traitement Bitcoin se fait avant la réponse
  • Le txid est retourné directement dans la réponse (pas d'UUID)
  • Le statut est toujours "confirmed" (pas de "pending")
  • Toutes les informations sont disponibles immédiatement (confirmations, block_height)
  • Le callback_url est optionnel et appelé après la réponse si fourni

Vérification d'ancrage

POST /api/anchor/verify
Content-Type: application/json
x-api-key: <API_KEY>

{
  "hash": "hash-sha256-du-fichier-64-hex",
  "txid": "txid-bitcoin-64-hex" (optionnel)
}

Réponse (200 OK) si ancré :

{
  "verified": true,
  "anchor_info": {
    "transaction_id": "txid-bitcoin-64-hex",
    "block_height": 198775,
    "confirmations": 6,
    "timestamp": "2026-01-23T11:03:33.668Z"
  }
}

Réponse (200 OK) si non ancré :

{
  "verified": false
}

Flux de traitement

1. Demande d'ancrage (synchrone)

  1. Le backend appelle POST /api/anchor/document avec le hash du fichier filigrané

  2. L'API ancre directement le hash sur Bitcoin Signet via JSON-RPC (attente synchrone)

  3. L'API retourne 200 OK avec toutes les informations :

    {
      "txid": "56504e002d95301ebcfb4b30eaedc5d3fd9a448e121ffdce4f356b8d34169e85",
      "status": "confirmed",
      "confirmations": 0,
      "block_height": 142062
    }
    

2. Callback (optionnel)

Si callback_url a été fourni lors de la demande d'ancrage :

  1. L'API appelle POST {callback_url} avec le résultat (après la réponse HTTP) :

    {
      "document_uid": "uuid-du-document",
      "status": "confirmed",
      "txid": "txid-bitcoin-64-hex",
      "confirmations": 0,
      "block_height": 142062
    }
    
  2. Le backend reçoit le callback et peut mettre à jour l'ancrage dans la base de données (optionnel)

Types TypeScript

Interfaces de l'API

interface IAnchorRequest {
  documentUid: string;
  hash: string; // 64 hex characters
  callback_url?: string;
}

interface IAnchorResponse {
  txid: string; // Hash Bitcoin de la transaction (64 hex) - retourné directement
  status: 'confirmed'; // Toujours 'confirmed' (pas de 'pending')
  confirmations: number; // Nombre de confirmations (0 si dans le mempool)
  block_height?: number; // Hauteur du bloc si confirmé
}

interface IVerifyRequest {
  hash: string; // 64 hex characters
  txid?: string; // Optionnel pour vérifier un txid spécifique
}

interface IVerifyResponse {
  verified: boolean;
  anchor_info?: {
    transaction_id: string;
    block_height: number;
    confirmations: number;
    timestamp: string;
  };
}

Utilisation dans le backend

Le backend utilise BitcoinSignetApiHelper pour interagir avec l'API :

// 1. Ancrer un hash (synchrone)
const result = await bitcoinSignetApiHelper.anchorHashViaApi(hash, {
  resourceUid: documentUid,
  resourceType: "document"
});

// result.txid est directement disponible (pas d'UUID)
// result.status est toujours 'confirmed'
// result.confirmations et result.block_height sont disponibles

Important :

  • L'API fonctionne de manière synchrone : le txid est retourné directement
  • Le code HTTP est 200 OK (pas 202 Accepted)
  • Toutes les informations sont disponibles immédiatement
  • Pas besoin de status endpoint ou de polling

Code source

Le code source de l'API d'ancrage se trouve dans la branche v2-remote du dépôt v1 :

  • Dépôt : https://git.4nkweb.com/4nk/lecoffreio
  • Branche : v2-remote
  • Répertoire : lecoffre-anchor-api/

Structure

lecoffre-anchor-api/
├── src/
│   ├── config/
│   │   ├── bitcoin.config.ts
│   │   └── logger.ts
│   ├── controllers/
│   │   └── AnchorController.ts
│   ├── services/
│   │   ├── BitcoinService.ts
│   │   └── AnchorQueueService.ts
│   └── index.ts
├── package.json
├── tsconfig.json
└── .env

Installation et déploiement

Clonage du code

cd /srv/4NK/certificator.4nkweb.com
git clone -b v2-remote --single-branch --depth 1 git@git.4nkweb.com:4nk/lecoffreio.git temp-anchor-api
mv temp-anchor-api/lecoffre-anchor-api .
rm -rf temp-anchor-api

Installation des dépendances

cd /srv/4NK/certificator.4nkweb.com/lecoffre-anchor-api
source /home/ncantu/.nvm/nvm.sh
npm install

Configuration

  1. Créer le fichier .env avec les variables d'environnement
  2. Vérifier que le cookie Bitcoin existe et est accessible
  3. Mettre à jour le service systemd pour pointer vers le bon répertoire

Démarrage du service

systemctl --user daemon-reload
systemctl --user start anchor-api.service
systemctl --user enable anchor-api.service

Vérification

Vérifier le statut du service

systemctl --user status anchor-api.service

Vérifier les logs

journalctl --user -u anchor-api.service -f

Tester l'API depuis l'extérieur

Test simple avec curl :

curl -k https://anchorage.certificator.4nkweb.com/health

Réponse attendue :

{
  "ok": true,
  "service": "anchor-api",
  "bitcoin": {
    "connected": true,
    "blocks": 152321
  }
}

Exemple de client Node.js :

Un exemple complet de client pour tester l'API :

#!/usr/bin/env node

/**
 * Exemple basique de client pour l'API Anchor
 * Usage: node example-client.js
 */

const https = require('https');
const crypto = require('crypto');

// Configuration
const API_URL = 'https://anchorage.certificator.4nkweb.com';
const API_KEY = 'your-api-key-here'; // Remplacez par votre clé API

/**
 * Effectue une requête HTTPS
 */
function makeRequest(method, path, data = null) {
  return new Promise((resolve, reject) => {
    const url = new URL(path, API_URL);

    const options = {
      hostname: url.hostname,
      port: url.port || 443,
      path: url.pathname,
      method: method,
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': API_KEY,
      },
    };

    const req = https.request(options, (res) => {
      let body = '';

      res.on('data', (chunk) => {
        body += chunk;
      });

      res.on('end', () => {
        try {
          const json = JSON.parse(body);
          if (res.statusCode >= 200 && res.statusCode < 300) {
            resolve(json);
          } else {
            reject(new Error(`HTTP ${res.statusCode}: ${json.error || body}`));
          }
        } catch (e) {
          reject(new Error(`Parse error: ${e.message}`));
        }
      });
    });

    req.on('error', (error) => {
      reject(error);
    });

    if (data) {
      req.write(JSON.stringify(data));
    }

    req.end();
  });
}

/**
 * Vérifie l'état de l'API
 */
async function checkHealth() {
  try {
    console.log('🔍 Vérification de l\'état de l\'API...');
    const response = await makeRequest('GET', '/health');
    console.log('✅ API opérationnelle');
    console.log(`   Bitcoin connecté: ${response.bitcoin.connected ? 'Oui' : 'Non'}`);
    console.log(`   Blocs: ${response.bitcoin.blocks}`);
    return response;
  } catch (error) {
    console.error('❌ Erreur:', error.message);
    throw error;
  }
}

/**
 * Ancre un document sur Bitcoin Signet
 */
async function anchorDocument(documentUid, hash, callbackUrl = null) {
  try {
    console.log(`\n📎 Ancrage du document: ${documentUid}`);
    console.log(`   Hash: ${hash.substring(0, 16)}...`);

    const data = {
      documentUid,
      hash,
    };

    if (callbackUrl) {
      data.callback_url = callbackUrl;
    }

    const response = await makeRequest('POST', '/api/anchor/document', data);

    console.log('✅ Document ancré avec succès');
    console.log(`   Transaction ID: ${response.txid}`);
    console.log(`   Confirmations: ${response.confirmations}`);
    if (response.block_height) {
      console.log(`   Block height: ${response.block_height}`);
    }

    return response;
  } catch (error) {
    console.error('❌ Erreur lors de l\'ancrage:', error.message);
    throw error;
  }
}

/**
 * Vérifie si un hash est ancré
 */
async function verifyAnchor(hash, txid = null) {
  try {
    console.log(`\n🔍 Vérification de l'ancrage...`);
    console.log(`   Hash: ${hash.substring(0, 16)}...`);

    const data = { hash };
    if (txid) {
      data.txid = txid;
      console.log(`   Transaction ID: ${txid.substring(0, 16)}...`);
    }

    const response = await makeRequest('POST', '/api/anchor/verify', data);

    if (response.verified) {
      console.log('✅ Hash vérifié et ancré');
      if (response.anchor_info) {
        console.log(`   Transaction ID: ${response.anchor_info.transaction_id}`);
        console.log(`   Block height: ${response.anchor_info.block_height}`);
        console.log(`   Confirmations: ${response.anchor_info.confirmations}`);
      }
    } else {
      console.log('❌ Hash non trouvé dans la blockchain');
    }

    return response;
  } catch (error) {
    console.error('❌ Erreur lors de la vérification:', error.message);
    throw error;
  }
}

/**
 * Fonction principale
 */
async function main() {
  console.log('=== Client API Anchor ===\n');

  try {
    // 1. Vérifier l'état de l'API
    await checkHealth();

    // 2. Générer un hash de test
    const documentContent = `Document de test - ${new Date().toISOString()}`;
    const hash = crypto.createHash('sha256').update(documentContent).digest('hex');
    const documentUid = `doc-${Date.now()}`;

    console.log(`\n📄 Document de test:`);
    console.log(`   UID: ${documentUid}`);
    console.log(`   Hash: ${hash}`);

    // 3. Ancrer le document
    const anchorResult = await anchorDocument(documentUid, hash);

    // 4. Attendre un peu pour que la transaction soit propagée
    console.log('\n⏳ Attente de 3 secondes pour la propagation...');
    await new Promise((resolve) => setTimeout(resolve, 3000));

    // 5. Vérifier l'ancrage
    await verifyAnchor(hash, anchorResult.txid);

    console.log('\n✅ Tous les tests sont passés avec succès!');

  } catch (error) {
    console.error('\n❌ Erreur:', error.message);
    process.exit(1);
  }
}

// Exécuter le script
if (require.main === module) {
  main();
}

// Exports pour utilisation comme module
module.exports = {
  checkHealth,
  anchorDocument,
  verifyAnchor,
};

Utilisation :

# Modifier API_KEY dans le script
node example-client.js

Points importants :

  • L'API retourne directement le txid dans la réponse (fonctionnement synchrone)
  • Le code HTTP est 200 OK (pas 202 Accepted)
  • Toutes les informations sont disponibles immédiatement (confirmations, block_height)

Bonnes pratiques

Utilisation de l'API

L'API fonctionne de manière synchrone et retourne directement toutes les informations :

const result = await bitcoinSignetApiHelper.anchorHashViaApi(hash, {
  resourceUid: documentUid,
  resourceType: "document",
});

// result.txid est directement disponible
// result.status est "confirmed"
// result.confirmations et result.block_height sont disponibles

Avantages :

  • Pas besoin de status endpoint ou de polling
  • Toutes les informations sont disponibles immédiatement
  • Comportement prévisible et fiable

Gestion des erreurs

Erreurs bloquantes : Si l'API retourne une erreur (HTTP 500), c'est un problème bloquant :

try {
  const result = await bitcoinSignetApiHelper.anchorHashViaApi(hash, options);
  // Le txid est obligatoire et doit être présent
  if (!result.txid) {
    throw new Error("Txid manquant (BLOQUANT)");
  }
} catch (error) {
  // Toute erreur est bloquante, le document ne peut pas être créé
  throw error;
}

Callback (optionnel)

Le callback_url est optionnel et appelé après la réponse HTTP. Il peut être utilisé pour :

  • Mise à jour asynchrone de la base de données
  • Notification d'autres services
  • Logging supplémentaire

Note : Le callback n'est pas nécessaire car toutes les informations sont déjà dans la réponse initiale.

Dépannage

Le service ne démarre pas

  1. Vérifier que le répertoire existe : /srv/4NK/certificator.4nkweb.com/lecoffre-anchor-api
  2. Vérifier que les dépendances sont installées : npm install
  3. Vérifier les logs : journalctl --user -u anchor-api.service -n 50

Erreur "Bitcoin node not available"

  1. Vérifier que le nœud Bitcoin est actif : systemctl --user status signet.service
  2. Vérifier que le cookie existe : ls -la /home/ncantu/.bitcoin/signet/.cookie
  3. Vérifier que le port RPC est accessible : ss -tlnp | grep 38332
  4. Vérifier la configuration dans .env : BITCOIN_COOKIE_PATH doit pointer vers le bon chemin

Erreur IPv6 "connect EADDRNOTAVAIL ::1:38332"

Date : 2026-01-25

Problème : L'API d'ancrage essaie de se connecter au nœud Bitcoin via IPv6 (::1:38332) mais le nœud n'écoute que sur IPv4.

Solution : Forcer IPv4 dans la configuration de l'API d'ancrage :

Fichier : /home/ncantu/Bureau/code/bitcoin/api-anchorage/.env

# Utiliser explicitement IPv4
BITCOIN_RPC_HOST=127.0.0.1
BITCOIN_ZMQ_HOST=127.0.0.1
BITCOIN_ZMQ_PORT=38332

Vérification : Vérifier que le nœud Bitcoin écoute sur IPv4 :

ss -tlnp | grep 38332
ss -tlnp | grep 18443

Erreur "too-long-mempool-chain"

Date : 2026-01-25

Problème : Le mempool Bitcoin Signet est saturé avec plus de 25 transactions non confirmées en chaîne (limite de sécurité Bitcoin).

Solution : Le script détecte cette erreur et s'arrête immédiatement avec un message clair. L'utilisateur doit attendre que les transactions soient confirmées avant de relancer.

Vérification :

bitcoin-cli -signet getmempoolinfo
bitcoin-cli -signet getrawmempool

Erreurs de timeout (504, ESOCKETTIMEDOUT, client timeout)

Date : 2026-01-26 à 2026-01-28

Problèmes :

  • 504 Gateway Timeout : Timeout du proxy Nginx (probablement proxy_read_timeout trop court)
  • ESOCKETTIMEDOUT : Timeout socket lors de la connexion au nœud Bitcoin
  • Client timeout (AbortController) : Timeout client de 5 minutes dépassé

Solutions appliquées :

  1. Détection des timeouts : Tous les timeouts (504, 500 avec timeout socket, client timeout) sont détectés comme retryable
  2. Retry automatique : 3 tentatives avec délai de 30 secondes entre chaque tentative
  3. Timeout client : Timeout explicite de 5 minutes avec AbortController (aligné avec proxy_read_timeout Nginx)
  4. Logs améliorés : Logs détaillés pour chaque tentative, timeout, retry et succès

Configuration Nginx recommandée :

location / {
    proxy_pass http://192.168.1.103:3004;
    proxy_connect_timeout 60s;
    proxy_send_timeout 600s;      # 10 minutes
    proxy_read_timeout 600s;      # 10 minutes
}

Logs à surveiller :

  • 📡 [BitcoinSignetService] Anchor API - Tentative X/3 pour hash ...
  • ⏱️ [BitcoinSignetService] Anchor API - Timeout client déclenché après 300000ms
  • ⚠️ [BitcoinSignetService] Anchor API timeout (504/500/client) - Tentative X/3, attente 30000ms avant retry...
  • ✅ [BitcoinSignetService] Anchor API - Ancrage réussi pour hash ...

Erreur "Cannot find module './config/logger'"

Le fichier src/config/logger.ts doit être créé manuellement :

const logger = {
  info: (message: string) => console.log('[INFO] ' + new Date().toISOString() + ' - ' + message),
  error: (message: string) => console.error('[ERROR] ' + new Date().toISOString() + ' - ' + message),
  warn: (message: string) => console.warn('[WARN] ' + new Date().toISOString() + ' - ' + message),
  debug: (message: string) => console.debug('[DEBUG] ' + new Date().toISOString() + ' - ' + message),
};

export default logger;

Erreur "Anchor API returned invalid txid"

Cause : Le code s'attend à recevoir un txid Bitcoin immédiatement, mais l'API retourne un UUID (job ID).

Solution : Le code a été corrigé pour accepter un UUID et récupérer le txid via le status endpoint ou le callback.

Erreur "Anchor API transaction not ready yet"

Cause : La transaction est en cours de traitement (status "pending" sans txid).

Solution : Attendre le callback. Ne pas relancer l'ancrage. Le callback mettra à jour le txid automatiquement.

Transactions non traitées

Cause : La file d'attente est en mémoire. Si le service redémarre, les transactions en cours sont perdues.

Solution actuelle : Les transactions sont recréées automatiquement lors du traitement des fichiers.

Amélioration future : Migrer vers Redis ou une base de données pour la persistance.

Configuration dans le backend

L'URL de l'API d'ancrage est configurée dans la base de données via system_configuration :

  • Clé : ANCHORE_API_URL

  • Valeur : https://anchorage.certificator.4nkweb.com

  • Catégorie : INTEGRATION_BITCOIN

  • Clé : ANCHORE_API_KEY

  • Valeur : <API_KEY>

  • Catégorie : INTEGRATION_BITCOIN

  • Sensible : true

Historique

2026-01-23 : Alignement du backend sur le fonctionnement synchrone de l'API

Problème : Le backend s'attendait à recevoir un UUID avec status "pending" et HTTP 202, mais l'API fonctionne de manière synchrone et retourne directement le txid avec HTTP 200.

Corrections :

  1. Alignement du backend sur le fonctionnement réel de l'API (synchrone)
  2. Attente de HTTP 200 (OK) au lieu de HTTP 202 (Accepted)
  3. Récupération directe du txid dans la réponse initiale
  4. Suppression de la méthode getStatus devenue inutile
  5. Suppression de toute logique de polling ou d'attente
  6. Documentation mise à jour pour refléter le fonctionnement synchrone

Résultat : Le backend reçoit directement toutes les informations (txid, confirmations, block_height) dans la réponse initiale, sans besoin de status endpoint ou de polling.

2026-01-23 : Restauration de l'API d'ancrage

Problème : L'API d'ancrage était inactive et le service systemd pointait vers un répertoire inexistant (/home/ank/dev/lecoffre-anchor-api).

Solution :

  1. Clonage du code depuis la branche v2-remote du dépôt v1 dans /srv/4NK/certificator.4nkweb.com/lecoffre-anchor-api
  2. Installation des dépendances npm
  3. Création du fichier logger.ts manquant
  4. Création du fichier .env avec la configuration Bitcoin
  5. Mise à jour du service systemd pour pointer vers le nouveau répertoire
  6. Correction du chemin du cookie Bitcoin (/home/ncantu/.bitcoin/signet/.cookie)

Résultat : L'API d'ancrage est opérationnelle et connectée au nœud Bitcoin Signet.

2026-01-27 : Mise à jour URL API d'ancrage

Problème : L'URL de l'API d'ancrage a été changée de https://prod.signet.4nkweb.com vers https://anchorage.certificator.4nkweb.com.

Solution :

  1. Mise à jour de ANCHORE_API_URL dans env-full-<env>-for-bdd-injection.txt
  2. Mise à jour dans system_configuration via setSettings
  3. Port interne : 3004 (configuré dans .env et systemd)
  4. Nginx : Route anchorage.certificator.4nkweb.com192.168.1.103:3004

Vérification :

cd lecoffre-back-main
npm run anchorage:test-anchor-api

Problème 502 Bad Gateway : Si vous rencontrez des erreurs 502 Bad Gateway :

  • Vérifier l'état du service : systemctl --user status anchor-api.service
  • Vérifier que le service écoute sur le port 3004 : ss -tlnp | grep 3004
  • Tester depuis le backend : curl -s http://localhost:3004/health
  • Configuration Nginx : Route anchorage.certificator.4nkweb.com192.168.1.103:3004

API Annuaire

Vue d'Ensemble

L'API Annuaire est appelée avec une authentification Basic Auth (login/password) distincte de l'API IdNot existante (clé API). Les recherches utilisent des appels API au moment de la demande (pas de tables annu_*).

Configuration

Variables d'environnement (via system_configuration) :

  • ANNUAIRE_API_LOGIN : Login Basic Auth
  • ANNUAIRE_API_PASSWORD : Password Basic Auth (sensible)
  • ANNUAIRE_API_BASE_URL : URL de base de l'API Annuaire

Lecture directe depuis la BDD : les helpers de recherche (IdNot/Annuaire) lisent ces configurations depuis system_configuration (via ConfigLoader), sans passer par .env.

Préparation configuration Genapi (push iNot)

Variables prévues pour injection depuis env-full-<env>-for-bdd-injection.txt vers system_configuration :

  • GENAPI_AUTH_URL : URL OAuth token Genapi (.../Authorization/OAuth/Token)
  • GENAPI_API_BASE_URL : URL de base API Genapi (.../actes)
  • GENAPI_TENANT_ID : Tenant Genapi (header tenantId)
  • GENAPI_CLIENT_ID : Client ID OAuth Genapi
  • GENAPI_CLIENT_SECRET : Client secret OAuth Genapi (sensible)
  • GENAPI_SCOPE : Scopes OAuth Genapi (ex: sas:read sas:write)

Backend endpoint for Genapi push

  • POST /api/v1/notary/documents/:uid/push-inot

Execution flow:

  1. Validate document status (VALIDATED required).
  2. Resolve dossier in Genapi by folder numero using /api/v1/Dossiers/Search.
  3. Create an eDocument in Genapi (/api/v1/Clients/{idParent}/Edocument or /api/v1/Partenaires/{idParent}/Edocument).
  4. Upload binary payload to /api/v1/Documents/{id}/Data.

Unavailable response:

  • If Genapi endpoint is unreachable, backend returns GENAPI_API_UNAVAILABLE.
  • Frontend displays a dedicated message for this case ("API Genapi indisponible").

Backend mutualization:

  • Shared helper for same auth protocol integrations: TenantOAuthHttpClient
  • Scope: external backend calls using OAuth client-credentials + tenantId header.
  • Frontend unavailable error mappings are centralized in front/Utils/errorMessages/constants/externalServiceErrorPatterns.ts.
  • External integration registry is centralized in backend: lecoffre-back-main/src/common/utils/externalServiceErrors.ts.
  • Standardized backend unavailable codes applied to multiple integrations:
    • GENAPI_API_UNAVAILABLE
    • ANNUAIRE_API_UNAVAILABLE
    • ANCHORAGE_API_UNAVAILABLE
    • IDNOT_API_UNAVAILABLE
    • OPENID_API_UNAVAILABLE
    • IPFS_API_UNAVAILABLE
    • MAILCHIMP_API_UNAVAILABLE
  • Common controller mapper for integration errors:
    • lecoffre-back-main/src/common/utils/controllerExternalErrorHelper.ts
    • Shared integration->HTTP mapping table moved to:
      • lecoffre-back-main/src/common/utils/externalIntegrationHttpMappings.ts
    • Variant wrapper available:
      • handleControllerErrorWithExternalIntegration(...)
    • Domain factory utility available:
      • getExternalIntegrationMappings("mailchimp" | "idnot" | "ipfs" | "openid" | "anchorage" | "genapi" | "stripe" | "bitcoin_signet" | "idnot_annuaire" | "all")
    • Subset mapping utilities available:
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_MAILCHIMP_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_IDNOT_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_IPFS_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_OPENID_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_ANCHORAGE_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_GENAPI_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_STRIPE_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_BITCOIN_SIGNET_ONLY
      • EXTERNAL_INTEGRATION_HTTP_MAPPINGS_IDNOT_ANNUAIRE
    • Mapping response messages are now derived from buildExternalServiceUnavailableCode(...) to keep integration error messages homogeneous.
    • Already wired in:
      • notary/DocumentsController
      • idnot/UserController
      • idnot/OfficeController
      • notary/MailchimpController
      • public/IncidentReportController
      • notary/FolderThirdPartiesController (resend-code flow)
      • super-admin/HealthTestsController
      • admin/SyncController
      • public/ThirdPartyAuthController (request-code)
      • notary/FolderSharingController (searchNotaries)
      • notary/DocumentAnchorsController (anchorDocument, anchorFolderDocuments, verifyPendingAnchors)
      • notary/DocumentsController (push-inot, resend-request, resend-request-batch)
      • notary/FolderThirdPartiesController (resend-code with mailchimp domain mapping)
      • idnot/UserController
      • idnot/OfficeController
    • Helper-level fallback utility available for non-controller flows:
      • handleErrorWithExternalIntegrationFallback(...)
    • Service-level propagation utility available for business layers without HTTP response object:
      • toExternalServiceUnavailableError(...)
      • isExternalServiceUnavailableError(...)
      • buildExternalOperationName(integration, operation) (integration.operation)
      • wrapExternalCall(integration, operation, fn)
      • wrapExternalCallWithLog(integration, operation, fn, logError)
      • createExternalIntegrationCallLogger(integrationLabel, context, level?, extraMetadata?)
      • buildStandardExternalLogMetadata({ targetEmail?, subscriptionId?, requestPath?, resourceUid? })
      • mergeExternalLogMetadata(...metadataItems)
      • buildStripeSubscriptionMetadata({ requestPath, subscriptionType?, frequency?, subscriptionId? })
      • buildIdNotRequestMetadata({ targetEmail?, requestPath })
      • buildMailchimpRequestMetadata({ targetEmail, requestPath })
      • buildOpenIdRequestMetadata({ requestPath, resourceUid? })
      • buildIpfsReadMetadata({ requestPath, resourceUid? })
      • buildIpfsWriteMetadata({ requestPath, resourceUid? })
      • buildOpenIdTokenMetadata({ requestPath, resourceUid? })
      • buildOpenIdJwksMetadata({ requestPath, resourceUid? })
      • buildOpenIdUserInfoMetadata({ requestPath, resourceUid? })
      • buildOpenIdLogoutMetadata({ requestPath, resourceUid? })
      • buildIpfsPinMetadata({ requestPath, resourceUid? })
      • buildIpfsUnpinMetadata({ requestPath, resourceUid? })
      • buildIpfsGatewayReadMetadata({ requestPath, resourceUid? })
      • buildAnchorageRequestMetadata({ requestPath, resourceUid? })
      • buildGenapiRequestMetadata({ requestPath, resourceUid? })
      • createIpfsGatewayReadLogger(context, requestPath)
      • createIpfsGatewayReadLoggerFromPath(context, pathOrUrl)
      • createIpfsPinLogger(context, requestPath, resourceUid?)
      • createIpfsPinLoggerFromFileName(context, fileName, requestPath?)
      • createIpfsUnpinLogger(context, requestPath, resourceUid?)
      • createIpfsUnpinLoggerFromHash(context, hashToUnpin)
      • createOpenIdUserInfoLogger(context, requestPath, resourceUid?)
      • createOpenIdLogoutLogger(context, requestPath, resourceUid?)
      • createOpenIdTokenLogger(context, requestPath, resourceUid?)
      • createOpenIdJwksLogger(context, requestPath, resourceUid?)
      • createOpenIdUserInfoLoggerFromPath(context, pathOrUrl, resourceUid?)
      • createOpenIdLogoutLoggerFromPath(context, pathOrUrl, resourceUid?)
      • createOpenIdTokenLoggerFromPath(context, pathOrUrl, resourceUid?)
      • createOpenIdJwksLoggerFromPath(context, pathOrUrl, resourceUid?)
      • createIpfsExternalCallLogger(context)
      • createMailchimpRequestLoggerFromPath(context, pathOrUrl, targetEmail)
      • createIdNotRequestLoggerFromPath(context, pathOrUrl, targetEmail?, level?)
      • createStripeSubscriptionLogger(context, { requestPath, subscriptionType?, frequency?, subscriptionId?, level? })
      • createStripeSubscriptionLoggerFromPath(context, { pathOrUrl, subscriptionType?, frequency?, subscriptionId?, level? })
      • createAnchorageRequestLogger(context, requestPath, resourceUid?, level?)
      • createAnchorageRequestLoggerFromPath(context, pathOrUrl, resourceUid?, level?)
      • createGenapiRequestLogger(context, requestPath, resourceUid?, level?)
      • createGenapiRequestLoggerFromPath(context, pathOrUrl, resourceUid?, level?)
      • createMailchimpExternalCallLogger(context, extraMetadata?)
      • createIdNotExternalCallLogger(context, level?, extraMetadata?)
      • createStripeExternalCallLogger(context, level?, extraMetadata?)
      • createAnchorageExternalCallLogger(context, level?, extraMetadata?)
      • createGenapiExternalCallLogger(context, level?, extraMetadata?)
      • lecoffre-back-main/src/common/utils/externalServiceErrorPropagation.ts
      • lecoffre-back-main/src/common/utils/externalServiceCallLoggers.ts
    • Helper-level fallback already wired in:
      • notary/helpers/FolderThirdPartiesResendCodeHelper.ts
      • public/helpers/ThirdPartyAuthHelper.ts
      • notary/helpers/FolderSharingResendHelper.ts
    • Service-level propagation already wired in:
      • services/notary/DocumentsService/DocumentsService.ts
      • services/notary/OfficesService/OfficesRibService.ts
      • services/common/FileProcessingService/helpers/FileProcessingStepHelper.ts
      • services/common/BitcoinSignetService/BitcoinSignetService.ts
      • services/common/MailchimpService/MailchimpService.ts
      • services/notary/OfficesService/helpers/OfficesRibProcessingHelper.ts
      • services/common/FileProcessingService/helpers/FileProcessingViewHelper.ts
    • wrapExternalCall(...) usage extended to secondary flows where failure is explicitly external:
      • IPFS read/view/download helpers
      • Mailchimp list subscription API call
    • wrapExternalCallWithLog(...) usage extended where log+rethrow was duplicated:
      • Bitcoin RPC anchorage flows
      • Secondary Mailchimp resend flows
      • Secondary IPFS/watermark processing flows
      • Secondary IPFS view/download/watermark read helper flows
      • Secondary RIB IPFS low-level download flow
      • Mailchimp list subscription flow (addToMailchimpList)
      • Annuaire/IdNot secondary email-search API flow (searchByEmail)
      • Stripe checkout-session creation flow (createCheckoutSession)
    • Standardized metadata keys used in external log payloads:
      • targetEmail
      • subscriptionId
      • requestPath
      • resourceUid
    • Inline callback metadata objects replaced by dedicated metadata builders on active IDNOT/MAILCHIMP/STRIPE wrappers.
    • Existing IPFS gateway-read wrappers aligned with buildIpfsGatewayReadMetadata(...).
    • Existing IPFS upload wrappers aligned with buildIpfsPinMetadata(...).
    • Remaining OPENID/IPFS wrapper logger lambdas replaced by dedicated logger wrappers.
    • Existing IPFS unpin flow migrated to wrapper + dedicated unpin logger (IpfsService.unpinFile).
    • Existing IPFS gateway-read logger usage migrated to createIpfsGatewayReadLoggerFromPath(...) for URL/path normalization.
    • Existing IPFS unpin logger usage migrated to createIpfsUnpinLoggerFromHash(...) for hash-only caller contract.
    • Existing IPFS pin logger usage migrated to createIpfsPinLoggerFromFileName(...) for default pin endpoint encapsulation.
    • OPENID userinfo/logout flows are now available in OpenIdService with dedicated logger wrappers.
    • Existing OPENID logger usage migrated to fromPath wrappers for normalized endpoint metadata.
    • OpenIdService implements common/system/OpenIdInterface.ts (getOpenIdConfig, getSigningKey, verifyIdToken, getUserInfo, notifyLogout).
    • Existing OPENID calls aligned with dedicated metadata builders for discovery/token and JWKS endpoints.
    • Existing Mailchimp and Annuaire/IdNot flows aligned with fromPath wrappers for endpoint normalization:
      • services/common/MailchimpService/MailchimpService.ts (addToMailchimpList)
      • services/common/IdNotDirectoryService/IdNotDirectoryService.ts (searchByEmail)
    • Existing Stripe checkout-session flow aligned with createStripeSubscriptionLoggerFromPath(...) for endpoint normalization:
      • services/common/StripeService/StripeService.ts (createCheckoutSession)
    • Existing Genapi HTTP paths aligned with createGenapiRequestLoggerFromPath(...):
      • services/common/GenapiService/GenapiService.ts (findDossierIdByFolderNumber, createEdocument, uploadDocumentData)
    • Existing Anchorage verify endpoint aligned with createAnchorageRequestLoggerFromPath(...):
      • services/common/BitcoinSignetService/helpers/BitcoinSignetApiHelper.ts (verifyHashViaApi)

Endpoints API

Offices : GET /api/pp/v2/entites?type=office&page={page}&size={size}&key={IDNOT_API_KEY} Personnes : GET /api/pp/v2/personnes?page={page}&size={size}&key={IDNOT_API_KEY} Rattachements : GET /api/pp/v2/personnes/{idnot}/rattachements?page={page}&size=100&key={IDNOT_API_KEY}

Notes :

  • Auth : la clé API se passe en query (puis fallback header x-api-key)
  • Pagination : utiliser page>=1 (ou 0 selon endpoint) et size=100
  • Erreurs : 403 accessDenied → essayer fallback /entites sans type et filtrer côté client

Tables

Les tables historiques annu_* ne sont plus utilisées par lapplication.

Synchronisation

Les synchronisations batch (IdNot/Annuaire) ne sont plus exécutées via des scripts dédiés. La recherche et la résolution de confrères reposent sur des appels API (IdNot/Annuaire) au moment de la demande, complétés par la base du site (users/contacts/offices) et les partages (folder_sharing).

Troubleshooting

Erreur : Missing ANNUAIRE_API_LOGIN or ANNUAIRE_API_PASSWORD

  • Solution : Injecter les clés dans system_configuration via le mécanisme standard dinjection (fichier .secrets/<env>/env-full-<env>-for-bdd-injection.txt puis déploiement).

Erreur : HTTP 401 Unauthorized

  • Cause : Login ou mot de passe incorrect
  • Solution : Vérifier les credentials en base et mettre à jour si nécessaire

Erreur : HTTP 403 Forbidden

  • Cause : Le compte n'a pas accès à l'API Annuaire
  • Solution : Contacter le support ID.NOT pour vérifier les droits du compte

Recherche confrères (appel direct API annuaire)

La recherche de notaires pour inviter un confrère au partage de dossier utilise uniquement lAPI Annuaire (pas de base du site ni de confrères déjà invités).

Endpoint exposé : GET /api/v1/notary/search/notaries?q={query}

  • Authentification : session notaire (authHandler, permissionContextInjector).
  • Paramètre : q (string, min. 2 caractères) nom, prénom, email ou nom doffice.
  • Réponse : { results: Array<...>, annuaireConfigured: boolean, message?: string }. results : tableau dobjets { idNot, email, firstName, lastName, officeName, crpcen, isPrimary, displayName, hasActiveLicense?, phone? }. Si lannuaire nest pas configuré : 503 Service Unavailable avec le même format (annuaireConfigured: false, message explicatif, results: []). Pas de 200 avec résultats vides.

Comportement :

  • Si ANNUAIRE_API_BASE_URL et IDNOT_API_KEY sont renseignés en base (system_configuration) : appel API Annuaire V2 avec pagination complète (pages de 50 jusquà épuisement) :
    • PB lookup : GET {ANNUAIRE_API_BASE_URL}/api/v2/directory/lookup?nomNotaire={query}&page=&size=50&key=... (toutes les pages).
    • Fallback PP : GET {ANNUAIRE_API_BASE_URL}/api/pp/v2/personnes?prenom=...&nomUsuel=...&page=&size=50&key=... (toutes les pages).
  • Si lannuaire nest pas configuré : réponse { results: [], annuaireConfigured: false, message } ; le front affiche le message dalerte (pas de fallback legacy IdNot).

Fichiers :

  • lecoffre-back-main/src/services/common/IdNotDirectoryService/IdNotDirectoryService.ts : searchNotaries() retourne SearchNotariesResponse ; getAnnuaireCredentials().
  • lecoffre-back-main/src/services/common/IdNotDirectoryService/helpers/IdNotDirectoryApiSearchHelper.ts : API Annuaire V2 (lookup + PP, pagination complète).
  • lecoffre-back-main/src/app/api/notary/helpers/FolderSharingSearchHelper.ts : renvoie { results, annuaireConfigured, message? }.

Configuration : ANNUAIRE_API_BASE_URL, IDNOT_API_KEY en base (system_configuration). Obligatoires pour que la recherche renvoie des résultats.

Notaire invité : email et IdNot en base (folder_sharing)

Après la création d'un partage, seul l'email en base associé au notaire invité est utilisé pour les envois (invitations, relances, documents). Aucune relecture de l'email côté IdNot.

  • Un seul champ email : invited_notary_email = email de préférence pour les envois. À l'invitation il est en général pré-rempli depuis IdNot (annuaire) ; il peut être modifié avant enregistrement. L'email IdNot et l'email de préférence sont donc le même champ en base pour le notaire invité.
  • Accès aux dossiers invités : croisement sur l'IdNot (invited_notary_idnot). Quand l'utilisateur connecté a un IdNot, seuls les partages dont invited_notary_idnot correspond sont pris en compte. L'email en base n'est pas utilisé pour ce croisement.
  • Profils avec IdNot : à l'invitation, le partage enregistre invited_notary_idnot. Les notaires invités (y compris sans seat) ont en base un identifiant IdNot : sur chaque partage (folder_sharing.invited_notary_idnot), et après première connexion via IdNot sur le profil utilisateur (users.idNot).

Voir aussi : docs/ARCHITECTURE.md § Partage Inter-Études, docs/README.md (consolidation opérationnelle).

Architecture

Les synchronisations batch historiques (et les tables annu_* associées) ne sont plus utilisées par lapplication. Les recherches de confrères et les résolutions (ex: par email) seffectuent via des appels API (IdNot / Annuaire) au moment de la demande, avec complément de la base du site et des partages (folder_sharing).

Déploiement

Déploiement standard du backend via deploy/scripts/build-and-deploy.sh <env>. Aucune migration spécifique de tables annu_* nest requise. Les appels API Annuaire/IdNot nécessitent que les clés correspondantes soient présentes dans system_configuration (injection via .secrets/<env>/env-full-<env>-for-bdd-injection.txt puis déploiement).

Sécurité

Authentification Basic Auth :

Authorization: Basic <base64(login:password)>

Stockage des credentials :

  • Login : Stocké en clair dans system_configuration (is_sensitive: false)
  • Password : Marqué comme sensible dans system_configuration (is_sensitive: true)
  • Affichage : Le mot de passe est masqué (***) dans l'interface super-admin

Logs : Les logs ne contiennent jamais les credentials. Seuls les URLs (sans query params) et les codes HTTP sont loggés.

Tests et Vérification

Les tests de connectivité et les vérifications se font via les parcours applicatifs qui déclenchent des appels API (IdNot / Annuaire), ou via les cartes de tests live de la page super-admin santé.

Troubleshooting API Annuaire

Erreur : Missing ANNUAIRE_API_LOGIN or ANNUAIRE_API_PASSWORD :

Cause : Les credentials ne sont pas en base de données.

Solution :

deploy/scripts/build-and-deploy.sh test

Erreur : HTTP 401 Unauthorized :

Cause : Login ou mot de passe incorrect.

Solution :

  1. Vérifier les credentials en base :
SELECT key, value FROM system_configuration WHERE key LIKE 'ANNUAIRE_API_%';
  1. Mettre à jour si nécessaire via l'interface super-admin ou le script.

Erreur : HTTP 403 Forbidden :

Cause : Le compte n'a pas accès à l'API Annuaire.

Solution : Contacter le support ID.NOT pour vérifier les droits du compte.

Aucune donnée synchronisée :

Cause : Les pages retournent 0 résultats ou l'API retourne un format inattendu.

Solution :

  1. Vérifier les logs backend (service API) pour les erreurs IdNot/Annuaire
  2. Regarder les "sample" logs (premier élément de chaque type)
  3. Adapter le parsing si nécessaire dans les helpers de recherche IdNot/Annuaire

Rappels Automatiques Documents

Statut : 100% | Version : 2.0.0

Architecture Backend

Service : DocumentRemindersService (~350 lignes) Fichier : lecoffre-back-main/src/services/notary/DocumentRemindersService/DocumentRemindersService.ts

Repository : DocumentRemindersConfigRepository

Endpoints

6 endpoints (config CRUD + trigger manuel) :

  • GET /api/v1/notary/document-types/:documentTypeUid/reminders/config : Récupérer configuration pour un type de document
  • GET /api/v1/notary/offices/:officeUid/reminders/config : Liste toutes les configurations d'un office
  • POST /api/v1/notary/document-types/:documentTypeUid/reminders/config : Créer configuration
  • PUT /api/v1/notary/document-types/:documentTypeUid/reminders/config : Modifier configuration
  • DELETE /api/v1/notary/document-types/:documentTypeUid/reminders/config : Supprimer configuration
  • POST /api/v1/notary/reminders/send : Déclencher manuellement (⚠️ DÉSACTIVÉ - relances automatiques désactivées)

Cron : sendDocumentReminders() quotidien 9h (⚠️ DÉSACTIVÉ - relances automatiques totalement désactivées, voir CronService.ts ligne 144-168)

Configuration Base de Données

Table : document_reminders_config

Champs :

  • document_type_uid : Type de document concerné
  • office_uid : Office concerné
  • reminder_intervals : JSON array d'intervalles en jours (ex: [7, 14, 21])
  • reminder_message : Message personnalisé optionnel
  • auto_send : Envoi automatique activé (défaut: true)
  • is_active : Configuration active/inactive

Frontend

Pages :

  • /reminders/config.tsx : Liste + toggle actif/inactif
  • /folders/[uid]/documents-reminder-history : Historique rappels

API : DocumentReminders (~130 lignes)

Support destinataires :

  • Clients : Relance via depositor.uid
  • Tiers : Relance via third_party_depositor.uid (v2.0.0)
  • Notaires invités : Relance via shared_to_office.uid (v2.0.1)

Fonctionnalités

  • Intervalles configurables : Ex: [7, 14, 21] jours après demande
  • Message personnalisé : Optionnel par type document + office
  • Configuration par type document + office : Granularité fine
  • Activation/désactivation : Via champ isActive dans la configuration (pas d'endpoint toggle dédié)
  • Trigger manuel : ⚠️ DÉSACTIVÉ - L'endpoint POST /api/v1/notary/reminders/send est désactivé. Utiliser /api/v1/notary/customers/:uid/send_reminder pour les relances manuelles
  • Évite doublons : 1 rappel/jour maximum par document
  • Support notaires invités : Relance possible pour les confrères via partage inter-études (v2.0.1)

Configuration Système

Variable : CRON_SEND_REMINDERS_ENABLED (dans system_configuration) Gestion : Désactivable via /super-admin/system-config


Opérations Batch

Statut : 100% | Version : 2.0.0

Certificat Unique Dossier

Service : FolderAnchorCertificateService Fonctionnalité : PDF unique pour dossier complet

Inclut :

  • Liste tous documents ancrés
  • Merkle root dossier
  • Hash ZIP complet
  • Informations blockchain

Envoi Documents par Destinataire

Backend :

  • Endpoint : POST /api/v1/notary/documents_notary/send
  • Body : recipientUid, folderUid, files (FormData)
  • Middleware : multer().array("files", 20) (20 fichiers max)
  • Logique : Un destinataire par appel ; le frontend boucle sur les destinataires sélectionnés et agrège les résultats

Note : Route exclue du middleware multer global (voir README.md)

Filtres Avancés

Backend :

  • Service : FolderSearchService
  • Controller : FolderSearchController
  • Endpoint : POST /api/v1/notary/folders/search

Filtres disponibles :

  • Numéro dossier
  • Nom dossier
  • Type acte
  • Office
  • Statut (LIVE, ARCHIVED, DELETED)
  • Date création
  • Collaborateurs

Téléchargement Multiple

Backend :

  • Service : ZipService
  • Endpoint : POST /api/v1/notary/files/download-multiple

Fonctionnalités :

  • Sélection multiple fichiers
  • Conversion automatique en PDF si nécessaire
  • Ajout filigrane "lecoffre.io" (préfixe "acpl_")
  • Ajout métadonnées d'ancrage dans les PDF
  • Inclut certificats d'ancrage si disponibles
  • Utilise versions filigrannées existantes

Fichier : lecoffre-back-main/src/services/common/ZipService/ZipService.ts

Certificat Agrégé Documents

Statut : 100% | Version : 2.1.0

Service : AggregatedCertificateService Fichier : lecoffre-back-main/src/services/notary/AggregatedCertificateService/AggregatedCertificateService.ts

Fonctionnalités :

  • Génération d'un certificat PDF unique pour plusieurs documents sélectionnés
  • Support des Documents (documents validés) et DocumentsNotary (documents envoyés)
  • Ancrage du certificat sur la blockchain Bitcoin Signet
  • Upload du certificat sur IPFS
  • Inclusion automatique dans les ZIP téléchargés

Endpoints :

  • POST /api/v1/notary/files/download-aggregated-certificate : Certificat agrégé pour Documents
  • POST /api/v1/notary/files-notary/download-aggregated-certificate : Certificat agrégé pour DocumentsNotary

Limite : 100 documents maximum par certificat


Mémo API Annuaire (Qualification)

Endpoints :

  • ID.not Auth: https://qual-connexion.idnot.fr (authorize/token)
  • Annuaire base: https://qual-api.notaires.fr/annuaire
  • Recherche personnes (si supporté): GET {base}/persons?q={query} avec header x-api-key
  • Entités (offices): GET /api/pp/v2/entites?type=office&page={page}&size={size}&key={IDNOT_API_KEY}
  • Fallback offices: GET /api/pp/v2/entites?key={IDNOT_API_KEY} → filtrer typeEntite.name==office
  • Personnes: GET /api/pp/v2/personnes?page={page}&size={size}&key={IDNOT_API_KEY}
  • Rattachements: GET /api/pp/v2/personnes/{idnot}/rattachements?page={page}&size=100&key={IDNOT_API_KEY}

Notes :

  • Auth : la clé API se passe en query (puis fallback header x-api-key)
  • Pagination : utiliser page>=1 (ou 0 selon endpoint) et size=100
  • Erreurs : 403 accessDenied → essayer fallback /entites sans type et filtrer côté client
  • Sécurité : ne publier aucune valeur de clé

Exemple de Client pour l'API d'Ancrage

Un exemple complet de client Node.js pour tester l'API d'ancrage est disponible dans le code source. Le client permet de :

  • Vérifier l'état de l'API (/health)
  • Ancrer un document (POST /api/anchor/document)
  • Vérifier un ancrage (POST /api/anchor/verify)

Fonctionnement synchrone : L'API retourne directement le txid dans la réponse (HTTP 200 OK), pas besoin de polling ou de status endpoint.

Points importants :

  • L'API retourne directement le txid dans la réponse (fonctionnement synchrone)
  • Le code HTTP est 200 OK (pas 202 Accepted)
  • Toutes les informations sont disponibles immédiatement (confirmations, block_height)

Voir le code source dans lecoffre-anchor-api/ pour l'exemple complet de client.

Mise à jour de l'URL de l'API d'ancrage

Date : 2026-01-27

URL mise à jour : L'URL de l'API d'ancrage a été changée de https://prod.signet.4nkweb.com vers https://anchorage.certificator.4nkweb.com.

Fichiers à mettre à jour :

1. Fichiers .secrets/<env>/env-full-<env>-for-bdd-injection.txt :

Ces fichiers sont utilisés pour injecter les variables dans la base de données via setSettings.

Emplacements :

  • .secrets/test/env-full-test-for-bdd-injection.txt
  • .secrets/pprod/env-full-pprod-for-bdd-injection.txt
  • .secrets/prod/env-full-prod-for-bdd-injection.txt

Variable à mettre à jour :

ANCHORE_API_URL=https://anchorage.certificator.4nkweb.com

Note : Ces fichiers ne sont pas versionnés (dans .gitignore) et doivent être mis à jour manuellement sur chaque machine de développement.

2. Base de données (via system_configuration) :

Si les fichiers env-full-*-for-bdd-injection.txt ont déjà été injectés, la valeur dans la base de données doit être mise à jour :

Requête SQL :

UPDATE system_configuration
SET value = 'https://anchorage.certificator.4nkweb.com'
WHERE key = 'ANCHORE_API_URL'
  AND category = 'INTEGRATION_BITCOIN';

Ou via le script setSettings :

# Après avoir mis à jour le fichier env-full-<env>-for-bdd-injection.txt
bash deploy/scripts_v2/remote/set-settings.sh <env> <domain> <app_root>

Vérification :

# Depuis le backend
cd lecoffre-back-main
npm run anchorage:test-anchor-api

Le script doit afficher :

✅ [TestAnchorAPI] Variables récupérées depuis la BDD
   URL: https://anchorage.certificator.4nkweb.com

Problème 502 Bad Gateway : Si vous rencontrez des erreurs 502 Bad Gateway :

  • Vérifier l'état du service : systemctl --user status anchor-api.service
  • Vérifier que le service écoute sur le port 3004 : ss -tlnp | grep 3004
  • Tester depuis le backend : curl -s http://localhost:3004/health
  • Configuration Nginx : Route anchorage.certificator.4nkweb.com192.168.1.103:3004

Configuration actuelle :

  • Service : anchor-api.service (user service)
  • Port : 3004 (configuré dans .env et systemd)
  • Nginx : Route anchorage.certificator.4nkweb.com192.168.1.103:3004
  • Bitcoin RPC : localhost:18443 (Signet)

Dernière mise à jour : 2026-01-28 (consolidation API.md, API_ANNUAIRE_IMPLEMENTATION.md, API_MEMO.md, ANCHOR_API_ANALYSIS.md, ANCHOR_API_EXAMPLE_CLIENT.md, UPDATE_ANCHOR_API_URL.md)

Correctif accès invités notaires (2026-03-02)

Problème observé :

  • Un notaire invité pouvait voir des dossiers partagés (GUEST_FOLDERS) mais être déconnecté à l'ouverture d'un dossier lors de GET /v1/notary/documents.

Cause racine :

  • Des chemins helper notaire appelaient encore FolderSharingService.hasAccess(folderUid, officeUid, email) sans propagation de idNot, malgré la centralisation d'identité déjà engagée.

Correctif appliqué :

  • Propagation de idNot dans les appels de contrôle d'accès des helpers notaire concernés.
  • Extension du même alignement à des helpers customer/middleware/service où la vérification restait email-only.
  • Réutilisation de l'extraction d'identité centralisée buildUserIdentityContext pour éviter les divergences de signature d'appel.

Effet attendu :

  • Les contrôles d'accès invités notaires utilisent de manière cohérente l'identité idNot sur le flux GUEST_FOLDERS, sans régression sur les flux non-notaire (où userIdNot reste optionnel).