mirror of
https://github.com/kasmtech/kasm-install-wizard.git
synced 2026-07-18 17:04:38 +00:00
Compare commits
19 commits
develop
...
1.18.0.bd1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b173217d1 | ||
|
|
8622084a02 | ||
|
|
08280190ea | ||
|
|
84f2bfdd48 | ||
|
|
b5c356997f | ||
|
|
58775a6491 | ||
|
|
e599398710 | ||
|
|
0c654da05a | ||
|
|
3e9264d25d | ||
|
|
a8735cf7e3 | ||
|
|
bfacaa3734 | ||
|
|
ee1ea94b59 | ||
|
|
a5c2f0635c | ||
|
|
675ca12955 | ||
|
|
c51cbe881d | ||
|
|
0aa9746d3b | ||
|
|
37dbb06b8c | ||
|
|
64bd739f1d | ||
|
|
1e5706208c |
3 changed files with 259 additions and 60 deletions
156
index.js
156
index.js
|
|
@ -6,6 +6,7 @@ var fsw = require('fs').promises;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
var yaml = require('js-yaml');
|
var yaml = require('js-yaml');
|
||||||
|
var _ = require('lodash');
|
||||||
var si = require('systeminformation');
|
var si = require('systeminformation');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var app = require('express')();
|
var app = require('express')();
|
||||||
|
|
@ -19,16 +20,72 @@ var arch = os.arch().replace('x64', 'amd64');
|
||||||
var baseUrl = process.env.SUBFOLDER || '/';
|
var baseUrl = process.env.SUBFOLDER || '/';
|
||||||
var version = process.env.VERSION || 'stable';
|
var version = process.env.VERSION || 'stable';
|
||||||
var port = process.env.KASM_PORT || '443';
|
var port = process.env.KASM_PORT || '443';
|
||||||
|
const { spawn } = require('node:child_process');
|
||||||
var EULA;
|
var EULA;
|
||||||
var images;
|
var images;
|
||||||
|
var currentVersion;
|
||||||
|
var gpuInfo;
|
||||||
|
var installSettings = {};
|
||||||
|
var upgradeSettings = {};
|
||||||
// Grab installer variables
|
// Grab installer variables
|
||||||
async function installerBlobs() {
|
async function installerBlobs() {
|
||||||
EULA = await fsw.readFile('/kasm_release/licenses/LICENSE.txt', 'utf8');
|
EULA = await fsw.readFile('/kasm_release/licenses/LICENSE.txt', 'utf8');
|
||||||
let imagesText = await fsw.readFile('/wizard/default_images_' + arch + '.yaml', 'utf8');
|
let imagesText = await fsw.readFile('/wizard/default_images_' + arch + '.yaml', 'utf8');
|
||||||
images = yaml.load(imagesText);
|
images = yaml.load(imagesText);
|
||||||
|
currentVersion = fs.readFileSync('/version.txt', 'utf8').replace(/(\r\n|\n|\r)/gm,'');
|
||||||
|
let gpuData = [];
|
||||||
|
let gpuCmd = spawn('/gpuinfo.sh');
|
||||||
|
gpuCmd.stdout.on('data', function(data) {
|
||||||
|
gpuData.push(data);
|
||||||
|
});
|
||||||
|
gpuCmd.on('close', function(code) {
|
||||||
|
try {
|
||||||
|
if (code == 0) {
|
||||||
|
gpuInfo = JSON.parse(gpuData.join(''));
|
||||||
|
} else {
|
||||||
|
gpuInfo = {};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Manually backfill GPU info if available
|
||||||
|
gpuInfo = {};
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
let num = i.toString();
|
||||||
|
if (fs.existsSync('/dev/dri/card' + num)) {
|
||||||
|
gpuInfo['/dev/dri/card' + num] = "Unknown GPU";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
installerBlobs();
|
installerBlobs();
|
||||||
|
|
||||||
|
// GPU image yaml merging
|
||||||
|
async function setGpu(imagesI) {
|
||||||
|
if (upgradeSettings['forceGpu'] !== undefined) {
|
||||||
|
installSettings = upgradeSettings;
|
||||||
|
}
|
||||||
|
let gpu = installSettings.forceGpu.split('|')[0];
|
||||||
|
let gpuName = installSettings.forceGpu.split('|')[1];
|
||||||
|
let card = gpu.slice(-1);
|
||||||
|
let render = (Number(card) + 128).toString();
|
||||||
|
// Handle NVIDIA Gpus
|
||||||
|
var baseRun;
|
||||||
|
if (gpuName.indexOf('NVIDIA') !== -1) {
|
||||||
|
baseRun = JSON.parse('{"runtime":"nvidia","environment":{"NVIDIA_DRIVER_CAPABILITIES":"all","KASM_EGL_CARD":"/dev/dri/card' + card + '","KASM_RENDERD":"/dev/dri/renderD' + render + '"},"device_requests":[{"driver": "","count": -1,"device_ids": null,"capabilities":[["gpu"]],"options":{}}]}');
|
||||||
|
} else {
|
||||||
|
baseRun = JSON.parse('{"environment":{"DRINODE":"/dev/dri/renderD' + render + '", "HW3D": true},"devices":["/dev/dri/card' + card + ':/dev/dri/card' + card + ':rwm","/dev/dri/renderD' + render + ':/dev/dri/renderD' + render + ':rwm"]}');
|
||||||
|
}
|
||||||
|
let baseExec = JSON.parse('{"first_launch":{"user":"root","cmd": "bash -c \'chown -R kasm-user:kasm-user /dev/dri/*\'"}}');
|
||||||
|
for (var i=0; i<imagesI.images.length; i++) {
|
||||||
|
console.log(imagesI.images[i]['run_config']);
|
||||||
|
finalRun = _.merge(imagesI.images[i]['run_config'], baseRun)
|
||||||
|
finalExec = _.merge(imagesI.images[i]['exec_config'], baseExec)
|
||||||
|
imagesI.images[i]['run_config'] = finalRun;
|
||||||
|
imagesI.images[i]['exec_config'] = finalExec;
|
||||||
|
}
|
||||||
|
return imagesI;
|
||||||
|
}
|
||||||
|
|
||||||
//// Http server ////
|
//// Http server ////
|
||||||
baserouter.use('/public', express.static(__dirname + '/public'));
|
baserouter.use('/public', express.static(__dirname + '/public'));
|
||||||
baserouter.get("/", function (req, res) {
|
baserouter.get("/", function (req, res) {
|
||||||
|
|
@ -45,31 +102,31 @@ io = socketIO(https, {path: baseUrl + 'socket.io'});
|
||||||
io.on('connection', async function (socket) {
|
io.on('connection', async function (socket) {
|
||||||
// Run bash install with our custom flags
|
// Run bash install with our custom flags
|
||||||
async function install(data) {
|
async function install(data) {
|
||||||
|
// Determine install settings
|
||||||
installSettings = data[0];
|
installSettings = data[0];
|
||||||
let imagesI = data[1];
|
var imagesI = data[1];
|
||||||
let imagesD = images;
|
installFlags = ['/kasm_release/install.sh', '-W', '-B' ,'-H', '-e', '-L', port, '-P', installSettings.adminPass, '-U', installSettings.userPass];
|
||||||
installFlags = ['/kasm_release/install.sh', '-B' ,'-H', '-e', '-L', port, '-P', installSettings.adminPass, '-U', installSettings.userPass];
|
if ((imagesI.hasOwnProperty('images')) && (imagesI.images.length < 10)) {
|
||||||
if (installSettings.useRolling == true) {
|
|
||||||
installFlags.push('-O');
|
|
||||||
}
|
|
||||||
if ((installSettings.noDownload == true) || (imagesI == false)) {
|
|
||||||
installFlags.push('-u');
|
|
||||||
}
|
|
||||||
if ((imagesI.hasOwnProperty('images')) && (Object.keys(imagesI.images).length < 10)) {
|
|
||||||
installFlags.push('-b');
|
installFlags.push('-b');
|
||||||
}
|
}
|
||||||
// Flag the images properly based on selection
|
|
||||||
for await (let image of Object.keys(images.images)) {
|
// GPU yaml merge
|
||||||
if ((imagesI.hasOwnProperty('images')) && (imagesI.images.hasOwnProperty(image))) {
|
if (installSettings.forceGpu !== 'disabled') {
|
||||||
imagesD.images[image].enabled = true;
|
imagesI = await setGpu(imagesI);
|
||||||
imagesD.images[image].hidden = false;
|
|
||||||
} else {
|
|
||||||
imagesD.images[image].enabled = false;
|
|
||||||
imagesD.images[image].hidden = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let yamlStr = yaml.dump(imagesD);
|
|
||||||
await fsw.writeFile('/kasm_release/conf/database/seed_data/default_images_' + arch + '.yaml', yamlStr);
|
// Write finalized image data
|
||||||
|
let yamlStr = yaml.dump(imagesI);
|
||||||
|
if (yamlStr.startsWith("false")) {
|
||||||
|
installFlags = installFlags.filter(function(e) { return e !== '-W' });
|
||||||
|
} else {
|
||||||
|
await fsw.writeFile('/kasm_release/conf/database/seed_data/default_images_' + arch + '.yaml', yamlStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over version
|
||||||
|
await fsw.copyFile('/version.txt', '/opt/version.txt');
|
||||||
|
|
||||||
|
// Run install
|
||||||
let cmd = pty.spawn('/bin/bash', installFlags);
|
let cmd = pty.spawn('/bin/bash', installFlags);
|
||||||
cmd.on('data', function(data) {
|
cmd.on('data', function(data) {
|
||||||
socket.emit('term', data);
|
socket.emit('term', data);
|
||||||
|
|
@ -80,19 +137,71 @@ io.on('connection', async function (socket) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run bash upgrade with our custom flags
|
||||||
|
async function upgrade(data) {
|
||||||
|
// Determine upgrade settings
|
||||||
|
upgradeSettings = data[0];
|
||||||
|
var imagesI = data[1];
|
||||||
|
upgradeFlags = ['/kasm_release/upgrade.sh', '-L', port];
|
||||||
|
if (upgradeSettings.keepOldImages == true) {
|
||||||
|
upgradeFlags.push('-K');
|
||||||
|
} else {
|
||||||
|
upgradeFlags.push('-U');
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPU yaml merge
|
||||||
|
if (upgradeSettings.forceGpu !== 'disabled') {
|
||||||
|
imagesI = await setGpu(imagesI);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write finalized image data
|
||||||
|
let yamlStr = yaml.dump(imagesI);
|
||||||
|
if (yamlStr.startsWith("false")) {
|
||||||
|
upgradeFlags = upgradeFlags.filter(function(e) { return e !== '-K' });
|
||||||
|
upgradeFlags = upgradeFlags.filter(function(e) { return e !== '-U' });
|
||||||
|
} else {
|
||||||
|
await fsw.writeFile('/kasm_release/conf/database/seed_data/default_images_' + arch + '.yaml', yamlStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy over version
|
||||||
|
await fsw.copyFile('/version.txt', '/opt/version.txt');
|
||||||
|
|
||||||
|
// Run upgrade
|
||||||
|
let cmd = pty.spawn('/bin/bash', upgradeFlags);
|
||||||
|
cmd.on('data', function(data) {
|
||||||
|
socket.emit('term', data);
|
||||||
|
});
|
||||||
|
cmd.on('exit', function(code, signal) {
|
||||||
|
if (code == 0) {
|
||||||
|
socket.emit('done', port);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Render landing page depending on installed status
|
// Render landing page depending on installed status
|
||||||
async function renderLanding() {
|
async function renderLanding() {
|
||||||
let containers = await docker.listContainers();
|
let containers = await docker.listContainers();
|
||||||
|
// This is a running system
|
||||||
if (containers.length !== 0) {
|
if (containers.length !== 0) {
|
||||||
let dashinfo = {};
|
let dashinfo = {};
|
||||||
|
// Get version information
|
||||||
|
if (fs.existsSync('/opt/version.txt')) {
|
||||||
|
dashinfo['localVersion'] = fs.readFileSync('/opt/version.txt', 'utf8').replace(/(\r\n|\n|\r)/gm,'');
|
||||||
|
} else {
|
||||||
|
dashinfo['localVersion'] = 'Unknown';
|
||||||
|
}
|
||||||
|
dashinfo['currentVersion'] = currentVersion;
|
||||||
|
dashinfo['gpuInfo'] = gpuInfo;
|
||||||
dashinfo['containers'] = containers;
|
dashinfo['containers'] = containers;
|
||||||
dashinfo['cpu'] = await si.cpu();
|
dashinfo['cpu'] = await si.cpu();
|
||||||
dashinfo['mem'] = await si.mem();
|
dashinfo['mem'] = await si.mem();
|
||||||
dashinfo['cpuPercent'] = await si.currentLoad();
|
dashinfo['cpuPercent'] = await si.currentLoad();
|
||||||
dashinfo['port'] = port;
|
dashinfo['port'] = port;
|
||||||
socket.emit('renderdash', dashinfo);
|
socket.emit('renderdash', [dashinfo, images]);
|
||||||
|
// Render installer
|
||||||
} else {
|
} else {
|
||||||
socket.emit('renderinstall', [EULA, images]);
|
socket.emit('renderinstall', [EULA, images, gpuInfo]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disable wizard
|
// Disable wizard
|
||||||
|
|
@ -104,5 +213,6 @@ io.on('connection', async function (socket) {
|
||||||
//// Incoming requests ////
|
//// Incoming requests ////
|
||||||
socket.on('renderlanding', renderLanding);
|
socket.on('renderlanding', renderLanding);
|
||||||
socket.on('install', install);
|
socket.on('install', install);
|
||||||
|
socket.on('upgrade', upgrade);
|
||||||
socket.on('nowizard', noWizard);
|
socket.on('nowizard', noWizard);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
"dockerode": "^3.3.2",
|
"dockerode": "^3.3.2",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"node-pty": "^0.10.1",
|
"node-pty": "^0.10.1",
|
||||||
"socket.io": "^4.5.1",
|
"socket.io": "^4.5.1",
|
||||||
"systeminformation": "^5.11.16"
|
"systeminformation": "^5.11.16"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
// Variables
|
// Variables
|
||||||
var EULA;
|
var EULA;
|
||||||
var images;
|
var images;
|
||||||
|
var gpus;
|
||||||
var term;
|
var term;
|
||||||
var installImages = [];
|
var installImages = [];
|
||||||
var installSettings = {};
|
var installSettings = {};
|
||||||
|
var upgradeSettings = {};
|
||||||
var selected = false;
|
var selected = false;
|
||||||
|
|
||||||
// Socket.io connection
|
// Socket.io connection
|
||||||
|
|
@ -25,20 +27,39 @@ async function install() {
|
||||||
showTerminal()
|
showTerminal()
|
||||||
titleChange('Installing');
|
titleChange('Installing');
|
||||||
// Create new object based on image selection
|
// Create new object based on image selection
|
||||||
let selectedImages = {images: {}};
|
let selectedImages = {alembic_version: images.alembic_version, images: [], group_images: []};
|
||||||
if (installImages.length == 0) {
|
if (installImages.length == 0) {
|
||||||
socket.emit('install', [installSettings, false]);
|
socket.emit('install', [installSettings, false]);
|
||||||
} else {
|
} else {
|
||||||
for await (let image of installImages) {
|
for await (let image of installImages) {
|
||||||
if (images.images[image].hasOwnProperty('enabled')) {
|
let srcImage = images.images.find(x => x.friendly_name === image);
|
||||||
images.images[image].enabled = true;
|
srcImage['enabled'] = true;
|
||||||
}
|
selectedImages.images.push(srcImage);
|
||||||
Object.assign(selectedImages.images, {[image]: images.images[image]});
|
selectedImages.group_images.push({image_id: srcImage.image_id, group_id: "68d557ac-4cac-42cc-a9f3-1c7c853de0f3"});
|
||||||
}
|
}
|
||||||
socket.emit('install', [installSettings, selectedImages]);
|
socket.emit('install', [installSettings, selectedImages]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute upgrade
|
||||||
|
async function upgrade() {
|
||||||
|
showTerminal()
|
||||||
|
titleChange('Upgrading');
|
||||||
|
// Create new object based on image selection
|
||||||
|
let selectedImages = {alembic_version: images.alembic_version, images: [], group_images: []};
|
||||||
|
if (installImages.length == 0) {
|
||||||
|
socket.emit('upgrade', [upgradeSettings, false]);
|
||||||
|
} else {
|
||||||
|
for await (let image of installImages) {
|
||||||
|
let srcImage = images.images.find(x => x.friendly_name === image);
|
||||||
|
srcImage['enabled'] = true;
|
||||||
|
selectedImages.images.push(srcImage);
|
||||||
|
selectedImages.group_images.push({image_id: srcImage.image_id, group_id: "68d557ac-4cac-42cc-a9f3-1c7c853de0f3"});
|
||||||
|
}
|
||||||
|
socket.emit('upgrade', [upgradeSettings, selectedImages]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Show page container
|
// Show page container
|
||||||
function showContainer() {
|
function showContainer() {
|
||||||
$('#terminal').empty();
|
$('#terminal').empty();
|
||||||
|
|
@ -73,6 +94,7 @@ function renderInstall(data) {
|
||||||
titleChange('EULA');
|
titleChange('EULA');
|
||||||
EULA = data[0];
|
EULA = data[0];
|
||||||
images = data[1];
|
images = data[1];
|
||||||
|
gpus = data[2];
|
||||||
let EULADiv = $('<div>', {id: 'EULA'}).text(EULA);
|
let EULADiv = $('<div>', {id: 'EULA'}).text(EULA);
|
||||||
$('#container').append(EULADiv);
|
$('#container').append(EULADiv);
|
||||||
let EULAButton = $('<button>', {id: 'EULAButton', onclick: 'pickSettings()', class: 'btn btn-default btn-ghost'}).text('Accept and continue');
|
let EULAButton = $('<button>', {id: 'EULAButton', onclick: 'pickSettings()', class: 'btn btn-default btn-ghost'}).text('Accept and continue');
|
||||||
|
|
@ -80,15 +102,34 @@ function renderInstall(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render Dashboard
|
// Render Dashboard
|
||||||
async function renderDash(info) {
|
async function renderDash(data) {
|
||||||
showContainer();
|
showContainer();
|
||||||
titleChange('Dashboard');
|
titleChange('Dashboard');
|
||||||
|
let info = data[0];
|
||||||
|
images = data[1];
|
||||||
|
// Store GPU info
|
||||||
|
$('body').data('gpuInfo', info.gpuInfo);
|
||||||
|
// Upgrade button if needed
|
||||||
|
var upgrade;
|
||||||
|
if (info.currentVersion !== info.localVersion) {
|
||||||
|
upgrade = $('<button>', {class: 'btn btn-primary', onclick: 'renderUpgrade()'}).text('Upgrade to ' + info.currentVersion)
|
||||||
|
} else {
|
||||||
|
upgrade = info.currentVersion;
|
||||||
|
}
|
||||||
// Kasm docker containers
|
// Kasm docker containers
|
||||||
containersTable = $('<tbody>');
|
containersTable = $('<tbody>');
|
||||||
containersTable.append(
|
containersTable.append(
|
||||||
$('<tr>').append([
|
$('<tr>').append([
|
||||||
$('<th>').text('Web URL'),
|
$('<th>').text('Web URL'),
|
||||||
$('<td>').append($('<a>', {href: 'https://' + host + ':' + info.port, target: '_blank'}).text('https://' + host + ':' + info.port))
|
$('<td>').append($('<a>', {href: 'https://' + host + ':' + info.port, target: '_blank'}).text('https://' + host + ':' + info.port))
|
||||||
|
]),
|
||||||
|
$('<tr>').append([
|
||||||
|
$('<th>').text('Installed Version'),
|
||||||
|
$('<td>').text(info.localVersion)
|
||||||
|
]),
|
||||||
|
$('<tr>').append([
|
||||||
|
$('<th>').text('Current Version'),
|
||||||
|
$('<td>').append(upgrade)
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
for await (let container of info.containers) {
|
for await (let container of info.containers) {
|
||||||
|
|
@ -187,13 +228,13 @@ async function pickSettings() {
|
||||||
$('<label>', {for: 'userPass'}).text('user@kasm.local Password: '),
|
$('<label>', {for: 'userPass'}).text('user@kasm.local Password: '),
|
||||||
$('<input>', {name: 'userPass', id: 'userPass', type: 'password', placeholder: 'required'}).prop('required',true)
|
$('<input>', {name: 'userPass', id: 'userPass', type: 'password', placeholder: 'required'}).prop('required',true)
|
||||||
]);
|
]);
|
||||||
let useRolling = $('<div>', {class: 'form-group'}).append([
|
let gpuOptions = [$('<option>', {value: 'disabled'}).text('Disabled')];
|
||||||
$('<label>', {for: 'useRolling'}).text('Use Rolling Images: '),
|
for await (let card of Object.keys(gpus)) {
|
||||||
$('<input>', {name: 'useRolling', id: 'useRolling', type: 'checkbox'})
|
gpuOptions.push($('<option>', {value: card + '|' + gpus[card]}).text(card + ' - ' + gpus[card]));
|
||||||
]);
|
}
|
||||||
let noDownload = $('<div>', {class: 'form-group'}).append([
|
let forceGpu = $('<div>', {class: 'form-group'}).append([
|
||||||
$('<label>', {for: 'noDownload'}).text('Skip Image Download: '),
|
$('<label>', {for: 'forceGpu'}).text('Use GPU on all images: '),
|
||||||
$('<input>', {name: 'noDownload', id: 'noDownload', type: 'checkbox'})
|
$('<select>', {name: 'forceGpu', id: 'forceGpu',}).append(gpuOptions)
|
||||||
]);
|
]);
|
||||||
let submit = $('<div>', {class: 'form-group'}).append([
|
let submit = $('<div>', {class: 'form-group'}).append([
|
||||||
$('<input>', {name: 'submit', type: 'submit', value: 'Next', class: 'btn btn-default btn-ghost'})
|
$('<input>', {name: 'submit', type: 'submit', value: 'Next', class: 'btn btn-default btn-ghost'})
|
||||||
|
|
@ -201,8 +242,7 @@ async function pickSettings() {
|
||||||
fieldset.append([
|
fieldset.append([
|
||||||
adminPass,
|
adminPass,
|
||||||
userPass,
|
userPass,
|
||||||
useRolling,
|
forceGpu,
|
||||||
noDownload,
|
|
||||||
submit
|
submit
|
||||||
]);
|
]);
|
||||||
form.append(fieldset);
|
form.append(fieldset);
|
||||||
|
|
@ -212,34 +252,79 @@ async function pickSettings() {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
installSettings.adminPass = $('#adminPass').val();
|
installSettings.adminPass = $('#adminPass').val();
|
||||||
installSettings.userPass = $('#userPass').val();
|
installSettings.userPass = $('#userPass').val();
|
||||||
installSettings.useRolling = $('#useRolling').is(":checked");
|
installSettings.forceGpu = $('#forceGpu').val();
|
||||||
installSettings.noDownload = $('#noDownload').is(":checked");
|
pickImages(false);
|
||||||
pickImages();
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render upgrade form
|
||||||
|
async function renderUpgrade() {
|
||||||
|
showContainer();
|
||||||
|
let gpus = $('body').data('gpuInfo');
|
||||||
|
titleChange('Upgrade Settings');
|
||||||
|
let form = $('<form>', {id: 'settingsform'});
|
||||||
|
let fieldset = $('<fieldset>').append($('<legend>').text('Kasm Upgrade Settings'));
|
||||||
|
let keepOldImages = $('<div>', {class: 'form-group'}).append([
|
||||||
|
$('<label>', {for: 'keepOldImages'}).text('Do not purge existing images: '),
|
||||||
|
$('<input>', {name: 'keepOldImages', id: 'keepOldImages', type: 'checkbox'})
|
||||||
|
]);
|
||||||
|
let gpuOptions = [$('<option>', {value: 'disabled'}).text('Disabled')];
|
||||||
|
for await (let card of Object.keys(gpus)) {
|
||||||
|
gpuOptions.push($('<option>', {value: card + '|' + gpus[card]}).text(card + ' - ' + gpus[card]));
|
||||||
|
}
|
||||||
|
let forceGpu = $('<div>', {class: 'form-group'}).append([
|
||||||
|
$('<label>', {for: 'forceGpu'}).text('Use GPU on all new images: '),
|
||||||
|
$('<select>', {name: 'forceGpu', id: 'forceGpu',}).append(gpuOptions)
|
||||||
|
]);
|
||||||
|
let submit = $('<div>', {class: 'form-group'}).append([
|
||||||
|
$('<input>', {name: 'submit', type: 'submit', value: 'Next', class: 'btn btn-default btn-ghost'})
|
||||||
|
]);
|
||||||
|
fieldset.append([
|
||||||
|
keepOldImages,
|
||||||
|
forceGpu,
|
||||||
|
submit
|
||||||
|
]);
|
||||||
|
form.append(fieldset);
|
||||||
|
$('#container').append(form);
|
||||||
|
// Grab data and move to image selection
|
||||||
|
form.on('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
upgradeSettings.keepOldImages = $('#keepOldImages').is(":checked");
|
||||||
|
upgradeSettings.forceGpu = $('#forceGpu').val();
|
||||||
|
pickImages(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Render image selection
|
// Render image selection
|
||||||
async function pickImages() {
|
async function pickImages(upgrade) {
|
||||||
|
var installText;
|
||||||
|
var installFunction;
|
||||||
|
if (upgrade) {
|
||||||
|
installText = 'Upgrade';
|
||||||
|
installFunction = 'upgrade()';
|
||||||
|
} else {
|
||||||
|
installText = 'Install';
|
||||||
|
installFunction = 'install()';
|
||||||
|
}
|
||||||
showContainer();
|
showContainer();
|
||||||
titleChange('Image Selection');
|
titleChange('Image Selection');
|
||||||
let imagesDiv = $('<div>', {class: 'cardcontainer', id: 'images'});
|
let imagesDiv = $('<div>', {class: 'cardcontainer', id: 'images'});
|
||||||
$('#container').append(imagesDiv);
|
$('#container').append(imagesDiv);
|
||||||
for await (let image of Object.keys(images.images).sort(Intl.Collator().compare)) {
|
for await (let image of images.images) {
|
||||||
let imageName = $('<p>').text(image);
|
let imageName = $('<p>').text(image.friendly_name);
|
||||||
let imageDiv = $('<div>', {
|
let imageDiv = $('<div>', {
|
||||||
class: 'card',
|
class: 'card',
|
||||||
id: image.replace(new RegExp(' ', 'g'), '_'),
|
id: image.friendly_name.replace(new RegExp(' ', 'g'), '_').replace('.', '-'),
|
||||||
title: images.images[image].description,
|
title: image.description,
|
||||||
onclick: 'selectImage(\'' + image.replace(new RegExp(' ', 'g'), '_') + '\')'
|
onclick: 'selectImage(\'' + image.friendly_name.replace(new RegExp(' ', 'g'), '_').replace('.', '-') + '\')'
|
||||||
}).append(imageName).css('filter', 'grayscale(100%)')
|
}).append(imageName).css('filter', 'grayscale(100%)')
|
||||||
let thumb = $('<img>', {class: 'thumb', src: 'public/' + images.images[image].image_src});
|
let thumb = $('<img>', {class: 'thumb', src: 'public/' + image.image_src});
|
||||||
imageDiv.append(thumb);
|
imageDiv.append(thumb);
|
||||||
$('#images').append(imageDiv);
|
$('#images').append(imageDiv);
|
||||||
}
|
}
|
||||||
let allButton = $('<button>', {class: 'btn btn-default btn-ghost center', onclick: 'selectAll()'}).text('Select All');
|
let allButton = $('<button>', {class: 'btn btn-default btn-ghost center', onclick: 'selectAll()'}).text('Select All');
|
||||||
let installButton = $('<button>', {class: 'btn btn-default btn-ghost center', onclick: 'install()'}).text('Install');
|
let installButton = $('<button>', {class: 'btn btn-default btn-ghost center', onclick: installFunction}).text(installText);
|
||||||
$('#container').append([
|
$('#container').append([
|
||||||
allButton,
|
allButton,
|
||||||
installButton
|
installButton
|
||||||
|
|
@ -248,16 +333,17 @@ async function pickImages() {
|
||||||
|
|
||||||
// Select an individual image
|
// Select an individual image
|
||||||
function selectImage(image) {
|
function selectImage(image) {
|
||||||
let imageKey = image.replace(new RegExp('_', 'g'), ' ');
|
let imageKey = image.replace(new RegExp('_', 'g'), ' ').replace('-', '.');
|
||||||
|
let safeSelectorId = $.escapeSelector(image); // Escape the ID for jQuery
|
||||||
if (installImages.includes(imageKey)) {
|
if (installImages.includes(imageKey)) {
|
||||||
installImages = installImages.filter(e => e !== imageKey)
|
installImages = installImages.filter(e => e !== imageKey)
|
||||||
$('#' + image).css({
|
$('#' + safeSelectorId).css({
|
||||||
filter: 'grayscale(100%)',
|
filter: 'grayscale(100%)',
|
||||||
background: ''
|
background: ''
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
installImages.push(imageKey);
|
installImages.push(imageKey);
|
||||||
$('#' + image).css({
|
$('#' + safeSelectorId).css({
|
||||||
filter: '',
|
filter: '',
|
||||||
background: '#89cff0'
|
background: '#89cff0'
|
||||||
});
|
});
|
||||||
|
|
@ -269,19 +355,21 @@ function selectAll() {
|
||||||
installImages = [];
|
installImages = [];
|
||||||
if (selected) {
|
if (selected) {
|
||||||
selected = false;
|
selected = false;
|
||||||
for (let image of Object.keys(images.images)) {
|
for (let image of images.images) {
|
||||||
let imageElem = image.replace(new RegExp(' ', 'g'), '_');
|
let imageElem = image.friendly_name.replace(new RegExp(' ', 'g'), '_').replace('.', '-');
|
||||||
$('#' + imageElem).css({
|
let safeSelectorId = $.escapeSelector(imageElem); // Escape the ID for jQuery
|
||||||
|
$('#' + safeSelectorId).css({
|
||||||
filter: 'grayscale(100%)',
|
filter: 'grayscale(100%)',
|
||||||
background: ''
|
background: ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selected = true;
|
selected = true;
|
||||||
for (let image of Object.keys(images.images)) {
|
for (let image of images.images) {
|
||||||
let imageElem = image.replace(new RegExp(' ', 'g'), '_');
|
let imageElem = image.friendly_name.replace(new RegExp(' ', 'g'), '_').replace('.', '-');
|
||||||
installImages.push(image);
|
let safeSelectorId = $.escapeSelector(imageElem); // Escape the ID for jQuery
|
||||||
$('#' + imageElem).css({
|
installImages.push(image.friendly_name);
|
||||||
|
$('#' + safeSelectorId).css({
|
||||||
filter: '',
|
filter: '',
|
||||||
background: '#89cff0'
|
background: '#89cff0'
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue