From 9e23de5e225760912cbb4d4b1cd784a67e7b4d91 Mon Sep 17 00:00:00 2001 From: Sammy Mansour Date: Sun, 26 Apr 2020 20:09:27 -0400 Subject: [PATCH 1/4] Term Schemes that plays nice with directories just incase they do not exist. --- apps/shell/app.js | 221 ++++++++++++++++++- apps/shell/package.json | 3 +- apps/shell/public/javascripts/ood_shell.1.js | 43 ++++ apps/shell/public/stylesheets/style.css | 58 ++++- apps/shell/views/index.hbs | 6 +- apps/shell/views/launch.hbs | 86 ++++++-- 6 files changed, 391 insertions(+), 26 deletions(-) diff --git a/apps/shell/app.js b/apps/shell/app.js index d1e1b71bf4..ee726a160f 100644 --- a/apps/shell/app.js +++ b/apps/shell/app.js @@ -9,6 +9,8 @@ var dotenv = require('dotenv'); var Tokens = require('csrf'); var url = require('url'); var uuidv4 = require('uuid/v4'); +var os = require('os'); +var termSchemes = require('term-schemes'); var port = 3000; // Read in environment variables @@ -23,6 +25,161 @@ if (fs.existsSync('.env')) { dotenv.config({path: '.env'}); } +//Start Terminal Color Scheme Implementation +//declared constants for directory paths +const xdg_config_dir = (process.env["XDG_CONFIG_HOME"] || path.join(os.homedir(), ".config")); +const ood_app_config_root = (process.env["OOD_APP_CONFIG_ROOT"] || '/etc/ood/config/ondemand/apps/shell'); +const userDir = path.join(xdg_config_dir, "ondemand", "apps", "shell", "themes"); +const systemDir = path.join(ood_app_config_root, "themes"); + +//declared global variables for color schemes +var schemeObjects; + +checkDirSync(userDir); +checkDirSync(systemDir); + +if (process.env["SYSTEM_SCHEMES"] === true) { + var user = getSchemeObjects(userDir); + var system = getSchemeObjects(systemDir); + + schemeObjects = {...getSchemeObjects(userDir), ...getSchemeObjects(systemDir)}; + +} else { + schemeObjects = {...getSchemeObjects(userDir)}; +} + +// helper functions +function checkDirSync(dir) { + try { + fs.statSync(dir); + } catch(e) { + if (dir === systemDir) { + process.env["SYSTEM_SCHEMES"] = false; + } else if (dir === userDir) { + fs.mkdirSync(dir); + } + } +} + +function getSchemeObjects(dir) { + var schemes = {}; + + fs.readdirSync(dir).forEach(function(file) { + fileInfo = path.parse(file); + schemes[fileInfo.name] = {name: fileInfo.name, file: fileInfo.base, ext: fileInfo.ext, dir: dir} + }); + return schemes; +} + +function getSchemeFilesArray() { + + return Object.keys(schemeObjects).map(i => schemeObjects[i]) +} + + +function getSchemeFileObject(base) { + var userDir = path.join(xdg_config_dir, "apps", "shell", "themes"); + fs.readdirSync(userDir).forEach(file => { + if (file === base) { + return path.parse(file); + } + + }) +} + +function rgbToHexMath (num) { + var hex = Number(num).toString(16); + if (hex.length < 2) { + hex = "0" + hex; + } + return hex; +}; + +function hexConverter (array) { + var red = array[0]; + var green = array[1]; + var blue = array[2]; + + return `#${rgbToHexMath(red)}${rgbToHexMath(green)}${rgbToHexMath(blue)}`.toUpperCase(); +} + +function findHost(uuid) { + sessions = terminals.instances; + var host = sessions[uuid].host; + + return host; +} + +function parseFile(fileObject) { + + const ext = fileObject.ext; + const file = fileObject.dir + "/" + fileObject.file; + const raw = String(fs.readFileSync(file)); + + switch(ext) { + case ".itermcolors": + return termSchemes.iterm2(raw); + break; + + case ".colorscheme": + return termSchemes.konsole(raw); + break; + + case ".colors": + return termSchemes.remmina(raw); + break; + + case ".terminal": + return termSchemes.terminal(raw); + break; + + case ".config": + return termSchemes.terminator(raw); + break; + + case ".config_0": + return termSchemes.tilda(raw); + break; + + case ".theme": + return termSchemes.xfce(raw); + break; + + case ".txt": + return termSchemes.termite(raw); + break; + + case ".xrdb" || ".Xresources": + return termSchemes.xresources(raw); + break; + + default: + schemeError = {error: "unknown file type."} + return schemeError; + break; + + } +} + +function convertSchemeObject(obj) { + newSchemeObj = {}; + colorArray = []; + for (var key of Object.keys(obj)) { + if(isNaN(key) === false) { + + colorArray.push(hexConverter(obj[key])); + + } else if (isNaN(key)) { + newSchemeObj[key] = hexConverter(obj[key]); + } + + } + + newSchemeObj["colorPaletteOverrides"] = colorArray; + + return newSchemeObj; +} + const tokens = new Tokens({}); const secret = tokens.secretSync(); @@ -40,19 +197,81 @@ router.get('/ssh/*', function (req, res) { }); +//For laumch page to start a new session with color scheme and host. +router.get('/new-session', function(req, res, next) { + var id = uuidv4(); + + + res.redirect(url.format({ + pathname: req.baseUrl + "/custom-term", + query: { + "host": req.query.host + ".osc.edu", + "scheme": req.query.scheme, + "session": id + } + })); + +}); + +router.get('/custom-term', function(req, res, next) { + res.locals.uuid = req.query.session; + var fileObject, schemeObject, schemeColorConvert; + var defaultObj = { + 'use-default-window-copy': true, + 'ctrl-v-paste': true, + 'ctrl-c-copy': true, + 'cursor-blink': true, + }; + + res.locals.schemeObject; + if (req.query.scheme === "default") { + if ('default' in schemeObjects) { + fileObject = schemeObjects[req.query.scheme]; + schemeObject = parseFile(fileObject); + schemeColorConvert = convertSchemeObject(schemeObject); + res.locals.schemeObject = schemeColorConvert; + } else { + + res.locals.schemeObject = defaultObj; + } + } else { + + fileObject = schemeObjects[req.query.scheme]; + schemeObject = parseFile(fileObject); + schemeColorConvert = convertSchemeObject(schemeObject); + res.locals.schemeObject = schemeColorConvert; + } + + next(); + +}, function(req, res, next) { + res.locals.host = req.query.host || findHost(req.query.session); + console.log(res.locals.schemeObject); + var cookieValue = JSON.stringify(res.locals.schemeObject); + + res.cookie(res.locals.uuid, cookieValue, {expires: new Date(Date.now() + 8 * 3600000) }); + + next(); + +}, function(req, res, next) { + + res.redirect(req.baseUrl + `/session/${req.query.session}/${res.locals.host}`) +}) + router.get('/session/:id/*', function (req, res) { res.render('index', { baseURI: req.baseUrl, csrfToken: tokens.create(secret), + session: req.params.id }); }); router.get('/launch', function (req, res) { - res.render('launch', {baseURI: req.baseUrl, sessions: terminal.sessionsInfo()}); + res.render('launch', {baseURI: req.baseUrl, sessions: terminals.sessionsInfo(), fileOptions: getSchemeFilesArray() || []}); }) diff --git a/apps/shell/package.json b/apps/shell/package.json index 8b80c0052a..e8052dc295 100644 --- a/apps/shell/package.json +++ b/apps/shell/package.json @@ -21,7 +21,8 @@ "minimist": ">=1.2.2", "node-pty": "^0.9.0", "ws": ">=7.2.0", - "uuid": "^3.3.3" + "uuid": "^3.3.3", + "term-schemes": "1.2.1" }, "private": true, "resolutions": { diff --git a/apps/shell/public/javascripts/ood_shell.1.js b/apps/shell/public/javascripts/ood_shell.1.js index 19d39ec2e6..8c928470ed 100644 --- a/apps/shell/public/javascripts/ood_shell.1.js +++ b/apps/shell/public/javascripts/ood_shell.1.js @@ -1,3 +1,46 @@ + function getCookie(uuid) { + + var cookie = document.cookie.match('(^|[^;]+)\\s*' + uuid + '\\s*=\\s*([^;]+)') + + return cookie ? cookie.pop() : ''; + + } + + function CustomTerm(uuid) { + this.uuid = uuid; +} + +CustomTerm.prototype.getTermPrefs = function () { + + var defaultPref = { + 'use-default-window-copy': true, + 'ctrl-v-paste': true, + 'ctrl-c-copy': true, + 'cursor-blink': true, + }; + + if (getCookie(this.uuid)) { + var decodedTheme = decodeURIComponent(getCookie(this.uuid)); + var jsonTheme = JSON.parse(decodedTheme); + + pref = { + 'use-default-window-copy': true, + 'ctrl-v-paste': true, + 'ctrl-c-copy': true, + 'cursor-blink': true, + 'background-color': jsonTheme['background'], + 'foreground-color': jsonTheme['text'], + 'color-palette-overrides': jsonTheme['colorPaletteOverrides'], + 'cursor-color': jsonTheme['cursor'] + '50', + }; + + return pref; + } + + return defaultPref; +} + + // Object that defines a terminal element function OodShell(element, socket, prefs) { diff --git a/apps/shell/public/stylesheets/style.css b/apps/shell/public/stylesheets/style.css index a72ee35257..96061e02e9 100644 --- a/apps/shell/public/stylesheets/style.css +++ b/apps/shell/public/stylesheets/style.css @@ -25,19 +25,57 @@ html, body { } /* For Launch page*/ -table { - border-collapse: collapse; - width: 100%; +.split { + height: 100%; + width: 50%; + position: fixed; + z-index: 1; + top: 0; + overflow-x: hidden; + padding-top: 20px; +} + +.left { + left: 0; + background-color: #F0E9E9; +} + +.right { + right: 0; + background-color: #F0E9E9; } -th, td { - text-align: left; - padding: 8px; +.centered { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; } -tr:nth-child(even){background-color: #f2f2f2} +.bubble { + padding: 10px; + background-color: white; + height: 500px; + width: 400px; + border-radius: 20px; + -moz-box-shadow: 2px 2px 5px #333; + -webkit-box-shadow: 2px 2px 5px #333; + box-shadow: 2px 2px 5px #333; +} + +.drop { + margin-top: 10px; + width: 200px; + height: 30px; + border-radius: 50px; +} + +.custom-label { + font-size: 20px; +} -th { - background-color: #FF0000; - color: white; +.button { + height: 40px; + border-radius: 5px; } \ No newline at end of file diff --git a/apps/shell/views/index.hbs b/apps/shell/views/index.hbs index df4160c45c..55844941e0 100644 --- a/apps/shell/views/index.hbs +++ b/apps/shell/views/index.hbs @@ -31,7 +31,11 @@ // hterm default preferences are defined here. Switch to the tag that we're using. // https://chromium.googlesource.com/apps/libapps/+/master/hterm/js/hterm_preference_manager.js - oodShell = new OodShell(terminalContainer, socket, terminalPrefs); + var uuid = "{{session}}"; + console.log(uuid); + var customTerm = new CustomTerm(uuid); + + oodShell = new OodShell(terminalContainer, socket, customTerm.getTermPrefs()); oodShell.createTerminal(); setInterval(check, 5000); diff --git a/apps/shell/views/launch.hbs b/apps/shell/views/launch.hbs index 15deec60a4..3060de493f 100644 --- a/apps/shell/views/launch.hbs +++ b/apps/shell/views/launch.hbs @@ -6,17 +6,77 @@ -

