|
|
@ -12,15 +12,10 @@ const port = process.env.PORT || 8082; |
|
|
|
const protocol = 'https' |
|
|
|
const protocol = 'https' |
|
|
|
const subclass = '192.168.4.' |
|
|
|
const subclass = '192.168.4.' |
|
|
|
|
|
|
|
|
|
|
|
// Setup and configure Express http server.
|
|
|
|
const publicKeyPath = path.resolve(process.env.HOME,'.ssh','id_rsa_offlineoctopus.pub') |
|
|
|
const app = express(); |
|
|
|
const publicKey = fs.readFileSync(publicKeyPath).toString().split(' ')[1] |
|
|
|
app.use(express.static(path.resolve(__dirname, ".", "examples"))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.use(function(req, res, next) { |
|
|
|
const md5fromPub = fs.readFileSync( path.resolve(__dirname, ".keyfrommd5")).toString().replace('\n','') |
|
|
|
res.header("Access-Control-Allow-Origin", "*");
|
|
|
|
|
|
|
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); |
|
|
|
|
|
|
|
next(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
process.title = 'offtopus' // offline-octopus seems too long
|
|
|
|
process.title = 'offtopus' // offline-octopus seems too long
|
|
|
|
|
|
|
|
|
|
|
@ -69,16 +64,14 @@ const utilsCmd = { // security risk but for now not accepting user input so safe |
|
|
|
// security risk if relying on user provided name, e.g replacing acorn by user input
|
|
|
|
// security risk if relying on user provided name, e.g replacing acorn by user input
|
|
|
|
// example that could be generalized to other package managers e.g .deb or opkg
|
|
|
|
// example that could be generalized to other package managers e.g .deb or opkg
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// could be interesting to consider also recent containers and ~/.bashrc for services
|
|
|
|
// example to disentangle own work for cloning existing repositories
|
|
|
|
|
|
|
|
// find ~/Prototypes/ -depth -maxdepth 4 -wholename "*/.git/config" | xargs grep -l git.benetou.fr | sed "s|.*Prototypes/\(.*\)/.git/config|\1|"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const instructions = ` |
|
|
|
const instructions = ` |
|
|
|
/home/deck/.ssh/ |
|
|
|
/home/deck/.ssh/ |
|
|
|
trusted context, i.e on closed WiFi and over https, still. could check as bearer |
|
|
|
trusted context, i.e on closed WiFi and over https with bearer authorization |
|
|
|
/home/deck/.ssh/config |
|
|
|
/home/deck/.ssh/config |
|
|
|
could limit to known IP range or class e.g cat .ssh/config | grep 192.168.4. -C 3 |
|
|
|
limit to known IP subclass e.g cat .ssh/config | grep 192.168.4. -C 3 |
|
|
|
|
|
|
|
see /sshconfig |
|
|
|
could also re-add new entries rather than extend the format |
|
|
|
could also re-add new entries rather than extend the format |
|
|
|
/home/deck/server.locatedb |
|
|
|
/home/deck/server.locatedb |
|
|
|
seems to be plain text with metadata |
|
|
|
seems to be plain text with metadata |
|
|
@ -99,6 +92,17 @@ util functions |
|
|
|
|
|
|
|
|
|
|
|
const auth_instructions = `generate md5 from pub offline-octopus then provide as bearer query param` |
|
|
|
const auth_instructions = `generate md5 from pub offline-octopus then provide as bearer query param` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Setup and configure Express http server.
|
|
|
|
|
|
|
|
const app = express(); |
|
|
|
|
|
|
|
app.use(express.static(path.resolve(__dirname, ".", "examples"))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CORS
|
|
|
|
|
|
|
|
app.use(function(req, res, next) { |
|
|
|
|
|
|
|
res.header("Access-Control-Allow-Origin", "*");
|
|
|
|
|
|
|
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); |
|
|
|
|
|
|
|
next(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
app.get('/', (req, res) => { |
|
|
|
app.get('/', (req, res) => { |
|
|
|
res.send( instructions ) |
|
|
|
res.send( instructions ) |
|
|
|
}) |
|
|
|
}) |
|
|
@ -108,6 +112,7 @@ app.get('/authtestviaheader', (req, res) => { |
|
|
|
res.sendStatus(200) |
|
|
|
res.sendStatus(200) |
|
|
|
// fetch('/authtestviaheader', {headers: {'authorization': 'Bearer '+ bearer}}).then(r=>r.text()).then(t=>console.log(t))
|
|
|
|
// fetch('/authtestviaheader', {headers: {'authorization': 'Bearer '+ bearer}}).then(r=>r.text()).then(t=>console.log(t))
|
|
|
|
// prevents from showing in browser history but also makes testing slightly harder
|
|
|
|
// prevents from showing in browser history but also makes testing slightly harder
|
|
|
|
|
|
|
|
// consider next() for middleware instead of copy/pasting this line
|
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
app.get('/authtest', (req, res) => { |
|
|
|
app.get('/authtest', (req, res) => { |
|
|
@ -270,18 +275,40 @@ app.get('/sshconfig', (req, res) => { |
|
|
|
// should filter on foundPeers to avoid offline peers
|
|
|
|
// should filter on foundPeers to avoid offline peers
|
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
// returns connections string for e.g sshfs
|
|
|
|
// note that stopping this process removes the mounts
|
|
|
|
|
|
|
|
function mountAll(){ |
|
|
|
|
|
|
|
getSshConfig().map( l => { |
|
|
|
|
|
|
|
let cs = 'sshfs ' + l.name + ':' |
|
|
|
|
|
|
|
if (l.custom) |
|
|
|
|
|
|
|
cs+= l.custom |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
cs+='/home/'+l.user |
|
|
|
|
|
|
|
return cs + ' ' + path.resolve(__dirname, "sshfsmounts", l.name) |
|
|
|
|
|
|
|
} ) |
|
|
|
|
|
|
|
.map( l => execSync(l)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function getSshConfig(){ |
|
|
|
function getSshConfig(){ |
|
|
|
// here assume user deck
|
|
|
|
|
|
|
|
let txt = fs.readFileSync(sshconfigpath).toString() |
|
|
|
let txt = fs.readFileSync(sshconfigpath).toString() |
|
|
|
return txt.split('Host ') |
|
|
|
return txt.split('Host ') |
|
|
|
.filter( m => m.match(subclass) ) |
|
|
|
.filter( m => m.match(subclass) ) |
|
|
|
.map( c => { |
|
|
|
.map( c => { |
|
|
|
p = c.replaceAll(' ','') |
|
|
|
let all = c.replaceAll(' ','') |
|
|
|
.split('\n') |
|
|
|
.split('\n') |
|
|
|
|
|
|
|
let p = all |
|
|
|
.filter(i=>i.match(/^[a-zA-Z]/)); |
|
|
|
.filter(i=>i.match(/^[a-zA-Z]/)); |
|
|
|
return p.filter(pm=>pm.match('User '))[0].replace('User ','')+ |
|
|
|
let custom = all |
|
|
|
p.filter(pm=>pm.match('HostName'))[0].replace('HostName ','@') |
|
|
|
.filter(i=>i.match(/^#Dir /))[0] |
|
|
|
|
|
|
|
?.split(' ')[1] |
|
|
|
|
|
|
|
// custom ~/.ssh/config parameter as comment
|
|
|
|
|
|
|
|
// user here for home directory in termux
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let user = p.filter(pm=>pm.match('User '))[0].replace('User ','') |
|
|
|
|
|
|
|
let hostname = p.filter(pm=>pm.match('HostName'))[0].replace('HostName ','') |
|
|
|
|
|
|
|
let connectionString = user + '@' + hostname |
|
|
|
|
|
|
|
let port = p.filter(pm=>pm.match('Port'))[0]?.replace('Port ','') |
|
|
|
|
|
|
|
if (port) connectionString += ':' + port |
|
|
|
|
|
|
|
return {name: p[0], user, connectionString, custom} |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -301,14 +328,11 @@ function getSshConfig(){ |
|
|
|
easier to revoke if need be |
|
|
|
easier to revoke if need be |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
const publicKeyPath = path.resolve(process.env.HOME,'.ssh','id_rsa_offlineoctopus.pub') |
|
|
|
|
|
|
|
const publicKey = fs.readFileSync(publicKeyPath).toString().split(' ')[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const md5fromPub = fs.readFileSync( path.resolve(__dirname, ".keyfrommd5")).toString().replace('\n','') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.get('/localprototypes', (req, res) => { |
|
|
|
app.get('/localprototypes', (req, res) => { |
|
|
|
// res.json( spawn('find Prototypes/ -iwholename */.git/config | xargs grep git.benetou.fr') )
|
|
|
|
// examples to disentangle own work for cloned existing repositories :
|
|
|
|
// should use execSync now
|
|
|
|
// find Prototypes/ -iwholename */.git/config | xargs grep git.benetou.fr
|
|
|
|
|
|
|
|
// find ~/Prototypes/ -depth -maxdepth 4 -wholename "*/.git/config" | xargs grep -l git.benetou.fr | sed "s|.*Prototypes/\(.*\)/.git/config|\1|"
|
|
|
|
|
|
|
|
res.json( execConfiguredCommand('listprototypes') ) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
app.get('/editor/recover', (req, res) => { |
|
|
|
app.get('/editor/recover', (req, res) => { |
|
|
|