Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/jsons/health.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ proc createJsonApiHealthRouter*(cfg: Config) =
router jsonapi_health:
get "/api/health":
cond cfg.enableJsonApi
let headers = {"Content-Type": "application/json; charset=utf-8"}
let origin = corsOrigin()
let headers = {
"Content-Type": "application/json; charset=utf-8",
"Vary": "Origin",
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true"
}
resp Http200, headers, """{"message": "OK"}"""
4 changes: 1 addition & 3 deletions src/jsons/search.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ proc createJsonApiSearchRouter*(cfg: Config) =
if q.len > 500:
respJsonError("Search input too long.", "invalid_input", Http400)

let
prefs = requestPrefs()
query = initQuery(params(request))
let query = initQuery(params(request))

case query.kind
of users:
Expand Down
41 changes: 34 additions & 7 deletions src/jsons/timeline.nim
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
import json, asyncdispatch, strutils, sequtils, uri, options, times

import options
import times

import jester, karax/vdom

import ".."/routes/[router_utils, timeline]
Expand Down Expand Up @@ -32,6 +29,33 @@ proc formatUserAsJson*(user: User): JsonNode =
"joinDate": user.joinDate.toTime.toUnix()
}

proc formatMediaAsJson*(m: Media): JsonNode =
case m.kind
of photoMedia:
return %*{"type": "photo", "url": m.photo.url, "altText": m.photo.altText}
of videoMedia:
var variants = newJArray()
for v in m.video.variants:
variants.add %*{
"contentType": $v.contentType,
"url": v.url,
"bitrate": v.bitrate,
"resolution": v.resolution
}
return %*{
"type": "video",
"durationMs": m.video.durationMs,
"url": m.video.url,
"thumb": m.video.thumb,
"available": m.video.available,
"reason": m.video.reason,
"title": m.video.title,
"description": m.video.description,
"variants": variants
}
of gifMedia:
return %*{"type": "gif", "url": m.gif.url, "thumb": m.gif.thumb, "altText": m.gif.altText}

proc formatTweetAsJson*(tweet: Tweet): JsonNode =
return %*{
"id": $tweet.id,
Expand All @@ -51,7 +75,8 @@ proc formatTweetAsJson*(tweet: Tweet): JsonNode =
"replies": tweet.stats.replies,
"retweets": tweet.stats.retweets,
"likes": tweet.stats.likes,
"quotes": tweet.stats.quotes
"quotes": tweet.stats.quotes,
"views": tweet.stats.views
},
"retweet": if tweet.retweet.isSome: formatTweetAsJson(get(
tweet.retweet)) else: newJNull(),
Expand All @@ -63,9 +88,11 @@ proc formatTweetAsJson*(tweet: Tweet): JsonNode =
tweet.quote)) else: newJNull(),
"card": if tweet.card.isSome: %*get(tweet.card) else: newJNull(),
"poll": if tweet.poll.isSome: %*get(tweet.poll) else: newJNull(),
"photos": (if tweet.hasPhotos: %tweet.getPhotos else: newJNull()),
"videos": (if tweet.hasVideos: %tweet.getVideos else: newJNull()),
"gifs": (if tweet.hasGifs: %tweet.media.filterIt(it.kind == gifMedia).mapIt(it.gif) else: newJNull())
"media": (if tweet.media.len > 0: %tweet.media.map(formatMediaAsJson) else: newJNull()),
"history": (if tweet.history.len > 0: %tweet.history else: newJNull()),
"note": (if tweet.note.len > 0: %tweet.note else: newJNull()),
"isAd": %tweet.isAd,
"isAI": %tweet.isAI
}

proc formatTimelineAsJson*(results: Timeline): JsonNode =
Expand Down
13 changes: 12 additions & 1 deletion src/nitter.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-only
import asyncdispatch, strformat, logging
import asyncdispatch, strformat, logging, re
from net import Port
from htmlgen import a
from os import getEnv
Expand Down Expand Up @@ -75,6 +75,17 @@ settings:
reusePort = true

routes:
options re"/api/.*":
let origin = if request.headers.hasKey("Origin"): request.headers["Origin"] else: "*"
resp Http204, {
"Vary": "Origin",
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, DNT",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Max-Age": "300"
}, ""

before:
# skip all file URLs
cond "." notin request.path
Expand Down
36 changes: 31 additions & 5 deletions src/routes/router_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,25 +58,51 @@ template applyUrlPrefs*() {.dirty.} =
else:
redirect(request.path)

template corsOrigin*(): string {.dirty.} =
if request.headers.hasKey("Origin"): request.headers["Origin"] else: "*"

template respJson*(node: JsonNode) =
resp $node, "application/json"
let origin = corsOrigin()
resp Http200, {
"Content-Type": "application/json",
"Vary": "Origin",
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true"
}, $node

template respJsonSuccess*(data: JsonNode) =
let origin = corsOrigin()
let successResponse = %*{
"code": 0,
"data": data
}
resp $successResponse, "application/json"
resp Http200, {
"Content-Type": "application/json",
"Vary": "Origin",
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true"
}, $successResponse

template respJsonError*(message: string, errorType: string = "", httpCode: HttpCode = Http200) =
let origin = corsOrigin()
var errorResponse = %*{
"code": -1,
"error": message
}
if errorType.len > 0:
errorResponse["error_type"] = %errorType
resp httpCode, $errorResponse, "application/json"
resp httpCode, {
"Content-Type": "application/json",
"Vary": "Origin",
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true"
}, $errorResponse

template respJsonNull*() =
let nullResponse = newJNull()
resp $nullResponse, "application/json"
let origin = corsOrigin()
resp Http200, {
"Content-Type": "application/json",
"Vary": "Origin",
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Credentials": "true"
}, $newJNull()
Loading