mirror of
https://github.com/Quad4-Software/Reticulum-Go
synced 2026-08-29 23:48:44 -04:00
186 lines
5.2 KiB
Lua
186 lines
5.2 KiB
Lua
-- SPDX-License-Identifier: Apache-2.0
|
|
-- Copyright (c) 2024-2026 Quad4.io
|
|
--
|
|
-- NomadNet-style pageserver over LuaJIT librns bindings.
|
|
-- Usage: luajit main.lua -c config [-i identity] [-a announce_sec] [-p page_file]
|
|
|
|
local rns = require("rns")
|
|
|
|
local DEFAULT_ANNOUNCE_SEC = 900
|
|
local DEFAULT_PAGE_PATH = "/page/index.mu"
|
|
local DEFAULT_FILE_PATH = "/file/test.txt"
|
|
local DEFAULT_PAGE_FILE = "pages/index.mu"
|
|
local DEFAULT_FILE_FILE = "files/test.txt"
|
|
local DEFAULT_IDENTITY_PATH = "identity"
|
|
local REQ_DATA_CAP = 64 * 1024
|
|
|
|
local FALLBACK_PAGE = table.concat({
|
|
"> Lua pageserver\n\n",
|
|
"librns via Reticulum-Go\n\n",
|
|
"Fallback page (file not found).\n\n",
|
|
})
|
|
|
|
local FALLBACK_FILE = "Test file from Reticulum-Go node!\n"
|
|
|
|
local function die(msg)
|
|
io.stderr:write(msg .. "\n")
|
|
os.exit(1)
|
|
end
|
|
|
|
local function print_last_error(what)
|
|
local msg = rns.last_error()
|
|
if msg ~= "" then
|
|
io.stderr:write(what .. ": " .. msg .. "\n")
|
|
else
|
|
io.stderr:write(what .. "\n")
|
|
end
|
|
end
|
|
|
|
local function load_bytes(path, fallback)
|
|
local f = io.open(path, "rb")
|
|
if not f then
|
|
io.stderr:write("warning: could not read " .. path .. ", using built-in content\n")
|
|
return fallback
|
|
end
|
|
local data = f:read("*a")
|
|
f:close()
|
|
return data
|
|
end
|
|
|
|
local function load_or_create_identity(path)
|
|
local ok, identity = pcall(rns.Identity.load, path)
|
|
if ok then
|
|
io.stderr:write("loaded identity from " .. path .. "\n")
|
|
return identity
|
|
end
|
|
identity = rns.Identity.generate()
|
|
identity:save(path)
|
|
io.stderr:write("created and saved identity to " .. path .. "\n")
|
|
return identity
|
|
end
|
|
|
|
local function parse_args(argv)
|
|
local config_path
|
|
local identity_path = DEFAULT_IDENTITY_PATH
|
|
local announce_sec = DEFAULT_ANNOUNCE_SEC
|
|
local page_file = DEFAULT_PAGE_FILE
|
|
local file_file = DEFAULT_FILE_FILE
|
|
local request_path = DEFAULT_PAGE_PATH
|
|
local i = 1
|
|
while i <= #argv do
|
|
local a = argv[i]
|
|
if a == "-c" then
|
|
i = i + 1
|
|
config_path = argv[i]
|
|
elseif a == "-i" then
|
|
i = i + 1
|
|
identity_path = argv[i]
|
|
elseif a == "-a" then
|
|
i = i + 1
|
|
announce_sec = tonumber(argv[i]) or -1
|
|
elseif a == "-p" then
|
|
i = i + 1
|
|
page_file = argv[i]
|
|
elseif a == "-f" then
|
|
i = i + 1
|
|
file_file = argv[i]
|
|
elseif a == "-P" then
|
|
i = i + 1
|
|
request_path = argv[i]
|
|
else
|
|
die("unexpected argument: " .. a)
|
|
end
|
|
i = i + 1
|
|
end
|
|
if not config_path then
|
|
die("usage: luajit main.lua -c config [options]")
|
|
end
|
|
if announce_sec < 0 then
|
|
die("announce interval must be >= 0")
|
|
end
|
|
return config_path, identity_path, announce_sec, page_file, file_file, request_path
|
|
end
|
|
|
|
local config_path, identity_path, announce_sec, page_file, file_file, request_path = parse_args(arg)
|
|
|
|
local ver = rns.version()
|
|
if ver ~= rns.API_VERSION then
|
|
die("librns version mismatch: got " .. ver)
|
|
end
|
|
|
|
local page_body = load_bytes(page_file, FALLBACK_PAGE)
|
|
local file_body = load_bytes(file_file, FALLBACK_FILE)
|
|
|
|
local node = rns.Node.create(config_path)
|
|
local identity = load_or_create_identity(identity_path)
|
|
node:set_identity(identity)
|
|
node:start()
|
|
|
|
local dest = rns.Destination.create(node, nil, "nomadnetwork", { "node" }, true)
|
|
dest:register_request_handler(request_path)
|
|
dest:register_request_handler(DEFAULT_FILE_PATH)
|
|
|
|
local dest_hex = rns.hash_to_hex(dest:hash())
|
|
print("DEST_HASH=" .. dest_hex)
|
|
print("REQUEST_PATH=" .. request_path)
|
|
print("FILE_PATH=" .. DEFAULT_FILE_PATH)
|
|
io.stderr:write("librns " .. ver .. " pageserver listening as nomadnetwork.node\n")
|
|
io.stderr:write("announce name=librns-lua-pageserver interval=" .. announce_sec .. "s\n")
|
|
|
|
local app_data = "librns-lua-pageserver"
|
|
if not pcall(function()
|
|
dest:announce(app_data)
|
|
end) then
|
|
print_last_error("destination.announce failed")
|
|
else
|
|
io.stderr:write("announce sent\n")
|
|
end
|
|
|
|
local last_announce = os.time()
|
|
while true do
|
|
if announce_sec > 0 and os.time() - last_announce >= announce_sec then
|
|
pcall(function()
|
|
dest:announce(app_data)
|
|
end)
|
|
io.stderr:write("announce refreshed\n")
|
|
last_announce = os.time()
|
|
end
|
|
|
|
local ok_ev, ev_or_err = pcall(rns.Event.poll, node, 200, REQ_DATA_CAP)
|
|
if not ok_ev then
|
|
if type(ev_or_err) ~= "table" or ev_or_err.code ~= rns.Error.TIMEOUT then
|
|
print_last_error("Event.poll failed")
|
|
dest:close()
|
|
identity:close()
|
|
node:close()
|
|
os.exit(1)
|
|
end
|
|
else
|
|
local ev = ev_or_err
|
|
local kind = ev:kind()
|
|
if kind == rns.EventKind.LINK_ESTABLISHED then
|
|
io.stderr:write("inbound link established\n")
|
|
elseif kind == rns.EventKind.LINK_CLOSED then
|
|
io.stderr:write("link closed\n")
|
|
elseif kind == rns.EventKind.REQUEST_INCOMING then
|
|
local path = ev:path()
|
|
io.stderr:write("request incoming path=" .. path .. "\n")
|
|
local req_id = ev:request_id()
|
|
if path == request_path then
|
|
if not pcall(rns.request_respond, node, req_id, page_body) then
|
|
print_last_error("request_respond failed")
|
|
else
|
|
io.stderr:write("served " .. request_path .. " (" .. #page_body .. " bytes)\n")
|
|
end
|
|
elseif path == DEFAULT_FILE_PATH then
|
|
if not pcall(rns.request_respond_file, node, req_id, "test.txt", file_body) then
|
|
print_last_error("request_respond_file failed")
|
|
else
|
|
io.stderr:write("served " .. DEFAULT_FILE_PATH .. " (" .. #file_body .. " bytes)\n")
|
|
end
|
|
else
|
|
pcall(rns.request_respond, node, req_id, "page not found\n")
|
|
end
|
|
end
|
|
end
|
|
end
|