@ditojs/server 1.1.0 → 1.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"node >= 14"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ditojs/admin": "^1.
|
|
25
|
-
"@ditojs/router": "^1.
|
|
26
|
-
"@ditojs/utils": "^1.
|
|
24
|
+
"@ditojs/admin": "^1.3.0",
|
|
25
|
+
"@ditojs/router": "^1.3.0",
|
|
26
|
+
"@ditojs/utils": "^1.3.0",
|
|
27
27
|
"@koa/cors": "^3.2.0",
|
|
28
28
|
"@koa/multer": "^3.0.0",
|
|
29
29
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
|
@@ -53,7 +53,6 @@
|
|
|
53
53
|
"koa-session": "^6.2.0",
|
|
54
54
|
"koa-static": "^5.0.0",
|
|
55
55
|
"mime-types": "^2.1.35",
|
|
56
|
-
"minimatch": "^5.0.1",
|
|
57
56
|
"multer": "^1.4.4",
|
|
58
57
|
"multer-s3": "^2.10.0",
|
|
59
58
|
"nanoid": "^3.3.1",
|
|
@@ -82,5 +81,5 @@
|
|
|
82
81
|
"pg": "^8.7.3",
|
|
83
82
|
"sqlite3": "^5.0.2"
|
|
84
83
|
},
|
|
85
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "23aac9eddfc17e3e9367db2b7ac73595bc2bad37"
|
|
86
85
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -122,6 +122,11 @@ export class Application extends Koa {
|
|
|
122
122
|
controller,
|
|
123
123
|
action
|
|
124
124
|
}
|
|
125
|
+
if (!(method in this.router)) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Unsupported HTTP method '${method}' in route '${path}'`
|
|
128
|
+
)
|
|
129
|
+
}
|
|
125
130
|
this.router[method](path, route)
|
|
126
131
|
}
|
|
127
132
|
|
package/src/app/Validator.js
CHANGED
|
@@ -193,12 +193,14 @@ export class Validator extends objection.Validator {
|
|
|
193
193
|
for (const error of errors) {
|
|
194
194
|
// Adjust dataPaths to reflect nested validation in Objection.
|
|
195
195
|
// NOTE: As of Ajv 8, `error.dataPath` is now called `error.instancePath`,
|
|
196
|
-
// but we stick to `error.dataPath` in Dito.js
|
|
197
|
-
|
|
196
|
+
// but we stick to `error.dataPath` in Dito.js, and support both in errors
|
|
197
|
+
// passed in here.
|
|
198
|
+
const instancePath = (error.instancePath ?? error.dataPath) || ''
|
|
199
|
+
const dataPath = `${options?.dataPath || ''}${instancePath}`
|
|
198
200
|
// Unknown properties are reported in `['propertyName']` notation,
|
|
199
201
|
// so replace those with dot-notation, see:
|
|
200
202
|
// https://github.com/epoberezkin/ajv/issues/671
|
|
201
|
-
const key = dataPath.replace(/\['([^']*)'\]/g, '
|
|
203
|
+
const key = dataPath.replace(/\['([^']*)'\]/g, '/$1').slice(1)
|
|
202
204
|
const { message, keyword, params } = error
|
|
203
205
|
const definition = keyword === 'format'
|
|
204
206
|
? this.getFormat(params.format)
|
|
@@ -13,7 +13,7 @@ import { merge } from '@ditojs/utils'
|
|
|
13
13
|
import { Controller } from './Controller.js'
|
|
14
14
|
import { handleConnectMiddleware } from '../middleware/index.js'
|
|
15
15
|
import { ControllerError } from '../errors/index.js'
|
|
16
|
-
import { formatJson } from '../utils/index.js'
|
|
16
|
+
import { formatJson, deprecate } from '../utils/index.js'
|
|
17
17
|
|
|
18
18
|
export class AdminController extends Controller {
|
|
19
19
|
// @override
|
|
@@ -34,11 +34,19 @@ export class AdminController extends Controller {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
getPath(name) {
|
|
37
|
-
const
|
|
37
|
+
const { config } = this
|
|
38
|
+
let str = config[name]
|
|
39
|
+
if (config.build?.path || config.dist?.path) {
|
|
40
|
+
deprecate(`config.admin.build.path and config.admin.dist.path are deprecated. Use config.admin.root and config.admin.dist instead.`)
|
|
41
|
+
|
|
42
|
+
str = name === 'root' ? config.build?.path
|
|
43
|
+
: name === 'dist' ? config.dist?.path
|
|
44
|
+
: null
|
|
45
|
+
}
|
|
38
46
|
if (!str) {
|
|
39
47
|
throw new ControllerError(
|
|
40
48
|
this,
|
|
41
|
-
`Missing admin.${name}
|
|
49
|
+
`Missing \`config.admin.${name}\` configuration.`
|
|
42
50
|
)
|
|
43
51
|
}
|
|
44
52
|
return path.resolve(str)
|
|
@@ -85,8 +93,8 @@ export class AdminController extends Controller {
|
|
|
85
93
|
this.koa = new Koa()
|
|
86
94
|
this.koa.use(this.middleware())
|
|
87
95
|
if (this.mode === 'development') {
|
|
88
|
-
// Calling getPath() throws exception if admin.
|
|
89
|
-
if (this.getPath('
|
|
96
|
+
// Calling getPath() throws exception if config.admin.root is not defined:
|
|
97
|
+
if (this.getPath('root')) {
|
|
90
98
|
this.app.once('after:start', () => this.setupViteServer())
|
|
91
99
|
}
|
|
92
100
|
} else {
|
|
@@ -128,14 +136,14 @@ export class AdminController extends Controller {
|
|
|
128
136
|
const development = this.mode === 'development'
|
|
129
137
|
|
|
130
138
|
const cwd = path.resolve('.')
|
|
131
|
-
const root = this.getPath('
|
|
139
|
+
const root = this.getPath('root')
|
|
132
140
|
const base = `${this.url}/`
|
|
133
141
|
const views = path.join(root, 'views')
|
|
134
142
|
|
|
135
143
|
// Read `package.json` from the closest package.json, so we can emulate
|
|
136
144
|
// ESM-style imports mappings in rollup / vite.
|
|
137
145
|
const pkg = findUpSync('package.json', { cwd: root })
|
|
138
|
-
const { imports } = JSON.parse(fs.readFileSync(pkg, 'utf8'))
|
|
146
|
+
const { imports = {} } = JSON.parse(fs.readFileSync(pkg, 'utf8'))
|
|
139
147
|
|
|
140
148
|
return defineConfig(merge({
|
|
141
149
|
root,
|
|
@@ -209,7 +217,7 @@ export class AdminController extends Controller {
|
|
|
209
217
|
replacement: '#',
|
|
210
218
|
customResolver(id) {
|
|
211
219
|
for (const [find, replacement] of Object.entries(imports)) {
|
|
212
|
-
picomatch.isMatch(id, find, {
|
|
220
|
+
picomatch.isMatch(id, find.replace('*', '**'), {
|
|
213
221
|
capture: true,
|
|
214
222
|
onMatch({ input, regex }) {
|
|
215
223
|
const replacementPath = path.resolve(replacement)
|
|
@@ -176,7 +176,7 @@ export class CollectionController extends Controller {
|
|
|
176
176
|
)
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
convertToCoreActions(actions) {
|
|
180
180
|
// Mark action object and methods as core, so `Controller.processValues()`
|
|
181
181
|
// can filter correctly.
|
|
182
182
|
for (const action of Object.values(actions)) {
|
|
@@ -188,7 +188,7 @@ export class CollectionController extends Controller {
|
|
|
188
188
|
return actions
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
collection = this.
|
|
191
|
+
collection = this.convertToCoreActions({
|
|
192
192
|
async get(ctx, modify) {
|
|
193
193
|
const result = await this.execute(ctx, (query, trx) => {
|
|
194
194
|
query.find(ctx.query, this.allowParam).modify(getModify(modify, trx))
|
|
@@ -235,7 +235,7 @@ export class CollectionController extends Controller {
|
|
|
235
235
|
}
|
|
236
236
|
})
|
|
237
237
|
|
|
238
|
-
member = this.
|
|
238
|
+
member = this.convertToCoreActions({
|
|
239
239
|
async get(ctx, modify) {
|
|
240
240
|
return this.execute(ctx, (query, trx) => query
|
|
241
241
|
.findById(ctx.memberId)
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
ResponseError, WrappedError, ControllerError, AuthorizationError
|
|
7
7
|
} from '../errors/index.js'
|
|
8
8
|
import {
|
|
9
|
-
getOwnProperty, getAllKeys, processHandlerParameters,
|
|
10
|
-
formatJson, deprecate
|
|
9
|
+
getOwnProperty, getOwnKeys, getAllKeys, processHandlerParameters,
|
|
10
|
+
describeFunction, formatJson, deprecate
|
|
11
11
|
} from '../utils/index.js'
|
|
12
12
|
import {
|
|
13
13
|
isObject, isString, isArray, isBoolean, isFunction, asArray, equals,
|
|
@@ -312,43 +312,44 @@ export class Controller {
|
|
|
312
312
|
// NOTE: `handleAllow()` and `handleAuthorize()` are applied in sequence of
|
|
313
313
|
// the `values` inheritance, from sub-class to base-class.
|
|
314
314
|
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
let hasOwnAllow = false
|
|
315
|
+
let allowMap = {}
|
|
316
|
+
const authorizeMap = {}
|
|
318
317
|
|
|
319
|
-
const
|
|
318
|
+
const includeKey = key => !['allow', 'authorize'].includes(key)
|
|
320
319
|
|
|
321
320
|
const handleAllow = (allow, current) => {
|
|
321
|
+
const getFilteredMap = keys =>
|
|
322
|
+
Object.fromEntries(keys.filter(includeKey).map(key => [key, true]))
|
|
323
|
+
|
|
322
324
|
if (allow) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
//
|
|
327
|
-
//
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
325
|
+
// The controller action object provides its own allow setting:
|
|
326
|
+
// - Clear whatever has been collected in `mergedAllow` so far
|
|
327
|
+
// - Merge the `allow` setting with all the own keys of the object,
|
|
328
|
+
// unless:
|
|
329
|
+
// - If the allow setting includes '*', allow all keys of the object,
|
|
330
|
+
// even the inherited ones.
|
|
331
|
+
let keys = asArray(allow)
|
|
332
|
+
if (keys.includes('*')) {
|
|
333
|
+
keys = getAllKeys(current)
|
|
334
|
+
} else {
|
|
335
|
+
keys = [
|
|
336
|
+
...keys,
|
|
337
|
+
...getOwnKeys(current)
|
|
338
|
+
]
|
|
338
339
|
}
|
|
340
|
+
allowMap = getFilteredMap(keys) // Clear previous keys by overriding.
|
|
341
|
+
} else {
|
|
342
|
+
// The controller action object does not provide its own allow setting,
|
|
343
|
+
// so add its own keys to the already allowed inherited keys so far.
|
|
344
|
+
Object.assign(allowMap, getFilteredMap(getOwnKeys(current)))
|
|
339
345
|
}
|
|
346
|
+
// console.log('allow', Object.keys(allowMap))
|
|
340
347
|
}
|
|
341
348
|
|
|
342
349
|
const handleAuthorize = authorize => {
|
|
343
350
|
const add = (key, value) => {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
key in values &&
|
|
348
|
-
!(key in mergedAuthorize) &&
|
|
349
|
-
!excludeKey(key)
|
|
350
|
-
) {
|
|
351
|
-
mergedAuthorize[key] = value
|
|
351
|
+
if (key in values && includeKey(key)) {
|
|
352
|
+
authorizeMap[key] = value
|
|
352
353
|
}
|
|
353
354
|
}
|
|
354
355
|
|
|
@@ -365,20 +366,23 @@ export class Controller {
|
|
|
365
366
|
}
|
|
366
367
|
}
|
|
367
368
|
|
|
368
|
-
// Process the `allow` and `authorize` settings in sequence of the
|
|
369
|
-
// inheritance, from
|
|
369
|
+
// Process the `allow` and `authorize` settings in reversed sequence of the
|
|
370
|
+
// `values` inheritance, from base-class to sub-class.
|
|
371
|
+
const chain = []
|
|
370
372
|
let current = values
|
|
371
373
|
while (current !== Object.prototype && !current.hasOwnProperty('$core')) {
|
|
372
|
-
|
|
373
|
-
handleAuthorize(getOwnProperty(current, 'authorize'))
|
|
374
|
+
chain.unshift(current)
|
|
374
375
|
current = Object.getPrototypeOf(current)
|
|
375
376
|
}
|
|
376
377
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
handleAllow(this.allow, values)
|
|
378
|
+
for (const current of chain) {
|
|
379
|
+
handleAllow(getOwnProperty(current, 'allow'), current)
|
|
380
|
+
handleAuthorize(getOwnProperty(current, 'authorize'))
|
|
381
381
|
}
|
|
382
|
+
|
|
383
|
+
// At the end of the chain, also support authorize settings on the
|
|
384
|
+
// controller-level, and thus applied to all action objects in the
|
|
385
|
+
// controller.
|
|
382
386
|
if (this.authorize) {
|
|
383
387
|
handleAuthorize(this.authorize)
|
|
384
388
|
}
|
|
@@ -387,7 +391,7 @@ export class Controller {
|
|
|
387
391
|
// Create a filtered `values` object that only contains the allowed fields
|
|
388
392
|
values: getAllKeys(values).reduce(
|
|
389
393
|
(result, key) => {
|
|
390
|
-
if (
|
|
394
|
+
if (allowMap[key]) {
|
|
391
395
|
result[key] = values[key]
|
|
392
396
|
}
|
|
393
397
|
return result
|
|
@@ -397,8 +401,8 @@ export class Controller {
|
|
|
397
401
|
// `super` in handler functions.
|
|
398
402
|
Object.create(Object.getPrototypeOf(values))
|
|
399
403
|
),
|
|
400
|
-
allow: Object.keys(
|
|
401
|
-
authorize:
|
|
404
|
+
allow: Object.keys(allowMap),
|
|
405
|
+
authorize: authorizeMap
|
|
402
406
|
}
|
|
403
407
|
}
|
|
404
408
|
|
package/src/utils/object.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { asArray } from '@ditojs/utils'
|
|
2
2
|
|
|
3
|
+
export const getOwnKeys = Object.keys
|
|
4
|
+
|
|
3
5
|
export function getAllKeys(object) {
|
|
4
|
-
// Unlike `
|
|
6
|
+
// Unlike `getOwnKeys()`, this returns all enumerable keys not just own ones.
|
|
5
7
|
const keys = []
|
|
6
8
|
for (const key in object) {
|
|
7
9
|
keys.push(key)
|