Bifurcated into client and server repos

This commit is contained in:
Samuel Marks 2018-09-09 12:35:50 +10:00
parent 88f050bbcf
commit 809cf3e203
14 changed files with 5 additions and 785 deletions

3
.gitignore vendored
View file

@ -50,3 +50,6 @@ jspm_packages
#Mac Files #Mac Files
.DS_Store .DS_Store
#IDE
.idea

View file

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>WebSSH2</title>
<style>
html, body {background-color: #000;height: 100%;margin: 0;}.dropup-content {display: none;}
</style>
<link rel="stylesheet" href="/webssh2.css" />
</head>
<body>
<div class="box">
<div id="header"></div>
<div id="terminal-container" class="terminal"></div>
<div id="bottomdiv">
<div class="dropup" id="menu">
<i class="fas fa-bars fa-fw"></i> Menu
<div id="dropupContent" class="dropup-content"></div>
</div>
<div id="footer"></div>
<div id="status"></div>
</div>
</div>
<script src="/webssh2.bundle.js" defer></script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

View file

@ -1,287 +0,0 @@
/**
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
* @license MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/**
* Default styles for xterm.js
*/
.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;
/**
* The z-index of the helpers must be higher than the canvases in order for
* IMEs to appear on top.
*/
z-index: 10;
}
.xterm .xterm-helper-textarea {
/*
* HACK: to fix IE's blinking cursor
* Move textarea out of the screen to the far left, so that the cursor is not visible.
*/
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -10;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.xterm .composition-view {
/* TODO: Composition position got messed up somewhere */
background: #000;
color: #FFF;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.xterm .composition-view.active {
display: block;
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
}
.xterm .xterm-screen {
position: relative;
}
.xterm .xterm-screen canvas {
position: absolute;
left: 0;
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
top: 0;
left: -9999em;
line-height: normal;
}
.xterm {
cursor: text;
}
.xterm.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.xterm.xterm-cursor-pointer {
cursor: pointer;
}
.xterm.xterm-cursor-crosshair {
/* Column selection mode */
cursor: crosshair;
}
.xterm .xterm-accessibility,
.xterm .xterm-message {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 100;
color: transparent;
}
.xterm .live-region {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
body, html {
font-family: helvetica, sans-serif, arial;
font-size: 1em;
color: #111;
background-color: rgb(0, 0, 0);
color: rgb(240, 240, 240);
height: 100%;
margin: 0;
}
#header {
color: rgb(240, 240, 240);
background-color: rgb(0, 128, 0);
width: 100%;
border-color: white;
border-style: none none solid none;
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(100% - 1 px);
margin: 0 auto;
padding: 2px;
height: calc(100% - 19px);
}
#terminal-container .terminal {
background-color: #000000;
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: rgb(50, 50, 50);
border-color: white;
border-style: solid none none none;
border-width: 1px;
z-index: 99;
height: 19px;
}
#footer {
display: inline-block;
color: rgb(240, 240, 240);
background-color: rgb(50, 50, 50);
padding-left: 5px;
padding-right: 5px;
border-color: white;
border-style: none none none solid;
border-width: 1px;
text-align: left;
}
#status {
display: inline-block;
color: rgb(240, 240, 240);
background-color: rgb(50, 50, 50);
padding-left: 10px;
padding-right: 10px;
border-color: white;
border-style: none solid none solid;
border-width: 1px;
text-align: left;
z-index: 100;
}
#menu {
display: inline-block;
font-size: 16px;
color: rgb(255, 255, 255);
padding-left: 10px;
z-index: 100;
}
#menu:hover .dropup-content {
display: block;
}
#logBtn, #credentialsBtn, #reauthBtn {
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:hover .dropup-content {
display: block;
}
.dropup:click .dropup-content {
display: block;
}
.dropup:hover .dropbtn {
background-color: #3e8e41;
}

View file

@ -1 +0,0 @@
Customizations and modifications to the client (browser) go here. Then run "npm run build" to integrate into ../public (where client files are served from). Note that ../public is a flat directory structure. ../public directory is deleted and refreshed eatch thime "npm run build" is run.

