1569 lines
58 KiB
Markdown
1569 lines
58 KiB
Markdown
# 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`](./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 l’env) sont identifiés par le token Bearer : l’API 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 :
|
||
|
||
```http
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
Le token est de la forme **base** + **env** (ex. `nicolecoffreiotest`, `nicolecoffreiopprod`). **env** est le nom d’environnement (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 l’env 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
|
||
|
||
```text
|
||
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` :
|
||
|
||
```bash
|
||
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`
|
||
|
||
```ini
|
||
[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** :
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```http
|
||
GET /health
|
||
```
|
||
|
||
**Réponse** :
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"service": "anchor-api",
|
||
"bitcoin": {
|
||
"connected": true,
|
||
"blocks": 198769
|
||
},
|
||
"timestamp": "2026-01-23T11:03:33.668Z"
|
||
}
|
||
```
|
||
|
||
### Ancrage de document
|
||
|
||
```http
|
||
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) :
|
||
|
||
```json
|
||
{
|
||
"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
|
||
|
||
```http
|
||
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é :
|
||
|
||
```json
|
||
{
|
||
"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é :
|
||
|
||
```json
|
||
{
|
||
"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 :
|
||
|
||
```json
|
||
{
|
||
"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) :
|
||
|
||
```json
|
||
{
|
||
"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
|
||
|
||
```typescript
|
||
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 :
|
||
|
||
```typescript
|
||
// 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
|
||
|
||
```text
|
||
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
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
systemctl --user daemon-reload
|
||
systemctl --user start anchor-api.service
|
||
systemctl --user enable anchor-api.service
|
||
```
|
||
|
||
## Vérification
|
||
|
||
### Vérifier le statut du service
|
||
|
||
```bash
|
||
systemctl --user status anchor-api.service
|
||
```
|
||
|
||
### Vérifier les logs
|
||
|
||
```bash
|
||
journalctl --user -u anchor-api.service -f
|
||
```
|
||
|
||
### Tester l'API depuis l'extérieur
|
||
|
||
**Test simple avec curl :**
|
||
|
||
```bash
|
||
curl -k https://anchorage.certificator.4nkweb.com/health
|
||
```
|
||
|
||
**Réponse attendue** :
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"service": "anchor-api",
|
||
"bitcoin": {
|
||
"connected": true,
|
||
"blocks": 152321
|
||
}
|
||
}
|
||
```
|
||
|
||
**Exemple de client Node.js :**
|
||
|
||
Un exemple complet de client pour tester l'API :
|
||
|
||
```javascript
|
||
#!/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 :**
|
||
|
||
```bash
|
||
# 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 :
|
||
|
||
```typescript
|
||
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 :
|
||
|
||
```typescript
|
||
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`
|
||
|
||
```bash
|
||
# 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 :
|
||
|
||
```bash
|
||
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** :
|
||
|
||
```bash
|
||
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** :
|
||
|
||
```nginx
|
||
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 :
|
||
|
||
```typescript
|
||
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.com` → `192.168.1.103:3004`
|
||
|
||
**Vérification** :
|
||
|
||
```bash
|
||
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.com` → `192.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 l’application.
|
||
|
||
### 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 d’injection (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 l’API 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 d’office.
|
||
- **Réponse** : `{ results: Array<...>, annuaireConfigured: boolean, message?: string }`. `results` : tableau d’objets `{ idNot, email, firstName, lastName, officeName, crpcen, isPrimary, displayName, hasActiveLicense?, phone? }`. Si l’annuaire n’est 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 l’annuaire n’est pas configuré : réponse `{ results: [], annuaireConfigured: false, message }` ; le front affiche le message d’alerte (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 l’application. Les recherches de confrères et les résolutions (ex: par email) s’effectuent 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_*` n’est 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** :
|
||
|
||
```http
|
||
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** :
|
||
|
||
```bash
|
||
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 :
|
||
|
||
```sql
|
||
SELECT key, value FROM system_configuration WHERE key LIKE 'ANNUAIRE_API_%';
|
||
```
|
||
|
||
2. 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](./README.md#consolidation-operationnelle-ex-operationsmd))
|
||
|
||
### 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** :
|
||
|
||
```bash
|
||
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** :
|
||
|
||
```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`** :
|
||
|
||
```bash
|
||
# 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** :
|
||
|
||
```bash
|
||
# Depuis le backend
|
||
cd lecoffre-back-main
|
||
npm run anchorage:test-anchor-api
|
||
```
|
||
|
||
Le script doit afficher :
|
||
|
||
```text
|
||
✅ [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.com` → `192.168.1.103:3004`
|
||
|
||
**Configuration actuelle** :
|
||
|
||
- **Service** : `anchor-api.service` (user service)
|
||
- **Port** : `3004` (configuré dans `.env` et systemd)
|
||
- **Nginx** : Route `anchorage.certificator.4nkweb.com` → `192.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).
|