channel concept
This commit is contained in:
parent
9c9397d6b3
commit
6ba185b376
80
util/channel.lua
Normal file
80
util/channel.lua
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
-- bi-directional http-channel
|
||||
-- with long-poll GET and POST on the same URL
|
||||
|
||||
local Channel = function(http, url, cfg)
|
||||
|
||||
cfg = cfg or {}
|
||||
local extra_headers = cfg.extra_headers or {}
|
||||
local timeout = cfg.timeout or 1
|
||||
local long_poll_timeout = cfg.long_poll_timeout or 30
|
||||
local error_retry = cfg.error_retry or 10
|
||||
|
||||
-- assemble post-header with json content
|
||||
local post_headers = { "Content-Type: application/json" }
|
||||
for _,header in pairs(cfg.extra_headers) do
|
||||
table.insert(post_headers, header)
|
||||
end
|
||||
|
||||
local recv_listeners = {}
|
||||
local run = true
|
||||
|
||||
local recv_loop
|
||||
|
||||
recv_loop = function()
|
||||
-- long-poll GET
|
||||
http.fetch({
|
||||
url = url,
|
||||
extra_headers = extra_headers,
|
||||
timeout = long_poll_timeout
|
||||
}, function(res)
|
||||
if res.succeeded and res.code == 200 then
|
||||
local data = minetest.parse_json(res.data)
|
||||
if data then
|
||||
for _,listener in pairs(recv_listeners) do
|
||||
listener(data)
|
||||
end
|
||||
end
|
||||
-- reschedule immediately
|
||||
minetest.after(0, recv_loop)
|
||||
else
|
||||
-- error, retry after some time
|
||||
minetest.after(error_retry, recv_loop)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
local send = function(data)
|
||||
-- POST
|
||||
http.fetch({
|
||||
url = url,
|
||||
extra_headers = post_headers,
|
||||
timeout = timeout,
|
||||
post_data = minetest.write_json(data)
|
||||
}, function(res)
|
||||
-- TODO: error-handling
|
||||
end)
|
||||
end
|
||||
|
||||
local receive = function(listener)
|
||||
table.insert(recv_listeners, listener)
|
||||
end
|
||||
|
||||
local close = function()
|
||||
run = false
|
||||
end
|
||||
|
||||
recv_loop();
|
||||
|
||||
return {
|
||||
send = send,
|
||||
receive = receive,
|
||||
close = close
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
return Channel
|
150
webmail.lua
150
webmail.lua
@ -1,126 +1,54 @@
|
||||
|
||||
local url, key, http
|
||||
|
||||
local webmail = {}
|
||||
|
||||
-- polls the webmail server and processes the logins made there
|
||||
webmail.auth_collector = function()
|
||||
http.fetch({
|
||||
url=url .. "/api/minetest/auth_collector",
|
||||
extra_headers = { "webmailkey: " .. key },
|
||||
timeout=15
|
||||
}, function(res)
|
||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local Channel = dofile(MP .. "/util/channel.lua")
|
||||
local channel
|
||||
|
||||
if res.code == 403 then
|
||||
-- unauthorized, abort
|
||||
minetest.log("error", "[webmail] invalid key specified!")
|
||||
return
|
||||
end
|
||||
|
||||
if res.succeeded and res.code == 200 then
|
||||
local auth = minetest.parse_json(res.data)
|
||||
if auth then
|
||||
local auth_response = {}
|
||||
local handler = minetest.get_auth_handler()
|
||||
local auth_handler = function(auth)
|
||||
local handler = minetest.get_auth_handler()
|
||||
|
||||
local success = false
|
||||
local entry = handler.get_auth(auth.name)
|
||||
if entry and minetest.check_password_entry(auth.name, entry.password, auth.password) then
|
||||
success = true
|
||||
end
|
||||
local success = false
|
||||
local entry = handler.get_auth(auth.name)
|
||||
if entry and minetest.check_password_entry(auth.name, entry.password, auth.password) then
|
||||
success = true
|
||||
end
|
||||
|
||||
-- send back auth response data
|
||||
http.fetch({
|
||||
url=url .. "/api/minetest/auth_collector",
|
||||
extra_headers = { "Content-Type: application/json", "webmailkey: " .. key },
|
||||
post_data = minetest.write_json({
|
||||
name = auth.name,
|
||||
success = success
|
||||
})
|
||||
}, function(res)
|
||||
-- stub
|
||||
end)
|
||||
channel.send({
|
||||
type = "auth",
|
||||
data = {
|
||||
name = auth.name,
|
||||
success = success
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
end
|
||||
-- execute again
|
||||
minetest.after(1, webmail.auth_collector)
|
||||
else
|
||||
-- execute again (error case)
|
||||
minetest.after(10, webmail.auth_collector)
|
||||
end
|
||||
|
||||
end)
|
||||
local send_handler = function(sendmail)
|
||||
-- send mail from webclient
|
||||
minetest.log("action", "[webmail] sending mail from webclient: " .. sendmail.src .. " -> " .. sendmail.dst)
|
||||
mail.send(sendmail.src, sendmail.dst, sendmail.subject, sendmail.body)
|
||||
end
|
||||
|
||||
-- called on mail saving to disk (every change)
|
||||
mail.webmail_save_hook = function()
|
||||
http.fetch({
|
||||
url=url .. "/api/minetest/messages",
|
||||
extra_headers = { "Content-Type: application/json", "webmailkey: " .. key },
|
||||
post_data = minetest.write_json(mail.messages)
|
||||
}, function(res)
|
||||
if not res.succeeded then
|
||||
minetest.log("error", "[webmail] message sync to web failed")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- polls the message endpoint for commands from the webclient
|
||||
webmail.message_command_loop = function()
|
||||
http.fetch({
|
||||
url=url .. "/api/minetest/messages",
|
||||
extra_headers = { "webmailkey: " .. key },
|
||||
timeout=15
|
||||
}, function(res)
|
||||
|
||||
if res.code == 403 then
|
||||
-- unauthorized, abort
|
||||
minetest.log("error", "[webmail] invalid key specified!")
|
||||
return
|
||||
end
|
||||
|
||||
if res.succeeded and res.code == 200 then
|
||||
local data = minetest.parse_json(res.data)
|
||||
if data then
|
||||
for _,cmd in pairs(data) do
|
||||
if cmd.type == "send" and cmd.mail and cmd.mail.src and cmd.mail.dst then
|
||||
-- send mail from webclient
|
||||
local sendmail = cmd.mail
|
||||
minetest.log("action", "[webmail] sending mail from webclient: " .. sendmail.src .. " -> " .. sendmail.dst)
|
||||
mail.send(sendmail.src, sendmail.dst, sendmail.subject, sendmail.body)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- execute again
|
||||
minetest.after(1, webmail.message_command_loop)
|
||||
else
|
||||
-- execute again (error case)
|
||||
minetest.after(10, webmail.message_command_loop)
|
||||
|
||||
-- update mails
|
||||
minetest.after(10, mail.webmail_save_hook)
|
||||
end
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
mail.webmail_init = function(_http, webmail_url, webmail_key)
|
||||
url = webmail_url
|
||||
key = webmail_key
|
||||
http = _http
|
||||
|
||||
minetest.after(4, function()
|
||||
-- start auth collector loop
|
||||
webmail.auth_collector()
|
||||
|
||||
-- start message command loop
|
||||
webmail.message_command_loop()
|
||||
|
||||
-- sync messages after server start
|
||||
if #mail.messages > 0 then
|
||||
-- only if mails available
|
||||
mail.webmail_save_hook()
|
||||
channel.send({
|
||||
type = "messages",
|
||||
data = mail.messages
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
mail.webmail_init = function(http, url, key)
|
||||
channel = Channel(http, url .. "/api/minetest/channel", {
|
||||
extra_headers = { "webmailkey: " .. key }
|
||||
})
|
||||
|
||||
channel.receive(function(data)
|
||||
if data.type == "auth" then
|
||||
auth_handler(data.data)
|
||||
elseif data.type == "send" then
|
||||
send_handler(data.data)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -6,30 +6,30 @@ const keycheck = require("./keycheck");
|
||||
const bodyParser = require('body-parser')
|
||||
const jsonParser = bodyParser.json()
|
||||
|
||||
|
||||
app.get('/api/minetest/auth_collector', function(req, res){
|
||||
// web -> mod
|
||||
app.get('/api/minetest/channel', function(req, res){
|
||||
if (!keycheck(req, res))
|
||||
return;
|
||||
|
||||
function handleEvent(auth){
|
||||
function handleEvent(obj){
|
||||
clearTimeout(handle);
|
||||
res.json(auth);
|
||||
res.json(obj);
|
||||
}
|
||||
|
||||
var handle = setTimeout(function(){
|
||||
res.json(null);
|
||||
events.removeListener("login", handleEvent);
|
||||
events.removeListener("channel-send", handleEvent);
|
||||
}, 10000);
|
||||
|
||||
events.once("login", handleEvent);
|
||||
events.once("channel-send", handleEvent);
|
||||
});
|
||||
|
||||
|
||||
app.post('/api/minetest/auth_collector', jsonParser, function(req, res){
|
||||
// mod -> web
|
||||
app.post('/api/minetest/channel', jsonParser, function(req, res){
|
||||
if (!keycheck(req, res))
|
||||
return;
|
||||
|
||||
events.emit("login-response", req.body);
|
||||
events.emit("channel-recv", req.body);
|
||||
|
||||
res.end();
|
||||
});
|
@ -1,4 +1,3 @@
|
||||
|
||||
// minetest mod related api endpoints
|
||||
require("./auth_collector");
|
||||
require("./messages");
|
||||
require("./channel");
|
||||
|
@ -1,42 +0,0 @@
|
||||
|
||||
const app = require("../../app");
|
||||
const events = require("../../events");
|
||||
const store = require("../../store");
|
||||
const keycheck = require("./keycheck");
|
||||
|
||||
const bodyParser = require('body-parser')
|
||||
const jsonParser = bodyParser.json()
|
||||
|
||||
// web -> mod
|
||||
app.get('/api/minetest/messages', function(req, res){
|
||||
if (!keycheck(req, res))
|
||||
return;
|
||||
|
||||
/*
|
||||
possible message commands:
|
||||
|
||||
{ type: "send", mail: { src,dst,subject,body } }
|
||||
{ type: "mark-read", data: {} }
|
||||
{ type: "mark-unread", data: {} }
|
||||
{ type: "delete", data: {} }
|
||||
*/
|
||||
|
||||
setTimeout(function(){
|
||||
res.json([]);
|
||||
//res.json([{ type: "send", mail: { src:"admin", dst: "testuser", subject: "blah", body: "x\ny\nz" } }]);
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
// mod -> web
|
||||
app.post('/api/minetest/messages', jsonParser, function(req, res){
|
||||
if (!keycheck(req, res))
|
||||
return;
|
||||
|
||||
console.log(req.body);
|
||||
|
||||
store.messages = req.body;
|
||||
events.emit("messages-update", req.body);
|
||||
|
||||
res.end()
|
||||
});
|
||||
|
@ -4,21 +4,26 @@ const events = require("../events");
|
||||
|
||||
module.exports = (username, password) => new Promise(function(resolve, reject){
|
||||
|
||||
events.emit("login", {
|
||||
name: username,
|
||||
password: password
|
||||
events.emit("channel-send", {
|
||||
type: "auth",
|
||||
data: {
|
||||
name: username,
|
||||
password: password
|
||||
}
|
||||
});
|
||||
|
||||
function handleEvent(result){
|
||||
events.removeListener("login-response", handleEvent);
|
||||
clearTimeout(handle);
|
||||
resolve(result);
|
||||
if (result.type == "auth" && result.data && result.data.name == username){
|
||||
events.removeListener("channel-recv", handleEvent);
|
||||
clearTimeout(handle);
|
||||
resolve(result.data);
|
||||
}
|
||||
}
|
||||
|
||||
events.on("login-response", handleEvent);
|
||||
events.on("channel-recv", handleEvent);
|
||||
|
||||
var handle = setTimeout(function(){
|
||||
events.removeListener("login-response", handleEvent);
|
||||
events.removeListener("channel-recv", handleEvent);
|
||||
reject("mod-comm timeout");
|
||||
}, 2500);
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user