@futdevpro/nts-dynamo 1.15.60 → 1.15.64
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/.dynamo/logs/cicd-pipeline/output.log +1551 -1541
- package/.dynamo/logs/cicd-pipeline/status.json +39 -39
- package/.github/workflows/main.yml +432 -426
- package/build/_collections/global-settings.const.d.ts.map +1 -1
- package/build/_collections/global-settings.const.js +6 -0
- package/build/_collections/global-settings.const.js.map +1 -1
- package/build/_collections/mongo-reconnect-guard.util.d.ts +74 -0
- package/build/_collections/mongo-reconnect-guard.util.d.ts.map +1 -0
- package/build/_collections/mongo-reconnect-guard.util.js +111 -0
- package/build/_collections/mongo-reconnect-guard.util.js.map +1 -0
- package/build/_models/interfaces/global-settings.interface.d.ts +21 -0
- package/build/_models/interfaces/global-settings.interface.d.ts.map +1 -1
- package/build/_services/core/global.service.d.ts.map +1 -1
- package/build/_services/core/global.service.js +15 -2
- package/build/_services/core/global.service.js.map +1 -1
- package/build/_services/server/app.server.d.ts.map +1 -1
- package/build/_services/server/app.server.js +21 -0
- package/build/_services/server/app.server.js.map +1 -1
- package/package.json +1 -1
- package/src/_collections/global-settings.const.ts +7 -0
- package/src/_collections/mongo-reconnect-guard.util.spec.ts +52 -0
- package/src/_collections/mongo-reconnect-guard.util.ts +172 -0
- package/src/_models/interfaces/global-settings.interface.ts +22 -0
- package/src/_services/core/global.service.spec.ts +17 -0
- package/src/_services/core/global.service.ts +19 -5
- package/src/_services/server/app.server.ts +22 -1
|
@@ -54,6 +54,13 @@ export const DyNTS_global_settings: DyNTS_Global_Settings = {
|
|
|
54
54
|
|
|
55
55
|
autoResearchIssues: true,
|
|
56
56
|
|
|
57
|
+
// Auth-service kötelező alapból (régi viselkedés). Public / no-auth szervereknél
|
|
58
|
+
// `auth_settings.optional = true` → getAuthService() undefined-ot ad dobás helyett,
|
|
59
|
+
// és a startup 'Authentication Service missing!' warn is elmarad.
|
|
60
|
+
auth_settings: {
|
|
61
|
+
optional: false,
|
|
62
|
+
},
|
|
63
|
+
|
|
57
64
|
bot_settings: null,
|
|
58
65
|
|
|
59
66
|
openAi_settings: null,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { shouldForceMongoReconnect, MongoReconnectState } from './mongo-reconnect-guard.util';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A Mongo reconnect-guard PURE döntés-magjának unit-tesztjei. A helyesség kritikus, mert ez dönti
|
|
5
|
+
* el, hogy egy futó service force-reconnectolja-e a Mongo-kapcsolatát (disconnect+connect →
|
|
6
|
+
* re-resolve). Healthy connection-t SOHA nem szabad bántani; csak tartós nem-connected + grace +
|
|
7
|
+
* cooldown letelt esetén szabad force-reconnectolni.
|
|
8
|
+
*/
|
|
9
|
+
describe('| mongo-reconnect-guard shouldForceMongoReconnect', () => {
|
|
10
|
+
const base: MongoReconnectState = {
|
|
11
|
+
readyState: 0,
|
|
12
|
+
msSinceUnhealthy: 60_000, // 60s unhealthy (a 30s grace fölött)
|
|
13
|
+
unhealthyGraceMs: 30_000,
|
|
14
|
+
reconnectInFlight: false,
|
|
15
|
+
msSinceLastReconnect: Number.POSITIVE_INFINITY,
|
|
16
|
+
reconnectCooldownMs: 20_000,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
it('| connected (readyState=1) → SOHA nem reconnect (a healthy connection-t nem bántjuk)', () => {
|
|
20
|
+
expect(shouldForceMongoReconnect({ ...base, readyState: 1 }).reconnect).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('| connecting (readyState=2) → nem reconnect (hagyjuk a drivert befejezni)', () => {
|
|
24
|
+
expect(shouldForceMongoReconnect({ ...base, readyState: 2 }).reconnect).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('| disconnected + grace + cooldown letelt + nincs in-flight → RECONNECT', () => {
|
|
28
|
+
const d = shouldForceMongoReconnect({ ...base, readyState: 0 });
|
|
29
|
+
expect(d.reconnect).toBe(true);
|
|
30
|
+
expect(d.reason).toContain('re-resolve');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('| disconnecting (readyState=3) ugyanígy → RECONNECT (tartós nem-connected)', () => {
|
|
34
|
+
expect(shouldForceMongoReconnect({ ...base, readyState: 3 }).reconnect).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('| grace-en BELÜL → nem reconnect (hagyjuk a driver saját reconnect-jét előbb)', () => {
|
|
38
|
+
expect(shouldForceMongoReconnect({ ...base, msSinceUnhealthy: 10_000 }).reconnect).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('| cooldown-on belül (frissen reconnectoltunk) → nem reconnect (anti-thrash)', () => {
|
|
42
|
+
expect(shouldForceMongoReconnect({ ...base, msSinceLastReconnect: 5_000 }).reconnect).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('| már van in-flight reconnect → nem indítunk másikat', () => {
|
|
46
|
+
expect(shouldForceMongoReconnect({ ...base, reconnectInFlight: true }).reconnect).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('| pontosan a grace-küszöbön (msSinceUnhealthy === grace) → RECONNECT', () => {
|
|
50
|
+
expect(shouldForceMongoReconnect({ ...base, msSinceUnhealthy: 30_000, unhealthyGraceMs: 30_000 }).reconnect).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { DyFM_Log, second } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DyNTS Mongo reconnect-guard (2026-06-20) — TÚLÉLI a Mongo-konténer IP-VÁLTÁSÁT.
|
|
5
|
+
*
|
|
6
|
+
* PROBLÉMA (élőben, 2026-06-19): a MongoDB-driver a hostnevet (`mongodb`) connect-kor EGYSZER
|
|
7
|
+
* resolválja és cache-eli az IP-t (pl. 172.18.0.20); standalone-nál disconnect után UGYANAZT az
|
|
8
|
+
* IP-t pingeli, NEM re-resolve-ol. Ha a Mongo-konténert recreate-elik (új docker-network IP), a
|
|
9
|
+
* driver örökre a HALOTT IP-n lóg → `connect ECONNREFUSED <oldIp>:29017` + `buffering timed out`,
|
|
10
|
+
* és a service magától SOHA nem áll vissza (csak külső restart-tal). Ez a 2026-06-19 incidens fél
|
|
11
|
+
* test-stacket levitt (auth/ftp/helocia/futdevpro/… mind a régi Mongo-IP-n ragadt).
|
|
12
|
+
*
|
|
13
|
+
* MEGOLDÁS: a guard figyeli a `mongoose.connection.readyState`-et; ha TARTÓSAN nem-connected
|
|
14
|
+
* (a driver saját reconnect-grace-e UTÁN is), **TELJES `disconnect()` + `connect()`-et** csinál
|
|
15
|
+
* → ÚJ MongoClient → ÚJ DNS-resolve → új IP → reconnect. Így minden DyNTS_App-service magától
|
|
16
|
+
* túléli a Mongo IP-váltását, KÜLSŐ beavatkozás nélkül. Best-effort, sose dob; csak konténer-IP-
|
|
17
|
+
* frissítés, NEM törli/írja az adatot.
|
|
18
|
+
*
|
|
19
|
+
* SAFE always-on: egy app újracsatlakozása a SAJÁT DB-jéhez korrekt, nem-destruktív viselkedés
|
|
20
|
+
* (nincs az infra-restart-érzékenység, ami a külső watchdog-oknál — FR-229/237). Csak akkor
|
|
21
|
+
* avatkozik be, ha readyState !== 1 a grace + cooldown letelte UTÁN is; healthy connection-t (===1)
|
|
22
|
+
* SOHA nem bánt.
|
|
23
|
+
*
|
|
24
|
+
* A döntés-mag (`shouldForceMongoReconnect`) PURE + unit-tesztelt; a futtató injektált dep-ekkel
|
|
25
|
+
* dolgozik (readyState-olvasás / reconnect / log) a tesztelhetőségért.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** A pure döntés inputja (minden numerikus → determinisztikus + tesztelhető). */
|
|
29
|
+
export interface MongoReconnectState {
|
|
30
|
+
/** mongoose.connection.readyState: 0=disconnected, 1=connected, 2=connecting, 3=disconnecting. */
|
|
31
|
+
readyState: number;
|
|
32
|
+
/** Mióta (ms) nem-connected a readyState (0, ha épp connected). */
|
|
33
|
+
msSinceUnhealthy: number;
|
|
34
|
+
/** Grace: ennyi ideig hagyjuk a drivert SAJÁT magát visszahozni, mielőtt force-reconnectolnánk. */
|
|
35
|
+
unhealthyGraceMs: number;
|
|
36
|
+
/** Épp folyamatban van-e már egy force-reconnect (ne torlódjon). */
|
|
37
|
+
reconnectInFlight: boolean;
|
|
38
|
+
/** Mióta (ms) volt az utolsó force-reconnect (Infinity, ha még nem volt). */
|
|
39
|
+
msSinceLastReconnect: number;
|
|
40
|
+
/** Két force-reconnect közti minimum (anti-thrash). */
|
|
41
|
+
reconnectCooldownMs: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A pure döntés eredménye. */
|
|
45
|
+
export interface MongoReconnectDecision {
|
|
46
|
+
reconnect: boolean;
|
|
47
|
+
reason: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* PURE döntés: kell-e force-reconnect. Healthy (===1) / connecting (===2) / in-flight / grace-en
|
|
52
|
+
* vagy cooldown-on belül → NEM. Csak tartós nem-connected (disconnected/disconnecting) + grace és
|
|
53
|
+
* cooldown letelt + nincs in-flight → IGEN.
|
|
54
|
+
*/
|
|
55
|
+
export function shouldForceMongoReconnect(s: MongoReconnectState): MongoReconnectDecision {
|
|
56
|
+
if (s.readyState === 1) {
|
|
57
|
+
return { reconnect: false, reason: 'connected (readyState=1)' };
|
|
58
|
+
}
|
|
59
|
+
if (s.readyState === 2) {
|
|
60
|
+
return { reconnect: false, reason: 'connecting (readyState=2) — letting the driver finish' };
|
|
61
|
+
}
|
|
62
|
+
if (s.reconnectInFlight) {
|
|
63
|
+
return { reconnect: false, reason: 'a force-reconnect is already in flight' };
|
|
64
|
+
}
|
|
65
|
+
if (s.msSinceUnhealthy < s.unhealthyGraceMs) {
|
|
66
|
+
return {
|
|
67
|
+
reconnect: false,
|
|
68
|
+
reason: `unhealthy for ${Math.round(s.msSinceUnhealthy / 1000)}s (< ${Math.round(s.unhealthyGraceMs / 1000)}s grace) — letting the driver self-heal first`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (s.msSinceLastReconnect < s.reconnectCooldownMs) {
|
|
72
|
+
return {
|
|
73
|
+
reconnect: false,
|
|
74
|
+
reason: `last force-reconnect ${Math.round(s.msSinceLastReconnect / 1000)}s ago (< ${Math.round(s.reconnectCooldownMs / 1000)}s cooldown)`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
reconnect: true,
|
|
79
|
+
reason: `Mongo connection unhealthy (readyState=${s.readyState}) for ${Math.round(s.msSinceUnhealthy / 1000)}s past grace — forcing disconnect+connect to re-resolve the host IP`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Injektált függőségek a futtatóhoz (best-effort; a futtató sose dob). */
|
|
84
|
+
export interface MongoReconnectGuardDeps {
|
|
85
|
+
/** A jelenlegi `mongoose.connection.readyState`. */
|
|
86
|
+
getReadyState: () => number;
|
|
87
|
+
/**
|
|
88
|
+
* Force-reconnect: `disconnect()` majd `connect(uri, opts)` — ÚJ MongoClient → ÚJ DNS-resolve.
|
|
89
|
+
* Dobhat (a connect elbukhat, ha a Mongo épp tényleg down) — a futtató elkapja.
|
|
90
|
+
*/
|
|
91
|
+
reconnect: () => Promise<void>;
|
|
92
|
+
/** Log. */
|
|
93
|
+
log: (msg: string) => void;
|
|
94
|
+
/** Tick-intervallum (ms). Default 10s. */
|
|
95
|
+
intervalMs?: number;
|
|
96
|
+
/** Grace, amíg a driver saját reconnect-jét várjuk (ms). Default 30s. */
|
|
97
|
+
unhealthyGraceMs?: number;
|
|
98
|
+
/** Cooldown két force-reconnect közt (ms). Default 20s. */
|
|
99
|
+
reconnectCooldownMs?: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Elindítja a periodikus reconnect-guard-ot. A visszaadott függvény leállítja (teszt/teardown).
|
|
104
|
+
* `.unref()` — nem tartja életben az event-loop-ot.
|
|
105
|
+
*/
|
|
106
|
+
export function startMongoReconnectGuard(deps: MongoReconnectGuardDeps): () => void {
|
|
107
|
+
const intervalMs: number = deps.intervalMs ?? 10 * second;
|
|
108
|
+
const unhealthyGraceMs: number = deps.unhealthyGraceMs ?? 30 * second;
|
|
109
|
+
const reconnectCooldownMs: number = deps.reconnectCooldownMs ?? 20 * second;
|
|
110
|
+
|
|
111
|
+
let unhealthySinceMs: number | null = null;
|
|
112
|
+
let lastReconnectAtMs: number | null = null;
|
|
113
|
+
let inFlight: boolean = false;
|
|
114
|
+
let ticking: boolean = false;
|
|
115
|
+
|
|
116
|
+
const tick = async (): Promise<void> => {
|
|
117
|
+
if (ticking) { return; }
|
|
118
|
+
ticking = true;
|
|
119
|
+
try {
|
|
120
|
+
const nowMs: number = Date.now();
|
|
121
|
+
const readyState: number = deps.getReadyState();
|
|
122
|
+
|
|
123
|
+
if (readyState === 1) {
|
|
124
|
+
if (unhealthySinceMs !== null) {
|
|
125
|
+
deps.log(`[mongo-reconnect-guard] Mongo connection healthy again (was unhealthy ${Math.round((nowMs - unhealthySinceMs) / 1000)}s)`);
|
|
126
|
+
}
|
|
127
|
+
unhealthySinceMs = null;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Nem-connected: indítsuk/folytassuk az unhealthy-órát.
|
|
132
|
+
if (unhealthySinceMs === null) { unhealthySinceMs = nowMs; }
|
|
133
|
+
|
|
134
|
+
const decision: MongoReconnectDecision = shouldForceMongoReconnect({
|
|
135
|
+
readyState: readyState,
|
|
136
|
+
msSinceUnhealthy: nowMs - unhealthySinceMs,
|
|
137
|
+
unhealthyGraceMs: unhealthyGraceMs,
|
|
138
|
+
reconnectInFlight: inFlight,
|
|
139
|
+
msSinceLastReconnect: lastReconnectAtMs === null ? Number.POSITIVE_INFINITY : nowMs - lastReconnectAtMs,
|
|
140
|
+
reconnectCooldownMs: reconnectCooldownMs,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!decision.reconnect) { return; }
|
|
144
|
+
|
|
145
|
+
deps.log(`[mongo-reconnect-guard] ${decision.reason}`);
|
|
146
|
+
inFlight = true;
|
|
147
|
+
lastReconnectAtMs = nowMs;
|
|
148
|
+
try {
|
|
149
|
+
await deps.reconnect();
|
|
150
|
+
deps.log('[mongo-reconnect-guard] force-reconnect dispatched ✓ (re-resolved host; awaiting connection open)');
|
|
151
|
+
} catch (e: unknown) {
|
|
152
|
+
// A connect elbukhat, ha a Mongo épp tényleg elérhetetlen — nem baj, a következő tick
|
|
153
|
+
// (cooldown után) újrapróbál; amint a Mongo új IP-n elérhető, a re-resolve becsatlakozik.
|
|
154
|
+
deps.log(`[mongo-reconnect-guard] force-reconnect attempt failed (will retry after cooldown): ${e instanceof Error ? e.message : String(e)}`);
|
|
155
|
+
} finally {
|
|
156
|
+
inFlight = false;
|
|
157
|
+
}
|
|
158
|
+
} catch (e: unknown) {
|
|
159
|
+
deps.log(`[mongo-reconnect-guard] tick error (non-fatal): ${e instanceof Error ? e.message : String(e)}`);
|
|
160
|
+
} finally {
|
|
161
|
+
ticking = false;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const handle: NodeJS.Timeout = setInterval((): void => { void tick(); }, intervalMs);
|
|
166
|
+
if (typeof handle.unref === 'function') { handle.unref(); }
|
|
167
|
+
DyFM_Log.info(
|
|
168
|
+
`[mongo-reconnect-guard] started — check every ${Math.round(intervalMs / 1000)}s, `
|
|
169
|
+
+ `force disconnect+reconnect after ${Math.round(unhealthyGraceMs / 1000)}s sustained-unhealthy (re-resolves host IP), cooldown ${Math.round(reconnectCooldownMs / 1000)}s`,
|
|
170
|
+
);
|
|
171
|
+
return (): void => { clearInterval(handle); };
|
|
172
|
+
}
|
|
@@ -122,6 +122,28 @@ export interface DyNTS_Global_Settings {
|
|
|
122
122
|
*/
|
|
123
123
|
assistant_settings?: any;
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Auth-service beállítások.
|
|
127
|
+
*
|
|
128
|
+
* Ha `optional: true`, a framework NEM követeli meg az egyedi Auth Service-t:
|
|
129
|
+
* a {@link DyNTS_GlobalService.getAuthService} hiányzó service esetén `undefined`-ot
|
|
130
|
+
* ad vissza a `"Unique Authentication Service missing!"` hiba DOBÁSA HELYETT, és
|
|
131
|
+
* a startup-warn ('Authentication Service missing!') is elmarad. Public / no-auth
|
|
132
|
+
* szervereknek (pl. nyílt RAG/MCP backend), ahol nincs saját auth-réteg.
|
|
133
|
+
*
|
|
134
|
+
* Default: `{ optional: false }` — az auth-service kötelező (visszafelé kompatibilis,
|
|
135
|
+
* a régi viselkedés: hiányzó service → dobott hiba).
|
|
136
|
+
*/
|
|
137
|
+
auth_settings?: {
|
|
138
|
+
/**
|
|
139
|
+
* Ha `true`, az Auth Service opcionális: `getAuthService()` NEM dob hiányzó
|
|
140
|
+
* service esetén, hanem `undefined`-ot ad vissza (a hívók optional-chaining-gel
|
|
141
|
+
* kezelik), és a startup 'Authentication Service missing!' warn is elmarad.
|
|
142
|
+
* Default: `false`.
|
|
143
|
+
*/
|
|
144
|
+
optional?: boolean;
|
|
145
|
+
};
|
|
146
|
+
|
|
125
147
|
/**
|
|
126
148
|
* this setting will set the doc chunking settings
|
|
127
149
|
*/
|
|
@@ -128,6 +128,23 @@ describe('| DyNTS_GlobalService', () => {
|
|
|
128
128
|
|
|
129
129
|
expect(result).toBe(authService);
|
|
130
130
|
});
|
|
131
|
+
|
|
132
|
+
it('| should NOT throw and return undefined when auth_settings.optional is true', () => {
|
|
133
|
+
// Suppress: public / no-auth szerver — a beállítás elnyomja a
|
|
134
|
+
// "Unique Authentication Service missing!" dobást. A flag-et finally-ben
|
|
135
|
+
// visszaállítjuk, hogy a randomizált futásban ne szivárogjon más tesztbe.
|
|
136
|
+
const prev = DyNTS_global_settings.auth_settings?.optional;
|
|
137
|
+
try {
|
|
138
|
+
DyNTS_global_settings.auth_settings = { optional: true };
|
|
139
|
+
let result: DyNTS_AuthService;
|
|
140
|
+
expect(() => {
|
|
141
|
+
result = DyNTS_GlobalService.getAuthService();
|
|
142
|
+
}).not.toThrow();
|
|
143
|
+
expect(result).toBeUndefined();
|
|
144
|
+
} finally {
|
|
145
|
+
DyNTS_global_settings.auth_settings = { optional: prev ?? false };
|
|
146
|
+
}
|
|
147
|
+
});
|
|
131
148
|
});
|
|
132
149
|
|
|
133
150
|
describe('| getDBServiceCollection', () => {
|
|
@@ -212,7 +212,10 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
212
212
|
private static async setAuthService(authService?: DyNTS_AuthService): Promise<void> {
|
|
213
213
|
try {
|
|
214
214
|
if (!authService) {
|
|
215
|
-
|
|
215
|
+
// Opcionális auth-service esetén ez VÁRT (public / no-auth szerver) — nincs warn.
|
|
216
|
+
if (!DyNTS_global_settings.auth_settings?.optional) {
|
|
217
|
+
DyFM_Log.warn(`Authentication Service missing!`);
|
|
218
|
+
}
|
|
216
219
|
} else {
|
|
217
220
|
this.instance.authService = authService;
|
|
218
221
|
}
|
|
@@ -300,16 +303,27 @@ export class DyNTS_GlobalService extends DyNTS_SingletonService {
|
|
|
300
303
|
*/
|
|
301
304
|
static getAuthService(): DyNTS_AuthService {
|
|
302
305
|
if (!this.instance?.authService) {
|
|
306
|
+
// Ha az app explicit opcionálisnak jelölte az auth-service-t
|
|
307
|
+
// (DyNTS_global_settings.auth_settings.optional), NEM dobunk: undefined-ot
|
|
308
|
+
// adunk vissza. A hívók (pl. a request-path issuer-kinyerés) optional-
|
|
309
|
+
// chaining-gel kezelik. Public / no-auth szervereknek — így a
|
|
310
|
+
// "Unique Authentication Service missing!" hiba suppress-elhető beállításból.
|
|
311
|
+
if (DyNTS_global_settings.auth_settings?.optional) {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
|
|
303
315
|
throw new Error(
|
|
304
|
-
`\n Unique Authentication Service missing!` +
|
|
305
|
-
`\n Please create a Unique Authentication Service extending DyNTS_AuthService, ` +
|
|
316
|
+
`\n Unique Authentication Service missing!` +
|
|
317
|
+
`\n Please create a Unique Authentication Service extending DyNTS_AuthService, ` +
|
|
306
318
|
`\n and Setup with DyNTS_GlobalServiceC.setServices(...)` +
|
|
307
319
|
'\n (If you set the globalErrorHandler, ' +
|
|
308
|
-
'please check if it is using the same node_modules as the app)'
|
|
320
|
+
'please check if it is using the same node_modules as the app)' +
|
|
321
|
+
'\n (To run WITHOUT an auth service, set ' +
|
|
322
|
+
'DyNTS_global_settings.auth_settings.optional = true)',
|
|
309
323
|
);
|
|
310
324
|
}
|
|
311
325
|
|
|
312
|
-
return this.instance.authService;
|
|
326
|
+
return this.instance.authService;
|
|
313
327
|
}
|
|
314
328
|
|
|
315
329
|
/**
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { DyNTS_defaultFallbackCacheMaxAge } from '../../_collections/default-fallback-cache-max-age.const';
|
|
30
30
|
import { DyNTS_defaultNotFoundPageHtml } from '../../_collections/default-not-found-page.const';
|
|
31
31
|
import { DyNTS_global_settings } from '../../_collections/global-settings.const';
|
|
32
|
+
import { startMongoReconnectGuard } from '../../_collections/mongo-reconnect-guard.util';
|
|
32
33
|
import { DyNTS_RouteSecurity } from '../../_enums/route-security.enum';
|
|
33
34
|
import { DyNTS_App_Params } from '../../_models/control-models/app-params.control-model';
|
|
34
35
|
import {
|
|
@@ -985,10 +986,30 @@ export abstract class DyNTS_App extends DyNTS_SingletonService {
|
|
|
985
986
|
} catch (error) {
|
|
986
987
|
throw new DyFM_Error({
|
|
987
988
|
...this._getDefaultErrorSettings('startDB', error),
|
|
988
|
-
|
|
989
|
+
|
|
989
990
|
errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-AS0-SDB0`,
|
|
990
991
|
});
|
|
991
992
|
}
|
|
993
|
+
|
|
994
|
+
// 2026-06-20 — Mongo reconnect-guard. A MongoDB-driver a hostnevet connect-kor EGYSZER
|
|
995
|
+
// resolválja + cache-eli az IP-t; ha a Mongo-konténert recreate-elik (új docker-network IP),
|
|
996
|
+
// a driver a HALOTT IP-n ragad → ECONNREFUSED + buffering-timeout, és a service magától SOHA
|
|
997
|
+
// nem áll vissza. A guard sustained-disconnect (readyState !== 1) esetén — a driver saját
|
|
998
|
+
// grace-e UTÁN — TELJES disconnect()+connect()-et csinál → ÚJ MongoClient → ÚJ DNS-resolve →
|
|
999
|
+
// új IP → reconnect. Best-effort, always-on, healthy connection-t (===1) SOHA nem bánt; csak
|
|
1000
|
+
// konténer-IP-frissítés, nem ír adatot. Lásd: mongo-reconnect-guard.util.ts.
|
|
1001
|
+
try {
|
|
1002
|
+
startMongoReconnectGuard({
|
|
1003
|
+
getReadyState: (): number => this.mongoose.connection.readyState,
|
|
1004
|
+
reconnect: async (): Promise<void> => {
|
|
1005
|
+
await this.mongoose.disconnect().catch((): void => undefined);
|
|
1006
|
+
await this.mongoose.connect(this._params.dbUri, this._params.dbOptions);
|
|
1007
|
+
},
|
|
1008
|
+
log: (msg: string): void => DyFM_Log.warn(msg),
|
|
1009
|
+
});
|
|
1010
|
+
} catch (guardErr) {
|
|
1011
|
+
DyFM_Log.warn(`[mongo-reconnect-guard] failed to start (non-fatal): ${guardErr instanceof Error ? guardErr.message : String(guardErr)}`);
|
|
1012
|
+
}
|
|
992
1013
|
}
|
|
993
1014
|
|
|
994
1015
|
/**
|