fhirsmith 0.18.1 → 1.0.0
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/ACCESSIBILITY.md +119 -0
- package/CHANGELOG.md +190 -9
- package/CONFIGURATION.md +305 -0
- package/LICENSE +29 -0
- package/README.md +29 -122
- package/cluster.js +1220 -0
- package/cluster.md +707 -0
- package/config-template.json +77 -8
- package/extension-tracker/extension-tracker-template.html +5 -8
- package/extension-tracker/extension-tracker.js +0 -2
- package/folder/folder.js +7 -6
- package/ips/analysis.js +560 -0
- package/ips/ips-template.html +5 -8
- package/ips/ips.js +331 -33
- package/ips/jurisdictions.js +65 -0
- package/ips/nationality.js +120 -0
- package/ips/pipeline.js +131 -17
- package/ips/presenter.js +1 -1
- package/ips/profile-editor.js +283 -4
- package/ips/readme.md +170 -20
- package/ips/render.js +769 -109
- package/ips/scripts.js +46 -0
- package/ips/store.js +123 -11
- package/ips/structure.js +247 -0
- package/ips/terminology.js +1 -1
- package/library/body-limit.js +22 -0
- package/library/cache-generation.js +15 -0
- package/library/cgroup-memory.js +133 -1
- package/library/cluster-counts.js +121 -0
- package/library/cluster-events.js +153 -0
- package/library/cluster-metrics.js +193 -0
- package/library/cluster-status.js +468 -0
- package/library/config-reader.js +96 -0
- package/library/daily-report.js +100 -2
- package/library/html-server.js +27 -1
- package/library/i18nsupport.js +23 -1
- package/library/keep-alive.js +18 -0
- package/library/languages.js +17 -3
- package/library/logger.js +31 -0
- package/library/memory-guard.js +428 -0
- package/library/native-memory.js +52 -0
- package/library/npm-audit.js +51 -25
- package/library/openapi-doc.js +7 -5
- package/library/package-manager.js +8 -0
- package/library/role.js +197 -0
- package/library/server-credentials.js +169 -0
- package/library/sqlite-utilities.js +59 -0
- package/library/ssrf-guard.js +100 -0
- package/library/static-response-stats.js +16 -0
- package/library/version-utilities.js +50 -0
- package/library/worker-count.js +192 -0
- package/nginx.md +30 -11
- package/npmprojector/indexer.js +18 -10
- package/package.json +11 -8
- package/packages/feed-check-page.js +117 -0
- package/packages/feed-check.js +279 -0
- package/packages/npm-check.js +57 -3
- package/packages/npm-signing.js +219 -0
- package/packages/openapi.yaml +207 -1
- package/packages/package-crawler.js +322 -41
- package/packages/packages-template.html +6 -8
- package/packages/packages.js +604 -57
- package/packages/readme.md +113 -1
- package/publisher/publisher-template.html +2 -5
- package/publisher/publisher.js +121 -110
- package/publisher/readme.md +75 -0
- package/registry/api.js +179 -1
- package/registry/crawler.js +86 -108
- package/registry/documentation/api.md +68 -2
- package/registry/documentation/configuration.md +34 -0
- package/registry/documentation/internals.md +4 -1
- package/registry/documentation/readme.md +3 -2
- package/registry/model.js +8 -0
- package/registry/openapi.yaml +84 -0
- package/registry/registry-template.html +5 -8
- package/registry/registry.js +58 -6
- package/root-bare-template.html +7 -4
- package/root-template.html +5 -8
- package/server.js +398 -198
- package/shl/shl.js +38 -37
- package/static/a11y/a11y-check.js +832 -0
- package/static/a11y/axe.min.js +12 -0
- package/static/assets/css/bootstrap-fhir.css +15 -15
- package/static/assets/images/flags/au.svg +18 -0
- package/static/assets/images/flags/unknown.svg +11 -0
- package/static/assets/images/flags/us.svg +63 -0
- package/static/assets/images/icon_audit.png +0 -0
- package/static/assets/images/icon_clinical.png +0 -0
- package/static/assets/images/icon_document.png +0 -0
- package/static/assets/images/icon_external.png +0 -0
- package/static/assets/images/icon_medication.png +0 -0
- package/static/assets/images/icon_organization.png +0 -0
- package/static/assets/images/icon_other.png +0 -0
- package/static/assets/images/icon_person.png +0 -0
- package/static/assets/images/icon_section.png +0 -0
- package/stats-db.js +45 -0
- package/stats.js +197 -10
- package/testing/testing-template.html +5 -8
- package/token/nginx_passport_setup.md +1 -1
- package/token/security_guide.md +1 -1
- package/token/token-template.html +2 -5
- package/token/token.js +40 -36
- package/translations/Messages.properties +19 -0
- package/tx/README.md +158 -4
- package/tx/closure/closure-store.js +126 -3
- package/tx/cm/cm-database.js +69 -57
- package/tx/cm/cm-package.js +50 -16
- package/tx/cs/cs-api.js +284 -19
- package/tx/cs/cs-api.md +41 -4
- package/tx/cs/cs-areacode.js +1 -1
- package/tx/cs/cs-country.js +17 -5
- package/tx/cs/cs-cpt.js +33 -16
- package/tx/cs/cs-cs.js +199 -24
- package/tx/cs/cs-currency.js +1 -1
- package/tx/cs/cs-icd11.js +21 -13
- package/tx/cs/cs-icf.js +22 -3
- package/tx/cs/cs-lang.js +2 -2
- package/tx/cs/cs-loinc.js +193 -67
- package/tx/cs/cs-mimetypes.js +12 -2
- package/tx/cs/cs-ndc.js +40 -25
- package/tx/cs/cs-omop.js +54 -29
- package/tx/cs/cs-rxnorm.js +300 -196
- package/tx/cs/cs-snomed.js +861 -95
- package/tx/cs/cs-ucum.js +14 -4
- package/tx/cs/cs-unii.js +18 -14
- package/tx/cs/cs-virtual.js +210 -0
- package/tx/ecosystem-resolver.js +413 -0
- package/tx/html/cluster-dash-metrics.liquid +26 -0
- package/tx/html/cluster-home-metrics.liquid +31 -0
- package/tx/html/cluster-metrics-script.liquid +251 -0
- package/tx/html/codesystem-operations.liquid +3 -3
- package/tx/html/ecl-panel.liquid +1 -1
- package/tx/html/external-metrics.liquid +17 -0
- package/tx/html/operations-form.liquid +17 -17
- package/tx/html/search-form.liquid +11 -11
- package/tx/html/status-line.liquid +58 -0
- package/tx/html/status-panel.liquid +140 -0
- package/tx/html/status-tasks.liquid +25 -0
- package/tx/html/tx-template.html +2 -5
- package/tx/importers/atc-to-fhir.js +17 -16
- package/tx/importers/export-icd11-codesystem.js +3 -2
- package/tx/importers/import-icd11.js +4 -3
- package/tx/importers/import-loinc.module.js +118 -56
- package/tx/importers/import-ndc.module.js +56 -44
- package/tx/importers/import-rxnorm.module.js +118 -79
- package/tx/importers/import-sct.module.js +125 -61
- package/tx/importers/import-unii.module.js +37 -36
- package/tx/importers/subset-loinc.module.js +49 -48
- package/tx/importers/subset-rxnorm.module.js +29 -28
- package/tx/importers/terminal.js +18 -0
- package/tx/importers/tx-import-base.js +13 -12
- package/tx/importers/tx-import-settings.js +5 -4
- package/tx/importers/tx-import.js +53 -52
- package/tx/library/canonical-resource.js +49 -0
- package/tx/library/codesystem.js +94 -5
- package/tx/library/designations.js +45 -2
- package/tx/library/extensions.js +13 -7
- package/tx/library/implicit-valuesets.js +66 -0
- package/tx/library/renderer.js +77 -21
- package/tx/library/sqlite-utilities.js +2 -0
- package/tx/library/tho-precedence.js +40 -0
- package/tx/library/ucum-parsers.js +11 -1
- package/tx/library/wait-for-cache.js +43 -0
- package/tx/library.js +321 -22
- package/tx/openapi-operations.js +6 -4
- package/tx/operation-context.js +460 -99
- package/tx/params.js +25 -0
- package/tx/problems.js +3 -5
- package/tx/provider.js +314 -67
- package/tx/resource-cache-sqlite.js +1050 -0
- package/tx/sct/documentation.md +45 -21
- package/tx/sct/ecl.js +81 -17
- package/tx/sct/editions.js +39 -0
- package/tx/sct/expressions.js +10 -1
- package/tx/sct/load-config.js +107 -0
- package/tx/sct/mmap.js +109 -0
- package/tx/sct/structures.js +175 -15
- package/tx/sct/text-index.js +5 -1
- package/tx/syndication/syndication-feed.js +2 -2
- package/tx/tx-html.js +44 -8
- package/tx/tx.fhir.org.yml +8 -8
- package/tx/tx.js +282 -80
- package/tx/usage-tracker.js +39 -11
- package/tx/vs/vs-api.js +19 -0
- package/tx/vs/vs-database.js +386 -62
- package/tx/vs/vs-package.js +44 -17
- package/tx/vs/vs-vsac.js +649 -101
- package/tx/workers/batch-validate.js +37 -10
- package/tx/workers/batch.js +6 -1
- package/tx/workers/cache-control.js +15 -4
- package/tx/workers/closure.js +156 -13
- package/tx/workers/compare.js +8 -5
- package/tx/workers/expand.js +1173 -289
- package/tx/workers/metadata.js +35 -0
- package/tx/workers/search.js +53 -1
- package/tx/workers/supplement-filters.js +145 -0
- package/tx/workers/translate.js +169 -4
- package/tx/workers/validate.js +223 -119
- package/tx/workers/worker.js +123 -31
- package/tx/xversion/xv-valueset.js +7 -4
- package/utilities/submit-test-report.js +3 -1
- package/utilities/tx-stress-test.js +482 -0
- package/vcl/readme.md +37 -0
- package/vcl/vcl-parser.js +15 -0
- package/xig/readme.md +52 -0
- package/xig/xig-template.html +5 -8
- package/xig/xig.js +238 -422
- package/changelog-template.md +0 -22
- package/registry/test-runner.js +0 -237
- package/release.sh +0 -80
- package/test-scripts/repro-re2-wasm-leak.js +0 -34
- package/tsconfig.eslint.json +0 -9
- package/tx/html/dash-metrics.liquid +0 -147
- package/tx/html/home-metrics.liquid +0 -225
|
@@ -295,8 +295,8 @@ textarea {
|
|
|
295
295
|
line-height: inherit;
|
|
296
296
|
}
|
|
297
297
|
a {
|
|
298
|
-
color: #
|
|
299
|
-
text-decoration:
|
|
298
|
+
color: #1f4465;
|
|
299
|
+
text-decoration: underline;
|
|
300
300
|
}
|
|
301
301
|
a:hover,
|
|
302
302
|
a:focus {
|
|
@@ -349,19 +349,19 @@ cite {
|
|
|
349
349
|
font-style: normal;
|
|
350
350
|
}
|
|
351
351
|
.text-muted {
|
|
352
|
-
color: #
|
|
352
|
+
color: #727272;
|
|
353
353
|
}
|
|
354
354
|
.text-primary {
|
|
355
|
-
color: #
|
|
355
|
+
color: #2c6597;
|
|
356
356
|
}
|
|
357
357
|
.text-warning {
|
|
358
|
-
color: #
|
|
358
|
+
color: #92681e;
|
|
359
359
|
}
|
|
360
360
|
.text-danger {
|
|
361
361
|
color: #b94a48;
|
|
362
362
|
}
|
|
363
363
|
.text-success {
|
|
364
|
-
color: #
|
|
364
|
+
color: #356735;
|
|
365
365
|
}
|
|
366
366
|
.text-info {
|
|
367
367
|
color: #3a87ad;
|
|
@@ -1463,7 +1463,7 @@ fieldset[disabled] .btn-default.active {
|
|
|
1463
1463
|
}
|
|
1464
1464
|
.btn-primary {
|
|
1465
1465
|
color: #ffffff;
|
|
1466
|
-
background-color: #
|
|
1466
|
+
background-color: #2c618f;
|
|
1467
1467
|
border-color: #428bca;
|
|
1468
1468
|
}
|
|
1469
1469
|
.btn-primary:hover,
|
|
@@ -2853,7 +2853,7 @@ button.close {
|
|
|
2853
2853
|
}
|
|
2854
2854
|
.pagination > .active > a,
|
|
2855
2855
|
.pagination > .active > span {
|
|
2856
|
-
color: #
|
|
2856
|
+
color: #565656;
|
|
2857
2857
|
cursor: default;
|
|
2858
2858
|
}
|
|
2859
2859
|
.pagination > .disabled > span,
|
|
@@ -3310,7 +3310,7 @@ button.close {
|
|
|
3310
3310
|
.alert {
|
|
3311
3311
|
padding: 10px 35px 10px 15px;
|
|
3312
3312
|
margin-bottom: 20px;
|
|
3313
|
-
color: #
|
|
3313
|
+
color: #872929;
|
|
3314
3314
|
background-color: #fcf8e3;
|
|
3315
3315
|
border: 1px solid #fbeed5;
|
|
3316
3316
|
border-radius: 4px;
|
|
@@ -3344,9 +3344,9 @@ button.close {
|
|
|
3344
3344
|
color: #356635;
|
|
3345
3345
|
}
|
|
3346
3346
|
.alert-danger {
|
|
3347
|
-
background-color: #
|
|
3347
|
+
background-color: #fcebeb;
|
|
3348
3348
|
border-color: #eed3d7;
|
|
3349
|
-
color: #
|
|
3349
|
+
color: #6c2322;
|
|
3350
3350
|
}
|
|
3351
3351
|
.alert-danger hr {
|
|
3352
3352
|
border-top-color: #e6c1c7;
|
|
@@ -3355,9 +3355,9 @@ button.close {
|
|
|
3355
3355
|
color: #953b39;
|
|
3356
3356
|
}
|
|
3357
3357
|
.alert-info {
|
|
3358
|
-
background-color: #
|
|
3358
|
+
background-color: #fff7e6;
|
|
3359
3359
|
border-color: #bce8f1;
|
|
3360
|
-
color: #
|
|
3360
|
+
color: #2a6785;
|
|
3361
3361
|
}
|
|
3362
3362
|
.alert-info hr {
|
|
3363
3363
|
border-top-color: #a6e1ec;
|
|
@@ -3445,7 +3445,7 @@ a.thumbnail:focus {
|
|
|
3445
3445
|
text-align: center;
|
|
3446
3446
|
white-space: nowrap;
|
|
3447
3447
|
vertical-align: baseline;
|
|
3448
|
-
background-color: #
|
|
3448
|
+
background-color: #6f6f6f;
|
|
3449
3449
|
border-radius: .25em;
|
|
3450
3450
|
}
|
|
3451
3451
|
.label[href]:hover,
|
|
@@ -3494,7 +3494,7 @@ a.thumbnail:focus {
|
|
|
3494
3494
|
vertical-align: baseline;
|
|
3495
3495
|
white-space: nowrap;
|
|
3496
3496
|
text-align: center;
|
|
3497
|
-
background-color: #
|
|
3497
|
+
background-color: #6d6d6d;
|
|
3498
3498
|
border-radius: 10px;
|
|
3499
3499
|
}
|
|
3500
3500
|
.badge:empty {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10080 5040" width="10080" height="5040">
|
|
2
|
+
<title>Australia</title>
|
|
3
|
+
<defs><clipPath id="s"><path d="M0,0 v30 h60 v-30 z"/></clipPath><clipPath id="t"><path d="M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z"/></clipPath></defs>
|
|
4
|
+
<rect width="10080" height="5040" fill="#012169"/>
|
|
5
|
+
<svg width="5040" height="2520" viewBox="0 0 60 30"><g clip-path="url(#s)">
|
|
6
|
+
<path d="M0,0 L60,30 M60,0 L0,30" stroke="#fff" stroke-width="6"/>
|
|
7
|
+
<path d="M0,0 L60,30 M60,0 L0,30" clip-path="url(#t)" stroke="#C8102E" stroke-width="4"/>
|
|
8
|
+
<path d="M30,0 v30 M0,15 h60" stroke="#fff" stroke-width="10"/>
|
|
9
|
+
<path d="M30,0 v30 M0,15 h60" stroke="#C8102E" stroke-width="6"/>
|
|
10
|
+
</g></svg>
|
|
11
|
+
<g fill="#fff">
|
|
12
|
+
<polygon points="2520.0,2700.0 2728.3,3347.5 3364.4,3106.6 2988.0,3673.2 3572.9,4020.3 2895.3,4079.3 2988.6,4753.0 2520.0,4260.0 2051.4,4753.0 2144.7,4079.3 1467.1,4020.3 2052.0,3673.2 1675.6,3106.6 2311.7,3347.5"/>
|
|
13
|
+
<polygon points="7560.0,3720.0 7652.6,4007.8 7935.3,3900.7 7768.0,4152.5 8028.0,4306.8 7726.8,4333.0 7768.3,4632.5 7560.0,4413.3 7351.7,4632.5 7393.2,4333.0 7092.0,4306.8 7352.0,4152.5 7184.7,3900.7 7467.4,4007.8"/>
|
|
14
|
+
<polygon points="6300.0,1725.0 6392.6,2012.8 6675.3,1905.7 6508.0,2157.5 6768.0,2311.8 6466.8,2338.0 6508.3,2637.5 6300.0,2418.3 6091.7,2637.5 6133.2,2338.0 5832.0,2311.8 6092.0,2157.5 5924.7,1905.7 6207.4,2012.8"/>
|
|
15
|
+
<polygon points="7560.0,360.0 7652.6,647.8 7935.3,540.7 7768.0,792.5 8028.0,946.8 7726.8,973.0 7768.3,1272.5 7560.0,1053.3 7351.7,1272.5 7393.2,973.0 7092.0,946.8 7352.0,792.5 7184.7,540.7 7467.4,647.8"/>
|
|
16
|
+
<polygon points="8680.0,1389.0 8772.6,1676.8 9055.3,1569.7 8888.0,1821.5 9148.0,1975.8 8846.8,2002.0 8888.3,2301.5 8680.0,2082.3 8471.7,2301.5 8513.2,2002.0 8212.0,1975.8 8472.0,1821.5 8304.7,1569.7 8587.4,1676.8"/>
|
|
17
|
+
<polygon points="8064.0,2450.0 8129.8,2639.4 8330.3,2643.5 8170.5,2764.6 8228.6,2956.5 8064.0,2842.0 7899.4,2956.5 7957.5,2764.6 7797.7,2643.5 7998.2,2639.4"/>
|
|
18
|
+
</g></svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
|
|
2
|
+
<title>Nationality unknown</title>
|
|
3
|
+
<g fill="none" stroke="#000" stroke-width="2.5">
|
|
4
|
+
<circle cx="32" cy="32" r="28"/>
|
|
5
|
+
<ellipse cx="32" cy="32" rx="12" ry="28"/>
|
|
6
|
+
<line x1="32" y1="4" x2="32" y2="60"/>
|
|
7
|
+
<line x1="4" y1="32" x2="60" y2="32"/>
|
|
8
|
+
<path d="M8.5 18 Q32 24 55.5 18"/>
|
|
9
|
+
<path d="M8.5 46 Q32 40 55.5 46"/>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 247 130" width="247" height="130">
|
|
2
|
+
<title>United States</title>
|
|
3
|
+
<rect width="247" height="130" fill="#fff"/>
|
|
4
|
+
<rect y="0.00" width="247" height="10.00" fill="#B22234"/>
|
|
5
|
+
<rect y="20.00" width="247" height="10.00" fill="#B22234"/>
|
|
6
|
+
<rect y="40.00" width="247" height="10.00" fill="#B22234"/>
|
|
7
|
+
<rect y="60.00" width="247" height="10.00" fill="#B22234"/>
|
|
8
|
+
<rect y="80.00" width="247" height="10.00" fill="#B22234"/>
|
|
9
|
+
<rect y="100.00" width="247" height="10.00" fill="#B22234"/>
|
|
10
|
+
<rect y="120.00" width="247" height="10.00" fill="#B22234"/>
|
|
11
|
+
<rect width="98.80" height="70.00" fill="#3C3B6E"/>
|
|
12
|
+
<g fill="#fff">
|
|
13
|
+
<polygon points="8.2,2.8 9.2,5.7 12.2,5.7 9.8,7.5 10.7,10.4 8.2,8.6 5.8,10.4 6.7,7.5 4.2,5.7 7.3,5.7"/>
|
|
14
|
+
<polygon points="24.7,2.8 25.6,5.7 28.7,5.7 26.2,7.5 27.2,10.4 24.7,8.6 22.2,10.4 23.2,7.5 20.7,5.7 23.8,5.7"/>
|
|
15
|
+
<polygon points="41.2,2.8 42.1,5.7 45.2,5.7 42.7,7.5 43.6,10.4 41.2,8.6 38.7,10.4 39.6,7.5 37.2,5.7 40.2,5.7"/>
|
|
16
|
+
<polygon points="57.6,2.8 58.6,5.7 61.6,5.7 59.2,7.5 60.1,10.4 57.6,8.6 55.2,10.4 56.1,7.5 53.6,5.7 56.7,5.7"/>
|
|
17
|
+
<polygon points="74.1,2.8 75.0,5.7 78.1,5.7 75.6,7.5 76.6,10.4 74.1,8.6 71.6,10.4 72.6,7.5 70.1,5.7 73.2,5.7"/>
|
|
18
|
+
<polygon points="90.6,2.8 91.5,5.7 94.6,5.7 92.1,7.5 93.0,10.4 90.6,8.6 88.1,10.4 89.0,7.5 86.6,5.7 89.6,5.7"/>
|
|
19
|
+
<polygon points="16.5,9.8 17.4,12.7 20.5,12.7 18.0,14.5 18.9,17.4 16.5,15.6 14.0,17.4 14.9,14.5 12.5,12.7 15.5,12.7"/>
|
|
20
|
+
<polygon points="32.9,9.8 33.9,12.7 36.9,12.7 34.5,14.5 35.4,17.4 32.9,15.6 30.5,17.4 31.4,14.5 28.9,12.7 32.0,12.7"/>
|
|
21
|
+
<polygon points="49.4,9.8 50.3,12.7 53.4,12.7 50.9,14.5 51.9,17.4 49.4,15.6 46.9,17.4 47.9,14.5 45.4,12.7 48.5,12.7"/>
|
|
22
|
+
<polygon points="65.9,9.8 66.8,12.7 69.9,12.7 67.4,14.5 68.3,17.4 65.9,15.6 63.4,17.4 64.3,14.5 61.9,12.7 64.9,12.7"/>
|
|
23
|
+
<polygon points="82.3,9.8 83.3,12.7 86.3,12.7 83.9,14.5 84.8,17.4 82.3,15.6 79.9,17.4 80.8,14.5 78.3,12.7 81.4,12.7"/>
|
|
24
|
+
<polygon points="8.2,16.8 9.2,19.7 12.2,19.7 9.8,21.5 10.7,24.4 8.2,22.6 5.8,24.4 6.7,21.5 4.2,19.7 7.3,19.7"/>
|
|
25
|
+
<polygon points="24.7,16.8 25.6,19.7 28.7,19.7 26.2,21.5 27.2,24.4 24.7,22.6 22.2,24.4 23.2,21.5 20.7,19.7 23.8,19.7"/>
|
|
26
|
+
<polygon points="41.2,16.8 42.1,19.7 45.2,19.7 42.7,21.5 43.6,24.4 41.2,22.6 38.7,24.4 39.6,21.5 37.2,19.7 40.2,19.7"/>
|
|
27
|
+
<polygon points="57.6,16.8 58.6,19.7 61.6,19.7 59.2,21.5 60.1,24.4 57.6,22.6 55.2,24.4 56.1,21.5 53.6,19.7 56.7,19.7"/>
|
|
28
|
+
<polygon points="74.1,16.8 75.0,19.7 78.1,19.7 75.6,21.5 76.6,24.4 74.1,22.6 71.6,24.4 72.6,21.5 70.1,19.7 73.2,19.7"/>
|
|
29
|
+
<polygon points="90.6,16.8 91.5,19.7 94.6,19.7 92.1,21.5 93.0,24.4 90.6,22.6 88.1,24.4 89.0,21.5 86.6,19.7 89.6,19.7"/>
|
|
30
|
+
<polygon points="16.5,23.8 17.4,26.7 20.5,26.7 18.0,28.5 18.9,31.4 16.5,29.6 14.0,31.4 14.9,28.5 12.5,26.7 15.5,26.7"/>
|
|
31
|
+
<polygon points="32.9,23.8 33.9,26.7 36.9,26.7 34.5,28.5 35.4,31.4 32.9,29.6 30.5,31.4 31.4,28.5 28.9,26.7 32.0,26.7"/>
|
|
32
|
+
<polygon points="49.4,23.8 50.3,26.7 53.4,26.7 50.9,28.5 51.9,31.4 49.4,29.6 46.9,31.4 47.9,28.5 45.4,26.7 48.5,26.7"/>
|
|
33
|
+
<polygon points="65.9,23.8 66.8,26.7 69.9,26.7 67.4,28.5 68.3,31.4 65.9,29.6 63.4,31.4 64.3,28.5 61.9,26.7 64.9,26.7"/>
|
|
34
|
+
<polygon points="82.3,23.8 83.3,26.7 86.3,26.7 83.9,28.5 84.8,31.4 82.3,29.6 79.9,31.4 80.8,28.5 78.3,26.7 81.4,26.7"/>
|
|
35
|
+
<polygon points="8.2,30.8 9.2,33.7 12.2,33.7 9.8,35.5 10.7,38.4 8.2,36.6 5.8,38.4 6.7,35.5 4.2,33.7 7.3,33.7"/>
|
|
36
|
+
<polygon points="24.7,30.8 25.6,33.7 28.7,33.7 26.2,35.5 27.2,38.4 24.7,36.6 22.2,38.4 23.2,35.5 20.7,33.7 23.8,33.7"/>
|
|
37
|
+
<polygon points="41.2,30.8 42.1,33.7 45.2,33.7 42.7,35.5 43.6,38.4 41.2,36.6 38.7,38.4 39.6,35.5 37.2,33.7 40.2,33.7"/>
|
|
38
|
+
<polygon points="57.6,30.8 58.6,33.7 61.6,33.7 59.2,35.5 60.1,38.4 57.6,36.6 55.2,38.4 56.1,35.5 53.6,33.7 56.7,33.7"/>
|
|
39
|
+
<polygon points="74.1,30.8 75.0,33.7 78.1,33.7 75.6,35.5 76.6,38.4 74.1,36.6 71.6,38.4 72.6,35.5 70.1,33.7 73.2,33.7"/>
|
|
40
|
+
<polygon points="90.6,30.8 91.5,33.7 94.6,33.7 92.1,35.5 93.0,38.4 90.6,36.6 88.1,38.4 89.0,35.5 86.6,33.7 89.6,33.7"/>
|
|
41
|
+
<polygon points="16.5,37.8 17.4,40.7 20.5,40.7 18.0,42.5 18.9,45.4 16.5,43.6 14.0,45.4 14.9,42.5 12.5,40.7 15.5,40.7"/>
|
|
42
|
+
<polygon points="32.9,37.8 33.9,40.7 36.9,40.7 34.5,42.5 35.4,45.4 32.9,43.6 30.5,45.4 31.4,42.5 28.9,40.7 32.0,40.7"/>
|
|
43
|
+
<polygon points="49.4,37.8 50.3,40.7 53.4,40.7 50.9,42.5 51.9,45.4 49.4,43.6 46.9,45.4 47.9,42.5 45.4,40.7 48.5,40.7"/>
|
|
44
|
+
<polygon points="65.9,37.8 66.8,40.7 69.9,40.7 67.4,42.5 68.3,45.4 65.9,43.6 63.4,45.4 64.3,42.5 61.9,40.7 64.9,40.7"/>
|
|
45
|
+
<polygon points="82.3,37.8 83.3,40.7 86.3,40.7 83.9,42.5 84.8,45.4 82.3,43.6 79.9,45.4 80.8,42.5 78.3,40.7 81.4,40.7"/>
|
|
46
|
+
<polygon points="8.2,44.8 9.2,47.7 12.2,47.7 9.8,49.5 10.7,52.4 8.2,50.6 5.8,52.4 6.7,49.5 4.2,47.7 7.3,47.7"/>
|
|
47
|
+
<polygon points="24.7,44.8 25.6,47.7 28.7,47.7 26.2,49.5 27.2,52.4 24.7,50.6 22.2,52.4 23.2,49.5 20.7,47.7 23.8,47.7"/>
|
|
48
|
+
<polygon points="41.2,44.8 42.1,47.7 45.2,47.7 42.7,49.5 43.6,52.4 41.2,50.6 38.7,52.4 39.6,49.5 37.2,47.7 40.2,47.7"/>
|
|
49
|
+
<polygon points="57.6,44.8 58.6,47.7 61.6,47.7 59.2,49.5 60.1,52.4 57.6,50.6 55.2,52.4 56.1,49.5 53.6,47.7 56.7,47.7"/>
|
|
50
|
+
<polygon points="74.1,44.8 75.0,47.7 78.1,47.7 75.6,49.5 76.6,52.4 74.1,50.6 71.6,52.4 72.6,49.5 70.1,47.7 73.2,47.7"/>
|
|
51
|
+
<polygon points="90.6,44.8 91.5,47.7 94.6,47.7 92.1,49.5 93.0,52.4 90.6,50.6 88.1,52.4 89.0,49.5 86.6,47.7 89.6,47.7"/>
|
|
52
|
+
<polygon points="16.5,51.8 17.4,54.7 20.5,54.7 18.0,56.5 18.9,59.4 16.5,57.6 14.0,59.4 14.9,56.5 12.5,54.7 15.5,54.7"/>
|
|
53
|
+
<polygon points="32.9,51.8 33.9,54.7 36.9,54.7 34.5,56.5 35.4,59.4 32.9,57.6 30.5,59.4 31.4,56.5 28.9,54.7 32.0,54.7"/>
|
|
54
|
+
<polygon points="49.4,51.8 50.3,54.7 53.4,54.7 50.9,56.5 51.9,59.4 49.4,57.6 46.9,59.4 47.9,56.5 45.4,54.7 48.5,54.7"/>
|
|
55
|
+
<polygon points="65.9,51.8 66.8,54.7 69.9,54.7 67.4,56.5 68.3,59.4 65.9,57.6 63.4,59.4 64.3,56.5 61.9,54.7 64.9,54.7"/>
|
|
56
|
+
<polygon points="82.3,51.8 83.3,54.7 86.3,54.7 83.9,56.5 84.8,59.4 82.3,57.6 79.9,59.4 80.8,56.5 78.3,54.7 81.4,54.7"/>
|
|
57
|
+
<polygon points="8.2,58.8 9.2,61.7 12.2,61.7 9.8,63.5 10.7,66.4 8.2,64.6 5.8,66.4 6.7,63.5 4.2,61.7 7.3,61.7"/>
|
|
58
|
+
<polygon points="24.7,58.8 25.6,61.7 28.7,61.7 26.2,63.5 27.2,66.4 24.7,64.6 22.2,66.4 23.2,63.5 20.7,61.7 23.8,61.7"/>
|
|
59
|
+
<polygon points="41.2,58.8 42.1,61.7 45.2,61.7 42.7,63.5 43.6,66.4 41.2,64.6 38.7,66.4 39.6,63.5 37.2,61.7 40.2,61.7"/>
|
|
60
|
+
<polygon points="57.6,58.8 58.6,61.7 61.6,61.7 59.2,63.5 60.1,66.4 57.6,64.6 55.2,66.4 56.1,63.5 53.6,61.7 56.7,61.7"/>
|
|
61
|
+
<polygon points="74.1,58.8 75.0,61.7 78.1,61.7 75.6,63.5 76.6,66.4 74.1,64.6 71.6,66.4 72.6,63.5 70.1,61.7 73.2,61.7"/>
|
|
62
|
+
<polygon points="90.6,58.8 91.5,61.7 94.6,61.7 92.1,63.5 93.0,66.4 90.6,64.6 88.1,66.4 89.0,63.5 86.6,61.7 89.6,61.7"/>
|
|
63
|
+
</g></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/stats-db.js
CHANGED
|
@@ -93,6 +93,29 @@ class StatsDatabase {
|
|
|
93
93
|
}
|
|
94
94
|
this.touchRunStatement = this.db.prepare(`UPDATE server_runs SET last_seen = ? WHERE id = ?`);
|
|
95
95
|
|
|
96
|
+
// Under the launcher (cluster.js): what it saw - processes exiting, being recycled and
|
|
97
|
+
// coming back, and what it answered itself. See library/cluster-events.js
|
|
98
|
+
this.db.exec(`
|
|
99
|
+
CREATE TABLE IF NOT EXISTS cluster_events (
|
|
100
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
101
|
+
time INTEGER NOT NULL,
|
|
102
|
+
kind TEXT NOT NULL,
|
|
103
|
+
process TEXT,
|
|
104
|
+
detail TEXT,
|
|
105
|
+
count INTEGER NOT NULL,
|
|
106
|
+
ms INTEGER NOT NULL
|
|
107
|
+
)
|
|
108
|
+
`);
|
|
109
|
+
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_cluster_events_time ON cluster_events(time)`);
|
|
110
|
+
this.insertClusterEvent = this.db.prepare(`
|
|
111
|
+
INSERT INTO cluster_events (time, kind, process, detail, count, ms) VALUES (?, ?, ?, ?, ?, ?)
|
|
112
|
+
`);
|
|
113
|
+
this.insertClusterEvents = this.db.transaction((rows) => {
|
|
114
|
+
for (const row of rows) {
|
|
115
|
+
this.insertClusterEvent.run(row.time, row.kind, row.process, row.detail, row.count, row.ms);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
96
119
|
this.insertStatement = this.db.prepare(`
|
|
97
120
|
INSERT INTO request_counts
|
|
98
121
|
(time, interval_ms, module, endpoint, operation, count_delta, count_total, time_delta, time_total)
|
|
@@ -199,6 +222,28 @@ class StatsDatabase {
|
|
|
199
222
|
`).all(module, since, now);
|
|
200
223
|
}
|
|
201
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Record what the launcher saw (rows as library/cluster-events.js validRow gives them).
|
|
227
|
+
*/
|
|
228
|
+
writeClusterEvents(rows) {
|
|
229
|
+
if (this.db && rows && rows.length > 0) {
|
|
230
|
+
this.insertClusterEvents(rows);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* What the launcher saw in (since, now], oldest first: [{time, kind, process, detail, count, ms}].
|
|
236
|
+
*/
|
|
237
|
+
clusterEventsSince(since, now) {
|
|
238
|
+
if (!this.db) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
return this.db.prepare(`
|
|
242
|
+
SELECT time, kind, process, detail, count, ms FROM cluster_events
|
|
243
|
+
WHERE time > ? AND time <= ? ORDER BY time, id
|
|
244
|
+
`).all(since, now);
|
|
245
|
+
}
|
|
246
|
+
|
|
202
247
|
/**
|
|
203
248
|
* Write one interval's worth of rows. All or nothing - if the transaction
|
|
204
249
|
* fails the caller keeps its deltas and tries again next interval.
|
package/stats.js
CHANGED
|
@@ -2,6 +2,8 @@ const { monitorEventLoopDelay } = require('perf_hooks');
|
|
|
2
2
|
const {Utilities} = require("./library/utilities");
|
|
3
3
|
const escape = require('escape-html');
|
|
4
4
|
const StatsDatabase = require('./stats-db');
|
|
5
|
+
const { validRow: validClusterEvent } = require('./library/cluster-events');
|
|
6
|
+
const { readProcessMemory } = require('./library/cgroup-memory');
|
|
5
7
|
|
|
6
8
|
// Requests that aren't counted by a module (the root page, the dashboard) are
|
|
7
9
|
// attributed here, and requests that have no endpoint dimension (everything
|
|
@@ -30,6 +32,10 @@ class ModuleStats {
|
|
|
30
32
|
this.parent.countRequest(name, tat, this.module, endpoint);
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
countExternalRequest(name, tat, endpoint = NO_ENDPOINT) {
|
|
36
|
+
this.parent.countExternalRequest(name, tat, this.module, endpoint);
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
addTask(name, frequency) { this.parent.addTask(name, frequency); }
|
|
34
40
|
task(name, state) { this.parent.task(name, state); }
|
|
35
41
|
taskDone(name, state) { this.parent.taskDone(name, state); }
|
|
@@ -58,11 +64,22 @@ class ServerStats {
|
|
|
58
64
|
started = false;
|
|
59
65
|
requestCount = 0;
|
|
60
66
|
staticRequestCount = 0;
|
|
67
|
+
// on the master under the launcher: the workers' requests added by mergeCounts
|
|
68
|
+
mergedRequestCount = 0;
|
|
61
69
|
requestTime = 0;
|
|
62
|
-
//
|
|
70
|
+
// requests that had to ask another server (see countExternalRequest): in requestCount, but
|
|
71
|
+
// not in requestTime, and sampled separately
|
|
72
|
+
externalCount = 0;
|
|
73
|
+
externalTime = 0;
|
|
74
|
+
externalCountSnapshot = 0;
|
|
75
|
+
// Collect metrics every 10 minutes (stats.sampleSeconds, else stats.intervalMinutes)
|
|
63
76
|
intervalMs = 10 * 60 * 1000;
|
|
77
|
+
// and write the counters to the database every 10 minutes (stats.intervalMinutes)
|
|
78
|
+
flushMs = 10 * 60 * 1000;
|
|
64
79
|
history = [];
|
|
65
80
|
requestCountSnapshot = 0;
|
|
81
|
+
refusedCount = 0;
|
|
82
|
+
refusedCountSnapshot = 0;
|
|
66
83
|
startMem = 0;
|
|
67
84
|
startTime = Date.now();
|
|
68
85
|
timer;
|
|
@@ -84,6 +101,12 @@ class ServerStats {
|
|
|
84
101
|
this.log = log || console;
|
|
85
102
|
if (this.config.intervalMinutes) {
|
|
86
103
|
this.intervalMs = this.config.intervalMinutes * 60 * 1000;
|
|
104
|
+
this.flushMs = this.intervalMs;
|
|
105
|
+
}
|
|
106
|
+
// the graphs' sample interval, when it isn't the counters' (with a role, a minute at most:
|
|
107
|
+
// library/role.js)
|
|
108
|
+
if (this.config.sampleSeconds > 0) {
|
|
109
|
+
this.intervalMs = this.config.sampleSeconds * 1000;
|
|
87
110
|
}
|
|
88
111
|
this.openDatabase();
|
|
89
112
|
this.timer = setInterval(() => {
|
|
@@ -129,11 +152,17 @@ class ServerStats {
|
|
|
129
152
|
const currentMem = process.memoryUsage().heapUsed;
|
|
130
153
|
const combinedCount = this.requestCount + this.staticRequestCount;
|
|
131
154
|
const requestsDelta = combinedCount - this.requestCountSnapshot;
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
155
|
+
// the external requests are timed separately: they are orders of magnitude slower
|
|
156
|
+
const externalDelta = this.externalCount - this.externalCountSnapshot;
|
|
157
|
+
const ownDelta = requestsDelta - externalDelta;
|
|
158
|
+
const requestsTat = ownDelta > 0 ? this.requestTime / ownDelta : 0;
|
|
159
|
+
// Under load, the timer can run late. Use the actual time since the last sample, so
|
|
160
|
+
// an event-loop stall does not inflate the displayed request rate.
|
|
161
|
+
const intervalMinutes = (now - (this.lastTime ?? this.startTime)) / 60000;
|
|
162
|
+
const requestsPerMin = intervalMinutes > 0 ? requestsDelta / intervalMinutes : 0;
|
|
163
|
+
// where this sample's interval began, so the cluster graphs can place a late sample
|
|
164
|
+
const intervalStart = this.lastTime ?? this.startTime;
|
|
165
|
+
const refused = this.refusedCount - this.refusedCountSnapshot;
|
|
137
166
|
|
|
138
167
|
const loopDelay = this.eventLoopMonitor.mean / 1e6;
|
|
139
168
|
// Worst single stall in the window - the mean dilutes a multi-second
|
|
@@ -146,11 +175,30 @@ class ServerStats {
|
|
|
146
175
|
// shows its current size.)
|
|
147
176
|
const clientConcepts = this._sumCachingModules('clientConceptCount');
|
|
148
177
|
|
|
149
|
-
|
|
178
|
+
const point = {time: now, mem: currentMem - this.startMem, rpm: requestsPerMin, tat: requestsTat, block: loopDelay, blockMax: loopMax, clientConcepts: clientConcepts};
|
|
179
|
+
// where the interval began, and this process's private memory (RssAnon: RSS less mapped
|
|
180
|
+
// files; null where /proc can't be read), for the graphs (library/cluster-metrics.js)
|
|
181
|
+
point.intervalStart = intervalStart;
|
|
182
|
+
// the external requests in the interval, and their mean turnaround (null: none)
|
|
183
|
+
point.ext = externalDelta;
|
|
184
|
+
point.extTat = externalDelta > 0 ? this.externalTime / externalDelta : null;
|
|
185
|
+
const own = readProcessMemory('self');
|
|
186
|
+
point.anon = own ? own.anon : null;
|
|
187
|
+
if (this.onMetric) {
|
|
188
|
+
// under the launcher: the memory guard's refusals, which only a role counts
|
|
189
|
+
point.refused = refused;
|
|
190
|
+
}
|
|
191
|
+
this.history.push(point);
|
|
192
|
+
if (this.onMetric) {
|
|
193
|
+
this.onMetric(point);
|
|
194
|
+
}
|
|
150
195
|
|
|
151
196
|
this.eventLoopMonitor.reset();
|
|
152
197
|
this.requestCountSnapshot = combinedCount;
|
|
198
|
+
this.refusedCountSnapshot = this.refusedCount;
|
|
153
199
|
this.requestTime = 0;
|
|
200
|
+
this.externalCountSnapshot = this.externalCount;
|
|
201
|
+
this.externalTime = 0;
|
|
154
202
|
this.lastTime = now;
|
|
155
203
|
|
|
156
204
|
// Prune old data (keep 24 hours). Only the in-memory history is pruned -
|
|
@@ -158,7 +206,11 @@ class ServerStats {
|
|
|
158
206
|
const cutoff = now - (24 * 60 * 60 * 1000); // 24 hours ago
|
|
159
207
|
this.history = this.history.filter(m => m.time > cutoff);
|
|
160
208
|
|
|
161
|
-
|
|
209
|
+
// the counters every flushMs: at each sample when the two intervals are the same, as
|
|
210
|
+
// before, else at the first sample after (half a sample's grace for a timer that fires early)
|
|
211
|
+
if (this.lastFlush === undefined || now - this.lastFlush >= this.flushMs - this.intervalMs / 2) {
|
|
212
|
+
this.flushCounters(now, this.flushMs);
|
|
213
|
+
}
|
|
162
214
|
}
|
|
163
215
|
}
|
|
164
216
|
|
|
@@ -291,6 +343,119 @@ class ServerStats {
|
|
|
291
343
|
counter.timeTotal = counter.timeTotal + tat;
|
|
292
344
|
}
|
|
293
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Count one request whose answer had to ask another server (the tx module's ecosystem
|
|
348
|
+
* resolution: tx/ecosystem-resolver.js). These take seconds, not milliseconds, so they are
|
|
349
|
+
* kept out of the turnaround time graph and sampled separately (point.ext, point.extTat),
|
|
350
|
+
* and counted under their own operation, '<name> (external)'
|
|
351
|
+
*/
|
|
352
|
+
countExternalRequest(name, tat, module = SERVER_MODULE, endpoint = NO_ENDPOINT) {
|
|
353
|
+
this.requestCount++;
|
|
354
|
+
this.externalCount++;
|
|
355
|
+
this.externalTime = this.externalTime + tat;
|
|
356
|
+
|
|
357
|
+
const counter = this.counterFor(module || SERVER_MODULE, endpoint || NO_ENDPOINT, (name || '(unnamed)') + ' (external)');
|
|
358
|
+
counter.countDelta++;
|
|
359
|
+
counter.countTotal++;
|
|
360
|
+
counter.timeDelta = counter.timeDelta + tat;
|
|
361
|
+
counter.timeTotal = counter.timeTotal + tat;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// The memory guard runs before routing and body parsing. Keep its refusals
|
|
365
|
+
// separate from completed module work and its turnaround-time denominator.
|
|
366
|
+
countRefusedRequest() {
|
|
367
|
+
this.refusedCount++;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* For a tx-worker under the launcher (library/cluster-counts.js): the requests counted
|
|
372
|
+
* since the last call, [{module, endpoint, operation, count, time}], and start again
|
|
373
|
+
* from zero. A worker has no statistics database; the master merges these into its
|
|
374
|
+
* own counters (mergeCounts), so the master's database and daily report count every
|
|
375
|
+
* process, each request once.
|
|
376
|
+
*/
|
|
377
|
+
takeCounts() {
|
|
378
|
+
const rows = [];
|
|
379
|
+
for (const counter of this.counters.values()) {
|
|
380
|
+
if (counter.countDelta > 0) {
|
|
381
|
+
rows.push({module: counter.module, endpoint: counter.endpoint, operation: counter.operation,
|
|
382
|
+
count: counter.countDelta, time: counter.timeDelta});
|
|
383
|
+
counter.countDelta = 0;
|
|
384
|
+
counter.timeDelta = 0;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return rows;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Put back what takeCounts gave, when it couldn't be sent: it goes with the next lot. */
|
|
391
|
+
restoreCounts(rows) {
|
|
392
|
+
for (const row of rows) {
|
|
393
|
+
const counter = this.counterFor(row.module, row.endpoint, row.operation);
|
|
394
|
+
counter.countDelta = counter.countDelta + row.count;
|
|
395
|
+
counter.timeDelta = counter.timeDelta + row.time;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* On the master: add another process's requests (from its takeCounts) to the counters,
|
|
401
|
+
* as if this process had counted them. They are written out with the next interval and
|
|
402
|
+
* are in countsSince() at once. Rows that aren't well formed are ignored.
|
|
403
|
+
* @returns {number} the requests added
|
|
404
|
+
*/
|
|
405
|
+
mergeCounts(rows) {
|
|
406
|
+
let added = 0;
|
|
407
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
408
|
+
if (!row || typeof row.module !== 'string' || typeof row.endpoint !== 'string' ||
|
|
409
|
+
typeof row.operation !== 'string' || !Number.isInteger(row.count) || row.count <= 0 ||
|
|
410
|
+
!Number.isFinite(row.time) || row.time < 0) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const counter = this.counterFor(row.module, row.endpoint, row.operation);
|
|
414
|
+
counter.countDelta = counter.countDelta + row.count;
|
|
415
|
+
counter.countTotal = counter.countTotal + row.count;
|
|
416
|
+
counter.timeDelta = counter.timeDelta + row.time;
|
|
417
|
+
counter.timeTotal = counter.timeTotal + row.time;
|
|
418
|
+
added = added + row.count;
|
|
419
|
+
}
|
|
420
|
+
this.mergedRequestCount = this.mergedRequestCount + added;
|
|
421
|
+
return added;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* On the master under the launcher: what the launcher saw (library/cluster-events.js),
|
|
426
|
+
* written straight to the database - there are few, and the daily report reads them from
|
|
427
|
+
* there. Rows that aren't well formed are ignored; without a database, all of them are.
|
|
428
|
+
* @returns {number} the rows written
|
|
429
|
+
*/
|
|
430
|
+
recordClusterEvents(rows) {
|
|
431
|
+
if (!this.db) {
|
|
432
|
+
return 0;
|
|
433
|
+
}
|
|
434
|
+
const valid = (Array.isArray(rows) ? rows : []).map(validClusterEvent).filter(Boolean);
|
|
435
|
+
try {
|
|
436
|
+
this.db.writeClusterEvents(valid);
|
|
437
|
+
return valid.length;
|
|
438
|
+
} catch (e) {
|
|
439
|
+
this.log.error(`Unable to record the launcher's events: ${e.message}`);
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* What the launcher saw in (since, now], oldest first, or null without a database.
|
|
446
|
+
*/
|
|
447
|
+
clusterEventsSince(since, now = Date.now()) {
|
|
448
|
+
if (!this.db) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
return this.db.clusterEventsSince(since, now);
|
|
453
|
+
} catch (e) {
|
|
454
|
+
this.log.error(`Unable to read the launcher's events: ${e.message}`);
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
294
459
|
/**
|
|
295
460
|
* Write out the counters that saw traffic this interval, and start the next
|
|
296
461
|
* one. The deltas are only cleared if the write succeeded, so a transient
|
|
@@ -404,6 +569,28 @@ class ServerStats {
|
|
|
404
569
|
}
|
|
405
570
|
}
|
|
406
571
|
|
|
572
|
+
/**
|
|
573
|
+
* The background tasks for the status page: those at work, in full
|
|
574
|
+
* ([{name, state, frequency, date, ago}]); the names of those whose last run failed (the status
|
|
575
|
+
* line lists them, with their state); and the names of the rest (resting, or not run yet)
|
|
576
|
+
*/
|
|
577
|
+
taskSummary(now = Date.now()) {
|
|
578
|
+
const working = [];
|
|
579
|
+
const failed = [];
|
|
580
|
+
const idle = [];
|
|
581
|
+
for (const [name, info] of this.taskMap) {
|
|
582
|
+
if (info.status === 'working') {
|
|
583
|
+
working.push({name: name, state: info.state, frequency: info.frequency, date: info.date,
|
|
584
|
+
ago: info.date ? Utilities.formatDuration(info.date, now) : ''});
|
|
585
|
+
} else if (info.status === 'error') {
|
|
586
|
+
failed.push(name);
|
|
587
|
+
} else {
|
|
588
|
+
idle.push(name);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return { working, failed, idle };
|
|
592
|
+
}
|
|
593
|
+
|
|
407
594
|
/**
|
|
408
595
|
* The background tasks whose last run failed: [{name, state, frequency, date}]
|
|
409
596
|
*/
|
|
@@ -449,7 +636,7 @@ class ServerStats {
|
|
|
449
636
|
this.touchRun();
|
|
450
637
|
// Synchronous, so the last partial interval is on disk before server.js
|
|
451
638
|
// calls process.exit().
|
|
452
|
-
this.flushCounters(Date.now(), Date.now() - (this.lastTime || this.startTime));
|
|
639
|
+
this.flushCounters(Date.now(), Date.now() - (this.lastFlush || this.lastTime || this.startTime));
|
|
453
640
|
if (this.db) {
|
|
454
641
|
try {
|
|
455
642
|
this.db.close();
|
|
@@ -503,4 +690,4 @@ class ServerStats {
|
|
|
503
690
|
}
|
|
504
691
|
module.exports = ServerStats;
|
|
505
692
|
module.exports.ModuleStats = ModuleStats;
|
|
506
|
-
module.exports.SERVER_MODULE = SERVER_MODULE;
|
|
693
|
+
module.exports.SERVER_MODULE = SERVER_MODULE;
|
|
@@ -55,8 +55,6 @@
|
|
|
55
55
|
<div class="container"> <!-- container -->
|
|
56
56
|
<div style="background-color: #ad1f2f; padding: 6px; color: white;"> <!-- container -->
|
|
57
57
|
|
|
58
|
-
<a href="http://www.hl7.org/fhir" style="color: gold" title="Fast Healthcare Interoperability Resources - Home Page"><img border="0" src="/icon-fhir-16.png" style="vertical-align: text-bottom"/> <b>FHIR</b></a> © HL7.org |
|
|
59
|
-
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] |
|
|
60
58
|
<a href="/" style="color: gold"> Server Home</a> |
|
|
61
59
|
<a href="/testing" style="color: gold">Test Reports</a> |
|
|
62
60
|
<a href="/testing/summary" style="color: gold">Summary</a> |
|
|
@@ -73,7 +71,7 @@
|
|
|
73
71
|
<div class="inner-wrapper">
|
|
74
72
|
<div class="col-12">
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
<h2><a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" title="FHIRsmith [%ver%]"><img border="0" src="/FHIRsmith32.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/></a> [%title%] </h2>
|
|
77
75
|
|
|
78
76
|
[%content%]
|
|
79
77
|
|
|
@@ -91,8 +89,7 @@
|
|
|
91
89
|
<div class="container"> <!-- container -->
|
|
92
90
|
<div class="inner-wrapper">
|
|
93
91
|
<p>
|
|
94
|
-
<a href="
|
|
95
|
-
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] © HealthIntersections.com.au 2023+ |
|
|
92
|
+
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] © HealthIntersections.com.au 2023+ |
|
|
96
93
|
([%ms%] ms)
|
|
97
94
|
[%sponsorMessage%]
|
|
98
95
|
</p>
|
|
@@ -115,10 +112,10 @@
|
|
|
115
112
|
================================================== -->
|
|
116
113
|
<!-- Placed at the end of the document so the pages load faster -->
|
|
117
114
|
<!-- <script src="/assets/js/jquery.js"/> -->
|
|
118
|
-
<script src="/assets/js/bootstrap.min.js"
|
|
119
|
-
<script src="/assets/js/respond.min.js"
|
|
115
|
+
<script src="/assets/js/bootstrap.min.js"></script>
|
|
116
|
+
<script src="/assets/js/respond.min.js"></script>
|
|
120
117
|
|
|
121
|
-
<script src="/assets/js/fhir.js"
|
|
118
|
+
<script src="/assets/js/fhir.js"></script>
|
|
122
119
|
|
|
123
120
|
<!-- Analytics Below
|
|
124
121
|
================================================== -->
|
|
@@ -48,7 +48,7 @@ initializeSession() {
|
|
|
48
48
|
rolling: true,
|
|
49
49
|
cookie: {
|
|
50
50
|
// This is key - secure cookies when behind HTTPS proxy
|
|
51
|
-
secure: this.config.
|
|
51
|
+
secure: this.config.cookieSecure ?? 'auto', // Secure when the request came over https (req.secure)
|
|
52
52
|
httpOnly: true,
|
|
53
53
|
maxAge: 24 * 60 * 60 * 1000,
|
|
54
54
|
sameSite: 'lax'
|
package/token/security_guide.md
CHANGED
|
@@ -17,7 +17,7 @@ This guide covers the security considerations and proper implementation of Passp
|
|
|
17
17
|
saveUninitialized: false, // Don't create sessions until something stored
|
|
18
18
|
rolling: true, // Reset expiration on each request
|
|
19
19
|
cookie: {
|
|
20
|
-
secure:
|
|
20
|
+
secure: this.config.cookieSecure ?? 'auto', // Secure when the request came over https (req.secure)
|
|
21
21
|
httpOnly: true, // Prevent XSS access to cookies
|
|
22
22
|
maxAge: 24 * 60 * 60 * 1000, // 24 hour expiration
|
|
23
23
|
sameSite: 'lax' // CSRF protection
|