housekeeping

Move bulk of server .js file files to /server, all client related files
live in /client (including /public), webpack scripts now live in
/scripts, cleanup of paths in /client/src/js/index.js and webpack.js.
This commit is contained in:
billchurch 2018-02-16 23:48:00 -05:00
parent cd3ac433e8
commit bc19432dfa
21 changed files with 179 additions and 73 deletions

View file

@ -0,0 +1,63 @@
#!/bin/bash
# Filename: /config/startup_script_webssh_commands.sh
# Initializes WebSSH2 tmm-to-node listener
# WebSSHVSIP should be the IP on an existing BIGIP virtual server assigned to
# the WebSSH2 service.
#
# bill@f5.com February 2018
export myFileName=$0
export REMOTEUSER=root
export WEBSSHVSIP=
# check to see if we're in /config/startup, if not add ourselves
IN_STARTUP=`grep startup_script_webssh_commands.sh /config/startup | wc -l`
if [ $IN_STARTUP -eq 0 ]; then
echo Adding script to /config/startup and ensuring correct permissions...
logger -p local0.notice -t $myFileName Adding $0 to /config/startup and ensuring correct permissions...
chmod 755 /config/startup
chmod 755 /config/startup_script_webssh_commands.sh
echo /config/startup_script_webssh_commands.sh \& >> /config/startup
echo >> /config/startup
fi
# Limit to 13 times in while-loop, ie. 12 x 10 secs sleep = 2 mins.
MAX_LOOP=13
while true
do
# check to see if tmm interface is up
IPLINKLIST=$(ip link list tmm 2>&1)
if [ $? -eq 0 ]; then
if [ ! -z $WEBSSHVSIP ]; then
IPADDRADD=$(/sbin/ip addr add $WEBSSHVSIP/32 dev tmm 2>&1)
if [ $? -eq 0 ]; then
# success
echo SUCCESS $IPADDRADD
logger -p local0.notice -t $myFileName IPADDRADD: SUCCESS: $IPADDRADD
else
# failure
echo FAILURE $IPADDRADD
logger -p local0.notice -t $myFileName IPADDRADD: FAILURE: $IPADDRADD
fi
else
echo FAILURE: WEBSSHVSIP not specified.
echo Open $0 and set the WEBSSHVSIP and try again
echo
logger -p local0.notice -t $myFileName IPADDRADD: FAILURE: NO WEBSSHVSIP SPECIFIED
fi
exit
fi
# If tmm interface is not up yet, script sleep 10 seconds and check again.
sleep 10
# Safety check not to run this script in background beyond 2 mins (ie. 12 times in while-loop).
if [ "$MAX_LOOP" -eq 1 ]; then
logger -p local0.notice -t $myFileName tmm interface not up within 2 minutes. Exiting script.
logger -p local0.notice -t $myFileName IPLINKLIST: $IPLINKLIST
exit
fi
((MAX_LOOP--))
done
# End of file /config/startup_script_webssh_commands.sh

