UNPKG

expresser

Version:

A ready to use Node.js web app wrapper, built on top of Express.

238 lines (197 loc) 9.86 kB
# EXPRESSER UTILS # ----------------------------------------------------------------------------- # General network, IO, client and server utilities. As this module can't reference # any other module but Settings, all its logging will be done to the console only. class Utils crypto = require "crypto" fs = require "fs" lodash = require "lodash" moment = require "moment" os = require "os" path = require "path" settings = require "./settings.coffee" # SERVER INFO UTILS # -------------------------------------------------------------------------- # Helper to get the correct filename for general files. For example # the settings.json file or cron.json for cron jobs. This will look into the current # directory, the running directory and the root directory of the app. # Returns null if no file is found. # @param [String] filename The base filename (with extension) of the config file. # @return [String] The full path to the config file if one was found, or null. getFilePath: (filename) -> originalFilename = "./" + filename.toString() if fs.existsSync? exists = fs.existsSync else exists = path.existsSync # Check if file exists. hasJson = exists filename return filename if hasJson # Try current path.. filename = path.resolve __dirname, originalFilename hasJson = exists filename return filename if hasJson # Try parent path.. filename = path.resolve __dirname, "../", originalFilename hasJson = exists filename return filename if hasJson # If file does not exist on local path, try application root path. filename = path.resolve path.dirname(require.main.filename), originalFilename hasJson = exists filename return filename if hasJson # Nothing found, so return null. return null # Returns a list of valid server IP addresses. If `firstOnly` is true it will # return only the very first IP address found. # @param [Boolean] firstOnly Optional, default is false which returns an array with all valid IPs, true returns a String will first valid IP. # @return The server IPv4 address, or null. getServerIP: (firstOnly) -> ifaces = os.networkInterfaces() result = [] # Parse network interfaces and try getting the server IPv4 address. for i of ifaces ifaces[i].forEach (details) -> if details.family is "IPv4" and not details.internal result.push details.address # Return only first IP or all of them? if firstOnly return result[0] else return result # Return an object with general information about the server. # @return [Object] Results with process pid, platform, memory, uptime and IP. getServerInfo: => result = {} # Save parsed OS info to the result object. result.uptime = moment.duration(process.uptime, "s").humanize() result.hostname = os.hostname() result.title = path.basename process.title result.platform = os.platform() + " " + os.arch() + " " + os.release() result.memoryTotal = (os.totalmem() / 1024 / 1024).toFixed(0) + " MB" result.memoryUsage = 100 - (os.freemem() / os.totalmem() * 100).toFixed(0) result.loadAvg = os.loadavg() result.ips = @getServerIP() result.process = {pid: process.pid, memoryUsage: (process.memoryUsage().rss / 1024 / 1024).toFixed(0) + " MB"} return result # CLIENT INFO UTILS # -------------------------------------------------------------------------- # Get the client or browser IP. Works for http and socket requests, even when behind a proxy. # @param [Object] reqOrSocket The request or socket object. # @return [String] The client IP address, or null. getClientIP: (reqOrSocket) -> return null if not reqOrSocket? # Try getting the xforwarded header first. if reqOrSocket.header? xfor = reqOrSocket.header "X-Forwarded-For" if xfor? and xfor isnt "" return xfor.split(",")[0] # Get remote address. if reqOrSocket.connection? return reqOrSocket.connection.remoteAddress else return reqOrSocket.remoteAddress # Get the client's device. This identifier string is based on the user agent. # @param [Object] req The request object. # @return [String] The client's device. getClientDevice: (req) -> ua = req.headers["user-agent"] # Find mobile devices. return "mobile-wp-8" if ua.indexOf("Windows Phone 8") > 0 return "mobile-wp-7" if ua.indexOf("Windows Phone 7") > 0 return "mobile-wp" if ua.indexOf("Windows Phone") > 0 return "mobile-iphone-5" if ua.indexOf("iPhone5") > 0 return "mobile-iphone-4" if ua.indexOf("iPhone4") > 0 return "mobile-iphone" if ua.indexOf("iPhone") > 0 return "mobile-android-5" if ua.indexOf("Android 5") > 0 return "mobile-android-4" if ua.indexOf("Android 4") > 0 return "mobile-android" if ua.indexOf("Android") > 0 # Find desktop browsers. return "desktop-chrome" if ua.indexOf("Chrome/") > 0 return "desktop-firefox" if ua.indexOf("Firefox/") > 0 return "desktop-safari" if ua.indexOf("Safari/") > 0 return "desktop-opera" if ua.indexOf("Opera/") > 0 return "desktop-ie-11" if ua.indexOf("MSIE 11") > 0 return "desktop-ie-10" if ua.indexOf("MSIE 10") > 0 return "desktop-ie-9" if ua.indexOf("MSIE 9") > 0 return "desktop-ie" if ua.indexOf("MSIE") > 0 # Return default desktop value if no specific devices were found on user agent. return "desktop" # IO AND DATAUTILS # -------------------------------------------------------------------------- # Copy the `src` file to the `target`, both must be the full file path. # @param [String] src The full source file path. # @param [String] target The full target file path. copyFileSync: (src, target) => srcContents = fs.readFileSync src fs.writeFileSync target, srcContents # Minify the passed JSON value. Removes comments, unecessary white spaces etc. # @param [String] source The JSON text to be minified. # @param [Boolean] asString If true, return as string instead of JSON object. # @return [String] The minified JSON, or an empty string if there's an error. minifyJson: (source, asString) -> source = JSON.stringify source if typeof source is "object" index = 0 length = source.length result = "" symbol = undefined position = undefined # Main iterator. while index < length symbol = source.charAt index switch symbol # Ignore whitespace tokens. According to ES 5.1 section 15.12.1.1, # whitespace tokens include tabs, carriage returns, line feeds, and # space characters. when "\t", "\r" , "\n" , " " index += 1 # Ignore line and block comments. when "/" symbol = source.charAt(index += 1) switch symbol # Line comments. when "/" position = source.indexOf("\n", index) # Check for CR-style line endings. position = source.indexOf("\r", index) if position < 0 index = (if position > -1 then position else length) # Block comments. when "*" position = source.indexOf("*/", index) if position > -1 # Advance the scanner's position past the end of the comment. index = position += 2 break throw SyntaxError("Unterminated block comment.") else throw SyntaxError("Invalid comment.") # Parse strings separately to ensure that any whitespace characters and # JavaScript-style comments within them are preserved. when "\"" position = index while index < length symbol = source.charAt(index += 1) if symbol is "\\" # Skip past escaped characters. index += 1 else break if symbol is "\"" if source.charAt(index) is "\"" result += source.slice(position, index += 1) break throw SyntaxError("Unterminated string.") # Preserve all other characters. else result += symbol index += 1 # Check if should return as string or JSON. if asString return result else return JSON.parse result # Singleton implementation # -------------------------------------------------------------------------- Utils.getInstance = -> @instance = new Utils() if not @instance? return @instance module.exports = exports = Utils.getInstance()