diff --git a/src/main/kotlin/eu/darken/octi/server/Server.kt b/src/main/kotlin/eu/darken/octi/server/Server.kt index 121ad77..99a0cd7 100644 --- a/src/main/kotlin/eu/darken/octi/server/Server.kt +++ b/src/main/kotlin/eu/darken/octi/server/Server.kt @@ -100,6 +100,13 @@ class Server @Inject constructor( allowHeader("Upload-Offset") exposeHeader(HttpHeaders.ETag) exposeHeader(HttpHeaders.LastModified) + // ModuleRoute sets X-Modified-At with the payload's server-side + // modification timestamp. Browsers can only read non-safelisted + // response headers cross-origin when they're listed here. + // octi-web's multi-connector merge uses it to order data when the + // same peer device is reachable via two connectors — newest + // X-Modified-At per (deviceId, moduleId) wins. + exposeHeader("X-Modified-At") exposeHeader(HttpHeaders.ContentRange) exposeHeader(HttpHeaders.AcceptRanges) exposeHeader(HttpHeaders.RetryAfter) diff --git a/src/test/kotlin/eu/darken/octi/server/common/CorsFlowTest.kt b/src/test/kotlin/eu/darken/octi/server/common/CorsFlowTest.kt index 084109d..af64607 100644 --- a/src/test/kotlin/eu/darken/octi/server/common/CorsFlowTest.kt +++ b/src/test/kotlin/eu/darken/octi/server/common/CorsFlowTest.kt @@ -128,6 +128,7 @@ class CorsFlowTest : TestRunner() { "upload-expires", "upload-state", "x-blob-id", + "x-modified-at", ) withClue("Access-Control-Expose-Headers='$exposed' missing: ${expected.filterNot { it in exposed }}") { expected.forEach { (it in exposed) shouldBe true }