Completed installation procedure on wizard

This commit is contained in:
Pax1601 2023-12-22 18:44:31 +01:00
parent 92b1a46e8a
commit 15e8c9e791
26 changed files with 1541 additions and 150 deletions

View File

@ -0,0 +1,110 @@
<style>
#manager-connections {
display: flex;
flex-direction: column;
row-gap: 15px;
}
#manager-connections .input-group {
color: var(--offwhite);
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-connections .input-group>span:nth-child(1) {
font-size: 14px;
font-weight: 600;
}
#manager-connections .input-group>span:nth-child(2) {
font-size: 13px;
font-weight: normal;
}
#manager-connections .input-group div {
display: flex;
align-items: center;
column-gap: 5px;
flex-wrap: wrap;
}
#manager-connections>.instructions {
margin-bottom: 10px;
}
#manager-connections>.buttons-footer {
margin-top: 10px;
}
#manager-connections .client-port input {
width: 150px;
}
#manager-connections .backend-port input {
width: 150px;
}
#manager-connections .success {
content: url("./icons/check-solid-green.svg");
height: 20px;
width: 20px;
}
#manager-connections .error img {
content: url("./icons/triangle-exclamation-solid.svg");
height: 20px;
width: 20px;
}
#manager-connections .error span {
font-weight: 600;
font-size: 12px;
color: var(--red);
height: fit-content;
}
</style>
<div id="manager-connections">
<div class="page-header">
Step 2: Port and address settings
</div>
<div class="input-group client-port">
<span>Client port</span>
<span>This port is used to allow access to Olympus. Be sure to allow this port through your firewall if you want people to connect remotely.</span>
<div>
<input type="number" min="1024" max="65535" value="<%= instance["clientPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
<div class="input-group backend-port">
<span>Backend port</span>
<span>This port is used to communicate with DCS. It is not necessary to allow this port through your firewall.</span>
<div>
<input type="number" min="1024" max="65535" value="<%= instance["backendPort"] %>">
<img class="success hide">
<div class="error hide">
<img> <span>Port already in use</span>
</div>
</div>
</div>
<div class="input-group backend-address">
<span>Backend address</span>
<span>This is the backend address Olympus will listen on. Unless you know what you are doing, leave it as localhost, even for dedicated server installations.</span>
<input type="text" value="<%= instance["backendAddress"] %>">
</div>
<div class="buttons-footer">
<div class="button back">
Back
</div>
<div class="button next">
Next
</div>
<div class="button cancel">
Cancel
</div>
</div>
</div>

View File

@ -1,15 +1,21 @@
<style>
#manager-installations {
#manager-installations .scroll-container {
height: 440px;
overflow-y: auto;
}
#manager-installations .scrollable {
display: flex;
flex-direction: column;
row-gap: 8px;
height: fit-content;
}
#manager-installations>.option {
#manager-installations .scrollable>.option {
cursor: pointer;
background-color: var(--darkgray);
width: 100%;
height: 80px;
height: 100px;
color: white;
display: flex;
font-size: 13px;
@ -20,18 +26,74 @@
border-left: 5px solid var(--blue);
}
#manager-installations>.selected {
#manager-installations .scrollable>.option * {
pointer-events: none;
}
#manager-installations .scrollable>.option.selected {
background-color: var(--blue);
}
#manager-installations .scrollable>.option .checkbox {
padding-right: 20px;
content: url("./icons/square-regular.svg");
}
#manager-installations .scrollable>.option.selected .checkbox {
content: url("./icons/check-solid.svg");
}
#manager-installations .scrollable>.option>div {
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-installations .scrollable>.option>div>span:nth-child(1) {
font-size: 18px;
font-weight: 600;
}
#manager-installations .scrollable>.option>div>span:nth-child(2) {
font-size: 13px;
font-weight: 600;
color: var(--gray);
}
#manager-installations .scrollable>.option>div>span:nth-child(2).installed {
font-weight: 600;
color: var(--green);
}
#manager-installations .scrollable>.option>div>span:nth-child(2).error {
font-weight: 600;
color: orange;
}
#manager-installations .scrollable>.option>div>span:nth-child(3) {
display: flex;
column-gap: 10px;
justify-content: center;
font-size: 13px;
font-weight: normal;
}
#manager-installations>.instructions {
margin-bottom: 10px;
}
#manager-installations>.buttons-footer {
margin-top: 10px;
}
</style>
<div id="manager-installations">
<div class="page-header">
Step 1: Select DCS Installations
</div>
<div class="instruction">
<div class="instructions">
<span>
Select the copies of DCS you want to install Olympus to.
Select the copy of DCS you want to install Olympus to.
</span>
<span>
For most people, this is your main DCS installation.
@ -40,10 +102,33 @@
If you are running a dedicated server, you would also install Olympus to this DCS version.
</span>
</div>
<%= instances[0] %>
<% for (let i = 0; i < instances.length; i++) {%>
<div class="option">
<%= instances[i] %>
<div class="scroll-container">
<div class="scrollable">
<% for (let i = 0; i < instances.length; i++) {%>
<div class="option" data-folder="<%= instances[i].folder %>">
<img class="checkbox">
<div>
<span><%= instances[i].name %></span>
<span class="<%= instances[i].installed? (instances[i].error? 'error': 'installed'): '' %>">
<%= instances[i].installed? (instances[i].error? 'Corrupted/outdated Olympus installation': 'Olympus already installed'): 'Olympus not installed yet' %>
</span>
<span><img src="./icons/folder-open-solid.svg"> <%= instances[i].folder %></span>
</div>
</div>
<% } %>
</div>
<% } %>
</div>
<div class="buttons-footer">
<div class="button back">
Back
</div>
<div class="button next">
Next
</div>
<div class="button cancel">
Cancel
</div>
</div>
</div>

