2024-10-30 16:43:36 -03:00
|
|
|
|
|
|
|
// Elements
|
|
|
|
const composeEditor = document.getElementById("composeEditor");
|
|
|
|
const composeFileSelection = document.getElementById("composeFileSelection");
|
|
|
|
const result = document.getElementById("result");
|
|
|
|
const saveButton = document.getElementById("save");
|
|
|
|
const saveAndRunButton = document.getElementById("saveAndRun");
|
|
|
|
const stopButton = document.getElementById("stop");
|
|
|
|
|
|
|
|
// Load initial settings
|
|
|
|
document.onload = initPage();
|
|
|
|
|
|
|
|
// Event Listeners
|
|
|
|
composeFileSelection.addEventListener("change", dockerComposeFileChanged);
|
|
|
|
saveButton.addEventListener("click", saveDockerCompose);
|
|
|
|
saveAndRunButton.addEventListener("click", saveAndRunDockerCompose);
|
|
|
|
stopButton.addEventListener("click", stopDockerCompose);
|
|
|
|
|
|
|
|
// Initialize the page
|
|
|
|
function initPage() {
|
|
|
|
const editor = ace.edit(composeEditor)
|
|
|
|
|
|
|
|
// Editor Options
|
|
|
|
editor.setOptions({
|
|
|
|
highlightSelectedWord: true,
|
|
|
|
copyWithEmptySelection: true,
|
|
|
|
navigateWithinSoftTabs: true,
|
|
|
|
useSoftTabs: true,
|
|
|
|
printMargin: false,
|
|
|
|
fontSize: "20px",
|
|
|
|
theme: "ace/theme/cloud_editor_dark",
|
|
|
|
});
|
|
|
|
editor.session.setMode("ace/mode/yaml");
|
|
|
|
|
|
|
|
// TODO: Replace /home/spiri/services with some root level path
|
|
|
|
// Search for all docker-compose.yaml files in the services directory and populate the dropdown with directory names
|
|
|
|
const command = "/usr/bin/ls -d ~/services/*/docker-compose.yaml | sed 's/\\/docker-compose.yaml//'"
|
|
|
|
cockpit.spawn(["bash", "-c", command])
|
|
|
|
.then((data) => {
|
|
|
|
pathPairs = data.split("\n").map((path) => [path, path.replace("/home/spiri/services/", "")]);
|
|
|
|
addDropDown(composeFileSelection, pathPairs, pathPairs[0][0]);
|
|
|
|
dockerComposeFileChanged()
|
|
|
|
})
|
|
|
|
.catch((error) => displayFail(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback for when a new docker-compose file is selected
|
|
|
|
// Populate the editor with the contents of the selected file or clear the editor if no file is selected
|
|
|
|
function dockerComposeFileChanged() {
|
|
|
|
if (composeFileSelection.value === "") {
|
|
|
|
populateEditor("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const composeLocation = composeFileSelection.value + "/docker-compose.yaml";
|
|
|
|
cockpit.file(composeLocation)
|
|
|
|
.read().then((content) => successReadFile(content))
|
|
|
|
.catch(error => failureReadFile(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populate the editor with passed in data
|
|
|
|
function populateEditor(data) {
|
|
|
|
const editor = ace.edit(composeEditor);
|
|
|
|
editor.setValue(data, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback for when the file is successfully read
|
|
|
|
// Currently, it just populates the editor with the data
|
|
|
|
function successReadFile(data) {
|
|
|
|
populateEditor(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback for when the file read fails
|
|
|
|
// Currently, it just logs the error (obviously)
|
|
|
|
function failureReadFile(error) {
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the contents of the editor to the selected docker-compose file
|
|
|
|
function saveDockerCompose() {
|
|
|
|
if (composeFileSelection.value === "") return;
|
|
|
|
const composeLocation = composeFileSelection.value + "/docker-compose.yaml";
|
|
|
|
const editor = ace.edit(composeEditor);
|
|
|
|
const composeData = editor.getValue();
|
|
|
|
|
|
|
|
// Read the file and replace the contents with the new data
|
|
|
|
cockpit.file(composeLocation)
|
|
|
|
.replace(composeData)
|
|
|
|
.then(() => displaySuccess("Docker Compose file saved successfully."))
|
|
|
|
.catch(error => displayFail(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSelectedText() {
|
|
|
|
const selectedOption = composeFileSelection.selectedOptions[0].text;
|
|
|
|
return selectedOption ? selectedOption.text : '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the contents of the editor to the selected docker-compose file and restart the services
|
|
|
|
function saveAndRunDockerCompose() {
|
|
|
|
if (composeFileSelection.value === "") return;
|
|
|
|
saveDockerCompose();
|
|
|
|
const composeLocation = composeFileSelection.value + "/docker-compose.yaml";
|
|
|
|
const commandBringDown = `docker compose -f ${composeLocation} down`;
|
2024-10-30 16:53:48 -03:00
|
|
|
const commandBringUp = `docker compose -f ${composeLocation} up -d`;
|
2024-10-30 16:43:36 -03:00
|
|
|
const selection = composeFileSelection.selectedOptions[0].text;
|
|
|
|
|
|
|
|
// Bring down the services then bring them back up with the new configuration
|
|
|
|
displaySuccess(`Bringing down '${selection}' services...`);
|
|
|
|
cockpit.spawn(["bash", "-c", commandBringDown], { superuser: true, err: "message" })
|
|
|
|
.then(() => {
|
|
|
|
displaySuccess(`'${selection}' services brought down successfully.`);
|
|
|
|
displaySuccess(`Applying changes to '${selection}' services and bringing them back up...`);
|
|
|
|
cockpit.spawn(["bash", "-c", commandBringUp], { superuser: true, err: "message" })
|
|
|
|
.then(() => displaySuccess("Done."))
|
|
|
|
.catch(error => displayFail(error.message));
|
|
|
|
})
|
|
|
|
.catch(error => displayFail(error.message));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop the services in the selected docker-compose file
|
|
|
|
function stopDockerCompose() {
|
|
|
|
if (composeFileSelection.value === "") return;
|
|
|
|
const composeLocation = composeFileSelection.value + "/docker-compose.yaml";
|
|
|
|
const commandBringDown = `docker compose -f ${composeLocation} down`;
|
|
|
|
const selection = composeFileSelection.selectedOptions[0].text;
|
|
|
|
|
|
|
|
displaySuccess(`Bringing down '${selection}' services...`);
|
|
|
|
cockpit.spawn(["bash", "-c", commandBringDown], { superuser: true, err: "message" })
|
|
|
|
.then(() => displaySuccess(`'${selection}' services brought down successfully.`))
|
|
|
|
.catch(error => displayFail(error.message));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add options to a dropdown box
|
|
|
|
function addDropDown(box, pairs, defaultValue) {
|
|
|
|
try {
|
|
|
|
for(let i = 0; i < pairs.length; i++){
|
|
|
|
if (pairs[i].length == 0 || pairs[i][0] === "" || pairs[i][1] === "") continue;
|
|
|
|
const option = document.createElement("option");
|
|
|
|
option.value = pairs[i][0];
|
|
|
|
option.text = pairs[i][1];
|
|
|
|
box.add(option);
|
|
|
|
if (defaultValue === option.value) {
|
|
|
|
box.value = option.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(e) {
|
|
|
|
displayFail(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display Success result
|
|
|
|
function displaySuccess(msg) {
|
|
|
|
msg = `<span style='color: green;'>${msg}</span>`;
|
|
|
|
if (result.innerHTML === "")
|
|
|
|
result.innerHTML = msg;
|
|
|
|
else
|
|
|
|
result.innerHTML += "<br>" + msg;
|
|
|
|
|
|
|
|
setTimeout(() => result.innerHTML = "", 10000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display failure message
|
|
|
|
function displayFail(error) {
|
|
|
|
console.error(error);
|
|
|
|
error = `<span style='color: red;'>Error : ${error}</span>`;
|
|
|
|
|
|
|
|
if (result.innerHTML === "")
|
|
|
|
result.innerHTML = error;
|
|
|
|
else
|
|
|
|
result.innerHTML += "<br>" + error;
|
|
|
|
|
|
|
|
setTimeout(() => result.innerHTML = "", 10000);
|
|
|
|
}
|