@ditojs/server 1.14.2 → 1.14.3
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/package.json +2 -3
- package/src/app/Application.js +36 -15
- package/src/cli/console.js +1 -1
- package/src/cli/db/createMigration.js +11 -2
- package/src/cli/db/seed.js +1 -1
- package/src/controllers/Controller.js +2 -2
- package/src/errors/DatabaseError.js +16 -11
- package/src/errors/RelationError.js +17 -14
- package/src/errors/ResponseError.js +36 -17
- package/src/errors/index.js +0 -1
- package/src/models/Model.js +2 -3
- package/src/storage/DiskStorage.js +3 -3
- package/types/index.d.ts +1 -2
- package/src/errors/WrappedError.js +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/master/packages/server",
|
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"data-uri-to-buffer": "^4.0.0",
|
|
40
40
|
"eventemitter2": "^6.4.9",
|
|
41
41
|
"file-type": "^18.0.0",
|
|
42
|
-
"fs-extra": "^10.1.0",
|
|
43
42
|
"image-size": "^1.0.2",
|
|
44
43
|
"is-svg": "^4.3.2",
|
|
45
44
|
"koa": "^2.13.4",
|
|
@@ -95,5 +94,5 @@
|
|
|
95
94
|
"typescript": "^4.9.3"
|
|
96
95
|
},
|
|
97
96
|
"types": "types",
|
|
98
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "cedf7eb2a28bb051ef194fc9b29205681e390ef9"
|
|
99
98
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import util from 'util'
|
|
3
3
|
import zlib from 'zlib'
|
|
4
|
-
import fs from 'fs
|
|
4
|
+
import fs from 'fs/promises'
|
|
5
5
|
import Koa from 'koa'
|
|
6
6
|
import Knex from 'knex'
|
|
7
7
|
import pico from 'picocolors'
|
|
@@ -594,7 +594,7 @@ export class Application extends Koa {
|
|
|
594
594
|
prettyPrint: {
|
|
595
595
|
colorize: true,
|
|
596
596
|
// List of keys to ignore in pretty mode.
|
|
597
|
-
ignore: 'req,res,durationMs,user,requestId
|
|
597
|
+
ignore: 'req,res,durationMs,user,requestId',
|
|
598
598
|
// SYS to use system time and not UTC.
|
|
599
599
|
translateTime: 'SYS:HH:MM:ss.l'
|
|
600
600
|
},
|
|
@@ -689,20 +689,40 @@ export class Application extends Koa {
|
|
|
689
689
|
return this.config.app.normalizePaths ? hyphenate(path) : path
|
|
690
690
|
}
|
|
691
691
|
|
|
692
|
-
|
|
693
|
-
|
|
692
|
+
formatError(error) {
|
|
693
|
+
// Clone the error to be able to delete hidden properties.
|
|
694
|
+
const copy = clone(error)
|
|
695
|
+
// Remove headers added by the CORS middleware.
|
|
696
|
+
delete copy.headers
|
|
697
|
+
if (this.config.log.errors?.stack === false) {
|
|
698
|
+
delete copy.stack
|
|
699
|
+
delete copy.cause
|
|
700
|
+
} else {
|
|
701
|
+
// These aren't enumerable and thus couldn't be cloned above.
|
|
702
|
+
if (error.stack !== undefined) {
|
|
703
|
+
copy.stack = error.stack
|
|
704
|
+
}
|
|
705
|
+
if (error.cause !== undefined) {
|
|
706
|
+
copy.cause = error.cause
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
// Use `util.inspect()` instead of Pino's internal error logging for better
|
|
710
|
+
// stack traces and logging of error data.
|
|
711
|
+
return util.inspect(copy, {
|
|
712
|
+
compact: false,
|
|
713
|
+
depth: null,
|
|
714
|
+
maxArrayLength: null
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
logError(error, ctx) {
|
|
719
|
+
if (!error.expose && !this.silent) {
|
|
694
720
|
try {
|
|
695
|
-
const clone = structuredClone(err)
|
|
696
|
-
// Remove headers added by the CORS middleware.
|
|
697
|
-
delete clone.headers
|
|
698
|
-
if (this.config.log.errors?.stack === false) {
|
|
699
|
-
delete clone.stack
|
|
700
|
-
delete clone.cause
|
|
701
|
-
}
|
|
702
|
-
const level =
|
|
703
|
-
err instanceof ResponseError && err.status < 500 ? 'info' : 'error'
|
|
704
721
|
const logger = ctx?.logger || this.logger
|
|
705
|
-
|
|
722
|
+
const level = error instanceof ResponseError && error.status < 500
|
|
723
|
+
? 'info'
|
|
724
|
+
: 'error'
|
|
725
|
+
logger[level](this.formatError(error))
|
|
706
726
|
} catch (e) {
|
|
707
727
|
console.error('Could not log error', e)
|
|
708
728
|
}
|
|
@@ -895,7 +915,8 @@ export class Application extends Koa {
|
|
|
895
915
|
data = await fs.readFile(filepath)
|
|
896
916
|
} else {
|
|
897
917
|
const response = await fetch(url)
|
|
898
|
-
|
|
918
|
+
const buffer = await response.arrayBuffer()
|
|
919
|
+
data = new DataView(buffer)
|
|
899
920
|
}
|
|
900
921
|
}
|
|
901
922
|
const importedFile = await storage.addFile(file, data)
|
package/src/cli/console.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import fs from 'fs
|
|
2
|
+
import fs from 'fs/promises'
|
|
3
3
|
import pico from 'picocolors'
|
|
4
4
|
import { getRelationClass, isThroughRelationClass } from '@ditojs/server'
|
|
5
5
|
import {
|
|
@@ -50,7 +50,7 @@ export async function createMigration(app, name, ...modelNames) {
|
|
|
50
50
|
: ''
|
|
51
51
|
const filename = `${getTimestamp()}_${name}.js`
|
|
52
52
|
const file = path.join(migrationDir, filename)
|
|
53
|
-
if (await
|
|
53
|
+
if (await exists(file)) {
|
|
54
54
|
// This should never happen, but let's be on the safe side here:
|
|
55
55
|
console.info(pico.red(`Migration '${filename}' already exists.`))
|
|
56
56
|
return false
|
|
@@ -204,3 +204,12 @@ function getTimestamp() {
|
|
|
204
204
|
padDate(d.getMinutes()) +
|
|
205
205
|
padDate(d.getSeconds())
|
|
206
206
|
}
|
|
207
|
+
|
|
208
|
+
async function exists(path) {
|
|
209
|
+
try {
|
|
210
|
+
await fs.access(path)
|
|
211
|
+
return true
|
|
212
|
+
} catch {
|
|
213
|
+
return false
|
|
214
|
+
}
|
|
215
|
+
}
|
package/src/cli/db/seed.js
CHANGED
|
@@ -3,7 +3,7 @@ import { EventEmitter } from '../lib/index.js'
|
|
|
3
3
|
import ControllerAction from './ControllerAction.js'
|
|
4
4
|
import MemberAction from './MemberAction.js'
|
|
5
5
|
import {
|
|
6
|
-
ResponseError,
|
|
6
|
+
ResponseError, ControllerError, AuthorizationError
|
|
7
7
|
} from '../errors/index.js'
|
|
8
8
|
import {
|
|
9
9
|
getOwnProperty, getOwnKeys, getAllKeys, processHandlerParameters,
|
|
@@ -185,7 +185,7 @@ export class Controller {
|
|
|
185
185
|
ctx.body = res
|
|
186
186
|
}
|
|
187
187
|
} catch (err) {
|
|
188
|
-
throw err instanceof ResponseError ? err : new
|
|
188
|
+
throw err instanceof ResponseError ? err : new ResponseError(err)
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
])
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ResponseError } from './ResponseError.js'
|
|
2
2
|
// TODO: Import directly once we can move to Objection 3 and this is fixed:
|
|
3
3
|
// import {
|
|
4
4
|
// DBError,
|
|
@@ -16,16 +16,21 @@ const {
|
|
|
16
16
|
ConstraintViolationError
|
|
17
17
|
} = objection
|
|
18
18
|
|
|
19
|
-
export class DatabaseError extends
|
|
19
|
+
export class DatabaseError extends ResponseError {
|
|
20
20
|
constructor(error, overrides) {
|
|
21
|
-
|
|
22
|
-
error
|
|
23
|
-
: error
|
|
24
|
-
: error
|
|
25
|
-
|
|
26
|
-
: error instanceof DBError ? 500
|
|
27
|
-
: 400
|
|
28
|
-
overrides = { type: error.constructor.name, status, ...overrides }
|
|
29
|
-
super(error, overrides, { message: 'Database error', status })
|
|
21
|
+
super(error, {
|
|
22
|
+
type: error.constructor.name,
|
|
23
|
+
message: 'Database error',
|
|
24
|
+
status: getStatus(error)
|
|
25
|
+
}, overrides)
|
|
30
26
|
}
|
|
31
27
|
}
|
|
28
|
+
|
|
29
|
+
function getStatus(error) {
|
|
30
|
+
return error instanceof CheckViolationError ? 400
|
|
31
|
+
: error instanceof NotNullViolationError ? 400
|
|
32
|
+
: error instanceof ConstraintViolationError ? 409
|
|
33
|
+
: error instanceof DataError ? 400
|
|
34
|
+
: error instanceof DBError ? 500
|
|
35
|
+
: 400
|
|
36
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isObject } from '@ditojs/utils'
|
|
1
|
+
import { ResponseError } from './ResponseError.js'
|
|
3
2
|
|
|
4
|
-
export class RelationError extends
|
|
3
|
+
export class RelationError extends ResponseError {
|
|
5
4
|
constructor(error) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
super(
|
|
6
|
+
error,
|
|
7
|
+
{ message: 'Relation error', status: 400 },
|
|
8
|
+
error instanceof Error ? getParsedOverrides(error) : null
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getParsedOverrides(error) {
|
|
14
|
+
// Adjust Objection.js error messages to point to the right property.
|
|
15
|
+
const parse = str => str?.replace(/\brelationMappings\b/g, 'relations')
|
|
16
|
+
const { message, stack } = error
|
|
17
|
+
return {
|
|
18
|
+
message: parse(message),
|
|
19
|
+
stack: parse(stack)
|
|
17
20
|
}
|
|
18
21
|
}
|
|
@@ -1,33 +1,52 @@
|
|
|
1
1
|
import { isPlainObject, isString } from '@ditojs/utils'
|
|
2
2
|
|
|
3
3
|
export class ResponseError extends Error {
|
|
4
|
-
constructor(
|
|
4
|
+
constructor(
|
|
5
|
+
error,
|
|
6
|
+
defaults = { message: 'Response error', status: 500 },
|
|
7
|
+
overrides
|
|
8
|
+
) {
|
|
5
9
|
const object = isPlainObject(error)
|
|
6
10
|
? error
|
|
7
11
|
: error instanceof Error
|
|
8
|
-
?
|
|
9
|
-
// Copy error into object so they can be merged with defaults after.
|
|
10
|
-
// First copy everything that is enumerable, unless the error is from
|
|
11
|
-
// axios, in which case we don't want to leak config information.
|
|
12
|
-
...(error.isAxiosError ? null : error),
|
|
13
|
-
// Also explicitly copy message, status and code.
|
|
14
|
-
message: error.message,
|
|
15
|
-
status: error.status,
|
|
16
|
-
code: error.code
|
|
17
|
-
}
|
|
12
|
+
? getErrorObject(error)
|
|
18
13
|
: isString(error)
|
|
19
14
|
? { message: error }
|
|
20
15
|
: error || {}
|
|
21
|
-
const { status, cause, ...data } = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
const { message, status, stack, cause, ...data } = {
|
|
17
|
+
...defaults,
|
|
18
|
+
...object,
|
|
19
|
+
...overrides
|
|
20
|
+
}
|
|
21
|
+
super(message, cause ? { cause } : {})
|
|
25
22
|
this.status = status
|
|
26
|
-
this.code = code
|
|
27
23
|
this.data = data
|
|
24
|
+
if (stack != null) {
|
|
25
|
+
this.stack = stack
|
|
26
|
+
}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
toJSON() {
|
|
31
|
-
return
|
|
30
|
+
return {
|
|
31
|
+
// Include the message in the JSON data sent back.
|
|
32
|
+
message: this.message,
|
|
33
|
+
...this.data
|
|
34
|
+
}
|
|
32
35
|
}
|
|
33
36
|
}
|
|
37
|
+
|
|
38
|
+
function getErrorObject(error) {
|
|
39
|
+
// For generic errors, explicitly copy message.
|
|
40
|
+
const object = error.toJSON?.() ?? { message: error.message }
|
|
41
|
+
// Additionally copy status and code if present.
|
|
42
|
+
if (error.status != null) {
|
|
43
|
+
object.status = error.status
|
|
44
|
+
}
|
|
45
|
+
if (error.code != null) {
|
|
46
|
+
object.code = error.code
|
|
47
|
+
}
|
|
48
|
+
// Preserve the cause if already set in the original error, and set it to the
|
|
49
|
+
// error itself otherwise.
|
|
50
|
+
object.cause = error.cause ?? error
|
|
51
|
+
return object
|
|
52
|
+
}
|
package/src/errors/index.js
CHANGED
package/src/models/Model.js
CHANGED
|
@@ -16,8 +16,7 @@ import {
|
|
|
16
16
|
ResponseError,
|
|
17
17
|
GraphError, ModelError,
|
|
18
18
|
NotFoundError,
|
|
19
|
-
RelationError
|
|
20
|
-
WrappedError
|
|
19
|
+
RelationError
|
|
21
20
|
} from '../errors/index.js'
|
|
22
21
|
import RelationAccessor from './RelationAccessor.js'
|
|
23
22
|
import definitions from './definitions/index.js'
|
|
@@ -239,7 +238,7 @@ export class Model extends objection.Model {
|
|
|
239
238
|
// TODO: Shouldn't this wrapping happen on the Controller level?
|
|
240
239
|
err = err instanceof ResponseError ? err
|
|
241
240
|
: err instanceof objection.DBError ? this.app.createDatabaseError(err)
|
|
242
|
-
: new
|
|
241
|
+
: new ResponseError(err)
|
|
243
242
|
return Promise.reject(err)
|
|
244
243
|
})
|
|
245
244
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs from 'fs
|
|
1
|
+
import fs from 'fs/promises'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import multer from '@koa/multer'
|
|
4
4
|
import { Storage } from './Storage.js'
|
|
@@ -15,7 +15,7 @@ export class DiskStorage extends Storage {
|
|
|
15
15
|
// Add `storageFile.key` property to internal storage file object.
|
|
16
16
|
storageFile.key = this.getUniqueKey(storageFile.originalname)
|
|
17
17
|
const dir = this._getPath(this._getNestedFolder(storageFile.key))
|
|
18
|
-
fs.
|
|
18
|
+
fs.mkdir(dir, { recursive: true })
|
|
19
19
|
.then(() => cb(null, dir))
|
|
20
20
|
.catch(cb)
|
|
21
21
|
},
|
|
@@ -41,7 +41,7 @@ export class DiskStorage extends Storage {
|
|
|
41
41
|
async _addFile(file, buffer) {
|
|
42
42
|
const filePath = this._getFilePath(file)
|
|
43
43
|
const dir = path.dirname(filePath)
|
|
44
|
-
await fs.
|
|
44
|
+
await fs.mkdir(dir, { recursive: true })
|
|
45
45
|
await fs.writeFile(filePath, buffer)
|
|
46
46
|
return file
|
|
47
47
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1617,8 +1617,7 @@ export class ResponseError extends Error {
|
|
|
1617
1617
|
export class AssetError extends ResponseError {}
|
|
1618
1618
|
export class AuthenticationError extends ResponseError {}
|
|
1619
1619
|
export class AuthorizationError extends ResponseError {}
|
|
1620
|
-
export class
|
|
1621
|
-
export class DatabaseError extends WrappedError {
|
|
1620
|
+
export class DatabaseError extends ResponseError {
|
|
1622
1621
|
constructor(
|
|
1623
1622
|
error:
|
|
1624
1623
|
| dbErrors.CheckViolationError
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ResponseError } from './ResponseError.js'
|
|
2
|
-
|
|
3
|
-
export class WrappedError extends ResponseError {
|
|
4
|
-
constructor(error, overrides, defaults = {
|
|
5
|
-
message: 'Wrapped error',
|
|
6
|
-
status: 400
|
|
7
|
-
}) {
|
|
8
|
-
super(
|
|
9
|
-
overrides
|
|
10
|
-
? Object.setPrototypeOf({ ...overrides }, error)
|
|
11
|
-
: error,
|
|
12
|
-
defaults
|
|
13
|
-
)
|
|
14
|
-
if (error?.stack) {
|
|
15
|
-
this.stack = error.stack
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|