From 1f4b3d52ec137479a9a33e9f2d078e43bfe934b6 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Mon, 17 Aug 2026 13:42:27 +0200 Subject: [PATCH] improved rssatom/federation/peertube plugins + ttemplates --- README.md | 3 +- src/.init.lua | 12 ++- src/.lua/config.lua | 2 + src/.lua/soakbean.lua | 10 ++- src/.lua/util.lua | 18 ++++ src/cmd/generate.lua | 2 - src/cmd/help.lua | 2 +- src/cmd/server.lua | 1 + src/css/xrforge.max.css | 8 +- src/img/download.svg | 16 ++++ src/img/drive.svg | 22 +++++ src/img/link.svg | 20 +++++ src/myverse/janusxr/helloworld/janusxr.xml | 5 -- .../{janusxr.png => janusxr_tutorial.png} | Bin src/myverse/janusxr_tutorial/AFRAME.jpg | Bin 0 -> 33229 bytes src/myverse/janusxr_tutorial/AFRAME/README.md | 6 ++ .../janusxr_tutorial/AFRAME/index.html | 12 +++ src/myverse/janusxr_tutorial/AFRAME/tree2.glb | Bin 0 -> 30848 bytes src/myverse/janusxr_tutorial/helloworld.jpg | Bin 0 -> 21225 bytes .../janusxr_tutorial/helloworld/README.md | 5 ++ .../janusxr_tutorial/helloworld/janusxr.xml | 10 +++ .../{janusxr => janusxr_tutorial}/tronverse | 0 .../tronverse.png | 0 src/myverse/tronverse/tron/README.md | 9 +- src/myverse/tronverse/tron/tron.glb | Bin 62724 -> 66712 bytes src/myverse/x3d/test_world.x3d | 52 ----------- src/myverse/x3d/test_world.x3d.glb | Bin 1724 -> 0 bytes src/page/about.html | 14 +-- src/page/admin/index.lua | 3 +- src/page/dir.html | 46 +++++----- src/page/dir.lua | 48 +++++----- src/page/footer.html | 17 ++-- src/page/header.html | 4 +- src/page/index.html | 13 ++- src/page/janusxr.html | 43 ++++----- src/plugin/diskscan.lua | 3 +- src/plugin/federation.lua | 50 +++++------ src/plugin/git.lua | 3 +- src/plugin/global.lua | 54 ++++++++++- src/plugin/janusxr.lua | 24 +++-- src/plugin/manyfold.lua | 1 + src/plugin/peertube.lua | 85 ++++++++++++++++++ src/plugin/rssatom.lua | 85 ++++++++++++++++++ src/plugin/staticwebgen.lua | 68 ++++++++------ src/plugin/webhook.lua | 1 + src/test/main.lua | 12 +-- src/util/xrforge.sh | 1 + 47 files changed, 553 insertions(+), 237 deletions(-) create mode 100644 src/img/download.svg create mode 100644 src/img/drive.svg create mode 100644 src/img/link.svg delete mode 100644 src/myverse/janusxr/helloworld/janusxr.xml rename src/myverse/{janusxr.png => janusxr_tutorial.png} (100%) create mode 100644 src/myverse/janusxr_tutorial/AFRAME.jpg create mode 100644 src/myverse/janusxr_tutorial/AFRAME/README.md create mode 100644 src/myverse/janusxr_tutorial/AFRAME/index.html create mode 100644 src/myverse/janusxr_tutorial/AFRAME/tree2.glb create mode 100644 src/myverse/janusxr_tutorial/helloworld.jpg create mode 100644 src/myverse/janusxr_tutorial/helloworld/README.md create mode 100644 src/myverse/janusxr_tutorial/helloworld/janusxr.xml rename src/myverse/{janusxr => janusxr_tutorial}/tronverse (100%) rename src/myverse/{janusxr => janusxr_tutorial}/tronverse.png (100%) delete mode 100644 src/myverse/x3d/test_world.x3d delete mode 100644 src/myverse/x3d/test_world.x3d.glb create mode 100644 src/plugin/peertube.lua create mode 100644 src/plugin/rssatom.lua diff --git a/README.md b/README.md index 73e4911..359e279 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ XRForge is a tiny, powerful tool to interconnect XR experiences via 3D portals...like a federated viverse.com

-An open, seamless XR web built from simple URLs and directories, without gatekeepers.
+An open, seamless XR web built from simple URLs and directories, without gatekeepers, built upon Next Generation Internet (NGI) specs:

