fix: replace client-side Swagger server detection with server-side dynamic spec
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m43s
CI / Security audit (push) Successful in 3m15s
Build and Push Docker Image / build (push) Successful in 4m6s
CI / Swagger Validation & Coverage (push) Successful in 4m14s
CI / Tests & coverage (push) Successful in 4m32s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 2m43s
CI / Security audit (push) Successful in 3m15s
Build and Push Docker Image / build (push) Successful in 4m6s
CI / Swagger Validation & Coverage (push) Successful in 4m14s
CI / Tests & coverage (push) Successful in 4m32s
- Change swaggerUi.setup to pass null and fetch spec from /api/swagger.json
- Update /api/swagger.json handler to dynamically set server URL based on request
- Remove dead client-side detection script (swagger-server-detection.js)
- Server-side detection respects TRUST_PROXY for reverse proxy scenarios
- req.protocol and req.get('host') automatically use X-Forwarded headers when configured
- Fixes issue where placeholder URL was never replaced due to window.ui being unavailable
This commit is contained in:
@@ -1,45 +0,0 @@
|
|||||||
// Swagger UI server URL auto-detection
|
|
||||||
// Automatically sets the server URL to match the current instance
|
|
||||||
(function() {
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
// Wait for Swagger UI to initialize
|
|
||||||
setTimeout(function() {
|
|
||||||
try {
|
|
||||||
// Detect current URL from browser
|
|
||||||
const protocol = window.location.protocol;
|
|
||||||
const host = window.location.host;
|
|
||||||
const detectedUrl = `${protocol}//${host}`;
|
|
||||||
|
|
||||||
// Get Swagger UI instance
|
|
||||||
const ui = window.ui;
|
|
||||||
if (!ui) {
|
|
||||||
console.warn('Swagger UI not found, cannot set server URL');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the spec servers
|
|
||||||
const spec = ui.specSelectors.specJson();
|
|
||||||
const servers = spec.getIn(['servers']);
|
|
||||||
|
|
||||||
if (!servers || servers.size === 0) {
|
|
||||||
console.warn('No servers defined in OpenAPI spec');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the first server with the detected URL
|
|
||||||
const updatedServers = servers.setIn([0, 'url'], detectedUrl);
|
|
||||||
|
|
||||||
// Apply the updated servers to the spec
|
|
||||||
const updatedSpec = spec.set('servers', updatedServers);
|
|
||||||
ui.specActions.updateSpec(updatedSpec.toJS());
|
|
||||||
|
|
||||||
// Also set the selected server in Swagger UI state
|
|
||||||
ui.specActions.setSelectedServer(updatedServers.get(0).get('url'));
|
|
||||||
|
|
||||||
console.log('Swagger UI server URL set to:', detectedUrl);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to auto-detect Swagger UI server URL:', error);
|
|
||||||
}
|
|
||||||
}, 100); // Small delay to ensure Swagger UI is initialized
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
+18
-6
@@ -181,18 +181,30 @@ function createApp({ skipRateLimits = false } = {}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Swagger UI - publicly accessible API documentation
|
// Swagger UI - publicly accessible API documentation
|
||||||
app.use('/api/swagger', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
app.use('/api/swagger', swaggerUi.serve, swaggerUi.setup(null, {
|
||||||
customSiteTitle: 'sofarr API Documentation',
|
customSiteTitle: 'sofarr API Documentation',
|
||||||
customCss: '.swagger-ui .topbar { display: none }',
|
customCss: '.swagger-ui .topbar { display: none }',
|
||||||
customJs: [
|
customJs: [
|
||||||
'/swagger-auth-banner.js',
|
'/swagger-auth-banner.js'
|
||||||
'/swagger-server-detection.js'
|
],
|
||||||
]
|
swaggerOptions: {
|
||||||
|
url: '/api/swagger.json'
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Serve the raw OpenAPI spec as JSON
|
// Serve the raw OpenAPI spec as JSON with dynamic server URL
|
||||||
app.get('/api/swagger.json', (req, res) => {
|
app.get('/api/swagger.json', (req, res) => {
|
||||||
res.json(swaggerSpec);
|
// Clone the spec to avoid modifying the original
|
||||||
|
const specCopy = JSON.parse(JSON.stringify(swaggerSpec));
|
||||||
|
|
||||||
|
// Replace the server URL with the current request's origin
|
||||||
|
if (specCopy.servers && specCopy.servers.length > 0) {
|
||||||
|
const protocol = req.protocol;
|
||||||
|
const host = req.get('host');
|
||||||
|
specCopy.servers[0].url = `${protocol}://${host}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(specCopy);
|
||||||
});
|
});
|
||||||
|
|
||||||
// API routes
|
// API routes
|
||||||
|
|||||||
+21
-4
@@ -296,15 +296,32 @@ app.get('/ready', (req, res) => {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Swagger UI - publicly accessible API documentation
|
// Swagger UI - publicly accessible API documentation
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
app.use('/api/swagger', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
app.use('/api/swagger', swaggerUi.serve, swaggerUi.setup(null, {
|
||||||
customSiteTitle: 'sofarr API Documentation',
|
customSiteTitle: 'sofarr API Documentation',
|
||||||
customCss: '.swagger-ui .topbar { display: none }',
|
customCss: '.swagger-ui .topbar { display: none }',
|
||||||
customJs: [
|
customJs: [
|
||||||
'/swagger-auth-banner.js',
|
'/swagger-auth-banner.js'
|
||||||
'/swagger-server-detection.js'
|
],
|
||||||
]
|
swaggerOptions: {
|
||||||
|
url: '/api/swagger.json'
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Serve the raw OpenAPI spec as JSON with dynamic server URL
|
||||||
|
app.get('/api/swagger.json', (req, res) => {
|
||||||
|
// Clone the spec to avoid modifying the original
|
||||||
|
const specCopy = JSON.parse(JSON.stringify(swaggerSpec));
|
||||||
|
|
||||||
|
// Replace the server URL with the current request's origin
|
||||||
|
if (specCopy.servers && specCopy.servers.length > 0) {
|
||||||
|
const protocol = req.protocol;
|
||||||
|
const host = req.get('host');
|
||||||
|
specCopy.servers[0].url = `${protocol}://${host}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(specCopy);
|
||||||
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Static files — served before API routes
|
// Static files — served before API routes
|
||||||
// index.html is served manually so we can inject the CSP nonce
|
// index.html is served manually so we can inject the CSP nonce
|
||||||
|
|||||||
Reference in New Issue
Block a user