commit 1219e6feccf68f24da70a0b4fe212e38ec1eb8a9 Author: Raphix Date: Sun Dec 29 15:38:16 2024 +0100 Version 1.1.0 - Revert to app diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65654f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,137 @@ +# ---> Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +#data +data +data/* + +private \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..1832095 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,19 @@ +pipeline { + agent any + + stages { + stage('[Neutral] - Déploiement') { + steps { + script { + + echo "[Neutral-Deploy] - Deploy Stage" + sh "ssh raphix@raphix.fr sudo apt update -y" + sh "ssh raphix@raphix.fr sudo apt upgrade -y" + sh "ssh raphix@raphix.fr sudo -S -u gitlab-ci /home/gitlab-ci/neutral_deploy.sh" + + } + + } + } + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8706731 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 infrastructure + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ac835e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# neutral + +Panel d'administration de Raphix diff --git a/bin/auth.js b/bin/auth.js new file mode 100644 index 0000000..171e4c7 --- /dev/null +++ b/bin/auth.js @@ -0,0 +1,113 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const alog = new LogType("Authentification") +const keygen = require("./keygen") +const users = require("./users") + +/** + * Vérifie si le token est présent et appartient à un utilisateur + * @param {string} token + */ + +module.exports.check = function(token) { + var isApproved = false; + var username = null + users.fetchUsers().forEach((fetchUser) => { + if(fetchUser.tokens.includes(token)) { + isApproved = true + username = fetchUser.username + } + + }) + + if(isApproved) { + return true + } else { + if(token) { + + alog.warn("Erreur d'authentification - Token n'existe pas : " + token) + } + return false + } +} + +/** + * Permet de se connecter à Neutral + * @param {object} data + * @returns Token or AUTH_FAILED + */ +module.exports.login = function(data) { + var username = data.username + var password = data.password + + if(users.fetchUsers().has(username)) { + const user = users.fetchUsers().get(username) + if(password == user.getPassword()) { + const token = user.generateToken() + alog.log("Connexion approuvé de l'utilisateur : " + username) + return token + } else { + alog.warn("Echec de connexion de l'utilisateur : " + username + " - Mot de passe incorrect") + return "AUTH_FAILED" + } + + } else { + alog.warn("Echec de connexion de l'utilisateur : " + username + " - Utilisateur non-inscrit dans la base de donnée") + return "AUTH_FAILED" + } +} + +/** + * Remove the token + * @param {string} token + * @returns + */ +module.exports.signout = function(token) { + var isDone = false; + var username = null + users.fetchUsers().forEach((fetchUser) => { + if(fetchUser.tokens.includes(token)) { + isDone = true + username = fetchUser.username + fetchUser.removeToken(token) + } + + }) + + if(isDone) { + alog.log("Suppression du Token '" + token + "' de l'utilisateur : " + username) + return true + } else { + if(token) { + + alog.warn("Erreur d'opération lors de la déconnexion - Token n'existe pas : " + token) + } + return false + } + +} + +module.exports.getUserByToken = function(token) { + var isApproved = false; + var userGetted = null + users.fetchUsers().forEach((fetchUser) => { + if(fetchUser.tokens.includes(token)) { + userGetted = fetchUser + } + + }) + + if(userGetted) { + return userGetted + + } else { + if(token) { + + alog.warn("Erreur d'authentification - Token n'existe pas : " + token) + } + return false + } + +} \ No newline at end of file diff --git a/bin/config.js b/bin/config.js new file mode 100644 index 0000000..ef9e047 --- /dev/null +++ b/bin/config.js @@ -0,0 +1,48 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const clog = new LogType("Configuration") + +setup() + +function setup() { + if(!fs.existsSync(__glob.CONFIG)) { + clog.log("Création du fichier de configuration dans : " + __glob.CONFIG) + fs.writeFileSync(__glob.CONFIG, JSON.stringify({ + ENCRYPTION_KEY: "1", + }, null, 2)) + } +} + +/** + * + * @returns Config File + */ +module.exports.getFile = function () { + const file = JSON.parse(fs.readFileSync(__glob.CONFIG)) + return file +} + +/** + * Update le fichier configuration avec un object + * @param {Array} file + */ +module.exports.updateFile = function (file) { + if(fs.existsSync(__glob.CONFIG)) { + clog.log("Mise à jour du fichier configuration dans : " + __glob.CONFIG) + fs.writeFileSync(__glob.CONFIG, JSON.stringify(file, null, 2)) + } +} + +module.exports.getSettings = function () { + const file = this.getFile() + return {"jenkins_token": file.JENKINS_TOKEN, "omega_token": file.OMEGA_KEY} +} + +module.exports.saveSettings = function (settings) { + const file = this.getFile() + file.JENKINS_TOKEN = settings.jenkins_token + file.OMEGA_KEY = settings.omega_token + this.updateFile(file) +} diff --git a/bin/files.js b/bin/files.js new file mode 100644 index 0000000..2cea76b --- /dev/null +++ b/bin/files.js @@ -0,0 +1,268 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const clog = new LogType("Fichier") +const os = require("os"); +const uuid = require('uuid'); +var mime = require('mime-types'); + + +// check if shared folder exist, if not create it +if(!fs.existsSync(__glob.SHARED)) { + fs.mkdirSync(__glob.SHARED) +} + +module.exports.getFiles = function(root) { + + var response = new Object() + response.content = new Array() + + + try{ + + if(root == "homepath") { + + root = os.homedir() + + } + + if(root == "sharepath") { + + root = __glob.SHARED + + } + + if(root == "logpath") { + + root = __glob.LOGS + + } + + + if(!fs.existsSync(root)) { + response.content = "NOT_EXIST" + + } else { + + for(var file of fs.readdirSync(root)) { + + const stat = fs.statSync(root + path.sep + file) + + response.content.push({"name":file, id: uuid.v4().toString() ,"fileDirectory" : root + path.sep + file , "type":mime.lookup(file), "size":stat.size, "lastedition":stat.mtimeMs, "directory":stat.isDirectory()}) + } + + } + + + + + + }catch(err) { + + response.content = "NOT_PERMITTED" + } + + response.root = root + response.parent = path.dirname(root) + + + return response + +} + +module.exports.createFolder = function(root) { + + // Check if folder already exist + + if(!fs.existsSync(root)) { + + try { + fs.mkdirSync(root) + return "OK" + } catch(err) { + + return "NOT_PERMITTED" + } + + } else { + + return 'EXIST' + } + +} + +module.exports.deleteFile = function(root) { + + if(fs.existsSync(root)) { + + try { + + fs.rmSync(root, { recursive: true, force: true }) + return "OK" + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } else { + + return "NOT_EXIST" + } +} + +module.exports.renameFile = function(settings) { + + if(fs.existsSync(settings.root)) { + + try { + + fs.renameSync(settings.root + path.sep + settings.oldName, settings.root + path.sep + settings.newName) + return "OK" + } catch(err) { + + return "NOT_PERMITTED" + } + } else { + + return "NOT_EXIST" + } +} + +module.exports.shareFile = function(settings) { + + + if(fs.existsSync(settings.root)) { + + try { + //Create a sybolic link to shared folder + + fs.symlinkSync(settings.root + path.sep + settings.name, __glob.SHARED + path.sep + settings.name) + + // return the link of the file shared + if(process.env.DEV) { + return "http://localhost:3001/shared/" + settings.name + + } else { + return "https://neutral.raphix.fr/shared/" + settings.name + + } + + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } else { + + return "NOT_EXIST" + } +} + + +module.exports.getFile = function(root) { + + if(fs.existsSync(root)) { + + try { + + // Check if the file is an image and if it is return the base64 + if(mime.lookup(root).includes("image")) { + return "data:" + mime.lookup(root) + ";base64," + fs.readFileSync(root, "base64") + } else { + return fs.readFileSync(root, "utf-8") + } + + + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } else { + + return "NOT_EXIST" + } + +} + +module.exports.saveFile = function(settings) { + + try { + + fs.writeFileSync(settings.root + path.sep + settings.name, settings.content) + return "OK" + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } +} + +module.exports.downloadFile = function(root) { + + if(fs.existsSync(root)) { + + try { + + return fs.readFileSync(root) + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } else { + + return "NOT_EXIST" + } +} + + + +module.exports.uploadFile = function(settings) { + + try { + + fs.writeFileSync(settings.root + path.sep + settings.name, settings.file) + return "OK" + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } +} + +module.exports.newFile = function(settings) { + + if(!fs.existsSync(settings)) { + + try { + + fs.writeFileSync(settings, "") + return "OK" + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } else { + + return "EXIST" + } + } + +module.exports.pasteFile = function(settings) { + if(settings.action == "cut") { + + try { + + fs.renameSync(settings.file.fileDirectory, settings.newPath + path.sep + settings.name) + return "OK" + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } else if(settings.action == "copy") { + + try { + fs.copyFileSync(settings.file.fileDirectory, settings.newPath + path.sep + settings.name) + return "OK" + } catch(err) { + console.log(err) + return "NOT_PERMITTED" + } + } +} \ No newline at end of file diff --git a/bin/global-variables.js b/bin/global-variables.js new file mode 100644 index 0000000..d563837 --- /dev/null +++ b/bin/global-variables.js @@ -0,0 +1,20 @@ +const path = require("path"); +const root = path.resolve(__dirname, '../') + +const __glob = { + ROUTES: root + path.sep + "routes" + path.sep, + ROOT: root, + LOGS: root + path.sep + "logs", + ICON: root + path.sep + "public" + path.sep + 'images' + path.sep + "FormatLogo_WHITE.ico", + DATA: root + path.sep + "data", + USERS: root + path.sep + "data" + path.sep + "users.json", + CONFIG: root + path.sep + "data" + path.sep + "config.json", + SHARED: root + path.sep + "data" + path.sep + "shared", + USERS_IMAGES: root + path.sep + "data" + path.sep + "user_images", + PACKAGE_JSON: root + path.sep + "package.json", +}; + + + +module.exports = { __glob }; + diff --git a/bin/keygen.js b/bin/keygen.js new file mode 100644 index 0000000..9f7a0a0 --- /dev/null +++ b/bin/keygen.js @@ -0,0 +1,29 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +var CryptoJS = require("crypto-js") +const { __glob } = require("./global-variables") +const clog = new LogType("KeyGen") +const config = require("./config") + +const keypass = config.getFile().ENCRYPTION_KEY + +setup() + +function setup() { + if(keypass) { + clog.log("Clé de chiffrement trouvé et importé") + } else { + clog.error("Clé de chiffrement inconnu : Passage en mode par défaut") + } +} + +module.exports.encrypt = function (text) { + let encryptedText = CryptoJS.AES.encrypt(text, keypass).toString(); + return encryptedText; + } + +module.exports.decrypt = function(text) { + let decryptedText = CryptoJS.AES.decrypt(text, keypass).toString(CryptoJS.enc.Utf8); + return decryptedText; + } \ No newline at end of file diff --git a/bin/links.js b/bin/links.js new file mode 100644 index 0000000..5850281 --- /dev/null +++ b/bin/links.js @@ -0,0 +1,106 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const ulog = new LogType("Links") +const uuid = require("uuid") +const config = require("./config") +const {ApplyLinks} = require("../routes/link") + + + +if(!fs.existsSync(__glob.DATA + path.sep + "links.json")) { + fs.writeFileSync(__glob.DATA + path.sep + "links.json", JSON.stringify([], null, 2)) +} + +module.exports.getLinks = function() { + return JSON.parse(fs.readFileSync(__glob.DATA + path.sep + "links.json")) +} + +const FirstLinkManager = new ApplyLinks(this.getLinks()) + + +module.exports.addLink = function(settings) { + + var canDo = true + const links = this.getLinks() + var id = makeid(8) + + if(settings.abstractLink) { + settings.dest = id.toString() + } + + + + // Check if a destination already exists between links and if it's the case, we return an error "ALREADY_EXiST" + links.forEach((link) => { + if(link.dest == settings.dest) { + + canDo = false + } + + }) + + const link = { + id: id, + title: settings.title, + url: settings.url, + dest: settings.dest, + } + + if(canDo) { + + links.push(link) + fs.writeFileSync(__glob.DATA + path.sep + "links.json", JSON.stringify(links, null, 2)) + const LinkManager = new ApplyLinks(this.getLinks()) + return "OK" + + } else { + + return "ALREADY_EXIST" + } + + +} + +module.exports.removeLink = function(id) { + const links = this.getLinks() + const newLinks = [] + links.forEach((link) => { + if(link.id != id) { + newLinks.push(link) + } + }) + fs.writeFileSync(__glob.DATA + path.sep + "links.json", JSON.stringify(newLinks, null, 2)) + const LinkManager = new ApplyLinks(this.getLinks()) + return "OK" +} + +module.exports.updateLink = function(id, settings) { + const links = this.getLinks() + const newLinks = [] + links.forEach((link) => { + if(link.id == id) { + link.title = settings.title + link.url = settings.url + } + newLinks.push(link) + }) + fs.writeFileSync(__glob.DATA + path.sep + "links.json", JSON.stringify(newLinks, null, 2)) + const LinkManager = new ApplyLinks(this.getLinks()) + return "OK" +} + + + +function makeid(length) { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + let counter = 0; + while (counter < length) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + counter += 1; + } + return result; +} diff --git a/bin/metrics.js b/bin/metrics.js new file mode 100644 index 0000000..a492baf --- /dev/null +++ b/bin/metrics.js @@ -0,0 +1,128 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +var CryptoJS = require("crypto-js") +const { __glob } = require("./global-variables") +const { captureRejectionSymbol } = require("events") +const clog = new LogType("Metrics") + + +if(!fs.existsSync(__glob.DATA + path.sep + "metrics.json")) { + fs.writeFileSync(__glob.DATA + path.sep + "metrics.json", JSON.stringify([], null, 2)) +} + +module.exports.getDataMetrics = function() { + return JSON.parse(fs.readFileSync(__glob.DATA + path.sep + "metrics.json")) +} + +module.exports.getMetrics = function() { + + + + const metrics = this.getDataMetrics() + var metricsToReturn = new Array() + + return new Promise(async (resolve, reject) => { + + // Count the number processed + + var processed = 0 + + if(metrics.length == 0) { + resolve(metricsToReturn) + } + + await metrics.forEach(async (metric) => { + + // Try to connect to the metric server with the key in query params named "privatekey" + // If the connection is successful, we add the metric to the list of metrics to return + + const url = `http://${metric.address}:${metric.port}/metrics?privatekey=${metric.key}` + const fet = await fetch(url, { + method: "GET", + headers: { + "Accept": "application/json", + }, + credentials: "include" + }).then(res => res.json()) + .then(res => { + + if(res) { + metric.data = res + metricsToReturn.push(metric) + processed++ + } else { + metric.data = "ERROR" + metricsToReturn.push(metric) + } + + if(processed == metrics.length) { + resolve(metricsToReturn) + } + + }).catch((err) => { + metric.data = "ERROR" + metricsToReturn.push(metric) + processed++ + if(processed == metrics.length) { + resolve(metricsToReturn) + } + }) + + + + }) + + + + + + + + }) + +} + +module.exports.addMetric = function(settings) { + + const metrics = this.getDataMetrics() + + const metric = { + id: makeid(8), + name: settings.name, + address: settings.address, + port: settings.port, + key: settings.key, + } + + metrics.push(metric) + fs.writeFileSync(__glob.DATA + path.sep + "metrics.json", JSON.stringify(metrics, null, 2)) + return "OK" +} + +module.exports.deleteMetric = function(id) { + + const metrics = this.getDataMetrics() + + metrics.forEach((metric) => { + if(metric.id == id) { + metrics.splice(metrics.indexOf(metric), 1) + } + }) + + fs.writeFileSync(__glob.DATA + path.sep + "metrics.json", JSON.stringify(metrics, null, 2)) + return "OK" +} + + + +function makeid(length) { + var result = []; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; + var charactersLength = characters.length; + for ( var i = 0; i < length; i++ ) { + result.push(characters.charAt(Math.floor(Math.random() * + charactersLength))); + } + return result.join(''); +} \ No newline at end of file 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/server-metrics.js b/bin/server-metrics.js new file mode 100644 index 0000000..5ac68b9 --- /dev/null +++ b/bin/server-metrics.js @@ -0,0 +1,74 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const clog = new LogType("Serveur Metrics") +const osutils = require("os-utils") +const os = require("os") +const { statfs } = require("fs") +const config = require("./config") + + + +module.exports.getMetrics = async function(server) { + + return new Promise((resolve, reject) => { + if(server == "Alpha") { + var resp = "NULL" + const space = statfs("/", (err, stats) => { + if (err) { + throw err + } + + + + osutils.cpuUsage(function(cpuUsage) { + resp = { + cpu: Math.round(cpuUsage * 1000), + usedram: osutils.totalmem() - osutils.freemem(), + totalram: osutils.totalmem(), + usedisk: stats.blocks - stats.bfree, + totaldisk: stats.blocks, + name: server + } + + resolve(resp) + + + }) + + }) + + + } else if(server == "Omega") { + + const key = config.getFile().OMEGA_KEY; + + fetch("http://omega.raphix.fr:4000/metrics?key="+key, { + method: "GET", + headers: { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "Accept-Encoding": "gzip, deflate", + "Accept-Language": "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7", + "Cache-Control": "no-cache", + "Connection": "keep-alive" + } + }) + .then(response => response.json()) + .then(raw => { + raw.name = server; + resolve(raw); + }) + .catch(error => { + + reject(error); + }); + + + + } + + }) + + +} \ No newline at end of file diff --git a/bin/servers.js b/bin/servers.js new file mode 100644 index 0000000..54c0609 --- /dev/null +++ b/bin/servers.js @@ -0,0 +1,296 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables.js") +const auth = require("./auth.js") +const users = require("./users.js") +const files = require("./files.js") +const config = require("./config.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") +const servermetrics = require("./server-metrics.js") +const metrics = require("./metrics.js") +const pm2 = require('pm2'); + +/** + * NOTE INTERNE + * + * Changer les pictures de users autre part + */ + +/** + * + * @param {http.Server} server + */ + +module.exports.serverIO = function(server) { + + const io = require('socket.io')(server, { + maxHttpBufferSize: 1e8, + pingTimeout: 60000 + }) + + + io.on("connection", (socket) => { + + let token = cook.parse(socket.handshake.headers.cookie).token + var user = auth.getUserByToken(token) + + if(user) { + plog.log("Connexion au panel par '" + user.username + "' avec le socket : " + socket.id) + user.setLastLogin(new Date()) + /** + * GET REQUEST + */ + + // Get Users + GetRequest("USERINFO", () => { + user = auth.getUserByToken(token) + GetAnswer("USERINFO", {username: user.username, display_name: user.display_name ,picture: user.picture, permission: user.permission}) + }) + + /** + * POST REQUEST + */ + + PostRequest("US_EDIT_PERSONNAL", async (settings) => { + user = auth.getUserByToken(token) + PostAnswer("US_EDIT_PERSONNAL", await users.editMySelf(settings, user)) + }) + + if(user.checkPermission("FILES_EXPLORER")) { + + PostRequest("FX_GET", (root) => { + + PostAnswer("FX_GET", files.getFiles(root)) + }) + + PostRequest("FX_NEW_FOLDER", (root) => { + + PostAnswer("FX_NEW_FOLDER", files.createFolder(root)) + }) + + PostRequest("FX_DELETE", (root) => { + + PostAnswer("FX_DELETE", files.deleteFile(root)) + } ) + + PostRequest("FX_RENAME", (settings) => { + + PostAnswer("FX_RENAME", files.renameFile(settings)) + }) + + PostRequest("FX_SHARE", (settings) => { + + PostAnswer("FX_SHARE", files.shareFile(settings)) + }) + + PostRequest("FX_GETFILE", (root) => { + + PostAnswer("FX_GETFILE", files.getFile(root)) + }) + + 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) => { + + PostAnswer("FX_UPLOAD", files.uploadFile(settings)) + + }) + + PostRequest("FX_PASTE", (settings) => { + + PostAnswer("FX_PASTE", files.pasteFile(settings)) + }) + + } + + 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}) + }) + + } + + if(user.checkPermission("LINKS")) { + PostRequest("LINKS_GET_ALL", () => { + PostAnswer("LINKS_GET_ALL", {answer: "OK", links: links.getLinks()}) + }) + + PostRequest("LINKS_ADD", (settings) => { + PostAnswer("LINKS_ADD", {answer: links.addLink(settings)}) + }) + + PostRequest("LINKS_DELETE", (id) => { + PostAnswer("LINKS_DELETE", links.removeLink(id)) + }) + + PostRequest("LINKS_EDIT", (settings) => { + PostAnswer("LINKS_EDIT", links.updateLink(settings.id, settings)) + }) + + + } + + if(user.checkPermission("SERVERS")) { + PostRequest("SERVER_GET_METRICS_Alpha", async (settings) => { + PostAnswer("SERVER_GET_METRICS_Alpha", {answer: "OK", metrics: await servermetrics.getMetrics(settings), name: settings}); + }); + PostRequest("SERVER_GET_METRICS_Omega", async (settings) => { + PostAnswer("SERVER_GET_METRICS_Omega", {answer: "OK", metrics: await servermetrics.getMetrics(settings), name: settings}); + }); + } + + 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)) + }) + + } + + if(user.checkPermission("USERS")) { + GetRequest("US_ALL", async () => { + GetAnswer("US_ALL", await users.getAllUsers()) + }) + + PostRequest("US_ADD", async (settings) => { + PostAnswer("US_ADD", await users.addUser(settings)) + }) + + PostRequest("US_DELETE", async (settings) => { + PostAnswer("US_DELETE", await users.deleteUser(settings)) + }) + + PostRequest("US_EDIT", async (settings) => { + PostAnswer("US_EDIT", await users.editUser(settings)) + }) + + PostRequest("US_CLEAR_TOKENS", async (settings) => { + + PostAnswer("US_CLEAR_TOKENS", await users.clearTokens(settings)) + }) + } + + if(user.checkPermission("SETTINGS")) { + PostRequest("SETTINGS_SAVE", async (settings) => { + PostAnswer("SETTINGS_SAVE", await config.saveSettings(settings)) + }) + PostRequest("SERVER_RESTART", async () => { + pm2.restart('Neutral') + }) + PostRequest("SERVER_STOP", async () => { + pm2.stop('Neutral') + }) + GetRequest("SERVER_GET_LOGS", async () => { + GetAnswer("SERVER_GET_LOGS", await fs.readdirSync(__glob.LOGS)) + }) + + PostRequest("SERVER_READ_LOG", async (logs) => { + PostAnswer("SERVER_READ_LOG", await fs.readFileSync(__glob.LOGS + path.sep + logs).toString()) + }) + + GetRequest("SETTINGS_GET", async () => { + GetAnswer("SETTINGS_GET", await config.getSettings()) + }) + } + + if(user.checkPermission("METRICS")) { + GetRequest("MT_ALL", async () => { + GetAnswer("MT_ALL", await metrics.getMetrics()) + }) + + PostRequest("MT_ADD", async (settings) => { + PostAnswer("MT_ADD", await metrics.addMetric(settings)) + }) + + PostRequest("MT_DELETE", async (settings) => { + PostAnswer("MT_DELETE", await metrics.deleteMetric(settings)) + }) + + + } + + + socket.on("disconnect", (reason) => { + plog.log("Déconnexion du panel par '" + user.username + "' avec le socket : " + socket.id) + }) + + socket.on("connect_error", (err) => { + console.log(err) + console.log(err.message); // prints the message associated with the error + }); + + function GetRequest(GQname, GQcallback) { + socket.on("GET/" + GQname, () => { + + plog.log(user.username + " - Socket : " + socket.id + " - GET/" + GQname + " - [RECIEVED]") + GQcallback() + }) + + } + + function GetAnswer(GRname, GRvalue) { + + plog.log(user.username + " - Socket : " + socket.id + " - GET/" + GRname + " - [ANSWERED]") + socket.emit("ANSWER/GET/" + GRname, GRvalue) + + } + + function PostRequest(GQname, GQcallback) { + socket.on("POST/" + GQname, (value) => { + plog.log(user.username + " - Socket : " + socket.id + " - POST/" + GQname + " - [RECIEVED]") + GQcallback(value) + }) + + } + + function PostAnswer(GRname, GRvalue) { + + plog.log(user.username + " - Socket : " + socket.id + " - POST/" + GRname + " - [ANSWERED]") + socket.emit("ANSWER/POST/" + GRname, GRvalue) + + } + + } else { + + socket.disconnect() + plog.warn("Connexion directe vers le panel avec un token inexistant : " + this.token) + } + + + + + + }) + + +} diff --git a/bin/services.js b/bin/services.js new file mode 100644 index 0000000..4449405 --- /dev/null +++ b/bin/services.js @@ -0,0 +1,137 @@ +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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no raphix@omega.raphix.fr sudo -S systemctl stop gitea`) + resolve("OK") + } else if(service == `https://jenkins.raphix.fr`) { + let req = child_process.exec(`ssh -o StrictHostKeyChecking=no raphix@omega.raphix.fr sudo -S systemctl stop jenkins `) + resolve("OK") + + } else if(service == `https://raphix.fr`) { + let req = child_process.exec(`ssh -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no raphix@omega.raphix.fr sudo -S systemctl start gitea`) + resolve("OK") + } else if(service == `https://jenkins.raphix.fr`) { + let req = child_process.exec(`ssh -o StrictHostKeyChecking=no raphix@omega.raphix.fr sudo -S systemctl start jenkins `) + resolve("OK") + + } else if(service == `https://raphix.fr`) { + let req = child_process.exec(`ssh -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no raphix@omega.raphix.fr sudo -S systemctl restart gitea`) + resolve("OK") + } else if(service == `https://jenkins.raphix.fr`) { + let req = child_process.exec(`ssh -o StrictHostKeyChecking=no raphix@omega.raphix.fr sudo -S systemctl restart jenkins `) + resolve("OK") + + } else if(service == `https://raphix.fr`) { + let req = child_process.exec(`ssh -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 -o StrictHostKeyChecking=no 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 new file mode 100644 index 0000000..f45874d --- /dev/null +++ b/bin/users.js @@ -0,0 +1,544 @@ +const { LogType } = require("loguix") +const fs = require("fs") +const path = require("path") +const { __glob } = require("./global-variables") +const ulog = new LogType("Users") +const keygen = require("./keygen") +const uuid = require("uuid") + +var usersList = new Map() + +setup() + +function setup() { + + if(!fs.existsSync(__glob.USERS)) { + ulog.log("Création du fichier utilisateur dans : " + __glob.USERS) + fs.writeFileSync(__glob.USERS, JSON.stringify([], null, 2)) + } +} + +module.exports.getAllUsers = async function() { + return new Promise(async (resolve, reject) => { + const users = await this.fetchUsers() + + // Remove for every people the password & the tokens + + for(var user of users) { + user = user[1] + user.password = null + user.tokens = null + } + + + resolve(JSON.stringify(Array.from(users))) + }) + + +} + +/** + * Get all users from Users Data Base + */ +module.exports.fetchUsers = function () { + + ulog.step.init("fetch_user", "Récupération de tous les utilisateurs inscrit dans la base de donnée") + const userFile = getFile() + usersList = new Map() + for(var userFetched of userFile) { + const user = new this.User({ + username: userFetched.username, + password: userFetched.password, + display_name: userFetched.display_name, + permission: userFetched.permission, + tokens: userFetched.tokens, + lastLogin: userFetched.lastLogin, + picture: userFetched.picture + + }) + + usersList.set(user.username, user) + } + + if(usersList.size == 0) { + const adminUser = new this.User({ + "username": "admin", + "password": "neutral", + "display_name": "Administrateur", + "permission": [ + "FILES_EXPLORER", + "SERVICES", + "SERVERS", + "PIPELINES", + "METRICS", + "USERS", + "LINKS", + "SETTINGS" + ], + "tokens": [], + "lastLogin": "DEFAULT ACCOUNT", + "picture": "/images/default.jpg" + }) + + + adminUser.register() + + + } + + + ulog.step.end("fetch_user") + + return usersList +} + +/** + * User Class is used to access to default user's properties and methods + * @param {object} properties User properties with : username, password, display_name, permission... + */ +module.exports.User = class { + username = null + password = null; + display_name = null + permission = [] + tokens = [] + lastLogin = new Date() + picture = "/images/default.jpg" + + constructor(properties) { + + if(properties) { + + this.username = properties.username + this.password = keygen.encrypt(properties.password) + this.display_name = properties.display_name + this.permission = properties.permission + this.tokens = properties.tokens + this.lastLogin = properties.lastLogin + this.picture = properties.picture + + const userFile = getFile() + + for(var userFetched of userFile) { + if(properties.username == userFetched.username) { + ulog.log("Récupération dans la base de donnée, de l'utilisateur : " + userFetched.username) + this.username = userFetched.username + this.password = userFetched.password + this.display_name = userFetched.display_name + this.permission = userFetched.permission + this.tokens = userFetched.tokens + this.lastLogin = userFetched.lastLogin + } + } + + + + + + } + + + if(this.username == null) { + ulog.error("One of user is without username ! [IMPORANT_FIELD_IS_MISSING]") + this.username = Math.random() + } + if(this.password == null) { + ulog.error("'" + this.username + "' is without password ! Password reset to 'default' [IMPORANT_FIELD_IS_MISSING]") + this.password = keygen.encrypt("default") + } + if(this.display_name == null) { + ulog.warn("'" + this.username + "' is without display name !") + this.display_name = this.username + } + if(this.permission == null) { + ulog.warn("'" + this.username + "' has no longer permission !") + + } + if(this.tokens == null) { + this.tokens = [] + } + if(this.permission == null) { + this.permission = [] + } + if(this.lastLogin == null) { + this.lastLogin = new Date() + } + if(this.picture == null) { + this.picture = "/images/default.jpg" + } + + + } + + register() { + + var alreadyExist = false + const userFile = getFile() + + for(var userFetched of userFile) { + if(userFetched.username == this.username) { + userFile.splice(userFile.indexOf(userFetched), 1) + ulog.log("Mise à jour dans la base de donnée, de l'utilisateur : " + this.username) + alreadyExist = true + } + } + if(!alreadyExist) { + ulog.log("Création dans la base de donnée de l'utilisateur : " + this.username) + } + userFile.push(this) + updateFile(userFile) + + usersList.set(this.username, this) + } + + unregister() { + + var alreadyExist = false + const userFile = getFile() + + for(var userFetched of userFile) { + if(userFetched.username == this.username) { + userFile.splice(userFile.indexOf(userFetched), 1) + ulog.log("Mise à jour dans la base de donnée, de l'utilisateur : " + this.username) + alreadyExist = true + } + } + if(!alreadyExist) { + ulog.log("L'utilisateur n'est pas enregistré dans la base de donnée : " + this.username) + } + + updateFile(userFile) + usersList.delete(this.username) + } + + checkPermission(name) { + this.#sync() + if(this.permission.includes(name)) { + return true + } else { + + return false + } + + } + + addPermission(name) { + this.#sync() + for(var perms of this.permission) { + if(name == perms) { + ulog.warn("'" + this.username + "' a déjà la permission : " + name) + return false + } + } + this.permission.push(name) + this.register() + } + + removePermission(name) { + this.#sync() + var havePermission = false + for(var perms of this.permission) { + if(name == perms) { + havePermission = true + } + } + if(havePermission) { + this.permission.splice(this.permission.indexOf(name), 1) + this.register() + } else { + + ulog.warn("'" + this.username + "' n'a pas la permission : " + name) + return false + } + + } + + setPassword(newPassword) { + this.#sync() + this.password = keygen.encrypt(newPassword) + this.register() + ulog.log("Le mot de passe de l'utilisateur a été modifié : " + this.username) + + } + getPassword() { + this.#sync() + return keygen.decrypt(this.password) + } + + generateToken() { + this.#sync() + const gToken = uuid.v4().toString() + this.tokens.push(gToken) + this.register() + return gToken + + } + + removeToken(token) { + this.#sync() + var haveToken = false + for(var aToken of this.tokens) { + if(token == aToken) { + haveToken = true + } + } + if(haveToken) { + this.tokens.splice(this.tokens.indexOf(token), 1) + this.register() + } else { + + ulog.warn("'" + this.username + "' n'a pas le token : " + token) + return false + } + + } + + setPicture(text) { + this.#sync() + this.picture = text + this.register() + ulog.log("La photo de l'utilisateur a été modifié : " + this.username) + + } + + setDisplayName(text) { + this.#sync() + this.display_name = text + this.register() + ulog.log("Le nom d'affichage de l'utilisateur a été modifié : " + this.username) + + } + + setNewUsername(text) { + this.#sync() + module.exports.deleteUser(this.username) + this.username = text + this.register() + + ulog.log("Le nom d'utilisateur de l'utilisateur a été modifié : " + this.username) + + } + + setLastLogin(text) { + this.#sync() + this.lastLogin = text + this.register() + } + + setPicture(file) { + this.#sync() + var pictureDir = __glob.USERS_IMAGES + path.sep + uuid.v4().toString() + ".png" + if(!fs.existsSync(__glob.USERS_IMAGES)) { + fs.mkdirSync(__glob.USERS_IMAGES) + } + fs.writeFileSync(pictureDir, file) + + this.picture = pictureDir.replace(__glob.USERS_IMAGES + path.sep, "/users/") + this.register() + } + + setPermissions(permissions) { + this.#sync() + this.permission = permissions + this.register() + } + + clearTokens() { + this.#sync() + this.tokens = [] + this.register() + } + + #sync() { + + for(var userGet of usersList.keys()) { + const userFetched = usersList.get(userGet) + if(this.username == userFetched.username) { + this.username = userFetched.username + this.password = userFetched.password + this.display_name = userFetched.display_name + this.permission = userFetched.permission + this.tokens = userFetched.tokens + this.lastLogin = userFetched.lastLogin + } + } + + } +} + +module.exports.addUser = function(settings) { + + if(settings.username == '') { + ulog.error("Le nom d'utilisateur est manquant") + return "USERNAME_MISSING" + } else if(settings.password == '') { + ulog.error("Le mot de passe est manquant") + return "PASSWORD_MISSING" + } else if(settings.display_name == '') { + ulog.error("Le nom d'affichage est manquant") + return "DISPLAY_NAME_MISSING" + } else if(this.getUser(settings.username)) { + ulog.error("L'utilisateur existe déjà : " + settings.username) + return "ALREADY_EXIST" + } else { + ulog.step.init("add_user", "Ajout d'un utilisateur dans la base de donnée : " + settings.username) + var pictureDir = null + + + if(settings.picture == null) { + pictureDir = "/images/default.jpg" + } else { + pictureDir = __glob.USERS_IMAGES + path.sep + uuid.v4().toString() + ".png" + fs.writeFileSync(pictureDir, settings.picture) + + + } + + const user = new this.User({ + username: settings.username, + display_name: settings.display_name, + permission: settings.permissions, + picture: pictureDir.replace(__glob.USERS_IMAGES + path.sep, "/images/users/") + + }) + + user.setPassword(settings.password) + user.register() + ulog.step.end("add_user") + } + +} + +module.exports.deleteUser = function(username) { + ulog.step.init("delete_user", "Suppression d'un utilisateur dans la base de donnée : " + username) + const user = this.getUser(username) + user.unregister() + ulog.step.end("delete_user") + return "OK" +} + + +module.exports.getUser = function(username) { + return usersList.get(username) +} + +module.exports.editUser = function(settings) { + if(settings.username == '') { + ulog.error("Le nom d'utilisateur est manquant") + return "USERNAME_MISSING" + } else if(settings.display_name == '') { + ulog.error("Le nom d'affichage est manquant") + return "DISPLAY_NAME_MISSING" + } else { + ulog.step.init("edit_user", "Modification d'un utilisateur dans la base de donnée : " + settings.username) + const user = this.fetchUsers().get(settings.username) + if(user) { + + if(settings.newusername && settings.newusername != settings.username) { + if(this.getUser(settings.newusername)) { + ulog.error("L'utilisateur existe déjà : " + settings.username) + return "ALREADY_EXIST" + } else { + + user.setNewUsername(settings.newusername) + } + } + if(settings.display_name) { + user.setDisplayName(settings.display_name) + } + + if(settings.password) { + user.setPassword(settings.password) + } + + if(settings.picture) { + user.setPicture(settings.picture) + } + if(settings.permissions) { + + user.setPermissions(settings.permissions) + } + ulog.step.end("edit_user") + return "OK" + } else { + ulog.step.end("edit_user") + return "NOT_EXIST" + } + + } +} + +module.exports.editMySelf = function (settings, user) { + if(user.username == settings.username) { + if(settings.username == '') { + ulog.error("Le nom d'utilisateur est manquant") + return "USERNAME_MISSING" + } else if(settings.display_name == '') { + ulog.error("Le nom d'affichage est manquant") + return "DISPLAY_NAME_MISSING" + } else { + ulog.step.init("edit_user", "Modification d'un utilisateur dans la base de donnée : " + settings.username) + const user = this.fetchUsers().get(settings.username) + if(user) { + console.log(settings) + if(settings.newusername && settings.newusername != settings.username) { + if(this.getUser(settings.newusername)) { + ulog.error("L'utilisateur existe déjà : " + settings.username) + return "ALREADY_EXIST" + } else { + + user.setNewUsername(settings.newusername) + } + } + if(settings.display_name) { + user.setDisplayName(settings.display_name) + } + + if(settings.password) { + user.setPassword(settings.password) + } + + if(settings.picture) { + user.setPicture(settings.picture) + } + ulog.step.end("edit_user") + return "OK" + } else { + ulog.step.end("edit_user") + return "NOT_EXIST" + } + + } + } else { + ulog.error("Vous ne pouvez pas modifier les informations d'un autre utilisateur !") + return "NOT_ALLOWED" + } +} + +module.exports.clearTokens = function(username) { + + const user = this.fetchUsers().get(username) + user.clearTokens() +} + + +/** + * + * @returns User File + */ +function getFile() { + const file = JSON.parse(fs.readFileSync(__glob.USERS)) + return file +} + +/** + * Update le fichier utilisateur avec un object + * @param {Array} file + */ +function updateFile(file) { + if(fs.existsSync(__glob.USERS)) { + ulog.log("Mise à jour du fichier utilisateur dans : " + __glob.USERS) + fs.writeFileSync(__glob.USERS, JSON.stringify(file, null, 2)) + } +} + diff --git a/bin/www b/bin/www new file mode 100644 index 0000000..aeb5560 --- /dev/null +++ b/bin/www @@ -0,0 +1,107 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ +var log = require("loguix") +var {LogType} = require("loguix") +var { __glob } = require("./global-variables") +log.setup(__glob.LOGS, __glob.PACKAGE_JSON) + +const wlog = new LogType("Serveur") + +if(process.env.DEV ) { + + wlog.log("MODE DEVELOPEMENT ACTIF") +} + +wlog.step.init("start_server", "Démarrage du serveur Express JS") + +var app = require('../main'); +var http = require('http'); +var config = require("./config") +var serverIO = require("./servers") + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3001'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); +serverIO.serverIO(server) + +/** + * Listen on provided port, on all network interfaces. + */ + + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + wlog.step.error("start_server", bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + wlog.step.error("start_server" , bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + wlog.log("Serveur entrain d'écouter sur le port : " + server.address().port) + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + wlog.step.end("start_server") +} diff --git a/main.js b/main.js new file mode 100644 index 0000000..5a258c0 --- /dev/null +++ b/main.js @@ -0,0 +1,87 @@ +const createError = require('http-errors'); +const express = require('express'); +const path = require('path'); +const cookieParser = require('cookie-parser'); +var favicon = require('serve-favicon'); +const app = express(); +const fs = require("fs"); +const log = require("loguix") + +const { __glob } = require('./bin/global-variables'); +const users = require("./bin/users") +const {User} = require("./bin/users") + + + +var wlog = log.getInstance("Serveur") + + + +setup() + + +function getRouters() { + + wlog.log("Récupération de " + fs.readdirSync(__glob.ROUTES).length + " routeurs depuis : " + __glob.ROUTES) + for(var route of fs.readdirSync(__glob.ROUTES)) { + + if(route == "index.js") { + + app.use("/", require(__glob.ROUTES + "index")) + } else { + + app.use("/" + route.replace(".js", ""), require(__glob.ROUTES + route)) + } + + } + +} + +function setup() { + + app.set('views', path.join(__dirname, 'views')); + app.set('view engine', 'ejs'); + + app.use(express.json()); + app.use(express.urlencoded({ extended: false })); + app.use(cookieParser()); + app.use(express.static(path.join(__dirname, 'public'))); + app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) + app.use("/shared", express.static(__glob.SHARED)) + app.use("/users", express.static(__glob.USERS_IMAGES)) + + + getRouters() + users.fetchUsers() + + // catch 404 and forward to error handler + app.use(function(req, res, next) { + res.locals.message = "Page non trouvé"; + res.locals.error = { + "status": "404", + "stack": "" + } + + // render the error page + res.status(404 || 404); + res.render('utils/error'); + }); + + // error handler + app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('utils/error'); + }); + + + +} + + + +module.exports = app; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e358e81 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2523 @@ +{ + "name": "neutral", + "version": "0.7.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "neutral", + "version": "0.7.0", + "license": "ISC", + "dependencies": { + "cookie-parser": "~1.4.4", + "crypto-js": "^4.2.0", + "debug": "~2.6.9", + "ejs": "~2.6.1", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "install": "^0.13.0", + "loguix": "^1.4.2", + "nodemon": "^3.0.1", + "os-utils": "^0.0.14", + "pm2": "^5.3.0", + "serve-favicon": "^2.0.4", + "socket.io": "^4.7.2", + "uuid": "^9.0.1" + } + }, + "node_modules/@opencensus/core": { + "version": "0.0.9", + "license": "Apache-2.0", + "dependencies": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@opencensus/core/node_modules/semver": { + "version": "5.7.2", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@opencensus/core/node_modules/uuid": { + "version": "3.4.0", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/@opencensus/propagation-b3": { + "version": "0.0.8", + "license": "Apache-2.0", + "dependencies": { + "@opencensus/core": "^0.0.8", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@opencensus/propagation-b3/node_modules/@opencensus/core": { + "version": "0.0.8", + "license": "Apache-2.0", + "dependencies": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@opencensus/propagation-b3/node_modules/semver": { + "version": "5.7.2", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@opencensus/propagation-b3/node_modules/uuid": { + "version": "3.4.0", + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/@pm2/agent": { + "version": "2.0.3", + "license": "AGPL-3.0", + "dependencies": { + "async": "~3.2.0", + "chalk": "~3.0.0", + "dayjs": "~1.8.24", + "debug": "~4.3.1", + "eventemitter2": "~5.0.1", + "fast-json-patch": "^3.0.0-1", + "fclone": "~1.0.11", + "nssocket": "0.6.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.0", + "proxy-agent": "~6.3.0", + "semver": "~7.5.0", + "ws": "~7.4.0" + } + }, + "node_modules/@pm2/agent/node_modules/dayjs": { + "version": "1.8.36", + "license": "MIT" + }, + "node_modules/@pm2/agent/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/agent/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/@pm2/agent/node_modules/ws": { + "version": "7.4.6", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@pm2/io": { + "version": "5.0.2", + "license": "Apache-2", + "dependencies": { + "@opencensus/core": "0.0.9", + "@opencensus/propagation-b3": "0.0.8", + "async": "~2.6.1", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "~7.5.4", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@pm2/io/node_modules/async": { + "version": "2.6.4", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/io/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/io/node_modules/eventemitter2": { + "version": "6.4.9", + "license": "MIT" + }, + "node_modules/@pm2/io/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/@pm2/js-api": { + "version": "0.6.7", + "license": "Apache-2", + "dependencies": { + "async": "^2.6.3", + "axios": "^0.21.0", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "ws": "^7.0.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pm2/js-api/node_modules/async": { + "version": "2.6.4", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/js-api/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/js-api/node_modules/eventemitter2": { + "version": "6.4.9", + "license": "MIT" + }, + "node_modules/@pm2/js-api/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/@pm2/js-api/node_modules/ws": { + "version": "7.5.9", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@pm2/pm2-version-check": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", + "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "dependencies": { + "debug": "^4.3.1" + } + }, + "node_modules/@pm2/pm2-version-check/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/pm2-version-check/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "license": "MIT" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.15", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.8.10", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/amp": { + "version": "0.3.1", + "license": "MIT" + }, + "node_modules/amp-message": { + "version": "0.1.2", + "license": "MIT", + "dependencies": { + "amp": "0.3.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/argparse/node_modules/sprintf-js": { + "version": "1.0.3", + "license": "BSD-3-Clause" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types/node_modules/tslib": { + "version": "2.6.2", + "license": "0BSD" + }, + "node_modules/async": { + "version": "3.2.5", + "license": "MIT" + }, + "node_modules/async-listener": { + "version": "0.6.10", + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^5.3.0", + "shimmer": "^1.1.0" + }, + "engines": { + "node": "<=0.11.8 || >0.11.10" + } + }, + "node_modules/async-listener/node_modules/semver": { + "version": "5.7.2", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/axios": { + "version": "0.21.4", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-ftp": { + "version": "5.0.4", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/blessed": { + "version": "0.1.81", + "license": "MIT", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/bodec": { + "version": "0.1.0", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.18.3", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/charm": { + "version": "0.1.2", + "license": "MIT/X11" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cli-tableau": { + "version": "2.0.1", + "dependencies": { + "chalk": "3.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.15.1", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/continuation-local-storage": { + "version": "3.2.1", + "license": "BSD-2-Clause", + "dependencies": { + "async-listener": "^0.6.0", + "emitter-listener": "^1.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "license": "MIT", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/croner": { + "version": "4.1.97", + "license": "MIT" + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "license": "MIT" + }, + "node_modules/culvert": { + "version": "0.1.2", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.1", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/dayjs": { + "version": "1.11.10", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/ejs": { + "version": "2.6.2", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emitter-listener": { + "version": "1.1.2", + "license": "BSD-2-Clause", + "dependencies": { + "shimmer": "^1.2.0" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.5.3", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.3.6", + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter2": { + "version": "5.0.1", + "license": "MIT" + }, + "node_modules/express": { + "version": "4.16.4", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.3.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "license": "MIT" + }, + "node_modules/fclone": { + "version": "1.0.11", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.4", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-uri": { + "version": "6.0.2", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.0", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/git-node-fs": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/git-sha1": { + "version": "0.1.2", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "1.6.3", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.23", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.3", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/install": { + "version": "0.13.0", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip": { + "version": "1.1.8", + "license": "MIT" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/js-git": { + "version": "0.7.8", + "license": "MIT", + "dependencies": { + "bodec": "^0.1.0", + "culvert": "^0.1.2", + "git-sha1": "^0.1.2", + "pako": "^0.2.5" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "license": "ISC", + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lazy": { + "version": "1.0.11", + "license": "MIT", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/log-driver": { + "version": "1.2.7", + "license": "ISC", + "engines": { + "node": ">=0.8.6" + } + }, + "node_modules/loguix": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loguix/-/loguix-1.4.2.tgz", + "integrity": "sha512-fWp699F5Dqszpnriwotr3XvsaPXYAjmV3X2L4tmqUXgwHggvx1Fak1z3Ac7CeuVdIdYY5l85UQrEYPmhAb+IUw==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.4.1", + "license": "MIT", + "bin": { + "mime": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "license": "ISC" + }, + "node_modules/needle": { + "version": "2.4.0", + "license": "MIT", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/nodemon": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nopt": { + "version": "1.0.10", + "license": "MIT", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nssocket": { + "version": "0.6.0", + "license": "MIT", + "dependencies": { + "eventemitter2": "~0.4.14", + "lazy": "~1.0.11" + }, + "engines": { + "node": ">= 0.10.x" + } + }, + "node_modules/nssocket/node_modules/eventemitter2": { + "version": "0.4.14", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/os-utils": { + "version": "0.0.14", + "license": "MIT" + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/pac-resolver": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "ip": "^1.1.8", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidusage": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pidusage/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/pm2": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.3.0.tgz", + "integrity": "sha512-xscmQiAAf6ArVmKhjKTeeN8+Td7ZKnuZFFPw1DGkdFPR/0Iyx+m+1+OpCdf9+HQopX3VPc9/wqPQHqVOfHum9w==", + "dependencies": { + "@pm2/agent": "~2.0.0", + "@pm2/io": "~5.0.0", + "@pm2/js-api": "~0.6.7", + "@pm2/pm2-version-check": "latest", + "async": "~3.2.0", + "blessed": "0.1.81", + "chalk": "3.0.0", + "chokidar": "^3.5.3", + "cli-tableau": "^2.0.0", + "commander": "2.15.1", + "croner": "~4.1.92", + "dayjs": "~1.11.5", + "debug": "^4.3.1", + "enquirer": "2.3.6", + "eventemitter2": "5.0.1", + "fclone": "1.0.11", + "mkdirp": "1.0.4", + "needle": "2.4.0", + "pidusage": "~3.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.1", + "pm2-deploy": "~1.0.2", + "pm2-multimeter": "^0.1.2", + "promptly": "^2", + "semver": "^7.2", + "source-map-support": "0.5.21", + "sprintf-js": "1.1.2", + "vizion": "~2.2.1", + "yamljs": "0.3.0" + }, + "bin": { + "pm2": "bin/pm2", + "pm2-dev": "bin/pm2-dev", + "pm2-docker": "bin/pm2-docker", + "pm2-runtime": "bin/pm2-runtime" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "pm2-sysmonit": "^1.2.8" + } + }, + "node_modules/pm2-axon": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "amp": "~0.3.1", + "amp-message": "~0.1.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-axon-rpc": { + "version": "0.7.1", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-axon-rpc/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pm2-axon-rpc/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/pm2-axon/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pm2-axon/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/pm2-deploy": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "run-series": "^1.1.8", + "tv4": "^1.3.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pm2-multimeter": { + "version": "0.1.2", + "license": "MIT/X11", + "dependencies": { + "charm": "~0.1.1" + } + }, + "node_modules/pm2-sysmonit": { + "version": "1.2.8", + "license": "Apache", + "optional": true, + "dependencies": { + "async": "^3.2.0", + "debug": "^4.3.1", + "pidusage": "^2.0.21", + "systeminformation": "^5.7", + "tx2": "~1.0.4" + } + }, + "node_modules/pm2-sysmonit/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pm2-sysmonit/node_modules/ms": { + "version": "2.1.2", + "license": "MIT", + "optional": true + }, + "node_modules/pm2-sysmonit/node_modules/pidusage": { + "version": "2.0.21", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pm2-sysmonit/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/pm2/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pm2/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/promptly": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "read": "^1.0.4" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.3.1", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.5.2", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.3.3", + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read": { + "version": "1.0.7", + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-in-the-middle": { + "version": "5.2.0", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/run-series": { + "version": "1.1.9", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.3.0", + "license": "ISC" + }, + "node_modules/semver": { + "version": "7.5.4", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.16.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-favicon": { + "version": "2.5.0", + "license": "MIT", + "dependencies": { + "etag": "~1.8.1", + "fresh": "0.5.2", + "ms": "2.1.1", + "parseurl": "~1.3.2", + "safe-buffer": "5.1.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-favicon/node_modules/ms": { + "version": "2.1.1", + "license": "MIT" + }, + "node_modules/serve-favicon/node_modules/safe-buffer": { + "version": "5.1.1", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.13.2", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.1.0", + "license": "ISC" + }, + "node_modules/shimmer": { + "version": "1.2.1", + "license": "BSD-2-Clause" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.7.2", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "license": "MIT", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/socks": { + "version": "2.7.1", + "license": "MIT", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.2", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/socks/node_modules/ip": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "license": "BSD-3-Clause" + }, + "node_modules/statuses": { + "version": "1.4.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/systeminformation": { + "version": "5.21.22", + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "license": "ISC", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tslib": { + "version": "1.9.3", + "license": "Apache-2.0" + }, + "node_modules/tv4": { + "version": "1.3.0", + "license": [ + { + "type": "Public Domain", + "url": "http://geraintluff.github.io/tv4/LICENSE.txt" + }, + { + "type": "MIT", + "url": "http://jsonary.com/LICENSE.txt" + } + ], + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tx2": { + "version": "1.0.5", + "license": "MIT", + "optional": true, + "dependencies": { + "json-stringify-safe": "^5.0.1" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vizion": { + "version": "2.2.1", + "license": "Apache-2.0", + "dependencies": { + "async": "^2.6.3", + "git-node-fs": "^1.0.0", + "ini": "^1.3.5", + "js-git": "^0.7.8" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vizion/node_modules/async": { + "version": "2.6.4", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.11.0", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "license": "ISC" + }, + "node_modules/yamljs": { + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "bin": { + "json2yaml": "bin/json2yaml", + "yaml2json": "bin/yaml2json" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..430c07d --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "neutral", + "version": "1.1.0", + "description": "Panel d'administration de Raphix", + "main": "index.js", + "scripts": { + "start": "nodemon ./bin/www", + "dev": "set DEV=true & nodemon ./bin/www" + }, + "repository": { + "type": "git", + "url": "https://git.raphix.fr/infrastructure/neutral.git" + }, + "nodemonConfig": { + "ext": "js, html", + "ignore": [ + "*.json" + ], + "delay": "10000000" + }, + "keywords": [], + "dependencies": { + "cookie-parser": "~1.4.4", + "crypto-js": "^4.2.0", + "debug": "~2.6.9", + "ejs": "~2.6.1", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "install": "^0.13.0", + "loguix": "^1.4.2", + "nodemon": "^3.0.1", + "os-utils": "^0.0.14", + "pm2": "^5.3.0", + "serve-favicon": "^2.0.4", + "socket.io": "^4.7.2", + "uuid": "^9.0.1" + }, + "author": "Raphix", + "license": "ISC" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..87d1dc9 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/images/FontLogo.png b/public/images/FontLogo.png new file mode 100644 index 0000000..4a93ad1 Binary files /dev/null and b/public/images/FontLogo.png differ diff --git a/public/images/FormatLogo_BLACK.png b/public/images/FormatLogo_BLACK.png new file mode 100644 index 0000000..b78d4e2 Binary files /dev/null and b/public/images/FormatLogo_BLACK.png differ diff --git a/public/images/FormatLogo_BLACK.svg b/public/images/FormatLogo_BLACK.svg new file mode 100644 index 0000000..dada85b --- /dev/null +++ b/public/images/FormatLogo_BLACK.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/FormatLogo_WHITE.ico b/public/images/FormatLogo_WHITE.ico new file mode 100644 index 0000000..87d1dc9 Binary files /dev/null and b/public/images/FormatLogo_WHITE.ico differ diff --git a/public/images/FormatLogo_WHITE.png b/public/images/FormatLogo_WHITE.png new file mode 100644 index 0000000..7c8d0bf Binary files /dev/null and b/public/images/FormatLogo_WHITE.png differ diff --git a/public/images/FormatLogo_WHITE.svg b/public/images/FormatLogo_WHITE.svg new file mode 100644 index 0000000..fca4d89 --- /dev/null +++ b/public/images/FormatLogo_WHITE.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/default.jpg b/public/images/default.jpg new file mode 100644 index 0000000..4a93ad1 Binary files /dev/null and b/public/images/default.jpg differ 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 new file mode 100644 index 0000000..37784f5 --- /dev/null +++ b/public/javascripts/basics.js @@ -0,0 +1,1332 @@ + +var xMousePos = null; +var yMousePos = null; + +document.onmousemove = function(e) { + xMousePos = e.clientX + window.scrollX; + yMousePos = e.clientY + window.scrollY; + +} + +document.oncontextmenu = function(e) { + + xMousePos = e.clientX + window.scrollX; + yMousePos = e.clientY + window.scrollY; + +} + +/** + * Récupère depuis le document l'identifiant de l'élément + * @param {string} id Identifiant de l'élément `id='identifiant'` + * @returns + */ +function getID(id) { + + return document.getElementById(id) +} + +/** + * + * @param {Date} GetDate + * @returns + */ +function getFormattedDate(GetDate) { + + const date = new Date(GetDate) + + var gmonth = date.getMonth() + 1 + var gday = date.getDate() + var gHour = date.getHours() + var gMinute = date.getMinutes() + var gSecondes = date.getSeconds() + + + if(date.getMonth() + 1 < 10) { + gmonth = "0" + (date.getMonth() + 1) + } + + if(date.getDate() + 1 <= 10) { + gday = "0" + date.getDate() + } + + if(date.getHours() + 1 <= 10) { + gHour = "0" + date.getHours() + } + + if(date.getMinutes() + 1 <= 10) { + gMinute = "0" + date.getMinutes() + } + + if(date.getSeconds() + 1 <= 10) { + gSecondes = "0" + date.getSeconds() + } + + return gday + "/" + gmonth + "/" + date.getFullYear() + " - " + gHour + ":" + gMinute +} + + +/** + * Permet de faire une pop-up automatique + */ +class TextResponse { + constructor(name) { + this.name = name + this.element = getID(this.name) + this.element.innerHTML = " " + this.element.style.fontSize = "14px" + this.element.style.textAlign = "center" + } + + clear() { + + this.element.innerHTML = " " + } + + err(text) { + + this.element.classList.add("yellow") + this.element.innerHTML = " " + text + + } + + info(text) { + + this.element.classList.remove("yellow") + + this.element.innerHTML = " " + text + } + + setSize(size) { + + this.element.style.fontSize = size + } + + setWidth(width) { + this.element.style.width = width + } + + +} + + +const AllViews = new Map() +var zIndex = 5 + + + +/** + * Permet de créer une fenêtre + * @param {object} properties Propriétés de la fenêtre + */ +class ViewWindow { + ViewHTML = null + ViewWindowDiv + ViewProperties = null + ViewSpanInteger = null + ViewPopupSpanInteger = null + ViewPopupHTML = null + ViewPopupTitle = null + ViewItem = new ViewItem(this) + constructor (properties) { + if(!AllViews.has(properties.title)) { + + this.ViewProperties = properties + AllViews.set(properties.title, true) + + + + this.ViewHTML = ` +
+ +
+ +

