diff --git a/bin/server.js b/bin/server.js index 050d409..629622c 100644 --- a/bin/server.js +++ b/bin/server.js @@ -4,6 +4,7 @@ const path = require("path") const { __glob } = require("./global-variables") const auth = require("./auth") const files = require("./files") +const service = require("./services") const plog = new LogType("Web") const cook = require("cookie") const http = require("http") @@ -44,6 +45,8 @@ module.exports.serverIO = function(server) { * POST REQUEST */ + if(user.checkPermission("FILES_EXPLORER")) { + PostRequest("FX_GET", (root) => { PostAnswer("FX_GET", files.getFiles(root)) @@ -90,6 +93,30 @@ module.exports.serverIO = function(server) { }) + } + + if(user.checkPermission("SERVICES")) { + + PostRequest("SV_GET_SERVICE_STATUS", async (sv) => { + PostAnswer("SV_GET_SERVICE_STATUS", {answer: await service.getServiceStatus(sv), name: sv}) + }) + + PostRequest("SV_START_SERVICE", async (sv) => { + PostAnswer("SV_START_SERVICE", {answer: await service.startService(sv), name: sv}) + }) + + PostRequest("SV_STOP_SERVICE", async (sv) => { + PostAnswer("SV_STOP_SERVICE", {answer: await service.stopService(sv), name: sv}) + }) + + PostRequest("SV_RESTART_SERVICE", async (sv) => { + PostAnswer("SV_RESTART_SERVICE", {answer: await service.restartService(sv), name: sv}) + }) + + + + } + socket.on("disconnect", () => { plog.log("Déconnexion au panel par '" + user.username + "' avec le socket : " + socket.id) diff --git a/bin/services.js b/bin/services.js new file mode 100644 index 0000000..ae02025 --- /dev/null +++ b/bin/services.js @@ -0,0 +1,136 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const clog = new LogType("Services") + +const http = require('http'); +const https = require('https'); + +module.exports.getServiceStatus = function(service) { + const protocol = service.startsWith('https') ? https : http; + const url = new URL(service); + const options = { + method: 'HEAD', + host: url.hostname, + port: url.port, + path: url.pathname, + }; + + return new Promise((resolve, reject) => { + const req = protocol.request(options, (res) => { + if(res.statusCode !== 502) { + resolve("ONLINE"); + } else { + + resolve("OFFLINE"); + } + + }); + + req.on('error', (err) => { + resolve("OFFLINE"); + }); + + req.end(); + }); +} + +module.exports.stopService = function(service) { + + + return new Promise((resolve, reject) => { + + const child_process = require('child_process'); + if(service == "https://subsonics.raphix.fr") { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 stop 'Subsonics'") + resolve("OK") + } else if(service == "https://git.raphix.fr" ) { + let req = child_process.exec("ssh raphix@omega.raphix.fr sudo -S systemctl stop gitea") + resolve("OK") + } else if(service == "https://jenkins.raphix.fr") { + let req = child_process.exec("ssh raphix@omega.raphix.fr sudo -S systemctl stop jenkins ") + resolve("OK") + + } else if(service == 'https://raphix.fr') { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 stop 'Website - Raphix'") + resolve("OK") + } else if(service == "https://cv.raphix.fr") { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 stop 'CV - Raphael'") + resolve("OK") + } else if(service == "http://omega.raphix.fr:2333") { + let req = child_process.exec(" ssh raphix@omega.raphix.fr sudo -S systemctl stop lavalink ") + resolve("OK") + } else { + resolve(false) + } + + }); + +} + +module.exports.startService = function(service) { + + + return new Promise((resolve, reject) => { + + const child_process = require('child_process'); + if(service == "https://subsonics.raphix.fr") { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 start /home/gitlab-ci/subsonic.config.js") + resolve("OK") + } else if(service == "https://git.raphix.fr" ) { + let req = child_process.exec("ssh raphix@omega.raphix.fr sudo -S systemctl start gitea") + resolve("OK") + } else if(service == "https://jenkins.raphix.fr") { + let req = child_process.exec("ssh raphix@omega.raphix.fr sudo -S systemctl start jenkins ") + resolve("OK") + + } else if(service == 'https://raphix.fr') { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 start /home/gitlab-ci/website.config.js") + resolve("OK") + } else if(service == "https://cv.raphix.fr") { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 start /home/gitlab-ci/cv.config.js") + resolve("OK") + } else if(service == "http://omega.raphix.fr:2333") { + let req = child_process.exec(" ssh raphix@omega.raphix.fr sudo -S systemctl start lavalink ") + resolve("OK") + } else { + resolve(false) + } + + }); + +} + +module.exports.restartService = function(service) { + + + return new Promise((resolve, reject) => { + + const child_process = require('child_process'); + if(service == "https://subsonics.raphix.fr") { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 restart 'Subsonics'") + resolve("OK") + } else if(service == "https://git.raphix.fr" ) { + let req = child_process.exec("ssh raphix@omega.raphix.fr sudo -S systemctl restart gitea") + resolve("OK") + } else if(service == "https://jenkins.raphix.fr") { + let req = child_process.exec("ssh raphix@omega.raphix.fr sudo -S systemctl restart jenkins ") + resolve("OK") + + } else if(service == 'https://raphix.fr') { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 restart 'Website - Raphix'") + resolve("OK") + } else if(service == "https://cv.raphix.fr") { + let req = child_process.exec("ssh raphix@alpha.raphix.fr sudo -S -u gitlab-ci pm2 restart 'CV - Raphael'") + resolve("OK") + } else if(service == "http://omega.raphix.fr:2333") { + let req = child_process.exec(" ssh raphix@omega.raphix.fr sudo -S systemctl restart lavalink ") + resolve("OK") + } else { + resolve(false) + } + + }); + +} \ No newline at end of file diff --git a/bin/users.js b/bin/users.js index b0c954f..f6d00a7 100644 --- a/bin/users.js +++ b/bin/users.js @@ -204,7 +204,6 @@ module.exports.User = class { this.#sync() if(this.permission.includes(name)) { return true - } else { return false diff --git a/public/images/services/cv.png b/public/images/services/cv.png new file mode 100644 index 0000000..0070038 Binary files /dev/null and b/public/images/services/cv.png differ diff --git a/public/images/services/gitea.svg b/public/images/services/gitea.svg new file mode 100644 index 0000000..ae433ce --- /dev/null +++ b/public/images/services/gitea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/services/jenkins.svg b/public/images/services/jenkins.svg new file mode 100644 index 0000000..988991c --- /dev/null +++ b/public/images/services/jenkins.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/services/lavalink.svg b/public/images/services/lavalink.svg new file mode 100644 index 0000000..63fa153 --- /dev/null +++ b/public/images/services/lavalink.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + diff --git a/public/images/services/raphix.png b/public/images/services/raphix.png new file mode 100644 index 0000000..ca99963 Binary files /dev/null and b/public/images/services/raphix.png differ diff --git a/public/images/services/subsonics.png b/public/images/services/subsonics.png new file mode 100644 index 0000000..20cf698 Binary files /dev/null and b/public/images/services/subsonics.png differ diff --git a/public/javascripts/basics.js b/public/javascripts/basics.js index dcdfe6a..b57320a 100644 --- a/public/javascripts/basics.js +++ b/public/javascripts/basics.js @@ -93,7 +93,7 @@ class ViewWindow {

${properties.title}

- +
@@ -182,10 +182,10 @@ class ViewWindow { createPopup(properties) { - this.ViewPopupHTML = `
+ this.ViewPopupHTML = `

${properties.title}

- +
${properties.content}
` @@ -243,17 +243,13 @@ class ViewWindow { function createView(viewType) { if(viewType == 'files_explorer') { - generateFileExplorer() + generateFileExplorerView() } if(viewType == 'service') { - const View = new ViewWindow({ - title: "Gestionnaire des services", - width: "1000px", - height: "600px" - }) + generateServiceView() } } diff --git a/public/javascripts/filexplorer.js b/public/javascripts/filexplorer.js index f0a7142..3da9ab2 100644 --- a/public/javascripts/filexplorer.js +++ b/public/javascripts/filexplorer.js @@ -3,7 +3,7 @@ document.addEventListener("contextmenu", (e) => { e.preventDefault() }) -function generateFileExplorer() { +function generateFileExplorerView() { const View = new ViewWindow({ title: ` Gestionnaire de fichiers`, @@ -68,7 +68,7 @@ function generateFileExplorer() { newFolder.addEventListener("click", () => { View.createPopup({ - title: ` Nouveau dossier`, + title: ` Nouveau dossier`, content: `
@@ -126,7 +126,7 @@ function generateFileExplorer() { newFile.addEventListener("click", () => { View.createPopup({ - title: ` Nouveau fichier`, + title: ` Nouveau fichier`, content: `
@@ -181,7 +181,7 @@ function generateFileExplorer() { fileInfo.clear() View.destroyPopup(` Nouveau fichier`) View.createPopup({ - title: ` Upload`, + title: ` Upload`, content: `
@@ -374,7 +374,7 @@ function generateFileExplorer() { loadFiles(result) }) View.createPopup({ - title: ` Erreur`, + title: ` Erreur`, content: `

Vous n'avez pas les permissions pour supprimer ce fichier.

` }) } else { @@ -383,7 +383,7 @@ function generateFileExplorer() { loadFiles(result) }) View.createPopup({ - title: ` Erreur`, + title: ` Erreur`, content: `

Une erreur est survenue.

` }) @@ -394,7 +394,7 @@ function generateFileExplorer() { dropMenu.hide() View.createPopup({ - title: ` Renommer`, + title: ` Renommer`, content: `
@@ -416,7 +416,7 @@ function generateFileExplorer() { return } - const regex = new RegExp(/^[a-zA-Z0-9-_]+$/) + const regex = new RegExp(/^[a-zA-Z0-9-_.]+$/) if(!regex.test(rename.value) | rename.value.replace(/\s/g, '').length < 1) { renameInfo.err("Le nom du fichier / dossier est invalide.") @@ -451,12 +451,12 @@ function generateFileExplorer() { reqFiles.then((result) => { if(result == "NOT_PERMITTED") { View.createPopup({ - title: ` Erreur`, + title: ` Erreur`, content: `

Vous n'avez pas les permissions pour partager ce fichier.

` }) } else { View.createPopup({ - title: ` Partager`, + title: ` Partager`, content: `
@@ -495,7 +495,7 @@ function generateFileExplorer() { reqFiles.then((result) => { if(result == "NOT_PERMITTED") { View.createPopup({ - title: ` Erreur`, + title: ` Erreur`, content: `

Vous n'avez pas les permissions pour télécharger ce fichier.

` }) } else { @@ -527,7 +527,7 @@ function generateFileExplorer() { reqFiles.then((result) => { if(result == "NOT_PERMITTED") { View.createPopup({ - title: ` Erreur`, + title: ` Erreur`, content: `

Vous n'avez pas les permissions pour éditer ce fichier.

` }) } else { @@ -564,13 +564,13 @@ function generateFileExplorer() { loadFiles(result) }) } else if(result == "NOT_PERMITTED") { - View.createPopup({ - title: ` Erreur`, + editor.createPopup({ + title: ` Erreur`, content: `

Vous n'avez pas les permissions pour éditer ce fichier.

` }) } else { - View.createPopup({ - title: ` Erreur`, + editor.createPopup({ + title: ` Erreur`, content: `

Une erreur est survenue.

` }) } diff --git a/public/javascripts/service.js b/public/javascripts/service.js new file mode 100644 index 0000000..5dbe1d8 --- /dev/null +++ b/public/javascripts/service.js @@ -0,0 +1,240 @@ +async function generateServiceView() { + + class Service { + name = null + description = null + icon = null + url = null + canAccess = false + isOnline = false + constructor(properties) { + this.name = properties.name + this.description = properties.description + this.icon = properties.icon + this.url = properties.url + this.canAccess = properties.canAccess + } + + generateHTML() { + return ` +
+
+ ${this.name} +
+

${this.name}

+

${this.description}

+

Etat :

+
+
+ +
+ ${this.canAccess ? `` : ""} + +
+
+ ` + + + } + + loadScript() { + + return new Promise((resolve, reject) => { + const statusSpan = getID(`${this.name}_status`) + const request = post(`SV_GET_SERVICE_STATUS`, this.url) + + + request.then((answer) => { + if(answer.name == this.url) { + if(answer.answer == "ONLINE") { + statusSpan.innerHTML = ' En ligne' + this.isOnline = true + } else { + statusSpan.innerHTML = ' Hors ligne' + + } + resolve("LOADED") + } + + }) + + const powerButton = getID(`${this.name}_svpower`) + + // Make a popup of View to select if you want to start, stop or restart the service by doing a request + + powerButton.addEventListener("click", () => { + View.createPopup({ + title: ` Gestion de l'alimentation du service`, + content: ` + +

${this.name}

+

+
+ + + +
+ ` + }) + + const startButton = getID(`${this.name}_start`) + const stopButton = getID(`${this.name}_stop`) + const restartButton = getID(`${this.name}_restart`) + const info = new InfoPop("sv-power-info") + + + + if(this.isOnline) { + startButton.style.display = "none" + info.info("Verifiez que le service n'est pas utilisé par quelqu'un d'autre avant de le redémarrer ou de l'arrêter") + } else { + stopButton.style.display = "none" + restartButton.style.display = "none" + info.info("Si le service ne démarre pas, vérifiez l'intégrité du service") + } + + startButton.addEventListener("click", () => { + const request = post(`SV_START_SERVICE`, this.url) + + + request.then((answer) => { + if(answer.answer == "OK") { + statusSpan.innerHTML = ' En ligne' + View.destroyPopup("` Gestion de l'alimentation du service`") + this.isOnline = true + } else { + info.err("Impossible de démarrer le service") + + } + }) + + }) + + stopButton.addEventListener("click", () => { + const request = post(`SV_STOP_SERVICE`, this.url) + + request.then((answer) => { + if(answer.answer == "OK") { + statusSpan.innerHTML = ' Hors ligne' + this.isOnline = false + View.destroyPopup("` Gestion de l'alimentation du service`") + + } else { + info.err("Impossible d'arrêter le service") + + } + + }) + }) + + restartButton.addEventListener("click", () => { + + console.log("RESTART") + + const request = post(`SV_RESTART_SERVICE`, this.url) + + request.then((answer) => { + if(answer.answer == "OK") { + statusSpan.innerHTML = ' En ligne' + View.destroyPopup("` Gestion de l'alimentation du service`") + this.isOnline = true + } else { + info.err("Impossible de redémarrer le service") + + } + + }) + }) + + + }) + + }) + } + + } + + + /** + * CODE OF SERVICE.JS + */ + + const allServices = new Array() + + const View = new ViewWindow({ + title: ' Gestion des services', + width: "700px", + height: "600px" + }) + + const subsonicsService = new Service({ + name: "Subsonics", + description: "Bot de streaming musical sur Discord", + icon: "/images/services/subsonics.png", + url: "https://subsonics.raphix.fr" , + canAccess: true + + + }) + + const giteaService = new Service({ + name: "Gitea", + description: "Gestionnaire de dépôt Git", + icon: "/images/services/gitea.svg", + url: "https://git.raphix.fr" , + canAccess: true + + }) + + const jenkinsService = new Service({ + name: "Jenkins", + description: "Gestionnaire de pipeline", + icon: "/images/services/jenkins.svg", + url: "https://jenkins.raphix.fr" , + canAccess: true + + }) + + const raphixwebsite = new Service({ + name: "Raphix.fr", + description: "Site web de Raphix", + icon: "/images/services/raphix.png", + url: "https://raphix.fr", + canAccess: true + + }) + + const cvraphix = new Service({ + name: "CV Raphix", + description: "Curriculum Vitae de Raphix", + icon: "/images/services/cv.png", + url: "https://cv.raphix.fr", + canAccess: true + }) + + const lavalink = new Service({ + name: "Lavalink", + description: "Serveur Lavalink pour Subsonics", + icon: "/images/services/lavalink.svg", + url: "http://omega.raphix.fr:2333", + canAccess: false + }) + + allServices.push(subsonicsService.generateHTML()) + allServices.push(lavalink.generateHTML()) + allServices.push(giteaService.generateHTML()) + allServices.push(jenkinsService.generateHTML()) + allServices.push(raphixwebsite.generateHTML()) + allServices.push(cvraphix.generateHTML()) + + View.setContent(`
${allServices.join("")}
`) + + await subsonicsService.loadScript() + await giteaService.loadScript() + await jenkinsService.loadScript() + await raphixwebsite.loadScript() + await cvraphix.loadScript() + await lavalink.loadScript() + +} + diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 81fd2c5..bb271e1 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -430,28 +430,7 @@ a { font-size: 72px; } -.view-popup { - position: absolute; - background-color: #3D3B3C; - backdrop-filter: blur(10px); - display: flex; - flex-direction: column; - gap: 20px; - align-items: center; - justify-content: center; - padding: 2%; - border-radius: 10px; -} -.view-popup-bar { - - display: flex; - justify-content: space-between; - flex-direction: row; - align-items: center; - gap: 20px; - width: 100%; -} .blur { @@ -494,6 +473,46 @@ a { } +.view-popup { + position: absolute; + background-color: #3D3B3C; + backdrop-filter: blur(10px); + display: flex; + flex-direction: column; + gap: 20px; + align-items: center; + justify-content: center; + padding: 2%; + border-radius: 10px; +} + +.view-popup-bar { + + display: flex; + justify-content: space-between; + flex-direction: row; + align-items: center; + gap: 20px; + width: 100%; +} + +.view-close { + color: white; + transition: 0.1s; + cursor: pointer; + margin-right: 10px; +} + +.view-close:hover { + color: #f01000; + +} + +.view-close:active { + + color: #ff5d51; +} + /* Files Explorer */ @@ -641,3 +660,66 @@ a { } + +/* Services */ + +.sv { + + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; + user-select: none; + -webkit-user-select: none; +} + +.sv-icon { + width: 150px; + border-radius: 15px; +} + +.sv-info { + + display: flex; + flex-direction: row; + gap: 20px; + font-size: 15px; + align-items: center; + +} + +.sv-list { + + padding: 10px; + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + height: 85%; + +} + +.sv-actions { + display: flex; + flex-direction: column; + gap: 10px; + align-items: flex-end; +} +.sv-power-select { + + background-color: #323031; + padding: 10px; + font-size: 14px; + border-radius: 10px ; +} + +.sv-power { + + display: flex; + flex-direction: column; + gap: 10px; +} \ No newline at end of file diff --git a/views/index.ejs b/views/index.ejs index 6ccb27b..3d84902 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -57,10 +57,9 @@ + - -