Terminal Sessions:

- - - - - - {{#each sessions}} - - - - - {{/each}} -
UUIDHOST
{{id}}{{host}}
+
+
+
+
+

New Session

+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+

Existing Session

+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ \ No newline at end of file From ffca4cbfa2201d8c5e687be9d2fa8d5e87f75396 Mon Sep 17 00:00:00 2001 From: Sammy Mansour Date: Wed, 17 Jun 2020 15:08:40 -0400 Subject: [PATCH 2/4] Refactored some code. Prevent a crash on directory null --- apps/shell/app.js | 120 ++++++++++--------------------------- apps/shell/views/index.hbs | 1 - 2 files changed, 31 insertions(+), 90 deletions(-) diff --git a/apps/shell/app.js b/apps/shell/app.js index ee726a160f..5190dd0c29 100644 --- a/apps/shell/app.js +++ b/apps/shell/app.js @@ -32,43 +32,23 @@ const ood_app_config_root = (process.env["OOD_APP_CONFIG_ROOT"] || '/etc/ood/con const userDir = path.join(xdg_config_dir, "ondemand", "apps", "shell", "themes"); const systemDir = path.join(ood_app_config_root, "themes"); -//declared global variables for color schemes -var schemeObjects; +//Search directories and +fs.mkdirSync(userDir, {recursive: true}); + var schemeObjects = {...getSchemeObjects(userDir), ...getSchemeObjects(systemDir)}; -checkDirSync(userDir); -checkDirSync(systemDir); - -if (process.env["SYSTEM_SCHEMES"] === true) { - var user = getSchemeObjects(userDir); - var system = getSchemeObjects(systemDir); - - schemeObjects = {...getSchemeObjects(userDir), ...getSchemeObjects(systemDir)}; - -} else { - schemeObjects = {...getSchemeObjects(userDir)}; -} - -// helper functions -function checkDirSync(dir) { - try { - fs.statSync(dir); - } catch(e) { - if (dir === systemDir) { - process.env["SYSTEM_SCHEMES"] = false; - } else if (dir === userDir) { - fs.mkdirSync(dir); - } - } -} function getSchemeObjects(dir) { var schemes = {}; - fs.readdirSync(dir).forEach(function(file) { - fileInfo = path.parse(file); - schemes[fileInfo.name] = {name: fileInfo.name, file: fileInfo.base, ext: fileInfo.ext, dir: dir} - }); - return schemes; + try { + fs.readdirSync(dir).forEach(function(file) { + fileInfo = path.parse(file); + schemes[fileInfo.name] = {name: fileInfo.name, file: fileInfo.base, ext: fileInfo.ext, dir: dir} + }); + return schemes; + } catch (err) { + return {}; + } } function getSchemeFilesArray() { @@ -76,17 +56,6 @@ function getSchemeFilesArray() { return Object.keys(schemeObjects).map(i => schemeObjects[i]) } - -function getSchemeFileObject(base) { - var userDir = path.join(xdg_config_dir, "apps", "shell", "themes"); - fs.readdirSync(userDir).forEach(file => { - if (file === base) { - return path.parse(file); - } - - }) -} - function rgbToHexMath (num) { var hex = Number(num).toString(16); if (hex.length < 2) { @@ -115,49 +84,23 @@ function parseFile(fileObject) { const ext = fileObject.ext; const file = fileObject.dir + "/" + fileObject.file; const raw = String(fs.readFileSync(file)); - - switch(ext) { - case ".itermcolors": - return termSchemes.iterm2(raw); - break; - - case ".colorscheme": - return termSchemes.konsole(raw); - break; - - case ".colors": - return termSchemes.remmina(raw); - break; - - case ".terminal": - return termSchemes.terminal(raw); - break; - - case ".config": - return termSchemes.terminator(raw); - break; - - case ".config_0": - return termSchemes.tilda(raw); - break; - - case ".theme": - return termSchemes.xfce(raw); - break; - - case ".txt": - return termSchemes.termite(raw); - break; - - case ".xrdb" || ".Xresources": - return termSchemes.xresources(raw); - break; - - default: - schemeError = {error: "unknown file type."} - return schemeError; - break; + const schemes = { + ".itermcolors": termSchemes.iterm2, + ".colorscheme": termSchemes.konsole, + ".colors": termSchemes.remmina, + ".terminal":termSchemes.terminal, + ".config": termSchemes.terminator, + ".config_0": termSchemes.tilda, + ".theme": termSchemes.xfce, + ".txt": termSchemes.termite, + ".Xresources": termSchemes.xresources, + ".xrdb": termSchemes.xresources, + } + try { + return schemes[ext](raw) + } catch (err) { + return {error: "unknown file type."} } } @@ -304,6 +247,8 @@ var terminals = { var cmd = 'ssh'; var args = dir ? [host, '-t', 'cd \'' + dir.replace(/\'/g, "'\\''") + '\' ; exec ${SHELL} -l'] : [host]; + process.env.LANG = 'en_US.UTF-8'; // this patch (from b996d36) lost when removing wetty (2c8a022) + this.instances[uuid] = {term: pty.spawn(cmd, args, { name: 'xterm-256color', cols: 80, @@ -350,7 +295,7 @@ var terminals = { }); ws.on('close', function () { - term.end(); + term.pause(); console.log('Closed terminal: ' + term.pid); }); @@ -383,9 +328,6 @@ wss.on('connection', function connection (ws, req) { } terminals.attach(uuid, ws); - - process.env.LANG = 'en_US.UTF-8'; // this patch (from b996d36) lost when removing wetty (2c8a022) - }); function custom_server_origin(default_value = null){ diff --git a/apps/shell/views/index.hbs b/apps/shell/views/index.hbs index 55844941e0..40f6de200e 100644 --- a/apps/shell/views/index.hbs +++ b/apps/shell/views/index.hbs @@ -32,7 +32,6 @@ // https://chromium.googlesource.com/apps/libapps/+/master/hterm/js/hterm_preference_manager.js var uuid = "{{session}}"; - console.log(uuid); var customTerm = new CustomTerm(uuid); oodShell = new OodShell(terminalContainer, socket, customTerm.getTermPrefs()); From 70ef3a41496802526033f670e309e046f1703c03 Mon Sep 17 00:00:00 2001 From: Sammy Mansour Date: Thu, 16 Jul 2020 12:13:04 -0400 Subject: [PATCH 3/4] Added comments to explain functions --- apps/shell/app.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/shell/app.js b/apps/shell/app.js index 08827b7fb1..1dbbd0c457 100644 --- a/apps/shell/app.js +++ b/apps/shell/app.js @@ -35,11 +35,11 @@ const ood_app_config_root = (process.env["OOD_APP_CONFIG_ROOT"] || '/etc/ood/con const userDir = path.join(xdg_config_dir, "ondemand", "apps", "shell", "themes"); const systemDir = path.join(ood_app_config_root, "themes"); -//Search directories and +//Search directories and make directory if not present. fs.mkdirSync(userDir, {recursive: true}); var schemeObjects = {...getSchemeObjects(userDir), ...getSchemeObjects(systemDir)}; - +//Parses through the files and returns them as objects. function getSchemeObjects(dir) { var schemes = {}; @@ -54,11 +54,13 @@ function getSchemeObjects(dir) { } } +// Show list of options for color schemes. function getSchemeFilesArray() { return Object.keys(schemeObjects).map(i => schemeObjects[i]) } +//Converts the colors returned by term-scheme to a hex number. This allows the colors to play nicely with hterm. function rgbToHexMath (num) { var hex = Number(num).toString(16); if (hex.length < 2) { @@ -67,6 +69,7 @@ function rgbToHexMath (num) { return hex; }; +//Converts to a complete hex color. function hexConverter (array) { var red = array[0]; var green = array[1]; @@ -75,6 +78,7 @@ function hexConverter (array) { return `#${rgbToHexMath(red)}${rgbToHexMath(green)}${rgbToHexMath(blue)}`.toUpperCase(); } +//Finds the host of the terminal and returns that value. function findHost(uuid) { sessions = terminals.instances; var host = sessions[uuid].host; @@ -82,6 +86,7 @@ function findHost(uuid) { return host; } +//Parses through the various file types of the terminal schemes. function parseFile(fileObject) { const ext = fileObject.ext; @@ -107,6 +112,7 @@ function parseFile(fileObject) { } } +//Converts scheme colors to an object. function convertSchemeObject(obj) { newSchemeObj = {}; colorArray = []; From d2141e630b6bf23bd6e4803fefa4e7b0910695d9 Mon Sep 17 00:00:00 2001 From: Sammy Mansour Date: Mon, 20 Jul 2020 08:40:38 -0400 Subject: [PATCH 4/4] JSDOC added for helper functions --- apps/shell/app.js | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/apps/shell/app.js b/apps/shell/app.js index 1dbbd0c457..15422ece10 100644 --- a/apps/shell/app.js +++ b/apps/shell/app.js @@ -39,7 +39,11 @@ const systemDir = path.join(ood_app_config_root, "themes"); fs.mkdirSync(userDir, {recursive: true}); var schemeObjects = {...getSchemeObjects(userDir), ...getSchemeObjects(systemDir)}; -//Parses through the files and returns them as objects. +/** +* Parse through file information in certain directories. +* @param {String} dir - The target directory. +* @return {Object} - The key information returned is an object within an object. +*/ function getSchemeObjects(dir) { var schemes = {}; @@ -54,13 +58,20 @@ function getSchemeObjects(dir) { } } -// Show list of options for color schemes. +/** +* Maps schemeObject to an array for frontend. +* @return {Array} - An array of shemeObjects. +*/ function getSchemeFilesArray() { return Object.keys(schemeObjects).map(i => schemeObjects[i]) } -//Converts the colors returned by term-scheme to a hex number. This allows the colors to play nicely with hterm. +/** +* Takes a number and converts it to a hex number. +* @param {Int} num - The number to convert. +* @return {String} - The hex number. +*/ function rgbToHexMath (num) { var hex = Number(num).toString(16); if (hex.length < 2) { @@ -69,7 +80,11 @@ function rgbToHexMath (num) { return hex; }; -//Converts to a complete hex color. +/** +* Converts a complete array of colors to a hex color value. +* @param {Array} array - The colors to convert. +* @return {String} - The hex code for the color. +*/ function hexConverter (array) { var red = array[0]; var green = array[1]; @@ -78,7 +93,11 @@ function hexConverter (array) { return `#${rgbToHexMath(red)}${rgbToHexMath(green)}${rgbToHexMath(blue)}`.toUpperCase(); } -//Finds the host of the terminal and returns that value. +/** +* Show the host of the specific terminal. +* @param {String} uuid - The id of the terminal. +* @return {String} - The host of the specific terminal. +*/ function findHost(uuid) { sessions = terminals.instances; var host = sessions[uuid].host; @@ -86,7 +105,11 @@ function findHost(uuid) { return host; } -//Parses through the various file types of the terminal schemes. +/** +* Convert file with scheme to an object of colors. +* @param {Object} fileObject - An object with information on file location and ext. +* @return {Object} - The colors parsed into an object. +*/ function parseFile(fileObject) { const ext = fileObject.ext; @@ -112,7 +135,11 @@ function parseFile(fileObject) { } } -//Converts scheme colors to an object. +/** +* Convert rgb color format to hex color codes. +* @param {Object} obj - The object of rgb colors. +* @return {Object} - The object of hex colors. +*/ function convertSchemeObject(obj) { newSchemeObj = {}; colorArray = [];