${properties.title}

+
+ + + +
+
+
+
+
+ +
+ ` + this.ViewSpanInteger = document.createElement("div") + document.body.appendChild(this.ViewSpanInteger); + this.ViewSpanInteger.outerHTML = this.ViewHTML + + const header = document.getElementById(properties.title + "_header") + const windowDiv = document.getElementById(properties.title + "_window") + this.ViewWindowDiv = windowDiv + const closeWindow = document.getElementById(properties.title + "_close") + const minimizeWindow = document.getElementById(properties.title + "_minimize") + const reduceWindow = document.getElementById(properties.title + "_reduce") + + closeWindow.addEventListener("click", () => { + + this.destroy() + + }) + + reduceWindow.addEventListener("click", () => { + windowDiv.style.display = "none" + + }) + + minimizeWindow.addEventListener("click", () => { + + const windowDiv = document.getElementById(properties.title + "_window") + const contentDiv = document.getElementById(properties.title + "_content") + const minimizeIcon = document.getElementById(properties.title + "_minimize").children[0] + + + if(windowDiv.classList.contains("minimized")) { + windowDiv.classList.remove("minimized") + contentDiv.style.display = "unset" + windowDiv.style.backgroundColor = "#605e58c1" + minimizeIcon.classList.remove("fa-window-restore") + minimizeIcon.classList.add("fa-window-maximize") + } else { + windowDiv.classList.add("minimized") + contentDiv.style.display = "none" + windowDiv.style.backgroundColor = "transparent" + minimizeIcon.classList.remove("fa-window-maximize") + minimizeIcon.classList.add("fa-window-restore") + this.destroyPopup() + } + }) + + + + + let isDragging = false; + let offsetX, offsetY; + windowDiv.style.zIndex = zIndex + 1 + + + header.addEventListener('mousedown', (e) => { + isDragging = true; + + zIndex+=2 + windowDiv.style.zIndex = zIndex + + + // Enregistrez la position de la souris par rapport à la fenêtre + offsetX = e.clientX - windowDiv.getBoundingClientRect().left; + offsetY = e.clientY - windowDiv.getBoundingClientRect().top; + }); + + // Gérer le déplacement + document.addEventListener('mousemove', (e) => { + if (isDragging) { + + header.style.cursor = "grab" + + // Calculez la nouvelle position de la fenêtre en fonction de la position de la souris + const newLeft = e.clientX - offsetX; + const newTop = e.clientY - offsetY; + + // Limitation de la position pour éviter un débordement + const maxX = window.innerWidth - windowDiv.offsetWidth; + const maxY = window.innerHeight - windowDiv.offsetHeight; + + windowDiv.style.left = Math.min(Math.max(newLeft, 0), maxX) + 'px'; + windowDiv.style.top = Math.min(Math.max(newTop, 0), maxY) + 'px'; + } + }); + + // Gérer la fin du glisser-déposer + document.addEventListener('mouseup', () => { + header.style.cursor = "unset" + isDragging = false; + }); + + + + } + + this.ViewItem.show() + + + + } + + destroy() { + const win = getID(`${this.ViewProperties.title}_window`) + win.outerHTML = " " + AllViews.delete(this.ViewProperties.title) + this.ViewItem.hide() + } + + setContent(text) { + const contentDiv = document.getElementById(this.ViewProperties.title + "_content") + contentDiv.innerHTML = text + } + + getViewTitle() { + + return this.ViewProperties.title + } + + + + createPopup(properties) { + + this.ViewPopupHTML = `
+
+

${properties.title}

+ +
+ ${properties.content} +
` + + const contentDiv = getID(this.ViewProperties.title + "_content") + const popupDiv = getID(this.ViewProperties.title + "_popupDiv") + this.ViewPopupSpanInteger = document.createElement("div") + popupDiv.appendChild(this.ViewPopupSpanInteger); + this.ViewPopupSpanInteger.outerHTML = this.ViewPopupHTML + + this.ViewPopupTitle = properties.title + + const popup = getID(properties.title + "_popup") + const popupClose = getID(properties.title + "_popupClose") + + popupClose.addEventListener("click", () => { + + this.destroyPopup() + + }) + + // Center the popup to the window div + + popup.style.left = (contentDiv.offsetWidth / 2) - (popup.offsetWidth / 2) + "px" + popup.style.top = (contentDiv.offsetHeight / 2) - (popup.offsetHeight / 2) + "px" + + // Render parent element styled blur + contentDiv.classList.add("blur") + + // Disable all interractions like click and events with contentDiv children + + for(var child of contentDiv.children) { + + child.style.pointerEvents = "none" + } + + } + + destroyPopup() { + + const popup = getID(this.ViewPopupTitle + "_popup") + + if(popup) { + const contentDiv = getID(this.ViewProperties.title + "_content") + for(var child of contentDiv.children) { + + child.style.pointerEvents = "unset" + } + contentDiv.classList.remove("blur") + + + popup.outerHTML = "" + + } + + } + +} + + + +var AllComponents = new Array() + +/** + * Permet de créer un composant de vue + * @param {object} properties Propriétés du composant de vue + */ +class ViewComponent { + name; + icon; + permission; + window; + constructor (properties) { + this.name = properties.name + this.icon = properties.icon + this.permission = properties.permission + + AllComponents.push(this) + } + + inject(array) { + + array.push(`
+ +

