fixed config + cli-args + admin form
This commit is contained in:
parent
2671f43b8b
commit
af2e1bfdb2
24 changed files with 549 additions and 368 deletions
|
|
@ -34,3 +34,11 @@ manual scan (via cli?)
|
||||||
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=name' -H 'accept: application/vnd.manyfold.v0+json'
|
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=name' -H 'accept: application/vnd.manyfold.v0+json'
|
||||||
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=recent' -H 'accept: application/vnd.manyfold.v0+json'
|
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=recent' -H 'accept: application/vnd.manyfold.v0+json'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Permacomputing Spirit
|
||||||
|
|
||||||
|
* developed on [Dillo](https://dillo-browser.github.io/) as lowest common denominator / compatibility
|
||||||
|
* does not use javascript unless really needed (WebXR being the exception)
|
||||||
|
* server is using lua-scripting via cosmopolitan libc's [redbean](https://redbean.dev)
|
||||||
|
|
||||||
|
> So yes in case you'd think the codebase is 90-ish: that's **exactly why it works** on potato-devices AND modern devices.
|
||||||
|
|
|
||||||
3
run.sh
Executable file
3
run.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd src
|
||||||
|
../result/bin/xrforge.com -D . -- server
|
||||||
|
|
@ -3,6 +3,8 @@ package.path = package.path .. ";src/.lua/?.lua"
|
||||||
package.path = package.path .. ";middleware/?.lua"
|
package.path = package.path .. ";middleware/?.lua"
|
||||||
package.path = package.path .. ";cmd/?.lua"
|
package.path = package.path .. ";cmd/?.lua"
|
||||||
|
|
||||||
|
print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging from now on
|
||||||
|
|
||||||
util = require "util"
|
util = require "util"
|
||||||
json = require "json"
|
json = require "json"
|
||||||
|
|
||||||
|
|
@ -15,24 +17,32 @@ dir = require "opendirectory"
|
||||||
-- create
|
-- create
|
||||||
app = require("soakbean") {
|
app = require("soakbean") {
|
||||||
bin = "./xrforge.com",
|
bin = "./xrforge.com",
|
||||||
|
bindir = arg[-1]:gsub("xrforge.com",""),
|
||||||
cache = {},
|
cache = {},
|
||||||
opts = {
|
opts = {},
|
||||||
title="My XRForge experiences",
|
|
||||||
subtitle="Portable XR experiences for PhoneVR, VRheadset, desktop & mobile."
|
|
||||||
},
|
|
||||||
cmd={
|
cmd={
|
||||||
help = {file="help.lua", info="displays help cmds"},
|
help = {file="cmd/help.lua", info="display all possible flags"},
|
||||||
server = {file="server.lua", info="starts the webserver"}
|
server = {file="cmd/server.lua", info="starts the webserver"}
|
||||||
},
|
}
|
||||||
title = 'XRForge - local-first XR experiences',
|
|
||||||
subtitle = 'XRForge makes local-first XR easy',
|
|
||||||
notes = {'🤩 portable selfsovereign', '🖧 JanusXR / XR Fragments', '♻ decentralized'}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- api skeleton (implemented via soakbean's app.on(...))
|
||||||
|
app.set = function(k,v) end
|
||||||
|
app.init = function() end
|
||||||
|
|
||||||
|
-- load config
|
||||||
|
app.opts = require('config')(app)
|
||||||
|
|
||||||
|
|
||||||
|
app.runcmd(app) -- run clicmds
|
||||||
|
|
||||||
|
-- init URL router
|
||||||
app.url['^/data'] = '/data.lua' -- setup custom file endpoint
|
app.url['^/data'] = '/data.lua' -- setup custom file endpoint
|
||||||
|
|
||||||
app.url['^/$'] = '/index.lua' -- alias for app.tpl( LoadAsset('index.html'), app )
|
app.url['^/$'] = '/page/index.lua' -- alias for app.tpl( LoadAsset('index.html'), app )
|
||||||
app.get('^/admin/$', app.template('admin/index.html') ) -- alias for app.tpl( LoadAsset('index.html'), app )
|
app.get('^/design[/]?$', util.pagerender('page/design.html'))
|
||||||
|
|
||||||
|
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
||||||
|
|
||||||
app.post('^/save', function(req,res,next) -- setup inline POST endpoint
|
app.post('^/save', function(req,res,next) -- setup inline POST endpoint
|
||||||
-- also .get(), .put(), .delete(), .options()
|
-- also .get(), .put(), .delete(), .options()
|
||||||
|
|
@ -43,23 +53,22 @@ app.post('^/save', function(req,res,next) -- setup inline POST endpoint
|
||||||
end)
|
end)
|
||||||
|
|
||||||
app --
|
app --
|
||||||
.use( require("httpauth")() )
|
.use( require("middleware/httpauth")() )
|
||||||
.use( require("json").middleware() ) -- try plug'n'play json API middleware
|
.use( require("json").middleware() ) -- try plug'n'play json API middleware
|
||||||
.use( app.router( app.url ) ) -- try url router
|
.use( app.router( app.url ) ) -- try url router
|
||||||
.use( app.response() ) -- try serve app response (if any)
|
.use( app.response() ) -- try serve app response (if any)
|
||||||
.use( function(req,next) Route() end) -- fallback default redbean fileserver
|
.use( function(req,next) Route() end) -- fallback default redbean fileserver
|
||||||
|
|
||||||
app.on('template', function(a,b,c,d,e)
|
|
||||||
print("TEMPLATE!")
|
|
||||||
end)
|
|
||||||
|
|
||||||
function OnHttpRequest() app.run() end
|
function OnHttpRequest() app.run() end
|
||||||
|
|
||||||
print("ja")
|
app.on('init', function()
|
||||||
--function OnHttpRequest()
|
if assert(unix.fork()) == 0 then
|
||||||
-- if( not dir.serve("./experiences") ) then
|
os.execute("ls ; sleep 5")
|
||||||
-- print("default")
|
print("ok")
|
||||||
-- Route()
|
--local ok, headers, body = Fetch('http://127.0.0.1:8080/test')
|
||||||
-- end
|
end
|
||||||
--end
|
end)
|
||||||
--
|
|
||||||
|
OnServerStart = function()
|
||||||
|
app.init() -- this triggers init event
|
||||||
|
end
|
||||||
|
|
|
||||||
1
src/.lua/cmd
Symbolic link
1
src/.lua/cmd
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../cmd
|
||||||
51
src/.lua/config.lua
Normal file
51
src/.lua/config.lua
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
local util = require('util')
|
||||||
|
|
||||||
|
return function(app)
|
||||||
|
|
||||||
|
local config = { -- default
|
||||||
|
name = "XRForge",
|
||||||
|
baseurl="/",
|
||||||
|
title="My XRForge experiences",
|
||||||
|
subtitle="Portable XR experiences for Phone, VRheadset & desktop.",
|
||||||
|
admin_title = "XRForge Admin",
|
||||||
|
admin_subtitle = "Manage your XR experiences here",
|
||||||
|
disk_dir = ".",
|
||||||
|
manyfold_instance_1 = "https://xrforge.isvery.ninja",
|
||||||
|
manyfold_instance_2 = "",
|
||||||
|
manyfold_instance_3 = "",
|
||||||
|
sync_interval = 30,
|
||||||
|
sync_strategy = "recent"
|
||||||
|
}
|
||||||
|
|
||||||
|
local configfile = "xrforge.lua"
|
||||||
|
local savedconfig = util.readfile( app.bindir .. configfile, 'r', true) -- check local config
|
||||||
|
or LoadAsset(configfile) -- then internal saved config
|
||||||
|
|
||||||
|
if savedconfig then
|
||||||
|
local eval = function(str) return load(str)() end
|
||||||
|
local status, result = pcall(eval, savedconfig ) -- Pass "string" as the first argument
|
||||||
|
if not status then
|
||||||
|
print("error: xrforge.lua contains errors:")
|
||||||
|
else
|
||||||
|
config = result -- overrule default config
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
app.on('set', function(k,v)
|
||||||
|
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)
|
||||||
|
newconfig = newconfig:gsub("^}$"," init = function() print('xrforge.lua config loaded') end\n}")
|
||||||
|
util.writefile( app.bindir .. configfile, newconfig )
|
||||||
|
print("stored xrforge.lua")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- store local config if not exist
|
||||||
|
if not util.readfile( app.bindir .. configfile, 'r', true) then
|
||||||
|
app.set('app.opts', config )
|
||||||
|
end
|
||||||
|
|
||||||
|
return config
|
||||||
|
end
|
||||||
1
src/.lua/middleware
Symbolic link
1
src/.lua/middleware
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../middleware
|
||||||
|
|
@ -23,7 +23,7 @@ sb = {
|
||||||
sb.data[k] = (function(k,v)
|
sb.data[k] = (function(k,v)
|
||||||
return function(a,b,c,d,e,f)
|
return function(a,b,c,d,e,f)
|
||||||
v(a,b,c,d,e,f)
|
v(a,b,c,d,e,f)
|
||||||
sb.pub(k,v)
|
sb.pub(k,v,a,b,c,d,e,f)
|
||||||
end
|
end
|
||||||
end)(k,v)
|
end)(k,v)
|
||||||
else
|
else
|
||||||
|
|
@ -38,10 +38,14 @@ sb = {
|
||||||
return sb.app
|
return sb.app
|
||||||
end,
|
end,
|
||||||
|
|
||||||
pub = function(k,v)
|
pub = function(k,v,a,b,c,d,e,f)
|
||||||
if sb.handler[k] ~= nil then
|
if sb.handler[k] ~= nil then
|
||||||
for i,handler in pairs(sb.handler[k]) do
|
for i,handler in pairs(sb.handler[k]) do
|
||||||
coroutine.wrap(handler)(v,k)
|
if a then --
|
||||||
|
coroutine.wrap(handler)(a,b,c,d,e,f) -- forward function args
|
||||||
|
else
|
||||||
|
coroutine.wrap(handler)(v,k) -- forward assignment
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
@ -187,13 +191,12 @@ sb = {
|
||||||
end,
|
end,
|
||||||
|
|
||||||
init = function(app)
|
init = function(app)
|
||||||
for k,v in pairs(argv) do
|
|
||||||
app.opts[ v:gsub("=.*","") ] = v:gsub(".*=","")
|
|
||||||
end
|
|
||||||
if( keys(app.cmd) > 0 ) then sb.runcmd(app) end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
runcmd = function(app)
|
runcmd = function(app)
|
||||||
|
for k,v in pairs(argv) do
|
||||||
|
app.opts[ v:gsub("=.*","") ] = v:gsub(".*=","")
|
||||||
|
end
|
||||||
for k,v in pairs(app.opts) do
|
for k,v in pairs(app.opts) do
|
||||||
if app.cmd[k] then
|
if app.cmd[k] then
|
||||||
local file = app.cmd[k].file
|
local file = app.cmd[k].file
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,26 @@
|
||||||
local util = { }
|
local util = { }
|
||||||
|
|
||||||
|
util.readfile = function(filename, mode, silent)
|
||||||
|
local file, err = io.open(filename, mode or "r")
|
||||||
|
if not file then
|
||||||
|
if not silent then print("Error opening file:", err) end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local content = file:read("*a")
|
||||||
|
file:close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
util.writefile = function(filename, content, mode)
|
||||||
|
local file, err = io.open(filename, mode or "w") -- Default write mode
|
||||||
|
if not file then
|
||||||
|
return print("Error opening file:", err)
|
||||||
|
end
|
||||||
|
file:write(content)
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
util.clone = function(orig)
|
util.clone = function(orig)
|
||||||
local orig_type = type(orig)
|
local orig_type = type(orig)
|
||||||
local copy
|
local copy
|
||||||
|
|
@ -28,11 +49,20 @@ util.flatten = function(tbl, prefix, result)
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
util.pagerender = function(file,data)
|
||||||
|
return function(req,res,next)
|
||||||
|
res.status(200)
|
||||||
|
res.header('content-type','text/html; charset=utf-8')
|
||||||
|
res.body( util.template( LoadAsset(file),data or app) )
|
||||||
|
next()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
util.template = function(content,data)
|
util.template = function(content,data)
|
||||||
local finaldata = util.merge(app,{})
|
local finaldata = util.merge(data or app,{})
|
||||||
finaldata = util.merge(finaldata, util.flatten(app.opts,"opts") )
|
finaldata = util.merge(finaldata, util.flatten(app.opts,"opts") )
|
||||||
finaldata.header = app.tpl( LoadAsset('template/header.html'), finaldata )
|
finaldata.header = app.tpl( LoadAsset('page/header.html'), finaldata )
|
||||||
finaldata.footer = app.tpl( LoadAsset('template/footer.html'), finaldata )
|
finaldata.footer = app.tpl( LoadAsset('page/footer.html'), finaldata )
|
||||||
return app.tpl( content, finaldata )
|
return app.tpl( content, finaldata )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +1,13 @@
|
||||||
return function(app,argv)
|
return function(app,argv)
|
||||||
print("help!")
|
foreach( app.cmd, function(k,v)
|
||||||
|
io.write( app.bin .. " " .. k .. "\t\t # " .. v.info .. "\n")
|
||||||
|
end)
|
||||||
|
io.write("\nexample: " .. app.bin .. " title='My XRForge experiences'\n\n")
|
||||||
|
io.write("Flags: (overrides xrforge.lua configfile)\n\n")
|
||||||
foreach( app.opts, function(k,v)
|
foreach( app.opts, function(k,v)
|
||||||
if not k:match("/") and not k:match("^help") then
|
if not k:match("/") and not k:match("^help") and not k:match("updated") then
|
||||||
print( app.bin .. " -- " .. k .. "=...\t # '" .. v .. "' e.g." )
|
io.write( " " .. k .. "='" .. v .. "'\n")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
foreach( app.cmd, function(k,v)
|
|
||||||
print( app.bin .. " -- " .. k .. "\t\t # " .. v.info )
|
|
||||||
end)
|
|
||||||
|
|
||||||
os.exit()
|
os.exit()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
.col2{ color:#AAD; }
|
.col2{ color:#AAD; }
|
||||||
.col3{ color:#878; }
|
.col3{ color:#878; }
|
||||||
.col4{ color:#7070ff; }
|
.col4{ color:#7070ff; }
|
||||||
.col5{ color:#557; }
|
.col5{ color:#447; }
|
||||||
.col5{ color:#224; }
|
.col5{ color:#224; }
|
||||||
.col5{ color:#112; }
|
.col5{ color:#112; }
|
||||||
|
|
||||||
|
|
@ -40,7 +40,28 @@ td,div,p{
|
||||||
line-height:24px;
|
line-height:24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn{
|
/* form elements */
|
||||||
|
|
||||||
|
input,
|
||||||
|
select{
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0px 0px 10px 0px;
|
||||||
|
padding:7px;
|
||||||
|
display:block;
|
||||||
|
max-width:300px;
|
||||||
|
width:100%;
|
||||||
|
background: #000;
|
||||||
|
border: 1px solid #557;
|
||||||
|
color: #AAD;
|
||||||
|
}
|
||||||
|
input[type=checkbox]{
|
||||||
|
transform:scale(1.7) translate(3px,4px)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* buttons */
|
||||||
|
|
||||||
|
.btn,
|
||||||
|
input[type=submit]{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0px 10px 10px 0px;
|
margin: 0px 10px 10px 0px;
|
||||||
}
|
}
|
||||||
|
|
@ -48,21 +69,28 @@ td,div,p{
|
||||||
.btn a,
|
.btn a,
|
||||||
.btn a:active,
|
.btn a:active,
|
||||||
.btn a:visited,
|
.btn a:visited,
|
||||||
.btn a:link {
|
.btn a:link,
|
||||||
|
input[type=submit] {
|
||||||
|
font-size:15px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding:8px 16px;
|
padding:8px 16px;
|
||||||
color: #f3f;
|
color: #f3f;
|
||||||
text-decoration:none;
|
text-decoration:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn a:hover{
|
.btn a:hover,
|
||||||
|
input[type=submit] {
|
||||||
color:#F7F;
|
color:#F7F;
|
||||||
background:#000;
|
background:#000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn a, .border{ border:1px dotted #AAD; }
|
.btn a,
|
||||||
|
.border,
|
||||||
|
input[type=submit]{
|
||||||
|
border:1px dotted #AAD;
|
||||||
|
}
|
||||||
|
|
||||||
a, a:active, a:visited, a:link {
|
a, a:active, a:visited, a:link, input[type=submit] {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color:#FFF;
|
color:#FFF;
|
||||||
border-bottom: 1px dotted #AAD;
|
border-bottom: 1px dotted #AAD;
|
||||||
|
|
@ -74,6 +102,27 @@ a:hover{
|
||||||
border-bottom:1px dotted #f3f;
|
border-bottom:1px dotted #f3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table{
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
table tr td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
form > table > tbody > tr > td{
|
||||||
|
height:32px;
|
||||||
|
}
|
||||||
|
form > table > tbody > tr > td:nth-child(1) {
|
||||||
|
font-weight:bold;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
form > table > tbody > tr > td:nth-child(3) {
|
||||||
|
font-size: 13px;
|
||||||
|
padding-top: 4px;
|
||||||
|
text-align:right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.border.primary{ border:1px dotted #87a; }
|
.border.primary{ border:1px dotted #87a; }
|
||||||
.border.secondary{ border:1px dotted #7070ff; }
|
.border.secondary{ border:1px dotted #7070ff; }
|
||||||
|
|
||||||
|
|
@ -123,10 +172,17 @@ ul{
|
||||||
padding-left:0.8em;
|
padding-left:0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
.menu a {
|
.menu a {
|
||||||
padding-right:30px;
|
padding-right:30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu br {
|
||||||
|
display:none
|
||||||
|
}
|
||||||
|
|
||||||
.grid .tile{ border-radius:5px; }
|
.grid .tile{ border-radius:5px; }
|
||||||
.grid .tile .text{ padding:3px 0px; }
|
.grid .tile .text{ padding:3px 0px; }
|
||||||
.grid .tile .img{
|
.grid .tile .img{
|
||||||
|
|
@ -136,6 +192,35 @@ ul{
|
||||||
|
|
||||||
img.icon{ display:none; }
|
img.icon{ display:none; }
|
||||||
|
|
||||||
|
/* tab */
|
||||||
|
|
||||||
|
.tab{
|
||||||
|
border-bottom: 1px solid #224;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
margin-left: 140px;
|
||||||
|
}
|
||||||
|
.tab .label{
|
||||||
|
display: block;
|
||||||
|
background: #000;
|
||||||
|
margin-bottom: -4px;
|
||||||
|
width: 158px;
|
||||||
|
margin-left: -140px;
|
||||||
|
border-top: 1px dotted #557;
|
||||||
|
border-left: 1px dotted #556;
|
||||||
|
border-right: 1px dotted #556;
|
||||||
|
padding: 9px 0px 0px 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification{
|
||||||
|
background: #7070ff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 4px dashed #222;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
/* progressive enhancement quirks */
|
/* progressive enhancement quirks */
|
||||||
@media (min-width:1px) {
|
@media (min-width:1px) {
|
||||||
.grid .tile{
|
.grid .tile{
|
||||||
|
|
@ -29,6 +29,13 @@ body{
|
||||||
width:100%;
|
width:100%;
|
||||||
margin: 0px 0px 15px 0px;
|
margin: 0px 0px 15px 0px;
|
||||||
}
|
}
|
||||||
|
form table tr > th:nth-child(3),
|
||||||
|
form table tr > td:nth-child(3) {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
.menu br {
|
||||||
|
display:initial !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid .tile .text{
|
.grid .tile .text{
|
||||||
|
|
@ -44,8 +51,12 @@ body{
|
||||||
}
|
}
|
||||||
|
|
||||||
.jumbotron{
|
.jumbotron{
|
||||||
padding:20px 0px;
|
padding:20px 20px;
|
||||||
background:linear-gradient(45deg,#0000,#0000,#F7F2,#0000,#07F2,#0000,#0000);
|
background:linear-gradient(45deg,#0000,#0000,#F7F2,#0000,#07F2,#0000,#0000);
|
||||||
|
background:url(./../img/backdrop.jpg);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: bottom;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.icon{
|
img.icon{
|
||||||
|
|
@ -55,6 +66,6 @@ img.icon{
|
||||||
width: 17px;
|
width: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn a.primary{ background: #ff33Ff; color:#000 !important;}
|
.btn .primary{ background: #ff33Ff; color:#000 !important;}
|
||||||
.btn a.secondary{ background: #7070ff; color:#000 !important;}
|
.btn .secondary{ background: #7070ff; color:#000 !important;}
|
||||||
|
|
||||||
BIN
src/img/backdrop.jpg
Normal file
BIN
src/img/backdrop.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
128
src/page/admin/index.html
Normal file
128
src/page/admin/index.html
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
${header}
|
||||||
|
|
||||||
|
<div style="height:120px;" class="jumbotron">
|
||||||
|
<h1>${opts.admin_title}</h1>
|
||||||
|
${opts.admin_subtitle}
|
||||||
|
</div>
|
||||||
|
<hr style="margin-top:0">
|
||||||
|
|
||||||
|
${notification}
|
||||||
|
|
||||||
|
<div class="tab">
|
||||||
|
<div class="label col2">
|
||||||
|
Local content
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<div class="margin1">
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<th width="165"/>
|
||||||
|
<th />
|
||||||
|
<th />
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Directory
|
||||||
|
</td>
|
||||||
|
<td >
|
||||||
|
<input type="text" value="${opts.disk_dir}"></input>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
Directory to scan for experiences (use '.' for current)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="tab">
|
||||||
|
<div class="label col2">
|
||||||
|
Manyfold instances
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="margin1">
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<th width="165"/>
|
||||||
|
<th />
|
||||||
|
<th />
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Instance URL 1
|
||||||
|
</td>
|
||||||
|
<td >
|
||||||
|
<input type="text" name="opts.manyfold_instance_1" value="${opts.manyfold_instance_1}"></input>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Instance URL 2
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="opts.manyfold_instance_2" value="${opts.manyfold_instance_2}"></input>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Instance URL 3
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="opts.manyfold_instance_3" value="${opts.manyfold_instance_3}"></input>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
<a href="https://manyfold.app/" target="_blank">Manyfold</a> URL goes here (https://xrforge.isvery.ninja e.g.)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Sync strategy
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select value="${opts.manyfold_sync_strategy}">
|
||||||
|
<option value="recent" default>only recent</option>
|
||||||
|
<option value="all+recent">everything + recent</option>
|
||||||
|
<option value="disabled">disabled</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
Specify how manyfold instances should be synced.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Sync interval
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select value="${opts.manyfold_sync_interval}">
|
||||||
|
<option value="30">every 30 mins</option>
|
||||||
|
<option value="15" selected>every 15 mins</option>
|
||||||
|
<option value="5">every 5 minutes</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
Specify how often manyfold instances should be checked for recent items.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div style="margin-left:165px">
|
||||||
|
<br/>
|
||||||
|
<div class="btn">
|
||||||
|
<input type="submit" class="btn secondary" value="save" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
${footer}
|
||||||
27
src/page/admin/index.lua
Normal file
27
src/page/admin/index.lua
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
local util = require("util")
|
||||||
|
|
||||||
|
local data = util.merge({notification=''},{ opts = app.opts })
|
||||||
|
|
||||||
|
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 = '<div class="notification">updated</div>'
|
||||||
|
else
|
||||||
|
data.notification = '<div class="notification">no changes detected</div>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local html = util.template( LoadAsset("page/admin/index.html"),data)
|
||||||
|
|
||||||
|
|
||||||
|
SetStatus(200)
|
||||||
|
SetHeader('Content-Type', 'text/html; charset=utf-8')
|
||||||
|
Write( html )
|
||||||
127
src/page/design.html
Normal file
127
src/page/design.html
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
${header}
|
||||||
|
|
||||||
|
<h1>HEADER 1</h1>
|
||||||
|
Lorem ipsum dolor sit amter.<br>
|
||||||
|
Amet puti solor fati corpus <a href="#foo" >a link</a> flap.<br><br>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="btn">
|
||||||
|
<a href="#foo">button</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn">
|
||||||
|
<a href="#foo" class="btn primary">button primary ⮮</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn">
|
||||||
|
<a href="https://foo.com" target="_blank" class="btn secondary">button secondary ⮩</a>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="tab">
|
||||||
|
<div class="label col3">
|
||||||
|
Category B
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="margin1">
|
||||||
|
<form>
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td width="165">
|
||||||
|
Instance URL 1
|
||||||
|
</td>
|
||||||
|
<td >
|
||||||
|
<input type="text"/>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
Infotext about input
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Sync interval
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select>
|
||||||
|
<option value="30">every 30 mins</option>
|
||||||
|
<option value="15" selected>every 15 mins</option>
|
||||||
|
<option value="5">every 5 minutes</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="col3">
|
||||||
|
Infotext about select (tip: use selectboxes not checkboxes)
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>foo bar</li>
|
||||||
|
<li>flop flap</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
|
||||||
|
┌─────────────────┐┌────────────────┐
|
||||||
|
│ Foo CLI ││ Foo Web │
|
||||||
|
└─────────────────┘└────────────────┘
|
||||||
|
┌───────────────────────────────────┐
|
||||||
|
│ Foo Repository │
|
||||||
|
│ ┌────────┐ ┌────────┐ ┌─────────┐ │
|
||||||
|
│ │ code │ │ issues │ │ patches │ │
|
||||||
|
│ └────────┘ └────────┘ └─────────┘ │
|
||||||
|
├───────────────────────────────────┤
|
||||||
|
│ Foo Storage (Git) │
|
||||||
|
└───────────────────────────────────┘
|
||||||
|
┌────────────────┐┌─────────────────┐
|
||||||
|
│ Foo Node ││ Foo HTTPD │
|
||||||
|
├────────────────┤├─────────────────┤
|
||||||
|
│ NoiseXK ││ HTTP + JSON │
|
||||||
|
└────────────────┘└─────────────────┘
|
||||||
|
</pre>
|
||||||
|
<pre class="primary">
|
||||||
|
|
||||||
|
┌─────────────────┐┌────────────────┐
|
||||||
|
│ Foo CLI ││ Foo Web │
|
||||||
|
└─────────────────┘└────────────────┘
|
||||||
|
┌───────────────────────────────────┐
|
||||||
|
│ Foo Repository │
|
||||||
|
│ ┌────────┐ ┌────────┐ ┌─────────┐ │
|
||||||
|
│ │ code │ │ issues │ │ patches │ │
|
||||||
|
│ └────────┘ └────────┘ └─────────┘ │
|
||||||
|
├───────────────────────────────────┤
|
||||||
|
│ Foo Storage (Git) │
|
||||||
|
└───────────────────────────────────┘
|
||||||
|
┌────────────────┐┌─────────────────┐
|
||||||
|
│ Foo Node ││ Foo HTTPD │
|
||||||
|
├────────────────┤├─────────────────┤
|
||||||
|
│ NoiseXK ││ HTTP + JSON │
|
||||||
|
└────────────────┘└─────────────────┘
|
||||||
|
</pre>
|
||||||
|
<pre class="secondary">
|
||||||
|
|
||||||
|
┌─────────────────┐┌────────────────┐
|
||||||
|
│ Foo CLI ││ Foo Web │
|
||||||
|
└─────────────────┘└────────────────┘
|
||||||
|
┌───────────────────────────────────┐
|
||||||
|
│ Foo Repository │
|
||||||
|
│ ┌────────┐ ┌────────┐ ┌─────────┐ │
|
||||||
|
│ │ code │ │ issues │ │ patches │ │
|
||||||
|
│ └────────┘ └────────┘ └─────────┘ │
|
||||||
|
├───────────────────────────────────┤
|
||||||
|
│ Foo Storage (Git) │
|
||||||
|
└───────────────────────────────────┘
|
||||||
|
┌────────────────┐┌─────────────────┐
|
||||||
|
│ Foo Node ││ Foo HTTPD │
|
||||||
|
├────────────────┤├─────────────────┤
|
||||||
|
│ NoiseXK ││ HTTP + JSON │
|
||||||
|
└────────────────┘└─────────────────┘
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${footer}
|
||||||
|
|
@ -3,25 +3,28 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<base href="${opts.baseurl}" />
|
||||||
<title>XRForge - selfsovereign interoperable XR experiences</title>
|
<title>XRForge - selfsovereign interoperable XR experiences</title>
|
||||||
<link rel="stylesheet" href="xrforge.css"></link>
|
<link rel="stylesheet" href="css/xrforge.css"></link>
|
||||||
</head>
|
</head>
|
||||||
<body background="#000000">
|
<body background="#000000">
|
||||||
|
|
||||||
<table width="100%" class="header">
|
<table width="100%" class="header">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:120px">
|
<td width="100">
|
||||||
<div class="margin1L">
|
<div class="margin1L">
|
||||||
|
<a href="${opts.baseurl}">
|
||||||
<div class="logo col1">XR<div class="logo col2">f̅o̅r̅g̅e̅</div>
|
<div class="logo col1">XR<div class="logo col2">f̅o̅r̅g̅e̅</div>
|
||||||
</div>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="menu">
|
<td class="menu">
|
||||||
<div class="margin1L">
|
<div class="margin1L">
|
||||||
<a href="#">Experiences</a>
|
<a href="#">Experiences</a><br/>
|
||||||
<a href="#">Collections</a>
|
<a href="#">Collections</a><br/>
|
||||||
<a href="#">About</a>
|
<a href="#">About</a><br/>
|
||||||
<a href="#">Howto</a>
|
<a href="#">Howto</a><br/>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -4,7 +4,7 @@ if not app.cache['index.html'] then
|
||||||
app.cache['index.html'] = util.memoize(
|
app.cache['index.html'] = util.memoize(
|
||||||
function()
|
function()
|
||||||
print("processing!")
|
print("processing!")
|
||||||
local html = util.template( LoadAsset("template/index.html"),app)
|
local html = util.template( LoadAsset("page/index.html"),app)
|
||||||
return html
|
return html
|
||||||
end,
|
end,
|
||||||
60*5 -- re-run every 5 mins (otherwise return cache)
|
60*5 -- re-run every 5 mins (otherwise return cache)
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
../result/bin/xrforge.com -D . -p 8081 -- server
|
|
||||||
147
src/ui-kit.html
147
src/ui-kit.html
File diff suppressed because one or more lines are too long
1
src/xrforge.lua
Symbolic link
1
src/xrforge.lua
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../result/bin/xrforge.lua
|
||||||
|
|
@ -25,7 +25,7 @@ download(){
|
||||||
build(){
|
build(){
|
||||||
download redbean
|
download redbean
|
||||||
cd src
|
cd src
|
||||||
zip -r ../result/bin/xrforge.com * .lua .init.lua
|
zip -r ../result/bin/xrforge.com * .lua .init.lua .args
|
||||||
}
|
}
|
||||||
|
|
||||||
"$@"
|
"$@"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue