From ff42630c8d038b3d9779759afe6251029840d437 Mon Sep 17 00:00:00 2001 From: Raphix Date: Wed, 6 Dec 2023 22:56:58 +0100 Subject: [PATCH] Version 0.5.0 - Ajout des pipelines --- bin/pipelines.js | 138 +++++++++++++++++ bin/servers.js | 86 ++++++----- package.json | 2 +- public/javascripts/basics.js | 236 +++++++++++++++++++++++++++++- public/javascripts/indexscript.js | 9 +- public/javascripts/middle.js | 2 +- public/javascripts/pipeline.js | 66 +++++++++ public/javascripts/service.js | 19 ++- public/stylesheets/style.css | 106 ++++++++++++-- views/index.ejs | 21 +-- 10 files changed, 609 insertions(+), 76 deletions(-) create mode 100644 bin/pipelines.js create mode 100644 public/javascripts/pipeline.js diff --git a/bin/pipelines.js b/bin/pipelines.js new file mode 100644 index 0000000..bb4dda6 --- /dev/null +++ b/bin/pipelines.js @@ -0,0 +1,138 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const clog = new LogType("Pïpeline") +const config = require("./config") + +const tokenkey = config.getFile().JENKINS_TOKEN + +module.exports.getAllPipelines = function() { + + return new Promise((resolve, reject) => { + + + fetch(`https://jenkins.raphix.fr/api/json`, { + method: "GET", + headers: { + "Accept": "application/json", + "Authorization": `Basic ${tokenkey}` + }, + credentials: "include" + }) + .then(res => res.json()) + .then(async list => { + + const pipelinesJobs = new Array() + + for(const job of list.jobs) { + await fetch(`${job.url}/api/json`, { + method: "GET", + headers: { + "Accept": "application/json", + "Authorization": `Basic ${tokenkey}` + }, + credentials: "include" + }) + .then(res => res.json()) + .then(async res => { + + if(res._class == "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") { + await getJobMain(res).then(resJ => { + res.jobs[0] = resJ + pipelinesJobs.push(res) + }) + } else { + pipelinesJobs.push(res) + + } + + + }) + } + + list.jobs = pipelinesJobs + + resolve(list) + + + }) + .catch(err => { + resolve("UNAVAILABLE") + }) + }) + + +} + +module.exports.startPipeline = function(pipeline) { + + +// If it's a freestyle job, build with params + + if(pipeline.type == "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") { + return new Promise((resolve, reject) => { + fetch(`${pipeline.url}/job/${pipeline.jobname}/build?delay=0sec`, { + method: "POST", + headers: { + "Authorization": `Basic ${tokenkey}` + }, + credentials: "include" + }) + .then(res => { + resolve("OK") + }) + }) + } else { + return new Promise((resolve, reject) => { + + const parameters = pipeline.fields + + const formatedParams = new URLSearchParams() + + for(const param of parameters) { + formatedParams.append(param.name, param.value) + } + + + + + fetch(`${pipeline.url}/buildWithParameters?delay=0sec`, { + method: "POST", + headers: { + "Authorization": `Basic ${tokenkey}`, + "Content-Type": "application/x-www-form-urlencoded" + }, + credentials: "include", + body: formatedParams + }) + .then(res => { + resolve("OK") + }) + }) + } + + + +} + +function getJobMain(res) { + + return new Promise(async (resolve, reject) => { + + await fetch(`${res.jobs[0].url}/api/json`, { + method: "GET", + headers: { + 'Accept': 'application/json', + 'Authorization': `Basic ${tokenkey}` + }, + credentials: 'include' + }).then(res => res.json()) + .then(res => { + resolve(res) + }) + + + }) + +} diff --git a/bin/servers.js b/bin/servers.js index cf72414..1de066e 100644 --- a/bin/servers.js +++ b/bin/servers.js @@ -6,6 +6,7 @@ const auth = require("./auth.js") const files = require("./files.js") const links = require("./links.js") const service = require("./services.js") +const pipeline = require("./pipelines.js") const plog = new LogType("Web") const cook = require("cookie") const http = require("http") @@ -49,56 +50,56 @@ module.exports.serverIO = function(server) { if(user.checkPermission("FILES_EXPLORER")) { - PostRequest("FX_GET", (root) => { + PostRequest("FX_GET", (root) => { - PostAnswer("FX_GET", files.getFiles(root)) - }) + PostAnswer("FX_GET", files.getFiles(root)) + }) - PostRequest("FX_NEW_FOLDER", (root) => { + PostRequest("FX_NEW_FOLDER", (root) => { - PostAnswer("FX_NEW_FOLDER", files.createFolder(root)) - }) + PostAnswer("FX_NEW_FOLDER", files.createFolder(root)) + }) - PostRequest("FX_DELETE", (root) => { + PostRequest("FX_DELETE", (root) => { - PostAnswer("FX_DELETE", files.deleteFile(root)) - } ) + PostAnswer("FX_DELETE", files.deleteFile(root)) + } ) - PostRequest("FX_RENAME", (settings) => { - - PostAnswer("FX_RENAME", files.renameFile(settings)) - }) - - PostRequest("FX_SHARE", (settings) => { + PostRequest("FX_RENAME", (settings) => { - PostAnswer("FX_SHARE", files.shareFile(settings)) - }) + PostAnswer("FX_RENAME", files.renameFile(settings)) + }) - PostRequest("FX_GETFILE", (root) => { - - PostAnswer("FX_GETFILE", files.getFile(root)) - }) + PostRequest("FX_SHARE", (settings) => { + + PostAnswer("FX_SHARE", files.shareFile(settings)) + }) - PostRequest("FX_SAVEFILE", (settings) => { - - PostAnswer("FX_SAVEFILE", files.saveFile(settings)) - }) - - PostRequest("FX_NEW_FILE", (settings) => { + PostRequest("FX_GETFILE", (root) => { - PostAnswer("FX_NEW_FILE", files.newFile(settings)) - }) - - PostRequest("FX_UPLOAD", (settings) => { + PostAnswer("FX_GETFILE", files.getFile(root)) + }) - PostAnswer("FX_UPLOAD", files.uploadFile(settings)) + PostRequest("FX_SAVEFILE", (settings) => { + + PostAnswer("FX_SAVEFILE", files.saveFile(settings)) + }) - }) + PostRequest("FX_NEW_FILE", (settings) => { + + PostAnswer("FX_NEW_FILE", files.newFile(settings)) + }) + + PostRequest("FX_UPLOAD", (settings) => { - PostRequest("FX_PASTE", (settings) => { - - PostAnswer("FX_PASTE", files.pasteFile(settings)) - }) + PostAnswer("FX_UPLOAD", files.uploadFile(settings)) + + }) + + PostRequest("FX_PASTE", (settings) => { + + PostAnswer("FX_PASTE", files.pasteFile(settings)) + }) } @@ -151,6 +152,17 @@ module.exports.serverIO = function(server) { }); } + if(user.checkPermission("PIPELINES")) { + GetRequest("PL_GET_ALL", async () => { + GetAnswer("PL_GET_ALL", await pipeline.getAllPipelines()) + }) + + PostRequest("PL_START", async (settings) => { + PostAnswer("PL_START", await pipeline.startPipeline(settings)) + }) + + } + socket.on("disconnect", () => { plog.log("Déconnexion du panel par '" + user.username + "' avec le socket : " + socket.id) diff --git a/package.json b/package.json index 1a2d616..88fab00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "neutral", - "version": "0.4.0", + "version": "0.5.0", "description": "Panel d'administration de Raphix", "main": "index.js", "scripts": { diff --git a/public/javascripts/basics.js b/public/javascripts/basics.js index 4c9b221..8164812 100644 --- a/public/javascripts/basics.js +++ b/public/javascripts/basics.js @@ -201,6 +201,7 @@ class ViewWindow { let offsetX, offsetY; windowDiv.style.zIndex = zIndex + 1 + header.addEventListener('mousedown', (e) => { isDragging = true; @@ -244,6 +245,8 @@ class ViewWindow { this.ViewItem.show() + + } destroy() { @@ -577,12 +580,14 @@ class Service { url = null canAccess = false isOnline = false + View = null constructor(properties) { this.name = properties.name this.description = properties.description this.icon = properties.icon this.url = properties.url this.canAccess = properties.canAccess + this.View = properties.View } generateHTML() { @@ -633,7 +638,7 @@ class Service { // 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({ + this.View.createPopup({ title: ` Gestion de l'alimentation du service`, content: ` @@ -650,7 +655,7 @@ class Service { 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") + const info = new TextResponse("sv-power-info") @@ -670,7 +675,7 @@ class Service { request.then((answer) => { if(answer.answer == "OK") { statusSpan.innerHTML = ' En ligne' - View.destroyPopup("` Gestion de l'alimentation du service`") + this.View.destroyPopup("` Gestion de l'alimentation du service`") this.isOnline = true } else { info.err("Impossible de démarrer le service") @@ -687,7 +692,7 @@ class Service { if(answer.answer == "OK") { statusSpan.innerHTML = ' Hors ligne' this.isOnline = false - View.destroyPopup("` Gestion de l'alimentation du service`") + this.View.destroyPopup("` Gestion de l'alimentation du service`") } else { info.err("Impossible d'arrêter le service") @@ -706,7 +711,7 @@ class Service { request.then((answer) => { if(answer.answer == "OK") { statusSpan.innerHTML = ' En ligne' - View.destroyPopup("` Gestion de l'alimentation du service`") + this.View.destroyPopup("` Gestion de l'alimentation du service`") this.isOnline = true } else { info.err("Impossible de redémarrer le service") @@ -724,6 +729,227 @@ class Service { } +/** + * Permet de créer un composant de vue de type "Pipeline" + * @param {object} properties Propriétés du composant de vue + */ + +class Pipeline { + name; + pipe; + class; + url; + View; + constructor(properties) { + this.name = properties.pipeline.name + this.class = properties.pipeline._class + this.url = properties.pipeline.url + this.pipe = properties.pipeline + this.View = properties.View + + if(!this.pipe.description) { + this.pipe.description = "Aucune description" + } + } + + generateHTML() { + var image = null + var classInfo = null + var lastBuildStatus = "" + + if(this.class == "hudson.model.FreeStyleProject") { + image = `` + + if(this.pipe.lastBuild) { + lastBuildStatus = `

Construction en cours ... - N°${this.pipe.nextBuildNumber - 1}

` + if(this.pipe.lastSuccessfulBuild) { + if(this.pipe.lastSuccessfulBuild.number == this.pipe.nextBuildNumber - 1) { + lastBuildStatus = `

Dernière construction réussie - N°${this.pipe.nextBuildNumber - 1}

` + } + + } + if(this.pipe.lastFailedBuild) { + if(this.pipe.lastFailedBuild.number == this.pipe.nextBuildNumber - 1) { + lastBuildStatus = `

Dernière construction échouée - N°${this.pipe.nextBuildNumber - 1}

` + } + + } + + + } else { + lastBuildStatus = `

Aucune construction

` + } + + + // TRUE : lastBuildStatus = `

Dernière construction réussie - N°${this.pipe.jobs[0].lastSuccessfulBuild.number}

` + // BUILDING : lastBuildStatus = `

Construction en cours ... - N°${this.pipe.nextBuildNumber - 1}

` + // FAILED : lastBuildStatus = `

Dernière construction échouée - N°${this.pipe.jobs[0].nextBuildNumber - 1}

` + // NOITHING : lastBuildStatus = `

Aucune construction

` + } else { + + image = `` + if(this.pipe.jobs[0].lastBuild) { + lastBuildStatus = `

Construction en cours ... - N°${this.pipe.jobs[0].nextBuildNumber - 1}

` + if(this.pipe.jobs[0].lastSuccessfulBuild) { + if(this.pipe.jobs[0].lastSuccessfulBuild.number == this.pipe.jobs[0].nextBuildNumber - 1) { + lastBuildStatus = `

Dernière construction réussie - N°${this.pipe.jobs[0].nextBuildNumber - 1}

` + } + + } + if(this.pipe.jobs[0].lastFailedBuild) { + if(this.pipe.jobs[0].lastFailedBuild.number == this.pipe.jobs[0].nextBuildNumber - 1) { + lastBuildStatus = `

Dernière construction échouée - N°${this.pipe.jobs[0].nextBuildNumber - 1}

` + } + + } + + + } else { + lastBuildStatus = `

Aucune construction

` + } + + + + } + + return ` +
+
+ ${image} +
+

${this.name}

+

${this.pipe.description}

+ ${lastBuildStatus} +
+
+
+ + +
+
+ ` + } + + + loadScript() { + + const powerButton = getID(`${this.name}_pipelinepower`) + + powerButton.addEventListener("click", () => { + if(this.class == "hudson.model.FreeStyleProject" && this.pipe.property[0].parameterDefinitions.length > 0) { + + var allProperties = new Array() + + for(const property of this.pipe.property[0].parameterDefinitions) { + console.log(property) + if(property.type == "StringParameterDefinition") { + allProperties.push("

" + property.name + "

") + } + if(property.type == "ChoiceParameterDefinition") { + + var options = new Array() + for(const option of property.choices) { + options.push(``) + } + + allProperties.push("

" + property.name + "

") + } + } + + + + this.View.createPopup({ + title: ` Démarrer la pipeline`, + content: ` + +

${this.name}

+
+ ${allProperties.join("")} +
+

+ + ` + }) + + const startButton = getID(`${this.name}_start`) + const info = new TextResponse("pl-power-info") + + startButton.addEventListener("click", () => { + info.clear() + + + var allFields = new Array() + + for(const property of this.pipe.property[0].parameterDefinitions) { + + + if(getID(property.name).value == "") { + info.err("Veuillez remplir tous les champs") + return + } + allFields.push({name: property.name,value: getID(property.name).value} ) + + } + + const request = post(`PL_START`, {name: this.name, fields: allFields, url: this.url, type: this.class}) + + request.then((answer) => { + + if(answer == "OK") { + info.info("La pipeline a été démarrée avec succès") + this.View.destroyPopup() + + } else { + info.err("Impossible de démarrer la pipeline") + + } + }) + + }) + + + + } else { + + // Open a confirmation popup to start the pipeline + + this.View.createPopup({ + title: ` Démarrer la pipeline`, + content: ` + +

${this.name}

+

+ + ` + }) + + const startButton = getID(`${this.name}_start`) + const info = new TextResponse("pl-power-info") + + startButton.addEventListener("click", () => { + info.clear() + const request = post(`PL_START`, {name: this.name, url: this.url, type: this.class, jobname: this.pipe.jobs[0].name}) + + request.then((answer) => { + console.log(answer) + if(answer == "OK") { + info.info("La pipeline a été démarrée avec succès") + this.View.destroyPopup() + + } else { + info.err("Impossible de démarrer la pipeline") + + } + }) + + }) + + } + }) + } + +} + /** * Permet de créer un item de la barre des tâches diff --git a/public/javascripts/indexscript.js b/public/javascripts/indexscript.js index 5441228..733680c 100644 --- a/public/javascripts/indexscript.js +++ b/public/javascripts/indexscript.js @@ -2,7 +2,7 @@ const infoUsername = getID("infoUsername") const infoUserimage = getID("infoUserimage") const infoDisplayname = getID("infoDisplayname") const views = getID("views") - +const panelBox = getID("panel-box") // User Request @@ -32,8 +32,10 @@ REQ_user.then((ANS_user) => { if(AvailableViews.join("") == "") { - AvailableViews.push("

Aucune permission ne semble vous êtes accordée
Demandez à Raphix afin de résoudre ce problème

") + AvailableViews.push("

Aucune permission ne semble vous êtes accordée
Demandez à Raphix afin de résoudre ce problème

") views.classList.remove("views-box") + panelBox.style.justifyContent = "unset" + console.log(panelBox.style) } else { views.classList.add("views-box") @@ -44,10 +46,9 @@ REQ_user.then((ANS_user) => { // BindView AllComponents.forEach((component) => { - component.bindView() + component.bindView() }) - servers.forceWindow() }) diff --git a/public/javascripts/middle.js b/public/javascripts/middle.js index 78f40a4..bffa2fc 100644 --- a/public/javascripts/middle.js +++ b/public/javascripts/middle.js @@ -40,7 +40,7 @@ const explorer = new ViewComponent({ }) const links = new ViewComponent({ - name: "Gestion des liens", + name: "Générateur des liens", icon: "fa-solid fa-link", permission: "LINKS" }) diff --git a/public/javascripts/pipeline.js b/public/javascripts/pipeline.js new file mode 100644 index 0000000..3260870 --- /dev/null +++ b/public/javascripts/pipeline.js @@ -0,0 +1,66 @@ +pipelines.createWindow(() => { + + const View = new ViewWindow({ + title: ` Gestion des pipelines`, + width: "900px", + height: "600px" + }) + + const pipelinesList = new Array() + + View.setContent(`
+

Chargement en cours ...

+
`) + + var AllPipelines = new Array() + + function getPipelines() { + + const pipes = get("PL_GET_ALL") + pipes.then((ANS_pipes) => { + + AllPipelines.length = 0 + pipelinesList.length = 0 + + if(ANS_pipes != "UNAVAILABLE") { + ANS_pipes.jobs.forEach((pipe) => { + console.log(pipe) + const pipeline = new Pipeline({ + pipeline: pipe, + View: View + }) + + pipelinesList.push(pipeline.generateHTML()) + AllPipelines.push(pipeline) + }) + + View.setContent(` +
+
Recharger les pipelines
+ ${pipelinesList.join("")} +
`) + + for(const pipeline of AllPipelines) { + pipeline.loadScript() + } + + getID("reload_Btn_pipeline").addEventListener("click", () => { + View.setContent(`
+

Chargement en cours ...

+
`) + getPipelines() + }) + + + } else { + View.setContent(`
+

Une erreur est survenue lors du chargement des pipelines

+
`) + } + + }) + } + + getPipelines() + +}) \ No newline at end of file diff --git a/public/javascripts/service.js b/public/javascripts/service.js index 3b5acf0..8ed1887 100644 --- a/public/javascripts/service.js +++ b/public/javascripts/service.js @@ -15,8 +15,8 @@ services.createWindow(async () => { description: "Bot de streaming musical sur Discord", icon: "/images/services/subsonics.png", url: "https://subsonics.raphix.fr" , - canAccess: true - + canAccess: true, + View: View }) @@ -25,7 +25,8 @@ services.createWindow(async () => { description: "Gestionnaire de dépôt Git", icon: "/images/services/gitea.svg", url: "https://git.raphix.fr" , - canAccess: true + canAccess: true, + View: View }) @@ -34,7 +35,8 @@ services.createWindow(async () => { description: "Gestionnaire de pipeline", icon: "/images/services/jenkins.svg", url: "https://jenkins.raphix.fr" , - canAccess: true + canAccess: true, + View: View }) @@ -43,7 +45,8 @@ services.createWindow(async () => { description: "Site web de Raphix", icon: "/images/services/raphix.png", url: "https://raphix.fr", - canAccess: true + canAccess: true, + View: View }) @@ -52,7 +55,8 @@ services.createWindow(async () => { description: "Curriculum Vitae de Raphix", icon: "/images/services/cv.png", url: "https://cv.raphix.fr", - canAccess: true + canAccess: true, + View: View }) const lavalink = new Service({ @@ -60,7 +64,8 @@ services.createWindow(async () => { description: "Serveur Lavalink pour Subsonics", icon: "/images/services/lavalink.svg", url: "http://omega.raphix.fr:2333", - canAccess: false + canAccess: false, + View: View }) allServices.push(subsonicsService.generateHTML()) diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index c7746a5..f95bf62 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -291,7 +291,7 @@ a { backdrop-filter: blur(10px); } -.subpanel-box { +.taskbar-box { width: 100%; position: absolute; @@ -303,31 +303,31 @@ a { } -.subpanel-content { +.taskbar-content { display: flex; justify-content: space-between; padding: 20px; } -.subpanel-image { +.taskbar-image { width: 50px; height: 50px; border-radius: 100%; } -.subpanel-logo { +.taskbar-logo { width: 50px; transition: 0.3s; } -.subpanel-logo:hover { +.taskbar-logo:hover { transform: scale(0.9); } -.subpanel-user { +.taskbar-user { display: flex; flex-direction: row; @@ -335,14 +335,14 @@ a { align-items: center; } -.subpanel-userinfo { +.taskbar-userinfo { text-align: end; display: flex; flex-direction: column; gap: 5px; } -.subpanel-actions { +.taskbar-actions { display: flex; flex-direction: row; @@ -351,7 +351,7 @@ a { } -.subpanel-username { +.taskbar-username { font-size: 14px; color: rgba(255, 255, 255, 0.68); @@ -419,7 +419,7 @@ a { } -.subpanel-dispname { +.taskbar-dispname { margin-block: 0 !important; } @@ -966,4 +966,88 @@ a { align-items: center; gap: 10px; -} \ No newline at end of file +} + +/* PIPELINES */ + +.pipelines { + + padding: 10px; + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + height: 85%; + +} + +.pipeline { + + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; + + +} + +.pipeline-info { + + display: flex; + gap: 10px; + align-items: center; + font-size: 50px; + +} + +.pipeline-title { + + font-size: 20px; +} + +.pipeline-text { + + display: flex; + flex-direction: column; + font-size: 14px; +} + +.pipeline-actions { + + display: flex; + flex-direction: column; + gap: 10px; + align-items: flex-end; + justify-content: center; +} + +.pipeline-options { + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; +} + + +.pl-reload { + display: flex; + align-items: center; + font-size: 17px; + transition: 0.1s; + gap: 10px; + padding: 5px; + border-radius: 10px; +} + + + +.pl-reload:hover { + + background-color: rgba(44, 40, 42, 0.614); + +} diff --git a/views/index.ejs b/views/index.ejs index b7e13ca..2709329 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -5,16 +5,16 @@ -
+

Aucune permission ne semble vous êtes accordée
Demandez à Raphix afin de résoudre ce problème

-
-
-
- +
+
+
+
-
-
-

DisplayName

-

Username

+
+
+

DisplayName

+

Username

- +
@@ -43,6 +43,7 @@ +