View File

@ -0,0 +1,159 @@
<style>
#manager-instances .scroll-container {
height: 440px;
overflow-y: auto;
}
#manager-instances .scrollable {
display: flex;
flex-direction: column;
row-gap: 8px;
height: fit-content;
}
#manager-instances .scrollable>.option {
cursor: pointer;
background-color: var(--darkgray);
width: 100%;
height: 230px;
color: white;
display: flex;
font-size: 13px;
font-weight: 600;
padding-left: 15px;
align-items: center;
border-radius: 5px;
border-left: 5px solid var(--blue);
}
#manager-instances .scrollable>.option * {
pointer-events: none;
}
#manager-instances .scrollable>.option.selected {
background-color: var(--blue);
}
#manager-instances .scrollable>.option .checkbox {
padding-right: 20px;
content: url("./icons/square-regular.svg");
}
#manager-instances .scrollable>.option.selected .checkbox {
content: url("./icons/check-solid.svg");
}
#manager-instances .scrollable>.option>div {
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-instances .scrollable>.option>div>span:nth-child(1) {
font-size: 18px;
font-weight: 600;
}
#manager-instances .scrollable>.option>div>span:nth-child(2) {
font-size: 13px;
font-weight: 600;
color: var(--gray);
}
#manager-instances .scrollable>.option>div>span:nth-child(2).installed {
font-weight: 600;
color: var(--green);
}
#manager-instances .scrollable>.option>div>span:nth-child(2).error {
font-weight: 600;
color: orange;
}
#manager-instances .scrollable>.option>div>span:nth-child(3) {
display: flex;
column-gap: 10px;
justify-content: center;
font-size: 13px;
font-weight: normal;
}
#manager-instances>.instructions {
margin-bottom: 10px;
}
#manager-instances>.buttons-footer {
margin-top: 10px;
}
#manager-instances .info {
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-instances .info>div:nth-child(1) {
font-weight: 600;
font-size: 14px;
}
#manager-instances .info>div:nth-child(2) {
font-weight: normal;
font-size: 14px;
}
</style>
<div id="manager-instances">
<div class="page-header">
Step 1: Select DCS Installations
</div>
<div class="instructions">
<span>
Select the copy of DCS you want to install Olympus to.
</span>
<span>
For most people, this is your main DCS installation.
</span>
<span>
If you are running a dedicated server, you would also install Olympus to this DCS version.
</span>
</div>
<div class="scroll-container">
<div class="scrollable">
<% for (let i = 0; i < instances.length; i++) {%>
<div class="option" data-folder="<%= instances[i].folder %>">
<div>
<span><%= instances[i].name %></span>
<span class="<%= instances[i].installed? (instances[i].error? 'error': ''): '' %>">
<%= instances[i].installed? (instances[i].error? 'Corrupted/outdated Olympus installation': ''): '' %>
</span>
<span><img src="./icons/folder-open-solid.svg"> <%= instances[i].folder %></span>
<div class="info">
<div>Client port</div>
<div> <%= instances[i].clientPort %> </div>
</div>
<div class="info">
<div>Backend port</div>
<div> <%= instances[i].backendPort %> </div>
</div>
<div class="info">
<div>Backend address</div>
<div> <%= instances[i].backendAddress %> </div>
</div>
</div>
</div>
<% } %>
</div>
</div>
<div class="buttons-footer">
<div class="button back">
Back
</div>
<div class="button next">
Next
</div>
<div class="button cancel">
Cancel
</div>
</div>
</div>

View File

@ -16,6 +16,11 @@
padding-left: 15px;
align-items: center;
border-radius: 5px;
cursor: pointer;
}
#manager-menu>.option * {
pointer-events: none;
}
.inverted {
@ -28,13 +33,13 @@
<div class="page-header">
What do you want to do?
</div>
<div class="option">
<div class="option install">
Install Olympus
</div>
<div class="option">
<div class="option update">
Update/remove Olympus
</div>
<div class="inverted">
<div class="option inverted manage">
View and manage instances
</div>
</div>

View File

@ -0,0 +1,73 @@
<style>
#manager-passwords {
display: flex;
flex-direction: column;
row-gap: 15px;
}
#manager-passwords .input-group {
color: var(--offwhite);
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-passwords .input-group>span:nth-child(1) {
font-size: 14px;
font-weight: 600;
}
#manager-passwords .input-group>span:nth-child(2) {
font-size: 13px;
font-weight: normal;
}
#manager-passwords>.instructions {
margin-bottom: 10px;
}
#manager-passwords>.buttons-footer {
margin-top: 10px;
}
#manager-passwords input {
width: 250px;
}
</style>
<div id="manager-passwords">
<div class="page-header">
Step 3: Passwords
</div>
<div class="input-group">
<span>Enter your passwords to access Olympus</span>
<span>By using the passwords below, you can access different roles in Olympus.</span>
</div>
<div class="input-group game-master">
<span>Game Master Password</span>
<span>This password is used to access Olympus as Game Master with full priviledges.</span>
<input type="password" minlength="8">
</div>
<div class="input-group blue-commander">
<span>Blue Commander Password</span>
<span>This password is used to access Olympus as blue coalition Commander.</span>
<input type="password" minlength="8">
</div>
<div class="input-group red-commander">
<span>Red Commander Password</span>
<span>This password is used to access Olympus as red coalition Commander.</span>
<input type="password" minlength="8">
</div>
<div class="buttons-footer">
<div class="button back">
Back
</div>
<div class="button next">
Next
</div>
<div class="button cancel">
Cancel
</div>
</div>
</div>

