mirror of
https://github.com/typicode/json-server.git
synced 2025-07-29 13:14:12 +08:00
248 lines
6.7 KiB
JavaScript
248 lines
6.7 KiB
JavaScript
const fs = require('fs')
|
|
const path = require('path')
|
|
const jph = require('json-parse-helpfulerror')
|
|
const _ = require('lodash')
|
|
const chalk = require('chalk')
|
|
const enableDestroy = require('server-destroy')
|
|
const pause = require('connect-pause')
|
|
const is = require('./utils/is')
|
|
const load = require('./utils/load')
|
|
const jsonServer = require('../server')
|
|
|
|
function prettyPrint(argv, object, rules) {
|
|
const root = `http://${argv.host}:${argv.port}`
|
|
|
|
console.log()
|
|
console.log(chalk.bold(' Resources'))
|
|
for (const prop in object) {
|
|
console.log(` ${root}/${prop}`)
|
|
}
|
|
|
|
if (rules) {
|
|
console.log()
|
|
console.log(chalk.bold(' Other routes'))
|
|
for (const rule in rules) {
|
|
console.log(` ${rule} -> ${rules[rule]}`)
|
|
}
|
|
}
|
|
|
|
console.log()
|
|
console.log(chalk.bold(' Home'))
|
|
console.log(` ${root}`)
|
|
console.log()
|
|
}
|
|
|
|
function createApp(db, routes, middlewares, argv) {
|
|
const app = jsonServer.create()
|
|
|
|
const { foreignKeySuffix } = argv
|
|
|
|
const router = jsonServer.router(
|
|
db,
|
|
foreignKeySuffix ? { foreignKeySuffix } : undefined,
|
|
)
|
|
|
|
const defaultsOpts = {
|
|
logger: !argv.quiet,
|
|
readOnly: argv.readOnly,
|
|
noCors: argv.noCors,
|
|
noGzip: argv.noGzip,
|
|
bodyParser: true,
|
|
}
|
|
|
|
if (argv.static) {
|
|
defaultsOpts.static = path.join(process.cwd(), argv.static)
|
|
}
|
|
|
|
const defaults = jsonServer.defaults(defaultsOpts)
|
|
app.use(defaults)
|
|
|
|
if (routes) {
|
|
const rewriter = jsonServer.rewriter(routes)
|
|
app.use(rewriter)
|
|
}
|
|
|
|
if (middlewares) {
|
|
app.use(middlewares)
|
|
}
|
|
|
|
if (argv.delay) {
|
|
app.use(pause(argv.delay))
|
|
}
|
|
|
|
router.db._.id = argv.id
|
|
app.db = router.db
|
|
app.use(router)
|
|
|
|
return app
|
|
}
|
|
|
|
module.exports = function (argv) {
|
|
const source = argv._[0]
|
|
let app
|
|
let server
|
|
|
|
if (!fs.existsSync(argv.snapshots)) {
|
|
console.log(`Error: snapshots directory ${argv.snapshots} doesn't exist`)
|
|
process.exit(1)
|
|
}
|
|
|
|
// noop log fn
|
|
if (argv.quiet) {
|
|
console.log = () => {}
|
|
}
|
|
|
|
console.log()
|
|
console.log(chalk.cyan(' \\{^_^}/ hi!'))
|
|
|
|
function start(cb) {
|
|
console.log()
|
|
|
|
console.log(chalk.gray(' Loading', source))
|
|
|
|
server = undefined
|
|
|
|
// create db and load object, JSON file, JS or HTTP database
|
|
return load(source).then((db) => {
|
|
// Load additional routes
|
|
let routes
|
|
if (argv.routes) {
|
|
console.log(chalk.gray(' Loading', argv.routes))
|
|
routes = JSON.parse(fs.readFileSync(argv.routes))
|
|
}
|
|
|
|
// Load middlewares
|
|
let middlewares
|
|
if (argv.middlewares) {
|
|
middlewares = argv.middlewares.map(function (m) {
|
|
console.log(chalk.gray(' Loading', m))
|
|
return require(path.resolve(m))
|
|
})
|
|
}
|
|
|
|
// Done
|
|
console.log(chalk.gray(' Done'))
|
|
|
|
// Create app and server
|
|
app = createApp(db, routes, middlewares, argv)
|
|
server = app.listen(argv.port, argv.host)
|
|
|
|
// Enhance with a destroy function
|
|
enableDestroy(server)
|
|
|
|
// Display server informations
|
|
prettyPrint(argv, db.getState(), routes)
|
|
|
|
// Catch and handle any error occurring in the server process
|
|
process.on('uncaughtException', (error) => {
|
|
if (error.errno === 'EADDRINUSE')
|
|
console.log(
|
|
chalk.red(
|
|
`Cannot bind to the port ${error.port}. Please specify another port number either through --port argument or through the json-server.json configuration file`,
|
|
),
|
|
)
|
|
else console.log('Some error occurred', error)
|
|
process.exit(1)
|
|
})
|
|
})
|
|
}
|
|
|
|
// Start server
|
|
start()
|
|
.then(() => {
|
|
// Snapshot
|
|
console.log(
|
|
chalk.gray(
|
|
' Type s + enter at any time to create a snapshot of the database',
|
|
),
|
|
)
|
|
|
|
// Support nohup
|
|
// https://github.com/typicode/json-server/issues/221
|
|
process.stdin.on('error', () => {
|
|
console.log(` Error, can't read from stdin`)
|
|
console.log(` Creating a snapshot from the CLI won't be possible`)
|
|
})
|
|
process.stdin.setEncoding('utf8')
|
|
process.stdin.on('data', (chunk) => {
|
|
if (chunk.trim().toLowerCase() === 's') {
|
|
const filename = `db-${Date.now()}.json`
|
|
const file = path.join(argv.snapshots, filename)
|
|
const state = app.db.getState()
|
|
fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8')
|
|
console.log(
|
|
` Saved snapshot to ${path.relative(process.cwd(), file)}\n`,
|
|
)
|
|
}
|
|
})
|
|
|
|
// Watch files
|
|
if (argv.watch) {
|
|
console.log(chalk.gray(' Watching...'))
|
|
console.log()
|
|
const source = argv._[0]
|
|
|
|
// Can't watch URL
|
|
if (is.URL(source)) throw new Error("Can't watch URL")
|
|
|
|
// Watch .js or .json file
|
|
// Since lowdb uses atomic writing, directory is watched instead of file
|
|
const watchedDir = path.dirname(source)
|
|
let readError = false
|
|
fs.watch(watchedDir, (event, file) => {
|
|
// https://github.com/typicode/json-server/issues/420
|
|
// file can be null
|
|
if (file) {
|
|
const watchedFile = path.resolve(watchedDir, file)
|
|
if (watchedFile === path.resolve(source)) {
|
|
if (is.FILE(watchedFile)) {
|
|
let obj
|
|
try {
|
|
obj = jph.parse(fs.readFileSync(watchedFile))
|
|
if (readError) {
|
|
console.log(chalk.green(` Read error has been fixed :)`))
|
|
readError = false
|
|
}
|
|
} catch (e) {
|
|
readError = true
|
|
console.log(chalk.red(` Error reading ${watchedFile}`))
|
|
console.error(e.message)
|
|
return
|
|
}
|
|
|
|
// Compare .json file content with in memory database
|
|
const isDatabaseDifferent = !_.isEqual(obj, app.db.getState())
|
|
if (isDatabaseDifferent) {
|
|
console.log(
|
|
chalk.gray(` ${source} has changed, reloading...`),
|
|
)
|
|
server && server.destroy(() => start())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// Watch routes
|
|
if (argv.routes) {
|
|
const watchedDir = path.dirname(argv.routes)
|
|
fs.watch(watchedDir, (event, file) => {
|
|
if (file) {
|
|
const watchedFile = path.resolve(watchedDir, file)
|
|
if (watchedFile === path.resolve(argv.routes)) {
|
|
console.log(
|
|
chalk.gray(` ${argv.routes} has changed, reloading...`),
|
|
)
|
|
server && server.destroy(() => start())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.log(err)
|
|
process.exit(1)
|
|
})
|
|
}
|