View file

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html> <html>
<head> <head>
<title>WebSSH2</title> <title>WebSSH2</title>

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
.xterm{font-family:courier-new,courier,monospace;font-feature-settings:"liga" 0;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:10}.xterm .xterm-helper-textarea{position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-10;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll}.xterm canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm .xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm:not(.enable-mouse-events){cursor:text}body{font-family:helvetica,sans-serif;font-size:1em;color:#111;background-color:#000;color:#f0f0f0;height:100%;margin:0}#header{color:#f0f0f0;background-color:green;width:100%;border-color:#fff;border-style:none none solid;border-width:1px;text-align:center;flex:0 1 auto;z-index:99;height:19px;display:none}.box{display:block;height:100%}#terminal-container{display:block;width:calc(1 - 1 px);margin:0 auto;padding:2px;height:calc(100% - 19px)}#terminal-container .terminal{background-color:#000;color:#fafafa;padding:2px;height:calc(100% - 19px)}#terminal-container .terminal:focus .terminal-cursor{background-color:#fafafa}#bottomdiv{position:fixed;left:0;bottom:0;width:100%;background-color:#323232;border-color:#fff;border-style:solid none none;border-width:1px;z-index:99;height:19px}#footer{padding-left:5px;padding-right:5px;border-style:none none none solid}#footer,#status{display:inline-block;color:#f0f0f0;background-color:#323232;border-color:#fff;border-width:1px;text-align:left}#status{padding-right:10px;border-style:none solid}#menu,#status{padding-left:10px;z-index:100}#menu{display:inline-block;font-size:16px;color:#fff}#menu:hover .dropup-content{display:block}.dropup{position:relative;display:inline-block}.dropup-content{display:none;position:absolute;background-color:#f1f1f1;font-size:16px;min-width:160px;bottom:18px;z-index:101}.dropup-content a{color:#777;padding:12px 16px;text-decoration:none;display:block}.dropup-content a:hover{background-color:#ccc}.dropup:click .dropup-content,.dropup:hover .dropup-content{display:block}.dropup:hover .dropbtn{background-color:#3e8e41} .xterm{font-family:courier-new,courier,monospace;font-feature-settings:"liga" 0;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:10}.xterm .xterm-helper-textarea{position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-10;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll}.xterm canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm .xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm:not(.enable-mouse-events){cursor:text}body{font-family:helvetica,sans-serif;font-size:1em;color:#111;background-color:#000;color:#f0f0f0;height:100%;margin:0}#header{color:#f0f0f0;background-color:green;width:100%;border-color:#fff;border-style:none none solid;border-width:1px;text-align:center;flex:0 1 auto;z-index:99;height:19px;display:none}.box{display:block;height:100%}#terminal-container{display:block;width:calc(1 - 1 px);margin:0 auto;padding:2px;height:calc(100% - 19px)}#terminal-container .terminal{background-color:#000;color:#fafafa;padding:2px;height:calc(100% - 19px)}#terminal-container .terminal:focus .terminal-cursor{background-color:#fafafa}#bottomdiv{position:fixed;left:0;bottom:0;width:100%;background-color:#323232;border-color:#fff;border-style:solid none none;border-width:1px;z-index:99;height:19px}#footer{padding-left:5px;padding-right:5px;border-style:none none none solid}#footer,#status{display:inline-block;color:#f0f0f0;background-color:#323232;border-color:#fff;border-width:1px;text-align:left}#status{padding-right:10px;border-style:none solid}#menu,#status{padding-left:10px;z-index:100}#menu{display:inline-block;font-size:16px;color:#fff}#menu:hover .dropup-content{display:block}#credentialsBtn,#logBtn{color:#000}.dropup{position:relative;display:inline-block;cursor:pointer}.dropup-content{display:none;position:absolute;background-color:#f1f1f1;font-size:16px;min-width:160px;bottom:18px;z-index:101}.dropup-content a{color:#777;padding:12px 16px;text-decoration:none;display:block}.dropup-content a:hover{background-color:#ccc}.dropup:click .dropup-content,.dropup:hover .dropup-content{display:block}.dropup:hover .dropbtn{background-color:#3e8e41}

View file

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html> <html>
<head> <head>
<title>WebSSH2</title> <title>WebSSH2</title>

View file

@ -85,9 +85,16 @@ body {
#menu:hover .dropup-content { #menu:hover .dropup-content {
display: block; display: block;
} }
#logBtn {
color: #000;
}
#credentialsBtn {
color: #000;
}
.dropup { .dropup {
position: relative; position: relative;
display: inline-block; display: inline-block;
cursor: pointer;
} }
.dropup-content { .dropup-content {
display: none; display: none;

View file

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,8 +1,10 @@
'use strict' 'use strict'
import * as io from '../../node_modules/socket.io-client/dist/socket.io.js' import * as io from 'socket.io-client/dist/socket.io.js'
import * as Terminal from '../../node_modules/xterm/dist/xterm' import * as Terminal from 'xterm/dist/xterm'
import * as fit from '../../node_modules/xterm/dist/addons/fit/fit' import * as fit from 'xterm/dist/addons/fit/fit'
// fontawesome, individual icon imports reduces file size dramatically but it's
// a little messy. this should be fixed by some updates with the fa library at some point
import fontawesome from '@fortawesome/fontawesome' import fontawesome from '@fortawesome/fontawesome'
import faBars from '@fortawesome/fontawesome-free-solid/faBars' import faBars from '@fortawesome/fontawesome-free-solid/faBars'
// import faQuestion from '@fortawesome/fontawesome-free-solid/faQuestion' // import faQuestion from '@fortawesome/fontawesome-free-solid/faQuestion'
@ -10,31 +12,29 @@ import faClipboard from '@fortawesome/fontawesome-free-solid/faClipboard'
import faDownload from '@fortawesome/fontawesome-free-solid/faDownload' import faDownload from '@fortawesome/fontawesome-free-solid/faDownload'
import faKey from '@fortawesome/fontawesome-free-solid/faKey' import faKey from '@fortawesome/fontawesome-free-solid/faKey'
import faCog from '@fortawesome/fontawesome-free-solid/faCog' import faCog from '@fortawesome/fontawesome-free-solid/faCog'
fontawesome.library.add(faBars, faClipboard, faDownload, faKey, faCog) fontawesome.library.add(faBars, faClipboard, faDownload, faKey, faCog)
fontawesome.config.searchPseudoElements = true fontawesome.config.searchPseudoElements = true
fontawesome.dom.i2svg() fontawesome.dom.i2svg()
require('../../node_modules/xterm/dist/xterm.css') require('xterm/dist/xterm.css')
require('../css/style.css') require('../css/style.css')
Terminal.applyAddon(fit) Terminal.applyAddon(fit)
/* global Blob */ /* global Blob, logBtn, credentialsBtn, downloadLogBtn */
var sessionLogEnable = false var sessionLogEnable = false
var loggedData = false var loggedData = false
var allowreplay = false
var sessionLog, sessionFooter, logDate, currentDate, myFile, errorExists var sessionLog, sessionFooter, logDate, currentDate, myFile, errorExists
var statusID = document.getElementById('status')
var headerID = document.getElementById('header')
var menuID = document.getElementById('dropupContent')
var terminalContainer = document.getElementById('terminal-container')
var socket, termid // eslint-disable-line var socket, termid // eslint-disable-line
var term = new Terminal() var term = new Terminal()
// DOM properties
var status = document.getElementById('status')
var header = document.getElementById('header')
var dropupContent = document.getElementById('dropupContent')
var footer = document.getElementById('footer')
var terminalContainer = document.getElementById('terminal-container')
term.open(terminalContainer) term.open(terminalContainer)
term.focus() term.focus()
term.fit() term.fit()
@ -57,76 +57,104 @@ if (document.location.pathname) {
socket = io.connect() socket = io.connect()
} }
term.on('data', function (data) {
socket.emit('data', data)
})
socket.on('data', function (data) {
term.write(data)
if (sessionLogEnable) {
sessionLog = sessionLog + data
}
})
socket.on('connect', function () { socket.on('connect', function () {
socket.emit('geometry', term.cols, term.rows) socket.emit('geometry', term.cols, term.rows)
}) })
socket.on('setTerminalOpts', function (data) { socket.on('setTerminalOpts', function (data) {
term.setOption('cursorBlink', data.cursorBlink) term.setOption('cursorBlink', data.cursorBlink)
term.setOption('scrollback', data.scrollback) term.setOption('scrollback', data.scrollback)
term.setOption('tabStopWidth', data.tabStopWidth) term.setOption('tabStopWidth', data.tabStopWidth)
}) })
term.on('data', function (data) {
socket.emit('data', data)
})
socket.on('title', function (data) { socket.on('title', function (data) {
document.title = data document.title = data
}).on('menu', function (data) { })
menuID.innerHTML = data
var downloadLogBtn = document.getElementById('downloadLogBtn') socket.on('menu', function (data) {
var credentialsBtn = document.getElementById('credentialsBtn') drawMenu(data)
var logBtn = document.getElementById('logBtn') })
logBtn.addEventListener('click', toggleLog)
logBtn.style.color = '#000' socket.on('status', function (data) {
}).on('status', function (data) { status.innerHTML = data
statusID.innerHTML = data })
}).on('ssherror', function (data) {
statusID.innerHTML = data socket.on('ssherror', function (data) {
statusID.style.backgroundColor = 'red' status.innerHTML = data
status.style.backgroundColor = 'red'
errorExists = true errorExists = true
}).on('headerBackground', function (data) { })
headerID.style.backgroundColor = data
}).on('header', function (data) { socket.on('headerBackground', function (data) {
header.style.backgroundColor = data
})
socket.on('header', function (data) {
if (data) { if (data) {
headerID.innerHTML = data header.innerHTML = data
headerID.style.display = 'block' header.style.display = 'block'
// header is 19px and footer is 19px, recaculate new terminal-container and resize // header is 19px and footer is 19px, recaculate new terminal-container and resize
terminalContainer.style.height = 'calc(100% - 38px)' terminalContainer.style.height = 'calc(100% - 38px)'
resizeScreen() resizeScreen()
} }
}).on('footer', function (data) { })
socket.on('footer', function (data) {
sessionFooter = data sessionFooter = data
document.getElementById('footer').innerHTML = data footer.innerHTML = data
}).on('statusBackground', function (data) { })
statusID.style.backgroundColor = data
}).on('allowreplay', function (data) { socket.on('statusBackground', function (data) {
status.style.backgroundColor = data
})
socket.on('allowreplay', function (data) {
if (data === true) { if (data === true) {
console.log('allowreplay: ' + data) console.log('allowreplay: ' + data)
menuID.innerHTML = menuID.innerHTML + '<a id="credentialsBtn" href="javascript:void(0);"><i class="fas fa-key fa-fw"></i> Credentials</a>' allowreplay = true
credentialsBtn.style.color = '#000' drawMenu(dropupContent.innerHTML + '<a id="credentialsBtn"><i class="fas fa-key fa-fw"></i> Credentials</a>')
credentialsBtn.addEventListener('click', replayCredentials)
} else { } else {
allowreplay = false
console.log('allowreplay: ' + data) console.log('allowreplay: ' + data)
credentialsBtn.style.color = '#666'
} }
}).on('data', function (data) { })
term.write(data)
if (sessionLogEnable) { socket.on('disconnect', function (err) {
sessionLog = sessionLog + data
}
}).on('disconnect', function (err) {
if (!errorExists) { if (!errorExists) {
statusID.style.backgroundColor = 'red' status.style.backgroundColor = 'red'
statusID.innerHTML = status.innerHTML =
'WEBSOCKET SERVER DISCONNECTED: ' + err 'WEBSOCKET SERVER DISCONNECTED: ' + err
} }
socket.io.reconnection(false) socket.io.reconnection(false)
}).on('error', function (err) { })
socket.on('error', function (err) {
if (!errorExists) { if (!errorExists) {
statusID.style.backgroundColor = 'red' status.style.backgroundColor = 'red'
statusID.innerHTML = 'ERROR: ' + err status.innerHTML = 'ERROR: ' + err
} }
}) })
// draw/re-draw menu and reattach listeners
// when dom is changed, listeners are abandonded
function drawMenu (data) {
dropupContent.innerHTML = data
logBtn.addEventListener('click', toggleLog)
allowreplay && credentialsBtn.addEventListener('click', replayCredentials)
loggedData && downloadLogBtn.addEventListener('click', downloadLog)
}
// replay password to server, requires // replay password to server, requires
function replayCredentials () { // eslint-disable-line function replayCredentials () { // eslint-disable-line
socket.emit('control', 'replayCredentials') socket.emit('control', 'replayCredentials')

View file

@ -6,8 +6,8 @@
*/ */
'use strict' 'use strict'
var config = require('./app').config var config = require('./server/app').config
var server = require('./app').server var server = require('./server/app').server
server.listen({ host: config.listen.ip, port: config.listen.port server.listen({ host: config.listen.ip, port: config.listen.port
}) })

View file

@ -34,17 +34,17 @@
"express": "^4.16.1", "express": "^4.16.1",
"express-session": "^1.15.6", "express-session": "^1.15.6",
"morgan": "^1.9.0", "morgan": "^1.9.0",
"read-config": "^2.0.0", "read-config": "^1.6.0",
"socket.io": "^2.0.4", "socket.io": "^2.0.4",
"ssh2": "^0.5.5", "ssh2": "^0.5.5",
"validator": "^9.0.0" "validator": "^9.0.0"
}, },
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
"builddev": "webpack --config webpack.dev.js", "build": "webpack --progress --colors --config scripts/webpack.prod.js",
"builddev": "webpack --progress --colors --config scripts/webpack.dev.js",
"test": "snyk test", "test": "snyk test",
"watch": "nodemon index.js", "watch": "nodemon index.js",
"build": "webpack --config webpack.prod.js",
"standard": "standard --verbose | snazzy", "standard": "standard --verbose | snazzy",
"cleanmac": "find . -name '.DS_Store' -type f -delete" "cleanmac": "find . -name '.DS_Store' -type f -delete"
}, },
@ -72,8 +72,10 @@
}, },
"standard": { "standard": {
"ignore": [ "ignore": [
"public/webssh2.bundle.js", "client/public/webssh2.bundle.js",
"bigip/*" "bigip/*",
"screenshots/*",
"bin/*"
] ]
} }
} }