View File

@ -0,0 +1,160 @@
<style>
#manager-result {
display: flex;
flex-direction: column;
row-gap: 15px;
}
#manager-result img.success {
content: url("./icons/check-solid-green.svg");
height: 20px;
width: 20px;
}
#manager-result img.error {
content: url("./icons/triangle-exclamation-solid.svg");
height: 20px;
width: 20px;
}
#manager-result img.wait {
content: url("./icons/spinner-solid.svg");
height: 20px;
width: 20px;
animation: rotate 2s linear infinite;
}
@keyframes rotate {
0% {
transform: rotate(0deg)
}
100% {
transform: rotate(360deg)
}
}
#manager-result .summary {
font-weight: 600;
font-size: 14px;
}
#manager-result .summary.success {
color: var(--green);
}
#manager-result .summary.error {
color: var(--red);
}
#manager-result .info {
font-weight: 600;
font-size: 14px;
color: var(--offwhite);
}
.step {
display: flex;
align-items: center;
font-size: 13px;
font-weight: 600;
color: var(--offwhite);
column-gap: 10px;
}
#manager-result>.result {
cursor: pointer;
background-color: var(--blue);
width: 100%;
height: 80px;
color: white;
display: flex;
font-size: 13px;
font-weight: 600;
padding-left: 15px;
align-items: center;
border-radius: 5px;
}
#manager-result>.result>img {
margin-left: 5px;
margin-right: 20px;
}
#manager-result>.result>div {
display: flex;
flex-direction: column;
row-gap: 5px;
}
#manager-result>.result>div>span:nth-child(1) {
font-size: 15px;
font-weight: 600;
}
#manager-result>.result>div>span:nth-child(2) {
display: flex;
column-gap: 10px;
justify-content: center;
font-size: 13px;
font-weight: normal;
}
</style>
<div id="manager-result">
<div class="page-header">
Please wait while Olympus is being installed
</div>
<div class="step hook">
Installing hook scripts<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step mod">
Installing mod folder<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step json">
Installing configuration file<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step config">
Applying configuration<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="step shortcuts">
Creating shortcuts<img class="wait"><img class="success hide"><img class="error hide">
</div>
<div class="summary success hide">
Olympus successfully installed in the following DCS instance
</div>
<div class="summary error hide">
An error has occurred while installing Olympus
</div>
<div class="result">
<img class="wait"><img class="success hide"><img class="error hide">
<div>
<span>
<%= instance.name %>
</span>
<span><img src="./icons/folder-open-solid.svg">
<%= instance.folder %>
</span>
</div>
</div>
<div class="info success">
You may now start DCS and use Olympus either with the shortcuts or the "View and manage Olympus" entry in the
main menu
</div>
<div class="info error">
Please make sure DCS is not currently being executed
</div>
<div class="buttons-footer">
<div class="button back">
Back to main menu
</div>
<div class="button cancel">
Close
</div>
</div>
</div>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path fill="#8bff63" d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M304 48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zm0 416a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM48 304a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm464-48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM142.9 437A48 48 0 1 0 75 369.1 48 48 0 1 0 142.9 437zm0-294.2A48 48 0 1 0 75 75a48 48 0 1 0 67.9 67.9zM369.1 437A48 48 0 1 0 437 369.1 48 48 0 1 0 369.1 437z"/></svg>

After

Width:  |  Height:  |  Size: 609 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM337 209L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>

After

Width:  |  Height:  |  Size: 529 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#F2F2F2" d="M384 80c8.8 0 16 7.2 16 16V416c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16H384zM64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64z"/></svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="#FF5858" d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@ -29,6 +29,16 @@
</div>
<div id="main-div">
</div>
<div id="grayout" class="hide"></div>
<div id="popup" class="hide">
<img src="./icons/triangle-exclamation-solid.svg">
<div class="content">
This is an example content
</div>
<div class="footer">
<div class="button close-popup"> Close </div>
</div>
</div>
<div id="footer">

View File

