Version 0.1.2 - Ajout du systême d'authentification
This commit is contained in:
		
							
								
								
									
										91
									
								
								bin/auth.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								bin/auth.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
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.getUsers().forEach((fetchUser) => {
 | 
			
		||||
        if(fetchUser.tokens.includes(token)) {
 | 
			
		||||
            isApproved = true
 | 
			
		||||
            username = fetchUser.username
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    if(isApproved) {
 | 
			
		||||
        alog.log("Connexion par Token de l'utilisateur : " + username)
 | 
			
		||||
        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.getUsers().has(username)) {
 | 
			
		||||
        const user = users.getUsers().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.getUsers().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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								bin/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								bin/config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
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))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -4,7 +4,10 @@ const root = path.resolve(__dirname, '../')
 | 
			
		||||
const __glob = {
 | 
			
		||||
    ROUTES: root + path.sep + "routes" + path.sep,
 | 
			
		||||
    ROOT: root,
 | 
			
		||||
    LOGS: root + path.sep + "logs"
 | 
			
		||||
    LOGS: root + path.sep + "logs",
 | 
			
		||||
    DATA: root + path.sep + "data",
 | 
			
		||||
    USERS: root + path.sep + "data" + path.sep + "users.json",
 | 
			
		||||
    CONFIG: root + path.sep + "data" + path.sep + "config.json"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								bin/keygen.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								bin/keygen.js
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
							
								
								
									
										291
									
								
								bin/users.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								bin/users.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,291 @@
 | 
			
		||||
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))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 * @returns Liste des utilisateurs
 | 
			
		||||
 */
 | 
			
		||||
module.exports.getUsers = function () {
 | 
			
		||||
 | 
			
		||||
    return usersList
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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,
 | 
			
		||||
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        usersList.set(user.username, user)    
 | 
			
		||||
    }
 | 
			
		||||
    ulog.step.end("fetch_user")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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()
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
            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()
 | 
			
		||||
        }
 | 
			
		||||
      
 | 
			
		||||
       
 | 
			
		||||
     
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #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
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
            
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 
 | 
			
		||||
 * @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))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								bin/www
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								bin/www
									
									
									
									
									
								
							@@ -7,13 +7,15 @@ var log = require("loguix")
 | 
			
		||||
var {LogType} = require("loguix")
 | 
			
		||||
var { __glob } = require("./global-variables")
 | 
			
		||||
log.setup(__glob.LOGS)
 | 
			
		||||
 | 
			
		||||
const wlog = new LogType("Web")
 | 
			
		||||
 | 
			
		||||
wlog.step.init("start_server",  "Démarrage du serveur Express JS")
 | 
			
		||||
 | 
			
		||||
var app = require('../main');
 | 
			
		||||
var debug = require('debug')('neutral:server');
 | 
			
		||||
var http = require('http');
 | 
			
		||||
 | 
			
		||||
var config = require("./config")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -91,9 +93,10 @@ function onError(error) {
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
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")
 | 
			
		||||
}
 | 
			
		||||
}  
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user