diff --git a/src/.init.lua b/src/.init.lua
index 98f0cf3..8ef02e8 100644
--- a/src/.init.lua
+++ b/src/.init.lua
@@ -14,7 +14,7 @@ HidePath('/usr/share/ssl/')
-- create
app = require("soakbean") {
- cache = { items = {}, url = {} },
+ cache = { items = {}, url = {}, shared = require('sharedcache') },
plugin = {},
opts = {},
forkpids = {},
@@ -52,7 +52,6 @@ app = require("soakbean") {
end
}
-util.fetchInstall(app) -- setup internal fetch callback mechanism
-- api skeleton (implemented via soakbean's app.on(...))
app.set = function(k,v) end
@@ -61,6 +60,8 @@ app.init = function() end
-- load config
app.opts = require('config')(app)
+util.fetchInstall(app) -- setup internal fetch callback mechanism
+
-- load system plugins (in specific order for admin panel)
app.plugin.global = app.addPlugin('plugin/global')
app.plugin.pubnix = app.addPlugin('plugin/pubnix')
@@ -78,8 +79,11 @@ app.plugin.peertube = app.addPlugin('plugin/peertube')
app.plugin.git = app.addPlugin('plugin/git')
app.plugin.webhook = app.addPlugin('plugin/webhook')
app.plugin.archive = app.addPlugin('plugin/archive')
+--app.plugin.scheduler = app.addPlugin('plugin/scheduler') -- should always be second last
app.plugin.staticwebgen = app.addPlugin('plugin/staticwebgen') -- should always be last
+if unix.getpid() == 0 then unix.exit(0) end -- child-threads shall not pass beyond here
+
app.runcmd(app) -- run clicmds
print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging from now on
diff --git a/src/.lua/sharedcache.lua b/src/.lua/sharedcache.lua
new file mode 100644
index 0000000..ac5f57b
--- /dev/null
+++ b/src/.lua/sharedcache.lua
@@ -0,0 +1,29 @@
+local sqlite3 = require 'lsqlite3'
+local sharedcache = { db= sqlite3.open( arg[-1] .. ".sqlite3" ) }
+
+-- sqlite3 will be used for IPC data
+sharedcache.update = function(t)
+ print_r(t)
+ local stmt = sharedcache.db:prepare("UPDATE sharedcache SET json = ?;")
+ local jsonstr, err = EncodeJson(t)
+ if err then
+ print_r(t)
+ return print("sharecache.update(): " .. err)
+ end
+ stmt:bind_values( jsonstr )
+ stmt:step()
+ stmt:finalize()
+end
+
+sharedcache.get = function()
+ for row in sharedcache.db:nrows("SELECT * FROM sharedcache;") do
+ print_r(row)
+ return DecodeJson( row.json )
+ end
+ return {empty=true}
+end
+sharedcache.db:exec('CREATE TABLE sharedcache ( id INTEGER PRIMARY KEY, json TEXT);')
+sharedcache.db:exec("INSERT INTO sharedcache (id,json) VALUES(1, NULL);")
+sharedcache.update({}) -- weird: for some reason the NULL + this update is needed for proper json
+
+return sharedcache
diff --git a/src/.lua/util.lua b/src/.lua/util.lua
index 36b22ae..3e76852 100644
--- a/src/.lua/util.lua
+++ b/src/.lua/util.lua
@@ -310,21 +310,27 @@ function util.pluck(path, tbl, default)
end
function util.fetchInstall(app)
- app.expectedreq = {}
+ app.cache.expectedreq = {}
app.post('/bus',function(req,res,next)
local reqid = GetParam('id')
- local cb = app.expectedreq[reqid]
- if cb then cb(res,req,next) end -- important: flipped req,res
+ local cb = app.cache.expectedreq[reqid]
+ if cb then
+ cb(res,req,next) -- important: flipped req,res
+ app.cache.expectedreq[reqid] = nil
+ else
+ trace("util.fetch: reqid not found " .. reqid .. " pid=" .. unix.getpid())
+ end
end)
end
function util.fetchServer(url, opts, cb)
- util.fetch( util.getURL(url,{server=true}), nil, cb )
+ util.fetch( util.getURL(url, {server=true}), opts, cb )
end
function util.fetch(url, opts, cb)
- local reqid = tostring( Rdrand() )
- app.expectedreq[reqid] = cb
+ local reqid = tostring( math.abs(Rdrand()) )
+ print("reqid = " .. reqid .. " pid:" .. unix.getpid() )
+ app.cache.expectedreq[reqid] = cb
local urlret = util.getURL('/bus?id=' .. reqid, {server=true} )
if assert(unix.fork()) == 0 then
print('🤞 fetch ' .. url)
@@ -367,4 +373,42 @@ util.queueServerRequest = function(method, path, params, host, headers, body, sc
Route = function() end
end
+function util.traverse(arr, cb, key, parent)
+ if key == nil then key = 'children' end
+ foreach( arr, function(k,child)
+ if type(child) == 'table' then
+ cb(child,parent)
+ if type(child[key]) == 'table' then
+ if child[key][1] then
+ util.traverse( child[key], cb, key, child )
+ end
+ end
+ end
+ end)
+end
+
+function util.traverseXML( parsedXml, cb)
+ util.traverse( parsedXml,
+ function(node, parent)
+ local struct = { tag = '?', prop = {} }
+ if node['name'] ~= nil then struct['tag'] = node:name():lower() end
+ foreach( node['___props'], function(k,v) -- move to props array
+ local val = node[ '@' .. v['name'] ]
+ if type(val) == "table" then
+ foreach( val, function(k,v) val = v end) -- pick last
+ end
+ struct['prop'][ v['name'] ] = val
+ end)
+ if parent ~= nil then
+ struct['parent'] = (function(parent)
+ return function() return parent end
+ end)(parent)
+ end
+ cb(struct,node)
+ end,
+ '___children'
+ )
+end
+
+
return util
diff --git a/src/.lua/xmlSimple.lua b/src/.lua/xmlSimple.lua
new file mode 100644
index 0000000..c991492
--- /dev/null
+++ b/src/.lua/xmlSimple.lua
@@ -0,0 +1,174 @@
+---------------------------------------------------------------------------------
+---------------------------------------------------------------------------------
+--@LIB
+-- xml.lua - XML parser for use with the Corona SDK.
+-- https://github.com/Cluain/Lua-Simple-XML-Parser
+--
+-- version: 1.2
+--
+-- CHANGELOG:
+--
+-- 1.2 - Created new structure for returned table
+-- 1.1 - Fixed base directory issue with the loadFile() function.
+--
+-- NOTE: This is a modified version of Alexander Makeev's Lua-only XML parser
+-- found here: http://lua-users.org/wiki/LuaXml
+--
+---------------------------------------------------------------------------------
+---------------------------------------------------------------------------------
+function newParser()
+
+ XmlParser = {};
+
+ function XmlParser:ToXmlString(value)
+ value = string.gsub(value, "&", "&"); -- '&' -> "&"
+ value = string.gsub(value, "<", "<"); -- '<' -> "<"
+ value = string.gsub(value, ">", ">"); -- '>' -> ">"
+ value = string.gsub(value, "\"", """); -- '"' -> """
+ value = string.gsub(value, "([^%w%&%;%p%\t% ])",
+ function(c)
+ return string.format("%X;", string.byte(c))
+ end);
+ return value;
+ end
+
+ function XmlParser:FromXmlString(value)
+ value = string.gsub(value, "([%x]+)%;",
+ function(h)
+ return string.char(tonumber(h, 16))
+ end);
+ value = string.gsub(value, "([0-9]+)%;",
+ function(h)
+ return string.char(tonumber(h, 10))
+ end);
+ value = string.gsub(value, """, "\"");
+ value = string.gsub(value, "'", "'");
+ value = string.gsub(value, ">", ">");
+ value = string.gsub(value, "<", "<");
+ value = string.gsub(value, "&", "&");
+ return value;
+ end
+
+ function XmlParser:ParseArgs(node, s)
+ string.gsub(s, "(%w+)=([\"'])(.-)%2", function(w, _, a)
+ node:addProperty(w, self:FromXmlString(a))
+ end)
+ end
+
+ function XmlParser:ParseXmlText(xmlText)
+ local stack = {}
+ local top = newNode()
+ table.insert(stack, top)
+ local ni, c, label, xarg, empty
+ local i, j = 1, 1
+ while true do
+ ni, j, c, label, xarg, empty = string.find(xmlText, "<(%/?)([%w_:]+)(.-)(%/?)>", i)
+ if not ni then break end
+ local text = string.sub(xmlText, i, ni - 1);
+ if not string.find(text, "^%s*$") then
+ local lVal = (top:value() or "") .. self:FromXmlString(text)
+ stack[#stack]:setValue(lVal)
+ end
+ if empty == "/" then -- empty element tag
+ local lNode = newNode(label)
+ self:ParseArgs(lNode, xarg)
+ top:addChild(lNode)
+ elseif c == "" then -- start tag
+ local lNode = newNode(label)
+ self:ParseArgs(lNode, xarg)
+ table.insert(stack, lNode)
+ top = lNode
+ else -- end tag
+ local toclose = table.remove(stack) -- remove top
+
+ top = stack[#stack]
+ if #stack < 1 then
+ error("XmlParser: nothing to close with " .. label)
+ end
+ if toclose:name() ~= label then
+ error("XmlParser: trying to close " .. toclose.name .. " with " .. label)
+ end
+ top:addChild(toclose)
+ end
+ i = j + 1
+ end
+ local text = string.sub(xmlText, i);
+ if #stack > 1 then
+ error("XmlParser: unclosed " .. stack[#stack]:name())
+ end
+ return top
+ end
+
+ function XmlParser:loadFile(xmlFilename, base)
+ if not base then
+ base = system.ResourceDirectory
+ end
+
+ local path = system.pathForFile(xmlFilename, base)
+ local hFile, err = io.open(path, "r");
+
+ if hFile and not err then
+ local xmlText = hFile:read("*a"); -- read file content
+ io.close(hFile);
+ return self:ParseXmlText(xmlText), nil;
+ else
+ print(err)
+ return nil
+ end
+ end
+
+ return XmlParser
+end
+
+function newNode(name)
+ local node = {}
+ node.___value = nil
+ node.___name = name
+ node.___children = {}
+ node.___props = {}
+
+ function node:value() return self.___value end
+ function node:setValue(val) self.___value = val end
+ function node:name() return self.___name end
+ function node:setName(name) self.___name = name end
+ function node:children() return self.___children end
+ function node:numChildren() return #self.___children end
+ function node:addChild(child)
+ if self[child:name()] ~= nil then
+ if type(self[child:name()].name) == "function" then
+ local tempTable = {}
+ table.insert(tempTable, self[child:name()])
+ self[child:name()] = tempTable
+ end
+ table.insert(self[child:name()], child)
+ else
+ self[child:name()] = child
+ end
+ table.insert(self.___children, child)
+ end
+
+ function node:properties() return self.___props end
+ function node:numProperties() return #self.___props end
+ function node:addProperty(name, value)
+ local lName = "@" .. name
+ if self[lName] ~= nil then
+ if type(self[lName]) == "string" then
+ local tempTable = {}
+ table.insert(tempTable, self[lName])
+ self[lName] = tempTable
+ end
+ table.insert(self[lName], value)
+ else
+ self[lName] = value
+ end
+ table.insert(self.___props, { name = name, value = self[name] })
+ end
+
+ return node
+end
+
+
+return {
+ newNode = newNode,
+ newParser = newParser
+}
diff --git a/src/css/xrforge.css b/src/css/xrforge.css
index 4e4feef..07a1e9c 100644
--- a/src/css/xrforge.css
+++ b/src/css/xrforge.css
@@ -182,6 +182,13 @@ h1{
font-size: 29px;
margin: 15px 0px;
}
+
+h1 a,
+h2 a,
+center a{
+ text-decoration:none;
+ border:none !important;
+}
.sym{ font-size:100px; color:#000; }
diff --git a/src/css/xrforge.max.css b/src/css/xrforge.max.css
index d196e4a..d76e161 100644
--- a/src/css/xrforge.max.css
+++ b/src/css/xrforge.max.css
@@ -32,11 +32,15 @@ body{
@media (max-width: 470px){
.menu a:nth-child(2){
- display:none;
+ display: text-decoration:none;
+ border:none;none;
}
}
@media (max-width: 890px){
+ table.footer tr td {
+ display: table-row;
+ }
.grid .tile {
width:47%;
}
@@ -62,6 +66,10 @@ body{
font-size:14px;
}
+img.icon.rss{
+ width: 14px;
+ transform: translate(0,-4px);
+}
img.icon.invert,
img.icon.link,
img.icon.play,
diff --git a/src/img/rss.svg b/src/img/rss.svg
new file mode 100644
index 0000000..9dfac0b
--- /dev/null
+++ b/src/img/rss.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/js/scraper.js b/src/js/scraper.js
new file mode 100644
index 0000000..22daf7d
--- /dev/null
+++ b/src/js/scraper.js
@@ -0,0 +1,7 @@
+function Scraper(domsel){
+ let el = document.querySelector(domsel)
+ if( !el ) throw 'scraper.js: invalid domselector'
+ alert("oi")
+}
+
+new Scraper('#scraper')
diff --git a/src/page/dir.html b/src/page/dir.html
index 5211de6..3ec2d18 100644
--- a/src/page/dir.html
+++ b/src/page/dir.html
@@ -3,9 +3,12 @@ ${header}
{if parent ~= nil and parent.parent == nil then
print([[
-
${opts.title}
+
${opts.plugin_global_title}
-
${opts.plugin_global_subtitle}
+
+ ${opts.plugin_global_subtitle}
+
+
]])
@@ -13,7 +16,7 @@ end}
{if parent ~= nil and parent.parent ~= nil then
print([[
- // ${parent.name}
+ // ${parent.name}
]])
end}
diff --git a/src/page/dir.lua b/src/page/dir.lua
index b612129..24e97f4 100644
--- a/src/page/dir.lua
+++ b/src/page/dir.lua
@@ -7,13 +7,16 @@ return {
middleware = function(req,res,next)
local rootpath = "/" .. app.opts.plugin_diskscan_dirname
local urlpath = req.url
- local isroot = urlpath == "/" or urlpath:match("^"..rootpath.."[/]?$") or urlpath == '/index.html'
+ local urlhash = urlpath .. util.dump( req.param )
+ local isroot = urlpath == "/" or urlpath:match("^"..rootpath.."[/]?$") or
+ urlpath == '/index.html' or urlpath == '/index.rss'
+ local isRSS = urlpath:match('%.rss$')
+ if isRSS then urlpath = urlpath:gsub("%.rss$","") end
res.items = {}
res.template = res.template or 'page/dir.html'
local isdir = urlpath:sub(-1) == "/"
local nslashes = util.countChar('/',urlpath)
local match = function(a,b) return util.stripPath(a) == util.stripPath(b) end
- local urlhash = urlpath .. util.dump( req.param )
local referrer = util.baseURLify( (isroot and not req.param.url) and "" or urlpath )
if (isroot or urlpath:match("^"..rootpath))
@@ -23,9 +26,11 @@ return {
local redirect = false
if not app.cache.url[ urlhash ] or util.count(req.params) > 0 then
local matchpath = urlpath
- if urlpath == '/' then matchpath = rootpath end -- generate janusxr footer for root url
+ -- generate janusxr footer / rss for root url
+ if urlpath == '/' or urlpath:match('index') then
+ matchpath = rootpath
+ end
foreach( app.cache.items, function(k,v)
- print("URL=" ..v.url)
if req.authenticated or app.plugin.moderation.isAllowed(v) then
if v.parent then
-- trace( "path = " .. util.stripPath(v.path) .. " " ..
@@ -95,6 +100,8 @@ return {
end)
if data.item then
res.template = 'page/index.html'
+ elseif isRSS then
+ res.template = 'page/rss.xml'
end
app.pub("dir", nil, {res=res,data=data, urlpath=urlpath, isroot=isroot, isdir=isdir})
if not redirect then
@@ -108,7 +115,7 @@ return {
next()
else
res.status(200)
- res.header('content-type','text/html')
+ res.header('content-type', isRSS and 'application/rss+xml' or 'text/html')
res.body( app.cache.url[ urlhash ] )
next()
end
diff --git a/src/page/rss.xml b/src/page/rss.xml
new file mode 100644
index 0000000..04f4cee
--- /dev/null
+++ b/src/page/rss.xml
@@ -0,0 +1,49 @@
+
+
+
+ ${opts.plugin_global_title}
+ ${opts.plugin_global_subtitle}
+ ${app.scheme}://${app.host}${app.opts.baseurl}
+
+ ${app.scheme}://${app.host}${app.opts.baseurl}img/backdrop.jpg
+ ${opts.plugin_global_title}
+ ${app.scheme}://${app.host}${app.opts.baseurl}
+
+ {if true then
+ print(' ' .. FormatHttpDateTime(GetDate()) .. '')
+ end}
+ XRForge
+ {if items then
+
+ function getURL(url)
+ if app.arg[1] == 'generate' or url:match("://") then
+ url = '?view=3D#janus.url=' .. EscapeFragment( url )
+ url = util.baseURLify(url)
+ else
+ url = util.stripPath( url )
+ end
+ return app.scheme .. '://' .. app.host .. app.opts.baseurl .. url
+ end
+
+ for i=1,#items do
+ local item = items[i]
+ local visible = item.view2D == nil or (item.view2D ~= nil and item.view2D ~= false)
+ if visible then
+ local thumbnail = ''
+ print([[
+ -
+ ]] .. getURL(item.url) .. [[
+ ]] .. getURL(item.url) .. [[
+ ]] .. FormatHttpDateTime(GetDate()) .. [[
+
+
+ nonadult
+
+
+ ]])
+ end
+ end
+ end}
+
+
+
diff --git a/src/plugin/archive.lua b/src/plugin/archive.lua
index 093a043..7f52996 100644
--- a/src/plugin/archive.lua
+++ b/src/plugin/archive.lua
@@ -1,4 +1,10 @@
local util = require('util')
+local xml = require('xmlSimple')
+app.on('init', function()
+ if app.opts.plugin_archive_scraper == 'enabled' then
+ app.plugin.archive.scraper.start()
+ end
+end)
app.on('scan_experience', function(exp)
--print_r(exp)
@@ -10,25 +16,36 @@ end)
app.on('set', function(k,v)
end)
-app.on('init', function()
- app.plugin.archive.waybackmachine()
-end)
-
-app.on('autobackup', function()
- app.plugin.archive.waybackmachine()
+app.on('scheduler', function(opts)
+ if opts.interval == app.opts.plugin_archive_when then
+ app.plugin.archive.waybackmachine()
+ end
end)
-- this registers the plugin UI
local plugin = {
- name = "
Archive.org",
- info = 'Notify archive.org to archive experiences.
NOTE: this only works in server mode. For static website generator-users: see Save Page Now',
+ name = "
Archiver",
+ infowide = true,
+ info = [[
+ scrape / archive experiences via waybackmachine or to local disk.
+
+ Recent log output:
+
+
+
+
+ ]],
opts = {
{
key='app.opts.plugin_archive_when',
title='When to trigger',
info='when should archive.org check this xrforge?',
default = "boot",
- values={boot="On start", boot_autobackup="On start + autobackup event"}
+ values={disabled="disabled", daily="daily"}
},
{
key='app.opts.plugin_archive_url',
@@ -38,7 +55,39 @@ local plugin = {
placeholder = "/myverse",
values=''
},
+ {
+ key='app.opts.plugin_archive_scraper',
+ title='XR Scraper',
+ info='archive external links in experiences as local copies',
+ default = os.getenv('DEBUG') and 'enabled' or "disabled",
+ values={disabled="disabled",enabled="enabled"}
+ },
+ {
+ key='app.opts.plugin_archive_scraper_depth',
+ title='XR Scraper depth',
+ info='How deep should the spatial scraper go?',
+ default = "degree1",
+ values={degree1="1st degree outbound links",degree2="1st + 2nd degree outbound links"}
+ },
+ {
+ key='app.opts.plugin_archive_scraper_rewritelinks',
+ title='XR Scraper proxy',
+ info='Local-first: serve scraped experiences instead of remote experiences (rewrites URLs)',
+ default = "enabled",
+ values={disabled="disabled",enabled="yes"}
+ }
},
+
+ url = {},
+
+ flush = function()
+ app.plugin.archive.url = {}
+ end,
+
+ add = function(url)
+ table.insert( app.plugin.archive.url, url )
+ end,
+
waybackmachine = function()
local url = 'https://web.archive.org/save/'.. app.scheme .. '://' .. app.host .. app.opts.plugin_archive_url
print("notifying archive.org: " .. url)
@@ -51,15 +100,60 @@ local plugin = {
end
end,
- url = {},
+ scraper = {
- flush = function()
- app.plugin.archive.url = {}
- end,
+ start = function()
+ local me = app.plugin.archive.scraper
+ me.scraperStart = os.time()
+ util.fetchServer('/', nil, function(req,res)
+ me.scrapeLinks( req,res, 0 )
+ end)
+ end,
- add = function(url)
- table.insert( app.plugin.archive.url, url )
- end
+ scrapeLinks = function(req, res, level)
+ local me = app.plugin.archive.scraper
+ if level < 2 then
+ foreach( me.scan, function( format, cb ) cb(req,res) end)
+ end
+ end,
+
+ loadXML = function(xml,res,obj)
+ util.traverseXML( res.xml:ParseXmlText(xml), function(node,raw)
+ if node.tag == 'link' and node.prop.url:match('[a-z]+://') then
+ local newdir = util.stripPath(
+ app.opts.plugin_diskscan_dir .. '/' ..
+ node.prop.url:gsub(".*://",'')
+ :gsub("%?.*",'')
+ :gsub("#.*",'')
+ )
+ if not util.fileExist( newdir ) then
+ util.execute('janusxr scrape "' .. node.prop.url .. '" "' .. newdir .. '" 1>&2')
+ else
+ print("already scraped: " .. node.prop.url )
+ end
+ end
+ end)
+ end,
+
+ scan = {
+
+ JML = function(req,res)
+ local me = app.plugin.archive.scraper
+ -- JML heuristic
+ if res.body:lower():match("") or res.body:lower():match("]") then
+ local JML0
+ local JML
+ res.xml = xml.newParser()
+ local xmlstr = res.body
+ -- cleanup JML0
+ local JML0 = xmlstr:gsub(".*<[Rr]oom",".*","")
+ me.loadXML( JML0, res, {} )
+ end
+ end
+ }
+
+ }
}
return plugin
diff --git a/src/plugin/clouddrive.lua b/src/plugin/clouddrive.lua
index 2a56fd4..a9e9915 100644
--- a/src/plugin/clouddrive.lua
+++ b/src/plugin/clouddrive.lua
@@ -11,27 +11,7 @@ end)
app.on('set', function(k,v)
end)
-app.on('init', function()
- local me = app.plugin.archive
- if arg[1] == 'server' then
- local code, pid = unix.fork()
- -- kill thread when main app gets CTRL-c
- unix.sigaction(unix.SIGINT, function()
- unix.exit()
- end)
- function autobackup()
- if app.opts.plugin_global_backup_trigger == 'daily' then
- app.pub('autobackup', nil, {})
- Sleep(60*24) -- 24 hours worth of seconds
- autobackup() -- recurse forever
- end
- unix.exit(0)
- end
- if assert( code) == 0 then autobackup() end -- go!
- end
-end)
-
-app.on('autobackup', function()
+app.on('autobackup', function() -- triggered by src/plugin/webhook.lua
if util.cmdExist('rclone') then
print("running RCLONE cmd sync!")
end
diff --git a/src/plugin/rssatom.lua b/src/plugin/rssatom.lua
index a79a1ba..1ea9e7e 100644
--- a/src/plugin/rssatom.lua
+++ b/src/plugin/rssatom.lua
@@ -48,8 +48,8 @@ local plugin = {
key='app.opts.plugin_rssatom_url_1',
title='RSS/Atom URL 1',
info="URL and/or Thumbnail URL (space separated)",
- default="http://mastodon.online/@lvk.rss https://pbs.twimg.com/profile_images/1604566357206601730/dlRNo-V-_400x400.jpg",
- values='http://mastodon.online/@lvk.rss https://pbs.twimg.com/profile_images/1604566357206601730/dlRNo-V-_400x400.jpg'
+ default="https://mastodon.online/@lvk.rss https://pbs.twimg.com/profile_images/1604566357206601730/dlRNo-V-_400x400.jpg",
+ values='https://mastodon.online/@lvk.rss https://pbs.twimg.com/profile_images/1604566357206601730/dlRNo-V-_400x400.jpg'
},
{
key='app.opts.plugin_rssatom_url_2',
diff --git a/src/plugin/scheduler.lua b/src/plugin/scheduler.lua
new file mode 100644
index 0000000..a3ac1b9
--- /dev/null
+++ b/src/plugin/scheduler.lua
@@ -0,0 +1,54 @@
+app.on('init', function()
+ app.plugin.scheduler.start()
+end)
+
+
+return {
+
+ name = 'Scheduler',
+ hide = true,
+
+ start = function()
+ local me = app.plugin.scheduler
+ if arg[1] == 'server' then
+ local pid, err = unix.fork()
+ -- kill thread when main app gets CTRL-c
+ if pid == 0 then -- child has zero pid
+ function periodically()
+ local check_step = os.getenv('DEBUG') and 1 or 5
+ local elapsed = { min5 = 0, hourly = 0, daily = 0, daily2 = 0, weekly = 0}
+ local intervals = {
+ min5 = 60*5, -- 5 mins worth seconds
+ hourly = 60*60, -- 1 hours worth of seconds
+ daily = 60*60*24, -- 24 hours worth of seconds
+ daily2 = 60*60*24*2, -- 24*2 hours worth of seconds
+ weekly = 60*60*24*7 -- 24*7 hours worth of seconds
+ }
+ Sleep(check_step)
+ foreach( elapsed, function(interval,time)
+ elapsed[interval] = time + check_step
+ if elapsed[interval] > intervals[interval]
+ or os.getenv('DEBUG')
+ then
+ trace("scheduler.lua: debug-event: 'scheduler' " .. interval)
+ app.pub('scheduler', nil, {interval=interval})
+ elapsed[interval]= 0 -- reset
+ end
+ end)
+ if not os.getenv('DEBUG') then
+ periodically() -- recurse forever
+ end
+ end
+ periodically() -- go!
+ else
+ -- main thread gets non-zero pid (of child)
+ unix.sigaction(unix.SIGINT, function()
+ if pid ~= nil then
+ unix.kill( pid, unix.SIGTERM)
+ end
+ unix.exit()
+ end)
+ end
+ end
+ end
+}
diff --git a/src/plugin/staticwebgen.lua b/src/plugin/staticwebgen.lua
index 63a5c1c..746ca4f 100644
--- a/src/plugin/staticwebgen.lua
+++ b/src/plugin/staticwebgen.lua
@@ -65,7 +65,7 @@ local plugin = {
generate = function()
local outdir = 'www'
local root = app.opts.plugin_diskscan_dirname
- local urls = {"/","/about", "/" .. root }
+ local urls = {"/","/about", "/" .. root, "/index.rss" }
local urlLast = ''
if util.fileExist(outdir) then
print("[!] folder 'www' was already detected..please delete it")
@@ -77,6 +77,7 @@ local plugin = {
os.execute("cd " .. outdir .. " && unzip " .. selfbinary .. " 'img/*' 'css/*' 'janusweb/*' 'shader/*'")
foreach( app.cache.items, function(k,v)
if v.dir then
+ table.insert(urls, v.url .. ".rss" )
table.insert(urls, v.url )
urlLast = v.url
end
diff --git a/src/plugin/webhook.lua b/src/plugin/webhook.lua
index 9836bbd..383006e 100644
--- a/src/plugin/webhook.lua
+++ b/src/plugin/webhook.lua
@@ -50,6 +50,13 @@ local plugin = {
info='URL to trigger when changes occur',
default="",
values=''
+ },
+ {
+ key='app.opts.plugin_event_autobackup',
+ title="Event 'autobackup'",
+ info='Trigger frequency',
+ default="daily",
+ values={min5="every 5 mins",hourly="every hour",daily="every day"}
}
}
}
diff --git a/src/util/build.sh b/src/util/build.sh
index 2a6c569..5ef4f97 100755
--- a/src/util/build.sh
+++ b/src/util/build.sh
@@ -52,7 +52,7 @@ build(){
rebuild(){
cd src
- zip -yr ../result/bin/xrforge.com .lua shader img middlware page plugin cmd css .init.lua .args test myverse
+ zip -yr ../result/bin/xrforge.com .lua js shader img middlware page plugin cmd css .init.lua .args test myverse
}
"$@"