Comparing version 4.0.4 to 5.0.0
@@ -7,3 +7,3 @@ { | ||
"name": "ctl", | ||
"version": "4.0.4", | ||
"version": "5.0.0", | ||
"dependencies": { | ||
@@ -16,3 +16,2 @@ "app-module-path": "^2.2.0", | ||
"express": "^4.16.4", | ||
"fs-extra": "^7.0.1", | ||
"lodash.merge": "^4.6.1", | ||
@@ -19,0 +18,0 @@ "nunjucks": "^3.1.7" |
@@ -0,14 +1,14 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const mods = require('app-module-path'); | ||
const path = require('path'); | ||
const merge = require('lodash.merge'); | ||
const fs = require('fs-extra'); | ||
const express = require('./services/express'); | ||
const CORE_PLUGINS = { | ||
stage: require('./plugins/stage'), | ||
log: require('./plugins/log'), | ||
config: require('./plugins/config'), | ||
}; | ||
const DEFAULTS = { | ||
plugins: { | ||
stage: require('./plugins/stage'), | ||
log: require('./plugins/log'), | ||
config: require('./plugins/config'), | ||
}, | ||
autoStart: true, | ||
@@ -18,8 +18,3 @@ service: express, | ||
src: '/src', | ||
controllers: '/controllers', | ||
lifecycle: '/lifecycle', | ||
views: '/views', | ||
models: '/models', | ||
static: '/static', | ||
staticUrl: '/static', | ||
}; | ||
@@ -29,122 +24,72 @@ | ||
async init(opts = {}) { | ||
this.setOptions(opts); | ||
this.setDirs(); | ||
this.setModulePath(); | ||
await this.setupPlugins(); | ||
await this.runLifecycleEvent('before'); | ||
await this.loadModels(); | ||
await this.run(); | ||
return this; | ||
} | ||
async run() { | ||
this.options = merge(DEFAULTS, opts); | ||
await this.loadCore(); | ||
const config = this.config(); | ||
if (config.script) { | ||
await this.runScript(config.script); | ||
if (config.scriptOnly) { | ||
process.exit(0); | ||
} | ||
const service = config.service || this.options.service; | ||
if (config.plugins) { | ||
await this.loadPlugins(config.plugins); | ||
} | ||
await this.runServer(); | ||
this.setLifecycle(config.lifecycle || this.options.lifecycle); | ||
await this.run('before'); | ||
const app = service.create(this); | ||
await this.run('startup', app); | ||
if (this.options.autoStart || config.autoStart === false) { | ||
await this.start(service, app); | ||
return true; | ||
} | ||
return (async () => this.start(service, app)); | ||
} | ||
async runServer() { | ||
this.setupServer(); | ||
await this.runLifecycleEvent('startup'); | ||
await this.setupRoutes(); | ||
if (this.options.autoStart) { | ||
await this.startServer(); | ||
async run(event, ...args) { | ||
if (this.lifecycle[event]) { | ||
await this.lifecycle[event](...args); | ||
} | ||
} | ||
async runScript(file) { | ||
const log = this.log('script'); | ||
const script = path.resolve(`${this.dirs.root}/${file}`); | ||
const relative = path.relative(process.cwd(), script); | ||
log.info(`Running "${relative}" script...`); | ||
const fn = require(script); | ||
await fn(); | ||
log.info(`Finished running "${relative}".`); | ||
async loadCore() { | ||
this.setDirs(); | ||
this.setModulePath(); | ||
await this.loadPlugins(CORE_PLUGINS); | ||
} | ||
setOptions(opts) { | ||
this.options = merge(DEFAULTS, opts); | ||
async start(service, app) { | ||
service.start(app, this); | ||
await this.run('after', app); | ||
} | ||
setModulePath() { | ||
mods.addPath(this.dirs.src); | ||
} | ||
setDirs() { | ||
const root = this.options.root || process.cwd(); | ||
const src = `${root}${this.options.src}`; | ||
this.dirs = { | ||
root, src, | ||
lifecycle: `${src}${this.options.lifecycle}`, | ||
controllers: `${src}${this.options.controllers}`, | ||
models: `${src}${this.options.models}`, | ||
views: `${src}${this.options.views}`, | ||
static: `${src}${this.options.static}`, | ||
} | ||
this.dirs = { root, src }; | ||
} | ||
async runLifecycleEvent(evt) { | ||
const log = this.log('lifecycle'); | ||
const file = path.resolve(`${this.dirs.lifecycle}/${evt}.js`); | ||
const exists = fs.existsSync(file); | ||
if (!exists) return log.warn('Missing lifecycle event:', evt, file); | ||
try { | ||
const fn = require(file); | ||
await fn(); | ||
} catch (e) { | ||
log.error('FATAL:', evt, e); | ||
process.exit(1); | ||
} | ||
setModulePath() { | ||
mods.addPath(this.dirs.src); | ||
} | ||
async startServer() { | ||
const config = this.config(); | ||
this.server = await this.options.service.run({ | ||
app: this.app, | ||
port: config.server.port, | ||
host: config.server.host, | ||
log: this.log('server'), | ||
}); | ||
await this.runLifecycleEvent('after'); | ||
} | ||
async loadModels() { | ||
let contents = []; | ||
try { | ||
contents = await fs.readdir(this.dirs.models); | ||
} catch (e) { | ||
if (e.code !== 'ENOENT') throw e; | ||
setLifecycle(lc) { | ||
if (!lc) throw new Error('missing_lifecycle'); | ||
if (typeof lc === 'object') { | ||
this.lifecycle = { | ||
before: lc.before || (async () => {}), | ||
startup: lc.startup || (async () => {}), | ||
after: lc.after || (async () => {}), | ||
}; | ||
return true; | ||
} | ||
for (let j = 0; j < contents.length; j += 1) { | ||
const ext = path.extname(contents[j]); | ||
if (ext !== '.js') continue; | ||
const file = path.resolve(`${this.dirs.models}/${contents[j]}`); | ||
const model = require(file); | ||
if (model.load) { | ||
await model.load(); | ||
} | ||
if (typeof lc === 'string') { | ||
const lcDir = `${this.dirs.src}${lc}`; | ||
this.lifecycle = { | ||
before: this.loadLifecycleFile(lcDir, 'before'), | ||
startup: this.loadLifecycleFile(lcDir, 'startup'), | ||
after: this.loadLifecycleFile(lcDir, 'after'), | ||
}; | ||
} | ||
} | ||
setupServer() { | ||
this.app = this.options.service.create(this); | ||
loadLifecycleFile(dir, evt) { | ||
const log = this.log('lifecycle'); | ||
const file = path.resolve(`${dir}/${evt}.js`); | ||
const exists = fs.existsSync(file); | ||
if (!exists) return log.warn('Missing lifecycle [%s] (looked in %s).', evt, path.relative(this.dirs.root, dir)); | ||
return require(file); | ||
} | ||
async setupRoutes() { | ||
let contents = []; | ||
try { | ||
contents = await fs.readdir(this.dirs.controllers); | ||
} catch (e) { | ||
if (e.code !== 'ENOENT') throw e; | ||
} | ||
for (let j = 0; j < contents.length; j += 1) { | ||
const ext = path.extname(contents[j]); | ||
if (ext !== '.js') continue; | ||
const file = path.resolve(`${this.dirs.controllers}/${contents[j]}`); | ||
const ctl = require(file); | ||
if (ctl.bind) { | ||
await ctl.bind(this.app); | ||
} | ||
} | ||
} | ||
async setupPlugins() { | ||
const log = require('better-logs')('plugins'); | ||
const plugins = Object.keys(this.options.plugins); | ||
async loadPlugins(pluginMap) { | ||
const plugins = Object.keys(pluginMap); | ||
for (let i = 0; i < plugins.length; i += 1) { | ||
const name = plugins[i]; | ||
const plugin = this.options.plugins[name]; | ||
const plugin = pluginMap[name]; | ||
if (this[name]) throw new Error(`plugin_${name}_already_declared`); | ||
@@ -151,0 +96,0 @@ this[name] = await plugin(this); |
@@ -8,8 +8,8 @@ const http = require('http'); | ||
function create(ctl) { | ||
const config = ctl.config(); | ||
const log = ctl.log('server'); | ||
const config = ctl.config(); | ||
const app = express(); | ||
app.set('x-powered-by', false); | ||
if (config.server.static.startsWith('/')) { | ||
app.use(config.server.static, express.static(ctl.dirs.static)); | ||
if (config.server.static) { | ||
app.use(config.server.static, express.static(config.server.staticDir)); | ||
} | ||
@@ -20,5 +20,5 @@ app.use(compress()); | ||
app.set('view engine', 'html'); | ||
const env = nunjucks.configure(ctl.dirs.views, { | ||
const env = nunjucks.configure(config.server.viewsDir, { | ||
express: app, | ||
noCache: (ctl.stage !== 'production'), | ||
noCache: (ctl.stage() !== 'production'), | ||
}); | ||
@@ -40,7 +40,15 @@ | ||
async function run({ app, port, host, log }) { | ||
async function start(app, ctl) { | ||
const config = ctl.config(); | ||
const log = ctl.log('server'); | ||
const { port, host } = config.server; | ||
const server = http.createServer(app); | ||
server.on('listening', () => { | ||
const realHost = server.address().address; | ||
const realPort = server.address().port; | ||
const addr = server.address(); | ||
let realHost = '<unknown-host>'; | ||
let realPort = '<unknown-port>'; | ||
if (addr) { | ||
realHost = addr.address; | ||
realPort = addr.port; | ||
} | ||
log.info('Server started listening at http://%s:%s', realHost, realPort); | ||
@@ -53,3 +61,3 @@ }); | ||
create, | ||
run, | ||
start, | ||
}; |
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
8
5
67733
222
- Removedfs-extra@^7.0.1
- Removedfs-extra@7.0.1(transitive)
- Removedjsonfile@4.0.0(transitive)
- Removeduniversalify@0.1.2(transitive)