fhirsmith 0.5.0 → 0.5.1
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/CHANGELOG.md +13 -1
- package/package.json +1 -1
- package/server.js +36 -7
- package/xig/xig.js +138 -136
package/CHANGELOG.md
CHANGED
|
@@ -5,13 +5,25 @@ All notable changes to the Health Intersections Node Server will be documented i
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [v0.5.1] - 2026-02-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Improved logging of startup conditions and failure
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Fixed bad cron scheduled processing in XIG module
|
|
15
|
+
|
|
16
|
+
### Tx Conformance Statement
|
|
17
|
+
|
|
18
|
+
FHIRsmith 0.5.1 passed all 1288 HL7 terminology service tests (modes tx.fhir.org,omop,general,snomed, tests v1.9.1-SNAPSHOT, runner v6.8.0)
|
|
19
|
+
|
|
8
20
|
## [v0.5.0] - 2026-02-19
|
|
9
21
|
|
|
10
22
|
### Added
|
|
11
23
|
- Prototype Implementation of $related operation
|
|
12
24
|
|
|
13
25
|
### Changed
|
|
14
|
-
- A great deal of QA work preparing the server to run tx.fhir.org, which led to 100s of fixes
|
|
26
|
+
- A great deal of QA work preparing the server to run tx.fhir.org, which led to 100s of fixes
|
|
15
27
|
|
|
16
28
|
### Tx Conformance Statement
|
|
17
29
|
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -10,6 +10,7 @@ const express = require('express');
|
|
|
10
10
|
// const cors = require('cors');
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
13
14
|
const folders = require('./library/folder-setup'); // <-- ADD: load early
|
|
14
15
|
const { statSync, readdirSync } = require('fs');
|
|
15
16
|
const escape = require('escape-html');
|
|
@@ -27,6 +28,17 @@ try {
|
|
|
27
28
|
|
|
28
29
|
const Logger = require('./library/logger');
|
|
29
30
|
const serverLog = Logger.getInstance().child({ module: 'server' });
|
|
31
|
+
const packageJson = require('./package.json');
|
|
32
|
+
|
|
33
|
+
// Startup banner
|
|
34
|
+
const totalMemGB = (os.totalmem() / 1024 / 1024 / 1024).toFixed(1);
|
|
35
|
+
const freeMemGB = (os.freemem() / 1024 / 1024 / 1024).toFixed(1);
|
|
36
|
+
serverLog.info(`========================================`);
|
|
37
|
+
serverLog.info(`FHIRsmith v${packageJson.version} starting (PID ${process.pid})`);
|
|
38
|
+
serverLog.info(`Node.js ${process.version} on ${os.type()} ${os.release()} (${os.arch()})`);
|
|
39
|
+
serverLog.info(`Memory: ${freeMemGB} GB free / ${totalMemGB} GB total`);
|
|
40
|
+
serverLog.info(`Data directory: ${folders.dataDir()}`);
|
|
41
|
+
serverLog.info(`========================================`);
|
|
30
42
|
|
|
31
43
|
const activeModules = config.modules ? Object.keys(config.modules)
|
|
32
44
|
.filter(mod => config.modules[mod].enabled)
|
|
@@ -43,7 +55,6 @@ const PublisherModule = require('./publisher/publisher.js');
|
|
|
43
55
|
const TokenModule = require('./token/token.js');
|
|
44
56
|
const NpmProjectorModule = require('./npmprojector/npmprojector.js');
|
|
45
57
|
const TXModule = require('./tx/tx.js');
|
|
46
|
-
const packageJson = require('./package.json');
|
|
47
58
|
|
|
48
59
|
const htmlServer = require('./library/html-server');
|
|
49
60
|
const ServerStats = require("./stats");
|
|
@@ -81,6 +92,7 @@ async function initializeModules() {
|
|
|
81
92
|
// Initialize SHL module
|
|
82
93
|
if (config.modules?.shl?.enabled) {
|
|
83
94
|
try {
|
|
95
|
+
serverLog.info('Initializing module: shl...');
|
|
84
96
|
modules.shl = new SHLModule(stats);
|
|
85
97
|
await modules.shl.initialize(config.modules.shl);
|
|
86
98
|
app.use('/shl', modules.shl.router);
|
|
@@ -93,6 +105,7 @@ async function initializeModules() {
|
|
|
93
105
|
// Initialize VCL module
|
|
94
106
|
if (config.modules?.vcl?.enabled) {
|
|
95
107
|
try {
|
|
108
|
+
serverLog.info('Initializing module: vcl...');
|
|
96
109
|
modules.vcl = new VCLModule(stats);
|
|
97
110
|
await modules.vcl.initialize(config.modules.vcl);
|
|
98
111
|
app.use('/VCL', modules.vcl.router);
|
|
@@ -101,11 +114,12 @@ async function initializeModules() {
|
|
|
101
114
|
throw error;
|
|
102
115
|
}
|
|
103
116
|
}
|
|
104
|
-
|
|
117
|
+
|
|
105
118
|
// Initialize XIG module
|
|
106
119
|
if (config.modules?.xig?.enabled) {
|
|
107
120
|
try {
|
|
108
|
-
|
|
121
|
+
serverLog.info('Initializing module: xig...');
|
|
122
|
+
await xigModule.initializeXigModule(stats, config.modules.xig);
|
|
109
123
|
app.use('/xig', xigModule.router);
|
|
110
124
|
modules.xig = xigModule;
|
|
111
125
|
} catch (error) {
|
|
@@ -117,6 +131,7 @@ async function initializeModules() {
|
|
|
117
131
|
// Initialize Packages module
|
|
118
132
|
if (config.modules?.packages?.enabled) {
|
|
119
133
|
try {
|
|
134
|
+
serverLog.info('Initializing module: packages...');
|
|
120
135
|
modules.packages = new PackagesModule(stats);
|
|
121
136
|
await modules.packages.initialize(config.modules.packages);
|
|
122
137
|
app.use('/packages', modules.packages.router);
|
|
@@ -130,6 +145,7 @@ async function initializeModules() {
|
|
|
130
145
|
// Initialize Registry module
|
|
131
146
|
if (config.modules?.registry?.enabled) {
|
|
132
147
|
try {
|
|
148
|
+
serverLog.info('Initializing module: registry...');
|
|
133
149
|
modules.registry = new RegistryModule(stats);
|
|
134
150
|
await modules.registry.initialize(config.modules.registry);
|
|
135
151
|
app.use('/tx-reg', modules.registry.router);
|
|
@@ -142,6 +158,7 @@ async function initializeModules() {
|
|
|
142
158
|
// Initialize Publisher module
|
|
143
159
|
if (config.modules?.publisher?.enabled) {
|
|
144
160
|
try {
|
|
161
|
+
serverLog.info('Initializing module: publisher...');
|
|
145
162
|
modules.publisher = new PublisherModule(stats);
|
|
146
163
|
await modules.publisher.initialize(config.modules.publisher);
|
|
147
164
|
app.use('/publisher', modules.publisher.router);
|
|
@@ -154,6 +171,7 @@ async function initializeModules() {
|
|
|
154
171
|
// Initialize Token module
|
|
155
172
|
if (config.modules?.token?.enabled) {
|
|
156
173
|
try {
|
|
174
|
+
serverLog.info('Initializing module: token...');
|
|
157
175
|
modules.token = new TokenModule(stats);
|
|
158
176
|
await modules.token.initialize(config.modules.token);
|
|
159
177
|
app.use('/token', modules.token.router);
|
|
@@ -166,6 +184,7 @@ async function initializeModules() {
|
|
|
166
184
|
// Initialize NpmProjector module
|
|
167
185
|
if (config.modules?.npmprojector?.enabled) {
|
|
168
186
|
try {
|
|
187
|
+
serverLog.info('Initializing module: npmprojector...');
|
|
169
188
|
modules.npmprojector = new NpmProjectorModule(stats);
|
|
170
189
|
await modules.npmprojector.initialize(config.modules.npmprojector);
|
|
171
190
|
const basePath = NpmProjectorModule.getBasePath(config.modules.npmprojector);
|
|
@@ -181,6 +200,7 @@ async function initializeModules() {
|
|
|
181
200
|
// because it supports multiple endpoints at different paths
|
|
182
201
|
if (config.modules?.tx?.enabled) {
|
|
183
202
|
try {
|
|
203
|
+
serverLog.info('Initializing module: tx...');
|
|
184
204
|
modules.tx = new TXModule(stats);
|
|
185
205
|
await modules.tx.initialize(config.modules.tx, app);
|
|
186
206
|
} catch (error) {
|
|
@@ -365,6 +385,13 @@ async function buildRootPageContent() {
|
|
|
365
385
|
// eslint-disable-next-line no-unused-vars
|
|
366
386
|
process.on('unhandledRejection', (reason, promise) => {
|
|
367
387
|
console.error('Unhandled Rejection:', reason);
|
|
388
|
+
serverLog.error('Unhandled Rejection:', reason);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
process.on('uncaughtException', (error) => {
|
|
392
|
+
console.error('FATAL - Uncaught Exception:', error);
|
|
393
|
+
serverLog.error('FATAL - Uncaught Exception:', error);
|
|
394
|
+
process.exitCode = 1;
|
|
368
395
|
});
|
|
369
396
|
|
|
370
397
|
app.get('/', async (req, res) => {
|
|
@@ -382,7 +409,7 @@ app.get('/', async (req, res) => {
|
|
|
382
409
|
}
|
|
383
410
|
|
|
384
411
|
const content = await buildRootPageContent();
|
|
385
|
-
|
|
412
|
+
|
|
386
413
|
// Build basic stats for root page
|
|
387
414
|
const stats = {
|
|
388
415
|
version: packageJson.version,
|
|
@@ -431,7 +458,7 @@ app.get('/', async (req, res) => {
|
|
|
431
458
|
Object.keys(enabledModules)
|
|
432
459
|
.filter(m => m !== 'tx')
|
|
433
460
|
.map(m => [
|
|
434
|
-
m,
|
|
461
|
+
m,
|
|
435
462
|
m === 'vcl' ? '/VCL' : `/${m}`
|
|
436
463
|
])
|
|
437
464
|
),
|
|
@@ -542,8 +569,10 @@ async function startServer() {
|
|
|
542
569
|
modules.packages.startInitialCrawler();
|
|
543
570
|
}
|
|
544
571
|
} catch (error) {
|
|
545
|
-
|
|
546
|
-
|
|
572
|
+
console.error('FATAL - Failed to start server:', error);
|
|
573
|
+
serverLog.error('FATAL - Failed to start server:', error);
|
|
574
|
+
// Give the logger a moment to flush before exiting
|
|
575
|
+
setTimeout(() => process.exit(1), 500);
|
|
547
576
|
}
|
|
548
577
|
}
|
|
549
578
|
|
package/xig/xig.js
CHANGED
|
@@ -2213,22 +2213,22 @@ router.get('/:packagePid/:resourceType/:resourceId', async (req, res) => {
|
|
|
2213
2213
|
const start = Date.now();
|
|
2214
2214
|
try {
|
|
2215
2215
|
|
|
2216
|
-
|
|
2216
|
+
const { packagePid, resourceType, resourceId } = req.params;
|
|
2217
2217
|
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2218
|
+
// Check if this looks like a package/resource pattern
|
|
2219
|
+
// Package PIDs typically contain dots and pipes: hl7.fhir.uv.extensions|current
|
|
2220
|
+
// Resource types are FHIR resource names: StructureDefinition, ValueSet, etc.
|
|
2221
2221
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2222
|
+
const isPackagePidFormat = packagePid.includes('.') || packagePid.includes('|');
|
|
2223
|
+
const isFhirResourceType = /^[A-Z][a-zA-Z]+$/.test(resourceType);
|
|
2224
2224
|
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2225
|
+
if (isPackagePidFormat && isFhirResourceType) {
|
|
2226
|
+
// This looks like a legacy resource URL, redirect to the proper format
|
|
2227
|
+
res.redirect(301, `/xig/resource/${packagePid}/${resourceType}/${resourceId}`);
|
|
2228
|
+
} else {
|
|
2229
|
+
// Not a resource URL pattern, return 404
|
|
2230
|
+
res.status(404).send('Not Found');
|
|
2231
|
+
}
|
|
2232
2232
|
} finally {
|
|
2233
2233
|
this.stats.countRequest(':id', Date.now() - start);
|
|
2234
2234
|
}
|
|
@@ -2333,74 +2333,74 @@ router.get('/stats', async (req, res) => {
|
|
|
2333
2333
|
const start = Date.now();
|
|
2334
2334
|
try {
|
|
2335
2335
|
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
try {
|
|
2336
|
+
const startTime = Date.now(); // Add this at the very beginning
|
|
2339
2337
|
|
|
2340
|
-
|
|
2341
|
-
getDatabaseInfo(),
|
|
2342
|
-
getDatabaseTableCounts()
|
|
2343
|
-
]);
|
|
2338
|
+
try {
|
|
2344
2339
|
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2340
|
+
const [dbInfo, tableCounts] = await Promise.all([
|
|
2341
|
+
getDatabaseInfo(),
|
|
2342
|
+
getDatabaseTableCounts()
|
|
2343
|
+
]);
|
|
2344
|
+
|
|
2345
|
+
const statsData = {
|
|
2346
|
+
cache: getCacheStats(),
|
|
2347
|
+
database: dbInfo,
|
|
2348
|
+
databaseAge: getDatabaseAgeInfo(),
|
|
2349
|
+
tableCounts: tableCounts,
|
|
2350
|
+
requests: getRequestStats()
|
|
2351
|
+
};
|
|
2352
2352
|
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2353
|
+
const content = buildStatsTable(statsData);
|
|
2354
|
+
|
|
2355
|
+
let introContent = '';
|
|
2356
|
+
const lastAttempt = getLastUpdateAttempt();
|
|
2357
|
+
|
|
2358
|
+
if (statsData.databaseAge.daysOld !== null && statsData.databaseAge.daysOld > 1) {
|
|
2359
|
+
introContent += `<div class="alert alert-warning">`;
|
|
2360
|
+
introContent += `<strong>⚠ Database is ${statsData.databaseAge.daysOld} days old.</strong> `;
|
|
2361
|
+
introContent += `Automatic updates are scheduled daily at 2 AM. `;
|
|
2362
|
+
if (lastAttempt) {
|
|
2363
|
+
if (lastAttempt.status === 'failed') {
|
|
2364
|
+
introContent += `<br><strong>Last update attempt failed</strong> at ${new Date(lastAttempt.timestamp).toLocaleString()}: `;
|
|
2365
|
+
introContent += `${escape(lastAttempt.error || 'Unknown error')}`;
|
|
2366
|
+
if (lastAttempt.downloadMeta && lastAttempt.downloadMeta.httpStatus) {
|
|
2367
|
+
introContent += ` (HTTP ${lastAttempt.downloadMeta.httpStatus})`;
|
|
2368
|
+
}
|
|
2369
|
+
} else if (lastAttempt.status === 'success') {
|
|
2370
|
+
introContent += `<br>Last successful update: ${new Date(lastAttempt.timestamp).toLocaleString()} `;
|
|
2371
|
+
introContent += `(file age based on filesystem mtime)`;
|
|
2368
2372
|
}
|
|
2369
|
-
} else
|
|
2370
|
-
introContent += `<br>
|
|
2371
|
-
introContent += `(file age based on filesystem mtime)`;
|
|
2373
|
+
} else {
|
|
2374
|
+
introContent += `<br>No update attempts recorded since server started.`;
|
|
2372
2375
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2376
|
+
introContent += `</div>`;
|
|
2377
|
+
} else if (lastAttempt && lastAttempt.status === 'failed') {
|
|
2378
|
+
// DB is fresh but last attempt failed — still worth showing
|
|
2379
|
+
introContent += `<div class="alert alert-warning">`;
|
|
2380
|
+
introContent += `<strong>Last update attempt failed</strong> at ${new Date(lastAttempt.timestamp).toLocaleString()}: `;
|
|
2381
|
+
introContent += `${escape(lastAttempt.error || 'Unknown error')}`;
|
|
2382
|
+
introContent += `</div>`;
|
|
2375
2383
|
}
|
|
2376
|
-
introContent += `</div>`;
|
|
2377
|
-
} else if (lastAttempt && lastAttempt.status === 'failed') {
|
|
2378
|
-
// DB is fresh but last attempt failed — still worth showing
|
|
2379
|
-
introContent += `<div class="alert alert-warning">`;
|
|
2380
|
-
introContent += `<strong>Last update attempt failed</strong> at ${new Date(lastAttempt.timestamp).toLocaleString()}: `;
|
|
2381
|
-
introContent += `${escape(lastAttempt.error || 'Unknown error')}`;
|
|
2382
|
-
introContent += `</div>`;
|
|
2383
|
-
}
|
|
2384
2384
|
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2385
|
+
if (!statsData.cache.loaded) {
|
|
2386
|
+
introContent += `<div class="alert alert-info">`;
|
|
2387
|
+
introContent += `<strong>Info:</strong> Cache is still loading. Some statistics may be incomplete.`;
|
|
2388
|
+
introContent += `</div>`;
|
|
2389
|
+
}
|
|
2390
2390
|
|
|
2391
|
-
|
|
2391
|
+
const fullContent = introContent + content;
|
|
2392
2392
|
|
|
2393
|
-
|
|
2394
|
-
|
|
2393
|
+
const stats = await gatherPageStatistics();
|
|
2394
|
+
stats.processingTime = Date.now() - startTime;
|
|
2395
2395
|
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2396
|
+
const html = renderPage('FHIR IG Statistics Status', fullContent, stats);
|
|
2397
|
+
res.setHeader('Content-Type', 'text/html');
|
|
2398
|
+
res.send(html);
|
|
2399
2399
|
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
xigLog.error(`Error generating stats page: ${error.message}`);
|
|
2402
|
+
htmlServer.sendErrorResponse(res, 'xig', error);
|
|
2403
|
+
}
|
|
2404
2404
|
} finally {
|
|
2405
2405
|
globalStats.countRequest('stats', Date.now() - start);
|
|
2406
2406
|
}
|
|
@@ -2410,56 +2410,56 @@ router.get('/stats', async (req, res) => {
|
|
|
2410
2410
|
router.get('/resource/:packagePid/:resourceType/:resourceId', async (req, res) => {
|
|
2411
2411
|
const start = Date.now();
|
|
2412
2412
|
try {
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2413
|
+
const startTime = Date.now(); // Add this at the very beginning
|
|
2414
|
+
try {
|
|
2415
|
+
const { packagePid, resourceType, resourceId } = req.params;
|
|
2416
2416
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2417
|
+
// Convert URL-safe package PID back to database format (| to #)
|
|
2418
|
+
const dbPackagePid = packagePid.replace(/\|/g, '#');
|
|
2419
2419
|
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2420
|
+
if (!xigDb) {
|
|
2421
|
+
throw new Error('Database not available');
|
|
2422
|
+
}
|
|
2423
2423
|
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2424
|
+
// Get package information first
|
|
2425
|
+
const packageObj = getPackageByPid(dbPackagePid);
|
|
2426
|
+
if (!packageObj) {
|
|
2427
|
+
return res.status(404).send(renderPage('Resource Not Found',
|
|
2428
|
+
`<div class="alert alert-danger">Unknown Package: ${escape(packagePid)}</div>`));
|
|
2429
|
+
}
|
|
2430
2430
|
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2431
|
+
// Get resource details
|
|
2432
|
+
const resourceQuery = `
|
|
2433
|
+
SELECT * FROM Resources
|
|
2434
|
+
WHERE PackageKey = ? AND ResourceType = ? AND Id = ?
|
|
2435
|
+
`;
|
|
2436
2436
|
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2437
|
+
const resourceData = await new Promise((resolve, reject) => {
|
|
2438
|
+
xigDb.get(resourceQuery, [packageObj.PackageKey, resourceType, resourceId], (err, row) => {
|
|
2439
|
+
if (err) reject(err);
|
|
2440
|
+
else resolve(row);
|
|
2441
|
+
});
|
|
2441
2442
|
});
|
|
2442
|
-
});
|
|
2443
2443
|
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2444
|
+
if (!resourceData) {
|
|
2445
|
+
return res.status(404).send(renderPage('Resource Not Found',
|
|
2446
|
+
`<div class="alert alert-danger">Unknown Resource: ${escape(resourceType)}/${escape(resourceId)} in package ${escape(packagePid)}</div>`));
|
|
2447
|
+
}
|
|
2448
2448
|
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2449
|
+
// Build the resource detail page
|
|
2450
|
+
const content = await buildResourceDetailPage(packageObj, resourceData, req.secure);
|
|
2451
|
+
const title = `${resourceType}/${resourceId}`;
|
|
2452
|
+
const stats = await gatherPageStatistics();
|
|
2453
|
+
stats.processingTime = Date.now() - startTime;
|
|
2454
2454
|
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2455
|
+
const html = renderPage(title, content, stats);
|
|
2456
|
+
res.setHeader('Content-Type', 'text/html');
|
|
2457
|
+
res.send(html);
|
|
2458
2458
|
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
xigLog.error(`Error rendering resource detail page: ${error.message}`);
|
|
2461
|
+
htmlServer.sendErrorResponse(res, 'xig', error);
|
|
2462
|
+
}
|
|
2463
2463
|
} finally {
|
|
2464
2464
|
globalStats.countRequest(':pid', Date.now() - start);
|
|
2465
2465
|
}
|
|
@@ -2874,27 +2874,27 @@ router.get('/status', async (req, res) => {
|
|
|
2874
2874
|
const start = Date.now();
|
|
2875
2875
|
try {
|
|
2876
2876
|
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2877
|
+
try {
|
|
2878
|
+
const dbInfo = await getDatabaseInfo();
|
|
2879
|
+
await res.json({
|
|
2880
|
+
status: 'OK',
|
|
2881
|
+
database: dbInfo,
|
|
2882
|
+
databaseAge: getDatabaseAgeInfo(),
|
|
2883
|
+
downloadUrl: XIG_DB_URL,
|
|
2884
|
+
localPath: XIG_DB_PATH,
|
|
2885
|
+
cache: getCacheStats(),
|
|
2886
|
+
updateInProgress: updateInProgress,
|
|
2887
|
+
lastUpdateAttempt: getLastUpdateAttempt(),
|
|
2888
|
+
updateHistory: getUpdateHistory()
|
|
2889
|
+
});
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
res.status(500).json({
|
|
2892
|
+
status: 'ERROR',
|
|
2893
|
+
error: error.message,
|
|
2894
|
+
cache: getCacheStats(),
|
|
2895
|
+
updateHistory: getUpdateHistory()
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
2898
|
} finally {
|
|
2899
2899
|
globalStats.countRequest('stats', Date.now() - start);
|
|
2900
2900
|
}
|
|
@@ -2929,7 +2929,7 @@ router.post('/update', async (req, res) => {
|
|
|
2929
2929
|
|
|
2930
2930
|
let globalStats;
|
|
2931
2931
|
// Initialize the XIG module
|
|
2932
|
-
async function initializeXigModule(stats) {
|
|
2932
|
+
async function initializeXigModule(stats, xigConfig) {
|
|
2933
2933
|
try {
|
|
2934
2934
|
globalStats = stats;
|
|
2935
2935
|
loadTemplate();
|
|
@@ -2944,13 +2944,15 @@ async function initializeXigModule(stats) {
|
|
|
2944
2944
|
}
|
|
2945
2945
|
|
|
2946
2946
|
if (globalStats) {
|
|
2947
|
-
globalStats.addTask('XIG Download', describeCron(
|
|
2947
|
+
globalStats.addTask('XIG Download', describeCron('0 2 * * *'));
|
|
2948
2948
|
}
|
|
2949
2949
|
// Check if auto-update is enabled
|
|
2950
2950
|
// Note: This assumes we're called only when XIG is enabled
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2951
|
+
if (xigConfig?.autoUpdate !== false) {
|
|
2952
|
+
cron.schedule('0 2 * * *', () => {
|
|
2953
|
+
updateXigDatabase();
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2954
2956
|
|
|
2955
2957
|
} catch (error) {
|
|
2956
2958
|
xigLog.error(`XIG module initialization failed: ${error.message}`);
|