Skip to content

Commit f9c6ff8

Browse files
committed
feat(api): fw-7456 add CORS support for JSON API endpoints
Add Access-Control-Allow-Origin and Access-Control-Allow-Credentials headers to all JSON API responses, and handle OPTIONS preflight requests with 204 No Content. This ensures cross-origin requests from browsers work consistently regardless of CDN cache state.
1 parent c8b6205 commit f9c6ff8

3 files changed

Lines changed: 51 additions & 7 deletions

File tree

src/jsons/health.nim

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,11 @@ proc createJsonApiHealthRouter*(cfg: Config) =
1111
router jsonapi_health:
1212
get "/api/health":
1313
cond cfg.enableJsonApi
14-
let headers = {"Content-Type": "application/json; charset=utf-8"}
14+
let origin = corsOrigin()
15+
let headers = {
16+
"Content-Type": "application/json; charset=utf-8",
17+
"Vary": "Origin",
18+
"Access-Control-Allow-Origin": origin,
19+
"Access-Control-Allow-Credentials": "true"
20+
}
1521
resp Http200, headers, """{"message": "OK"}"""

src/nitter.nim

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SPDX-License-Identifier: AGPL-3.0-only
2-
import asyncdispatch, strformat, logging
2+
import asyncdispatch, strformat, logging, re
33
from net import Port
44
from htmlgen import a
55
from os import getEnv
@@ -71,6 +71,18 @@ settings:
7171
reusePort = true
7272

7373
routes:
74+
# CORS preflight handler for API routes
75+
options re"/api/.*":
76+
let origin = if request.headers.hasKey("Origin"): request.headers["Origin"] else: "*"
77+
resp Http204, {
78+
"Vary": "Origin",
79+
"Access-Control-Allow-Origin": origin,
80+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
81+
"Access-Control-Allow-Headers": "Content-Type, Authorization, DNT",
82+
"Access-Control-Allow-Credentials": "true",
83+
"Access-Control-Max-Age": "300"
84+
}, ""
85+
7486
get "/":
7587
resp renderMain(renderSearch(), request, cfg, themePrefs())
7688

src/routes/router_utils.nim

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,51 @@ template getCursor*(req: Request): string =
4343
proc getNames*(name: string): seq[string] =
4444
name.strip(chars={'/'}).split(",").filterIt(it.len > 0)
4545

46+
template corsOrigin*(): string {.dirty.} =
47+
if request.headers.hasKey("Origin"): request.headers["Origin"] else: "*"
48+
4649
template respJson*(node: JsonNode) =
47-
resp $node, "application/json"
50+
let origin = corsOrigin()
51+
resp Http200, {
52+
"Content-Type": "application/json",
53+
"Vary": "Origin",
54+
"Access-Control-Allow-Origin": origin,
55+
"Access-Control-Allow-Credentials": "true"
56+
}, $node
4857

4958
template respJsonSuccess*(data: JsonNode) =
59+
let origin = corsOrigin()
5060
let successResponse = %*{
5161
"code": 0,
5262
"data": data
5363
}
54-
resp $successResponse, "application/json"
64+
resp Http200, {
65+
"Content-Type": "application/json",
66+
"Vary": "Origin",
67+
"Access-Control-Allow-Origin": origin,
68+
"Access-Control-Allow-Credentials": "true"
69+
}, $successResponse
5570

5671
template respJsonError*(message: string, errorType: string = "", httpCode: HttpCode = Http200) =
72+
let origin = corsOrigin()
5773
var errorResponse = %*{
5874
"code": -1,
5975
"error": message
6076
}
6177
if errorType.len > 0:
6278
errorResponse["error_type"] = %errorType
63-
resp httpCode, $errorResponse, "application/json"
79+
resp httpCode, {
80+
"Content-Type": "application/json",
81+
"Vary": "Origin",
82+
"Access-Control-Allow-Origin": origin,
83+
"Access-Control-Allow-Credentials": "true"
84+
}, $errorResponse
6485

6586
template respJsonNull*() =
66-
let nullResponse = newJNull()
67-
resp $nullResponse, "application/json"
87+
let origin = corsOrigin()
88+
resp Http200, {
89+
"Content-Type": "application/json",
90+
"Vary": "Origin",
91+
"Access-Control-Allow-Origin": origin,
92+
"Access-Control-Allow-Credentials": "true"
93+
}, $newJNull()

0 commit comments

Comments
 (0)