@ -0,0 +1,180 @@
var regedit = require('regedit')
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
var fs = require('fs')
var path = require('path')
const vi = require('win-version-info');
const checkPort = require('./net')
const dircompare = require('dir-compare');
class DCSInstance {
static instances = null;
static async getInstances() {
if (this.instances === null) {
this.instances = await this.findInstances();
}
return this.instances;
}
static async findInstances() {
let promise = new Promise((res, rej) => {
regedit.list(shellFoldersKey, function (err, result) {
if (err) {
rej(err);
}
else {
if (result[shellFoldersKey] !== undefined && result[shellFoldersKey]["exists"] && result[shellFoldersKey]['values'][saveGamesKey] !== undefined && result[shellFoldersKey]['values'][saveGamesKey]['value'] !== undefined) {
const searchpath = result[shellFoldersKey]['values'][saveGamesKey]['value'];
const folders = fs.readdirSync(searchpath);
var instances = [];
folders.forEach((folder) => {
if (fs.existsSync(path.join(searchpath, folder, "Config", "appsettings.lua")) ||
fs.existsSync(path.join(searchpath, folder, "Config", "serversettings.lua"))) {
instances.push(new DCSInstance(path.join(searchpath, folder)));
}
})
res(instances);
} else {
console.error("An error occured while trying to fetch the location of the DCS instances.")
rej("An error occured while trying to fetch the location of the DCS instances.");
}
}
})
});
return promise;
}
folder = "";
name = "";
clientPort = 3000;
backendPort = 3001;
backendAddress = "localhost";
gameMasterPassword = "";
blueCommanderPassword = "";
redCommanderPassword = "";
installed = false;
error = false;
constructor(folder) {
this.folder = folder;
this.name = path.basename(folder);
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))){
this.installed = true;
const options = { compareContent: true };
var err1 = true;
var err2 = true;
var res1;
var res2;
try {
res1 = dircompare.compareSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), options);
res2 = dircompare.compareSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"), options);
err1 = res1.differences !== 0;
err2 = res2.differences !== 0;
} catch(e) {
console.log(e);
}
if (err1 || err2) {
console.log(res1)
console.log(res2)
this.error = true;
}
}
}
async setClientPort(newPort) {
if (await this.checkClientPort(newPort)) {
console.log(`Instance ${this.folder} client port set to ${newPort}`)
this.clientPort = newPort;
return true;
}
return false;
}
async setBackendPort(newPort) {
if (await this.checkBackendPort(newPort)) {
console.log(`Instance ${this.folder} client port set to ${newPort}`)
this.backendPort = newPort;
return true;
}
return false;
}
setBackendAddress(newAddress) {
this.backendAddress = newAddress;
}
setGameMasterPassword(newPassword) {
this.gameMasterPassword = newPassword;
}
setBlueCommanderPassword(newPassword) {
this.blueCommanderPassword = newPassword;
}
setRedCommanderPassword(newPassword) {
this.redCommanderPassword = newPassword;
}
async checkClientPort(port) {
var promise = new Promise((res, rej) => {
checkPort(port, async (portFree) => {
if (portFree) {
portFree = !(await DCSInstance.getInstances()).some((instance) => {
if (instance !== this) {
if (instance.clientPort === port || instance.backendPort === port) {
console.log(`Port ${port} already selected by other instance`);
return true;
}
} else {
if (instance.backendPort === port) {
console.log(`Port ${port} equal to backend port`);
return true;
}
}
return false;
})
}
else {
console.log(`Port ${port} currently in use`);
}
res(portFree);
})
})
return promise;
}
async checkBackendPort(port) {
var promise = new Promise((res, rej) => {
checkPort(port, async (portFree) => {
if (portFree) {
portFree = !(await DCSInstance.getInstances()).some((instance) => {
if (instance !== this) {
if (instance.clientPort === port || instance.backendPort === port) {
console.log(`Port ${port} already selected by other instance`);
return true;
}
} else {
if (instance.clientPort === port) {
console.log(`Port ${port} equal to client port`);
return true;
}
}
return false;
})
} else {
console.log(`Port ${port} currently in use`);
}
res(portFree);
})
})
return promise;
}
}
module.exports = DCSInstance;

View File

