feat: normaliser les URL recette par entité
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 2m55s
Some checks failed
Validation applicative / TypeScript, tests et build (push) Failing after 2m55s
This commit is contained in:
4
app.json
4
app.json
@@ -3,8 +3,8 @@
|
|||||||
"name": "Démat. Facturation DSI",
|
"name": "Démat. Facturation DSI",
|
||||||
"category": "SANTINOVA",
|
"category": "SANTINOVA",
|
||||||
"urls": {
|
"urls": {
|
||||||
"recette": "https://demat-facturation.recette.santinova-soft.org",
|
"recette": "https://demat-facturation-santinova.recette.santinova-soft.org",
|
||||||
"prod": "https://demat-facturation.santinova-soft.org"
|
"prod": "https://demat-facturation-santinova.santinova-soft.org"
|
||||||
},
|
},
|
||||||
"containerName": "demat-facturation-app",
|
"containerName": "demat-facturation-app",
|
||||||
"image": "images/demat-facturation-dsi.jpg",
|
"image": "images/demat-facturation-dsi.jpg",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ services:
|
|||||||
JWT_SECRET: demat-facturation-jwt-secret-recette-2026
|
JWT_SECRET: demat-facturation-jwt-secret-recette-2026
|
||||||
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
|
MISTRAL_API_KEY: ${MISTRAL_API_KEY:-}
|
||||||
# Passerelle centralisée : le jeton reste dans l'environnement serveur.
|
# Passerelle centralisée : le jeton reste dans l'environnement serveur.
|
||||||
REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-https://referentiel-ia.recette.santinova-soft.org}
|
REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-https://referentiel-ia-santinova.recette.santinova-soft.org}
|
||||||
REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}
|
REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}
|
||||||
REFERENTIEL_IA_TOKEN_FILE: /run/secrets/referentiel_ia_token
|
REFERENTIEL_IA_TOKEN_FILE: /run/secrets/referentiel_ia_token
|
||||||
UPLOAD_DIR: /app/uploads
|
UPLOAD_DIR: /app/uploads
|
||||||
@@ -95,7 +95,7 @@ services:
|
|||||||
priority: 100
|
priority: 100
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.demat-facturation.rule=Host(`${APP_DOMAIN:-demat-facturation.recette.santinova-soft.org}`)"
|
- "traefik.http.routers.demat-facturation.rule=Host(`demat-facturation.recette.santinova-soft.org`) || Host(`demat-facturation-santinova.recette.santinova-soft.org`)"
|
||||||
- "traefik.http.routers.demat-facturation.entrypoints=websecure"
|
- "traefik.http.routers.demat-facturation.entrypoints=websecure"
|
||||||
- "traefik.http.routers.demat-facturation.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.demat-facturation.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.services.demat-facturation.loadbalancer.server.port=3000"
|
- "traefik.http.services.demat-facturation.loadbalancer.server.port=3000"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Contenu de `config.json` :
|
|||||||
{
|
{
|
||||||
"sfrLogin": "votre-login@sfr.fr",
|
"sfrLogin": "votre-login@sfr.fr",
|
||||||
"sfrPassword": "votre-mot-de-passe",
|
"sfrPassword": "votre-mot-de-passe",
|
||||||
"appUrl": "https://demat-facturation.santinova-soft.org",
|
"appUrl": "https://demat-facturation-santinova.santinova-soft.org",
|
||||||
"apiToken": "votre-token-api-copié-depuis-lappli",
|
"apiToken": "votre-token-api-copié-depuis-lappli",
|
||||||
"downloadDir": "/tmp/sfr-invoices",
|
"downloadDir": "/tmp/sfr-invoices",
|
||||||
"processedFile": "/tmp/sfr-processed.json"
|
"processedFile": "/tmp/sfr-processed.json"
|
||||||
@@ -34,7 +34,7 @@ Ou utiliser des variables d'environnement :
|
|||||||
```bash
|
```bash
|
||||||
export SFR_LOGIN=votre-login@sfr.fr
|
export SFR_LOGIN=votre-login@sfr.fr
|
||||||
export SFR_PASSWORD=votre-mot-de-passe
|
export SFR_PASSWORD=votre-mot-de-passe
|
||||||
export APP_URL=https://demat-facturation.santinova-soft.org
|
export APP_URL=https://demat-facturation-santinova.santinova-soft.org
|
||||||
export API_TOKEN=votre-token-api
|
export API_TOKEN=votre-token-api
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
/**
|
/**
|
||||||
* Connecteur SFR Pro - Script cron pour import automatique de factures
|
* Connecteur SFR Pro - Script cron pour import automatique de factures
|
||||||
*
|
*
|
||||||
* Prérequis sur le serveur LWS :
|
* Prérequis sur le serveur LWS :
|
||||||
* npm install playwright @playwright/test
|
* npm install playwright @playwright/test
|
||||||
* npx playwright install chromium
|
* npx playwright install chromium
|
||||||
*
|
*
|
||||||
* Configuration :
|
* Configuration :
|
||||||
* Copier .env.example en .env et remplir les variables
|
* Copier .env.example en .env et remplir les variables
|
||||||
*
|
*
|
||||||
* Utilisation :
|
* Utilisation :
|
||||||
* node sfr-connector.mjs
|
* node sfr-connector.mjs
|
||||||
*
|
*
|
||||||
* Cron (mensuel le 5 du mois à 8h) :
|
* Cron (mensuel le 5 du mois à 8h) :
|
||||||
* 0 8 5 * * /usr/bin/node /opt/web-import/sfr-connector.mjs >> /var/log/sfr-import.log 2>&1
|
* 0 8 5 * * /usr/bin/node /opt/web-import/sfr-connector.mjs >> /var/log/sfr-import.log 2>&1
|
||||||
*/
|
*/
|
||||||
@@ -33,7 +33,7 @@ const CONFIG = {
|
|||||||
login: process.env.SFR_LOGIN || '',
|
login: process.env.SFR_LOGIN || '',
|
||||||
password: process.env.SFR_PASSWORD || '',
|
password: process.env.SFR_PASSWORD || '',
|
||||||
// URL de l'application de dématérialisation
|
// URL de l'application de dématérialisation
|
||||||
appUrl: process.env.APP_URL || 'https://demat-facturation.santinova-soft.org',
|
appUrl: process.env.APP_URL || 'https://demat-facturation-santinova.santinova-soft.org',
|
||||||
// Token API de la source web (récupéré depuis l'interface Connecteurs web)
|
// Token API de la source web (récupéré depuis l'interface Connecteurs web)
|
||||||
apiToken: process.env.API_TOKEN || '',
|
apiToken: process.env.API_TOKEN || '',
|
||||||
// Dossier temporaire pour les PDFs téléchargés
|
// Dossier temporaire pour les PDFs téléchargés
|
||||||
@@ -63,7 +63,7 @@ function saveProcessed(list) {
|
|||||||
async function pushInvoiceToApp(filePath, fileName) {
|
async function pushInvoiceToApp(filePath, fileName) {
|
||||||
const fileBuffer = fs.readFileSync(filePath);
|
const fileBuffer = fs.readFileSync(filePath);
|
||||||
const fileBase64 = fileBuffer.toString('base64');
|
const fileBase64 = fileBuffer.toString('base64');
|
||||||
|
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
apiToken: CONFIG.apiToken,
|
apiToken: CONFIG.apiToken,
|
||||||
fileName,
|
fileName,
|
||||||
@@ -104,7 +104,7 @@ async function pushInvoiceToApp(filePath, fileName) {
|
|||||||
// ============ CONNECTEUR SFR ============
|
// ============ CONNECTEUR SFR ============
|
||||||
async function runSfrConnector() {
|
async function runSfrConnector() {
|
||||||
log('Démarrage du connecteur SFR Pro');
|
log('Démarrage du connecteur SFR Pro');
|
||||||
|
|
||||||
if (!CONFIG.login || !CONFIG.password || !CONFIG.apiToken) {
|
if (!CONFIG.login || !CONFIG.password || !CONFIG.apiToken) {
|
||||||
log('ERREUR : SFR_LOGIN, SFR_PASSWORD et API_TOKEN sont requis');
|
log('ERREUR : SFR_LOGIN, SFR_PASSWORD et API_TOKEN sont requis');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
@@ -140,14 +140,14 @@ async function runSfrConnector() {
|
|||||||
await page.fill('input[type="email"], input[name="login"], input[id*="login"], input[id*="email"]', CONFIG.login);
|
await page.fill('input[type="email"], input[name="login"], input[id*="login"], input[id*="email"]', CONFIG.login);
|
||||||
await page.fill('input[type="password"], input[name="password"], input[id*="password"]', CONFIG.password);
|
await page.fill('input[type="password"], input[name="password"], input[id*="password"]', CONFIG.password);
|
||||||
await page.click('button[type="submit"], input[type="submit"], button:has-text("Connexion"), button:has-text("Se connecter")');
|
await page.click('button[type="submit"], input[type="submit"], button:has-text("Connexion"), button:has-text("Se connecter")');
|
||||||
|
|
||||||
await page.waitForNavigation({ waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.waitForNavigation({ waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
log('Connecté');
|
log('Connecté');
|
||||||
|
|
||||||
// 4. Naviguer vers la section factures
|
// 4. Naviguer vers la section factures
|
||||||
// Adapter selon la structure réelle du site SFR Pro
|
// Adapter selon la structure réelle du site SFR Pro
|
||||||
await page.goto(`${CONFIG.portalUrl}factures`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
await page.goto(`${CONFIG.portalUrl}factures`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => {});
|
||||||
|
|
||||||
// Chercher les liens de factures PDF
|
// Chercher les liens de factures PDF
|
||||||
const invoiceLinks = await page.$$eval(
|
const invoiceLinks = await page.$$eval(
|
||||||
'a[href*=".pdf"], a[href*="facture"], a[href*="invoice"], a[download]',
|
'a[href*=".pdf"], a[href*="facture"], a[href*="invoice"], a[download]',
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ describe("routage et persistance de déploiement", () => {
|
|||||||
it("injecte uniquement les secrets serveur de la passerelle Référentiel IA", () => {
|
it("injecte uniquement les secrets serveur de la passerelle Référentiel IA", () => {
|
||||||
const composeFile = readFileSync(join(process.cwd(), "docker-compose.yml"), "utf8");
|
const composeFile = readFileSync(join(process.cwd(), "docker-compose.yml"), "utf8");
|
||||||
|
|
||||||
expect(composeFile).toContain("REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-https://referentiel-ia.recette.santinova-soft.org}");
|
expect(composeFile).toContain("REFERENTIEL_IA_URL: ${REFERENTIEL_IA_URL:-https://referentiel-ia-santinova.recette.santinova-soft.org}");
|
||||||
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}");
|
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN: ${REFERENTIEL_IA_TOKEN:-}");
|
||||||
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN_FILE: /run/secrets/referentiel_ia_token");
|
expect(composeFile).toContain("REFERENTIEL_IA_TOKEN_FILE: /run/secrets/referentiel_ia_token");
|
||||||
expect(composeFile).toContain("file: /opt/manus-secrets/demat-facturation-referentiel-ia-token");
|
expect(composeFile).toContain("file: /opt/manus-secrets/demat-facturation-referentiel-ia-token");
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
import { readSecretFile, resolveServerSecret } from "./_core/env";
|
import { readSecretFile, resolveServerSecret } from "./_core/env";
|
||||||
|
|
||||||
const connection: ReferentielIaConnection = {
|
const connection: ReferentielIaConnection = {
|
||||||
baseUrl: "https://referentiel-ia.recette.santinova-soft.org",
|
baseUrl: "https://referentiel-ia-santinova.recette.santinova-soft.org",
|
||||||
token: "test-token",
|
token: "test-token",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ describe("Référentiel IA", () => {
|
|||||||
receivedUrl = String(url);
|
receivedUrl = String(url);
|
||||||
receivedInit = init;
|
receivedInit = init;
|
||||||
if (callCount === 1) {
|
if (callCount === 1) {
|
||||||
expect(receivedUrl).toBe("https://referentiel-ia.recette.santinova-soft.org/api/integration/v1/recommendations");
|
expect(receivedUrl).toBe("https://referentiel-ia-santinova.recette.santinova-soft.org/api/integration/v1/recommendations");
|
||||||
expect(JSON.parse(String(receivedInit?.body))).toEqual({
|
expect(JSON.parse(String(receivedInit?.body))).toEqual({
|
||||||
useCase: "texte",
|
useCase: "texte",
|
||||||
optimizeCost: true,
|
optimizeCost: true,
|
||||||
@@ -84,7 +84,7 @@ describe("Référentiel IA", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(receivedUrl).toBe("https://referentiel-ia.recette.santinova-soft.org/api/integration/v1/invoke");
|
expect(receivedUrl).toBe("https://referentiel-ia-santinova.recette.santinova-soft.org/api/integration/v1/invoke");
|
||||||
expect(receivedInit?.headers).toMatchObject({ authorization: "Bearer test-token" });
|
expect(receivedInit?.headers).toMatchObject({ authorization: "Bearer test-token" });
|
||||||
expect(JSON.parse(String(receivedInit?.body))).toMatchObject({
|
expect(JSON.parse(String(receivedInit?.body))).toMatchObject({
|
||||||
engineSlug: "santino-expert",
|
engineSlug: "santino-expert",
|
||||||
|
|||||||
Reference in New Issue
Block a user