aziendasanitaria-utils 1.2.72 → 1.2.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "engines": {
4
4
  "node": ">=14.0.0"
5
5
  },
6
- "version": "1.2.72",
6
+ "version": "1.2.73",
7
7
  "repository": "deduzzo/aziendasanitaria-utils",
8
8
  "description": "Un utility per gestire i flussi sanitari Siciliani e non solo..",
9
9
  "main": "index.js",
@@ -236,10 +236,23 @@ export class Nar2 {
236
236
  password: this._password
237
237
  }, {
238
238
  headers: {
239
- 'Content-Type': 'application/json'
239
+ 'Content-Type': 'application/json',
240
+ // NAR2 valida l'header Origin sul login (controllo CSRF): SENZA,
241
+ // risponde 200 con body "error" e nessun token. Nel browser è
242
+ // automatico; da Node lo impostiamo a mano. (Diagnosi 13/06/2026.)
243
+ 'Origin': new URL(Nar2.LOGIN_URL).origin
240
244
  }
241
245
  });
242
- Nar2.#token = out.data.accessToken;
246
+ Nar2.#token = (out.data && typeof out.data === "object") ? out.data.accessToken : undefined;
247
+ if (!Nar2.#token) {
248
+ // Login andato in 200 ma SENZA token (es. body letterale "error"):
249
+ // il login username/password potrebbe non essere più attivo (NAR2 via
250
+ // SPID). Mostro l'indicazione del server e ritorno undefined: il
251
+ // chiamante può richiedere un token esterno (setTokenEsterno).
252
+ const ind = typeof out.data === "string" ? out.data : JSON.stringify(out.data ?? null);
253
+ console.log(`[getToken] Login NAR2 SENZA accessToken (HTTP ${out.status}, risposta: ${String(ind).slice(0, 200)}). ` +
254
+ `Login username/password non attivo? Imposta un token con Nar2.setTokenEsterno(<bearer SPID>).`);
255
+ }
243
256
  return Nar2.#token;
