From 11cb3d011aae2e81fcdf14a29b77bb8da17ea0a6 Mon Sep 17 00:00:00 2001 From: butlerx Date: Tue, 1 Sep 2020 09:16:19 +0100 Subject: [PATCH] namespace fucntions for easier maintenance --- src/client/wetty.ts | 45 ++++--------- src/client/wetty/disconnect.ts | 4 +- src/client/wetty/shared/type.ts | 5 ++ src/client/wetty/term.ts | 24 +++++++ .../{options.ts => term/confiruragtion.ts} | 31 ++++----- .../{ => term/confiruragtion}/clipboard.ts | 7 +- src/client/wetty/term/confiruragtion/load.ts | 12 ++++ src/main.ts | 8 +-- src/server.ts | 18 +++-- src/server/command.ts | 8 +-- src/server/command/ssh.ts | 2 +- src/server/login.ts | 2 +- src/server/socketServer.ts | 66 +++++-------------- src/server/socketServer/assets.ts | 6 ++ src/server/socketServer/html.ts | 2 +- src/server/socketServer/middleware.ts | 15 +++++ src/server/socketServer/socket.ts | 36 ++++++++++ src/server/{ => socketServer}/ssl.ts | 0 src/server/spawn.ts | 4 +- src/{server/default.ts => shared/defaults.ts} | 0 src/shared/logger.ts | 2 +- 21 files changed, 169 insertions(+), 128 deletions(-) create mode 100644 src/client/wetty/shared/type.ts create mode 100644 src/client/wetty/term.ts rename src/client/wetty/{options.ts => term/confiruragtion.ts} (68%) rename src/client/wetty/{ => term/confiruragtion}/clipboard.ts (80%) create mode 100644 src/client/wetty/term/confiruragtion/load.ts create mode 100644 src/server/socketServer/assets.ts create mode 100644 src/server/socketServer/middleware.ts create mode 100644 src/server/socketServer/socket.ts rename src/server/{ => socketServer}/ssl.ts (100%) rename src/{server/default.ts => shared/defaults.ts} (100%) diff --git a/src/client/wetty.ts b/src/client/wetty.ts index 0ed9b6b..b7949c9 100644 --- a/src/client/wetty.ts +++ b/src/client/wetty.ts @@ -1,50 +1,27 @@ import _ from 'lodash'; -import { Terminal } from 'xterm'; -import { FitAddon } from 'xterm-addon-fit'; import { dom, library } from '@fortawesome/fontawesome-svg-core'; import { faCogs } from '@fortawesome/free-solid-svg-icons'; -import { FileDownloader } from './wetty/download.js'; -import { copySelected, copyShortcut } from './wetty/clipboard.js'; -import { disconnect } from './wetty/disconnect.js'; -import { configureTerm } from './wetty/options.js'; -import { mobileKeyboard } from './wetty/mobile.js'; -import { overlay, terminal } from './shared/elements.js'; -import { socket } from './wetty/socket.js'; -import { verifyPrompt } from './shared/verify.js'; +import { FileDownloader } from './wetty/download'; +import { disconnect } from './wetty/disconnect'; +import { mobileKeyboard } from './wetty/mobile'; +import { overlay } from './shared/elements'; +import { socket } from './wetty/socket'; +import { verifyPrompt } from './shared/verify'; +import { terminal } from './wetty/term'; // Setup for fontawesome library.add(faCogs); dom.watch(); socket.on('connect', () => { - const term = new Terminal(); - if (_.isNull(terminal)) return; - const fitAddon = new FitAddon(); - term.loadAddon(fitAddon); - term.open(terminal); - const resize = (): void => { - fitAddon.fit(); - socket.emit('resize', { cols: term.cols, rows: term.rows }); - }; - - configureTerm(term, resize); + const term = terminal(socket); + if (_.isUndefined(term)) return; if (!_.isNull(overlay)) overlay.style.display = 'none'; window.addEventListener('beforeunload', verifyPrompt, false); - term.attachCustomKeyEventHandler(copyShortcut); - - document.addEventListener( - 'mouseup', - event => { - if (term.hasSelection()) copySelected(event, term.getSelection()); - }, - false, - ); - - window.onresize = resize; - resize(); + term.resizeTerm(); term.focus(); mobileKeyboard(); const fileDownloader = new FileDownloader(); @@ -64,7 +41,7 @@ socket.on('connect', () => { }) .on('login', () => { term.writeln(''); - resize(); + term.resizeTerm(); }) .on('logout', disconnect) .on('disconnect', disconnect) diff --git a/src/client/wetty/disconnect.ts b/src/client/wetty/disconnect.ts index 72a6cef..88547d8 100644 --- a/src/client/wetty/disconnect.ts +++ b/src/client/wetty/disconnect.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import { verifyPrompt } from '../shared/verify.js'; -import { overlay } from '../shared/elements.js'; +import { verifyPrompt } from '../shared/verify'; +import { overlay } from '../shared/elements'; export function disconnect(reason: string): void { if (_.isNull(overlay)) return; diff --git a/src/client/wetty/shared/type.ts b/src/client/wetty/shared/type.ts new file mode 100644 index 0000000..44d68e6 --- /dev/null +++ b/src/client/wetty/shared/type.ts @@ -0,0 +1,5 @@ +import { Terminal } from 'xterm'; + +export class Term extends Terminal { + resizeTerm(): void {} +} diff --git a/src/client/wetty/term.ts b/src/client/wetty/term.ts new file mode 100644 index 0000000..04f8290 --- /dev/null +++ b/src/client/wetty/term.ts @@ -0,0 +1,24 @@ +import _ from 'lodash'; +import type { Socket } from 'socket.io-client'; +import { FitAddon } from 'xterm-addon-fit'; +import { Terminal } from 'xterm'; + +import type { Term } from './shared/type'; +import { configureTerm } from './term/confiruragtion'; +import { terminal as termElement } from '../shared/elements'; + +export function terminal(socket: typeof Socket): Term | undefined { + const term = new Terminal() as Term; + if (_.isNull(termElement)) return; + const fitAddon = new FitAddon(); + term.loadAddon(fitAddon); + term.open(termElement); + term.resizeTerm = () => { + fitAddon.fit(); + socket.emit('resize', { cols: term.cols, rows: term.rows }); + }; + configureTerm(term); + window.onresize = term.resizeTerm; + + return term; +} diff --git a/src/client/wetty/options.ts b/src/client/wetty/term/confiruragtion.ts similarity index 68% rename from src/client/wetty/options.ts rename to src/client/wetty/term/confiruragtion.ts index 2a0c8e7..22487da 100644 --- a/src/client/wetty/options.ts +++ b/src/client/wetty/term/confiruragtion.ts @@ -1,20 +1,11 @@ import _ from 'lodash'; -import type { Terminal } from 'xterm'; -import { editor } from '../shared/elements.js'; +import type { Term } from '../shared/type'; +import { copySelected, copyShortcut } from './confiruragtion/clipboard'; +import { editor } from '../../shared/elements'; +import { loadOptions } from './confiruragtion/load'; -function loadOptions(): object { - const defaultOptions = { fontSize: 14 }; - try { - return _.isUndefined(localStorage.options) - ? defaultOptions - : JSON.parse(localStorage.options); - } catch { - return defaultOptions; - } -} - -export function configureTerm(term: Terminal, resize: Function): void { +export function configureTerm(term: Term): void { const options = loadOptions(); Object.entries(options).forEach(([key, value]) => { term.setOption(key, value); @@ -33,7 +24,7 @@ export function configureTerm(term: Terminal, resize: Function): void { const value = updated[key]; term.setOption(key, value); }); - resize(); + term.resizeTerm(); } catch { // skip editor.classList.add('error'); @@ -48,4 +39,14 @@ export function configureTerm(term: Terminal, resize: Function): void { }); } } + + term.attachCustomKeyEventHandler(copyShortcut); + + document.addEventListener( + 'mouseup', + () => { + if (term.hasSelection()) copySelected(term.getSelection()); + }, + false, + ); } diff --git a/src/client/wetty/clipboard.ts b/src/client/wetty/term/confiruragtion/clipboard.ts similarity index 80% rename from src/client/wetty/clipboard.ts rename to src/client/wetty/term/confiruragtion/clipboard.ts index 4849efd..33cde21 100644 --- a/src/client/wetty/clipboard.ts +++ b/src/client/wetty/term/confiruragtion/clipboard.ts @@ -1,12 +1,11 @@ /** Copy text selection to clipboard on double click or select - @param event - the event this function is bound to eg mouseup @param text - the selected text to copy @returns boolean to indicate success or failure */ -export function copySelected(event: Event, text: string): boolean { - if (event.clipboardData?.setData) { - event.clipboardData.setData('Text', text); +export function copySelected(text: string): boolean { + if (window.clipboardData?.setData) { + window.clipboardData.setData('Text', text); return true; } if ( diff --git a/src/client/wetty/term/confiruragtion/load.ts b/src/client/wetty/term/confiruragtion/load.ts new file mode 100644 index 0000000..ecfcae6 --- /dev/null +++ b/src/client/wetty/term/confiruragtion/load.ts @@ -0,0 +1,12 @@ +import _ from 'lodash'; + +export function loadOptions(): object { + const defaultOptions = { fontSize: 14 }; + try { + return _.isUndefined(localStorage.options) + ? defaultOptions + : JSON.parse(localStorage.options); + } catch { + return defaultOptions; + } +} diff --git a/src/main.ts b/src/main.ts index e1b190a..62b66b3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,15 +3,15 @@ * @module WeTTy */ import yargs from 'yargs'; -import isUndefined from 'lodash/isUndefined.js'; -import { logger } from './shared/logger.js'; +import isUndefined from 'lodash/isUndefined'; +import { logger } from './shared/logger'; import { sshDefault, serverDefault, forceSSHDefault, defaultCommand, -} from './server/default.js'; -import { startServer } from './server.js'; +} from './shared/defaults'; +import { startServer } from './server'; const opts = yargs .option('ssl-key', { diff --git a/src/server.ts b/src/server.ts index 4a32ff1..d433c7b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,19 +2,18 @@ * Create WeTTY server * @module WeTTy */ -import type { SSH, SSL, SSLBuffer, Server } from './shared/interfaces'; -import { getCommand } from './server/command.js'; -import { loadSSL } from './server/ssl.js'; -import { logger } from './shared/logger.js'; -import { login } from './server/login.js'; -import { server } from './server/socketServer.js'; -import { spawn } from './server/spawn.js'; +import type { SSH, SSL, Server } from './shared/interfaces'; +import { getCommand } from './server/command'; +import { logger } from './shared/logger'; +import { login } from './server/login'; +import { server } from './server/socketServer'; +import { spawn } from './server/spawn'; import { sshDefault, serverDefault, forceSSHDefault, defaultCommand, -} from './server/default.js'; +} from './shared/defaults'; /** * Starts WeTTy Server @@ -36,8 +35,7 @@ export async function startServer( !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`); } - const sslBuffer: SSLBuffer = await loadSSL(ssl); - const io = server(serverConf, sslBuffer); + const io = await server(serverConf, ssl); /** * Wetty server connected too * @fires WeTTy#connnection diff --git a/src/server/command.ts b/src/server/command.ts index e44b815..e6302e2 100644 --- a/src/server/command.ts +++ b/src/server/command.ts @@ -1,9 +1,9 @@ import url from 'url'; import { Socket } from 'socket.io'; -import { SSH } from '../shared/interfaces.js'; -import { address } from './command/address.js'; -import { loginOptions } from './command/login.js'; -import { sshOptions } from './command/ssh.js'; +import { SSH } from '../shared/interfaces'; +import { address } from './command/address'; +import { loginOptions } from './command/login'; +import { sshOptions } from './command/ssh'; const localhost = (host: string): boolean => process.getuid() === 0 && diff --git a/src/server/command/ssh.ts b/src/server/command/ssh.ts index 6e3a2a6..cf521a1 100644 --- a/src/server/command/ssh.ts +++ b/src/server/command/ssh.ts @@ -1,5 +1,5 @@ import isUndefined from 'lodash/isUndefined.js'; -import { logger } from '../../shared/logger.js'; +import { logger } from '../../shared/logger'; export function sshOptions( { diff --git a/src/server/login.ts b/src/server/login.ts index 6cc035b..756cd47 100644 --- a/src/server/login.ts +++ b/src/server/login.ts @@ -1,5 +1,5 @@ import pty from 'node-pty'; -import { xterm } from './shared/xterm.js'; +import { xterm } from './shared/xterm'; export function login(socket: SocketIO.Socket): Promise { // Check request-header for username diff --git a/src/server/socketServer.ts b/src/server/socketServer.ts index a58a58e..a9199dc 100644 --- a/src/server/socketServer.ts +++ b/src/server/socketServer.ts @@ -1,31 +1,23 @@ -import compression from 'compression'; import express from 'express'; -import favicon from 'serve-favicon'; +import compression from 'compression'; import helmet from 'helmet'; -import http from 'http'; -import https from 'https'; -import isUndefined from 'lodash/isUndefined.js'; -import socket from 'socket.io'; import winston from 'express-winston'; -import { join, resolve } from 'path'; - -import type { SSLBuffer, Server } from '../shared/interfaces'; -import { html } from './socketServer/html.js'; -import { logger } from '../shared/logger.js'; -const trim = (str: string): string => str.replace(/\/*$/, ''); -const serveStatic = (path: string) => - express.static(resolve(process.cwd(), 'build', path)); +import type { SSL, SSLBuffer, Server } from '../shared/interfaces'; +import { favicon, redirect } from './socketServer/middleware'; +import { html } from './socketServer/html'; +import { listen } from './socketServer/socket'; +import { logger } from '../shared/logger'; +import { serveStatic, trim } from './socketServer/assets'; +import { loadSSL } from './socketServer/ssl'; -export function server( +export async function server( { base, port, host, title, bypassHelmet }: Server, - { key, cert }: SSLBuffer, -): SocketIO.Server { + ssl?: SSL, +): Promise { const basePath = trim(base); - logger.info('Starting server', { - key, - cert, + ssl, port, base, title, @@ -38,15 +30,8 @@ export function server( .use(`${basePath}/client`, serveStatic('client')) .use(winston.logger(logger)) .use(compression()) - .use(favicon(join('build', 'assets', 'favicon.ico'))); - /* .use((req, res, next) => { - if (req.path.substr(-1) === '/' && req.path.length > 1) - res.redirect( - 301, - req.path.slice(0, -1) + req.url.slice(req.path.length), - ); - else next(); - }); */ + .use(favicon) + .use(redirect); // Allow helmet to be bypassed. // Unfortunately, order matters with middleware @@ -58,24 +43,7 @@ export function server( const client = html(basePath, title); app.get(basePath, client).get(`${basePath}/ssh/:user`, client); - return socket( - !isUndefined(key) && !isUndefined(cert) - ? https.createServer({ key, cert }, app).listen(port, host, () => { - logger.info('Server started', { - port, - connection: 'https', - }); - }) - : http.createServer(app).listen(port, host, () => { - logger.info('Server started', { - port, - connection: 'http', - }); - }), - { - path: `${basePath}/socket.io`, - pingInterval: 3000, - pingTimeout: 7000, - }, - ); + const sslBuffer: SSLBuffer = await loadSSL(ssl); + + return listen(app, host, port, basePath, sslBuffer); } diff --git a/src/server/socketServer/assets.ts b/src/server/socketServer/assets.ts new file mode 100644 index 0000000..ef08aa2 --- /dev/null +++ b/src/server/socketServer/assets.ts @@ -0,0 +1,6 @@ +import { resolve } from 'path'; +import express from 'express'; + +export const trim = (str: string): string => str.replace(/\/*$/, ''); +export const serveStatic = (path: string) => + express.static(resolve(process.cwd(), 'build', path)); diff --git a/src/server/socketServer/html.ts b/src/server/socketServer/html.ts index 47785ed..feb9c8c 100644 --- a/src/server/socketServer/html.ts +++ b/src/server/socketServer/html.ts @@ -1,5 +1,5 @@ import type express from 'express'; -import { isDev } from '../../shared/env.js'; +import { isDev } from '../../shared/env'; const jsFiles = isDev ? ['dev', 'wetty'] : ['wetty']; const cssFiles = ['styles', 'options', 'overlay', 'terminal']; diff --git a/src/server/socketServer/middleware.ts b/src/server/socketServer/middleware.ts new file mode 100644 index 0000000..ddef3e8 --- /dev/null +++ b/src/server/socketServer/middleware.ts @@ -0,0 +1,15 @@ +import type express from 'express'; +import { join } from 'path'; +import { default as _favicon } from 'serve-favicon'; + +export const favicon = _favicon(join('build', 'assets', 'favicon.ico')); + +export function redirect( + req: express.Request, + res: express.Response, + next: Function, +) { + if (req.path.substr(-1) === '/' && req.path.length > 1) + res.redirect(301, req.path.slice(0, -1) + req.url.slice(req.path.length)); + else next(); +} diff --git a/src/server/socketServer/socket.ts b/src/server/socketServer/socket.ts new file mode 100644 index 0000000..d35fa94 --- /dev/null +++ b/src/server/socketServer/socket.ts @@ -0,0 +1,36 @@ +import type express from 'express'; +import socket from 'socket.io'; +import http from 'http'; +import https from 'https'; +import isUndefined from 'lodash/isUndefined.js'; + +import { logger } from '../../shared/logger'; +import type { SSLBuffer } from '../../shared/interfaces'; + +export const listen = ( + app: express.Express, + host: string, + port: number, + path: string, + { key, cert }: SSLBuffer, +): SocketIO.Server => + socket( + !isUndefined(key) && !isUndefined(cert) + ? https.createServer({ key, cert }, app).listen(port, host, () => { + logger.info('Server started', { + port, + connection: 'https', + }); + }) + : http.createServer(app).listen(port, host, () => { + logger.info('Server started', { + port, + connection: 'http', + }); + }), + { + path: `${path}/socket.io`, + pingInterval: 3000, + pingTimeout: 7000, + }, + ); diff --git a/src/server/ssl.ts b/src/server/socketServer/ssl.ts similarity index 100% rename from src/server/ssl.ts rename to src/server/socketServer/ssl.ts diff --git a/src/server/spawn.ts b/src/server/spawn.ts index 1a1896f..a7c1566 100644 --- a/src/server/spawn.ts +++ b/src/server/spawn.ts @@ -1,7 +1,7 @@ import isUndefined from 'lodash/isUndefined.js'; import pty from 'node-pty'; -import { logger } from '../shared/logger.js'; -import { xterm } from './shared/xterm.js'; +import { logger } from '../shared/logger'; +import { xterm } from './shared/xterm'; export function spawn(socket: SocketIO.Socket, args: string[]): void { const term = pty.spawn('/usr/bin/env', args, xterm); diff --git a/src/server/default.ts b/src/shared/defaults.ts similarity index 100% rename from src/server/default.ts rename to src/shared/defaults.ts diff --git a/src/shared/logger.ts b/src/shared/logger.ts index 6cf2c62..c33b56c 100644 --- a/src/shared/logger.ts +++ b/src/shared/logger.ts @@ -1,6 +1,6 @@ import winston from 'winston'; -import { isDev } from './env.js'; +import { isDev } from './env'; const { combine, timestamp, label, simple, json, colorize } = winston.format;