UNPKG

citizen

Version:

Node.js MVC web application framework. Includes routing, serving, caching, session management, and other helpful tools.

206 lines (177 loc) 6.78 kB
// core framework functions that might also be of use in the app // node import fs from 'node:fs' import http from 'node:http' import util from 'node:util' const copy = (object) => { var objectCopy if ( !object || typeof object === 'number' || typeof object === 'string' || typeof object === 'boolean' || typeof object === 'symbol' || typeof object === 'function' || object.constructor === Date || object._onTimeout ) { // Node returns typeof === 'object' for setTimeout() objectCopy = object } else if ( Array.isArray(object) ) { objectCopy = [] object.forEach( function (item, index) { objectCopy[index] = copy(item) }) } else if ( object.constructor === Object || Object(object) === object ) { objectCopy = Object.assign({}, object) for ( var property in objectCopy ) { objectCopy[property] = copy(object[property]) } } else { objectCopy = object } return objectCopy } const extend = (original, extension) => { var mergedObject = Object.assign({}, original) || {} extension = Object.assign({}, extension) || {} Object.keys(extension).forEach( item => { if ( extension[item] && extension[item].constructor === Object ) { mergedObject[item] = extend(mergedObject[item], extension[item]) } else { mergedObject[item] = copy(extension[item]) } }) return mergedObject } const log = (options) => { let type = options.type || 'debug', toConsole = options.console || CTZN.config.citizen.mode === 'development', toFile = options.file || ( type === 'access' && CTZN.config.citizen.logs.access ) || ( type === 'error:client' && CTZN.config.citizen.logs.error?.client ) || ( type === 'error:server' && CTZN.config.citizen.logs.error?.server ) || ( type === 'debug' && CTZN.config.citizen.logs.debug ), depth = options.depth || CTZN.config.citizen.development.debug.depth, showHidden = options.showHidden || CTZN.config.citizen.development.debug.showHidden if ( toConsole ) { let dividerTop = '' if ( options.divider && options.divider.top ) { // Default divider if ( options.divider.top === true ) { dividerTop = '\n----------------------------------------------------------------------\n\n' // Custom divider } else { dividerTop = options.divider.top } } let label = '', time = new Date().toISOString() if ( options.label ) { if ( options.timestamp === false ) { label = '\x1b[1m' + options.label + '\x1b[0m' } else { if ( type === 'error' ) { label = '[' + time + '] ' + options.label } else { label = '[' + time + '] ' + '\x1b[1m' + options.label + '\x1b[0m' } } } else if ( options.timestamp !== false ) { label = '[' + time + '] ' } let content = '\n' if ( options.content ) { switch ( typeof options.content ) { case 'string': if ( options.content.length ) { content = '\n' + options.content + '\n' } else { content = '\n(empty string)\n' } break case 'number': content = '\n' + options.content + '\n' break default: content = '\n' + util.inspect(options.content, { depth: depth, colors: true, showHidden: showHidden }) + '\n' break } } let dividerBottom = '' if ( options.divider && options.divider.bottom ) { // Default divider if ( options.divider.bottom === true ) { dividerBottom = '\n----------------------------------------------------------------------\n\n' // Custom divider } else { dividerBottom = options.divider.bottom } } let log = dividerTop + label + content if ( type === 'error' ) { log = '\x1b[31m' + log + dividerBottom + '\x1b[0m' } else { log += dividerBottom } console.log(log) } if ( toFile ) { let dividerTop = '' if ( options.divider && options.divider.top ) { // Default divider if ( options.divider.top === true ) { dividerTop = '\n----------------------------------------------------------------------\n\n' // Custom divider } else { dividerTop = options.divider.top } } let label = '', time = new Date().toISOString() if ( options.label ) { if ( options.timestamp === false ) { label = options.label } else { label = '[' + time + '] ' + options.label } } else if ( options.timestamp !== false ) { label = '[' + time + '] ' } let content = '\n' if ( options.content ) { switch ( typeof options.content ) { case 'string': if ( options.content.length ) { content = '\n ' + options.content + '\n' } else { content = '\n(empty string)\n' } break case 'number': content = '\n ' + options.content + '\n' break default: content = '\n ' + util.inspect(options.content, { depth: depth, colors: false, showHidden: showHidden }) + '\n' break } } let dividerBottom = '' if ( options.divider && options.divider.bottom ) { // Default divider if ( options.divider.bottom === true ) { dividerBottom = '\n----------------------------------------------------------------------\n\n' // Custom divider } else { dividerBottom = options.divider.bottom } } let file = options.file || ( type === 'access' ? 'access.log' : 'error.log' ), log = dividerTop + label + content + dividerBottom fs.appendFile(CTZN.config.citizen.directories.logs + '/' + file, log, function (err) { if ( err ) { switch ( err.code ) { case 'ENOENT': console.log('Error in app.log(): Unable to write to the log file because the specified log file path doesn\'t exist:\n\n') console.log(' ' + CTZN.config.citizen.directories.logs + '\n\n') console.log('Please set a valid file path in your citizen configuration.') break default: console.log('Error in app.log(): There was a problem writing to the log file:\n\n') console.log(' ' + CTZN.config.citizen.directories.logs + '/' + file + '\n\n') console.log(err) break } } }) } } const serverLogLabel = (statusCode, params, request) => { return statusCode + ' ' + http.STATUS_CODES[statusCode] + ' ' + request.method + ' ' + params.route.url + ' ' + request.remoteAddress + ' "' + request.headers['user-agent'] + '"' } export default { copy, extend, log, serverLogLabel } export { log }