${this.name}

+
`) + + + } + + createWindow(functionToExecute) { + + this.window = functionToExecute + } + + bindView() { + + const component = getID(this.name) + if(component) { + component.addEventListener("click", () => { + console.log(component) + if(this.window) { + this.window() + } + }) + + } + + } + + forceWindow() { + + if(this.window) { + this.window() + } + } + + + +} + +/** + * Permet de créer un menu déroulant + * @param {object} properties Propriétés du menu déroulant + */ + + +var ActualDroppableMenu = null +class DroppableMenu { + options = new Array() + id = null + DMSpanInteger = null + #enableHide = false + constructor() { + + this.id = Math.random().toString(36).substring(7) + this.options.push(`
`) + + } + + add(action, string) { + + this.options.push("
" + string + "
") + + } + + show() { + console.log("DROPPABLE MENU - " + this.id) + if(ActualDroppableMenu) { + ActualDroppableMenu.hide() + } + ActualDroppableMenu = this + + this.options.push(`
`) + + if(xMousePos && yMousePos) { + + this.DMSpanInteger = document.createElement("span") + + document.body.appendChild(this.DMSpanInteger); + this.DMSpanInteger.outerHTML = this.options.join('') + + const menu = getID(`dm-${this.id}`) + + menu.style.zIndex = zIndex + 2 + zIndex+=1 + + menu.style.left = (xMousePos - 40) + "px" + menu.style.top = (yMousePos - 40) + "px" + + } + + this.#enableHide = false + + document.addEventListener("click", () => { + + this.hide() + + }) + + + + } + + get(action) { + + return getID(this.id + "_" + action) + } + + hide() { + + const menu = getID(`dm-${this.id}`) + if(menu) { + menu.outerHTML = "" + } + + } +} + +/** + * Permet de créer un composant de type "Serveur" + */ +class Server { + constructor(properties) { + this.name = properties.name + this.description = properties.description + this.icon = properties.icon + } + + generateHTML() { + return ` +
+
+
+ +

${this.name}

+ +
+

${this.description}

+
+
+
+
+
+ +

RAM

+
+

-- Mo / -- Mo

+
+
+
+ +

CPU

+
+

---%

+
+
+
+ +

DISK

+
+

-- Go / -- Go

+
+
+
+
+ + ` + } + + + loadScript() { + + const ramSpan = getID(`${this.name}_ram`) + const cpuSpan = getID(`${this.name}_cpu`) + const diskSpan = getID(`${this.name}_disk`) + + + setInterval(() => { + + if(getID(`${this.name}_ram`)) { + + const request = post(`SERVER_GET_METRICS_` + this.name, this.name ) + console.log(this.name) + + + request.then((answer) => { + console.log(answer) + console.log(this.name) + //Transform all metrics in mb to answer.metrics to Go or % + answer.metrics.usedram = Math.round(answer.metrics.usedram) + answer.metrics.totalram = Math.round(answer.metrics.totalram) + + //Transform all metrics in blocks to Go + answer.metrics.usedisk = Math.round((answer.metrics.usedisk * 3.814697265625) / 1000000) + answer.metrics.totaldisk = Math.round((answer.metrics.totaldisk * 3.814697265625) / 1000000) + + if(answer.metrics.name == this.name) { + ramSpan.innerHTML = `${answer.metrics.usedram} Mo / ${answer.metrics.totalram} Mo` + cpuSpan.innerHTML = answer.metrics.cpu + "%" + diskSpan.innerHTML = `${answer.metrics.usedisk} Go / ${answer.metrics.totaldisk} Go` + + } + + }) + } + + + + }, 1000) + + + } +} + +/** + * Créer un composant de vue de type "Service" + * @param {object} properties Propriétés du composant de vue + */ +class Service { + name = null + description = null + icon = null + 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() { + return ` +
+
+ ${this.name} +
+

${this.name}

+

${this.description}

+

Etat : Vérification en cours ...

