1
0
mirror of https://github.com/bingohuang/docker-labs.git synced 2025-07-13 17:42:53 +08:00
2017-05-09 19:18:47 +08:00

537 lines
19 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function() {
'use strict';
var app = angular.module('DockerPlay', ['ngMaterial']);
// Automatically redirects user to a new session when bypassing captcha.
// Controller keeps code/logic separate from the HTML
// app.controller("BypassController", ['$scope', '$log', '$http', '$location', '$timeout', function($scope, $log, $http, $location, $timeout) {
// setTimeout(function() {
// document.getElementById("welcomeFormBypass").submit();
// }, 500);
// }]);
app.controller('PlayController', ['$scope', '$log', '$http', '$location', '$timeout', '$mdDialog', '$window', 'TerminalService', 'KeyboardShortcutService', 'InstanceService', function($scope, $log, $http, $location, $timeout, $mdDialog, $window, TerminalService, KeyboardShortcutService, InstanceService) {
$scope.sessionId = window.location.pathname.replace('/p/', '');
$scope.instances = [];
$scope.idx = {};
$scope.selectedInstance = null;
$scope.isAlive = true;
$scope.ttl = '--:--:--';
$scope.connected = true;
$scope.isInstanceBeingCreated = false;
$scope.newInstanceBtnText = '+ 创建工作台';
$scope.deleteInstanceBtnText = '删除工作台';
$scope.isInstanceBeingDeleted = false;
var selectedKeyboardShortcuts = KeyboardShortcutService.getCurrentShortcuts();
angular.element($window).bind('resize', function() {
if ($scope.selectedInstance) {
$scope.resize($scope.selectedInstance.term.proposeGeometry());
}
});
$scope.$on("settings:shortcutsSelected", function(e, preset) {
selectedKeyboardShortcuts = preset;
});
$scope.showAlert = function(title, content, parent) {
$mdDialog.show(
$mdDialog.alert()
.parent(angular.element(document.querySelector(parent || '#popupContainer')))
.clickOutsideToClose(true)
.title(title)
.textContent(content)
.ok('收到!')
);
}
$scope.resize = function(geometry) {
$scope.socket.emit('viewport resize', geometry.cols, geometry.rows);
}
KeyboardShortcutService.setResizeFunc($scope.resize);
$scope.closeSession = function() {
$scope.socket.emit('session close');
}
$scope.upsertInstance = function(info) {
var i = info;
if (!$scope.idx[i.name]) {
$scope.instances.push(i);
i.buffer = '';
$scope.idx[i.name] = i;
} else {
$scope.idx[i.name].ip = i.ip;
$scope.idx[i.name].hostname = i.hostname;
}
return $scope.idx[i.name];
}
$scope.newInstance = function() {
updateNewInstanceBtnState(true);
$http({
method: 'POST',
url: '/sessions/' + $scope.sessionId + '/instances',
data : { ImageName : InstanceService.getDesiredImage() }
}).then(function(response) {
var i = $scope.upsertInstance(response.data);
$scope.showInstance(i);
}, function(response) {
if (response.status == 409) {
$scope.showAlert('工作台到达上限', '工作台最大个数是 5已达上限')
}
}).finally(function() {
updateNewInstanceBtnState(false);
});
}
$scope.getSession = function(sessionId) {
$http({
method: 'GET',
url: '/sessions/' + $scope.sessionId,
}).then(function(response) {
if (response.data.created_at) {
$scope.expiresAt = moment(response.data.expires_at);
setInterval(function() {
$scope.ttl = moment.utc($scope.expiresAt.diff(moment())).format('HH:mm:ss');
$scope.$apply();
}, 1000);
}
var socket = io({ path: '/sessions/' + sessionId + '/ws' });
socket.on('terminal out', function(name, data) {
var instance = $scope.idx[name];
if (!instance) {
// instance is new and was created from another client, we should add it
$scope.upsertInstance({ name: name });
instance = $scope.idx[name];
}
if (!instance.term) {
instance.buffer += data;
} else {
instance.term.write(data);
}
});
socket.on('session end', function() {
$scope.showAlert('实验室关门啦!', '你的实验室关门了,所有工作台都已删除。', '#sessionEnd')
$scope.isAlive = false;
});
socket.on('viewport', function(rows, cols) {
});
socket.on('new instance', function(name, ip, hostname) {
$scope.upsertInstance({ name: name, ip: ip, hostname: hostname });
$scope.$apply(function() {
if ($scope.instances.length == 1) {
$scope.showInstance($scope.instances[0]);
}
});
});
socket.on('delete instance', function(name) {
$scope.removeInstance(name);
$scope.$apply();
});
socket.on('viewport resize', function(cols, rows) {
// viewport has changed, we need to resize all terminals
$scope.instances.forEach(function(instance) {
instance.term.resize(cols, rows);
});
});
socket.on('connect_error', function() {
$scope.connected = false;
});
socket.on('connect', function() {
$scope.connected = true;
});
socket.on('instance stats', function(name, mem, cpu, isManager, ports) {
$scope.idx[name].mem = mem;
$scope.idx[name].cpu = cpu;
$scope.idx[name].isManager = isManager;
$scope.idx[name].ports = ports;
$scope.$apply();
});
$scope.socket = socket;
var i = response.data;
for (var k in i.instances) {
var instance = i.instances[k];
$scope.instances.push(instance);
$scope.idx[instance.name] = instance;
}
// If instance is passed in URL, select it
let inst = $scope.idx[$location.hash()];
if (inst) $scope.showInstance(inst);
}, function(response) {
if (response.status == 404) {
document.write('该实验室没找到');
return
}
});
}
$scope.getProxyUrl = function(instance, port) {
var url = window.location.protocol + '//pwd' + instance.ip.replace(/\./g, '_') + '-' + port + '.' + window.location.host;
return url;
}
$scope.showInstance = function(instance) {
$scope.selectedInstance = instance;
$location.hash(instance.name);
if (!instance.creatingTerminal) {
if (!instance.term) {
$timeout(function() {
createTerminal(instance);
TerminalService.setFontSize(TerminalService.getFontSize());
instance.term.focus();
}, 0, false);
return
}
}
$timeout(function() {
instance.term.focus();
}, 0, false);
}
$scope.removeInstance = function(name) {
if ($scope.idx[name]) {
delete $scope.idx[name];
$scope.instances = $scope.instances.filter(function(i) {
return i.name != name;
});
if ($scope.instances.length) {
$scope.showInstance($scope.instances[0]);
}
}
}
$scope.deleteInstance = function(instance) {
updateDeleteInstanceBtnState(true);
$http({
method: 'DELETE',
url: '/sessions/' + $scope.sessionId + '/instances/' + instance.name,
}).then(function(response) {
$scope.removeInstance(instance.name);
}, function(response) {
console.log('error', response);
}).finally(function() {
updateDeleteInstanceBtnState(false);
});
}
$scope.getSession($scope.sessionId);
function createTerminal(instance, cb) {
if (instance.term) {
return instance.term;
}
var terminalContainer = document.getElementById('terminal-' + instance.name);
var term = new Terminal({
cursorBlink: false
});
term.attachCustomKeydownHandler(function(e) {
// Ctrl + Alt + C
if (e.ctrlKey && e.altKey && (e.keyCode == 67)) {
document.execCommand('copy');
return false;
}
});
term.attachCustomKeydownHandler(function(e) {
if (selectedKeyboardShortcuts == null)
return;
var presets = selectedKeyboardShortcuts.presets
.filter(function(preset) { return preset.keyCode == e.keyCode })
.filter(function(preset) { return (preset.metaKey == undefined && !e.metaKey) || preset.metaKey == e.metaKey })
.filter(function(preset) { return (preset.ctrlKey == undefined && !e.ctrlKey) || preset.ctrlKey == e.ctrlKey })
.filter(function(preset) { return (preset.altKey == undefined && !e.altKey) || preset.altKey == e.altKey })
.forEach(function(preset) { preset.action({ terminal : term })});
});
term.open(terminalContainer);
// Set geometry during the next tick, to avoid race conditions.
setTimeout(function() {
$scope.resize(term.proposeGeometry());
}, 4);
term.on('data', function(d) {
$scope.socket.emit('terminal in', instance.name, d);
});
instance.term = term;
if (instance.buffer) {
term.write(instance.buffer);
instance.buffer = '';
}
if (cb) {
cb();
}
}
function updateNewInstanceBtnState(isInstanceBeingCreated) {
if (isInstanceBeingCreated === true) {
$scope.newInstanceBtnText = '+ 创建中...';
$scope.isInstanceBeingCreated = true;
} else {
$scope.newInstanceBtnText = '+ 创建工作台';
$scope.isInstanceBeingCreated = false;
}
}
function updateDeleteInstanceBtnState(isInstanceBeingDeleted) {
if (isInstanceBeingDeleted === true) {
$scope.deleteInstanceBtnText = '删除中...';
$scope.isInstanceBeingDeleted = true;
} else {
$scope.deleteInstanceBtnText = '删除工作台';
$scope.isInstanceBeingDeleted = false;
}
}
}])
.config(['$mdIconProvider', '$locationProvider', function($mdIconProvider, $locationProvider) {
$locationProvider.html5Mode({enabled: true, requireBase: false});
$mdIconProvider.defaultIconSet('../assets/social-icons.svg', 24);
}])
.component('settingsIcon', {
template : "<md-button ng-click='$ctrl.onClick()'><md-icon class='material-icons'>settings</md-icon></md-button>",
controller : function($mdDialog) {
var $ctrl = this;
$ctrl.onClick = function() {
$mdDialog.show({
controller : function() {},
template : "<settings-dialog></settings-dialog>",
parent: angular.element(document.body),
clickOutsideToClose : true
})
}
}
})
.component("settingsDialog", {
templateUrl : "settings-modal.html",
controller : function($mdDialog, KeyboardShortcutService, $rootScope, InstanceService, TerminalService) {
var $ctrl = this;
$ctrl.$onInit = function() {
$ctrl.keyboardShortcutPresets = KeyboardShortcutService.getAvailablePresets();
$ctrl.selectedShortcutPreset = KeyboardShortcutService.getCurrentShortcuts();
$ctrl.instanceImages = InstanceService.getAvailableImages();
$ctrl.selectedInstanceImage = InstanceService.getDesiredImage();
$ctrl.terminalFontSizes = TerminalService.getFontSizes();
};
$ctrl.currentShortcutConfig = function(value) {
if (value !== undefined) {
value = JSON.parse(value);
KeyboardShortcutService.setCurrentShortcuts(value);
$ctrl.selectedShortcutPreset = angular.copy(KeyboardShortcutService.getCurrentShortcuts());
$rootScope.$broadcast('settings:shortcutsSelected', $ctrl.selectedShortcutPreset);
}
return JSON.stringify(KeyboardShortcutService.getCurrentShortcuts());
};
$ctrl.currentDesiredInstanceImage = function(value) {
if (value !== undefined) {
InstanceService.setDesiredImage(value);
}
return InstanceService.getDesiredImage(value);
};
$ctrl.currentTerminalFontSize = function(value) {
if (value !== undefined) {
// set font size
TerminalService.setFontSize(value);
return;
}
return TerminalService.getFontSize();
}
$ctrl.close = function() {
$mdDialog.cancel();
}
}
})
.service("InstanceService", function($http) {
var instanceImages = [];
_prepopulateAvailableImages();
return {
getAvailableImages : getAvailableImages,
setDesiredImage : setDesiredImage,
getDesiredImage : getDesiredImage,
};
function getAvailableImages() {
return instanceImages;
}
function getDesiredImage() {
var image = localStorage.getItem("settings.desiredImage");
if (image == null)
return instanceImages[0];
return image;
}
function setDesiredImage(image) {
if (image === null)
localStorage.removeItem("settings.desiredImage");
else
localStorage.setItem("settings.desiredImage", image);
}
function _prepopulateAvailableImages() {
return $http
.get("/instances/images")
.then(function(response) {
instanceImages = response.data;
});
}
})
.run(function(InstanceService) { /* forcing pre-populating for now */ })
.service("KeyboardShortcutService", ['TerminalService', function(TerminalService) {
var resizeFunc;
return {
getAvailablePresets : getAvailablePresets,
getCurrentShortcuts : getCurrentShortcuts,
setCurrentShortcuts : setCurrentShortcuts,
setResizeFunc : setResizeFunc
};
function setResizeFunc(f) {
resizeFunc = f;
}
function getAvailablePresets() {
return [
{
name : "None",
presets : [
{ description : "全屏终端", command : "Alt+enter", altKey : true, keyCode : 13, action : function(context) { TerminalService.toggleFullscreen(context.terminal, resizeFunc); }}
]
},
{
name : "Mac OSX",
presets : [
{ description : "清空终端", command : "Cmd+K", metaKey : true, keyCode : 75, action : function(context) { context.terminal.clear(); }},
{ description : "全屏终端", command : "Alt+enter", altKey : true, keyCode : 13, action : function(context) { TerminalService.toggleFullscreen(context.terminal, resizeFunc); }}
]
}
]
}
function getCurrentShortcuts() {
var shortcuts = localStorage.getItem("shortcut-preset-name");
if (shortcuts == null) {
shortcuts = getDefaultShortcutPrefixName();
if (shortcuts == null)
return null;
}
var preset = getAvailablePresets()
.filter(function(preset) { return preset.name == shortcuts; });
if (preset.length == 0)
console.error("Unable to find preset with name '" + shortcuts + "'");
return preset[0];
return (shortcuts == null) ? null : JSON.parse(shortcuts);
}
function setCurrentShortcuts(config) {
localStorage.setItem("shortcut-preset-name", config.name);
}
function getDefaultShortcutPrefixName() {
if (window.navigator.platform.toUpperCase().indexOf('MAC') >= 0)
return "Mac OSX";
return "None";
}
}])
.service('TerminalService', ['$window', function($window) {
var fullscreen;
var fontSize = getFontSize();
return {
getFontSizes : getFontSizes,
setFontSize : setFontSize,
getFontSize : getFontSize,
increaseFontSize : increaseFontSize,
decreaseFontSize : decreaseFontSize,
toggleFullscreen : toggleFullscreen
};
function getFontSizes() {
var terminalFontSizes = [];
for (var i=3; i<40; i++) {
terminalFontSizes.push(i+'px');
}
return terminalFontSizes;
};
function getFontSize() {
if (!fontSize) {
return $('.terminal').css('font-size');
}
return fontSize;
}
function setFontSize(value) {
fontSize = value;
var size = parseInt(value);
$('.terminal').css('font-size', value).css('line-height', (size + 2)+'px');
//.css('line-height', value).css('height', value);
angular.element($window).trigger('resize');
}
function increaseFontSize() {
var sizes = getFontSizes();
var size = getFontSize();
var i = sizes.indexOf(size);
if (i == -1) {
return;
}
if (i+1 > sizes.length) {
return;
}
setFontSize(sizes[i+1]);
}
function decreaseFontSize() {
var sizes = getFontSizes();
var size = getFontSize();
var i = sizes.indexOf(size);
if (i == -1) {
return;
}
if (i-1 < 0) {
return;
}
setFontSize(sizes[i-1]);
}
function toggleFullscreen(terminal, resize) {
if(fullscreen) {
terminal.toggleFullscreen();
resize(fullscreen);
fullscreen = null;
} else {
fullscreen = terminal.proposeGeometry();
terminal.toggleFullscreen();
angular.element($window).trigger('resize');
}
}
}]);
})();