+
diff --git a/src/.init.lua b/src/.init.lua index 0dd58f4..8ac293b 100644 --- a/src/.init.lua +++ b/src/.init.lua @@ -14,8 +14,6 @@ HidePath('/usr/share/ssl/') -- create app = require("soakbean") { - bin = "./xrforge.com", - bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/"), cache = { items = {}, url = {} }, plugin = {}, opts = {}, @@ -62,11 +60,13 @@ app.plugin.moderation = app.addPlugin('plugin/moderation') app.plugin.xrfragments = app.addPlugin('plugin/xrfragments') app.plugin.janusxr = app.addPlugin('plugin/janusxr') app.plugin.clouddrive = app.addPlugin('plugin/clouddrive') +app.plugin.markdown = app.addPlugin('plugin/markdown') +app.plugin.rssatom = app.addPlugin('plugin/rssatom') app.plugin.federation = app.addPlugin('plugin/federation') app.plugin.manyfold = app.addPlugin('plugin/manyfold') +app.plugin.peertube = app.addPlugin('plugin/peertube') app.plugin.git = app.addPlugin('plugin/git') app.plugin.webhook = app.addPlugin('plugin/webhook') -app.plugin.markdown = app.addPlugin('plugin/markdown') app.plugin.staticwebgen = app.addPlugin('plugin/staticwebgen') -- should always be last app.runcmd(app) -- run clicmds @@ -92,7 +92,11 @@ app.post('^/save', function(req,res,next) -- setup inline POST endpoint end) app -- -.use( require("middleware/httpauth")() ) +.use( require("middleware/httpauth")({ + user = app.opts.admin_user, + pass = app.opts.admin_pass, + path = "^/admin" +})) .use( require("json").middleware() ) -- try plug'n'play json API middleware .use( require('page/dir').middleware ) -- directory browser .use( app.router( app.url ) ) -- try url router diff --git a/src/.lua/config.lua b/src/.lua/config.lua index 87b5fd1..df9874f 100644 --- a/src/.lua/config.lua +++ b/src/.lua/config.lua @@ -7,6 +7,8 @@ return function(app) baseurl="/", admin_title = "admin panel", admin_subtitle = "Manage your XR experiences here", + admin_user = "admin", + admin_pass = "admin" } local configfile = "xrforge.lua" diff --git a/src/.lua/soakbean.lua b/src/.lua/soakbean.lua index 5d15066..3ce5672 100644 --- a/src/.lua/soakbean.lua +++ b/src/.lua/soakbean.lua @@ -181,7 +181,8 @@ sb = { run = function(req) local next = function() end local k = 0 - local req = { + local internal = req + req = req or { param={}, method=GetMethod(), host=GetHost(), @@ -195,7 +196,7 @@ sb = { _header={}, _body="" } - local params = GetParams() + local params = internal and {} or GetParams() if params ~= nil then for i,p in pairs(params) do req.param[ p[1] ] = p[2] end end @@ -238,6 +239,7 @@ sb = { response = function() return function(req,res,next) + sb.pub('response', {req=req, res=res, next=next}) if res._status ~= nil then SetStatus(res._status) for k,v in pairs(res._header) do @@ -293,7 +295,7 @@ sb = { end print("\nUsage: " .. app.bin .. " [opts]\n\n") for k,v in pairs(app.cmd) do - print("\t" .. app.bin .. " " .. k .. "\t\t" .. v.info ) + print( string.format("\t%s %-15s %s", app.bin, k, v.info ) ) end print("") os.exit() @@ -304,6 +306,8 @@ sb = { return function(data) local app = {} setmetatable(app,sb) + sb.bin = arg[-1]:gsub(".*/","") + sb.bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/") sb.app = app sb.data = data sb.data.url = {} diff --git a/src/.lua/util.lua b/src/.lua/util.lua index 174b2e9..4a41541 100644 --- a/src/.lua/util.lua +++ b/src/.lua/util.lua @@ -96,6 +96,8 @@ util.template = function(content,data) mdata.url = mdata.url or util.getURL() mdata.title = mdata.title or app.opts.subtitle mdata.header_include = mdata.header_include or '' + mdata.footerlinks = {} + app.pub('util.template', nil, mdata) mdata.header = app.tpl( LoadAsset('page/header.html'), mdata ) if not mdata.janusxr then mdata.janusxr = app.tpl( LoadAsset('page/janusxr.html'), mdata ) @@ -328,5 +330,21 @@ function util.stripPath(str) return str:gsub("^./",""):gsub("^/",""):gsub("/$","") end +-- WARNING: this is a destructive function which renders the redbean server inactive +-- only to be used in tests or staticwebgen plugin +util.queueServerRequest = function(method, path, params, host, headers, body, scheme, port) + GetMethod = function() return method end + GetPath = function() return path end + GetParams = function() return params or {} end -- note: { {"key","value"} } format + GetHost = function() return host or 'localhost' end + GetHeaders = function() return headers or {} end + GetScheme = function() return scheme or 'https' end + GetBody = function() return body or '' end + GetPort = function() return port or 80 end + SetStatus = function() end + Write = function() end + SetHeader = function() end + Route = function() end +end return util diff --git a/src/cmd/generate.lua b/src/cmd/generate.lua index 8d065f0..510032e 100644 --- a/src/cmd/generate.lua +++ b/src/cmd/generate.lua @@ -2,6 +2,4 @@ return function(app,argv) print('generating..') -- do nothing here, as this is handled by the src/plugin/staticwebgen.lua -- AFTER all plugins are loaded - print('TODO: disabled for now..not stable') - os.exit() end diff --git a/src/cmd/help.lua b/src/cmd/help.lua index 1f3076a..443eac3 100644 --- a/src/cmd/help.lua +++ b/src/cmd/help.lua @@ -7,7 +7,7 @@ return function(app,argv) io.write("Flags:\n\n") foreach( app.opts, function(k,v) if not k:match("/") and not k:match("^help") and not k:match("updated") then - io.write( " " .. k .. "='" .. v .. "'\n") + io.write( " " .. k .. "='" .. v:gsub("\n","") .. "'\n") end end) io.write("\n> NOTE: flags override flags in xrforge.lua configfile]\n") diff --git a/src/cmd/server.lua b/src/cmd/server.lua index e815841..ad8501f 100644 --- a/src/cmd/server.lua +++ b/src/cmd/server.lua @@ -4,6 +4,7 @@ return function(app,argv) print("starting server") if not util.contains(arg,"--nobrowser") then if arg[1] == 'admin' then + print("ℹ️ admin login/pass is " .. app.opts.admin_user .. "/" .. app.opts.admin_pass) LaunchBrowser('/admin') else LaunchBrowser('/') diff --git a/src/css/xrforge.max.css b/src/css/xrforge.max.css index 8c38e4c..76f78a4 100644 --- a/src/css/xrforge.max.css +++ b/src/css/xrforge.max.css @@ -54,11 +54,15 @@ body{ font-size:14px; } +img.icon.invert, +img.icon.link, +img.icon.play, +img.icon.download, img.icon.list-tree{ filter: invert(1); } -img.icon.play{ - filter: invert(1); +img.icon.download, +img.icon.play,{ transform: translate(0,-1px); } diff --git a/src/img/download.svg b/src/img/download.svg new file mode 100644 index 0000000..7a45f84 --- /dev/null +++ b/src/img/download.svg @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/src/img/drive.svg b/src/img/drive.svg new file mode 100644 index 0000000..1cc3be1 --- /dev/null +++ b/src/img/drive.svg @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/src/img/link.svg b/src/img/link.svg new file mode 100644 index 0000000..f8786a6 --- /dev/null +++ b/src/img/link.svg @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/src/myverse/janusxr/helloworld/janusxr.xml b/src/myverse/janusxr/helloworld/janusxr.xml deleted file mode 100644 index b5018a9..0000000 --- a/src/myverse/janusxr/helloworld/janusxr.xml +++ /dev/null @@ -1,5 +0,0 @@ -world 1 title - - - - diff --git a/src/myverse/janusxr.png b/src/myverse/janusxr_tutorial.png similarity index 100% rename from src/myverse/janusxr.png rename to src/myverse/janusxr_tutorial.png diff --git a/src/myverse/janusxr_tutorial/AFRAME.jpg b/src/myverse/janusxr_tutorial/AFRAME.jpg new file mode 100644 index 0000000000000000000000000000000000000000..272b50c8e6e18fefb804c57541a709793896887a GIT binary patch literal 33229 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 literal 0 HcmV?d00001 diff --git a/src/myverse/janusxr_tutorial/AFRAME/README.md b/src/myverse/janusxr_tutorial/AFRAME/README.md new file mode 100644 index 0000000..ed5b01a --- /dev/null +++ b/src/myverse/janusxr_tutorial/AFRAME/README.md @@ -0,0 +1,6 @@ +# AFRAME hello world + +[XRForge's](https://xrforge.isvery.ninja/) [JanusXR](https://coderofsalvation.github.io/janus-guide/)-viewer ([janusweb](https://github.com/jbaicoianu/janusweb)) can detect [AFRAME](https://aframe.io) markup in HTML/XML/even binary files(!). +This basically extends [the seamless JanusXR portalsystem](https://coderofsalvation.github.io/janus-guide/#/home/about) by interconnecting otherwise immersively-disjointed ecosystems like [AFRAME](https://aframe.io), by [reprojecting them in 3D](https://coderofsalvation.github.io/janus-guide/#/home/about). + +Currently only geometry markup and models are supported. diff --git a/src/myverse/janusxr_tutorial/AFRAME/index.html b/src/myverse/janusxr_tutorial/AFRAME/index.html new file mode 100644 index 0000000..f94228c --- /dev/null +++ b/src/myverse/janusxr_tutorial/AFRAME/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/myverse/janusxr_tutorial/AFRAME/tree2.glb b/src/myverse/janusxr_tutorial/AFRAME/tree2.glb new file mode 100644 index 0000000000000000000000000000000000000000..86f7a6e5fb3a59c90af4145bf76df1ae2b17d0fa GIT binary patch literal 30848 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 Y=wO+}4#B^NV>UYi{~jh^C_3Ez2P1c-AOHXW literal 0 HcmV?d00001 diff --git a/src/myverse/janusxr_tutorial/helloworld.jpg b/src/myverse/janusxr_tutorial/helloworld.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5c50ad127c790234a61c7abc2aa1f281ef575730 GIT binary patch literal 21225 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 JanusWeb supports various URLs, containing [JML](https://coderofsalvation.github.io/janus-guide/#/build/room), [XRFragments](https://xrfragment.org), RSS/Atom, HTML, and so on..basically an in-browser VR browser diff --git a/src/myverse/janusxr_tutorial/helloworld/janusxr.xml b/src/myverse/janusxr_tutorial/helloworld/janusxr.xml new file mode 100644 index 0000000..125930b --- /dev/null +++ b/src/myverse/janusxr_tutorial/helloworld/janusxr.xml @@ -0,0 +1,10 @@ +JanusXR hello world + + + + + + + + + diff --git a/src/myverse/janusxr/tronverse b/src/myverse/janusxr_tutorial/tronverse similarity index 100% rename from src/myverse/janusxr/tronverse rename to src/myverse/janusxr_tutorial/tronverse diff --git a/src/myverse/janusxr/tronverse.png b/src/myverse/janusxr_tutorial/tronverse.png similarity index 100% rename from src/myverse/janusxr/tronverse.png rename to src/myverse/janusxr_tutorial/tronverse.png diff --git a/src/myverse/tronverse/tron/README.md b/src/myverse/tronverse/tron/README.md index ac93bdd..171ef65 100644 --- a/src/myverse/tronverse/tron/README.md +++ b/src/myverse/tronverse/tron/README.md @@ -1,7 +1,8 @@ # Tron World 1 -This is a test +This is a glTF world which is only 65 kilobytes in size.
+Say what?
+
+> Fitting an entire virtual world into 65 KB is like trying to condense the whole Marvel Cinematic Universe into a single tweet. It’s the digital equivalent of trying to build a full-scale, functioning Disneyland inside a hollowed-out peanut shell. -> This is a blockquote - -And [this](https://xrfragment.org) is an URL. +Read more about [best practices at xrfragment.org](https://xrfragment.org/#Best%20practices) diff --git a/src/myverse/tronverse/tron/tron.glb b/src/myverse/tronverse/tron/tron.glb index d60f1339ac8544aaae4ea77fa24fe2dd46486944..0446ff4f145251c229a4613aa81a399bcb217458 100644 GIT binary patch delta 4488 zcmb7GdvH|c760~;WXU_(B;@N5H2)dVu#$XXTNGvuaVUuh^+Puha92txo zuy(Lk34V6!jLO5xjJ0KuZd*D7?1jp7>{M{9W2FN_qZM1LomQuR)SqJ)tYsP+9hvN#nutek^qdLoe||6#;I*Ea zo&YlrK9HH5u_!YG^vD>bU^akPAE!zH*>oI> z#Mp~9vO)ojVZ;#wtR7D?l&e0M_u1uCa8+aDT2zY z0W}2Oi{f>JMqFNcxBE7G(e}6}=(+q=v_2Z|qAAC*?-YoelRsw#^L{gbPKwid3X@8sTYue`E6ZZZGk zc{crfi)v`DXC2+`T|@VKY&xCyERyt2I=*DJy`o=Ua@HR4Xh}7l&41M%c~!wVd&JKQ zPS_*j8*rxZpgnSLkztRByI(9mWRLuIao;M5g?*-KA}b1+?XSoh51WwH#G4TsDY@5P z@S`OIAywd6QI=R$4JaXwmsA{M)?Dq0S{ol9iU+?XtyK*b( zF7Ix8V><6yO3N$i7AyMs3Y$e=tT8!X7VC%%#3!cUBn(L$yivfEG@g?MR!bIF z-{zpf+7OM`oN@?BuJ(e1mel3vT5V8YU2CfvU^Z;rbZ`55rj6GE%;vkctlhYA)17@R z(NIhe@!UD$>KvElW8V_YF2)RAjVO$>NPBk%eD_Z7W;og%+<1wg5fU+zNVy zOJP}XX{DvDUWY>Gn?rO{Yq?8NS-K6-w_D4dqCkBu0s8ya3YRRJxv4i$;nJ)+=w{I2 z-$6Qfxm$z>oQ__eBZ@Lavf`JyMMdH1d(8p*K+x+_WJs~Hr9zT!Xc2Z2J=h$mTEvjf zUnR&l^e6G0X0Iy+;!XOK)ti$AyT9(H*Ls?h+a8BH=KZfxcJfEP4*a;N(|I@cLjCCUv0}bmiaqTCwVU{&)|lI zzaB(fs)C~)N-E{WA*Z~tUF(I>HpB2)N(e$%0GHaSm`^|d74k?@ScfZegx{iCe8$@1e}}I%Sw-(Suw38zJ=SOd&O5&;)Q`_yPW}8)m%iiU zV`2T1cH_}oea6-zE|IGGfi=1hFAk($X0{r`?GV(pE_La#CB`db*Ng;RGqCu2YRN1QP3Bl zeSY1w(=aYQYg}f`^tf~wS1>cLaAscC1H^UKBzvIEM~aK|r6=A}>9*Bt5}^FbB3Xp@ zJrC-?e4OYjx~5Hu_kRr%eeJKOO^G<(uW7I(+S!-B45J+-+EJn%CEB0MGmlm|{&_iuJv5+-co{heQi zk#}xx!IVvjKYkHzCOO1U9K=mru)4?!U^_`UU^#&K;c@`x0&E3bXv-yDuq`1TSUsj+ z9$+rGoTLSC^z)EBSn~i~LW)Qstc504K=J|22W~6i2@Fz53Scb&I2Q~R0GE+cSW8Vy z2`L6!F<1hCW2jP60&5AN`G8jg+Cb`Itv4-oq!w(oUEGM^sfq|6)v-Z&jo8PGE!xDSVWyI(AxbG?5f~9j zVFxT)S}N_77p52_xNuPr^$*lq#YNCox^EN|XCto8y}$cAzkAO4owqO4scogOZ{TLD z%i(aS{~V5uhMB@wubtgm$7DxL8+vfBXGCjCO=v@1gFRZ4mXyF9B$zfCn}A==Ie1)D z0!yxG=u+k(s?Nb*wLiiP!5GGrqnLLhq+1FSOE5OuNNSlAS8szsh}}yFoZg{KM!ML$b85Tkp%*-?(6M+SP0zv@19Edx zhwcd5Y0jms(w%Vb(QQ6pn-he1u(WRO+*n?mj{XwVbY%H#=dlvL2QyzQD7J zsP(4ugVS2Bey6&GfJDuv9e&nS0;;XDk2BE<7_O~$Ap!e!Ro184M%B8~kWyTX!-9S> zz<5l%J3iXe-rGCUGpaRV9Z>;J67%cy#0#c6Tqe;VCns}=sS9|5>tT<0RZJPRdG^JP z4JPeyl6q*QUN_OX)R5xPkG%>vuuN)E - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/myverse/x3d/test_world.x3d.glb b/src/myverse/x3d/test_world.x3d.glb deleted file mode 100644 index 80977012960f120c091a367f648b608c67986439..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1724 zcma)7>28}q5TH zeS>yBj2$~^tE@E4F!#;@qx5wDsbLu3UmC`@7l!fSq;ouFQCX^rg;RE=beR-63z@Ju zGucSxN=H?piPIUYytNI@)8ula7aF=OF%BacXWOw^; zySvP!y^PNuq_?oS?3Bf}+=E;sJ$BUMFdn#A2=GP=yEn;5%(RI1$~E%bgA*TZ(s zE!PAzErIXGaG^qzW-`6-a>oqY*yDvm6vMXsxcD+j2U=kv?0V=Mv)xHw^-jCn$0z%p?omCy6d1d-;bL#7NjSFc3NEB4d?ygn zc73YWzXksl!b}7{c@skVZI3q6mL>(O1=I;HMBoJQVQvUZa)&!@A$->pfe>n=jghL4 zOM~Kgj??zZd_lKe8E{WR4;}y*g9cOORw^MLR3aIcYt>j$maEDmn%i;jXs?I*p=vY_ zXb$-=WBTMat!bJx{{++mD7Ajk_j~8yn4VwXHSl8vgT4>Yt95?<{sMXn`1PE(wW7bE zYaI$;JR`vW0tfgB!E*xobr|#uscGO>1UQgGh64;;@L&r)(7=#^2mi1I4GbB0IIsl` G4A~#h#@i(T diff --git a/src/page/about.html b/src/page/about.html index 01be761..1e92259 100644 --- a/src/page/about.html +++ b/src/page/about.html @@ -6,7 +6,7 @@ header h2{ font-weight: normal; } header{ - background-image: linear-gradient(0deg,#0000,#101,#0000), url(/myverse/tronverse/tron/tron.png); + background-image: linear-gradient(0deg,#0000,#101,#0000), url(myverse/tronverse/tron/tron.png); background-position-x: 0%; background-position-y: 0%; background-repeat: repeat; @@ -20,7 +20,7 @@ header{ div#hint{ float:right; - background: url(/img/blinkhint.svg); + background: url(img/blinkhint.svg); height: 129px; background-size: cover; display:inline-block; @@ -28,7 +28,7 @@ div#hint{ } div#badges{ - background: url(/img/badges.png); + background: url(img/badges.png); height: 90px; background-repeat:no-repeat; background-size: contain; @@ -39,7 +39,7 @@ div#badges{ } div#xrecosystem{ - background: #000 url(/img/xrecosystem.png); + background: #000 url(img/xrecosystem.png); background-repeat: no-repeat; height: 200px; background-size: cover; @@ -67,7 +67,7 @@ div#xrecosystem{
- + Check the sourcecode
docker run codeforge.org/coderofsalvation/xrforge:latest
@@ -79,7 +79,7 @@ div#xrecosystem{
- + chat with community
irc.isvery.ninja port 443 via ObsidianIRC
@@ -347,7 +347,7 @@ div#xrecosystem{

JanusXR: The deep spatial web (within the web)

diff --git a/src/page/admin/index.lua b/src/page/admin/index.lua index 93f6a83..ddc2bd1 100644 --- a/src/page/admin/index.lua +++ b/src/page/admin/index.lua @@ -66,7 +66,7 @@ end) function pluginUI(seen,data) return function(name,plugin) if plugin.hide or seen[ plugin.name ] then return end -- already processed - table.insert( data.tabs, plugin.name ) -- insert tabname + table.insert( data.tabs, plugin.tab or plugin.name ) -- insert tabname seen[ plugin.name ] = true local plugindata = {plugin=plugin} plugindata.opts = "" @@ -88,6 +88,7 @@ function saveData() end end) if saveconfig then + app.set('app.opts', app.opts) data.notification = '
updated
' else data.notification = '
no changes detected
' diff --git a/src/page/dir.html b/src/page/dir.html index 40238a3..029b863 100644 --- a/src/page/dir.html +++ b/src/page/dir.html @@ -23,41 +23,37 @@ end} local item = items[i] local icon = 'list-tree' local thumbnail = '' - if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment(item.thumbnail) .. ');"' end + if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. ');"' end if item.view3D or item.portal3D == false then icon = 'play' end print([[ ]]) end end} - {if parent.parent == nil then - for i=1,5 do - local urls = app.opts[ 'plugin_federation_url_' .. i] - if #urls > 0 then - local title = urls:gsub(' .*','') - local fediurl = '/?url=' .. EscapeFragment(title) - local thumburl = urls:gsub('.* ','') - local icon = 'list-tree' - local thumbnail = '' - if #thumburl > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment(thumburl) .. ');"' end - print([[ - - ]]) - end + {if itemsremote then + for i=1,#itemsremote do + local item = itemsremote[i] + local icon = 'link' + local thumbnail = '' + if #item.thumbnail > 0 then thumbnail = 'style="background-image: url(' .. EscapeFragment( util.stripPath(item.thumbnail) ) .. ');"' end + if item.view3D or item.portal3D == false then icon = 'play' end + print([[ + + ]]) end end}
diff --git a/src/page/dir.lua b/src/page/dir.lua index 132d2bd..d777100 100644 --- a/src/page/dir.lua +++ b/src/page/dir.lua @@ -6,17 +6,17 @@ return { middleware = function(req,res,next) local rootpath = "/" .. app.opts.plugin_diskscan_dirname - local urlpath = GetPath() - res.items = {} - res.template = res.template or 'page/dir.html' + local urlpath = req.url + local isroot = urlpath == "/" or urlpath:match("^"..rootpath.."[/]?$") + 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 ) - if urlpath == "/" - or urlpath:match("^"..rootpath) - and not urlpath:gsub(".*/",""):match("%.") + if (isroot or urlpath:match("^"..rootpath)) + and not urlpath:gsub(".*/",""):match("%.") -- no regular file with extension and not req.param.url then local redirect = false @@ -26,10 +26,10 @@ return { foreach( app.cache.items, function(k,v) if req.authenticated or app.plugin.moderation.isAllowed(v) then if v.parent then - trace( "path = " .. util.stripPath(v.path) .. " " .. - "parent = " .. util.stripPath(v.parent.path) .. " " .. - "matchpath = " .. util.stripPath(matchpath) - ) +-- trace( "path = " .. util.stripPath(v.path) .. " " .. +-- "parent = " .. util.stripPath(v.parent.path) .. " " .. +-- "matchpath = " .. util.stripPath(matchpath) +-- ) if match(v.path, matchpath) then -- show parent folder if any local parent = app.cache.items[ v.parent.id ] if v.portal then @@ -45,7 +45,6 @@ return { -- add items from dir if match(v.parent.path, matchpath) then if v.dir or v.view3D then - trace("-- dir item: " .. v.path ) local parent = app.cache.items[ v.parent.id ] res.items[ v.id ] = v res.parent = parent @@ -57,32 +56,35 @@ return { local data = util.merge({ url = req.param.url, -- query hint to render janus.url in iframe (index.html) - html_items='', items = {}, parent=res.parent },{ opts = app.opts }) local i = 0 foreach( res.items, function(k,v) - -- position items for janusxr JML (dir.html) + -- add extra metadata for janusxr JML (janusxr.html and dir.html) table.insert(data.items, util.merge({x = i*2, i = i},v) ) i=i+1 if v.view3D then - data.item = v - data.title = data.item.title - data.parent = v.parent - local port = ':' .. GetPort() - if port == 80 then port = '' end - data.url = GetScheme() .. '://' .. GetHost() .. port .. v.url - if v.janusxr and v.janusxr.jml then - -- what to do here? no generated portals? - data.janusxr = app.tpl( util.readfile(v.path), data ) + local isMarkupExperience = function(v) + return v.janusxr and (v.janusxr.jml or v.janusxr.aframe) + end + -- prioritize markup experience above binary 3D files as experience containers + if not data.item or (isMarkupExperience(v) and not isMarkupExperience(data.item)) then + data.item = v + data.title = data.item.title + data.parent = v.parent + data.url = app.opts.baseurl .. util.stripPath(v.url) -- because we are running in iframe + if v.janusxr and (v.janusxr.jml or v.janusxr.aframe) then + -- what to do here? no generated portals? + data.janusxr = app.tpl( util.readfile(v.path), data ) + end end end end) if data.item then res.template = 'page/index.html' end - app.pub("dir", nil, {res=res,data=data, urlpath=urlpath, isdir=isdir}) + app.pub("dir", nil, {res=res,data=data, urlpath=urlpath, isroot=isroot, isdir=isdir}) if not redirect then app.cache.url[ urlhash ] = util.templateFile( res.template, data) end diff --git a/src/page/footer.html b/src/page/footer.html index ad6f19b..b3ad9c1 100644 --- a/src/page/footer.html +++ b/src/page/footer.html @@ -17,13 +17,20 @@ NLNET
- View from XR Device
- Content overview
- About
+ View from XR Device
+ Content overview
+ About
- Sourcecode
- +
+ Sourcecode
+ {if footerlinks then + for i,v in pairs(footerlinks) do + print([[ + ]] .. v.title .. [[
+ ]]) + end + end} diff --git a/src/page/header.html b/src/page/header.html index ddf66d7..be1e8ac 100644 --- a/src/page/header.html +++ b/src/page/header.html @@ -5,7 +5,7 @@ ${opts.plugin_global_title} - + ${opts.plugin_global_headertags}
]]) @@ -31,9 +31,14 @@ end} if( url == document.location.href ) return try{ // handle external urls via ?url= let urlObj = new URL(url) - url = document.location.origin + document.location.pathname + '?url=' + url + if( document.location.origin != urlObj.origin ){ + url = document.location.origin + document.location.pathname + '?url=' + url + } }catch(e){} - window.history.replaceState( url, iframe.contentDocument.title, url ); + const isFileWithExtension = url.replace( new RegExp('./','g'),"").match("\\.") + if( !isFileWithExtension ){ + window.history.replaceState( url, iframe.contentDocument.title, url ); + } setTimeout( () => { document.title = iframe.contentDocument.title }, 1000 ) diff --git a/src/page/janusxr.html b/src/page/janusxr.html index a57e473..2c7300c 100644 --- a/src/page/janusxr.html +++ b/src/page/janusxr.html @@ -14,6 +14,13 @@ end} end end end} + {if itemsremote and #itemsremote > 0 then + for i,v in pairs(itemsremote) do + if #v.thumbnail > 0 then + print('\t') + end + end + end} {if showfediportals then for i=1,5 do local urls = app.opts[ 'plugin_federation_url_' .. i] @@ -49,9 +56,6 @@ end} end} {if not item and items and #items > 0 then - local portals = {} - for i,v in pairs(items) do table.insert(portals, v) end - if parent then table.insert(portals, p) end for i,v in pairs(items) do local width = 3.2 local center = -((#items*width)/1.3) + (i*width) @@ -65,27 +69,24 @@ end} addPortal('dir',center, v, url,i, -4, thumb, '0 0 0', '0.2 0.2 0.2') end end} - {if showfediportals and not item then - local portals = {} - for i=1,5 do - local urls = app.opts[ 'plugin_federation_url_' .. i] - if #urls > 0 then - local fediurl = urls:gsub(' .*','') - table.insert(portals, fediurl ) - end - end - if #portals > 0 then - print([[ - - ]]) - end - for i,fediurl in pairs(portals) do + {if not item and itemsremote and #itemsremote > 0 then + for i,v in pairs(itemsremote) do local width = 3.2 - local center = -((#portals*width)/1.3) + (i*width) - local thumb = ' thumb_id="federated' .. i .. '"' - addPortal('federated',center, {title=fediurl}, fediurl,i, 4, thumb, '0 180 0', '0.1 0.1 0.1') + local center = -((#itemsremote*width)/1.3) + (i*width) + local url = v.url + local thumb = ' shader_id="defaultportal"' + if #v.thumbnail > 0 then + thumb = ' thumb_id="imgremote' .. i .. '" ' + end + if v.dir then url = url .. "/" end + if v.redirect then url = v.redirect end -- dont wrap standalone glb's in janusxr + addPortal('remote',center, v, url,i, 4, thumb, '0 180 0', '0.2 0.2 0.2') end + print([[ + + ]]) end} + ${opts.plugin_janusxr_jml} diff --git a/src/plugin/diskscan.lua b/src/plugin/diskscan.lua index 219962b..f63d567 100644 --- a/src/plugin/diskscan.lua +++ b/src/plugin/diskscan.lua @@ -8,7 +8,8 @@ end) -- this registers the plugin UI /funcs return { - name = "Local Content", + name = "Files", + tab = " Files", info = '', opts = { { diff --git a/src/plugin/federation.lua b/src/plugin/federation.lua index 5105495..0705157 100644 --- a/src/plugin/federation.lua +++ b/src/plugin/federation.lua @@ -11,70 +11,70 @@ app.on('set', function(k,v) end) app.on('dir', function(opts) - local me = app.plugin.federation - if opts.urlpath == '/' then - print_r(me.portals) - --foreach( me.portals, function(k,portal) - -- print("JAAA") - -- table.insert( opts.data.items,{ - -- url = portal.url, - -- name = portal.name, - -- thumbnail = portal.thumbnail - -- }) - --end) + local showportals = app.opts.plugin_federation_strategy == 'always' or + (app.opts.plugin_federation_strategy == 'homepage' and opts.isroot) + if showportals then + opts.data.itemsremote = opts.data.itemsremote or {} + local itemsremote = opts.data.itemsremote + for i=1,5 do + local urls = app.opts[ 'plugin_federation_url_' .. i] + if #urls > 0 then + local url = urls:gsub(' .*','') + local thumburl = urls:match(" ") and urls:gsub('.* ','') or nil + table.insert(itemsremote, { + title = url:gsub(".*//",""), + thumbnail = thumburl, + url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url) + }) + end + end end end) local plugin = { name = 'Federation', + tab = " Federation", info = 'Add links to other (janus)XR(forges), and have them show up as extra portals in portalrooms', opts = { -- TODO populate this based on xrforge-federation.json - { - key='app.opts.plugin_federation_title', - title='Title', - info="Displayed above federated portals", - default="Federated neighbourhoods", - values='' - }, { key='app.opts.plugin_federation_strategy', title='Display portals', info="When to show federated portals?", - default="always", + default="homepage", values={always='always',homepage='only homepage'} }, { key='app.opts.plugin_federation_url_1', title='Federated URL 1', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="https://xrforge.isvery.ninja https://i.imgur.com/x7fzn9v.png", values='' }, { key='app.opts.plugin_federation_url_2', title='Federated URL 2', - info="URL + Thumbnail URL (space separated)", - default="https://www.janusxr.org/new/ https://archive.org/download/janusxr-1/janusxr.png", + info="URL and/or Thumbnail URL (space separated)", + default="https://www.janusxr.org https://www.janusvr.com/images/logos/logotextoverlight.png", values='' }, { key='app.opts.plugin_federation_url_3', title='Federated URL 3', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="", values='' }, { key='app.opts.plugin_federation_url_4', title='Federated URL 4', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="", values='' }, { key='app.opts.plugin_federation_url_5', title='Federated URL 5', - info="URL + Thumbnail URL (space separated)", + info="URL and/or Thumbnail URL (space separated)", default="", values='' } diff --git a/src/plugin/git.lua b/src/plugin/git.lua index 7d60bbb..27e8bc7 100644 --- a/src/plugin/git.lua +++ b/src/plugin/git.lua @@ -26,7 +26,8 @@ end) -- this registers the plugin UI local plugin = { name = 'Git', - info = '', + info = 'Git repositories will be cloned before xrforge starts scanning for experiences', + tab = " Git", opts = { { key='app.opts.plugin_git_repo_1', diff --git a/src/plugin/global.lua b/src/plugin/global.lua index f50e40e..0600680 100644 --- a/src/plugin/global.lua +++ b/src/plugin/global.lua @@ -10,6 +10,38 @@ end) app.on('set', function(k,v) end) +app.on('init', function() + if app.opts.plugin_global_credible_exit == 'platform' then + if not util.fileExist( app.bin ) then -- check for binary in local filesystem + os.execute("ln -fs " .. arg[-1] .. " .") + os.execute("ln -fs " .. arg[-1] .. " " .. app.bin:gsub(".com",".zip")) + end + else + if not util.fileExist( app.bin ) then + os.execute("test -L " .. app.bin .. " && rm " .. app.bin ) -- delete symlink + end + end +end) + +app.on('util.template', function(data) + if data.urlpath then + local isAdminPath = data.urlpath:match("^/admin") + if app.opts.plugin_global_credible_exit == 'platform' or + (app.opts.plugin_global_credible_exit == 'platformadmin' and isAdminPath) + then + data.footerlinks = data.footerlinks or {} + table.insert(data.footerlinks, { + title=" Download data", + url="/" .. app.bin:gsub(".com",".zip") + }) + table.insert(data.footerlinks, { + title=" Download data+platform", + url="/" .. app.bin + }) + end + end +end) + -- this registers the plugin UI local plugin = { name = "Global settings", @@ -27,13 +59,13 @@ local plugin = { key='app.opts.plugin_global_subtitle', title='Subtitle', info='Slogan here', - default = "Portable XR experiences for Phone, VRheadset & desktop.", + default = "Interconnected XR experiences for Phone, VRheadset & desktop.", values='' }, { key='app.opts.plugin_global_topmenu_mapname', title='Overview link', - info='Top-right link (overview page)', + info='Top-right link (overview page)', default = "map", values='' }, @@ -41,14 +73,21 @@ local plugin = { key='app.opts.plugin_global_homepage', title='Homepage', info='Display 3D or 2D experiences on homepage', - default = "2D", + default = "3D", values={ ['3D'] = '3D', ['2D'] = '2D' }, }, + { + key='app.opts.plugin_global_credible_exit', + title='Credible exit', + info='How can users reproduce experiences locally?', + default = "platform", + values={ platform="download platform+data", platformadmin="download platform+data (admin only)" } + }, { key='app.opts.plugin_global_topmenu_html', title='Menu html', info='Add extra menu items here', - default = 'about', + default = 'about', control='textarea', values='' }, @@ -64,6 +103,13 @@ local plugin = { control='textarea', values='' }, + { + key='app.opts.plugin_global_remote_title', + title='Title', + info="Displayed above remote portals", + default="Remote / Federated", + values='' + }, }, middleware = { homepage_2D_or_3D = function(req,res,next) diff --git a/src/plugin/janusxr.lua b/src/plugin/janusxr.lua index 48ae3e8..83b9024 100644 --- a/src/plugin/janusxr.lua +++ b/src/plugin/janusxr.lua @@ -2,6 +2,7 @@ local util = require('util') local pattern_fireboxroom = "[Ff][Ii][Rr][Ee][Bb][Oo][Xx][Rr][Oo][Oo][Mm][ ]?.*>" local pattern_title = "<[Tt][Ii][Tt][Ll][Ee]>(.*)<" +local pattern_aframe = " 100kb local data = util.readfile( exp.file.fullpath) if data then - if data:match(pattern_fireboxroom) then - table.insert(exp.tag, "janusxr") + if data:match(pattern_fireboxroom) or + data:match(pattern_aframe) then local title = data:match(pattern_title) if title then exp.title = title:gsub("<.*","") @@ -41,8 +42,13 @@ app.on('scan_experience_file', function(exp) app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins app.cache.items[ exp.file.ino ] = exp exp.view3D = true + exp.parent.redirect = exp.url -- mark parent to redirect to this standalone url instead (not glb assets e.g.) exp.janusxr = {} print("✅ └─ plugin/janusxr: " .. exp.file.name) + if data:match(pattern_aframe) and app.opts.plugin_janusxr_wrap_aframe == 'enabled' then + exp.janusxr.aframe = true + print("✅ └─ plugin/janusxr: AFRAME markup detected") + end end else -- symlink to directory @@ -79,31 +85,31 @@ local plugin = { }, { key='app.opts.plugin_janusxr_wrap_3dfile', - title='wrap 3D file', + title='VR wrap 3D file', info='render 3D files in janusxr.xml room', default="room_plane", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_image', - title='wrap images', + title='VR wrap images', info='render images in janusxr.xml room', default="room_plane", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_video', - title='wrap videos', + title='VR wrap videos', info='render videos in janusxr.xml room', default="room_plane", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_aframe', - title='wrap AFRAME', - info='render AFRAME markup in janusxr.xml room', - default="room_plane", - values=util.merge(rooms,{disabled="-- disabled --"}) + title='AFRAME support', + info='render AFRAME (geometry) markup when detected in URLs', + default="enabled", + values={enabled="enabled",disabled="disabled"} }, { key='app.opts.plugin_janusxr_portalroom', diff --git a/src/plugin/manyfold.lua b/src/plugin/manyfold.lua index 481a06c..625b2de 100644 --- a/src/plugin/manyfold.lua +++ b/src/plugin/manyfold.lua @@ -13,6 +13,7 @@ end) -- this registers the plugin UI local plugin = { name = "Manyfold", + tab = " Manyfold", info = '', opts = { { diff --git a/src/plugin/peertube.lua b/src/plugin/peertube.lua new file mode 100644 index 0000000..5d54ec2 --- /dev/null +++ b/src/plugin/peertube.lua @@ -0,0 +1,85 @@ +local util = require('util') + +app.on('scan_experience', function(exp) + --print_r(exp) +end) + +app.on('scan_experience_file', function(exp) +end) + +app.on('set', function(k,v) +end) + +app.on('dir', function(opts) + local showportals = app.opts.plugin_peertube_strategy == 'always' or + (app.opts.plugin_peertube_strategy == 'homepage' and opts.isroot) + if showportals then + opts.data.itemsremote = opts.data.itemsremote or {} + local itemsremote = opts.data.itemsremote + for i=1,5 do + local urls = app.opts[ 'plugin_peertube_url_' .. i] + if #urls > 0 then + local url = urls:gsub(' .*','') + local thumburl = urls:match(" ") and urls:gsub('.* ','') or nil + table.insert(itemsremote, { + title = url:gsub(".*//",""), + thumbnail = thumburl, + url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url) + }) + end + end + end +end) + +-- this registers the plugin UI +local plugin = { + name = "Peertube", + tab = " Peertube", + info = "disabled Peertube videos/channels/livestreams can be rendered as rooms in JanusWeb", + opts = { + { + key='app.opts.plugin_peertube_strategy', + title='Display portals', + info="When to show peertube portals?", + default="homepage", + values={always='always',homepage='only homepage'} + }, + { + key='app.opts.plugin_peertube_url_1', + title='Peertube URL 1', + info="URL and/or Thumbnail URL (space separated)", + default="https://tilvids.com/c/arthurpizza https://oss.iqrator.org/wp-content/uploads/2026/01/00-16-510x510.png", + values='https://tilvids.com/c/arthurpizza https://oss.iqrator.org/wp-content/uploads/2026/01/00-16-510x510.png' + }, + { + key='app.opts.plugin_peertube_url_2', + title='Peertube URL 2', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_peertube_url_3', + title='Peertube URL 3', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_peertube_url_4', + title='Peertube URL 4', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_peertube_url_5', + title='Peertube URL 5', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + } + } +} + +return plugin diff --git a/src/plugin/rssatom.lua b/src/plugin/rssatom.lua new file mode 100644 index 0000000..9efc7f5 --- /dev/null +++ b/src/plugin/rssatom.lua @@ -0,0 +1,85 @@ +local util = require('util') + +app.on('scan_experience', function(exp) + --print_r(exp) +end) + +app.on('scan_experience_file', function(exp) +end) + +app.on('set', function(k,v) +end) + +app.on('dir', function(opts) + local showportals = app.opts.plugin_rssatom_strategy == 'always' or + (app.opts.plugin_rssatom_strategy == 'homepage' and opts.isroot) + if showportals then + opts.data.itemsremote = opts.data.itemsremote or {} + local itemsremote = opts.data.itemsremote + for i=1,5 do + local urls = app.opts[ 'plugin_rssatom_url_' .. i] + if #urls > 0 then + local url = urls:gsub(' .*','') + local thumburl = urls:match(" ") and urls:gsub('.* ','') or nil + table.insert(itemsremote, { + title = url:gsub(".*//",""), + thumbnail = thumburl, + url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url) + }) + end + end + end +end) + +-- this registers the plugin UI +local plugin = { + name = "RSS Atom", + tab = " RSS Atom", + info = "disabled RSS feeds can be rendered as rooms in JanusWeb", + opts = { + { + key='app.opts.plugin_rssatom_strategy', + title='Display portals', + info="When to show rss portals?", + default="homepage", + values={always='always',homepage='only homepage'} + }, + { + 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' + }, + { + key='app.opts.plugin_rssatom_url_2', + title='RSS/Atom URL 2', + info="URL and/or Thumbnail URL (space separated)", + default="https://nlnet.nl/feed.atom https://nlnet.nl/logo/history/logo-640x732-pre-2024.jpg", + values='https://nlnet.nl/feed.atom https://nlnet.nl/logo/history/logo-640x732-pre-2024.jpg' + }, + { + key='app.opts.plugin_rssatom_url_3', + title='RSS/Atom URL 3', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_rssatom_url_4', + title='RSS/Atom URL 4', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + }, + { + key='app.opts.plugin_rssatom_url_5', + title='RSS/Atom URL 5', + info="URL and/or Thumbnail URL (space separated)", + default="", + values='' + } + } +} + +return plugin diff --git a/src/plugin/staticwebgen.lua b/src/plugin/staticwebgen.lua index ef27542..63a5c1c 100644 --- a/src/plugin/staticwebgen.lua +++ b/src/plugin/staticwebgen.lua @@ -8,32 +8,10 @@ app.on('set', function(k,v) end) app.on('init', function() - local outdir = 'www' - local url = {"/"} + local me = app.plugin.staticwebgen if util.contains(argv, 'generate') then - if util.fileExist(outdir) then - print("[!] folder 'www' was already detected..please delete it") - os.exit() - end - local selfbinary = '../' .. argv[-1] - os.execute("mkdir www") - os.execute("cp -Lr " .. app.opts.plugin_diskscan_dir .. " " .. outdir .. "/.") - os.execute("cd " .. outdir .. " && unzip " .. selfbinary .. " 'img/*' 'css/*' 'janusweb/*' 'shader/*'") - foreach( app.cache.items, function(k,v) - if v.dir then - table.insert(url, v.url ) - end - end) - foreach( url, function(k,url) - url = url:gsub("//","/") - util.fetchServer( url, nil, function(req,res,next) - print( outdir .. url .. '/index.html') - util.writefile( outdir .. url .. '/index.html', res.body ) - end) - end) + me.generate() end - --print("files generated in 'www' folder") - --os.exit() end) -- this registers the plugin UI @@ -77,7 +55,47 @@ local plugin = { default="", values='' } - } + }, + + done = function() + print("✅ finished generating 'www' folder") + os.exit(0) + end, + + generate = function() + local outdir = 'www' + local root = app.opts.plugin_diskscan_dirname + local urls = {"/","/about", "/" .. root } + local urlLast = '' + if util.fileExist(outdir) then + print("[!] folder 'www' was already detected..please delete it") + os.exit() + end + local selfbinary = '../' .. argv[-1] + os.execute("mkdir www www/about") + os.execute("cp -Lr " .. app.opts.plugin_diskscan_dir .. " " .. outdir .. "/.") + 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 ) + urlLast = v.url + end + end) + + app.on('response', function(opts) + local body = opts.res._body -- capture the output + local url = opts.req.url:gsub("//","/") + util.writefile( outdir .. url .. '/index.html', body ) + end) + + foreach( urls, function(k,url) + url = url:gsub("//","/") + print("saving " .. url) + util.queueServerRequest('GET', url, nil, nil, nil, nil, nil, 8080) + app.run() + end) + app.plugin.staticwebgen.done() + end } return plugin diff --git a/src/plugin/webhook.lua b/src/plugin/webhook.lua index 83f693c..9836bbd 100644 --- a/src/plugin/webhook.lua +++ b/src/plugin/webhook.lua @@ -13,6 +13,7 @@ end) -- this registers the plugin UI local plugin = { name = 'Webhooks disabled', + tab = " Webhooks", info = '', opts = { { diff --git a/src/test/main.lua b/src/test/main.lua index 474c5a9..b772149 100644 --- a/src/test/main.lua +++ b/src/test/main.lua @@ -1,17 +1,9 @@ local lust = require 'test/lust' +local util = require 'util' local describe, it, expect = lust.describe, lust.it, lust.expect -- usage: https://github.com/bjornbytes/lust -lust.queueServerRequest = function(method, path, params, host, headers, body, scheme, port) - GetMethod = function() return method end - GetPath = function() return path end - GetParams = function() return params or {} end -- note: { {"key","value"} } format - GetHost = function() return host or 'localhost' end - GetHeaders = function() return headers or {} end - GetScheme = function() return scheme or 'https' end - GetBody = function() return body or '' end - GetPort = function() return port or 80 end -end +lust.queueServerRequest = util.queueServerRequest require('test/plugin_global')(lust) require('test/plugin_git')(lust) diff --git a/src/util/xrforge.sh b/src/util/xrforge.sh index e89da1d..69f5afd 100755 --- a/src/util/xrforge.sh +++ b/src/util/xrforge.sh @@ -47,6 +47,7 @@ build(){ download janusweb cd src zip -yr ../result/bin/xrforge.com * .lua .init.lua .args + test -n "$ARCH" && cp ../result/bin/xrforge.com ../result/bin/xrforge-$ARCH.com } rebuild(){