+
+
+ +
+ ${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", () => { + this.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 TextResponse("sv-power-info") + info.setWidth("350px") + + + 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' + this.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 + this.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' + this.View.destroyPopup("` Gestion de l'alimentation du service`") + this.isOnline = true + } else { + info.err("Impossible de redémarrer le 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") + + } + }) + + }) + + } + }) + } + +} + +class User { + constructor(properties) { + this.username = properties.username + this.display_name = properties.display_name + this.picture = properties.picture + this.permission = properties.permission + this.View = properties.window + this.Component = properties.component + } + + generateHTML() { + return ` +
+
+ ${this.username} +
+

${this.display_name}

+

${this.username}

+
+
+ +
+ ` + } + + loadScript() { + const editButton = getID(`${this.username}_edit`) + const deleteButton = getID(`${this.username}_delete`) + + editButton.addEventListener("click", () => { + this.View.createPopup({ + title: ` Editer l'utilisateur : ${this.username}`, + content: ` +
+
+ ${this.username} +

${this.display_name}

+

${this.username}

+
+
+

Nom d'utilisateur

+ +
+
+

Nom d'affichage

+ +
+
+

Mot de passe

+ +
+
+

Permissions

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+

Photo de profil

+ +
+
+ + +
+
+ + + +
+ + +
+ ` + }) + + const editCButton = getID(`${this.username}_cedit`) + const editTokensButton = getID(`${this.username}_edittokens`) + const editUsername = getID(`${this.username}_editusername`) + const editDisplay = getID(`${this.username}_editdisplay`) + const editPassword = getID(`${this.username}_editpassword`) + const editPicture = getID(`${this.username}_addpicture`) + const returnInfo = new TextResponse(`${this.username}_returninfo`) + + const editPermissions = new Array() + + editTokensButton.addEventListener("click", () => { + const request = post(`US_CLEAR_TOKENS`, this.username) + request.then((answer) => { + if(answer == "NO_TOKENS") { + returnInfo.err("Aucun token n'a été généré pour cet utilisateur") + } else { + returnInfo.info("Les tokens ont été éffacés avec succès") + } + }) + }) + + + editPermissions.push(getID(`${this.username}_perm_FILES_EXPLORER`)) + editPermissions.push(getID(`${this.username}_perm_SERVICES`)) + editPermissions.push(getID(`${this.username}_perm_LINKS`)) + editPermissions.push(getID(`${this.username}_perm_SERVERS`)) + editPermissions.push(getID(`${this.username}_perm_PIPELINES`)) + editPermissions.push(getID(`${this.username}_perm_METRICS`)) + editPermissions.push(getID(`${this.username}_perm_USERS`)) + editPermissions.push(getID(`${this.username}_perm_SETTINGS`)) + + + for(const actualPerm of this.permission) { + + getID(`${this.username}_perm_${actualPerm}`).checked = true + } + + editUsername.addEventListener("change", () => { + editUsername.value = editUsername.value.replace(/[^a-zA-Z0-9]/g, '') + }) + + + + + editCButton.addEventListener("click", () => { + const newPerms = new Array() + for(const permission of editPermissions) { + if(permission.checked) { + newPerms.push(permission.id.replace(`${this.username}_perm_`, "")) + } + + } + + + + + const request = post(`US_EDIT`, {username: this.username, newusername: editUsername.value, display_name: editDisplay.value, password: editPassword.value, permissions: newPerms, picture: editPicture.files[0]}) + request.then((answer) => { + if(answer == "ALREADY_EXIST") { + + returnInfo.err("L'utilisateur existe déjà !") + + } else if(answer == "USERNAME_MISSING") { + + returnInfo.err("Le nom d'utilisateur est manquant !") + } else if(answer == "DISPLAY_NAME_MISSING") { + + returnInfo.err("Le nom d'affichage est manquant !") + } else if(answer == "PASSWORD_MISSING") { + + returnInfo.err("Le mot de passe est manquant !") + } else { + + this.View.destroyPopup() + this.View.destroy() + this.Component.forceWindow() + loadUserInfo() + } + }) + }) + + + }) + + if(this.username != "raphix") { + deleteButton.addEventListener("click", () => { + this.View.createPopup({ + title: ` Supprimer l'utilisateur : ${this.username}`, + content: ` +

Voulez-vous vraiment supprimer l'utilisateur ${this.username} ?

+ + + + ` + }) + + const deleteCButton = getID(`${this.username}_deleteconfirm`) + + deleteCButton.addEventListener("click", () => { + const request = post(`US_DELETE`, this.username) + request.then((answer) => { + if(answer == "OK") { + this.View.destroyPopup() + this.View.destroy() + this.Component.forceWindow() + } else { + console.log(answer) + } + }) + }) + }) + } + + } +} + +/** + * Permet de créer un composant de vue de type "Metrics" + */ +class Metric { + properties; + View; + Component; + data; + constructor(settings) { + this.properties = settings.properties + this.View = settings.View + this.Component = settings.Component + this.data = settings.properties.data + } + + generateHTML() { + return ` +
+
+
+

${this.properties.name}

+

${this.properties.id}

+
+
+
+
+
+ + +
+ + +
+ ` + + } + + loadScript() { + + const contentDiv = getID(`${this.properties.id}_content`) + + if(this.data == "ERROR") { + contentDiv.innerHTML += `

Impossible de charger les données de la métrique

` + + } else { + for(const data of this.data) { + contentDiv.innerHTML += `

${data.description}

${data.name}

Valeur : ${data.value}

` + + } + } + + + const deleteButton = getID(`${this.properties.id}_metricpower`) + + deleteButton.addEventListener("click", () => { + this.View.createPopup({ + title: ` Supprimer la métrique : ${this.properties.name}`, + content: ` +

Voulez-vous vraiment supprimer la métrique ${this.properties.name} ?

+ + + + ` + }) + + const deleteCButton = getID(`${this.properties.id}_deleteconfirm`) + + deleteCButton.addEventListener("click", () => { + const request = post(`MT_DELETE`, this.properties.id) + request.then((answer) => { + if(answer == "OK") { + this.View.destroyPopup() + this.View.destroy() + this.Component.forceWindow() + + } else { + console.log(answer) + } + }) + }) + }) + } + + + +} + +/** + * Permet de créer un item de la barre des tâches + * @param {ViewWindow} window ViewWindow associé à l'item + */ +class ViewItem { + /** + * + * @param {ViewWindow} window + */ + constructor(window) { + this.window = window + } + + show() { + const viewItems = getID("views-items") + const item = document.createElement("div") + item.id = `${this.window.ViewProperties.title}_item` + item.classList.add("view-item") + item.innerHTML = this.window.ViewProperties.title + + viewItems.appendChild(item) + + item.addEventListener("click", () => { + zIndex+=1 + this.window.ViewWindowDiv.style.zIndex = zIndex + 1 + this.window.ViewWindowDiv.style.display = "unset" + }) + + item.addEventListener("contextmenu", () => { + const dp = new DroppableMenu() + + dp.add("index", ` Afficher`) + dp.add("close", ` Fermer`) + dp.show() + + const index = dp.get("index") + const close = dp.get("close") + + index.addEventListener("click", () => { + zIndex+=1 + this.window.ViewWindowDiv.style.display = "unset" + this.window.ViewWindowDiv.style.zIndex = zIndex + 1 + dp.hide() + }) + + close.addEventListener("click", () => { + this.window.destroy() + dp.hide() + }) + + }) + + } + + hide() { + const viewItems = getID("views-items") + const item = getID(`${this.window.ViewProperties.title}_item`) + + item.outerHTML = "" + } + +} + diff --git a/public/javascripts/filexplorer.js b/public/javascripts/filexplorer.js new file mode 100644 index 0000000..bb3e59c --- /dev/null +++ b/public/javascripts/filexplorer.js @@ -0,0 +1,786 @@ +document.addEventListener("contextmenu", (e) => { + + e.preventDefault() +}) + +explorer.createWindow(() => { + + var inCopyOrCut = null; + + const View = new ViewWindow({ + title: ` Gestionnaire de fichiers`, + width: "1000px", + height: "600px" + }) + + + + + + + goHomePath() + function goHomePath() { + const rFiles = post("FX_GET", "homepath") + + rFiles.then((result) => { + loadFiles(result) + }) + } + + function goSharePath() { + const rFiles = post("FX_GET", "sharepath") + + rFiles.then((result) => { + loadFiles(result) + }) + } + + + + + View.setContent(` +
+
+ + + +
+ + +
+
+
+
+

Chargement en cours ...

+
+
+
+ + `) + + const rootInput = getID(View.getViewTitle() + '_rootInput') + const explorer = getID(View.getViewTitle() + '_explorer') + const newFolder = getID(View.getViewTitle() + '_newFolder') + const newFile = getID(View.getViewTitle() + '_newFile') + const home = getID(View.getViewTitle() + '_home') + const sharebtn = getID(View.getViewTitle() + '_sharebtn') + + + + + + View.ViewWindowDiv.addEventListener("contextmenu", () => { + + + if(inCopyOrCut != null) { + + const dMenu = new DroppableMenu() + dMenu.add("paste", " Coller") + dMenu.show() + + dMenu.get("paste").addEventListener("click", () => { + console.log({newPath: rootInput.value, action: inCopyOrCut.action, file: inCopyOrCut.file, name: inCopyOrCut.file.name, orginalPath: inCopyOrCut.orginalPath}) + dMenu.hide() + const reqFiles = post("FX_PASTE", {newPath: rootInput.value, action: inCopyOrCut.action, file: inCopyOrCut.file, name: inCopyOrCut.file.name, orginalPath: inCopyOrCut.orginalPath}) + reqFiles.then((result) => { + if(result == "OK") { + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + }) + } else if(result == "NOT_PERMITTED") { + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + }) + View.createPopup({ + title: ` Erreur`, + content: `

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

` + }) + } else { + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + }) + View.createPopup({ + title: ` Erreur`, + content: `

Une erreur est survenue.

` + }) + } + }) + + inCopyOrCut = null + }) + } else { + console.log("LISTENER : " + rootInput.value) + + } + + }) + + home.addEventListener("click", () => { + + goHomePath() + + }) + + sharebtn.addEventListener("click", () => { + + goSharePath() + + }) + + + newFolder.addEventListener("click", () => { + + View.createPopup({ + title: ` Nouveau dossier`, + content: ` + +
+ + `}) + + const foldername = getID(View.getViewTitle() + "_foldername") + const foldercreate = getID(View.getViewTitle() + "_foldercreate") + const folderInfo = new TextResponse(View.getViewTitle() + "_folderInfo") + + folderInfo.setSize("13px") + + + foldercreate.addEventListener("click", () => { + folderInfo.clear() + if(foldername.value.length < 1) { + folderInfo.err("Le nom du dossier est trop court.") + return + } + + const regex = new RegExp(/^[a-zA-Z0-9-_]+$/) + + if(!regex.test(foldername.value) | foldername.value.replace(/\s/g, '').length < 1) { + folderInfo.err("Le nom du dossier est invalide.") + return + } + + const reqFiles = post("FX_NEW_FOLDER", rootInput.value + "/" + foldername.value) + reqFiles.then((result) => { + if(result == "OK") { + View.destroyPopup(` Nouveau dossier`) + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + }) + } else if(result == "EXIST") { + folderInfo.err("Le dossier existe déjà.") + } else if(result == "NOT_PERMITTED") { + folderInfo.err("Vous n'avez pas les permissions pour créer un dossier ici.") + } else { + folderInfo.err("Une erreur est survenue.") + } + }) + + + }) + + + + }) + + + // Create a new file with a popup wth 2 options, upload or create, if create give a input name or if upload, upload a file + + newFile.addEventListener("click", () => { + + View.createPopup({ + title: ` Nouveau fichier`, + content: ` + +
+ + + `}) + + const filename = getID(View.getViewTitle() + "_filename") + const filecreate = getID(View.getViewTitle() + "_filecreate") + const fileupload = getID(View.getViewTitle() + "_fileupload") + const fileInfo = new TextResponse(View.getViewTitle() + "_fileInfo") + + fileInfo.setSize("13px") + + filecreate.addEventListener("click", () => { + + fileInfo.clear() + if(filename.value.length < 1) { + fileInfo.err("Le nom du fichier est trop court.") + return + } + + const regex = new RegExp(/^[a-zA-Z0-9-_.]+$/) + + if(!regex.test(filename.value) | filename.value.replace(/\s/g, '').length < 1) { + fileInfo.err("Le nom du fichier est invalide.") + return + } + + const reqFiles = post("FX_NEW_FILE", rootInput.value + "/" + filename.value) + reqFiles.then((result) => { + if(result == "OK") { + View.destroyPopup(` Nouveau fichier`) + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + }) + } else if(result == "EXIST") { + fileInfo.err("Le fichier existe déjà.") + } else if(result == "NOT_PERMITTED") { + fileInfo.err("Vous n'avez pas les permissions pour créer un fichier ici.") + } else { + fileInfo.err("Une erreur est survenue.") + } + }) + + + }) + + fileupload.addEventListener("click", () => { + + fileInfo.clear() + View.destroyPopup(` Nouveau fichier`) + View.createPopup({ + title: ` Upload`, + content: ` + +
+ + `}) + + const fileuploadInput = getID(View.getViewTitle() + "_fileuploadInput") + const fileuploadBtn = getID(View.getViewTitle() + "_fileuploadBtn") + const fileuploadInfo = new TextResponse(View.getViewTitle() + "_fileuploadInfo") + + fileuploadBtn.addEventListener("click", () => { + + fileuploadInfo.clear() + if(fileuploadInput.files.length < 1) { + fileuploadInfo.err("Aucun fichier sélectionné.") + return + } + + const file = fileuploadInput.files[0] + console.log(file) + + const reqFiles = post("FX_UPLOAD", {name: file.name ,root: rootInput.value, file: file}) + + reqFiles.then((result) => { + + if(result == "OK") { + View.destroyPopup(` Upload`) + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + }) + } + if(result == "EXIST") { + fileuploadInfo.err("Le fichier existe déjà.") + } + if(result == "NOT_PERMITTED") { + fileuploadInfo.err("Vous n'avez pas les permissions pour uploader un fichier ici.") + } + if(result == "NOT_FILE") { + fileuploadInfo.err("Le fichier n'est pas valide.") + } + if(result == "NOT_EXIST") { + fileuploadInfo.err("Le fichier n'existe pas.") + } + if(result == "TOO_BIG") { + fileuploadInfo.err("Le fichier est trop volumineux.") + } + if(result == "ERROR") { + fileuploadInfo.err("Une erreur est survenue.") + } + }) + }) + }) + }) + + + + rootInput.addEventListener("change", () => { + + const reqFiles = post("FX_GET", rootInput.value) + reqFiles.then((result) => { + loadFiles(result) + + }) + }) + + function loadFiles(files) { + + rootInput.value = files.root + var fileElements = new Array() + + if(files.content == "NOT_PERMITTED") { + fileElements.unshift(`

Revenir au dossier parent

`) + fileElements.push("

Vous n'avez pas les permissions pour accéder à ce dossier.

") + + } else if(files.content == "NOT_EXIST") { + fileElements.unshift(`

Revenir au dossier parent

`) + explorer.innerHTML = "

Ce dossier n'existe pas.

" + } else { + + + for(const file of files.content) { + + if(file.directory) { + file.size = "Dossier" + + } else { + console.log('------------') + console.log(file.size) + console.log(bytesToSize(file.size)) + console.log('------------') + file.size = bytesToSize(file.size) + } + + fileElements.push(`
+
+ ${getIcon(file)} +

${file.name}

+
+

Taille : ${file.size}

+

Date de modification : ${getFormattedDate(file.lastedition)}

+
`) + + + } + + // Sort the files by directory and then by name + + fileElements.sort((a, b) => { + if(a.includes("Dossier") && !b.includes("Dossier")) { + return -1 + } else if(!a.includes("Dossier") && b.includes("Dossier")) { + return 1 + } else { + return 0 + } + + }) + + fileElements.unshift(`

Revenir au dossier parent

`) + + + } + + explorer.innerHTML = fileElements.join("") + + const parent = document.getElementById("fx-parent") + + parent.addEventListener("click", () => { + + const reqFiles = post("FX_GET", files.parent) + reqFiles.then((result) => { + loadFiles(result) + }) + + }) + + if(files.content != "NOT_PERMITTED" && files.content != "NOT_EXIST") { + + // If it's a directory, get the file directory and make the request to get the files in it and loadIt + + for(const file of files.content) { + + + + + const element = document.getElementById(file.id) + + if(file.directory) { + + + element.addEventListener("click", () => { + + const reqFiles = post("FX_GET", file.fileDirectory) + reqFiles.then((result) => { + loadFiles(result) + }) + }) + } else { + + element.addEventListener("dblclick", () => { + editFile() + }) + + + } + + + + + + + getID(file.id).addEventListener("contextmenu", () => { + + const dropMenu = new DroppableMenu() + + + + if(!file.directory) { + dropMenu.add("edit", " Editer") + dropMenu.add("share", " Partager") + dropMenu.add("download", " Télécharger") + } + + dropMenu.add("copy", " Copier") + dropMenu.add("cut", " Couper") + dropMenu.add("rename", " Renommer") + dropMenu.add("delete", " Supprimer") + dropMenu.show() + + + dropMenu.get("copy").addEventListener("click", () => { + dropMenu.hide() + inCopyOrCut = {"action": "copy", "file": file, "orginalPath": files.root} + }) + + dropMenu.get("cut").addEventListener("click", () => { + dropMenu.hide() + inCopyOrCut = {"action": "cut", "file": file, "orginalPath": files.root} + }) + + dropMenu.get("delete").addEventListener("click", () => { + dropMenu.hide() + const reqFiles = post("FX_DELETE", file.fileDirectory) + reqFiles.then((result) => { + if(result == "OK") { + const reqFiles = post("FX_GET", files.root) + reqFiles.then((result) => { + loadFiles(result) + }) + } else if(result == "NOT_PERMITTED") { + const reqFiles = post("FX_GET", files.root) + reqFiles.then((result) => { + loadFiles(result) + }) + View.createPopup({ + title: ` Erreur`, + content: `

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

` + }) + } else { + const reqFiles = post("FX_GET", files.root) + reqFiles.then((result) => { + loadFiles(result) + }) + View.createPopup({ + title: ` Erreur`, + content: `

Une erreur est survenue.

` + }) + + } + }) + }) + dropMenu.get("rename").addEventListener("click", () => { + + dropMenu.hide() + View.createPopup({ + title: ` Renommer`, + content: ` + +
+ + `}) + + const rename = getID(View.getViewTitle() + "_rename") + const renameBtn = getID(View.getViewTitle() + "_renameBtn") + const renameInfo = new TextResponse(View.getViewTitle() + "_renameInfo") + renameInfo.setSize("13px") + rename.value = file.name + rename.focus() + rename.select() + + renameBtn.addEventListener("click", () => { + renameInfo.clear() + if(rename.value.length < 1) { + renameInfo.err("Le nom du fichier / dossier est trop court.") + return + } + + 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.") + return + } + + const reqFiles = post("FX_RENAME", {root: files.root, oldName: file.name, newName: rename.value}) + reqFiles.then((result) => { + if(result == "OK") { + View.destroyPopup(` Renommer`) + const reqFiles = post("FX_GET", files.root) + reqFiles.then((result) => { + loadFiles(result) + }) + } else if(result == "EXIST") { + renameInfo.err("Le fichier / dossier existe déjà.") + } else if(result == "NOT_PERMITTED") { + renameInfo.err("Vous n'avez pas les permissions pour renommer ce fichier / dossier.") + } else { + renameInfo.err("Une erreur est survenue.") + } + }) + + }) + + }) + + if(!file.directory) { + dropMenu.get("share").addEventListener("click", () => { + dropMenu.hide() + const reqFiles = post("FX_SHARE", {root: files.root, name: file.name}) + reqFiles.then((result) => { + if(result == "NOT_PERMITTED") { + View.createPopup({ + title: ` Erreur`, + content: `

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

` + }) + } else { + View.createPopup({ + title: ` Partager`, + content: ` + +
+ + `}) + + const sharelink = getID(View.getViewTitle() + "_sharelink") + const shareBtn = getID(View.getViewTitle() + "_shareBtn") + const shareInfo = new TextResponse(View.getViewTitle() + "_shareInfo") + shareInfo.setSize("13px") + sharelink.value = result + sharelink.focus() + sharelink.select() + + shareBtn.addEventListener("click", () => { + sharelink.focus() + sharelink.select() + window.navigator.clipboard.writeText(sharelink.value) + shareInfo.ok("Copié !") + }) + } + }) + }) + // Edit file with an ViewWindow with 2 options close & save and the name of the window is like File - Editor + + dropMenu.get("edit").addEventListener("click", () => { + dropMenu.hide() + + editFile() + + }) + + dropMenu.get("download").addEventListener("click", () => { + dropMenu.hide() + const reqFiles = post("FX_GETFILE", file.fileDirectory) + reqFiles.then((result) => { + if(result == "NOT_PERMITTED") { + View.createPopup({ + title: ` Erreur`, + content: `

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

` + }) + } else { + // Make Download using result + + const element = document.createElement('a'); + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(result)); + element.setAttribute('download', file.name); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + } + }) + }) + + + + } + + }) + + + + + + function editFile() { + const reqFiles = post("FX_GETFILE", file.fileDirectory) + //Create a popup for the loading + + View.createPopup({ + title: ` Editeur`, + content: `

Chargement en cours ...

` + }) + + reqFiles.then((result) => { + View.destroyPopup(` Editeur}`) + if(result == "NOT_PERMITTED") { + View.createPopup({ + title: ` Erreur`, + content: `

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