@ -1,115 +1,148 @@
const sha256 = require('sha256')
const createShortcut = require('create-desktop-shortcuts');
const fs = require('fs');
const path = require('path');
export function installOlympus(folder) {
console.log(`Installing Olympus in ${folder}`);
try {
fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true });
fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"));
fs.cpSync(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json"));
if (createShortcut({
async function fixInstances(instances) {
var promise = new Promise((res, rej) => {
var instancePromises = instances.map((instance) => {
var instancePromise = new Promise((instanceRes, instanceErr) => {
installMod(instance.folder)
.then(() => installHooks(instance.folder))
.then(() => installShortCuts(instance.folder, instance.name))
.then(() => instanceRes(true), (err) => { instanceErr(err) })
})
return instancePromise;
});
console.log(instancePromises);
Promise.all(instancePromises).then(() => res(true), (err) => { rej(err) });
})
console.log(promise);
return promise;
}
async function installMod(folder) {
console.log(`Installing mod in ${folder}`)
var promise = new Promise((res, rej) => {
fs.cp(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true }, (err) => {
if (err) {
console.log(`Error installing mod in ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Mod succesfully installed in ${folder}`)
res(true);
}
});
})
return promise;
}
async function installHooks(folder) {
console.log(`Installing hooks in ${folder}`)
var promise = new Promise((res, rej) => {
fs.cp(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hooks", "OlympusHook.lua"), (err) => {
if (err) {
console.log(`Error installing hooks in ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Hooks succesfully installed in ${folder}`)
res(true);
}
});
})
return promise;
}
async function installJSON(folder) {
console.log(`Installing config in ${folder}`)
var promise = new Promise((res, rej) => {
fs.cp(path.join("..", "olympus.json"), path.join(folder, "Config", "olympus.json"), (err) => {
if (err) {
console.log(`Error installing config in ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Config succesfully installed in ${folder}`)
res(true);
}
});
})
return promise;
}
async function applyConfiguration(folder, instance) {
console.log(`Applying configuration to Olympus in ${folder}`);
var promise = new Promise((res, rej) => {
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json")));
config["client"]["port"] = instance.clientPort;
config["server"]["port"] = instance.backendPort;
config["server"]["address"] = instance.backendAddress;
config["authentication"]["gameMasterPassword"] = sha256(instance.gameMasterPassword);
config["authentication"]["blueCommanderPassword"] = sha256(instance.blueCommanderPassword);
config["authentication"]["redCommanderPassword"] = sha256(instance.redCommanderPassword);
fs.writeFile(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4), (err) => {
if (err) {
console.log(`Error applying config in ${folder}: ${err}`)
rej(err);
}
else {
console.log(`Config succesfully applied in ${folder}`)
res(true);
}
});
} else {
rej("File does not exist")
}
res(true);
});
return promise;
}
async function installShortCuts(folder, name) {
console.log(`Installing shortcuts for Olympus in ${folder}`);
var promise = new Promise((res, rej) => {
var res1 = createShortcut({
windows: {
filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'),
outputPath: folder,
name: "DCS Olympus Client",
name: `DCS Olympus Client (${name})`,
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'),
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
}
}) &&
createShortcut({
windows: {
filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'),
outputPath: folder,
name: "DCS Olympus Server",
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'),
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
}
})) {
console.log("Shorcuts created succesfully")
});
var res2 = createShortcut({
windows: {
filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'),
outputPath: folder,
name: `DCS Olympus Server (${name})`,
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'),
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
}
});
if (res1 && res2) {
res(true);
} else {
return false;
rej("An error occurred while creating the shortcuts")
}
} catch (e) {
console.error(e);
return false;
}
loadDivs();
return true;
});
return promise;
}
export function uninstallOlympus(folder) {
console.log(`Uninstalling Olympus from ${folder}`);
try {
fs.rmSync(path.join(folder, "Mods", "Services", "Olympus"), { recursive: true, force: true });
fs.rmSync(path.join(folder, "Config", "olympus.json"), {force: true});
loadDivs();
} catch (e) {
console.error(e);
return false;
}
return true;
module.exports = {
applyConfiguration: applyConfiguration,
installJSON: installJSON,
installHooks: installHooks,
installMod: installMod,
installShortCuts, installShortCuts,
fixInstances: fixInstances
}
export function applyConfiguration(folder, data) {
console.log(`Applying configuration to Olympus from ${folder}`);
if (fs.existsSync(path.join(folder, "Config", "olympus.json"))) {
var config = JSON.parse(fs.readFileSync(path.join(folder, "Config", "olympus.json")));
config["client"]["port"] = data["clientPort"];
config["server"]["port"] = data["backendPort"];
config["server"]["address"] = data["backendAddress"];
config["authentication"]["gameMasterPassword"] = sha256(data["gameMasterPassword"]);
config["authentication"]["blueCommanderPassword"] = sha256(data["blueCommanderPassword"]);
config["authentication"]["redCommanderPassword"] = sha256(data["redCommanderPassword"]);
try {
fs.writeFileSync(path.join(folder, "Config", "olympus.json"), JSON.stringify(config, null, 4));
} catch (e) {
console.error(e);
return false;
}
} else {
return false;
}
return true;
}
export function updateOlympus(folder) {
console.log(`Updating Olympus in ${folder}`);
try {
fs.cpSync(path.join("..", "mod"), path.join(folder, "Mods", "Services", "Olympus"), { recursive: true });
fs.cpSync(path.join("..", "scripts", "OlympusHook.lua"), path.join(folder, "Scripts", "Hook", "OlympusHook.lua"));
loadDivs();
} catch (e) {
console.error(e);
return false;
}
return true;
}
export function createDesktopShortcuts(folder) {
if (createShortcut({
windows: {
filePath: path.resolve(__dirname, '..', '..', 'client', 'client.vbs'),
name: "DCS Olympus Client",
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus.ico'),
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
}
}) && createShortcut({
windows: {
filePath: path.resolve(__dirname, '..', '..', 'client', 'server.vbs'),
name: "DCS Olympus Server",
arguments: `"${path.join(folder, "Config", "olympus.json")}"`,
icon: path.resolve(__dirname, '..', '..', 'img', 'olympus_server.ico'),
workingDirectory: path.resolve(__dirname, '..', '..', 'client')
}
})) {
showPopup("Shortcuts created successfully!")
} else {
showPopup("And error occurred while creating the shortcuts.")
}
}

View File

@ -0,0 +1,69 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerConnections extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
instance;
constructor(options) {
super(options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
this.element.querySelector(".client-port").querySelector("input").addEventListener("change", async (e) => { this.setClientPort(Number(e.target.value)); })
this.element.querySelector(".backend-port").querySelector("input").addEventListener("change", async (e) => { this.setBackendPort(Number(e.target.value)); })
this.element.querySelector(".backend-address").querySelector("input").addEventListener("change", async (e) => { this.instance.setBackendAddress(e.target.value); })
}
show(instance) {
this.instance = instance;
this.options["instance"] = instance;
ejs.renderFile("./ejs/managerconnections.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
this.setClientPort(this.instance.clientPort);
this.setBackendPort(this.instance.backendPort);
} else {
console.error(err);
}
});
super.show();
}
async setClientPort(newPort) {
const success = await this.instance.setClientPort(newPort);
var successEls = this.element.querySelector(".client-port").querySelectorAll(".success");
for (let i = 0; i < successEls.length; i++) {
successEls[i].classList.toggle("hide", !success);
}
var errorEls = this.element.querySelector(".client-port").querySelectorAll(".error");
for (let i = 0; i < errorEls.length; i++) {
errorEls[i].classList.toggle("hide", success);
}
}
async setBackendPort(newPort) {
const success = await this.instance.setBackendPort(newPort);
var successEls = this.element.querySelector(".backend-port").querySelectorAll(".success");
for (let i = 0; i < successEls.length; i++) {
successEls[i].classList.toggle("hide", !success);
}
var errorEls = this.element.querySelector(".backend-port").querySelectorAll(".error");
for (let i = 0; i < errorEls.length; i++) {
errorEls[i].classList.toggle("hide", success);
}
}
}
module.exports = ManagerConnections;

