forked from Vyacheslav/internet-programming
fix
This commit is contained in:
5
lab3/node_modules/json-server/lib/cli/bin.js
generated
vendored
Normal file
5
lab3/node_modules/json-server/lib/cli/bin.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
require('please-upgrade-node')(require('../../package.json'));
|
||||
require('./')();
|
||||
77
lab3/node_modules/json-server/lib/cli/index.js
generated
vendored
Normal file
77
lab3/node_modules/json-server/lib/cli/index.js
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
"use strict";
|
||||
|
||||
const yargs = require('yargs');
|
||||
const run = require('./run');
|
||||
const pkg = require('../../package.json');
|
||||
module.exports = function () {
|
||||
const argv = yargs.config('config').usage('$0 [options] <source>').options({
|
||||
port: {
|
||||
alias: 'p',
|
||||
description: 'Set port',
|
||||
default: 3000
|
||||
},
|
||||
host: {
|
||||
alias: 'H',
|
||||
description: 'Set host',
|
||||
default: 'localhost'
|
||||
},
|
||||
watch: {
|
||||
alias: 'w',
|
||||
description: 'Watch file(s)'
|
||||
},
|
||||
routes: {
|
||||
alias: 'r',
|
||||
description: 'Path to routes file'
|
||||
},
|
||||
middlewares: {
|
||||
alias: 'm',
|
||||
array: true,
|
||||
description: 'Paths to middleware files'
|
||||
},
|
||||
static: {
|
||||
alias: 's',
|
||||
description: 'Set static files directory'
|
||||
},
|
||||
'read-only': {
|
||||
alias: 'ro',
|
||||
description: 'Allow only GET requests'
|
||||
},
|
||||
'no-cors': {
|
||||
alias: 'nc',
|
||||
description: 'Disable Cross-Origin Resource Sharing'
|
||||
},
|
||||
'no-gzip': {
|
||||
alias: 'ng',
|
||||
description: 'Disable GZIP Content-Encoding'
|
||||
},
|
||||
snapshots: {
|
||||
alias: 'S',
|
||||
description: 'Set snapshots directory',
|
||||
default: '.'
|
||||
},
|
||||
delay: {
|
||||
alias: 'd',
|
||||
description: 'Add delay to responses (ms)'
|
||||
},
|
||||
id: {
|
||||
alias: 'i',
|
||||
description: 'Set database id property (e.g. _id)',
|
||||
default: 'id'
|
||||
},
|
||||
foreignKeySuffix: {
|
||||
alias: 'fks',
|
||||
description: 'Set foreign key suffix (e.g. _id as in post_id)',
|
||||
default: 'Id'
|
||||
},
|
||||
quiet: {
|
||||
alias: 'q',
|
||||
description: 'Suppress log messages from output'
|
||||
},
|
||||
config: {
|
||||
alias: 'c',
|
||||
description: 'Path to config file',
|
||||
default: 'json-server.json'
|
||||
}
|
||||
}).boolean('watch').boolean('read-only').boolean('quiet').boolean('no-cors').boolean('no-gzip').help('help').alias('help', 'h').version(pkg.version).alias('version', 'v').example('$0 db.json', '').example('$0 file.js', '').example('$0 http://example.com/db.json', '').epilog('https://github.com/typicode/json-server').require(1, 'Missing <source> argument').argv;
|
||||
run(argv);
|
||||
};
|
||||
213
lab3/node_modules/json-server/lib/cli/run.js
generated
vendored
Normal file
213
lab3/node_modules/json-server/lib/cli/run.js
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
"use strict";
|
||||
|
||||
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) {
|
||||
// skip printing $schema nodes
|
||||
if (prop === '$schema') continue;
|
||||
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);
|
||||
});
|
||||
};
|
||||
16
lab3/node_modules/json-server/lib/cli/utils/is.js
generated
vendored
Normal file
16
lab3/node_modules/json-server/lib/cli/utils/is.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
FILE,
|
||||
JS,
|
||||
URL
|
||||
};
|
||||
function FILE(s) {
|
||||
return !URL(s) && /\.json$/.test(s);
|
||||
}
|
||||
function JS(s) {
|
||||
return !URL(s) && /\.c?js$/.test(s);
|
||||
}
|
||||
function URL(s) {
|
||||
return /^(http|https):/.test(s);
|
||||
}
|
||||
69
lab3/node_modules/json-server/lib/cli/utils/load.js
generated
vendored
Normal file
69
lab3/node_modules/json-server/lib/cli/utils/load.js
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const low = require('lowdb');
|
||||
const FileAsync = require('lowdb/adapters/FileAsync');
|
||||
const Memory = require('lowdb/adapters/Memory');
|
||||
const is = require('./is');
|
||||
const chalk = require('chalk');
|
||||
const example = {
|
||||
posts: [{
|
||||
id: 1,
|
||||
title: 'json-server',
|
||||
author: 'typicode'
|
||||
}],
|
||||
comments: [{
|
||||
id: 1,
|
||||
body: 'some comment',
|
||||
postId: 1
|
||||
}],
|
||||
profile: {
|
||||
name: 'typicode'
|
||||
}
|
||||
};
|
||||
module.exports = function (source) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (is.FILE(source)) {
|
||||
if (!fs.existsSync(source)) {
|
||||
console.log(chalk.yellow(` Oops, ${source} doesn't seem to exist`));
|
||||
console.log(chalk.yellow(` Creating ${source} with some default data`));
|
||||
console.log();
|
||||
fs.writeFileSync(source, JSON.stringify(example, null, 2));
|
||||
}
|
||||
resolve(low(new FileAsync(source)));
|
||||
} else if (is.URL(source)) {
|
||||
// Normalize the source into a URL object.
|
||||
const sourceUrl = new URL(source);
|
||||
// Pick the client based on the protocol scheme
|
||||
const client = sourceUrl.protocol === 'https:' ? https : http;
|
||||
client.get(sourceUrl, res => {
|
||||
let dbData = '';
|
||||
res.on('data', data => {
|
||||
dbData += data;
|
||||
});
|
||||
res.on('end', () => {
|
||||
resolve(low(new Memory()).setState(JSON.parse(dbData)));
|
||||
});
|
||||
}).on('error', error => {
|
||||
return reject(error);
|
||||
});
|
||||
} else if (is.JS(source)) {
|
||||
// Clear cache
|
||||
const filename = path.resolve(source);
|
||||
delete require.cache[filename];
|
||||
const dataFn = require(filename);
|
||||
if (typeof dataFn !== 'function') {
|
||||
throw new Error('The database is a JavaScript file but the export is not a function.');
|
||||
}
|
||||
|
||||
// Run dataFn to generate data
|
||||
const data = dataFn();
|
||||
resolve(low(new Memory()).setState(data));
|
||||
} else {
|
||||
throw new Error(`Unsupported source ${source}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
9
lab3/node_modules/json-server/lib/server/body-parser.js
generated
vendored
Normal file
9
lab3/node_modules/json-server/lib/server/body-parser.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
const bodyParser = require('body-parser');
|
||||
module.exports = [bodyParser.json({
|
||||
limit: '10mb',
|
||||
extended: false
|
||||
}), bodyParser.urlencoded({
|
||||
extended: false
|
||||
})];
|
||||
73
lab3/node_modules/json-server/lib/server/defaults.js
generated
vendored
Normal file
73
lab3/node_modules/json-server/lib/server/defaults.js
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const express = require('express');
|
||||
const logger = require('morgan');
|
||||
const cors = require('cors');
|
||||
const compression = require('compression');
|
||||
const errorhandler = require('errorhandler');
|
||||
const bodyParser = require('./body-parser');
|
||||
module.exports = function (opts) {
|
||||
const userDir = path.join(process.cwd(), 'public');
|
||||
const defaultDir = path.join(__dirname, '../../public');
|
||||
const staticDir = fs.existsSync(userDir) ? userDir : defaultDir;
|
||||
opts = Object.assign({
|
||||
logger: true,
|
||||
static: staticDir
|
||||
}, opts);
|
||||
const arr = [];
|
||||
|
||||
// Compress all requests
|
||||
if (!opts.noGzip) {
|
||||
arr.push(compression());
|
||||
}
|
||||
|
||||
// Enable CORS for all the requests, including static files
|
||||
if (!opts.noCors) {
|
||||
arr.push(cors({
|
||||
origin: true,
|
||||
credentials: true
|
||||
}));
|
||||
}
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// only use in development
|
||||
arr.push(errorhandler());
|
||||
}
|
||||
|
||||
// Serve static files
|
||||
arr.push(express.static(opts.static));
|
||||
|
||||
// Logger
|
||||
if (opts.logger) {
|
||||
arr.push(logger('dev', {
|
||||
skip: req => process.env.NODE_ENV === 'test' || req.path === '/favicon.ico'
|
||||
}));
|
||||
}
|
||||
|
||||
// No cache for IE
|
||||
// https://support.microsoft.com/en-us/kb/234067
|
||||
arr.push((req, res, next) => {
|
||||
res.header('Cache-Control', 'no-cache');
|
||||
res.header('Pragma', 'no-cache');
|
||||
res.header('Expires', '-1');
|
||||
next();
|
||||
});
|
||||
|
||||
// Read-only
|
||||
if (opts.readOnly) {
|
||||
arr.push((req, res, next) => {
|
||||
if (req.method === 'GET') {
|
||||
next(); // Continue
|
||||
} else {
|
||||
res.sendStatus(403); // Forbidden
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add middlewares
|
||||
if (opts.bodyParser) {
|
||||
arr.push(bodyParser);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
10
lab3/node_modules/json-server/lib/server/index.js
generated
vendored
Normal file
10
lab3/node_modules/json-server/lib/server/index.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const express = require('express');
|
||||
module.exports = {
|
||||
create: () => express().set('json spaces', 2),
|
||||
defaults: require('./defaults'),
|
||||
router: require('./router'),
|
||||
rewriter: require('./rewriter'),
|
||||
bodyParser: require('./body-parser')
|
||||
};
|
||||
76
lab3/node_modules/json-server/lib/server/mixins.js
generated
vendored
Normal file
76
lab3/node_modules/json-server/lib/server/mixins.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
nanoid
|
||||
} = require('nanoid');
|
||||
const pluralize = require('pluralize');
|
||||
module.exports = {
|
||||
getRemovable,
|
||||
createId,
|
||||
deepQuery
|
||||
};
|
||||
|
||||
// Returns document ids that have unsatisfied relations
|
||||
// Example: a comment that references a post that doesn't exist
|
||||
function getRemovable(db, opts) {
|
||||
const _ = this;
|
||||
const removable = [];
|
||||
_.each(db, (coll, collName) => {
|
||||
_.each(coll, doc => {
|
||||
_.each(doc, (value, key) => {
|
||||
if (new RegExp(`${opts.foreignKeySuffix}$`).test(key)) {
|
||||
// Remove foreign key suffix and pluralize it
|
||||
// Example postId -> posts
|
||||
const refName = pluralize.plural(key.replace(new RegExp(`${opts.foreignKeySuffix}$`), ''));
|
||||
// Test if table exists
|
||||
if (db[refName]) {
|
||||
// Test if references is defined in table
|
||||
const ref = _.getById(db[refName], value);
|
||||
if (_.isUndefined(ref)) {
|
||||
removable.push({
|
||||
name: collName,
|
||||
id: doc.id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return removable;
|
||||
}
|
||||
|
||||
// Return incremented id or uuid
|
||||
// Used to override lodash-id's createId with utils.createId
|
||||
function createId(coll) {
|
||||
const _ = this;
|
||||
const idProperty = _.__id();
|
||||
if (_.isEmpty(coll)) {
|
||||
return 1;
|
||||
} else {
|
||||
let id = _(coll).maxBy(idProperty)[idProperty];
|
||||
|
||||
// Increment integer id or generate string id
|
||||
return _.isFinite(id) ? ++id : nanoid(7);
|
||||
}
|
||||
}
|
||||
function deepQuery(value, q) {
|
||||
const _ = this;
|
||||
if (value && q) {
|
||||
if (_.isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
if (_.deepQuery(value[i], q)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (_.isObject(value) && !_.isArray(value)) {
|
||||
for (const k in value) {
|
||||
if (_.deepQuery(value[k], q)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (value.toString().toLowerCase().indexOf(q) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
lab3/node_modules/json-server/lib/server/rewriter.js
generated
vendored
Normal file
14
lab3/node_modules/json-server/lib/server/rewriter.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
const express = require('express');
|
||||
const rewrite = require('express-urlrewrite');
|
||||
module.exports = routes => {
|
||||
const router = express.Router();
|
||||
router.get('/__rules', (req, res) => {
|
||||
res.json(routes);
|
||||
});
|
||||
Object.keys(routes).forEach(key => {
|
||||
router.use(rewrite(key, routes[key]));
|
||||
});
|
||||
return router;
|
||||
};
|
||||
10
lab3/node_modules/json-server/lib/server/router/delay.js
generated
vendored
Normal file
10
lab3/node_modules/json-server/lib/server/router/delay.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const pause = require('connect-pause');
|
||||
module.exports = function delay(req, res, next) {
|
||||
// NOTE: for some reason unknown to me, if the default is 0, the tests seems to add 2 seconds
|
||||
// NOTE: to each test, a default value of 1 does not seem to be effected by that issue
|
||||
const _delay = !isNaN(parseFloat(req.query._delay)) ? parseFloat(req.query._delay) : 1;
|
||||
delete req.query._delay;
|
||||
pause(_delay)(req, res, next);
|
||||
};
|
||||
10
lab3/node_modules/json-server/lib/server/router/get-full-url.js
generated
vendored
Normal file
10
lab3/node_modules/json-server/lib/server/router/get-full-url.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const url = require('url');
|
||||
module.exports = function getFullURL(req) {
|
||||
const root = url.format({
|
||||
protocol: req.protocol,
|
||||
host: req.get('host')
|
||||
});
|
||||
return `${root}${req.originalUrl}`;
|
||||
};
|
||||
91
lab3/node_modules/json-server/lib/server/router/index.js
generated
vendored
Normal file
91
lab3/node_modules/json-server/lib/server/router/index.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
"use strict";
|
||||
|
||||
const express = require('express');
|
||||
const methodOverride = require('method-override');
|
||||
const _ = require('lodash');
|
||||
const lodashId = require('lodash-id');
|
||||
const low = require('lowdb');
|
||||
const Memory = require('lowdb/adapters/Memory');
|
||||
const FileSync = require('lowdb/adapters/FileSync');
|
||||
const bodyParser = require('../body-parser');
|
||||
const validateData = require('./validate-data');
|
||||
const plural = require('./plural');
|
||||
const nested = require('./nested');
|
||||
const singular = require('./singular');
|
||||
const mixins = require('../mixins');
|
||||
module.exports = (db, opts) => {
|
||||
opts = Object.assign({
|
||||
foreignKeySuffix: 'Id',
|
||||
_isFake: false
|
||||
}, opts);
|
||||
if (typeof db === 'string') {
|
||||
db = low(new FileSync(db));
|
||||
} else if (!_.has(db, '__chain__') || !_.has(db, '__wrapped__')) {
|
||||
db = low(new Memory()).setState(db);
|
||||
}
|
||||
|
||||
// Create router
|
||||
const router = express.Router();
|
||||
|
||||
// Add middlewares
|
||||
router.use(methodOverride());
|
||||
router.use(bodyParser);
|
||||
validateData(db.getState());
|
||||
|
||||
// Add lodash-id methods to db
|
||||
db._.mixin(lodashId);
|
||||
|
||||
// Add specific mixins
|
||||
db._.mixin(mixins);
|
||||
|
||||
// Expose database
|
||||
router.db = db;
|
||||
|
||||
// Expose render
|
||||
router.render = (req, res) => {
|
||||
res.jsonp(res.locals.data);
|
||||
};
|
||||
|
||||
// GET /db
|
||||
router.get('/db', (req, res) => {
|
||||
res.jsonp(db.getState());
|
||||
});
|
||||
|
||||
// Handle /:parent/:parentId/:resource
|
||||
router.use(nested(opts));
|
||||
|
||||
// Create routes
|
||||
db.forEach((value, key) => {
|
||||
if (key === '$schema') {
|
||||
// ignore $schema
|
||||
return;
|
||||
}
|
||||
if (_.isPlainObject(value)) {
|
||||
router.use(`/${key}`, singular(db, key, opts));
|
||||
return;
|
||||
}
|
||||
if (_.isArray(value)) {
|
||||
router.use(`/${key}`, plural(db, key, opts));
|
||||
return;
|
||||
}
|
||||
const sourceMessage = '';
|
||||
// if (!_.isObject(source)) {
|
||||
// sourceMessage = `in ${source}`
|
||||
// }
|
||||
|
||||
const msg = `Type of "${key}" (${typeof value}) ${sourceMessage} is not supported. ` + `Use objects or arrays of objects.`;
|
||||
throw new Error(msg);
|
||||
}).value();
|
||||
router.use((req, res) => {
|
||||
if (!res.locals.data) {
|
||||
res.status(404);
|
||||
res.locals.data = {};
|
||||
}
|
||||
router.render(req, res);
|
||||
});
|
||||
router.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).send(err.stack);
|
||||
});
|
||||
return router;
|
||||
};
|
||||
26
lab3/node_modules/json-server/lib/server/router/nested.js
generated
vendored
Normal file
26
lab3/node_modules/json-server/lib/server/router/nested.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
const express = require('express');
|
||||
const pluralize = require('pluralize');
|
||||
const delay = require('./delay');
|
||||
module.exports = opts => {
|
||||
const router = express.Router();
|
||||
router.use(delay);
|
||||
|
||||
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request query
|
||||
function get(req, res, next) {
|
||||
const prop = pluralize.singular(req.params.resource);
|
||||
req.query[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
|
||||
req.url = `/${req.params.nested}`;
|
||||
next();
|
||||
}
|
||||
|
||||
// Rewrite URL (/:resource/:id/:nested -> /:nested) and request body
|
||||
function post(req, res, next) {
|
||||
const prop = pluralize.singular(req.params.resource);
|
||||
req.body[`${prop}${opts.foreignKeySuffix}`] = req.params.id;
|
||||
req.url = `/${req.params.nested}`;
|
||||
next();
|
||||
}
|
||||
return router.get('/:resource/:id/:nested', get).post('/:resource/:id/:nested', post);
|
||||
};
|
||||
273
lab3/node_modules/json-server/lib/server/router/plural.js
generated
vendored
Normal file
273
lab3/node_modules/json-server/lib/server/router/plural.js
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
"use strict";
|
||||
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
||||
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
||||
const express = require('express');
|
||||
const _ = require('lodash');
|
||||
const pluralize = require('pluralize');
|
||||
const write = require('./write');
|
||||
const getFullURL = require('./get-full-url');
|
||||
const utils = require('../utils');
|
||||
const delay = require('./delay');
|
||||
module.exports = (db, name, opts) => {
|
||||
// Create router
|
||||
const router = express.Router();
|
||||
router.use(delay);
|
||||
|
||||
// Embed function used in GET /name and GET /name/id
|
||||
function embed(resource, e) {
|
||||
e && [].concat(e).forEach(externalResource => {
|
||||
if (db.get(externalResource).value) {
|
||||
const query = {};
|
||||
const singularResource = pluralize.singular(name);
|
||||
query[`${singularResource}${opts.foreignKeySuffix}`] = resource.id;
|
||||
resource[externalResource] = db.get(externalResource).filter(query).value();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Expand function used in GET /name and GET /name/id
|
||||
function expand(resource, e) {
|
||||
e && [].concat(e).forEach(innerResource => {
|
||||
const plural = pluralize(innerResource);
|
||||
if (db.get(plural).value()) {
|
||||
const prop = `${innerResource}${opts.foreignKeySuffix}`;
|
||||
resource[innerResource] = db.get(plural).getById(resource[prop]).value();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// GET /name
|
||||
// GET /name?q=
|
||||
// GET /name?attr=&attr=
|
||||
// GET /name?_end=&
|
||||
// GET /name?_start=&_end=&
|
||||
// GET /name?_embed=&_expand=
|
||||
function list(req, res, next) {
|
||||
// Resource chain
|
||||
let chain = db.get(name);
|
||||
|
||||
// Remove q, _start, _end, ... from req.query to avoid filtering using those
|
||||
// parameters
|
||||
let q = req.query.q;
|
||||
let _start = req.query._start;
|
||||
let _end = req.query._end;
|
||||
let _page = req.query._page;
|
||||
const _sort = req.query._sort;
|
||||
const _order = req.query._order;
|
||||
let _limit = req.query._limit;
|
||||
const _embed = req.query._embed;
|
||||
const _expand = req.query._expand;
|
||||
delete req.query.q;
|
||||
delete req.query._start;
|
||||
delete req.query._end;
|
||||
delete req.query._sort;
|
||||
delete req.query._order;
|
||||
delete req.query._limit;
|
||||
delete req.query._embed;
|
||||
delete req.query._expand;
|
||||
|
||||
// Automatically delete query parameters that can't be found
|
||||
// in the database
|
||||
Object.keys(req.query).forEach(query => {
|
||||
const arr = db.get(name).value();
|
||||
for (const i in arr) {
|
||||
if (_.has(arr[i], query) || query === 'callback' || query === '_' || /_lte$/.test(query) || /_gte$/.test(query) || /_ne$/.test(query) || /_like$/.test(query)) return;
|
||||
}
|
||||
delete req.query[query];
|
||||
});
|
||||
if (q) {
|
||||
// Full-text search
|
||||
if (Array.isArray(q)) {
|
||||
q = q[0];
|
||||
}
|
||||
q = q.toLowerCase();
|
||||
chain = chain.filter(obj => {
|
||||
for (const key in obj) {
|
||||
const value = obj[key];
|
||||
if (db._.deepQuery(value, q)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
Object.keys(req.query).forEach(key => {
|
||||
// Don't take into account JSONP query parameters
|
||||
// jQuery adds a '_' query parameter too
|
||||
if (key !== 'callback' && key !== '_') {
|
||||
// Always use an array, in case req.query is an array
|
||||
const arr = [].concat(req.query[key]);
|
||||
const isDifferent = /_ne$/.test(key);
|
||||
const isRange = /_lte$/.test(key) || /_gte$/.test(key);
|
||||
const isLike = /_like$/.test(key);
|
||||
const path = key.replace(/(_lte|_gte|_ne|_like)$/, '');
|
||||
chain = chain.filter(element => {
|
||||
return arr.map(function (value) {
|
||||
// get item value based on path
|
||||
// i.e post.title -> 'foo'
|
||||
const elementValue = _.get(element, path);
|
||||
|
||||
// Prevent toString() failing on undefined or null values
|
||||
if (elementValue === undefined || elementValue === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (isRange) {
|
||||
const isLowerThan = /_gte$/.test(key);
|
||||
return isLowerThan ? value <= elementValue : value >= elementValue;
|
||||
} else if (isDifferent) {
|
||||
return value !== elementValue.toString();
|
||||
} else if (isLike) {
|
||||
return new RegExp(value, 'i').test(elementValue.toString());
|
||||
} else {
|
||||
return value === elementValue.toString();
|
||||
}
|
||||
}).reduce((a, b) => isDifferent ? a && b : a || b);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Sort
|
||||
if (_sort) {
|
||||
const _sortSet = _sort.split(',');
|
||||
const _orderSet = (_order || '').split(',').map(s => s.toLowerCase());
|
||||
chain = chain.orderBy(_sortSet, _orderSet);
|
||||
}
|
||||
|
||||
// Slice result
|
||||
if (_end || _limit || _page) {
|
||||
res.setHeader('X-Total-Count', chain.size());
|
||||
res.setHeader('Access-Control-Expose-Headers', `X-Total-Count${_page ? ', Link' : ''}`);
|
||||
}
|
||||
if (_page) {
|
||||
_page = parseInt(_page, 10);
|
||||
_page = _page >= 1 ? _page : 1;
|
||||
_limit = parseInt(_limit, 10) || 10;
|
||||
const page = utils.getPage(chain.value(), _page, _limit);
|
||||
const links = {};
|
||||
const fullURL = getFullURL(req);
|
||||
if (page.first) {
|
||||
links.first = fullURL.replace(`page=${page.current}`, `page=${page.first}`);
|
||||
}
|
||||
if (page.prev) {
|
||||
links.prev = fullURL.replace(`page=${page.current}`, `page=${page.prev}`);
|
||||
}
|
||||
if (page.next) {
|
||||
links.next = fullURL.replace(`page=${page.current}`, `page=${page.next}`);
|
||||
}
|
||||
if (page.last) {
|
||||
links.last = fullURL.replace(`page=${page.current}`, `page=${page.last}`);
|
||||
}
|
||||
res.links(links);
|
||||
chain = _.chain(page.items);
|
||||
} else if (_end) {
|
||||
_start = parseInt(_start, 10) || 0;
|
||||
_end = parseInt(_end, 10);
|
||||
chain = chain.slice(_start, _end);
|
||||
} else if (_limit) {
|
||||
_start = parseInt(_start, 10) || 0;
|
||||
_limit = parseInt(_limit, 10);
|
||||
chain = chain.slice(_start, _start + _limit);
|
||||
}
|
||||
|
||||
// embed and expand
|
||||
chain = chain.cloneDeep().forEach(function (element) {
|
||||
embed(element, _embed);
|
||||
expand(element, _expand);
|
||||
});
|
||||
res.locals.data = chain.value();
|
||||
next();
|
||||
}
|
||||
|
||||
// GET /name/:id
|
||||
// GET /name/:id?_embed=&_expand
|
||||
function show(req, res, next) {
|
||||
const _embed = req.query._embed;
|
||||
const _expand = req.query._expand;
|
||||
const resource = db.get(name).getById(req.params.id).value();
|
||||
if (resource) {
|
||||
// Clone resource to avoid making changes to the underlying object
|
||||
const clone = _.cloneDeep(resource);
|
||||
|
||||
// Embed other resources based on resource id
|
||||
// /posts/1?_embed=comments
|
||||
embed(clone, _embed);
|
||||
|
||||
// Expand inner resources based on id
|
||||
// /posts/1?_expand=user
|
||||
expand(clone, _expand);
|
||||
res.locals.data = clone;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// POST /name
|
||||
function create(req, res, next) {
|
||||
let resource;
|
||||
if (opts._isFake) {
|
||||
const id = db.get(name).createId().value();
|
||||
resource = _objectSpread(_objectSpread({}, req.body), {}, {
|
||||
id
|
||||
});
|
||||
} else {
|
||||
resource = db.get(name).insert(req.body).value();
|
||||
}
|
||||
res.setHeader('Access-Control-Expose-Headers', 'Location');
|
||||
res.location(`${getFullURL(req)}/${resource.id}`);
|
||||
res.status(201);
|
||||
res.locals.data = resource;
|
||||
next();
|
||||
}
|
||||
|
||||
// PUT /name/:id
|
||||
// PATCH /name/:id
|
||||
function update(req, res, next) {
|
||||
const id = req.params.id;
|
||||
let resource;
|
||||
if (opts._isFake) {
|
||||
resource = db.get(name).getById(id).value();
|
||||
if (req.method === 'PATCH') {
|
||||
resource = _objectSpread(_objectSpread({}, resource), req.body);
|
||||
} else {
|
||||
resource = _objectSpread(_objectSpread({}, req.body), {}, {
|
||||
id: resource.id
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let chain = db.get(name);
|
||||
chain = req.method === 'PATCH' ? chain.updateById(id, req.body) : chain.replaceById(id, req.body);
|
||||
resource = chain.value();
|
||||
}
|
||||
if (resource) {
|
||||
res.locals.data = resource;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// DELETE /name/:id
|
||||
function destroy(req, res, next) {
|
||||
let resource;
|
||||
if (opts._isFake) {
|
||||
resource = db.get(name).value();
|
||||
} else {
|
||||
resource = db.get(name).removeById(req.params.id).value();
|
||||
|
||||
// Remove dependents documents
|
||||
const removable = db._.getRemovable(db.getState(), opts);
|
||||
removable.forEach(item => {
|
||||
db.get(item.name).removeById(item.id).value();
|
||||
});
|
||||
}
|
||||
if (resource) {
|
||||
res.locals.data = {};
|
||||
}
|
||||
next();
|
||||
}
|
||||
const w = write(db);
|
||||
router.route('/').get(list).post(create, w);
|
||||
router.route('/:id').get(show).put(update, w).patch(update, w).delete(destroy, w);
|
||||
return router;
|
||||
};
|
||||
52
lab3/node_modules/json-server/lib/server/router/singular.js
generated
vendored
Normal file
52
lab3/node_modules/json-server/lib/server/router/singular.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
||||
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
||||
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
||||
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
||||
const express = require('express');
|
||||
const write = require('./write');
|
||||
const getFullURL = require('./get-full-url');
|
||||
const delay = require('./delay');
|
||||
module.exports = (db, name, opts) => {
|
||||
const router = express.Router();
|
||||
router.use(delay);
|
||||
function show(req, res, next) {
|
||||
res.locals.data = db.get(name).value();
|
||||
next();
|
||||
}
|
||||
function create(req, res, next) {
|
||||
if (opts._isFake) {
|
||||
res.locals.data = req.body;
|
||||
} else {
|
||||
db.set(name, req.body).value();
|
||||
res.locals.data = db.get(name).value();
|
||||
}
|
||||
res.setHeader('Access-Control-Expose-Headers', 'Location');
|
||||
res.location(`${getFullURL(req)}`);
|
||||
res.status(201);
|
||||
next();
|
||||
}
|
||||
function update(req, res, next) {
|
||||
if (opts._isFake) {
|
||||
if (req.method === 'PUT') {
|
||||
res.locals.data = req.body;
|
||||
} else {
|
||||
const resource = db.get(name).value();
|
||||
res.locals.data = _objectSpread(_objectSpread({}, resource), req.body);
|
||||
}
|
||||
} else {
|
||||
if (req.method === 'PUT') {
|
||||
db.set(name, req.body).value();
|
||||
} else {
|
||||
db.get(name).assign(req.body).value();
|
||||
}
|
||||
res.locals.data = db.get(name).value();
|
||||
}
|
||||
next();
|
||||
}
|
||||
const w = write(db);
|
||||
router.route('/').get(show).post(create, w).put(update, w).patch(update, w);
|
||||
return router;
|
||||
};
|
||||
17
lab3/node_modules/json-server/lib/server/router/validate-data.js
generated
vendored
Normal file
17
lab3/node_modules/json-server/lib/server/router/validate-data.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
const _ = require('lodash');
|
||||
function validateKey(key) {
|
||||
if (key.indexOf('/') !== -1) {
|
||||
const msg = [`Oops, found / character in database property '${key}'.`, '', "/ aren't supported, if you want to tweak default routes, see", 'https://github.com/typicode/json-server/#add-custom-routes'].join('\n');
|
||||
throw new Error(msg);
|
||||
}
|
||||
}
|
||||
module.exports = obj => {
|
||||
if (_.isPlainObject(obj)) {
|
||||
Object.keys(obj).forEach(validateKey);
|
||||
} else {
|
||||
throw new Error(`Data must be an object. Found ${Array.isArray(obj) ? 'array' : typeof obj}.
|
||||
'See https://github.com/typicode/json-server for example.`);
|
||||
}
|
||||
};
|
||||
8
lab3/node_modules/json-server/lib/server/router/write.js
generated
vendored
Normal file
8
lab3/node_modules/json-server/lib/server/router/write.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function write(db) {
|
||||
return (req, res, next) => {
|
||||
db.write();
|
||||
next();
|
||||
};
|
||||
};
|
||||
26
lab3/node_modules/json-server/lib/server/utils.js
generated
vendored
Normal file
26
lab3/node_modules/json-server/lib/server/utils.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
getPage
|
||||
};
|
||||
function getPage(array, page, perPage) {
|
||||
const obj = {};
|
||||
const start = (page - 1) * perPage;
|
||||
const end = page * perPage;
|
||||
obj.items = array.slice(start, end);
|
||||
if (obj.items.length === 0) {
|
||||
return obj;
|
||||
}
|
||||
if (page > 1) {
|
||||
obj.prev = page - 1;
|
||||
}
|
||||
if (end < array.length) {
|
||||
obj.next = page + 1;
|
||||
}
|
||||
if (obj.items.length !== array.length) {
|
||||
obj.current = page;
|
||||
obj.first = 1;
|
||||
obj.last = Math.ceil(array.length / perPage);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
Reference in New Issue
Block a user