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%X;", string.byte(c)) + end); + return value; + end + + function XmlParser:FromXmlString(value) + value = string.gsub(value, "&#x([%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 @@ + +RSS + + 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 } "$@"