View file

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>WebSSH2</title>
<style>
html, body {background-color: #000;height: 100%;margin: 0;}.dropup-content {display: none;}
</style>
<link rel="stylesheet" href="/webssh2.css" />
</head>
<body>
<div class="box">
<div id="header"></div>
<div id="terminal-container" class="terminal"></div>
<div id="bottomdiv">
<div class="dropup" id="menu">
<i class="fas fa-bars fa-fw"></i> Menu
<div id="dropupContent" class="dropup-content"></div>
</div>
<div id="footer"></div>
<div id="status"></div>
</div>
</div>
<script src="/webssh2.bundle.js" defer></script>
</body>
</html>

View file

@ -1,123 +0,0 @@
body, html {
font-family: helvetica, sans-serif, arial;
font-size: 1em;
color: #111;
background-color: rgb(0, 0, 0);
color: rgb(240, 240, 240);
height: 100%;
margin: 0;
}
#header {
color: rgb(240, 240, 240);
background-color: rgb(0, 128, 0);
width: 100%;
border-color: white;
border-style: none none solid none;
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(100% - 1 px);
margin: 0 auto;
padding: 2px;
height: calc(100% - 19px);
}
#terminal-container .terminal {
background-color: #000000;
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: rgb(50, 50, 50);
border-color: white;
border-style: solid none none none;
border-width: 1px;
z-index: 99;
height: 19px;
}
#footer {
display: inline-block;
color: rgb(240, 240, 240);
background-color: rgb(50, 50, 50);
padding-left: 5px;
padding-right: 5px;
border-color: white;
border-style: none none none solid;
border-width: 1px;
text-align: left;
}
#status {
display: inline-block;
color: rgb(240, 240, 240);
background-color: rgb(50, 50, 50);
padding-left: 10px;
padding-right: 10px;
border-color: white;
border-style: none solid none solid;
border-width: 1px;
text-align: left;
z-index: 100;
}
#menu {
display: inline-block;
font-size: 16px;
color: rgb(255, 255, 255);
padding-left: 10px;
z-index: 100;
}
#menu:hover .dropup-content {
display: block;
}
#logBtn, #credentialsBtn, #reauthBtn {
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:hover .dropup-content {
display: block;
}
.dropup:click .dropup-content {
display: block;
}
.dropup:hover .dropbtn {
background-color: #3e8e41;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,245 +0,0 @@
'use strict'
import * as io from 'socket.io-client'
import * as Terminal from 'xterm/dist/xterm'
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 faBars from '@fortawesome/fontawesome-free-solid/faBars'
// import faQuestion from '@fortawesome/fontawesome-free-solid/faQuestion'
import faClipboard from '@fortawesome/fontawesome-free-solid/faClipboard'
import faDownload from '@fortawesome/fontawesome-free-solid/faDownload'
import faKey from '@fortawesome/fontawesome-free-solid/faKey'
import faCog from '@fortawesome/fontawesome-free-solid/faCog'
fontawesome.library.add(faBars, faClipboard, faDownload, faKey, faCog)
require('xterm/dist/xterm.css')
require('../css/style.css')
Terminal.applyAddon(fit)
/* global Blob, logBtn, credentialsBtn, reauthBtn, downloadLogBtn */
var sessionLogEnable = false
var loggedData = false
var allowreplay = false
var allowreauth = false
var sessionLog, sessionFooter, logDate, currentDate, myFile, errorExists
var socket, termid // eslint-disable-line
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.focus()
term.fit()
window.addEventListener('resize', resizeScreen, false)
function resizeScreen () {
term.fit()
socket.emit('resize', { cols: term.cols, rows: term.rows })
}
if (document.location.pathname) {
var parts = document.location.pathname.split('/')
var base = parts.slice(0, parts.length - 1).join('/') + '/'
var resource = base.substring(1) + 'socket.io'
socket = io.connect(null, {
resource: resource
})
} else {
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.emit('geometry', term.cols, term.rows)
})
socket.on('setTerminalOpts', function (data) {
term.setOption('cursorBlink', data.cursorBlink)
term.setOption('scrollback', data.scrollback)
term.setOption('tabStopWidth', data.tabStopWidth)
term.setOption('bellStyle', data.bellStyle)
})
socket.on('title', function (data) {
document.title = data
})
socket.on('menu', function (data) {
drawMenu(data)
})
socket.on('status', function (data) {
status.innerHTML = data
})
socket.on('ssherror', function (data) {
status.innerHTML = data
status.style.backgroundColor = 'red'
errorExists = true
})
socket.on('headerBackground', function (data) {
header.style.backgroundColor = data
})
socket.on('header', function (data) {
if (data) {
header.innerHTML = data
header.style.display = 'block'
// header is 19px and footer is 19px, recaculate new terminal-container and resize
terminalContainer.style.height = 'calc(100% - 38px)'
resizeScreen()
}
})
socket.on('footer', function (data) {
sessionFooter = data
footer.innerHTML = data
})
socket.on('statusBackground', function (data) {
status.style.backgroundColor = data
})
socket.on('allowreplay', function (data) {
if (data === true) {
console.log('allowreplay: ' + data)
allowreplay = true
drawMenu(dropupContent.innerHTML + '<a id="credentialsBtn"><i class="fas fa-key fa-fw"></i> Credentials</a>')
} else {
allowreplay = false
console.log('allowreplay: ' + data)
}
})
socket.on('allowreauth', function (data) {
if (data === true) {
console.log('allowreauth: ' + data)
allowreauth = true
drawMenu(dropupContent.innerHTML + '<a id="reauthBtn"><i class="fas fa-key fa-fw"></i> Switch User</a>')
} else {
allowreauth = false
console.log('allowreauth: ' + data)
}
})
socket.on('disconnect', function (err) {
if (!errorExists) {
status.style.backgroundColor = 'red'
status.innerHTML =
'WEBSOCKET SERVER DISCONNECTED: ' + err
}
socket.io.reconnection(false)
})
socket.on('error', function (err) {
if (!errorExists) {
status.style.backgroundColor = 'red'
status.innerHTML = 'ERROR: ' + err
}
})
term.on('title', function (title) {
document.title = title
})
// draw/re-draw menu and reattach listeners
// when dom is changed, listeners are abandonded
function drawMenu (data) {
dropupContent.innerHTML = data
logBtn.addEventListener('click', toggleLog)
allowreauth && reauthBtn.addEventListener('click', reauthSession)
allowreplay && credentialsBtn.addEventListener('click', replayCredentials)
loggedData && downloadLogBtn.addEventListener('click', downloadLog)
}
// reauthenticate
function reauthSession () { // eslint-disable-line
console.log('re-authenticating')
window.location.href = '/reauth'
return false
}
// replay password to server, requires
function replayCredentials () { // eslint-disable-line
socket.emit('control', 'replayCredentials')
console.log('replaying credentials')
term.focus()
return false
}
// Set variable to toggle log data from client/server to a varialble
// for later download
function toggleLog () { // eslint-disable-line
if (sessionLogEnable === true) {
sessionLogEnable = false
loggedData = true
logBtn.innerHTML = '<i class="fas fa-clipboard fa-fw"></i> Start Log'
console.log('stopping log, ' + sessionLogEnable)
currentDate = new Date()
sessionLog = sessionLog + '\r\n\r\nLog End for ' + sessionFooter + ': ' +
currentDate.getFullYear() + '/' + (currentDate.getMonth() + 1) + '/' +
currentDate.getDate() + ' @ ' + currentDate.getHours() + ':' +
currentDate.getMinutes() + ':' + currentDate.getSeconds() + '\r\n'
logDate = currentDate
term.focus()
return false
} else {
sessionLogEnable = true
loggedData = true
logBtn.innerHTML = '<i class="fas fa-cog fa-spin fa-fw"></i> Stop Log'
downloadLogBtn.style.color = '#000'
downloadLogBtn.addEventListener('click', downloadLog)
console.log('starting log, ' + sessionLogEnable)
currentDate = new Date()
sessionLog = 'Log Start for ' + sessionFooter + ': ' +
currentDate.getFullYear() + '/' + (currentDate.getMonth() + 1) + '/' +
currentDate.getDate() + ' @ ' + currentDate.getHours() + ':' +
currentDate.getMinutes() + ':' + currentDate.getSeconds() + '\r\n\r\n'
logDate = currentDate
term.focus()
return false
}
}
// cross browser method to "download" an element to the local system
// used for our client-side logging feature
function downloadLog () { // eslint-disable-line
if (loggedData === true) {
myFile = 'WebSSH2-' + logDate.getFullYear() + (logDate.getMonth() + 1) +
logDate.getDate() + '_' + logDate.getHours() + logDate.getMinutes() +
logDate.getSeconds() + '.log'
// regex should eliminate escape sequences from being logged.
var blob = new Blob([sessionLog.replace(/[\u001b\u009b][[\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><;]/g, '')], {
type: 'text/plain'
})
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, myFile)
} else {
var elem = window.document.createElement('a')
elem.href = window.URL.createObjectURL(blob)
elem.download = myFile
document.body.appendChild(elem)
elem.click()
document.body.removeChild(elem)
}
}
term.focus()
}

