backend: router hot-reloading

This commit is contained in:
41666 2019-02-25 00:47:08 -06:00
parent 11cf1d4805
commit 959a2eb46f
4 changed files with 53 additions and 20 deletions

View file

@ -3,16 +3,17 @@ const Sequelize = require('sequelize')
const fetchModels = require('./models') const fetchModels = require('./models')
const fetchApis = require('./api') const fetchApis = require('./api')
const Next = require('next') const Next = require('next')
const betterRouter = require('koa-better-router')
class Roleypoly { class Roleypoly {
constructor (router, io, app) { constructor (io, app) {
this.router = router
this.io = io this.io = io
this.ctx = {} this.ctx = {}
this.ctx.config = { this.ctx.config = {
appUrl: process.env.APP_URL appUrl: process.env.APP_URL,
dev: process.env.NODE_ENV !== 'production',
hotReload: process.env.NO_HOT_RELOAD !== '1'
} }
this.ctx.io = io this.ctx.io = io
@ -52,10 +53,11 @@ class Roleypoly {
this.ctx.P = new (require('./services/presentation'))(this.ctx) this.ctx.P = new (require('./services/presentation'))(this.ctx)
} }
async mountRoutes () { async loadRoutes (forceClear = false) {
await this.ctx.ui.prepare() await this.ctx.ui.prepare()
fetchApis(this.router, this.ctx) this.router = betterRouter().loadMethods()
fetchApis(this.router, this.ctx, { forceClear })
// after routing, add the * for ui handler // after routing, add the * for ui handler
this.router.get('*', async ctx => { this.router.get('*', async ctx => {
@ -63,7 +65,31 @@ class Roleypoly {
ctx.respond = false ctx.respond = false
}) })
this.__app.use(this.router.middleware()) return this.router.middleware()
}
async mountRoutes () {
let mw = await this.loadRoutes()
if (this.ctx.config.dev && this.ctx.config.hotReload) {
// hot-reloading system
log.info('API hot-reloading is active.')
const chokidar = require('chokidar')
let hotMiddleware = mw
this.__apiWatcher = chokidar.watch('api/**')
this.__apiWatcher.on('all', async (path) => {
log.info('reloading APIs...', path)
hotMiddleware = await this.loadRoutes(true)
})
// custom passthrough so we use a specially scoped middleware.
mw = (ctx, next) => {
return hotMiddleware(ctx, next)
}
}
this.__app.use(mw)
} }
} }

View file

@ -3,7 +3,7 @@ const glob = require('glob')
const PROD = process.env.NODE_ENV === 'production' const PROD = process.env.NODE_ENV === 'production'
module.exports = async (router, ctx) => { module.exports = async (router, ctx, { forceClear = false } = {}) => {
const apis = glob.sync(`./api/**/!(index).js`) const apis = glob.sync(`./api/**/!(index).js`)
log.debug('found apis', apis) log.debug('found apis', apis)
@ -14,7 +14,11 @@ module.exports = async (router, ctx) => {
} }
log.debug(`mounting ${a}`) log.debug(`mounting ${a}`)
try { try {
require(a.replace('api/', ''))(router, ctx) const pathname = a.replace('api/', '')
if (forceClear) {
delete require.cache[require.resolve(pathname)]
}
require(pathname)(router, ctx)
} catch (e) { } catch (e) {
log.error(`couldn't mount ${a}`, e) log.error(`couldn't mount ${a}`, e)
} }

View file

@ -5,9 +5,6 @@ const http = require('http')
const Koa = require('koa') const Koa = require('koa')
const app = new Koa() const app = new Koa()
const _io = require('socket.io') const _io = require('socket.io')
const fs = require('fs')
const path = require('path')
const router = require('koa-better-router')().loadMethods()
const Roleypoly = require('./Roleypoly') const Roleypoly = require('./Roleypoly')
const ksuid = require('ksuid') const ksuid = require('ksuid')
@ -20,7 +17,7 @@ Array.prototype.areduce = async function (predicate, acc = []) { // eslint-disab
return acc return acc
} }
Array.prototype.filterNot = Array.prototype.filterNot || function (predicate) { Array.prototype.filterNot = Array.prototype.filterNot || function (predicate) { // eslint-disable-line
return this.filter(v => !predicate(v)) return this.filter(v => !predicate(v))
} }
@ -28,9 +25,7 @@ Array.prototype.filterNot = Array.prototype.filterNot || function (predicate) {
const server = http.createServer(app.callback()) const server = http.createServer(app.callback())
const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' }) const io = _io(server, { transports: ['websocket'], path: '/api/socket.io' })
const M = new Roleypoly(io, app) // eslint-disable-line no-unused-vars
const M = new Roleypoly(router, io, app) // eslint-disable-line no-unused-vars
app.keys = [ process.env.APP_KEY ] app.keys = [ process.env.APP_KEY ]

View file

@ -3,12 +3,13 @@
"version": "2.0.0", "version": "2.0.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "pm2 start index.js", "start": "node index.js",
"dev": "pm2 start index.js --watch", "dev": "node index.js",
"build": "next build" "build": "next build ui"
}, },
"dependencies": { "dependencies": {
"@discordjs/uws": "^11.149.1", "@discordjs/uws": "^11.149.1",
"@primer/components": "^10.0.1",
"chalk": "^2.4.2", "chalk": "^2.4.2",
"discord.js": "^11.4.2", "discord.js": "^11.4.2",
"dotenv": "^6.2.0", "dotenv": "^6.2.0",
@ -32,5 +33,12 @@
"socket.io": "^2.2.0", "socket.io": "^2.2.0",
"superagent": "^4.1.0", "superagent": "^4.1.0",
"uuid": "^3.3.2" "uuid": "^3.3.2"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"chokidar": "^2.1.2",
"flow-type": "^1.0.1",
"standard": "^12.0.1"
} }
} }