mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
routing for desk, error handling, test for router
This commit is contained in:
parent
b305e1e4f4
commit
df9fc91123
@ -1,7 +1,7 @@
|
||||
const frappe = require('frappe-core');
|
||||
const path = require('path');
|
||||
|
||||
class RESTClient {
|
||||
module.exports = class RESTClient {
|
||||
constructor({server, protocol='http'}) {
|
||||
this.server = server;
|
||||
this.protocol = protocol;
|
||||
@ -124,7 +124,3 @@ class RESTClient {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Database: RESTClient
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
const frappe = require('frappe-core');
|
||||
const Search = require('./search');
|
||||
const Router = require('./router');
|
||||
const Router = require('frappe-core/common/router');
|
||||
const Page = require('frappe-core/client/view/page');
|
||||
const List = require('frappe-core/client/view/list');
|
||||
const Form = require('frappe-core/client/view/form');
|
||||
|
||||
module.exports = class Desk {
|
||||
constructor() {
|
||||
frappe.router = new Router();
|
||||
frappe.router.listen();
|
||||
|
||||
this.wrapper = document.querySelector('.desk');
|
||||
|
||||
@ -15,20 +19,67 @@ module.exports = class Desk {
|
||||
this.main = frappe.ui.add('div', 'main', this.body);
|
||||
|
||||
this.sidebar_items = [];
|
||||
this.list_pages = {};
|
||||
this.edit_pages = {};
|
||||
this.pages = {
|
||||
lists: {},
|
||||
forms: {}
|
||||
};
|
||||
|
||||
this.init_routes();
|
||||
|
||||
// this.search = new Search(this.nav);
|
||||
}
|
||||
|
||||
init_routes() {
|
||||
frappe.router.on('list/:doctype', (params) => {
|
||||
|
||||
})
|
||||
frappe.router.on('edit/:doctype/:name', (params) => {
|
||||
frappe.router.add('list/:doctype', async (params) => {
|
||||
let page = this.get_list_page(params.doctype);
|
||||
await page.show(params);
|
||||
});
|
||||
|
||||
frappe.router.add('edit/:doctype/:name', async (params) => {
|
||||
let page = this.get_form_page(params.doctype);
|
||||
await page.show(params);
|
||||
})
|
||||
|
||||
frappe.router.add('new/:doctype', async (params) => {
|
||||
let doc = await frappe.get_new_doc(params.doctype);
|
||||
frappe.router.set_route('edit', doc.doctype, doc.name);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
get_list_page(doctype) {
|
||||
if (!this.pages.lists[doctype]) {
|
||||
let page = new Page('List ' + doctype);
|
||||
page.list = new List({
|
||||
doctype: doctype,
|
||||
parent: page.body
|
||||
});
|
||||
page.on('show', async () => {
|
||||
await page.list.run();
|
||||
});
|
||||
this.pages.lists[doctype] = page;
|
||||
}
|
||||
return this.pages.lists[doctype];
|
||||
}
|
||||
|
||||
get_form_page(doctype) {
|
||||
if (!this.pages.forms[doctype]) {
|
||||
let page = new Page('Edit ' + doctype);
|
||||
page.form = new Form({
|
||||
doctype: doctype,
|
||||
parent: page.body
|
||||
});
|
||||
page.on('show', async (params) => {
|
||||
try {
|
||||
page.doc = await frappe.get_doc(params.doctype, params.name);
|
||||
page.form.use(page.doc);
|
||||
} catch (e) {
|
||||
page.render_error(e.status_code, e.message);
|
||||
}
|
||||
});
|
||||
this.pages.forms[doctype] = page;
|
||||
}
|
||||
return this.pages.forms[doctype];
|
||||
}
|
||||
|
||||
add_sidebar_item(label, action) {
|
||||
|
@ -1,82 +0,0 @@
|
||||
const frappe = require('frappe-core');
|
||||
|
||||
module.exports = class Router {
|
||||
constructor() {
|
||||
this.current_page = null;
|
||||
this.static_routes = {};
|
||||
this.dynamic_routes = {};
|
||||
this.listen();
|
||||
}
|
||||
|
||||
add(route, handler) {
|
||||
let page = {handler: handler};
|
||||
|
||||
// '/todo/:name/:place'.match(/:([^/]+)/g);
|
||||
page.param_keys = route.match(/:([^/]+)/g);
|
||||
|
||||
if (page.param_keys) {
|
||||
// make expression
|
||||
// '/todo/:name/:place'.replace(/\/:([a-z1-9]+)/g, "\/([a-z0-9]+)");
|
||||
page.expression = route.replace(/\/:([a-z1-9]+)/g, "\/([a-z0-9]+)");
|
||||
this.dynamic_routes[route] = page;
|
||||
} else {
|
||||
this.static_routes[route] = page;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
listen() {
|
||||
window.onhashchange = this.changed.bind(this);
|
||||
this.changed();
|
||||
}
|
||||
|
||||
async changed(event) {
|
||||
if (window.location.hash.length > 0) {
|
||||
const page_name = window.location.hash.substr(1);
|
||||
this.show(page_name);
|
||||
} else if (this.static_routes['default']) {
|
||||
this.show('default');
|
||||
}
|
||||
}
|
||||
|
||||
show(route) {
|
||||
if (!route) {
|
||||
route = 'default';
|
||||
}
|
||||
|
||||
if (route[0]==='#') {
|
||||
route = route.substr(1);
|
||||
}
|
||||
|
||||
let page = this.match(route);
|
||||
|
||||
if (page) {
|
||||
if (typeof page.handler==='function') {
|
||||
page.handler(page.params);
|
||||
} else {
|
||||
page.handler.show(page.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match(route) {
|
||||
for(let key in this.static_routes) {
|
||||
if (key === route) {
|
||||
return {handler: this.static_routes[key].handler};
|
||||
}
|
||||
}
|
||||
|
||||
for(let key in this.dynamic_routes) {
|
||||
let page = this.dynamic_routes[key];
|
||||
let matches = route.match(new RegExp(page.expression));
|
||||
|
||||
if (matches && matches.length == page.param_keys.length + 1) {
|
||||
let params = {}
|
||||
for (let i=0; i < page.param_keys.length; i++) {
|
||||
params[page.param_keys[i].substr(1)] = matches[i + 1];
|
||||
}
|
||||
return {handler:page.handler, params: params};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
const common = require('frappe-core/common');
|
||||
const Database = require('frappe-core/backends/rest_client').Database;
|
||||
const RESTClient = require('frappe-core/backends/rest_client');
|
||||
const frappe = require('frappe-core');
|
||||
frappe.ui = require('./ui');
|
||||
const Desk = require('./desk');
|
||||
@ -11,7 +11,9 @@ module.exports = {
|
||||
common.init_libs(frappe);
|
||||
|
||||
frappe.fetch = window.fetch.bind();
|
||||
frappe.db = await new Database({server: server});
|
||||
frappe.db = await new RESTClient({server: server});
|
||||
|
||||
frappe.flags.cache_docs = true;
|
||||
|
||||
frappe.desk = new Desk();
|
||||
await frappe.login();
|
||||
|
@ -1,7 +1,7 @@
|
||||
const frappe = require('frappe-core');
|
||||
const controls = require('./controls');
|
||||
|
||||
class Form {
|
||||
module.exports = class Form {
|
||||
constructor({doctype, parent, submit_label='Submit'}) {
|
||||
this.parent = parent;
|
||||
this.doctype = doctype;
|
||||
@ -85,7 +85,7 @@ class Form {
|
||||
|
||||
async submit() {
|
||||
try {
|
||||
if (this.is_new) {
|
||||
if (this.is_new || this.doc.__not_inserted) {
|
||||
await this.doc.insert();
|
||||
} else {
|
||||
await this.doc.update();
|
||||
@ -104,5 +104,3 @@ class Form {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {Form: Form};
|
@ -1,6 +1,6 @@
|
||||
const frappe = require('frappe-core');
|
||||
|
||||
class ListView {
|
||||
module.exports = class List {
|
||||
constructor({doctype, parent, fields}) {
|
||||
this.doctype = doctype;
|
||||
this.parent = parent;
|
||||
@ -118,7 +118,3 @@ class ListView {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
ListView: ListView
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
const frappe = require('frappe-core');
|
||||
|
||||
class Page {
|
||||
module.exports = class Page {
|
||||
constructor(title) {
|
||||
this.handlers = {};
|
||||
this.title = title;
|
||||
@ -8,12 +8,12 @@ class Page {
|
||||
}
|
||||
|
||||
make() {
|
||||
this.body = frappe.ui.add('div', 'page hide', frappe.desk.main);
|
||||
this.wrapper = frappe.ui.add('div', 'page hide', frappe.desk.main);
|
||||
this.body = frappe.ui.add('div', 'page-body', this.wrapper);
|
||||
}
|
||||
|
||||
hide() {
|
||||
frappe.ui.add_class(this.body, 'hide');
|
||||
|
||||
this.wrapper.classList.add('hide');
|
||||
this.trigger('hide');
|
||||
}
|
||||
|
||||
@ -21,25 +21,38 @@ class Page {
|
||||
if (frappe.router.current_page) {
|
||||
frappe.router.current_page.hide();
|
||||
}
|
||||
frappe.ui.remove_class(this.body, 'hide');
|
||||
this.wrapper.classList.remove('hide');
|
||||
this.body.classList.remove('hide');
|
||||
|
||||
if (this.page_error) {
|
||||
this.page_error.classList.add('hide');
|
||||
}
|
||||
|
||||
frappe.router.current_page = this;
|
||||
document.title = this.title;
|
||||
|
||||
this.trigger('show', params);
|
||||
}
|
||||
|
||||
render_error(status_code, message) {
|
||||
if (!this.page_error) {
|
||||
this.page_error = frappe.ui.add('div', 'page-error', this.wrapper);
|
||||
}
|
||||
this.body.classList.add('hide');
|
||||
this.page_error.classList.remove('hide');
|
||||
this.page_error.innerHTML = `<h3 class="text-extra-muted">${status_code}</h3><p class="text-muted">${message}</p>`;
|
||||
}
|
||||
|
||||
on(event, fn) {
|
||||
if (!this.handlers[event]) this.handlers.event = [];
|
||||
if (!this.handlers[event]) this.handlers[event] = [];
|
||||
this.handlers[event].push(fn);
|
||||
}
|
||||
|
||||
trigger(event, params) {
|
||||
async trigger(event, params) {
|
||||
if (this.handlers[event]) {
|
||||
for (let handler of this.handlers[event]) {
|
||||
handler(params);
|
||||
await handler(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Page: Page };
|
21
common/errors.js
Normal file
21
common/errors.js
Normal file
@ -0,0 +1,21 @@
|
||||
class BaseError extends Error {
|
||||
constructor(status_code, ...params) {
|
||||
super(...params);
|
||||
this.status_code = status_code;
|
||||
}
|
||||
}
|
||||
|
||||
class ValidationError extends BaseError {
|
||||
constructor(...params) { super(417, ...params); }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ValidationError: ValidationError,
|
||||
ValueError: class ValueError extends ValidationError { },
|
||||
NotFound: class NotFound extends BaseError {
|
||||
constructor(...params) { super(404, ...params); }
|
||||
},
|
||||
Forbidden: class Forbidden extends BaseError {
|
||||
constructor(...params) { super(403, ...params); }
|
||||
},
|
||||
}
|
@ -4,6 +4,7 @@ const model = require('../model');
|
||||
const _document = require('../model/document');
|
||||
const meta = require('../model/meta');
|
||||
const _session = require('../session');
|
||||
const errors = require('./errors');
|
||||
|
||||
|
||||
module.exports = {
|
||||
@ -14,5 +15,6 @@ module.exports = {
|
||||
frappe.document = _document;
|
||||
frappe.meta = meta;
|
||||
frappe._session = _session;
|
||||
frappe.errors = errors;
|
||||
}
|
||||
}
|
99
common/router.js
Normal file
99
common/router.js
Normal file
@ -0,0 +1,99 @@
|
||||
const frappe = require('frappe-core');
|
||||
|
||||
module.exports = class Router {
|
||||
constructor() {
|
||||
this.current_page = null;
|
||||
this.static_routes = [];
|
||||
this.dynamic_routes = [];
|
||||
}
|
||||
|
||||
add(route, handler) {
|
||||
let page = {handler: handler, route: route};
|
||||
|
||||
// '/todo/:name/:place'.match(/:([^/]+)/g);
|
||||
page.param_keys = route.match(/:([^/]+)/g);
|
||||
|
||||
if (page.param_keys) {
|
||||
// make expression
|
||||
// '/todo/:name/:place'.replace(/\/:([a-z1-9]+)/g, "\/([a-z0-9]+)");
|
||||
page.expression = route.replace(/\/:([a-z1-9]+)/g, "\/([a-z0-9]+)");
|
||||
this.dynamic_routes.push(page);
|
||||
this.sort_dynamic_routes();
|
||||
} else {
|
||||
this.static_routes.push(page);
|
||||
this.sort_static_routes();
|
||||
}
|
||||
}
|
||||
|
||||
sort_dynamic_routes() {
|
||||
// routes with fewer parameters first
|
||||
this.dynamic_routes = this.dynamic_routes.sort((a, b) => {
|
||||
if (a.param_keys.length > b.param_keys.length) {
|
||||
return 1;
|
||||
} else if (a.param_keys.length < b.param_keys.length) {
|
||||
return -1;
|
||||
} else {
|
||||
return a.route.length > b.route.length ? 1 : -1;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
sort_static_routes() {
|
||||
// longer routes on first
|
||||
this.static_routes = this.static_routes.sort((a, b) => {
|
||||
return a.route.length > b.route.length ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
listen() {
|
||||
window.addEventListener('hashchange', (event) => {
|
||||
this.show(window.location.hash);
|
||||
});
|
||||
}
|
||||
|
||||
set_route(...parts) {
|
||||
const route = parts.join('/');
|
||||
window.location.hash = route;
|
||||
}
|
||||
|
||||
async show(route) {
|
||||
if (route && route[0]==='#') {
|
||||
route = route.substr(1);
|
||||
}
|
||||
|
||||
if (!route) {
|
||||
route = this.default;
|
||||
}
|
||||
let page = this.match(route);
|
||||
|
||||
if (page) {
|
||||
if (typeof page.handler==='function') {
|
||||
await page.handler(page.params);
|
||||
} else {
|
||||
await page.handler.show(page.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match(route) {
|
||||
// match static
|
||||
for(let page of this.static_routes) {
|
||||
if (page.route === route) {
|
||||
return {handler: page.handler};
|
||||
}
|
||||
}
|
||||
|
||||
// match dynamic
|
||||
for(let page of this.dynamic_routes) {
|
||||
let matches = route.match(new RegExp(page.expression));
|
||||
|
||||
if (matches && matches.length == page.param_keys.length + 1) {
|
||||
let params = {}
|
||||
for (let i=0; i < page.param_keys.length; i++) {
|
||||
params[page.param_keys[i].substr(1)] = matches[i + 1];
|
||||
}
|
||||
return {handler:page.handler, params: params};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
docs/client/desk.md
Normal file
27
docs/client/desk.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Desk
|
||||
|
||||
Desk includes the default routing and menu system for the single page application
|
||||
|
||||
## Menus
|
||||
|
||||
You can add a new menu to the desk via
|
||||
|
||||
```js
|
||||
frappe.desk.add_sidebar_item('New ToDo', '#new/todo');
|
||||
```
|
||||
|
||||
## Views
|
||||
|
||||
Default route handling for various views
|
||||
|
||||
### List Documents
|
||||
|
||||
All list views are rendered at `/list/:doctype`
|
||||
|
||||
### Edit Documents
|
||||
|
||||
Documents can be edited via `/edit/:doctype/:name`
|
||||
|
||||
### New Documents
|
||||
|
||||
New Documents can be created via `/new/:doctype`
|
16
docs/errors.md
Normal file
16
docs/errors.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Errors
|
||||
|
||||
Frappe.js comes with standard error classes that have an HTTP status code attached.
|
||||
|
||||
For example you can raise a "not found" (HTTP Status Code 404) via:
|
||||
|
||||
```js
|
||||
throw new frappe.errors.NotFound('Document Not Found');
|
||||
```
|
||||
|
||||
### Standard Errors
|
||||
|
||||
- 403: Forbidden
|
||||
- 404: NotFound
|
||||
- 417: ValidationError
|
||||
- 417: ValueError
|
42
index.js
42
index.js
@ -2,7 +2,6 @@ module.exports = {
|
||||
async init() {
|
||||
if (this._initialized) return;
|
||||
this.init_config();
|
||||
this.init_errors();
|
||||
this.init_globals();
|
||||
this._initialized = true;
|
||||
},
|
||||
@ -14,12 +13,30 @@ module.exports = {
|
||||
};
|
||||
},
|
||||
|
||||
init_errors() {
|
||||
this.ValueError = class extends Error { };
|
||||
},
|
||||
|
||||
init_globals() {
|
||||
this.meta_cache = {};
|
||||
this.docs = {};
|
||||
this.flags = {
|
||||
cache_docs: false
|
||||
}
|
||||
},
|
||||
|
||||
add_to_cache(doc) {
|
||||
if (!this.flags.cache_docs) return;
|
||||
|
||||
// add to `docs` cache
|
||||
if (doc.doctype && doc.name) {
|
||||
if (!this.docs[doc.doctype]) {
|
||||
this.docs[doc.doctype] = {};
|
||||
}
|
||||
this.docs[doc.doctype][doc.name] = doc;
|
||||
}
|
||||
},
|
||||
|
||||
get_doc_from_cache(doctype, name) {
|
||||
if (this.docs[doctype] && this.docs[doctype][name]) {
|
||||
return this.docs[doctype][name];
|
||||
}
|
||||
},
|
||||
|
||||
get_meta(doctype) {
|
||||
@ -37,9 +54,14 @@ module.exports = {
|
||||
|
||||
async get_doc(data, name) {
|
||||
if (typeof data==='string' && typeof name==='string') {
|
||||
let doc = this.get_doc_from_cache(data, name);
|
||||
if (!doc) {
|
||||
let controller_class = this.models.get_controller(data);
|
||||
var doc = new controller_class({doctype:data, name: name});
|
||||
doc = new controller_class({doctype:data, name: name});
|
||||
await doc.load();
|
||||
this.add_to_cache(doc);
|
||||
}
|
||||
return doc;
|
||||
} else {
|
||||
let controller_class = this.models.get_controller(data.doctype);
|
||||
var doc = new controller_class(data);
|
||||
@ -47,6 +69,14 @@ module.exports = {
|
||||
return doc;
|
||||
},
|
||||
|
||||
async get_new_doc(doctype) {
|
||||
let doc = await frappe.get_doc({doctype: doctype});
|
||||
doc.set_name();
|
||||
doc.__not_inserted = true;
|
||||
this.add_to_cache(doc);
|
||||
return doc;
|
||||
},
|
||||
|
||||
async insert(data) {
|
||||
const doc = await this.get_doc(data);
|
||||
return await doc.insert();
|
||||
|
@ -98,7 +98,12 @@ class Document {
|
||||
}
|
||||
|
||||
async load() {
|
||||
Object.assign(this, await frappe.db.get(this.doctype, this.name));
|
||||
let data = await frappe.db.get(this.doctype, this.name);
|
||||
if (data.name) {
|
||||
Object.assign(this, data);
|
||||
} else {
|
||||
throw new frappe.errors.NotFound(`Not Found: ${this.doctype} ${this.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
async insert() {
|
||||
|
@ -77,7 +77,7 @@ class Meta extends Document {
|
||||
options = df.options.split('\n');
|
||||
}
|
||||
if (!options.includes(value)) {
|
||||
throw new frappe.ValueError(`${value} must be one of ${options.join(", ")}`);
|
||||
throw new frappe.errors.ValueError(`${value} must be one of ${options.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,12 @@ class todo_meta extends frappe.meta.Meta {
|
||||
setup_meta() {
|
||||
Object.assign(this, require('./todo.json'));
|
||||
this.name = 'ToDo';
|
||||
this.list_options.fields = ['name', 'subject', 'status', 'description'];
|
||||
this.list_options.fields = ['name', 'subject', 'status'];
|
||||
}
|
||||
|
||||
get_row_html(data) {
|
||||
return `<a href="#edit/todo/${data.name}">${data.subject}</a>`;
|
||||
const sign = data.status === 'Open' ? '' : '✔';
|
||||
return `<p><a href="#edit/todo/${data.name}">${sign} ${data.subject}</a></p>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,10 +36,6 @@ module.exports = {
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.static('./'));
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
console.error(err.stack);
|
||||
res.status(500).send('Something broke!');
|
||||
})
|
||||
// routes
|
||||
rest_api.setup(app);
|
||||
|
||||
|
@ -48,7 +48,7 @@ describe('Document', () => {
|
||||
() => {
|
||||
doc.set('status', 'Illegal');
|
||||
},
|
||||
frappe.ValueError
|
||||
frappe.errors.ValueError
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -4,7 +4,7 @@ const fetch = require('node-fetch');
|
||||
const helpers = require('./helpers');
|
||||
const { spawn } = require('child_process');
|
||||
const process = require('process');
|
||||
const Database = require('frappe-core/backends/rest_client').Database
|
||||
const RESTClient = require('frappe-core/backends/rest_client')
|
||||
|
||||
// create a copy of frappe
|
||||
|
||||
@ -19,7 +19,7 @@ describe('REST', () => {
|
||||
await frappe.init();
|
||||
await frappe.login();
|
||||
|
||||
frappe.db = await new Database({server: 'localhost:8000'});
|
||||
frappe.db = await new RESTClient({server: 'localhost:8000'});
|
||||
frappe.fetch = fetch;
|
||||
|
||||
// wait for server to start
|
||||
|
28
tests/test_router.js
Normal file
28
tests/test_router.js
Normal file
@ -0,0 +1,28 @@
|
||||
const Router = require('frappe-core/common/router');
|
||||
const assert = require('assert');
|
||||
|
||||
describe('Router', () => {
|
||||
it('router should sort static routes', () => {
|
||||
let router = new Router();
|
||||
router.add('/a', 'x');
|
||||
router.add('/a/b', 'y');
|
||||
router.add('/a/b/clong/', 'z');
|
||||
|
||||
assert.equal(router.match('/a/b').handler, 'y');
|
||||
assert.equal(router.match('/a').handler, 'x');
|
||||
assert.equal(router.match('/a/b/clong/').handler, 'z');
|
||||
});
|
||||
|
||||
it('router should sort dynamic routes', () => {
|
||||
let router = new Router();
|
||||
router.add('/edit/todo/mytest', 'static');
|
||||
router.add('/edit/:doctype/:name', 'all');
|
||||
router.add('/edit/todo/:name', 'todo');
|
||||
|
||||
assert.equal(router.match('/edit/todo/test').handler, 'todo');
|
||||
assert.equal(router.match('/edit/user/test').handler, 'all');
|
||||
assert.equal(router.match('/edit/todo/mytest').handler, 'static');
|
||||
});
|
||||
|
||||
|
||||
});
|
@ -5,7 +5,10 @@ module.exports = {
|
||||
|
||||
async_handler(fn) {
|
||||
return (req, res, next) => Promise.resolve(fn(req, res, next))
|
||||
.catch(next);
|
||||
.catch((err) => {
|
||||
// handle error
|
||||
res.status(err.status_code).send({ error: err.message });
|
||||
});
|
||||
},
|
||||
|
||||
async sleep(seconds) {
|
||||
|
Loading…
Reference in New Issue
Block a user