View file

@ -1,5 +1,5 @@
{ {
"name": "webssh2", "name": "webssh2-server",
"version": "0.2.3", "version": "0.2.3",
"ignore": [ "ignore": [
".gitignore" ".gitignore"
@ -41,17 +41,12 @@
}, },
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
"build": "webpack --progress --colors --config scripts/webpack.prod.js",
"builddev": "webpack --progress --colors --config scripts/webpack.dev.js",
"analyze": "webpack --json --config scripts/webpack.prod.js | webpack-bundle-size-analyzer",
"test": "snyk test", "test": "snyk test",
"watch": "nodemon index.js", "watch": "nodemon index.js",
"standard": "standard --verbose --fix | snazzy", "standard": "standard --verbose --fix | snazzy",
"cleanmac": "find . -name '.DS_Store' -type f -delete" "cleanmac": "find . -name '.DS_Store' -type f -delete"
}, },
"devDependencies": { "devDependencies": {
"@fortawesome/fontawesome": "^1.1.8",
"@fortawesome/fontawesome-free-solid": "^5.0.13",
"ajv": "^6.5.2", "ajv": "^6.5.2",
"bithound": "^1.7.0", "bithound": "^1.7.0",
"clean-webpack-plugin": "^0.1.19", "clean-webpack-plugin": "^0.1.19",
@ -70,8 +65,7 @@
"webpack": "^4.16.5", "webpack": "^4.16.5",
"webpack-cli": "^3.1.0", "webpack-cli": "^3.1.0",
"webpack-merge": "^4.1.4", "webpack-merge": "^4.1.4",
"webpack-stream": "^5.1.0", "webpack-stream": "^5.1.0"
"xterm": "^3.5.1"
}, },
"standard": { "standard": {
"ignore": [ "ignore": [

View file

@ -1,43 +0,0 @@
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
context: path.resolve('__dirname', '../'),
entry: {
webssh2: './client/src/js/index.js'
},
plugins: [
new CleanWebpackPlugin(['client/public'], {
root: path.resolve('__dirname', '../'),
verbose: true
}),
new CopyWebpackPlugin([
'./client/src/client.htm',
'./client/src/favicon.ico'
]),
new ExtractTextPlugin('[name].css')
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../client/public')
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
minimize: {discardComments: {removeAll: true}}
}
}
]
})
}
]
}
}

View file

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

View file

@ -1,18 +0,0 @@
const merge = require('webpack-merge')
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
const common = require('./webpack.common.js')
module.exports = merge(common, {
plugins: [
new UglifyJSPlugin({
uglifyOptions: {
ie8: false,
dead_code: true,
output: {
comments: false,
beautify: false
}
}
})
]
})