@ -9,9 +9,8 @@ const express = require("express"); // could be good to replace with c
// Get port or default to 8082
// Get port or default to 8082
const port = process . env . PORT || 8082 ;
const port = process . env . PORT || 8082 ;
const protocol = 'https'
const protocol = process . env . PROTOCOL || 'https'
const subclass = '10.160.168.'
const subclass = process . env . SUBCLASS || '192.168.4.'
//const subclass = '192.168.4.'
const publicKeyPath = path . resolve ( process . env . HOME , '.ssh' , 'id_rsa_offlineoctopus.pub' )
const publicKeyPath = path . resolve ( process . env . HOME , '.ssh' , 'id_rsa_offlineoctopus.pub' )
const publicKey = fs . readFileSync ( publicKeyPath ) . toString ( ) . split ( ' ' ) [ 1 ]
const publicKey = fs . readFileSync ( publicKeyPath ) . toString ( ) . split ( ' ' ) [ 1 ]
@ -28,6 +27,12 @@ const minfileSaveFullPath = path.join(__dirname,'examples', minfilename)
const sshconfigpath = path . resolve ( process . env . HOME , '.ssh' , 'config' )
const sshconfigpath = path . resolve ( process . env . HOME , '.ssh' , 'config' )
const propath = path . resolve ( process . env . HOME , 'Prototypes' )
const propath = path . resolve ( process . env . HOME , 'Prototypes' )
const sshfsmounts = "sshfsmounts"
let workspaces = { }
let mountPoints = [ ]
// note that stopping this process removes the mounts
// does not apply in a P2P fashion, must rely on local configuration here config.json
// does not apply in a P2P fashion, must rely on local configuration here config.json
let localServices = [ ]
let localServices = [ ]
const configFilePath = path . resolve ( _ _dirname , "config.json" )
const configFilePath = path . resolve ( _ _dirname , "config.json" )
@ -232,7 +237,8 @@ app.get('/hmdlink', (req, res) => {
} )
} )
app . get ( '/webxr' , ( req , res ) => {
app . get ( '/webxr' , ( req , res ) => {
res . redirect ( '/local-metaverse-tooling/local-aframe-test.html' )
res . redirect ( '/spasca-offline/engine/index.html' )
//res.redirect( '/local-metaverse-tooling/local-aframe-test.html' )
} )
} )
// user for /scan to populate foundPeers
// user for /scan to populate foundPeers
@ -251,9 +257,8 @@ app.get('/scan', (req, res) => {
} )
} )
function scanpeers ( ) {
function scanpeers ( ) {
sendEventsToAll ( { 'action' : 'scanning started' } )
foundPeers = [ ]
foundPeers = [ ]
for ( let i = 1 ; i < 25 ; i ++ ) { // async so blasting, gives very quick result for positives
for ( let i = 1 ; i < 254 ; i ++ ) { // async so blasting, gives very quick result for positives
let url = protocol + '://' + subclass + i + ':' + port + '/available'
let url = protocol + '://' + subclass + i + ':' + port + '/available'
let opt = { rejectUnauthorized : false }
let opt = { rejectUnauthorized : false }
https . get ( url , opt , res => {
https . get ( url , opt , res => {
@ -274,20 +279,57 @@ function scanpeers(){
app . get ( '/sshconfig' , ( req , res ) => {
app . get ( '/sshconfig' , ( req , res ) => {
res . json ( getSshConfig ( ) )
res . json ( getSshConfig ( ) )
// should filter on foundPeers to avoid offline peers
} )
} )
// note that stopping this process removes the mounts
app . get ( '/sshconfig/live' , ( req , res ) => {
res . json ( getSshConfig ( ) . filter ( i => foundPeers . map ( e => subclass + e ) . filter ( x => i . connectionString . match ( x ) ) . length ) )
} )
app . get ( '/unifiedworkspaces' , ( req , res ) => {
res . json ( workspaces )
} )
function getUnifiedworkspaces ( ) {
mountPoints . map ( mp => {
let dir = path . resolve ( _ _dirname , sshfsmounts , mp )
workspaces [ mp ] = fs . readdirSync ( dir )
console . log ( 'reading' , mp , workspaces . mp )
} )
return workspaces
}
function mountAll ( ) {
function mountAll ( ) {
getSshConfig ( ) . map ( l => {
// should scanpeers() first
if ( foundPeers . length == 0 ) return
getSshConfig ( ) . filter ( i => foundPeers . map ( e => subclass + e ) . filter ( x => i . connectionString . match ( x ) ) . length )
. map ( l => {
let cs = 'sshfs ' + l . name + ':'
let cs = 'sshfs ' + l . name + ':'
if ( l . custom )
if ( l . custom )
cs += l . custom
cs += l . custom
else
else
cs += '/home/' + l . user
cs += '/home/' + l . user
return cs + ' ' + path . resolve ( _ _dirname , "sshfsmounts" , l . name )
let targetPath = path . resolve ( _ _dirname , sshfsmounts , l . name )
if ( ! fs . existsSync ( targetPath ) ) { fs . mkdirSync ( targetPath , { recursive : true } ) ; }
mountPoints . push ( l . name )
return cs + ' ' + targetPath
} )
} )
. map ( l => execSync ( l ) )
//.map( l => console.log(l))
. map ( l => { try { execSync ( l ) } catch ( err ) { console . log ( err ) } } )
}
function umountAll ( ) {
mpath = path . resolve ( _ _dirname , sshfsmounts )
fs . readdirSync ( mpath )
// could rely on mountPoints instead
. map ( f => {
try {
execSync ( 'umount ' + path . resolve ( _ _dirname , sshfsmounts , f ) )
// should update mountPoints instead
} catch ( err ) {
console . log ( err )
}
} )
mountPoints = [ ]
}
}
function getSshConfig ( ) {
function getSshConfig ( ) {
@ -381,30 +423,86 @@ const webServer = https.createServer(credentials, app);
// const webServer = http.createServer(app);
// const webServer = http.createServer(app);
// Listen on port
// Listen on port
webServer . listen ( port , ( ) => {
webServer . listen ( port , ( ) => {
console . log ( "listening on " + protocol + "://localhost:" + port ) ;
console . log ( "listening on " + protocol + "://localhost:" + port )
getCommand ( )
} ) ;
// REPL testing
const readline = require ( "readline" ) ;
const rl = readline . createInterface ( {
input : process . stdin ,
output : process . stdout ,
completer : completer
} ) ;
} ) ;
// SSE from https://www.digitalocean.com/community/tutorials/nodejs-server-sent-events-build-realtime-app
// https://nodejs.org/api/readline.html#use-of-the-completer-function
// adapted from jxr-permanence
function completer ( line ) {
let clients = [ ] ;
const completions = help ( ) . split ( '\n' ) ;
const hits = completions . filter ( ( c ) => c . startsWith ( line ) ) ;
function eventsHandler ( request , response , next ) {
// Show all completions if none found
const headers = {
return [ hits . length ? hits : completions , line ] ;
'Content-Type' : 'text/event-stream' ,
'Connection' : 'keep-alive' ,
'Cache-Control' : 'no-cache'
} ;
response . writeHead ( 200 , headers ) ;
const clientId = Date . now ( ) ;
const newClient = { id : clientId , response } ;
clients . push ( newClient ) ;
request . on ( 'close' , ( ) => { clients = clients . filter ( client => client . id !== clientId ) ; } ) ;
}
}
function sendEventsToAll ( data ) {
let command = ''
// function used to broadcast
function getCommand ( ) {
clients . forEach ( client => client . response . write ( ` data: ${ JSON . stringify ( data ) } \n \n ` ) )
rl . question ( process . title + " REPL: " , function ( command ) {
if ( command == "close" ) {
rl . close ( ) ;
} else {
try {
console . log ( command , eval ( command ) )
} catch ( err ) {
console . log ( err )
}
getCommand ( )
// somehow switch to node REPL proper after?!
}
} ) ;
}
}
app . get ( '/events' , eventsHandler ) ;
function help ( ) {
// for example /events.html shows when /scan begings (but not ends)
return `
help ( )
execConfiguredCommand ( cmdName )
getSshConfig ( )
mountAll ( )
umountAll ( )
scanpeers ( )
getUnifiedworkspaces ( )
foundPeers
port
protocol
subclass
publicKeyPath
publicKey
md5fromPub
process . title
filename
fileSaveFullPath
minfilename
minfileSaveFullPath
sshconfigpath
propath
localServices
configFilePath
utilsCmd
instructions
auth _instructions
process . title
sshfsmounts
mountPoints
workspaces
`
}
rl . on ( "close" , function ( ) {
console . log ( "\ndone" ) ;
umountAll ( )
process . exit ( 0 ) ;
} ) ;
// Demo Day target :
// show files from ~/Prototypes as cubes from ssh mounted on a virtual workspace
// sshfs might not even be needed, see allpeers/exec instead
// wouldn't easily get content back though, just meta data