` + }) + } else { + const editor = new ViewWindow({ + title: ` Editeur - ${file.fileDirectory}`, + width: "1000px", + height: "650px" + }) + + + if(file.type == "image/png" | file.type == "image/jpeg") { + editor.setContent(` +
+ +
`) + + + } else { + editor.setContent(` + +
+ +
+ + + `) + + const editorSave = getID(editor.getViewTitle() + "_save") + const editorContent = getID(editor.getViewTitle() + "_editorContent") + + // Sauvegarder le fichier en l'envoyant + + editorSave.addEventListener("click", () => { + + const reqFiles = post("FX_SAVEFILE", {root: files.root, name: file.name, content: editorContent.value}) + reqFiles.then((result) => { + if(result == "OK") { + editor.destroy() + const reqFiles = post("FX_GET", files.root) + reqFiles.then((result) => { + loadFiles(result) + }) + } else if(result == "NOT_PERMITTED") { + editor.createPopup({ + title: ` Erreur`, + content: `

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

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

Une erreur est survenue.

` + }) + } + }) + }) + + + + //forbid textarea resize + editorContent.style.resize = "none" + + + } + + + } + + }) + } + + } + + + } + + } + + + +}) + + +function getIcon(file) { + if(file.type == "application/json") { + + return '' + } + if(file.type == "application/msword" | file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document") { + + return '' + } + if(file.type == "application/vnd.ms-powerpoint") { + + return '' + } + if(file.type == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | file.type == "application/vnd.ms-excel") { + + return '' + } + + if(file.type == "application/java-archive") { + + return '' + } + + if(file.type == "application/x-sh") { + + return '' + } + + if(file.type == "application/x-msdos-program" | file.type == "application/x-msdownload") { + + return'' + } + if(file.type == "application/javascript") { + + return '' + } + if(file.type == "image/png" | file.type == "image/jpeg") { + + return '' + } + if(file.type == "text/html") { + + return '' + } + if(file.type == "text/css") { + + return '' + } + if(file.type == "application/zip") { + + return '' + } + if(file.type == "audio/mpeg") { + + return '' + } + if(file.type == "application/pdf") { + + return '' + } + if(file.directory) { + + return '' + } else { + + return '' + } + +} + +function bytesToSize(bytes) { + var sizes = ['o', 'Ko', 'Mo', 'Go', 'To']; + if (bytes == 0) return '0 o'; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; +} \ No newline at end of file diff --git a/public/javascripts/indexscript.js b/public/javascripts/indexscript.js new file mode 100644 index 0000000..450226b --- /dev/null +++ b/public/javascripts/indexscript.js @@ -0,0 +1,68 @@ + + + +// User Request + +loadUserInfo() + + +function loadUserInfo() { + + + const infoUsername = getID("infoUsername") + const infoUserimage = getID("infoUserimage") + const infoDisplayname = getID("infoDisplayname") + const views = getID("views") + const panelBox = getID("panel-box") + + const REQ_user = get("USERINFO") + + REQ_user.then((ANS_user) => { + console.log(ANS_user) + infoUserimage.src = ANS_user.picture + infoUsername.innerHTML = ANS_user.username + infoDisplayname.innerHTML = ANS_user.display_name + + const permissions = ANS_user.permission + + const AvailableViews = new Array() + // + permissions.forEach((permValue) => { + + AllComponents.forEach((component) => { + if(component.permission == permValue) { + component.inject(AvailableViews) + } + }) + + + }) + + + if(AvailableViews.join("") == "") { + + 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") + } + + views.innerHTML = AvailableViews.join("") + + // BindView + + AllComponents.forEach((component) => { + component.bindView() + }) + + + + }) + + +} + + diff --git a/public/javascripts/io.js b/public/javascripts/io.js new file mode 100644 index 0000000..fc86368 --- /dev/null +++ b/public/javascripts/io.js @@ -0,0 +1,62 @@ +// Socket IO - Communication + +var socketLink = null + +fetch('/internal/socketlink', { + method: "GET" +}).then(link => {socketLink = link}) + +const socket = io(socketLink); + +socket.on("connect", () => { + console.log("Connecté au serveur par le Socket avec l'ID : " + socket.id) +}); + +socket.on("disconnect", (log) => { + window.location.href = "/" +}) + +function get(request) { + + return new Promise((resolve, reject) => { + + socket.emit("GET/" + request) + console.log("Envoi de la requête GET : " + request) + + socket.once("ANSWER/GET/" + request, (answer) => { + + console.log("Réponse pour la requête : " + request) + + resolve(answer) + + }) + + }) + +} + +function post(request, value) { + + return new Promise((resolve, reject) => { + + socket.emit("POST/" + request, value) + console.log("Envoi de la requête POST : " + request) + + socket.once("ANSWER/POST/" + request, (answer) => { + + console.log("Réponse pour la requête : " + request) + + resolve(answer) + + }) + + }) + +} + + + +function getSocket() { + + return socket +} diff --git a/public/javascripts/link.js b/public/javascripts/link.js new file mode 100644 index 0000000..370bc97 --- /dev/null +++ b/public/javascripts/link.js @@ -0,0 +1,299 @@ +links.createWindow(() => { + + const View = new ViewWindow({ + title: ` Générateur de liens`, + width: "600px", + height: "600px", + }) + + View.setContent(` + +
+ +
+ + `) + + const addBtn = getID(`${View.getViewTitle()}_add`) + const linksDiv = getID(`${View.getViewTitle()}_links`) + var links = new Array() + + addBtn.addEventListener("click", () => { + + View.createPopup({ + title: ` Ajouter un lien`, + content: ` +
+ + +
+

Abstraire le lien :

+ +
+ +

+ +
+ ` + }) + + const titleInput = getID(`${View.getViewTitle()}_title`) + const urlInput = getID(`${View.getViewTitle()}_url`) + const addBtn = getID(`${View.getViewTitle()}_confirm`) + const info = new TextResponse(`${View.getViewTitle()}_info`) + const ablink = getID(`${View.getViewTitle()}_ablink`) + const newlinkSpan = getID(`${View.getViewTitle()}_newlink_span`) + + ablink.checked = true + + ablink.addEventListener("click", () => { + + if(!ablink.checked) { + + newlinkSpan.innerHTML = ` + + ` + + + + } else { + + newlinkSpan.innerHTML = "" + + } + + }) + + + + addBtn.addEventListener("click", () => { + info.clear() + console.log(ablink.checked) + + if(!titleInput.value) { + + info.err("Un titre est nécéssaire") + + } else if(!urlInput.value) { + + info.err("Une URL est nécéssaire") + + } else { + + var newlink = getID(`${View.getViewTitle()}_newlink`) + + if(!newlink) { + + newlink = { value: "" } + + + } else { + + if(!newlink.value) { + + info.err("Un nouveau lien est nécéssaire") + return false + } + + if(!newlink.value.match(/^[a-zA-Z0-9-_]+$/)) { + info.err("L'URL n'est pas valide") + return false + + } + } + + + + + + + + const request = post(`LINKS_ADD`, { + title: titleInput.value, + url: urlInput.value, + abstractLink: ablink.checked, + dest: newlink.value + }) + + request.then((answer) => { + + if(answer.answer == "OK") { + + info.info("Le lien a bien été ajouté") + View.destroyPopup() + getLinks() + + + } else if(answer.answer == "ALREADY_EXISTS") { + + info.err("Ce lien existe déjà") + + } else { + + info.err("Impossible d'ajouter le lien") + + } + + }) + + } + + }) + + }) + + getLinks() + + function getLinks() { + + + linksDiv.innerHTML = "" + links = new Array() + + const request = post(`LINKS_GET_ALL`) + request.then((answer) => { + + if(answer.answer == "OK") { + + if(answer.links.length == 0) { + + linksDiv.innerHTML = `

Aucun lien disponible

` + } + + answer.links.forEach((link) => { + + links.push(link) + + }) + + links.forEach((link) => { + + linksDiv.innerHTML += ` + + ` + + }) + + links.forEach((link) => { + const editBtn = getID(`${link.id}_edit`) + + editBtn.addEventListener("click", () => { + + View.createPopup({ + title: ` Modifier un lien`, + content: ` +
+ + +

+ +
+ ` + }) + + const titleInput = getID(`${View.getViewTitle()}_edittitle`) + const urlInput = getID(`${View.getViewTitle()}_editurl`) + const editBtnConfirm = getID(`${View.getViewTitle()}_editconfirm`) + const info = new TextResponse(`${View.getViewTitle()}_editinfo`) + + titleInput.value = link.title + urlInput.value = link.url + + editBtnConfirm.addEventListener("click", () => { + + if(!titleInput.value) { + + info.err("Un titre est nécéssaire") + + } else if(!urlInput.value) { + + info.err("Une URL est nécéssaire") + + } else { + + const request = post(`LINKS_EDIT`, { + id: link.id, + title: titleInput.value, + url: urlInput.value + }) + + request.then((answer) => { + + if(answer == "OK") { + + info.info("Le lien a bien été modifié") + View.destroyPopup() + getLinks() + + + } else { + + info.err("Impossible de modifier le lien") + + } + + }) + + } + + }) + + + + + }) + + + const delBtn = getID(`${link.id}_remove`) + delBtn.addEventListener("click", () => { + + const request = post(`LINKS_DELETE`, link.id) + request.then((answer) => { + + if(answer != "OK") { + + View.createPopup({ + title: ` Erreur`, + content: ` +

Impossible de supprimer le lien

+ ` + }) + + + } else { + + getLinks() + } + + }) + + }) + }) + + } else { + + info.err("Impossible de récupérer les liens") + + } + + + + }) + } + + +}) \ No newline at end of file diff --git a/public/javascripts/loginscript.js b/public/javascripts/loginscript.js new file mode 100644 index 0000000..57b78e0 --- /dev/null +++ b/public/javascripts/loginscript.js @@ -0,0 +1,67 @@ +const username = getID("username") +const password = getID("password") +const submit = getID("submit") + +const loginInfo = new TextResponse("login-info") + +loginInfo.clear() + +submit.addEventListener("click", () => { + + if(!username.value) { + + loginInfo.err("Un nom d'utilisateur est nécéssaire pour se connecter !") + + } else if(!password.value) { + + loginInfo.err("Un mot de passe est nécéssaire pour se connecter !") + } else { + + + login() + } +}) + +password.addEventListener("keyup", (event) => { + if (event.key === "Enter") { + login() + } +}); + +function login() { + + loginInfo.clear() + + fetch('/login', { + method: 'POST', + redirect: 'follow', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + username: username.value, + password: password.value + }) + }) + .then(response => redirect(response)) + + async function redirect(response) { + + response = await response.text() + + if(response == "AUTH_FAILED") { + + + loginInfo.err("Le nom d'utilisateur et le mot de passe sont incorrects.") + + } else if(response == "AUTH_SUCCESS") { + + window.location.href = "/" + + } + + + + } +} diff --git a/public/javascripts/metric.js b/public/javascripts/metric.js new file mode 100644 index 0000000..1378214 --- /dev/null +++ b/public/javascripts/metric.js @@ -0,0 +1,144 @@ +metrics.createWindow(() => { + + const View = new ViewWindow({ + title: ` Web Metrik`, + width: "600px", + height: "600px", + }) + + loadMetrics() + + function loadMetrics() { + + View.setContent(` +
+
+

WebMetrik : Calcul en cours

+
+ + +
+
+
+ +
+

Chargement en cours ...

+
+ +
`) + + const metricsList = new Array() + const metricsComponent = new Array() + const metricsReq = get("MT_ALL") + + const metricsDiv = document.getElementById(`${View.getViewTitle()}_metrics`) + const metricsNumber = document.getElementById(`${View.getViewTitle()}_number`) + const addButton = document.getElementById(`${View.getViewTitle()}_add`) + const refreshButton = document.getElementById(`${View.getViewTitle()}_refresh`) + const metricsView = document.getElementById(`${View.getViewTitle()}_metrics`) + + refreshButton.addEventListener("click", () => { + loadMetrics() + }) + + metricsReq.then((ANS_metrics) => { + console.log(ANS_metrics) + if(ANS_metrics != "UNAVAILABLE") { + metricsList.length = 0 + if(ANS_metrics.length == 0) { + metricsList.push(`
+

Aucune WebMetrik n'a été ajoutée

+
`) + } + ANS_metrics.forEach((metric) => { + const metricComponent = new Metric({ + properties: metric, + Component: metrics, + View: View + }) + metricsList.push(metricComponent.generateHTML()) + + metricsComponent.push(metricComponent) + }) + metricsDiv.innerHTML = metricsList.join("") + metricsNumber.innerHTML = ANS_metrics.length + + for(const metric of metricsComponent) { + metric.loadScript() + } + } else { + metricsDiv.innerHTML = `
+

Une erreur est survenue lors du chargement des WebMetrik

+
` + } + }) + + // Generate a pop to add metrics with 3 settings, the adress, the port, the name of the metrics and the key to authentificate + + addButton.addEventListener("click", () => { + + View.createPopup({ + title: "Ajouter un WebMetrik", + content: ` +
+