244
257
  } else {
245
258
  const data = {username: this._username, password: this._password, type: "token"};
@@ -456,14 +469,17 @@ export class Nar2 {
456
469
  * @returns {Promise<{ok:boolean, data:Array<Object>|null, error?:string}>}
457
470
  */
458
471
  async getMotiviScelta(saId, config = {}) {
459
- const {soloScelta = true, semplifica = false, retryVuoto = 3} = config;
472
+ // Dropdown non critico: l'endpoint motiviOperazioneFiltered è spesso lento/intermittente.
473
+ // Timeout breve e pochi retry → se non risponde fallisce in fretta e il chiamante usa il
474
+ // default, invece di restare appeso (prima fino a ~4×10 tentativi senza timeout).
475
+ const {soloScelta = true, semplifica = false, retryVuoto = 0, timeout = 8000, maxRetry = 1} = config;
460
476
  let data = null;
461
477
  let lastRes = null;
462
478
  for (let i = 0; i <= retryVuoto; i++) {
463
479
  // NB: endpoint con envelope {status, result} → modalità default (unwrap di result),
464
480
  // a differenza di getMotiviOperazione il cui endpoint risponde con array nudo.
465
481
  const res = await this.#getDataFromUrlIdOrParams(Nar2.MOTIVI_OPERAZIONE_FILTERED, {
466
- replaceFromUrl: {sa_id: saId}
482
+ replaceFromUrl: {sa_id: saId}, timeout, maxRetry
467
483
  });
468
484
  lastRes = res;
469
485
  if (res.ok && Array.isArray(res.data)) {
@@ -2028,7 +2044,9 @@ export class Nar2 {
2028
2044
  console.log("[getDatiAssistitoNar2FromCf] Eccezione durante recupero Nar2:", e.message);
2029
2045
  }
2030
2046
  if (datiAssistito && datiAssistito.ok) break;
2031
- else console.log("[getDatiAssistitoNar2FromCf] Errore Nar2, tentativi rimanenti:" + (retry - i));
2047
+ else console.log("[getDatiAssistitoNar2FromCf] Errore Nar2, tentativi rimanenti:" + (retry - i)
2048
+ + (datiIdAssistito?.error ? ` | indicazione: ${datiIdAssistito.error}` : "")
2049
+ + (datiIdAssistito?.errorDetail?.statusCode ? ` (HTTP ${datiIdAssistito.errorDetail.statusCode})` : ""));
2032
2050
  }
2033
2051
  if (datiAssistito && datiAssistito.ok) {
2034
2052
  try {
@@ -2189,6 +2207,8 @@ export class Nar2 {
2189
2207
  getParams = null,
2190
2208
  replaceFromUrl = null,
2191
2209
  rawResponse = false, // se true, accetta risposte non incapsulate in {status, result}
2210
+ timeout = 30000, // ms: evita hang indefiniti su endpoint NAR2 lenti/intermittenti
2211
+ maxRetry = this._maxRetry,
2192
2212
  } = config;
2193
2213
  let out = {ok: false, data: null, error: null, errorDetail: null};
2194
2214
  let ok = false;
@@ -2208,7 +2228,7 @@ export class Nar2 {
2208
2228
  finalUrl = finalUrl.replace(`{${key}}`, (value === null || typeof value === "undefined") ? "null" : value.toString());
2209
2229
  }
2210
2230
 
2211
- for (let i = 0; i < this._maxRetry && !ok; i++) {
2231
+ for (let i = 0; i < maxRetry && !ok; i++) {
2212
2232
  try {
2213
2233
  await this.getToken();
2214
2234
  let response = null;
@@ -2217,6 +2237,7 @@ export class Nar2 {
2217
2237
  headers: {
2218
2238
  Authorization: `Bearer ${Nar2.#token}`,
2219
2239
  },
2240
+ timeout,
2220
2241
  });
2221
2242
  else
2222
2243
  response = await axios.get(finalUrl, {
@@ -2224,6 +2245,7 @@ export class Nar2 {
2224
2245
  Authorization: `Bearer ${Nar2.#token}`,
2225
2246
  },
2226
2247
  params: params,
2248
+ timeout,
2227
2249
  });
2228
2250
 
2229
2251
  // Caso 1: risposta raw (array nudo o oggetto senza envelope)
@@ -3037,7 +3059,10 @@ export class Nar2 {
3037
3059
  assistito = new Assistito();
3038
3060
  }
3039
3061
 
3040
- for (let i = 0; i < this._maxRetry && !ok; i++) {
3062
+ // Sogei va spesso in 500 lato server (consistente): bastano pochi tentativi prima di
3063
+ // passare al fallback legacy TS — inutile insistere 10 volte.
3064
+ const maxTentativiSogei = 2;
3065
+ for (let i = 0; i < maxTentativiSogei && !ok; i++) {
3041
3066
  try {
3042
3067
  await this.getToken({fallback});
3043
3068
  let out = null;
@@ -3087,12 +3112,16 @@ export class Nar2 {
3087
3112
  };
3088
3113
  }
3089
3114
  } catch (e) {
3090
- console.log(`[getDatiAssistitoFromCfSuSogeiNew] Errore tentativo Sogei:`, e.message);
3115
+ const stHttp = e.response?.status;
3116
+ const body = e.response?.data;
3117
+ const ind = typeof body === "string" ? body.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, 200)
3118
+ : (body ? JSON.stringify(body).slice(0, 200) : "");
3119
+ console.log(`[getDatiAssistitoFromCfSuSogeiNew] Errore tentativo Sogei: HTTP ${stHttp ?? "?"} — ${e.message}${ind ? ` | risposta: ${ind}` : ""}`);
3091
3120
  await this.getToken({fallback});
3092
3121
  }
3093
3122
  }
3094
3123
  if (!ok) {
3095
- console.log(`[getDatiAssistitoFromCfSuSogeiNew] Sogei fallito dopo ${this._maxRetry} tentativi per CF: ${cf?.substring(0, 6)}*** — provo fallback legacy TS`);
3124
+ console.log(`[getDatiAssistitoFromCfSuSogeiNew] Sogei fallito dopo ${maxTentativiSogei} tentativi per CF: ${cf?.substring(0, 6)}*** — provo fallback legacy TS`);
3096
3125
  try {
3097
3126
  const fb = await this.#fallbackLegacyTsToP801(cf, assistito);
3098
3127
  if (fb) {