File diff suppressed because one or more lines are too long

View file

@ -2,22 +2,25 @@ const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin') const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin') const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = { module.exports = {
context: path.resolve('__dirname', '../'),
entry: { entry: {
webssh2: './src/js/index.js' webssh2: './client/src/js/index.js'
}, },
plugins: [ plugins: [
new CleanWebpackPlugin(['./public']), new CleanWebpackPlugin(['client/public'], {
root: path.resolve('__dirname', '../'),
verbose: true
}),
new CopyWebpackPlugin([ new CopyWebpackPlugin([
'./src/client.htm', './client/src/client.htm',
'./src/favicon.ico' './client/src/favicon.ico'
]), ]),
new ExtractTextPlugin('[name].css') new ExtractTextPlugin('[name].css')
], ],
output: { output: {
filename: '[name].bundle.js', filename: '[name].bundle.js',
path: path.resolve(__dirname, './public') path: path.resolve(__dirname, '../client/public')
}, },
module: { module: {
rules: [ rules: [

View file

@ -4,6 +4,6 @@ const common = require('./webpack.common.js')
module.exports = merge(common, { module.exports = merge(common, {
devtool: 'inline-source-map', devtool: 'inline-source-map',
devServer: { devServer: {
contentBase: './public' contentBase: '../client/public'
} }
}) })

View file

@ -3,6 +3,7 @@
var path = require('path') var path = require('path')
// configPath = path.join(__dirname, 'config.json') // configPath = path.join(__dirname, 'config.json')
var configPath = path.join(path.dirname(require.main.filename), 'config.json') var configPath = path.join(path.dirname(require.main.filename), 'config.json')
var publicPath = path.join(path.dirname(require.main.filename), 'client', 'public')
console.log('Reading config from: ' + configPath) console.log('Reading config from: ' + configPath)
var config = require('read-config')(configPath) var config = require('read-config')(configPath)
var express = require('express') var express = require('express')
@ -31,10 +32,10 @@ if (config.accesslog) app.use(logger('common'))
app.disable('x-powered-by') app.disable('x-powered-by')
// static files // static files
app.use(express.static(path.join(__dirname, 'public'), expressOptions)) app.use(express.static(publicPath, expressOptions))
app.get('/ssh/host/:host?', function (req, res, next) { app.get('/ssh/host/:host?', function (req, res, next) {
res.sendFile(path.join(path.join(__dirname, 'public', 'client.htm'))) res.sendFile(path.join(path.join(publicPath, 'client.htm')))
// capture, assign, and validated variables // capture, assign, and validated variables
req.session.ssh = { req.session.ssh = {
host: (validator.isIP(req.params.host + '') && req.params.host) || host: (validator.isIP(req.params.host + '') && req.params.host) ||

View file

@ -7,7 +7,7 @@ var SSH = require('ssh2').Client
// var fs = require('fs') // var fs = require('fs')
// var hostkeys = JSON.parse(fs.readFileSync('./hostkeyhashes.json', 'utf8')) // var hostkeys = JSON.parse(fs.readFileSync('./hostkeyhashes.json', 'utf8'))
var termCols, termRows var termCols, termRows
var menuData = '<a id="logBtn" href="javascript:void(0);"><i class="fas fa-clipboard fa-fw"></i> Start Log</a><a id="downloadLogBtn" href="javascript:void(0);"><i class="fas fa-download fa-fw"></i> Download Log</a>' var menuData = '<a id="logBtn"><i class="fas fa-clipboard fa-fw"></i> Start Log</a><a id="downloadLogBtn"><i class="fas fa-download fa-fw"></i> Download Log</a>'
// public // public
module.exports = function socket (socket) { module.exports = function socket (socket) {