Skip to content

Commit c93d839

Browse files
committed
fix metadata link
1 parent e1671c2 commit c93d839

3 files changed

Lines changed: 43 additions & 6 deletions

File tree

server.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ htmlServer.setSponsorMessage(config.sponsorMessage ? config.sponsorMessage : '')
6868

6969
const app = express();
7070

71+
// Behind nginx (or any reverse proxy): honor X-Forwarded-* so req.protocol and
72+
// req.hostname reflect what the client actually requested, not the loopback hop.
73+
app.set('trust proxy', config.server.trustProxy ?? true);
74+
7175
const PORT = process.env.PORT || config.server.port || 3000;
7276

7377
// Middleware

tests/tx/tx-module.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ describe('TX Module', () => {
5151
expect(response.body.kind).toBe('instance');
5252
});
5353

54+
test('url and implementation.url reflect the client host + endpoint path', async () => {
55+
const response = await request(app)
56+
.get('/tx/r5/metadata')
57+
.set('Host', 'tx.example.org')
58+
.set('Accept', 'application/json');
59+
60+
expect(response.status).toBe(200);
61+
// Must echo the host the client used, not a hardcoded value
62+
expect(response.body.implementation.url).toBe('http://tx.example.org/tx/r5');
63+
expect(response.body.url).toBe('http://tx.example.org/tx/r5/CapabilityStatement/tx');
64+
});
65+
5466
test('should list supported resource types', async () => {
5567
const response = await request(app)
5668
.get('/tx/r5/metadata')

tx/workers/metadata.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,37 @@ class MetadataHandler {
3636

3737
if (mode === 'terminology') {
3838
this.logInfo = 'termcaps';
39-
const tc = new TerminologyCapabilities(await this.buildTerminologyCapabilities(endpoint, provider));
39+
const tc = new TerminologyCapabilities(await this.buildTerminologyCapabilities(endpoint, provider, req));
4040
return res.json(tc.jsonObj);
4141
}
4242
this.logInfo = 'metadata';
4343

4444
// Default: return CapabilityStatement
45-
const cs = new CapabilityStatement(this.buildCapabilityStatement(endpoint, provider));
45+
const cs = new CapabilityStatement(this.buildCapabilityStatement(endpoint, provider, req));
4646
return res.json(cs.jsonObj);
4747
}
4848

49+
/**
50+
* Resolve the public base URL for this request.
51+
* Derived from what the client actually asked for (scheme + host) plus the
52+
* endpoint path, so the server echoes the host the client used rather than a
53+
* fixed, hardcoded value. Falls back to config/host only when no request is
54+
* available (e.g. unit tests calling the build methods directly).
55+
* @param {express.Request} [req] - Express request
56+
* @param {Object} endpoint - Endpoint info {path, ...}
57+
* @returns {string} Base URL, e.g. https://tx.fhir.org/r4
58+
*/
59+
resolveBaseUrl(req, endpoint) {
60+
if (!req) {
61+
return this.config.baseUrl || `https://${this.host}${endpoint.path}`;
62+
}
63+
// Requires app.set('trust proxy', ...) and nginx forwarding X-Forwarded-Proto
64+
// for the scheme to be correct behind the reverse proxy.
65+
const proto = req.protocol || 'https';
66+
const host = req.get('host') || this.host;
67+
return `${proto}://${host}${endpoint.path}`;
68+
}
69+
4970
/**
5071
* Handle GET /$versions request
5172
* @param {express.Request} req - Express request (with txEndpoint attached)
@@ -106,11 +127,11 @@ class MetadataHandler {
106127
* @param {Object} provider - Provider for code systems and resources
107128
* @returns {Object} CapabilityStatement resource
108129
*/
109-
buildCapabilityStatement(endpoint) {
130+
buildCapabilityStatement(endpoint, provider, req) {
110131

111132
const now = new Date().toISOString();
112133
const fhirVersion = this.mapFhirVersion(endpoint.fhirVersion);
113-
const baseUrl = this.config.baseUrl || `https://${this.host}${endpoint.path}`;
134+
const baseUrl = this.resolveBaseUrl(req, endpoint);
114135
const serverVersion = this.config.serverVersion;
115136

116137
return {
@@ -281,9 +302,9 @@ class MetadataHandler {
281302
* @param {Object} provider - Provider for code systems and resources
282303
* @returns {Object} TerminologyCapabilities resource
283304
*/
284-
async buildTerminologyCapabilities(endpoint, provider) {
305+
async buildTerminologyCapabilities(endpoint, provider, req) {
285306
const now = new Date().toISOString();
286-
const baseUrl = this.config.baseUrl || `https://${this.host}${endpoint.path}`;
307+
const baseUrl = this.resolveBaseUrl(req, endpoint);
287308
const serverVersion = this.config.serverVersion || '1.0.0';
288309

289310
const tc = {

0 commit comments

Comments
 (0)