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"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.existsSyncCheck if file exists.
hasJson = exists filename
return filename if hasJsonTry current path..
filename = path.resolve __dirname, originalFilename
hasJson = exists filename
return filename if hasJsonTry parent path..
filename = path.resolve __dirname, "../", originalFilename
hasJson = exists filename
return filename if hasJsonIf 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 hasJsonNothing found, so return null.
return nullReturns 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.addressReturn only first IP or all of them?
if firstOnly
return result[0]
else
return resultReturn 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 resultGet 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.remoteAddressGet 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") > 0Find 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") > 0Return default desktop value if no specific devices were found on user agent.
return "desktop"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, srcContentsMinify 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 = undefinedMain iterator.
while index < length
symbol = source.charAt index
switch symbolIgnore 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 += 1Ignore line and block comments.
when "/"
symbol = source.charAt(index += 1)
switch symbolLine 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 > -1Advance 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 += 1Check if should return as string or JSON.
if asString
return result
else
return JSON.parse resultHelper to convert HSL colour to HEX. Used by Philips Hue and other colored lights. @param [Number] h The hue value. @param [Number] s The saturation value. @param [Number] l The brightness value. @return [String] The colour in HEX format.
hslToHex: (h, s, l) ->
x = h
y = s
z = 1.0 - x - y
Y = l
X = (Y / y) * x
Z = (Y / y) * z
r = X * 1.612 - Y * 0.203 - Z * 0.302
g = -X * 0.509 + Y * 1.412 + Z * 0.066
b = X * 0.026 - Y * 0.072 + Z * 0.962
r = (if r <= 0.0031308 then 12.92 * r else (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055)
g = (if g <= 0.0031308 then 12.92 * g else (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055)
b = (if b <= 0.0031308 then 12.92 * b else (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055)
cap = (x) -> Math.max 0, Math.min(1, x)Helper to convert RGB to hex.
rgbhex = (v) ->
v = Math.round(v * 255)
s = "0" + v.toString(16)
return s.substr -2Cap and transform RGB values.
r = rgbhex cap r
g = rgbhex cap g
b = rgbhex cap bConvert RGB to hex and return result.
return "##{r}#{g}#{b}"Utils.getInstance = ->
@instance = new Utils() if not @instance?
return @instance
module.exports = exports = Utils.getInstance()