Adresse

+ +

Port

+ +

Nom

+ +

Clé d'authentification

+ + + +
+ ` + }) + + const addBtn = document.getElementById("mt-add-btn") + const address = document.getElementById("mt-add-address") + const port = document.getElementById("mt-add-port") + const name = document.getElementById("mt-add-name") + const key = document.getElementById("mt-add-key") + const infoMTAdd = new TextResponse("mtaddinfo") + + + addBtn.addEventListener("click", () => { + + // Check if all fields are filled + + if(address.value == "" || port.value == "" || name.value == "" || key.value == "") { + infoMTAdd.err("Veuillez remplir tous les champs") + return + } + + post("MT_ADD", { + address: address.value, + port: port.value, + name: name.value, + key: key.value + }).then((res) => { + if(res != "ERROR") { + View.destroyPopup() + loadMetrics() + } else { + View.createPopup({ + title: "Erreur", + content: `

Une erreur est survenue lors de l'ajout de la WebMetrik

` + }) + } + }) + }) + + }) + + + } +}) + + + + /* +
+

Chargement en cours ...

+
*/ \ No newline at end of file diff --git a/public/javascripts/middle.js b/public/javascripts/middle.js new file mode 100644 index 0000000..89eb4e5 --- /dev/null +++ b/public/javascripts/middle.js @@ -0,0 +1,59 @@ +// My View Component + + + +const explorer = new ViewComponent({ + name: "Explorateur de fichier", + icon: "fa-solid fa-folder", + permission: "FILES_EXPLORER" + }) + + + + const services = new ViewComponent({ + name: "Gestion des services", + icon: "fa-solid fa-layer-group", + permission: "SERVICES" + }) + + const servers = new ViewComponent({ + name: "Gestion des serveurs", + icon: "fa fa-server", + permission: "SERVERS" + }) + + const pipelines = new ViewComponent({ + name: "Gestion des pipelines", + icon: "fa-solid fa-code-merge", + permission: "PIPELINES" + }) + + const metrics = new ViewComponent({ + name: "Web Metrik", + icon: "fa-solid fa-square-poll-vertical", + permission: "METRICS" + }) + + const users = new ViewComponent({ + name: "Gestion des utilisateurs", + icon: "fa-solid fa-users", + permission: "USERS" + + }) + + const links = new ViewComponent({ + name: "Générateur des liens", + icon: "fa-solid fa-link", + permission: "LINKS" + }) + + const settings = new ViewComponent({ + name: "Paramètres", + icon: "fa-solid fa-cog", + permission: "SETTINGS" + }) + + + + + diff --git a/public/javascripts/personal.js b/public/javascripts/personal.js new file mode 100644 index 0000000..977c2dc --- /dev/null +++ b/public/javascripts/personal.js @@ -0,0 +1,119 @@ + +const menulogo = getID("menu-logo") +const usersettingsBtn = getID("user-settings-button") + +menu.style.display = "none" + +menulogo.addEventListener("click", () => { + + const menu = getID("menu") + + if(menu.style.display == "block") { + menu.style.display = "none" + } else { + menu.style.display = "block" + } + +}) + +usersettingsBtn.addEventListener("click", () => { + const View = new ViewWindow({ + title: ` Mon Compte`, + width: "600px", + height: "650px" + }) + + View.setContent(` + + `) + + const usSettingsImage = getID("us-settings-image") + const usSettingsUsername = getID("us-settings-username") + const usSettingsDisplayname = getID("us-settings-displayname") + const usSettingsPassword = getID("us-settings-password") + const usSettingsPermissions = getID("us-settings-permissions") + const usSettingsSave = getID("us-settings-save") + const usSettingsPicture = getID("us-settings-picture") + const returnInfo = new TextResponse("us-settings-return-info") + + const REQ_user = get("USERINFO") + + var actualUsername = "" + + REQ_user.then((ANS_user) => { + usSettingsImage.src = ANS_user.picture + usSettingsUsername.value = ANS_user.username + usSettingsDisplayname.value = ANS_user.display_name + actualUsername = ANS_user.username + + const permissions = ANS_user.permission + const permValid = new Array() + + permissions.forEach((permValue) => { + + permValid.push("

" + permValue + "

") + + }) + + usSettingsPermissions.innerHTML = permValid.join("") + }) + + usSettingsSave.addEventListener("click", () => { + const request = post(`US_EDIT_PERSONNAL`, {username: actualUsername, newusername: usSettingsUsername.value, display_name: usSettingsDisplayname.value, password: usSettingsPassword.value, picture: usSettingsPicture.files[0]}) + request.then((answer) => { + if(answer == "ALREADY_EXIST") { + + returnInfo.err("L'utilisateur existe déjà !") + + } else if(answer == "USERNAME_MISSING") { + + returnInfo.err("Le nom d'utilisateur est manquant !") + } else if(answer == "DISPLAY_NAME_MISSING") { + + returnInfo.err("Le nom d'affichage est manquant !") + } else if(answer == "PASSWORD_MISSING") { + + returnInfo.err("Le mot de passe est manquant !") + } else { + + View.destroy() + loadUserInfo() + } + }) + }) + + +}) + +document.addEventListener("click", (e) => { + if(e.target != menulogo) { + menu.style.display = "none" + } +}) \ No newline at end of file diff --git a/public/javascripts/pipeline.js b/public/javascripts/pipeline.js new file mode 100644 index 0000000..2e9fbfd --- /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(` +
+ + ${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/server.js b/public/javascripts/server.js new file mode 100644 index 0000000..b2b1699 --- /dev/null +++ b/public/javascripts/server.js @@ -0,0 +1,73 @@ +servers.createWindow(async () => { + const View = new ViewWindow({ + title: ` Gestion des serveurs`, + width: "1000px", + height: "450px" + }) + + const serversList = new Array() + + + const alpha = new Server({ + name: "Alpha", + description: "Serveur principal", + icon: "fa-solid fa-hourglass-start", + }) + + const omega = new Server({ + name: "Omega", + description: "Serveur secondaire", + icon: "fa-solid fa-stop", + }) + + serversList.push(alpha.generateHTML()) + serversList.push(omega.generateHTML()) + + + View.setContent(` +
+ ${serversList.join("")} +
`) + + await alpha.loadScript() + await omega.loadScript() + +}) + + +/** +
+
+
+ +

Alpha

+
+ +
+
+
+
+
+ +

RAM

+
+

1.5 Go / 2 Go

+
+
+
+ +

CPU

+
+

1.5 Go / 2 Go

+
+
+
+ +

DISK

+
+

1.5 Go / 2 Go

+
+
+
+
+ */ \ No newline at end of file diff --git a/public/javascripts/service.js b/public/javascripts/service.js new file mode 100644 index 0000000..8ed1887 --- /dev/null +++ b/public/javascripts/service.js @@ -0,0 +1,88 @@ +services.createWindow(async () => { + /** + * 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, + View: View + + }) + + 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, + View: View + + }) + + const jenkinsService = new Service({ + name: "Jenkins", + description: "Gestionnaire de pipeline", + icon: "/images/services/jenkins.svg", + url: "https://jenkins.raphix.fr" , + canAccess: true, + View: View + + }) + + const raphixwebsite = new Service({ + name: "Raphix.fr", + description: "Site web de Raphix", + icon: "/images/services/raphix.png", + url: "https://raphix.fr", + canAccess: true, + View: View + + }) + + const cvraphix = new Service({ + name: "CV Raphix", + description: "Curriculum Vitae de Raphix", + icon: "/images/services/cv.png", + url: "https://cv.raphix.fr", + canAccess: true, + View: View + }) + + const lavalink = new Service({ + name: "Lavalink", + description: "Serveur Lavalink pour Subsonics", + icon: "/images/services/lavalink.svg", + url: "http://omega.raphix.fr:2333", + canAccess: false, + View: View + }) + + 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/javascripts/setting.js b/public/javascripts/setting.js new file mode 100644 index 0000000..007b082 --- /dev/null +++ b/public/javascripts/setting.js @@ -0,0 +1,123 @@ +settings.createWindow(async () => { + + const View = new ViewWindow({ + title: ` Paramètres`, + width: "500px", + height: "620px" + }) + + View.setContent(` +
+
+

Options d'alimentation

+ +
+ + +
+ +
+
+

Configuration des Tokens

+

Jenkins

+ +

Omega

+ + +
+
+

Accès aux logs

+ + +
+
+ `) + + + const allLogs = document.getElementById("all-logs") + const readLogs = document.getElementById("read-logs") + + get("SERVER_GET_LOGS").then((logs) => { + logs.reverse() + allLogs.innerHTML = logs.map((log) => { + return `` + }).join("") + }) + + readLogs.addEventListener("click", () => { + const log = allLogs.value + post("SERVER_READ_LOG", log).then((logContent) => { + const logView = new ViewWindow({ + title: ` ${log}`, + width: "1000px", + height: "520px" + }) + + logContent = logContent.replaceAll("[INFO]", "[INFO]") + logContent = logContent.replaceAll("[WARN]", "[WARN]") + logContent = logContent.replaceAll("[ERROR]", "[ERROR]") + logContent = logContent.replaceAll("[STEP]", "[STEP]") + + logContent = logContent.replaceAll("[Users]", "[Users]") + logContent = logContent.replaceAll("[Web]", "[Web]") + logContent = logContent.replaceAll("[Serveur]", "[Serveur]") + logContent = logContent.replaceAll("[Authentification]", "[Authentification]") + + // Get every line of logs and add a set the style in blue when it's the date in [] + + const logLines = logContent.split("\n") + const newLogLines = new Array() + + logLines.forEach((line) => { + if(line.startsWith("[") && line.includes("]")) { + const date = line.split("]")[0] + "]" + const content = line.replace(date, "") + newLogLines.push(`${date}${content}\n`) + } else { + + newLogLines.push(`${line}\n`) + } + }) + + logView.setContent(` +
+
${newLogLines.join("")}
+
+ `) + + }) + }) + + const restartButton = document.getElementById("st-restart") + const stopButton = document.getElementById("st-stop") + const saveButton = document.getElementById("st-save") + const jenkinsToken = document.getElementById("jenkins_token") + const omegaToken = document.getElementById("omega_token") + + get("SETTINGS_GET").then((settings) => { + jenkinsToken.value = settings.jenkins_token + omegaToken.value = settings.omega_token + }) + + restartButton.addEventListener("click", () => { + post("SERVER_RESTART") + }) + + stopButton.addEventListener("click", () => { + post("SERVER_STOP") + }) + + saveButton.addEventListener("click", () => { + post("SETTINGS_SAVE", { + jenkins_token: jenkinsToken.value, + omega_token: omegaToken.value + }) + + get("SETTINGS_GET").then((settings) => { + jenkinsToken.value = settings.jenkins_token + omegaToken.value = settings.omega_token + }) + }) +}) \ No newline at end of file diff --git a/public/javascripts/user.js b/public/javascripts/user.js new file mode 100644 index 0000000..8a20bd8 --- /dev/null +++ b/public/javascripts/user.js @@ -0,0 +1,183 @@ +users.createWindow(async () => { + const usersComponent = users + + const View = new ViewWindow({ + title: ` Gestion des utilisateurs`, + width: "500px", + height: "700px" + }) + + loadUsers() + + function loadUsers() { + + View.setContent(` +
+
+

Utilisateurs : Calcul en cours

+ +
+
+
+

Chargement en cours ...

+
+
+
`) + + + const usersList = new Array() + const users = get("US_ALL") + + const usersDiv = document.getElementById(`${View.getViewTitle()}_users`) + const usersNumber = document.getElementById(`${View.getViewTitle()}_number`) + const addButton = document.getElementById(`${View.getViewTitle()}_add`) + + addButton.addEventListener("click", () => { + View.createPopup({ + title: "Ajouter un utilisateur", + content: ` +
+

Nom d'utilisateur

+ +

Nom d'affichage

+ +

Mot de passe

+ +

Permissions

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +

Photo de profil

+ + + + +
+ ` + }) + + + const addCButton = document.getElementById("us-add-button") + const username = document.getElementById("us-add-username") + const displayname = document.getElementById("us-add-displayname") + const password = document.getElementById("us-add-password") + const picture = document.getElementById("us-add-picture") + const permissions = document.getElementsByClassName("permissions")[0].children + const returnInfo = new TextResponse("user-addreturn-info") + + + addCButton.addEventListener("click", () => { + var permissionsList = new Array() + for(var permission of permissions) { + console.log(permission.children[0].checked) + if(permission.children[0].checked) { + permissionsList.push(permission.children[0].id.replace("perm_", "")) + } + } + + + post("US_ADD", { + username: username.value, + display_name: displayname.value, + password: password.value, + picture: picture.files[0], + permissions: permissionsList + }).then((answer) => { + if(answer == "ALREADY_EXIST") { + + returnInfo.err("L'utilisateur existe déjà !") + + } else if(answer == "USERNAME_MISSING") { + + returnInfo.err("Le nom d'utilisateur est manquant !") + } else if(answer == "DISPLAY_NAME_MISSING") { + + returnInfo.err("Le nom d'affichage est manquant !") + } else if(answer == "PASSWORD_MISSING") { + + returnInfo.err("Le mot de passe est manquant !") + } else { + + View.destroyPopup() + loadUsers() + } + + }) + + }) + + }) + + users.then(users => { + users = new Map(JSON.parse(users)) + for(var user of users) { + + user = user[1] + console.log(user) + const userComponent = new User({ + username: user.username, + display_name: user.display_name, + picture: user.picture, + permission: user.permission, + window: View, + component: usersComponent + }) + usersList.push(userComponent) + } + + + usersNumber.innerHTML = usersList.length + + + + const usersContent = new Array() + + for(var user of usersList) { + usersContent.push(user.generateHTML()) + + } + + usersDiv.innerHTML = usersContent.join("") + + for(var user of usersList) { + user.loadScript() + } + }) + } + + +}) + + diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..478cb62 --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,1370 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap'); +@import url('https://fonts.cdnfonts.com/css/gunship'); + +body { + padding: 2%; + background: linear-gradient(120deg, #323031 40%, rgba(241,0,0,1) 100%); + color: white; + font-size: 20px; + font-family: 'Roboto', sans-serif !important; + overflow: hidden; +} + +html { + min-height: 100%; + margin: 0; +} + +/*Button*/ + +.btn { + + background-color: transparent; + outline: none; + border-radius: 10px; + border: 1px solid currentColor; + color: currentColor; + padding: 10px; + transition: 0.2s; + cursor: pointer; +} + +.btn span { + transition: 0.2s; +} + +.btn:hover { + box-shadow: 0px 0px 10px currentColor; + background-color: currentColor; + +} + +.btn:hover span { + color: black + +} + +.btn:active { + box-shadow: 0px 0px 10px currentColor; + background-color: transparent; + +} + +.btn:active span { + color: white + +} + +.btn span a { + text-decoration: none; +} + +.btn-cover { + color: white; + transition: 0.1s; + cursor: pointer; +} + +.btn-cover:hover { + color: rgba(255, 255, 255, 0.675); + +} + +.btn-cover:active { + + color: rgba(255, 255, 255, 0.575); +} +/* Miniaturiez Button */ + +.min { + + width: 40px !important; + height: 40px !important; + border-radius: 100% !important; + font-size: 1vw !important; + transition: 0.2s; + padding: 0 !important; +} + +.min span { + + font-size: 20px !important; + +} + + + +/*Colors*/ + +.red { + + color: #f01000!important; +} + +.lightred { + color: #ff5d51 !important; +} + +.blue { + + color: rgb(66, 242, 255) !important; +} + +.yellow { + + color: rgb(255, 255, 27) !important; +} + +.green { + + color: rgb(64, 248, 64) !important; +} + +/*Balise Properties*/ + +code { + + background-color: #3D3B3C; + padding: 0.3vw; + border-radius: 10px; +} + +p { + margin-block-start: 0.5em !important; + margin-block-end: 0.5em !important; +} + +a { + color: currentColor; +} + +/*Lists*/ + +.center { + + text-align: center !important; + + +} + +.col { + + display: flex; + flex-direction: column; + gap: 2vw; + +} + +@media (max-width: 500px) { + + .row { + + + display: flex; + flex-direction: column; + gap: 2vw; + + } + +} + +@media (min-width: 500px) { + +.row { + + + display: flex; + flex-direction: row; + gap: 2vw; + +} + +} + +/* Fields */ + +.field { + + width: fit-content; + border-radius: 5px; + padding-bottom: 0.3vw; + padding-top: 0.3vw; + padding-left: 0.5vw; + border: 1px solid white; + background-color: transparent; + transition: 0.2s; + color: white; +} + +.field:focus { + color: black; + outline: none; + background-color: white; + box-shadow: 0px 0px 8px #C1BDB3 ; +} + + + + +/* Logo */ + +.logo { + + user-select: none; +} + +.user-actions { + + display: flex; + gap: 10px; +} + +@media (max-width: 640px) { + + .logo { + + font-family: 'Gunship', sans-serif; + text-shadow: 0px 0px 10px #ffffff; + font-size: 50px; + display: flex; + justify-content: center; + text-align: center; + flex-direction: column; + align-items: center; + } + + .logo-img { + + width: 70px; + margin-right: 10px; + } + + +} + +@media (min-width: 640px) { + + .logo { + + font-family: 'Gunship', sans-serif; + text-shadow: 0px 0px 10px #ffffff; + font-size: 50px; + display: flex; + + justify-content: center; + text-align: center; + align-items: center; + } + + .logo-img { + + width: 70px; + margin-right: 10px; + } + + +} + + + +/* Form Box */ + + +.form-box { + + width: 500px; + height: 400px; + border-radius: 10px; + display: flex; + padding: 2%; + flex-direction: column; + justify-content: space-between; + background-color: #605e5863; + backdrop-filter: blur(10px); +} + +.panel-box { + + width: 900px; + height: 500px; + border-radius: 10px; + display: flex; + padding: 2%; + flex-direction: column; + justify-content: space-between; + background-color: #605e5863; + gap: 50px; + backdrop-filter: blur(10px); +} + +.taskbar-box { + + width: 100%; + position: absolute; + background-color: #09090963; + backdrop-filter: blur(10px); + bottom: 0; + align-items: center; + + +} + +.taskbar-content { + + display: flex; + justify-content: space-between; + padding: 20px; +} + +.taskbar-image { + + width: 50px; + height: 50px; + border-radius: 100%; +} + +.taskbar-logo { + width: 50px; + + transition: 0.3s; +} + +.taskbar-logo:hover { + transform: scale(0.9); +} + +.taskbar-user { + + display: flex; + flex-direction: row; + gap: 10px; + align-items: center; +} + +.taskbar-userinfo { + text-align: end; + display: flex; + flex-direction: column; + gap: 5px; +} + +.taskbar-actions { + + display: flex; + flex-direction: row; + gap: 20px; + align-items: center; + +} + +.taskbar-username { + + font-size: 14px; + color: rgba(255, 255, 255, 0.68); + margin-block: 0 !important; +} + +.views-items { + + width: 100%; + display: flex; + flex-direction: row; + gap: 15px; +} + +.view-item { + + + display: flex; + align-items: center; + flex-direction: row; + gap: 10px; + font-size: 14px; + background-color: #3230319d; + padding: 5px; + padding-left: 10px; + padding-right: 10px; + border-radius: 15px; + transition: 0.3s; + user-select: none; + +} + +.view-item:hover { + + background-color: #4e4b4d9d; + cursor: pointer; + transform: scale(1.05); + +} + +.view-item:active { + + background-color: #4e4b4d9d; + cursor: pointer; + transform: scale(0.975); + +} + +.view-item p { + + text-align: start; +} +.menu-signout { + + color: #ff5d51; + cursor: pointer; + font-size: 14px; + display: grid; + grid-template-columns: 0.2fr 0.8fr; + gap: 10px; +} + +.dm-element:hover .menu-signout { + color: black; +} + + +.taskbar-dispname { + + margin-block: 0 !important; +} + +.m-align { + + margin-right: auto !important; + margin-left: auto !important; +} + +.t-center { + + text-align: center; +} + + +/* LOGIN Specs */ + +.LOG_btn { + + margin-top: 2%; + margin-bottom: 2%; +} + +.LOG_body { + + display: flex; + flex-direction: column; + justify-content: center; + gap: 10px; + align-items: center; + height: 100vh; + padding: 0 !important; + margin: 0 !important; +} + +/*INDEX Specs*/ + +@media (max-width: 640px) { + + .views-box { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 10px; + + + } + + +} + +@media (min-width: 640px) { + + .views-box { + width: 100%; + height: 100%; + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-template-rows: repeat(2, 1fr); + grid-column-gap: 10px; + grid-row-gap: 10px; + + } + + +} + + +.view { + display: flex; + flex-direction: column; + user-select: none; + -webkit-user-select: none; + transition: 0.1s; + +} + +.view:hover { + + cursor: pointer; + color: #ff5d51; + text-shadow: 0px 3px 2px #070607; +} +.view:active { + + cursor: pointer; + color: #ff5d5194; +} + +.view-image { + + font-size: 72px; +} + + + +.blur { + + filter: blur(10px); +} + +/*ViewWindow*/ + +.view-window { + + position: absolute; + display: flex; + flex-direction: column; + z-index: 1; + background-color: #605e58c1; + backdrop-filter: blur(10px); + border-radius: 10px; + -webkit-user-drag: auto; + +} + +.view-window-header { + + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + background-color: rgba(33, 32, 33, 0.753); + border-top-right-radius: 10px; + border-top-left-radius: 10px; + user-select: none; +} + +.view-window-content { + + height: 100%; + display: flex; + flex-direction: column; + width: 100%; + +} + +.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; +} + +.view-closediv { + + display: flex; + flex-direction: row; + gap: 20px; +} + +.minimized { + + height: 7% !important; +} + +/* Files Explorer */ + +.fx-window { + + display: flex; + flex-direction: column; + +} + +.fx-root-input { + background-color: #323031; + border: solid 1px #323031; + border-radius: 10px; + padding: 1%; + color: white; + outline:0px; + width: 100%; + +} + +.fx-explorer { + display: flex; + flex-direction: column; + overflow-y: auto ; + height: 472px; + +} + +.fx-element { + + display: grid; + grid-template-columns: 3fr 1fr 2fr; + grid-column-gap: 10px; + align-items: center; + padding: 5px; + padding-left: 30px; + padding-right: 30px; + font-size: 14px; + transition: 0.1s; +} + +.fx-element div { + display: flex; + justify-content: start; + gap: 10px; + align-items: center; +} + +.fx-element:hover { + + background-color: rgba(44, 40, 42, 0.614); + +} + +.fx-actions { + justify-content: end !important; + width: 100%; +} + +.fx-bar { + + margin: 1%; + display: flex; + gap: 10px; + align-items: center; + +} + +.fx-bar-actions { + + width: 33%; + display: flex; + flex-direction: row; + justify-content: end; + gap: 10px; + +} + +.fx-editor-content { + border: none; + border-top: 1px solid rgb(70, 67, 67); + background-color: transparent; + color: white; + padding: 15px; + height: 100%; + +} + +.fx-editor-content:focus { + + outline: none; +} + + +/* ===== Scrollbar CSS ===== */ + + + + /* Chrome, Edge, and Safari */ + *::-webkit-scrollbar { + width: 13px; + } + + *::-webkit-scrollbar-track { + background: transparent; + } + + *::-webkit-scrollbar-thumb { + background-color: #ff5d51; + border-radius: 17px; + border: 2px solid transparent; + } + +/* Dropable Menu */ + +.dm-menu { + position: absolute; + display: flex; + flex-direction: column; + width: 150px; + border-radius: 10px; + z-index: 3; + height: auto; +} + + +.dm-element { + + background-color: #1a1819c9; + padding: 10px; + cursor: pointer; + font-size: 14px; + display: grid; + grid-template-columns: 0.2fr 0.8fr; + +} + +.dm-element:hover { + background-color: #ffffffc9; + color:black + +} + + +/* 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; +} + +/* Links */ + +.ln-bar { + + display: flex; + justify-content: right; + padding: 10px; + gap: 10px; +} + +.ln-create { + + display: flex; + flex-direction: column; + gap: 10px; +} + +.ln-abstract-label { + + text-align: center; + font-size: 12px; +} + +.ln-abstract-div { + + display: flex; + flex-direction: row; + text-align: center; + align-items: center; + justify-content: center; +} + +.ln-link-actions { + + display: flex; + flex-direction: column; + gap: 10px; + align-items: flex-end; + +} + +.ln-link { + + display: grid; + grid-template-columns: 1fr 3fr 1fr; + align-items: center; + justify-content: space-between; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; + + +} + +.ln-links { + + display: flex; + flex-direction: column; + gap: 10px; + height: 75%; + padding: 10px; + overflow-y: auto; +} + +.ln-link-url { + + font-size: 12px; +} + +/* SERVERS */ + +.servers { + + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 20px; + user-select: none; + gap: 30px; + -webkit-user-select: none; +} + +.servers-box { + + display: flex; + flex-direction: column; + + height: 100%; + width: 100%; +} + +.servers-box-title { + + display: flex; + flex-direction: row; + gap: 10px; + align-items: center; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; + justify-content: space-between; +} + +.servers-box-title-info { + + display: flex; + flex-direction: row; + gap: 10px; + align-items: center; +} + +.servers-metrics { + + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; +} + +.servers-box-content { + + padding: 10px; + background-color: #3D3B3C; + margin-top: 20px; + border-radius: 10px; +} + +.servers-metrics-box { + + display: flex; + flex-direction: column; + gap: 10px; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; +} + +.servers-metrics-box div { + + display: flex; + align-items: center; + gap: 10px; + +} + +/* 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); + +} + + +/* USERS */ + +.us-bar{ + + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px; + + +} + +.us-bar p { + + font-size: 15px; +} + +.user-line { + + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; + align-items: center; + gap: 10px; +} + + +.user-line-info { + + display: flex; + flex-direction: row; + gap: 10px; + align-items: center; +} + +.user-line-displayname { + + font-size: 15px; + margin-block: 0 !important; +} + +.us-users { + display: flex; + padding: 10px; + flex-direction: column; + gap: 10px; + overflow-y: auto ; + height: 550px; +} + +.user-edit-actual { + + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + justify-content: center; +} +.user-edit-line { + + display: flex; + flex-direction: column; + + align-items: center; + justify-content: center; +} + +.user-edit-info { + + font-size: 14px; +} + +.user-edit-actions { + + display: flex; + flex-direction: row; + gap: 10px; + align-items: space-around; + justify-content: center; +} + +.category { + + display: flex; + gap: 10px; + flex-direction: column; + align-items: center; + background-color: #1b1b1bc1; + padding: 10px; + margin: 5px; + border-radius: 10px; +} + +.permissions { + + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + font-size: 12px; + gap: 10px; +} + +.us-add { + + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + justify-content: center; +} + +.us-add p { + font-size: 14px; + +} + +.us-edit { + + display: flex; + flex-direction: column; + justify-content: center; + gap: 10px; +} + +#st-save { + margin-top: 10px; + +} + + +.st-act { + + display: flex; + gap: 10px; +} + +.us-delete { + + font-size: 12px; +} + +.mt-metrics { + + overflow-y: auto; + height: 470px; + + +} + +.mt-bar { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 10px; + +} + +.mt-bar p { + font-size: 15px; +} + + +.mt-add { + + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + justify-content: center; +} + +.mt-add p { + font-size: 14px; + margin-block: 0 !important; +} + +#mt-add-btn { + + margin-top: 10px; +} + + +.metric { + + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 10px; + border-radius: 10px; + background-color: #1b1b1bc1; + transition: 0.1s; + align-items: center; + gap: 10px; + margin: 10px; +} + + +.metric-content { + + display: flex; + flex-direction: column; + gap: 10px; + font-size: 14px; + padding: 5px; +} + +.metric-id { + + font-size: 14px; + color: #c0c0c0; +} + + +.metric-data { + + display: grid; + align-items: center; + grid-template-columns: 1fr 1fr; + justify-content: center; + gap: 30px; +} + +.metric-data-id { + + font-size: 12px; + color: #c0c0c0; +} + + + +.metric-data-value { + + color: #c7c7c7; +} + +.metric-data-value span { + + color: #ffffff; +} + + +.menu-drop { + + display: flex; + flex-direction: column; + position: absolute; + bottom: 110%; + left: 0.7%; +} + +.menu-settings { + + display: flex; + gap: 10px; + padding: 10px; + align-items: center; + background-color: #1b1b1bc1; + transition: 0.1s; + font-size: 14px; + cursor: pointer; +} + +.menu-settings:hover { + + background-color: rgba(255, 255, 255, 0.614); + color: black; +} + +.user-settings { + + padding: 10px; + overflow-y: auto; + height: 566px; +} + +.us-settings-info { + font-size: 12px; +} + +.us-settings-image { + + width: 125px; + height: 125px; + border-radius: 100%; +} + +#us-settings-permissions { + + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr ; + gap: 10px; + width: 50%; + font-size: 12px; +} + +#us-settings-permissions p { + margin: 0 !important; + background-color: #323031; + padding: 10px; + border-radius: 10px; + text-align: center; + width: 100px; +} + +.metric-error { + + color: yellow; +} \ No newline at end of file diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..0e1ee89 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,25 @@ +var express = require('express'); +var router = express.Router(); +var auth = require("../bin/auth") + +/* GET home page. */ +router.get('/', function(req, res, next) { + + if(!auth.check(req.cookies.token)) { + res.clearCookie('token') + res.redirect(302, "/login") +} else { + + if(process.env.DEV ) { + + res.render('index', {dev: "

DÉVELOPEMENT

"}); + } else { + + res.render('index', {dev: ""}); + } + + + } +}); + +module.exports = router; diff --git a/routes/internal.js b/routes/internal.js new file mode 100644 index 0000000..a9f5c4e --- /dev/null +++ b/routes/internal.js @@ -0,0 +1,16 @@ +var express = require('express'); +var router = express.Router(); +var path = require("path") + +router.get("/socketlink", (req,res,next) => { + + if(process.env.DEV == true) { + + res.send("ws://localhost:3001") + } else { + res.send("ws://neutral.raphix.fr") + } +}) + + +module.exports = router; \ No newline at end of file diff --git a/routes/link.js b/routes/link.js new file mode 100644 index 0000000..69e32cc --- /dev/null +++ b/routes/link.js @@ -0,0 +1,23 @@ +var express = require('express'); +var router = express.Router(); +var path = require("path") + +module.exports = router; + + +module.exports.ApplyLinks = class { + constructor(links) { + // Remove all routes + router.stack = [] + + for(var link of links) { + const url = link.url + router.get("/" + link.dest, (req,res,next) => { + res.redirect(302, url) + }) + } + + module.exports = router; + } + +} \ No newline at end of file diff --git a/routes/login.js b/routes/login.js new file mode 100644 index 0000000..6bcdd0e --- /dev/null +++ b/routes/login.js @@ -0,0 +1,61 @@ +var express = require('express'); +var router = express.Router(); +var auth = require("../bin/auth") + +/* GET home page. */ +router.get('/', function(req, res, next) { + + if(auth.check(req.cookies.token)) { + + res.redirect(302, "/") + } else { + res.clearCookie('token') + res.render('login', {version: require("../package.json").version}); + } + + +}); + +module.exports = router; + +router.post("/", (req, res) => { + const body = req.body + + const token = auth.login({ + username: body.username, + password: body.password + }) + + if(token == "AUTH_FAILED") { + + setTimeout(() => { + res.status(403).send("AUTH_FAILED") + }, 1000) + + + + } else { + + res.cookie('token' , token, { maxAge: 900000000, httpOnly: true }) + res.status(200).send("AUTH_SUCCESS") + } + +}) + +router.get('/signout', function(req, res, next) { + + if(!auth.check(req.cookies.token)) { + + res.clearCookie('token') + res.redirect(302, "/") + + + } else { + + auth.signout(req.cookies.token) + res.clearCookie('token') + res.redirect(302, "/") + + } + +}); \ No newline at end of file diff --git a/routes/stylepage.js b/routes/stylepage.js new file mode 100644 index 0000000..6aaac1d --- /dev/null +++ b/routes/stylepage.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('utils/stylepage'); +}); + +module.exports = router; diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 0000000..156382f --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,65 @@ + + + + Neutral + + + +
+ + +
+

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

+
+
+
+ +
+
+ +
+ <%- dev %> +
+
+ +
+
+

DisplayName

+

Username

+
+ + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + diff --git a/views/login.ejs b/views/login.ejs new file mode 100644 index 0000000..e022378 --- /dev/null +++ b/views/login.ejs @@ -0,0 +1,31 @@ + + + + Neutral + + + +
+ + +
+

Nom d'utilisateur

+ +
+
+

Mot de passe

+ +
+

+ +

Version : <%- version %>

+

Panel d'administration

+ Revenir sur raphix.fr +
+ + + + + + + diff --git a/views/utils/error.ejs b/views/utils/error.ejs new file mode 100644 index 0000000..dffc8b0 --- /dev/null +++ b/views/utils/error.ejs @@ -0,0 +1,32 @@ + + + + Neutral - Erreur + + + + +
+

Erreur <%= error.status %>

+

<%= message %> - <%= error.status %>

+
<%= error.stack %>
+ +
+
+ + + + diff --git a/views/utils/stylepage.ejs b/views/utils/stylepage.ejs new file mode 100644 index 0000000..6795125 --- /dev/null +++ b/views/utils/stylepage.ejs @@ -0,0 +1,75 @@ + + + + Neutral + + + +

Neutral - Page de style

+

Cette page est dédiée au développement et n'est pas en relation avec le Panel Neutral.

+ +
+

Police : 'Roboto', sans-serif

+

Liens : https://neutral.raphix.fr/stylepage

+
+
+ + +
+

Classe : btn

+
+ + + + + +
+

Classe : btn min

+
+ + + +
+
+

#323031

+

#f10000

+

#605e5863

+
+

Classe : field

+ + + + + +