From 3981800e423c0c741ade75f80a83ff85b3d6c7ff Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Mon, 24 Aug 2026 13:29:35 +0200 Subject: [PATCH] fixed symlink & referrer stuff --- src/.args | 4 +- src/.init.lua | 11 +++++- src/.lua/util.lua | 1 + src/middleware/onlyGET.lua | 2 +- src/page/admin/index.lua | 4 +- src/page/dir.html | 3 +- src/page/dir.lua | 13 ++++--- src/page/index.html | 6 ++- src/page/janusxr.html | 26 ++++++++----- src/plugin/diskscan.lua | 46 ++++++++++++++-------- src/plugin/janusxr.lua | 79 +++++++++++++++++++++++++------------- src/plugin/xrfragments.lua | 2 +- 12 files changed, 128 insertions(+), 69 deletions(-) diff --git a/src/.args b/src/.args index 8ba1cb2..baa52d4 100644 --- a/src/.args +++ b/src/.args @@ -1,8 +1,8 @@ -H Access-Control-Allow-Origin: * -H -Access-Control-Allow-Method: GET, OPTIONS +Access-Control-Allow-Method: GET, HEAD, OPTIONS -H -Allow: GET, POST, OPTIONS +Allow: GET, HEAD, POST, OPTIONS -D . diff --git a/src/.init.lua b/src/.init.lua index d64f89b..2b9e889 100644 --- a/src/.init.lua +++ b/src/.init.lua @@ -26,6 +26,7 @@ app = require("soakbean") { generate = {file="cmd/generate.lua", info="generate www-folder with XR experiences"}, test = {file="cmd/test.lua", info="run testsuite (selftest)"} }, + addPlugin = function(file) local p = require(file) table.insert(app.plugin,p) @@ -40,6 +41,14 @@ app = require("soakbean") { end) app.set('app.opts', app.opts) return p + end, + + addExperience = function(experience, ino) + if app.cache.items[ino] then -- handle duplicate symlinks, symlinks to symlinks e.g. + app.cache.items[ino .. ' ' .. experience.parent.id ] = experience + else + app.cache.items[ino] = experience + end end } @@ -78,9 +87,9 @@ print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging fro -- init URL router app.url['^/data'] = '/data.lua' -- setup custom file endpoint -app.use( require('middleware/onlyGET')({exclude={'^/admin'}}) ) -- we are staticwebgen-first server-last app.use( app.plugin.global.middleware.homepage_2D_or_3D ) app.url['^/admin[/]?$'] = '/page/admin/index.lua' +app.use( require('middleware/onlyGET')({exclude={'^/admin'}}) ) -- we are staticwebgen-first server-last app.get('^/design[/]?$', util.pagerender('page/design.html')) app.get('^/about[/]?$', util.pagerender('page/about.html')) diff --git a/src/.lua/util.lua b/src/.lua/util.lua index 2fde540..36b22ae 100644 --- a/src/.lua/util.lua +++ b/src/.lua/util.lua @@ -106,6 +106,7 @@ util.template = function(content,data) -- set fallback vars mdata.urlpath = util.baseURLify( GetPath() ) mdata.url = mdata.url or mdata.urlpath + mdata.janusurl = mdata.janusurl or mdata.urlpath mdata.title = mdata.title or app.opts.subtitle mdata.header_include = mdata.header_include or '' mdata.footerlinks = {} diff --git a/src/middleware/onlyGET.lua b/src/middleware/onlyGET.lua index 6194010..f984294 100644 --- a/src/middleware/onlyGET.lua +++ b/src/middleware/onlyGET.lua @@ -3,7 +3,7 @@ return function(opts) opts = opts or {exclude={"^/admin"}} return function(req,res,next) local pass = false - if req.method == "GET" then + if req.method == "GET" or req.method == "HEAD" then pass = true else foreach(opts.exclude, function(k,path) diff --git a/src/page/admin/index.lua b/src/page/admin/index.lua index ddc2bd1..f4214c8 100644 --- a/src/page/admin/index.lua +++ b/src/page/admin/index.lua @@ -57,9 +57,7 @@ end app.on('set', function(k,v) -- clear url cache if k:match("app.opts") then -- after (admin) updates to app.opts.* - foreach( app.cache.url, function(k,v) - app.cache.url = {} - end) + app.cache.url = {} end end) diff --git a/src/page/dir.html b/src/page/dir.html index 81ce6fa..5211de6 100644 --- a/src/page/dir.html +++ b/src/page/dir.html @@ -22,8 +22,7 @@ end} function getURL(url) if app.arg[1] == 'generate' or url:match("://") then - url = 'janusweb/index.html#janus.url=' .. - EscapeFragment( url ) .. '&janus.referrer=' .. util.getURL(urlpath ) + url = '?view=3D#janus.url=' .. EscapeFragment( url ) url = util.baseURLify(url) else url = util.stripPath( url ) diff --git a/src/page/dir.lua b/src/page/dir.lua index b01c83d..a8f39e5 100644 --- a/src/page/dir.lua +++ b/src/page/dir.lua @@ -25,6 +25,7 @@ return { local matchpath = urlpath if urlpath == '/' then matchpath = rootpath end -- generate janusxr footer for root url 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) .. " " .. @@ -57,7 +58,7 @@ return { end) local data = util.merge({ - url = req.param.url, -- query hint to render janus.url in iframe (index.html) + janusurl = req.param.url, -- query hint to render janus.url in iframe (index.html) items = {}, isroot = isroot, referrer = referrer, @@ -71,7 +72,7 @@ return { i=i+1 if v.view3D then local isMarkupExperience = function(v) - return v.janusxr and (v.janusxr.jml or v.janusxr.aframe) + return v.janusxr and (v.janusxr.jml or v.janusxr.wrap) end -- prioritize markup experience above binary 3D files as experience containers if not data.item or (isMarkupExperience(v) and not isMarkupExperience(data.item)) then @@ -80,9 +81,11 @@ return { 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 - -- .. EscapeFragment( (v.url:match("#") and "&" or "#" ) .. 'janus.referrer=' .. EscapeFragment(v.referrer) ) - if v.janusxr and (v.janusxr.jml or v.janusxr.aframe) then + data.janusurl = (v.janusxr and v.janusxr.wrap) and + app.opts.baseurl .. util.stripPath(v.parent.url) + or + app.opts.baseurl .. util.stripPath(v.url) + if v.janusxr and (v.janusxr.jml or v.janusxr.wrap and v.janusxr.wrap.aframe) then -- what to do here? no generated portals? data.janusxr = app.tpl( util.readfile(v.path), data ) end diff --git a/src/page/index.html b/src/page/index.html index 8ed8d73..97c2065 100644 --- a/src/page/index.html +++ b/src/page/index.html @@ -3,7 +3,7 @@ ${header}
- + {if defined(item) and item.parent and item.parent.info then print([[ @@ -45,6 +45,10 @@ end} }, 1000 ) }); }); + + // reroute janus.url to iframe if needed + let params = new URLSearchParams( document.location.href.replace(/.*#/,'') ) + if( params?.get('janus.url') ) iframe.src = 'janusweb/index.html#janus.url=' + encodeURIComponent( params.get('janus.url') ) diff --git a/src/page/janusxr.html b/src/page/janusxr.html index 5b75549..d7a4951 100644 --- a/src/page/janusxr.html +++ b/src/page/janusxr.html @@ -40,17 +40,21 @@ end} end} {if true then - local room = app.opts.plugin_janusxr_portalroom - if defined(item) and item.janusxr and item.janusxr.room then - room = item.janusxr.room + local room = '' + pos = '0 0 0' + if not defined(item) and defined(items) then + room = app.opts.plugin_janusxr_portalroom end - if room == 'random' then room = app.plugin.janusxr.getRandomRoom() end - if defined(item) and item.tag.xrfragments and item.tag.xrfragments.qualify then + if defined(item) and item.janusxr and item.janusxr.wrap then + pos = '0 0 2' + room = app.plugin.janusxr.wrapRoomAroundFile(item) + end + if defined(item) and item.tag.xrfragments and item.tag.xrfragments.href then room = '' -- xrfragments 3D files dont need to be wrapped with room end - print(' ') + print(' ') if defined(item) then - print(' ') + print(' ') end end} @@ -87,6 +91,10 @@ end} local width = 3.2 local center = -((#itemsremote*width)/1.3) + (i*width) local url = v.url + --if url:match("://") then + -- url = url .. ( url:match("#") and '&' or '#' ) .. 'janus.referrer=' .. + -- EscapeFragment( util.getURL() ) + --end local thumb = ' shader_id="defaultportal"' if #v.thumbnail > 0 then thumb = ' thumb_id="imgremote' .. i .. '" ' @@ -106,10 +114,10 @@ end} url = referrer, title = app.opts.plugin_janusxr_portal_up, } - addPortal('parent',4, v, v.url, 0, 0, ' shader_id="defaultportal"', '0 -90 0', '0.2 0.2 0.2') + addPortal('parent',5, v, v.url, 0, 0, ' shader_id="defaultportal"', '0 -90 0', '0.2 0.2 0.2') end} - + {if not defined(item) then print('${janusxr_jml_deco}') end} diff --git a/src/plugin/diskscan.lua b/src/plugin/diskscan.lua index 71b2795..0ec142d 100644 --- a/src/plugin/diskscan.lua +++ b/src/plugin/diskscan.lua @@ -37,7 +37,7 @@ return { shouldHideFile = function(name) return name == ".." -- parent or name == "util" -- repository - or name:match("^%.") -- hidden + or name:match("^%.[a-zA-Z0-9_-]") -- hidden or name == "src" -- repository or name == "result" -- repository end, @@ -46,7 +46,7 @@ return { local me = app.plugin.diskscan local seen = {symlinks={}} -- ensure rootdir as experience - app.cache.items[ scandir ] = { + app.addExperience({ path = scandir, url = "/" .. util.stripPath(scandir), name = scandir:gsub(".*/",""), @@ -54,7 +54,7 @@ return { title = scandir:gsub(".*/",""):gsub("-"," "), file = { ino = scandir }, id = scandir - } + },scandir) me.scanDir(scandir, nil, seen ) -- deal with (circular) symlinks foreach( seen.symlinks, function(ino,experience) @@ -65,8 +65,8 @@ return { print("✅ └─ plugin/diskscan: detected symlink..creating portal") experience.file = refItem.file experience.portal = refItem - app.pub("scan_experience", nil, experience) - app.cache.items[experience.id] = experience + app.pub("scan_experience" .. (experience.dir and '' or '_file') , nil, experience) + app.addExperience(experience,ino) end end end) @@ -100,26 +100,34 @@ return { } experience.parent = parent - if seen[ino] then return end -- dont process dirs/files twice - if (kind == unix.DT_LNK) then + experience.symlink = true local stat, err = unix.stat(fullpath) if stat then - experience.id_target = stat:ino() + experience.id_target = stat:ino() -- stat follows symlink by default + local realpath = unix.realpath(fullpath) + state,err = unix.opendir(realpath) + if not err then + experience.symlink = true + experience.dir = true + state:close() + end seen.symlinks[ ino ] = experience end - return end + + --local already_seen = seen[ino] and seen[ino].parent.file.ino == experience.parent.file.ino and true or false -- process once + --if already_seen and not experience.symlink then return end -- found directory - if (kind == unix.DT_DIR and kind ~= unix.DT_LNK) - and not me.shouldHideFile(name) then - seen[ino] = experience -- prevent recursion with symlinks - print("✅ ".. fullpath .. " " ..ino) + if not me.shouldHideFile(name) and + ( (kind == unix.DT_DIR and kind ~= unix.DT_LNK) or experience.dir ) + then + print("✅ ".. fullpath ) experience.dir = true experience.portal3D = true -- can be viewed as 3d portal app.pub("scan_experience", nil, experience) - app.cache.items[ino] = experience + app.addExperience(experience,ino) -- check for directory thumbnail sidecarfile local thumbExt = {"png","jpg","webp"} foreach( thumbExt, function(k,ext) @@ -136,14 +144,18 @@ return { end ---- found file - if (kind ~= unix.DT_DIR and kind ~= unix.DT_LNK) - and not me.shouldHideFile(name) then - seen[ino] = experience.file -- prevent recursion with symlinks + if not me.shouldHideFile(name) and + ( (kind ~= unix.DT_DIR and kind ~= unix.DT_LNK) or not experience.dir ) + then trace("ℹ️ ".. fullpath) experience.tag.file = true app.pub("scan_experience_file", nil, experience) local size = attr:size() end + + -- remember file/parent relationship + seen[ino] = experience + end local dref, derr; diff --git a/src/plugin/janusxr.lua b/src/plugin/janusxr.lua index cc60abf..c207828 100644 --- a/src/plugin/janusxr.lua +++ b/src/plugin/janusxr.lua @@ -13,7 +13,7 @@ app.on('dir', function(opts) if #app.opts.plugin_janusxr_random_objects > 0 and #app.opts.plugin_janusxr_random_colors > 0 then opts.data.janusxr_jml_deco = app.plugin.janusxr.getRandomDeco(7,50,hash,0,30) .. - app.plugin.janusxr.getRandomDeco(7,50,hash .. hash,30,30) + app.plugin.janusxr.getRandomDeco(7,50, "2" .. hash,30,30) end end) @@ -31,10 +31,8 @@ app.on('scan_experience_file', function(exp) end) if wrap then exp.view3D = true - exp.janusxr = { - room = app.opts.plugin_janusxr_wrap_3dfile - } - app.cache.items[ exp.file.ino ] = exp + exp.janusxr = { wrap = { object = true }} + app.addExperience( exp, exp.file.ino ) print("✅ └─ plugin/janusxr: wrapping " .. exp.path) end end @@ -49,13 +47,13 @@ app.on('scan_experience_file', function(exp) exp.title = title:gsub("<.*","") end app.pub("scan_experience_janusxr", nil, {exp=exp, item=item}) -- be nice to other plugins - app.cache.items[ exp.file.ino ] = exp + app.addExperience( exp, exp.file.ino ) 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 + exp.janusxr.wrap = {aframe = true } print("✅ └─ plugin/janusxr: AFRAME markup detected") end end @@ -68,7 +66,7 @@ end) app.on('set', function(k,v) end) -- this registers the plugin UI -local rooms = {random="random room",room_plane="wirefloor",room1="room1",room2="room2",room3="room3",room4="room4",room5="room5",room6="room6",room7="room7"} +local rooms = {random="random room",randomurl="random room (URL-deterministic)",room_plane="wirefloor",room1="room1",room2="room2",room3="room3",room4="room4",room5="room5",room6="room6",room7="room7"} local plugin = { name = "JanusXR", @@ -95,29 +93,29 @@ local plugin = { key='app.opts.plugin_janusxr_wrap_3dfile', title='VR wrap 3D file', info='render 3D files in janusxr.xml room', - default="room_plane", + default="randomurl", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_image', title='VR wrap images', info='render images in janusxr.xml room', - default="room_plane", + default="randomurl", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_video', title='VR wrap videos', info='render videos in janusxr.xml room', - default="room_plane", + default="randomurl", values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_wrap_aframe', title='AFRAME support', info='render AFRAME (geometry) markup when detected in URLs', - default="enabled", - values={enabled="enabled",disabled="disabled"} + default="room_plane", + values=util.merge(rooms,{disabled="-- disabled --"}) }, { key='app.opts.plugin_janusxr_portalroom', @@ -138,7 +136,7 @@ local plugin = { key='app.opts.plugin_janusxr_random_objects', title='Random objects', info="Random object id's in portal rooms", - default="capsule cone cube cylinder pyramid sphere torus", + default="capsule cone cylinder pyramid sphere torus", placeholder="capsule cone cylinder pyramid sphere torus", values="", }, @@ -174,21 +172,54 @@ local plugin = { } }, - getRandomRoom = function() + wrapRoomAroundFile = function(item) + local room = '' + if item.janusxr.wrap.object then + room = app.opts.plugin_janusxr_wrap_3dfile + end + if room == 'random' then + room = app.plugin.janusxr.getRandomRoom() + end + if room == 'randomurl' then + room = app.plugin.janusxr.getRandomRoom(item.url) + end + return room + end, + + getRandomRoom = function(url) local me = app.plugin.janusxr local room= '' math.randomseed(os.time()) foreach( me.opts, function(k,v) if v.key == 'app.opts.plugin_janusxr_portalroom' then local ordered = {} - foreach( v.values, function(k,v) table.insert(ordered,v) end) - room = ordered[ math.random( #ordered ) ] + foreach( v.values, function(k,v) + if not v:match("random") then + table.insert(ordered,v) + end + end) + table.sort(ordered) -- ensure same order + if url then + room = ordered[ me.hash_string_to_number(url) % #ordered ] + else + room = ordered[ math.random( #ordered ) ] + end end end) return room end, + hash_string_to_number = function(str) + local hash = 5381 + for i = 1, #str do + local char = string.byte(str, i) + hash = (hash * 33 + char) % 2147483647 + end + return hash + end, + getRandomDeco = function(num_objects, radius, seed_string, y, scale) + local me = app.plugin.janusxr num_objects = num_objects or 6 y = y or 0 radius = radius or 2 -- Configurable distance from (0, 0, 0) @@ -196,18 +227,12 @@ local plugin = { local JML = '' local colors = util.split( app.opts.plugin_janusxr_random_colors, ' ') local ids = util.split( app.opts.plugin_janusxr_random_objects, ' ') + table.sort(colors) -- ensure same order + table.sort(ids) -- ensure same order - local function hash_string_to_number(str) - local hash = 5381 - for i = 1, #str do - local char = string.byte(str, i) - hash = (hash * 33 + char) % 2147483647 - end - return hash - end local index = { - color= hash_string_to_number(seed_string) % #colors, - id = hash_string_to_number(seed_string) % #ids + color= me.hash_string_to_number(seed_string) % #colors, + id = me.hash_string_to_number(seed_string) % #ids } for i = 1, num_objects do diff --git a/src/plugin/xrfragments.lua b/src/plugin/xrfragments.lua index 784d810..1c6fcbe 100644 --- a/src/plugin/xrfragments.lua +++ b/src/plugin/xrfragments.lua @@ -51,7 +51,7 @@ app.on('scan_experience_file', function(exp) -- finally insert if viewable if exp.view3D then print("✅ └─ plugin/xrfragments: marking as 3D viewable") - app.cache.items[ exp.file.ino ] = exp + app.addExperience(exp, exp.file.ino) end end end)