View File

@ -2,30 +2,53 @@ const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerInstallations extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
ejs.renderFile("./ejs/managerinstallations.ejs", options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(str);
}
});
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
this.element.innerHTML = str;
var options = element.querySelectorAll(".option");
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].onclick = (e) => {this.onOptionClicked(e);}
}
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
onOptionClicked(e) {
e.target.classList.toggle("selected")
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].classList.remove("selected");
}
e.target.classList.add("selected");
}
getSelectedInstance() {
return this.options.instances.find((instance) => {
const selected = this.element.querySelector(".selected");
return selected? selected.dataset.folder === instance.folder: false;
});
}
show() {
ejs.renderFile("./ejs/managerinstallations.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}

View File

@ -0,0 +1,55 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerInstances extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
}
render(str) {
this.element.innerHTML = str;
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].onclick = (e) => {this.onOptionClicked(e);}
}
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
onOptionClicked(e) {
var options = this.element.querySelectorAll(".option");
for (let i = 0; i < options.length; i++) {
options[i].classList.remove("selected");
}
e.target.classList.add("selected");
}
getSelectedInstance() {
return this.options.instances.find((instance) => {
const selected = this.element.querySelector(".selected");
return selected? selected.dataset.folder === instance.folder: false;
});
}
show() {
ejs.renderFile("./ejs/managerinstances.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}
module.exports = ManagerInstances;

View File

@ -2,6 +2,10 @@ const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerMenu extends ManagerPage {
onInstallClicked;
onUpdateClicked;
onManageClicked;
constructor(options) {
super(options);
@ -9,7 +13,7 @@ class ManagerMenu extends ManagerPage {
if (!err) {
this.render(str);
} else {
console.error(str);
console.error(err);
}
});
}
@ -17,6 +21,10 @@ class ManagerMenu extends ManagerPage {
render(str) {
const element = this.getElement();
element.innerHTML = str;
element.querySelector(".install").addEventListener("click", (e) => this.onInstallClicked(e));
element.querySelector(".update").addEventListener("click", (e) => this.onUpdateClicked(e))
element.querySelector(".manage").addEventListener("click", (e) => this.onManageClicked(e))
}
}

View File

@ -1,15 +1,24 @@
class ManagerPage {
element;
options;
constructor(options) {
this.options = options ?? {};
this.element = document.createElement('div');
this.element.classList.add("manager-page");
this.element.classList.add("manager-page", "hide");
}
getElement() {
return this.element;
}
show() {
this.element.classList.remove("hide");
}
hide() {
this.element.classList.add("hide");
}
}
module.exports = ManagerPage;

View File

@ -0,0 +1,42 @@
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerPasswords extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".next").addEventListener("click", (e) => this.onNextClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
this.element.querySelector(".game-master").querySelector("input").addEventListener("change", async (e) => { this.instance.setGameMasterPassword(e.target.value); })
this.element.querySelector(".blue-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setBlueCommanderPassword(e.target.value); })
this.element.querySelector(".red-commander").querySelector("input").addEventListener("change", async (e) => { this.instance.setRedCommanderPassword(e.target.value); })
}
show(instance) {
this.instance = instance;
this.options["instance"] = instance;
ejs.renderFile("./ejs/managerpasswords.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
}
module.exports = ManagerPasswords;

View File

@ -0,0 +1,113 @@
const { installMod, installHooks, installJSON, applyConfiguration, installShortCuts } = require("./filesystem");
const ManagerPage = require("./managerpage");
const ejs = require('ejs')
class ManagerResult extends ManagerPage {
onBackClicked;
onNextClicked;
onCancelClicked;
constructor(options) {
super(options);
}
render(str) {
const element = this.getElement();
element.innerHTML = str;
this.element.querySelector(".back").addEventListener("click", (e) => this.onBackClicked(e));
this.element.querySelector(".cancel").addEventListener("click", (e) => this.onCancelClicked(e));
}
show(instance) {
this.instance = instance;
this.options["instance"] = instance;
ejs.renderFile("./ejs/managerresult.ejs", this.options, {}, (err, str) => {
if (!err) {
this.render(str);
} else {
console.error(err);
}
});
super.show();
}
startInstallation() {
installHooks(this.instance.folder).then(
() => {
this.applyStepSuccess(".hook");
},
(err) => {
this.applyStepFailure(".hook");
return Promise.reject(err);
}
).then(() => installMod(this.instance.folder)).then(
() => {
this.applyStepSuccess(".mod");
},
(err) => {
this.applyStepFailure(".mod");
return Promise.reject(err);
}
).then(() => installJSON(this.instance.folder)).then(
() => {
this.applyStepSuccess(".json");
},
(err) => {
this.applyStepFailure(".json");
return Promise.reject(err);
}
).then(() => applyConfiguration(this.instance.folder, this.instance)).then(
() => {
this.applyStepSuccess(".config");
},
(err) => {
this.applyStepFailure(".config");
return Promise.reject(err);
}
).then(() => installShortCuts(this.instance.folder, this.instance.name)).then(
() => {
this.applyStepSuccess(".shortcuts");
},
(err) => {
this.applyStepFailure(".shortcuts");
return Promise.reject(err);
}
).then(
() => {
this.element.querySelector(".summary.success").classList.remove("hide");
this.element.querySelector(".summary.error").classList.add("hide");
this.element.querySelector(".info.success").classList.remove("hide");
this.element.querySelector(".info.error").classList.add("hide");
this.element.querySelector(".result .success").classList.remove("hide");
this.element.querySelector(".result .error").classList.add("hide");
this.element.querySelector(".result .wait").classList.add("hide");
this.element.querySelector(".page-header").innerText = "Install successfull!";
},
() => {
this.element.querySelector(".summary.success").classList.add("hide");
this.element.querySelector(".summary.error").classList.remove("hide");
this.element.querySelector(".result .success").classList.add("hide");
this.element.querySelector(".result .error").classList.remove("hide");
this.element.querySelector(".result .wait").classList.add("hide");
this.element.querySelector(".page-header").innerText = "Install error!";
}
);
}
applyStepSuccess(step) {
this.element.querySelector(step).querySelector(".success").classList.remove("hide");
this.element.querySelector(step).querySelector(".error").classList.add("hide");
this.element.querySelector(step).querySelector(".wait").classList.add("hide");
}
applyStepFailure(step) {
this.element.querySelector(step).querySelector(".success").classList.add("hide");
this.element.querySelector(step).querySelector(".error").classList.remove("hide");
this.element.querySelector(step).querySelector(".wait").classList.add("hide");
}
}
module.exports = ManagerResult;

View File

@ -1,6 +1,6 @@
const portfinder = require('portfinder')
export function checkPort(port, callback) {
function checkPort(port, callback) {
portfinder.getPort({ port: port, stopPort: port }, (err, res) => {
if (err !== null) {
console.error(`Port ${port} already in use`);
@ -10,3 +10,5 @@ export function checkPort(port, callback) {
}
});
}
module.exports = checkPort;

View File

@ -0,0 +1,19 @@
function showPopup(message, onCloseCallback) {
document.getElementById("grayout").classList.remove("hide");
document.getElementById("popup").classList.remove("hide");
document.getElementById("popup").querySelector(".close-popup").addEventListener("click", (e) => {
hidePopup();
onCloseCallback();
})
document.getElementById("popup").querySelector(".content").innerText = message;
}
function hidePopup() {
document.getElementById("grayout").classList.add("hide");
document.getElementById("popup").classList.add("hide");
}
module.exports = {
showPopup: showPopup,
hidePopup: hidePopup
}

View File

@ -1,26 +1,22 @@
var regedit = require('regedit')
var fs = require('fs')
var path = require('path')
const ejs = require('ejs')
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
const vi = require('win-version-info');
const ManagerMenu = require("./managermenu");
const ManagerMenu = require("./managermenu");
const ManagerInstallations = require('./managerinstallations');
const shellFoldersKey = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
const saveGamesKey = '{4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4}'
var instanceDivs = [];
const DCSInstance = require('./dcsinstance');
const ManagerConnections = require('./managerconnections');
const ManagerPasswords = require('./managerpasswords');
const { showPopup } = require('./popup');
const ManagerResult = require('./managerresult');
const { fixInstances } = require('./filesystem');
const ManagerInstances = require('./managerinstances');
/* White-listed channels. */
const ipc = {
'render': {
/* From render to main. */
'send': [
'window:minimize',
'window:minimize',
'window:maximize',
'window:restore',
'window:close'
@ -64,10 +60,153 @@ contextBridge.exposeInMainWorld(
}
);
var managerMenu = new ManagerMenu();
var managerInstallations = new ManagerInstallations({instances: ["asd/asd1", "asd/asd2"]});
var activeInstance;
window.addEventListener('DOMContentLoaded', () => {
//document.body.appendChild(managerMenu.getElement());
async function setup() {
var instances = await DCSInstance.getInstances();
if (instances.some((instance) => {
return instance.installed && instance.error;
})) {
showPopup("One or more Olympus instances are corrupted or need updating. Press Close to fix this.", async () => {
fixInstances(instances.filter((instance) => {
return instance.installed && instance.error;
})).then(
() => { location.reload() },
() => { showPopup("An error occurred while trying to fix you installations. Please reinstall Olympus manually") }
)
})
}
/* Menu */
var managerMenu = new ManagerMenu();
managerMenu.onInstallClicked = (e) => {
managerMenu.hide();
managerInstallations.show();
}
managerMenu.onUpdateClicked = (e) => {
managerMenu.hide();
managerInstances.show();
}
/* Installations */
var managerInstallations = new ManagerInstallations({ instances: instances });
managerInstallations.onBackClicked = (e) => {
managerInstallations.hide();
managerMenu.show();
}
managerInstallations.onNextClicked = (e) => {
activeInstance = managerInstallations.getSelectedInstance();
if (activeInstance) {
managerInstallations.hide();
managerConnections.show(activeInstance);
} else {
showPopup("Please select the instance you want to install Olympus into.")
}
}
managerInstallations.onCancelClicked = (e) => {
managerInstallations.hide();
managerMenu.show();
}
/* Instances */
var managerInstances = new ManagerInstances({ instances: instances });
managerInstances.onBackClicked = (e) => {
managerInstances.hide();
managerMenu.show();
}
managerInstances.onNextClicked = (e) => {
activeInstance = managerInstances.getSelectedInstance();
if (activeInstance) {
managerInstances.hide();
managerConnections.show(activeInstance);
} else {
showPopup("Please select the instance you want to manage.")
}
}
managerInstances.onCancelClicked = (e) => {
managerInstances.hide();
managerMenu.show();
}
/* Connections */
var managerConnections = new ManagerConnections();
managerConnections.onBackClicked = (e) => {
managerConnections.hide();
managerInstallations.show();
}
managerConnections.onNextClicked = async (e) => {
if (activeInstance) {
if (await activeInstance.checkClientPort(activeInstance.clientPort) && await activeInstance.checkBackendPort(activeInstance.backendPort)) {
managerConnections.hide();
managerPasswords.show(activeInstance);
} else {
showPopup("Please make sure the selected ports are not already in use.")
}
} else {
showPopup("An error has occurred, please restart the Olympus Manager.")
}
}
managerConnections.onCancelClicked = (e) => {
managerConnections.hide();
managerMenu.show();
}
/* Passwords */
var managerPasswords = new ManagerPasswords();
managerPasswords.onBackClicked = (e) => {
if (activeInstance) {
managerPasswords.hide();
managerConnections.show(activeInstance);
} else {
showPopup("An error has occurred, please restart the Olympus Manager.")
}
}
managerPasswords.onNextClicked = (e) => {
if (activeInstance) {
if (activeInstance.gameMasterPassword === "" || activeInstance.blueCommanderPassword === "" || activeInstance.redCommanderPassword === "") {
showPopup("Please fill all the password inputs.")
}
else if (activeInstance.gameMasterPassword === activeInstance.blueCommanderPassword || activeInstance.blueCommanderPassword === activeInstance.redCommanderPassword || activeInstance.gameMasterPassword === activeInstance.redCommanderPassword) {
showPopup("All the passwords must be different from each other.")
} else {
managerPasswords.hide();
managerResult.show(activeInstance);
managerResult.startInstallation();
}
} else {
showPopup("An error has occurred, please restart the Olympus Manager.")
}
}
managerPasswords.onCancelClicked = (e) => {
managerPasswords.hide();
managerMenu.show();
}
/* Result */
var managerResult = new ManagerResult();
managerResult.onBackClicked = (e) => {
managerResult.hide();
managerMenu.show();
}
managerResult.onCancelClicked = (e) => {
managerResult.hide();
managerMenu.show();
}
document.body.appendChild(managerMenu.getElement());
document.body.appendChild(managerInstallations.getElement());
document.body.appendChild(managerInstances.getElement());
document.body.appendChild(managerConnections.getElement());
document.body.appendChild(managerPasswords.getElement());
document.body.appendChild(managerResult.getElement());
managerMenu.show();
}
/* On content loaded */
window.addEventListener('DOMContentLoaded', () => {
setup();
})

View File

@ -11,6 +11,7 @@
"dependencies": {
"create-desktop-shortcuts": "^1.10.1",
"create-windowless-app": "^11.0.0",
"dir-compare": "^4.2.0",
"ejs": "^3.1.9",
"electron": "^28.0.0",
"portfinder": "^1.0.32",

View File

@ -123,7 +123,7 @@ body {
}
.page-header {
font-size: 14px;
font-size: 18px;
font-weight: 600;
color: var(--offwhite);
border-bottom: 1px solid var(--offwhite);
@ -131,18 +131,109 @@ body {
margin-bottom: 10px;
}
.instruction {
.instructions {
color: var(--offwhite);
display: flex;
flex-direction: column;
row-gap: 4px;
}
.instruction>span:first-child {
.instructions>span:first-child {
font-size: 14px;
font-weight: 600;
}
.instruction>span:not(:first-child) {
.instructions>span:not(:first-child) {
font-size: 13px;
}
.buttons-footer {
display: flex;
column-gap: 10px;
}
.button {
padding: 10px 15px;
border-radius: 5px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
}
.back {
color: var(--background);
background-color: var(--offwhite);
}
.next {
color: var(--offwhite);
background-color: var(--blue);
}
.cancel {
padding: 10px 5px;
color: var(--offwhite);
}
.close-popup {
color: var(--offwhite);
background-color: var(--blue);
}
input {
outline: none;
font-weight: 600;
color: var(--background);
font-size: 13px;
padding: 3px 10px;
border-radius: 5px;
}
.hide {
display: none !important;
}
#grayout {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: black;
opacity: 30%;
}
#popup {
width: 300px;
height: 200px;
position: absolute;
background-color: var(--background);
border-radius: 5px;
left: calc(50% - 150px);
top: calc(50% - 100px);
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 20px;
align-items: center;
}
#popup img {
width: 50px;
height: 50px;
}
#popup .content {
color: var(--offwhite);
font-size: 13px;
font-weight: 600;
width: 100%;
text-align: left;
}
#popup .footer {
height: fit-content;
display: flex;
justify-content: end;
width: 100%;
}