diff --git a/.env b/.env index d085537..1afc1dd 100644 --- a/.env +++ b/.env @@ -1,7 +1,8 @@ # aliases build(){ $(pwd)/util/xrforge.sh build; } +rebuild(){ $(pwd)/util/xrforge.sh rebuild; } -# server hook +# forgejo server hook #test "$GITEA_ROOT_URL" = "https://xrforge.isvery.ninja/" && { # cp -r example/assets/* . #} diff --git a/.gitignore b/.gitignore index 425ba6d..3377cf3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -experiences +experienceA +experienceB result +src/v +janusweb* diff --git a/README.md b/README.md index d65ff7b..19848b7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,3 @@ - -``` -$ source .env # not needed if your system has autoenv -$ util/xrforge.sh build # fast: repeatable build -$ nix-build # slow: reproducable nix build (for experts) -``` - NOTES: ``` @@ -44,6 +37,21 @@ curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=name' -H curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=recent' -H 'accept: application/vnd.manyfold.v0+json' ``` +## Developing + +```bash +$ source .env # not needed if your system has autoenv +$ build # fast: repeatable build +$ rebuild # faster: lua-repeatable build +$ nix-build # slow: reproducable nix build (for experts) +``` + +Then run with debug flag to see output of `trace()` calls +```bash +$ DEBUG=1 result/bin/xrforge.com +``` + + ## Permacomputing Spirit * developed on [Dillo](https://dillo-browser.github.io/) as lowest common denominator / compatibility diff --git a/src/.args b/src/.args index 2d471fe..fcc4b4b 100644 --- a/src/.args +++ b/src/.args @@ -1,8 +1,8 @@ -H -"Access-Control-Allow-Origin: *" +Access-Control-Allow-Origin: * -H -"Access-Control-Allow-Method: GET, POST, OPTIONS" +Access-Control-Allow-Method: GET, POST, OPTIONS -H -"Allow: GET, POST, OPTIONS" +Allow: GET, POST, OPTIONS -D . diff --git a/src/.init.lua b/src/.init.lua index ee6fe90..f53711b 100644 --- a/src/.init.lua +++ b/src/.init.lua @@ -2,6 +2,7 @@ package.path = package.path .. ";.lua/?.lua" package.path = package.path .. ";src/.lua/?.lua" package.path = package.path .. ";middleware/?.lua" package.path = package.path .. ";cmd/?.lua" +package.path = package.path .. ";plugin/?.lua" print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging from now on @@ -12,27 +13,30 @@ json = require "json" HidePath('/usr/share/zoneinfo/') HidePath('/usr/share/ssl/') -dir = require "opendirectory" - -- create app = require("soakbean") { bin = "./xrforge.com", bindir = arg[-1]:gsub("xrforge.com",""), - cache = {}, + cache = { items = {} }, + plugin = {}, opts = {}, cmd={ - help = {file="cmd/help.lua", info="display all possible flags"}, - server = {file="cmd/server.lua", info="starts the webserver"} + help = {file="cmd/help.lua", info="display all possible flags"}, + server = {file="cmd/server.lua", info="starts the webserver"}, + createplugin = {file="cmd/createplugin.lua", info="generates skeleton plugin"} } } -- api skeleton (implemented via soakbean's app.on(...)) app.set = function(k,v) end app.init = function() end +app.scan = require('scan').scan -- load config app.opts = require('config')(app) +-- load plugins +app.plugin.janusxr = require('plugin/janusxr') app.runcmd(app) -- run clicmds @@ -62,16 +66,17 @@ app -- .use( app.response() ) -- try serve app response (if any) .use( function(req,next) Route() end) -- fallback default redbean fileserver -function OnHttpRequest() app.run() end - app.on('init', function() if assert(unix.fork()) == 0 then - os.execute("ls ; sleep 5") + --os.execute("ls ; sleep 5") + app.scan() print("ok") --local ok, headers, body = Fetch('http://127.0.0.1:8080/test') end end) +function OnHttpRequest() app.run() end + OnServerStart = function() app.init() -- this triggers init event end diff --git a/src/.lua/config.lua b/src/.lua/config.lua index 56fb305..63c9f53 100644 --- a/src/.lua/config.lua +++ b/src/.lua/config.lua @@ -36,7 +36,23 @@ return function(app) if k == 'app.opts' then app.opts = v app.opts.updated = os.time() - local newconfig = "app.on('init', function()\n\tprint('config loaded')\nend)\n\nreturn " .. util.dump(app.opts) + local newconfig = [[ + +-- TIP: run 'xrforge.com createplugin' to start writing plugins + +app.on('init', function() + print('config loaded') + + local scan = app.scan + app.scan = function() + os.execute("echo scan before-hook") + scan() + os.execute("echo scan after-hook") + end + +end) + + ]] .. "\n\nreturn " .. util.dump(app.opts) newconfig = newconfig:gsub("^}$"," init = function() print('xrforge.lua config loaded') end\n}") util.writefile( app.bindir .. configfile, newconfig ) print("stored xrforge.lua") diff --git a/src/.lua/opendirectory.lua b/src/.lua/opendirectory.lua index d56ca98..4f121c2 100644 --- a/src/.lua/opendirectory.lua +++ b/src/.lua/opendirectory.lua @@ -46,9 +46,10 @@ dir.foreach = function(path,cb) if name == "." then goto continue end attr,err = unix.stat(path..'/'..name, unix.AT_SYMLINK_NOFOLLOW) if err then print("error: ".. name .. ": " ..err) end - cb(path,stat,state,name,kind,ino,off,attr) + cb(path,stat,state,name,kind,ino,off,attr, path .. "/" ..name) ::continue:: end + return state end dir.serve = function(folder) -- raw redbean function (use dir.foreach in soakbean) @@ -97,7 +98,7 @@ dir.serve = function(folder) -- raw redbean function (use dir.foreach in soakbea Write('\r\n') local stat, state = dir.scan(path) - dir.foreach(path, function(path,stat,state,name,kind,ino,off,attr) + dir.foreach(path, function(path,stat,state,name,kind,ino,off,attr,fullpath) Write('\r\n') Write('') local link = EscapeHtml(name) diff --git a/src/.lua/plugin b/src/.lua/plugin new file mode 120000 index 0000000..041aad1 --- /dev/null +++ b/src/.lua/plugin @@ -0,0 +1 @@ +../plugin \ No newline at end of file diff --git a/src/.lua/scan.lua b/src/.lua/scan.lua new file mode 100644 index 0000000..742f936 --- /dev/null +++ b/src/.lua/scan.lua @@ -0,0 +1,50 @@ +local dir = require "opendirectory" + +local scan +scan = { + + shouldHideFile = function(name) + return name == ".." + or name == "util" -- repository + or name == ".git" -- files + or name == "src" -- + or name == "result" -- + end, + + scan = function() + -- scan dirs + local dref = dir.foreach( app.opts.disk_dir, function(path,stat,state,name,kind,ino,off,attr,fullpath) + if kind == unix.DT_DIR and not scan.shouldHideFile(name) then + local experience = { + path = fullpath, + name = name, + tag = {}, + file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path} + } + app.pub("scan_experience", nil, experience) + print("ℹ️ ".. fullpath) + + -- scan files + local fref = dir.foreach(fullpath, function(path,stat,state,name,kind,ino,off,attr, fullpath) + if kind ~= unix.DT_DIR and not scan.shouldHideFile(name) then + experience.file = {stat=stat,state=state,kind=kind,ino=ino,off=off,attr=attr,fullpath=fullpath,name=name,path=path} + app.pub("scan_experience_file", nil, experience) + local size = attr:size() + trace("ℹ️ ".. fullpath) + end + end) + fref:close() + end + end) + dref:close() + + foreach( app.cache.items, function(k,v) + -- remove file attributes (which are closed already) + v.file.stat = nil + v.file.attr = nil + v.file.state = nil + end) + end +} + +return scan diff --git a/src/.lua/soakbean.lua b/src/.lua/soakbean.lua index 13ff4d7..06a9d4e 100644 --- a/src/.lua/soakbean.lua +++ b/src/.lua/soakbean.lua @@ -197,9 +197,9 @@ sb = { for k,v in pairs(argv) do app.opts[ v:gsub("=.*","") ] = v:gsub(".*=","") end - for k,v in pairs(app.opts) do - if app.cmd[k] then - local file = app.cmd[k].file + for k,v in pairs(argv) do + if app.cmd[v] then + local file = app.cmd[v].file return require( file:sub(0,-5) )(app,argv) end end diff --git a/src/.lua/util.lua b/src/.lua/util.lua index 2838101..34f77b7 100644 --- a/src/.lua/util.lua +++ b/src/.lua/util.lua @@ -69,6 +69,16 @@ util.template = function(content,data) return app.tpl( content, mdata ) end +util.contains = function(tab,k_or_v) + local result = false + foreach( tab, function(k,v) + if k == k_or_v or v == k_or_v then + result = true + end + end) + return result +end + util.memoize = function(fn, ttl_seconds) local cached_results = nil local last_executed = 0 diff --git a/src/cmd/createplugin.lua b/src/cmd/createplugin.lua new file mode 100644 index 0000000..f113546 --- /dev/null +++ b/src/cmd/createplugin.lua @@ -0,0 +1,48 @@ +return function(app,argv) + print( [[ + +-- +-- 1. put the code below in a file ('xrforge.myplugin.lua' e.g.) +-- 2. make sure to require it at the top of xrforge.lua (require('xrforge.myplugin') e.g.) +-- 3. profit! +-- + +app.on('scan_experience', function(exp) + print_r(exp) +end) + +app.on('scan_experience_file', function(exp) + print_r(exp) +end) + +app.on('set', function(k,v) + print("content was updated") +end) + +-- plugin UI +local plugin = { + name = "MyPlugin", + info = "description / usage of your plugin goes here (html supported)", + opts = { + { + key='app.opts.plugin_myplugin_foo', + title='foo title', + info='Enable or disable foo', + default="yes", + values={yes="yes",no="no",maybe="maaaaybe"} + }, + { + key='app.opts.plugin_myplugin_bar', + title='foo title', + info='Name your bar', + default="bar", + values="" + } + } +} + +return plugin + +]] ) + os.exit() +end diff --git a/src/css/xrforge.css b/src/css/xrforge.css index 0d5e160..f49418a 100644 --- a/src/css/xrforge.css +++ b/src/css/xrforge.css @@ -235,6 +235,15 @@ img.icon{ display:none; } font-weight: normal; } +b.tag{ + display:inline-block; + background: #77f; + color: white; + font-size: 12px; + padding: 1px 4px; + border-radius: 5px; +} + /* progressive enhancement quirks */ .spectrum{ display:none } diff --git a/src/css/xrforge.max.css b/src/css/xrforge.max.css index 53893e0..8f177f5 100644 --- a/src/css/xrforge.max.css +++ b/src/css/xrforge.max.css @@ -39,11 +39,12 @@ body{ } .grid .tile .text{ - padding: 10px 20px 10px 20px !important; + padding: 10px 20px 10px 15px !important; + font-size:14px; } .grid .tile img.icon{ - transform: translate(0,5px); + transform: translate(0,-3px); } .clear{ @@ -69,11 +70,11 @@ img.icon{ iframe#viewer{ position: fixed; width: 100%; - height: calc( 100vh - 59px); + height: calc( 99.7vh - 59px); margin-top: 0px; background: #FFF; left: 0; - top: 70px; + top: 62px; } /* telescopic text */ diff --git a/src/page/admin/index.html b/src/page/admin/index.html index 152920e..3f02bee 100644 --- a/src/page/admin/index.html +++ b/src/page/admin/index.html @@ -56,7 +56,7 @@ ${notification} Keywords - + Space-separated, lua patterns are allowed @@ -145,12 +145,16 @@ ${notification} -
-
-
- -
-
+ + +
+
+ ${plugins} + +
+
+ +
diff --git a/src/page/admin/index.lua b/src/page/admin/index.lua index 47f102d..69793d2 100644 --- a/src/page/admin/index.lua +++ b/src/page/admin/index.lua @@ -1,24 +1,77 @@ local util = require("util") local data = util.merge({notification=''},{ opts = app.opts }) +data.plugins = "" -if GetMethod() == 'POST' then - local params = GetParams() - local saveconfig = false - foreach(params, function(k,v) - if v[1]:match("^opts.") then - app.opts[ v[1]:gsub("^opts.","") ] = v[2] -- update - saveconfig = true - end - end) - if saveconfig then - app.set('app.opts', app.opts ) - data.notification = '
updated
' +function pluginInput(opt) + local html = "" + html = html .. [[ + + + ]] + html = html .. opt.title + html = html .. [[ + + + ]] + if type(opt.values) == 'table' then + html = html .. util.template('\n" else - data.notification = '
no changes detected
' + html = html .. util.template('',{ + key = opt.key, + value = app.opts[key] + }) + end + html = html .. util.template([[ + + ${info} + + + ]],opt) + return html +end + +function pluginUI(name,plugin) + local plugindata = util.flatten(plugin,"plugin") + plugindata.opts = "" + foreach(plugin.opts, function(k,opt) + plugindata.opts = plugindata.opts .. pluginInput(opt) + end) + print_r(plugindata) + data.plugins = data.plugins .. util.template( LoadAsset('/page/admin/plugin.html'), plugindata) +end + +function saveData() + if GetMethod() == 'POST' then + local params = GetParams() + local saveconfig = false + foreach(params, function(k,v) + if v[1]:match("^opts.") then + app.opts[ v[1]:gsub("^opts.","") ] = v[2] -- update + saveconfig = true + end + end) + if saveconfig then + app.set('app.opts', app.opts ) + data.notification = '
updated
' + else + data.notification = '
no changes detected
' + end end end +foreach( app.plugin, pluginUI ) +saveData() local html = util.template( LoadAsset("page/admin/index.html"),data) diff --git a/src/page/admin/plugin.html b/src/page/admin/plugin.html new file mode 100644 index 0000000..2149fea --- /dev/null +++ b/src/page/admin/plugin.html @@ -0,0 +1,21 @@ +
+
${plugin.name} plugin
+
+
+ + + + + + + + + ${opts} +
+ + +
${plugin.info}

+
+ +
+
diff --git a/src/page/experiences.html b/src/page/experiences.html index 566a53b..d238e28 100644 --- a/src/page/experiences.html +++ b/src/page/experiences.html @@ -6,21 +6,6 @@ ${header}
-
- button -
- -
- button primary -
- -
- button secondary -
-
-
-
-
diff --git a/src/page/index.html b/src/page/index.html index 67aa4de..c702d94 100644 --- a/src/page/index.html +++ b/src/page/index.html @@ -1,5 +1,5 @@ ${header} -