admindb 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -16
- package/dist/app.js +119 -171
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/databases.js +1 -1
- package/dist/public/js/designer.js +1 -1
- package/dist/public/js/forms.js +1 -1
- package/dist/public/js/home.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/schema.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +2 -2
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/import-export.js +8 -6
- package/dist/routes/api/index.js +16 -9
- package/dist/routes/api/query.js +8 -5
- package/dist/routes/api/rows.js +36 -21
- package/dist/routes/api/seed.js +29 -16
- package/dist/routes/api/tables.js +32 -18
- package/dist/routes/databases.js +2 -0
- package/dist/routes/pages.d.ts +1 -1
- package/dist/routes/pages.js +137 -43
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/databases.hbs +1 -1
- package/dist/views/pages/erd.hbs +1 -1
- package/dist/views/pages/form.hbs +1 -1
- package/dist/views/pages/home.hbs +1 -1
- package/dist/views/pages/info.hbs +1 -1
- package/dist/views/pages/schema.hbs +1 -1
- package/dist/views/pages/seed-select.hbs +6 -5
- package/dist/views/pages/seed.hbs +3 -3
- package/dist/views/pages/table.hbs +1 -1
- package/dist/views/partials/navbar.hbs +1 -1
- package/dist/views/partials/sidebar.hbs +1 -1
- package/docs/API.md +60 -55
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -309,22 +309,22 @@ ADMINDB_AUTH=false npx admindb
|
|
|
309
309
|
AdminDB exposes a comprehensive REST API under `basePath` returning `{ success, data?, error? }`:
|
|
310
310
|
|
|
311
311
|
```text
|
|
312
|
-
GET /api/tables
|
|
313
|
-
GET /api/tables/:table/rows
|
|
314
|
-
GET /api/tables/:table/row/:id
|
|
315
|
-
GET /api/tables/:table/row/:id/blob/:column
|
|
316
|
-
GET /api/tables/:table/row/:id/blob/:col/meta
|
|
317
|
-
PUT /api/tables/:table/row/:id/blob/:column
|
|
318
|
-
POST /api/tables/:table/rows
|
|
319
|
-
PUT /api/tables/:table/row/:id
|
|
320
|
-
DELETE /api/tables/:table/row/:id
|
|
321
|
-
POST /api/tables/:table/rows/bulk-update
|
|
322
|
-
POST /api/tables/:table/rows/bulk-delete
|
|
323
|
-
POST /api/tables/:table/seed
|
|
324
|
-
POST /api/tables
|
|
325
|
-
GET /api/tables/:table/schema
|
|
326
|
-
GET /api/tables/:table/ddl
|
|
327
|
-
POST /api/query
|
|
312
|
+
GET /api/tables/:db List tables
|
|
313
|
+
GET /api/tables/:db/:table/rows Paginated rows (with filtering & sorting)
|
|
314
|
+
GET /api/tables/:db/:table/row/:id Get a single row
|
|
315
|
+
GET /api/tables/:db/:table/row/:id/blob/:column Stream raw BLOB / BYTEA binary data
|
|
316
|
+
GET /api/tables/:db/:table/row/:id/blob/:col/meta BLOB / BYTEA metadata, MIME analysis & hex dump
|
|
317
|
+
PUT /api/tables/:db/:table/row/:id/blob/:column Upload / update binary content
|
|
318
|
+
POST /api/tables/:db/:table/rows Insert row (single or batch)
|
|
319
|
+
PUT /api/tables/:db/:table/row/:id Update row
|
|
320
|
+
DELETE /api/tables/:db/:table/row/:id Delete row
|
|
321
|
+
POST /api/tables/:db/:table/rows/bulk-update Apply staged inline edits atomically
|
|
322
|
+
POST /api/tables/:db/:table/rows/bulk-delete Delete selected rows atomically
|
|
323
|
+
POST /api/tables/:db/:table/seed Generate & insert realistic seed rows
|
|
324
|
+
POST /api/tables/:db Create a new table
|
|
325
|
+
GET /api/tables/:db/:table/schema Inspect table schema & constraints
|
|
326
|
+
GET /api/tables/:db/:table/ddl Get table CREATE SQL & indexes
|
|
327
|
+
POST /api/query/:db Execute arbitrary SQL
|
|
328
328
|
GET /api/databases List managed database connections & files
|
|
329
329
|
```
|
|
330
330
|
|
package/dist/app.js
CHANGED
|
@@ -20,54 +20,10 @@ const serverless_1 = require("./serverless");
|
|
|
20
20
|
const auth_1 = require("./auth");
|
|
21
21
|
const APP_VERSION = (0, args_1.getPackageVersion)();
|
|
22
22
|
function createRouter(options = {}) {
|
|
23
|
-
return options.manager ? createManagerApp(options) : createSingleDbApp(options);
|
|
24
|
-
}
|
|
25
|
-
function setupViewEngine(app, _logger) {
|
|
26
|
-
app.engine('hbs', (0, express_handlebars_1.engine)({
|
|
27
|
-
extname: '.hbs',
|
|
28
|
-
defaultLayout: 'main',
|
|
29
|
-
layoutsDir: node_path_1.default.join(__dirname, 'views', 'layouts'),
|
|
30
|
-
partialsDir: node_path_1.default.join(__dirname, 'views', 'partials'),
|
|
31
|
-
helpers: {
|
|
32
|
-
eq: (a, b) => a === b,
|
|
33
|
-
startsWith: (a, b) => String(a ?? '').startsWith(String(b ?? '')),
|
|
34
|
-
isNull: (v) => v === null || v === undefined,
|
|
35
|
-
truncate: (v, n) => {
|
|
36
|
-
const s = v == null ? '' : String(v);
|
|
37
|
-
return s.length > n ? `${s.slice(0, n)}…` : s;
|
|
38
|
-
},
|
|
39
|
-
json: (v) => (JSON.stringify(v ?? null) ?? 'null').replace(/</g, '\\u003c'),
|
|
40
|
-
add: (a, b) => Number(a) + Number(b),
|
|
41
|
-
sub: (a, b) => Number(a) - Number(b),
|
|
42
|
-
gt: (a, b) => Number(a) > Number(b),
|
|
43
|
-
lt: (a, b) => Number(a) < Number(b),
|
|
44
|
-
join: (arr, sep) => (Array.isArray(arr) ? arr.join(String(sep ?? ',')) : String(arr ?? '')),
|
|
45
|
-
icon: (name, options) => {
|
|
46
|
-
const customClass = (options && options.hash && options.hash.class) || undefined;
|
|
47
|
-
return (0, icons_1.renderIcon)(String(name ?? ''), customClass);
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
}));
|
|
51
|
-
app.set('view engine', 'hbs');
|
|
52
|
-
app.set('views', node_path_1.default.join(__dirname, 'views'));
|
|
53
|
-
}
|
|
54
|
-
function addErrorHandlers(app, logger) {
|
|
55
|
-
app.use((req, res) => {
|
|
56
|
-
if (req.path.startsWith('/api/')) {
|
|
57
|
-
return res.status(404).json({ success: false, error: 'Not found.' });
|
|
58
|
-
}
|
|
59
|
-
res.status(404).render('pages/error', { title: 'Not found', status: 404, error: 'The page you requested does not exist.' });
|
|
60
|
-
});
|
|
61
|
-
app.use((err, req, res, _next) => {
|
|
62
|
-
logger.error('Unhandled error', err);
|
|
63
|
-
if (req.path.startsWith('/api/')) {
|
|
64
|
-
return res.status(500).json({ success: false, error: (0, common_1.errorMessage)(err) });
|
|
65
|
-
}
|
|
66
|
-
res.status(500).render('pages/error', { title: 'Error', status: 500, error: (0, common_1.errorMessage)(err) });
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
function createSingleDbApp(options) {
|
|
70
23
|
const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
24
|
+
const basePath = (options.basePath ?? '').replace(/\/+$/, '');
|
|
25
|
+
const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
|
|
26
|
+
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
71
27
|
const envTarget = process.env.ADMINDB_CONNECTION ||
|
|
72
28
|
process.env.DATABASE_URL ||
|
|
73
29
|
process.env.POSTGRES_URL ||
|
|
@@ -77,81 +33,37 @@ function createSingleDbApp(options) {
|
|
|
77
33
|
const isPg = Boolean(options.pgOptions) ||
|
|
78
34
|
(0, common_1.isPostgresConnectionString)(rawTarget) ||
|
|
79
35
|
options.db?.dialect === 'postgres';
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
36
|
+
const explicitReadonly = options.readonly !== undefined ? Boolean(options.readonly) : undefined;
|
|
37
|
+
const globalReadonly = explicitReadonly !== undefined
|
|
38
|
+
? explicitReadonly
|
|
39
|
+
: options.manager
|
|
40
|
+
? options.manager.isReadOnly
|
|
41
|
+
: isServerless
|
|
42
|
+
? !isPg
|
|
43
|
+
: false;
|
|
44
|
+
const manager = options.manager;
|
|
45
|
+
let singleDb;
|
|
46
|
+
let defaultDbId = options.dbId;
|
|
47
|
+
if (!manager) {
|
|
48
|
+
if (options.db) {
|
|
49
|
+
singleDb = options.db;
|
|
50
|
+
if (options.readonly !== undefined && singleDb.isReadOnly !== Boolean(options.readonly)) {
|
|
51
|
+
try {
|
|
52
|
+
Object.defineProperty(singleDb, 'isReadOnly', { value: Boolean(options.readonly), configurable: true });
|
|
53
|
+
}
|
|
54
|
+
catch { }
|
|
91
55
|
}
|
|
92
|
-
catch { }
|
|
93
56
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
db = new postgres_1.PostgresDatabase(options.pgOptions || rawTarget, logger, { readonly });
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
db = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly });
|
|
100
|
-
}
|
|
101
|
-
const databasesUrl = options.databasesUrl;
|
|
102
|
-
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
103
|
-
const router = (0, express_1.default)();
|
|
104
|
-
router.disable('x-powered-by');
|
|
105
|
-
router.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
|
|
106
|
-
setupViewEngine(router, logger);
|
|
107
|
-
router.use(express_1.default.json({ limit: '10mb' }));
|
|
108
|
-
router.use(express_1.default.urlencoded({ extended: false }));
|
|
109
|
-
(0, auth_1.registerAuthRoutes)(router, authConfig, basePath, logger);
|
|
110
|
-
router.use((0, auth_1.createAuthMiddleware)(authConfig, basePath, logger));
|
|
111
|
-
router.use(async (req, res, next) => {
|
|
112
|
-
try {
|
|
113
|
-
res.locals.basePath = basePath;
|
|
114
|
-
res.locals.currentPath = req.path;
|
|
115
|
-
res.locals.currentTable = null;
|
|
116
|
-
res.locals.dbPath = db.path;
|
|
117
|
-
res.locals.dbId = options.dbId ?? (db.dialect === 'postgres' ? 'PostgreSQL' : (node_path_1.default.basename(db.path) || 'SQLite'));
|
|
118
|
-
res.locals.dialect = db.dialect;
|
|
119
|
-
res.locals.isPostgres = db.dialect === 'postgres';
|
|
120
|
-
res.locals.isSqlite = db.dialect === 'sqlite';
|
|
121
|
-
res.locals.dialectName = db.dialect === 'postgres' ? 'PostgreSQL' : 'SQLite';
|
|
122
|
-
res.locals.databasesUrl = databasesUrl ?? null;
|
|
123
|
-
res.locals.databasesMode = false;
|
|
124
|
-
res.locals.readonly = db.isReadOnly;
|
|
125
|
-
res.locals.serverless = isServerless;
|
|
126
|
-
res.locals.version = APP_VERSION;
|
|
127
|
-
res.locals.authEnabled = authConfig.enabled;
|
|
128
|
-
res.locals.isDefaultPassword = authConfig.isDefaultPassword;
|
|
129
|
-
res.locals.icons = icons_1.ICONS;
|
|
130
|
-
if (!req.path.startsWith('/api/')) {
|
|
131
|
-
const all = await db.listTables();
|
|
132
|
-
const names = (all.data ?? []).map((t) => t.name);
|
|
133
|
-
res.locals.tables = names.filter((n) => !n.startsWith('_'));
|
|
134
|
-
res.locals.internalTables = names.filter((n) => n.startsWith('_'));
|
|
135
|
-
}
|
|
136
|
-
next();
|
|
57
|
+
else if (isPg) {
|
|
58
|
+
singleDb = new postgres_1.PostgresDatabase(options.pgOptions || rawTarget, logger, { readonly: globalReadonly });
|
|
137
59
|
}
|
|
138
|
-
|
|
139
|
-
|
|
60
|
+
else {
|
|
61
|
+
singleDb = new database_1.SqliteDatabase(options.dbPath ?? 'admindb.db', logger, { readonly: globalReadonly });
|
|
140
62
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return router;
|
|
146
|
-
}
|
|
147
|
-
function createManagerApp(options) {
|
|
148
|
-
const isServerless = options.serverless !== undefined ? options.serverless : (0, serverless_1.isServerlessEnvironment)();
|
|
149
|
-
const explicitReadonly = options.readonly !== undefined ? Boolean(options.readonly) : undefined;
|
|
150
|
-
const manager = options.manager;
|
|
151
|
-
const globalReadonly = explicitReadonly ?? manager.isReadOnly;
|
|
152
|
-
const basePath = (options.basePath ?? '').replace(/\/+$/, '');
|
|
153
|
-
const logger = options.logger ?? (0, logger_1.createLogger)(options.logLevel ?? 'info', 'admindb');
|
|
154
|
-
const authConfig = (0, auth_1.resolveAuthConfig)(options.auth);
|
|
63
|
+
if (!defaultDbId) {
|
|
64
|
+
defaultDbId = singleDb.dialect === 'postgres' ? 'PostgreSQL' : (node_path_1.default.basename(singleDb.path) || 'SQLite');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
155
67
|
const app = (0, express_1.default)();
|
|
156
68
|
app.disable('x-powered-by');
|
|
157
69
|
app.use(express_1.default.static(node_path_1.default.join(__dirname, 'public')));
|
|
@@ -165,75 +77,111 @@ function createManagerApp(options) {
|
|
|
165
77
|
res.locals.basePath = basePath;
|
|
166
78
|
res.locals.currentPath = req.path;
|
|
167
79
|
res.locals.currentTable = null;
|
|
168
|
-
res.locals.dbPath = manager.directory;
|
|
169
|
-
res.locals.dbId = null;
|
|
170
|
-
res.locals.databasesUrl = null;
|
|
171
|
-
res.locals.databasesMode =
|
|
80
|
+
res.locals.dbPath = manager ? manager.directory : singleDb?.path;
|
|
81
|
+
res.locals.dbId = defaultDbId ?? null;
|
|
82
|
+
res.locals.databasesUrl = manager ? `${basePath}/` : (options.databasesUrl ?? null);
|
|
83
|
+
res.locals.databasesMode = false;
|
|
172
84
|
res.locals.readonly = globalReadonly;
|
|
173
85
|
res.locals.serverless = isServerless;
|
|
174
86
|
res.locals.version = APP_VERSION;
|
|
175
87
|
res.locals.authEnabled = authConfig.enabled;
|
|
176
88
|
res.locals.isDefaultPassword = authConfig.isDefaultPassword;
|
|
177
89
|
res.locals.icons = icons_1.ICONS;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
res.locals.internalTables = [];
|
|
181
|
-
}
|
|
90
|
+
res.locals.tables = [];
|
|
91
|
+
res.locals.internalTables = [];
|
|
182
92
|
next();
|
|
183
93
|
}
|
|
184
94
|
catch (err) {
|
|
185
95
|
next(err);
|
|
186
96
|
}
|
|
187
97
|
});
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
browseRoot: options.browseRoot,
|
|
196
|
-
invalidate: (id) => subApps.delete(id),
|
|
197
|
-
});
|
|
198
|
-
app.use('/:dbId', (req, res, next) => {
|
|
199
|
-
const dbId = String(req.params.dbId);
|
|
200
|
-
if (!manager.has(dbId)) {
|
|
201
|
-
if (req.path.startsWith('/api/')) {
|
|
202
|
-
return res.status(404).json({ success: false, error: `Database "${dbId}" does not exist.` });
|
|
98
|
+
const getDb = (req) => {
|
|
99
|
+
if (manager) {
|
|
100
|
+
const dbId = String(req.params.db || req.params.dbId || req.params.id || '');
|
|
101
|
+
if (!manager.has(dbId)) {
|
|
102
|
+
const err = new Error(`Database "${dbId}" does not exist.`);
|
|
103
|
+
err.status = 404;
|
|
104
|
+
throw err;
|
|
203
105
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const rawRo = req.query.readonly;
|
|
211
|
-
if (rawRo !== undefined) {
|
|
212
|
-
const explicitRo = rawRo === '1' || rawRo === 'true';
|
|
213
|
-
if (!globalReadonly || explicitRo) {
|
|
214
|
-
manager.setReadonly(dbId, explicitRo);
|
|
215
|
-
subApps.delete(dbId);
|
|
106
|
+
const rawRo = req.query.readonly;
|
|
107
|
+
if (rawRo !== undefined) {
|
|
108
|
+
const explicitRo = rawRo === '1' || rawRo === 'true';
|
|
109
|
+
if (!globalReadonly || explicitRo) {
|
|
110
|
+
manager.setReadonly(dbId, explicitRo);
|
|
111
|
+
}
|
|
216
112
|
}
|
|
217
|
-
}
|
|
218
|
-
let sub = subApps.get(dbId);
|
|
219
|
-
if (!sub) {
|
|
220
|
-
const subBase = `${basePath || ''}/${encodeURIComponent(dbId)}`;
|
|
221
113
|
const effectiveRo = globalReadonly ? true : manager.isDbReadOnly(dbId);
|
|
222
|
-
|
|
223
|
-
db: manager.open(dbId, effectiveRo),
|
|
224
|
-
basePath: subBase,
|
|
225
|
-
logger,
|
|
226
|
-
logLevel: options.logLevel,
|
|
227
|
-
dbId,
|
|
228
|
-
databasesUrl: `${basePath || ''}/`,
|
|
229
|
-
readonly: effectiveRo,
|
|
230
|
-
serverless: isServerless,
|
|
231
|
-
auth: authConfig,
|
|
232
|
-
});
|
|
233
|
-
subApps.set(dbId, sub);
|
|
114
|
+
return manager.open(dbId, effectiveRo);
|
|
234
115
|
}
|
|
235
|
-
return
|
|
236
|
-
}
|
|
116
|
+
return singleDb;
|
|
117
|
+
};
|
|
118
|
+
if (manager) {
|
|
119
|
+
(0, databases_1.registerDatabasesRoutes)(app, {
|
|
120
|
+
manager,
|
|
121
|
+
logger,
|
|
122
|
+
basePath,
|
|
123
|
+
readonly: globalReadonly,
|
|
124
|
+
allowBrowse: isServerless ? false : options.allowBrowse,
|
|
125
|
+
browseRoot: options.browseRoot,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
app.get('/', (_req, res) => {
|
|
130
|
+
res.redirect(302, `${basePath}/home/${encodeURIComponent(defaultDbId)}`);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
(0, pages_1.registerPages)(app, { getDb, logger });
|
|
134
|
+
(0, index_1.registerApi)(app, { getDb, logger });
|
|
237
135
|
addErrorHandlers(app, logger);
|
|
238
136
|
return app;
|
|
239
137
|
}
|
|
138
|
+
function setupViewEngine(app, _logger) {
|
|
139
|
+
app.engine('hbs', (0, express_handlebars_1.engine)({
|
|
140
|
+
extname: '.hbs',
|
|
141
|
+
defaultLayout: 'main',
|
|
142
|
+
layoutsDir: node_path_1.default.join(__dirname, 'views', 'layouts'),
|
|
143
|
+
partialsDir: node_path_1.default.join(__dirname, 'views', 'partials'),
|
|
144
|
+
helpers: {
|
|
145
|
+
eq: (a, b) => a === b,
|
|
146
|
+
startsWith: (a, b) => String(a ?? '').startsWith(String(b ?? '')),
|
|
147
|
+
isNull: (v) => v === null || v === undefined,
|
|
148
|
+
truncate: (v, n) => {
|
|
149
|
+
const s = v == null ? '' : String(v);
|
|
150
|
+
return s.length > n ? `${s.slice(0, n)}…` : s;
|
|
151
|
+
},
|
|
152
|
+
json: (v) => (JSON.stringify(v ?? null) ?? 'null').replace(/</g, '\\u003c'),
|
|
153
|
+
add: (a, b) => Number(a) + Number(b),
|
|
154
|
+
sub: (a, b) => Number(a) - Number(b),
|
|
155
|
+
gt: (a, b) => Number(a) > Number(b),
|
|
156
|
+
lt: (a, b) => Number(a) < Number(b),
|
|
157
|
+
join: (arr, sep) => (Array.isArray(arr) ? arr.join(String(sep ?? ',')) : String(arr ?? '')),
|
|
158
|
+
encodeUri: (str) => encodeURIComponent(String(str ?? '')),
|
|
159
|
+
or: (a, b) => a || b,
|
|
160
|
+
tableName: (t) => (typeof t === 'object' && t !== null && 'name' in t ? String(t.name ?? '') : String(t ?? '')),
|
|
161
|
+
icon: (name, options) => {
|
|
162
|
+
const customClass = (options && options.hash && options.hash.class) || undefined;
|
|
163
|
+
return (0, icons_1.renderIcon)(String(name ?? ''), customClass);
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}));
|
|
167
|
+
app.set('view engine', 'hbs');
|
|
168
|
+
app.set('views', node_path_1.default.join(__dirname, 'views'));
|
|
169
|
+
}
|
|
170
|
+
function addErrorHandlers(app, logger) {
|
|
171
|
+
app.use((req, res) => {
|
|
172
|
+
if (req.path.startsWith('/api/')) {
|
|
173
|
+
return res.status(404).json({ success: false, error: 'Not found.' });
|
|
174
|
+
}
|
|
175
|
+
res.status(404).render('pages/error', { title: 'Not found', status: 404, error: 'The page you requested does not exist.' });
|
|
176
|
+
});
|
|
177
|
+
app.use((err, req, res, _next) => {
|
|
178
|
+
const status = typeof err?.status === 'number' ? err.status : 500;
|
|
179
|
+
if (status >= 500) {
|
|
180
|
+
logger.error('Unhandled error', err);
|
|
181
|
+
}
|
|
182
|
+
if (req.path.startsWith('/api/')) {
|
|
183
|
+
return res.status(status).json({ success: false, error: (0, common_1.errorMessage)(err) });
|
|
184
|
+
}
|
|
185
|
+
res.status(status).render('pages/error', { title: status === 404 ? 'Not found' : 'Error', status, error: (0, common_1.errorMessage)(err) });
|
|
186
|
+
});
|
|
187
|
+
}
|