@ditojs/server 1.4.3 → 1.5.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 +15 -16
- package/src/app/Application.js +30 -14
- package/src/controllers/AdminController.js +7 -31
- package/src/controllers/CollectionController.js +12 -11
- package/src/models/Model.js +3 -5
- package/src/schema/relations.test.js +1 -1
- package/src/storage/Storage.js +9 -0
- package/src/utils/glob.js +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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,22 +21,22 @@
|
|
|
21
21
|
"node >= 16"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ditojs/admin": "^1.
|
|
25
|
-
"@ditojs/
|
|
26
|
-
"@ditojs/
|
|
27
|
-
"@
|
|
24
|
+
"@ditojs/admin": "^1.5.0",
|
|
25
|
+
"@ditojs/build": "^1.5.0",
|
|
26
|
+
"@ditojs/router": "^1.5.0",
|
|
27
|
+
"@ditojs/utils": "^1.5.0",
|
|
28
|
+
"@koa/cors": "^3.3.0",
|
|
28
29
|
"@koa/multer": "^3.0.0",
|
|
29
30
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
|
30
31
|
"ajv": "^8.11.0",
|
|
31
32
|
"ajv-formats": "^2.1.1",
|
|
32
|
-
"aws-sdk": "^2.
|
|
33
|
+
"aws-sdk": "^2.1106.0",
|
|
33
34
|
"axios": "^0.26.1",
|
|
34
35
|
"bcryptjs": "^2.4.3",
|
|
35
36
|
"bytes": "^3.1.2",
|
|
36
37
|
"data-uri-to-buffer": "^4.0.0",
|
|
37
38
|
"eventemitter2": "^6.4.5",
|
|
38
39
|
"file-type": "^17.1.1",
|
|
39
|
-
"find-up": "^6.3.0",
|
|
40
40
|
"fs-extra": "^10.0.1",
|
|
41
41
|
"image-size": "^1.0.1",
|
|
42
42
|
"is-svg": "^4.3.2",
|
|
@@ -55,31 +55,30 @@
|
|
|
55
55
|
"mime-types": "^2.1.35",
|
|
56
56
|
"multer": "^1.4.4",
|
|
57
57
|
"multer-s3": "^2.10.0",
|
|
58
|
-
"nanoid": "^3.3.
|
|
58
|
+
"nanoid": "^3.3.2",
|
|
59
59
|
"parse-duration": "^1.0.2",
|
|
60
60
|
"passport-local": "^1.0.0",
|
|
61
61
|
"passthrough-counter": "^1.0.0",
|
|
62
62
|
"picocolors": "^1.0.0",
|
|
63
|
-
"picomatch": "^2.3.1",
|
|
64
63
|
"pino": "^7.9.2",
|
|
65
|
-
"pino-pretty": "^7.
|
|
64
|
+
"pino-pretty": "^7.6.0",
|
|
66
65
|
"pluralize": "^8.0.0",
|
|
67
66
|
"repl": "^0.1.3",
|
|
68
67
|
"uuid": "^8.3.2",
|
|
69
|
-
"vite": "^2.
|
|
68
|
+
"vite": "^2.9.1",
|
|
70
69
|
"vite-plugin-vue2": "^1.9.3",
|
|
71
70
|
"vue": "^2.6.14",
|
|
72
71
|
"vue-template-compiler": "^2.6.14"
|
|
73
72
|
},
|
|
74
73
|
"peerDependencies": {
|
|
75
|
-
"knex": "^0.
|
|
76
|
-
"objection": "^
|
|
74
|
+
"knex": "^1.0.4",
|
|
75
|
+
"objection": "^3.0.1"
|
|
77
76
|
},
|
|
78
77
|
"devDependencies": {
|
|
79
|
-
"knex": "^0.
|
|
80
|
-
"objection": "^
|
|
78
|
+
"knex": "^1.0.4",
|
|
79
|
+
"objection": "^3.0.1",
|
|
81
80
|
"pg": "^8.7.3",
|
|
82
81
|
"sqlite3": "^5.0.2"
|
|
83
82
|
},
|
|
84
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "36a57512228cc233211bf6e4038123a9e775f5c5"
|
|
85
84
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import os from 'os'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import util from 'util'
|
|
4
|
+
import zlib from 'zlib'
|
|
5
|
+
import fs from 'fs-extra'
|
|
1
6
|
import Koa from 'koa'
|
|
2
7
|
import Knex from 'knex'
|
|
3
|
-
import util from 'util'
|
|
4
8
|
import axios from 'axios'
|
|
5
9
|
import pico from 'picocolors'
|
|
6
|
-
import zlib from 'zlib'
|
|
7
10
|
import pino from 'pino'
|
|
8
|
-
import os from 'os'
|
|
9
11
|
import parseDuration from 'parse-duration'
|
|
10
12
|
import bodyParser from 'koa-bodyparser'
|
|
11
13
|
import cors from '@koa/cors'
|
|
@@ -46,11 +48,10 @@ import {
|
|
|
46
48
|
handleUser,
|
|
47
49
|
logRequests
|
|
48
50
|
} from '../middleware/index.js'
|
|
49
|
-
import
|
|
51
|
+
import {
|
|
50
52
|
Model,
|
|
51
53
|
BelongsToOneRelation,
|
|
52
|
-
|
|
53
|
-
// knexSnakeCaseMappers,
|
|
54
|
+
knexSnakeCaseMappers,
|
|
54
55
|
ref
|
|
55
56
|
} from 'objection'
|
|
56
57
|
|
|
@@ -626,7 +627,7 @@ export class Application extends Koa {
|
|
|
626
627
|
if (snakeCaseOptions) {
|
|
627
628
|
knex = {
|
|
628
629
|
...knex,
|
|
629
|
-
...
|
|
630
|
+
...knexSnakeCaseMappers(snakeCaseOptions)
|
|
630
631
|
}
|
|
631
632
|
}
|
|
632
633
|
this.knex = Knex(knex)
|
|
@@ -853,23 +854,38 @@ export class Application extends Koa {
|
|
|
853
854
|
if (file.data || file.url) {
|
|
854
855
|
let { data } = file
|
|
855
856
|
if (!data) {
|
|
857
|
+
const { url } = file
|
|
858
|
+
if (!storage.isImportSourceAllowed(url)) {
|
|
859
|
+
throw new AssetError(
|
|
860
|
+
`Unable to import asset from foreign source: '${
|
|
861
|
+
file.name
|
|
862
|
+
}' ('${
|
|
863
|
+
url
|
|
864
|
+
}'): The source needs to be explicitly allowed.`
|
|
865
|
+
)
|
|
866
|
+
}
|
|
856
867
|
console.info(
|
|
857
868
|
`${
|
|
858
869
|
pico.red('INFO:')
|
|
859
870
|
} Asset ${
|
|
860
871
|
pico.green(`'${file.name}'`)
|
|
861
872
|
} is from a foreign source, fetching from ${
|
|
862
|
-
pico.green(`'${
|
|
873
|
+
pico.green(`'${url}'`)
|
|
863
874
|
} and adding to storage ${
|
|
864
875
|
pico.green(`'${storage.name}'`)
|
|
865
876
|
}...`
|
|
866
877
|
)
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
878
|
+
if (url.startsWith('file://')) {
|
|
879
|
+
const filepath = path.resolve(url.substring(7))
|
|
880
|
+
data = await fs.readFile(filepath)
|
|
881
|
+
} else {
|
|
882
|
+
const response = await axios.request({
|
|
883
|
+
method: 'get',
|
|
884
|
+
responseType: 'arraybuffer',
|
|
885
|
+
url
|
|
886
|
+
})
|
|
887
|
+
data = response.data
|
|
888
|
+
}
|
|
873
889
|
}
|
|
874
890
|
const importedFile = await storage.addFile(file, data)
|
|
875
891
|
await this.createAssets(storage, [importedFile], 0, trx)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import fs from 'fs'
|
|
3
2
|
import Koa from 'koa'
|
|
4
3
|
import serve from 'koa-static'
|
|
5
4
|
import { defineConfig, createServer } from 'vite'
|
|
@@ -7,8 +6,10 @@ import { createVuePlugin } from 'vite-plugin-vue2'
|
|
|
7
6
|
import {
|
|
8
7
|
viteCommonjs as createCommonJsPlugin
|
|
9
8
|
} from '@originjs/vite-plugin-commonjs'
|
|
10
|
-
import
|
|
11
|
-
|
|
9
|
+
import {
|
|
10
|
+
createRollupImportsResolver,
|
|
11
|
+
testModuleIdentifier
|
|
12
|
+
} from '@ditojs/build'
|
|
12
13
|
import { merge } from '@ditojs/utils'
|
|
13
14
|
import { Controller } from './Controller.js'
|
|
14
15
|
import { handleConnectMiddleware } from '../middleware/index.js'
|
|
@@ -135,16 +136,11 @@ export class AdminController extends Controller {
|
|
|
135
136
|
getViteConfig(config = {}) {
|
|
136
137
|
const development = this.mode === 'development'
|
|
137
138
|
|
|
138
|
-
const cwd =
|
|
139
|
+
const cwd = process.cwd()
|
|
139
140
|
const root = this.getPath('root')
|
|
140
141
|
const base = `${this.url}/`
|
|
141
142
|
const views = path.join(root, 'views')
|
|
142
143
|
|
|
143
|
-
// Read `package.json` from the closest package.json, so we can emulate
|
|
144
|
-
// ESM-style imports mappings in rollup / vite.
|
|
145
|
-
const pkg = findUpSync('package.json', { cwd: root })
|
|
146
|
-
const { imports = {} } = JSON.parse(fs.readFileSync(pkg, 'utf8'))
|
|
147
|
-
|
|
148
144
|
return defineConfig(merge({
|
|
149
145
|
root,
|
|
150
146
|
base,
|
|
@@ -185,7 +181,7 @@ export class AdminController extends Controller {
|
|
|
185
181
|
return 'common'
|
|
186
182
|
} else {
|
|
187
183
|
const module = id.match(/node_modules\/([^/$]*)/)?.[1] || ''
|
|
188
|
-
return
|
|
184
|
+
return testModuleIdentifier(module, CORE_DEPENDENCIES)
|
|
189
185
|
? 'core'
|
|
190
186
|
: 'vendor'
|
|
191
187
|
}
|
|
@@ -210,27 +206,7 @@ export class AdminController extends Controller {
|
|
|
210
206
|
find: '@',
|
|
211
207
|
replacement: root
|
|
212
208
|
},
|
|
213
|
-
{
|
|
214
|
-
// Use a custom rollup resolver to emulate ESM-style imports
|
|
215
|
-
// mappings in vite, as read from `package.json` above:
|
|
216
|
-
find: /^#/,
|
|
217
|
-
replacement: '#',
|
|
218
|
-
customResolver(id) {
|
|
219
|
-
for (const [find, replacement] of Object.entries(imports)) {
|
|
220
|
-
picomatch.isMatch(id, find.replace('*', '**'), {
|
|
221
|
-
capture: true,
|
|
222
|
-
onMatch({ input, regex }) {
|
|
223
|
-
const replacementPath = path.resolve(replacement)
|
|
224
|
-
const match = input.match(regex)?.[1]
|
|
225
|
-
id = match
|
|
226
|
-
? replacementPath.replace('*', match)
|
|
227
|
-
: replacementPath
|
|
228
|
-
}
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
return id
|
|
232
|
-
}
|
|
233
|
-
}
|
|
209
|
+
createRollupImportsResolver({ cwd: root })
|
|
234
210
|
]
|
|
235
211
|
}
|
|
236
212
|
}, config))
|
|
@@ -68,17 +68,18 @@ export class CollectionController extends Controller {
|
|
|
68
68
|
return this.extendContext(ctx, { memberId })
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
getModelId(model) {
|
|
72
72
|
const idProperty = this.modelClass.getIdProperty()
|
|
73
73
|
// Handle both composite keys and normal ones.
|
|
74
|
-
|
|
75
|
-
?
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
return isArray(idProperty)
|
|
75
|
+
? idProperty.map(property => model[property])
|
|
76
|
+
: model[idProperty]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getCollectionIds(ctx) {
|
|
80
|
+
return asArray(ctx.request.body).map(
|
|
81
|
+
model => this.validateId(this.getModelId(model))
|
|
82
|
+
)
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
getIds(ctx) {
|
|
@@ -219,9 +220,9 @@ export class CollectionController extends Controller {
|
|
|
219
220
|
.modify(getModify(modify, trx))
|
|
220
221
|
)
|
|
221
222
|
: await this.executeAndFetch('insert', ctx, modify)
|
|
222
|
-
ctx.status = 201
|
|
223
|
+
ctx.status = 201 // Created
|
|
223
224
|
if (isObject(result)) {
|
|
224
|
-
ctx.set('Location', this.getUrl('collection', result
|
|
225
|
+
ctx.set('Location', this.getUrl('collection', this.getModelId(result)))
|
|
225
226
|
}
|
|
226
227
|
return result
|
|
227
228
|
},
|
package/src/models/Model.js
CHANGED
|
@@ -929,11 +929,9 @@ export class Model extends objection.Model {
|
|
|
929
929
|
)
|
|
930
930
|
: assetDataPaths
|
|
931
931
|
|
|
932
|
-
// `dataPaths`
|
|
932
|
+
// `dataPaths` is empty in the case of an update/insert that does not
|
|
933
933
|
// affect the assets.
|
|
934
|
-
if (dataPaths.length === 0)
|
|
935
|
-
return
|
|
936
|
-
}
|
|
934
|
+
if (dataPaths.length === 0) return
|
|
937
935
|
|
|
938
936
|
// Load the model's asset files in their current state before the query is
|
|
939
937
|
// executed.
|
|
@@ -972,7 +970,7 @@ export class Model extends objection.Model {
|
|
|
972
970
|
if (modifiedFiles.length > 0) {
|
|
973
971
|
// TODO: `modifiedFiles` should be restored as well, but that's far
|
|
974
972
|
// from trivial since no backup is kept in `handleModifiedAssets`
|
|
975
|
-
console.
|
|
973
|
+
console.warn(
|
|
976
974
|
`Unable to restore these already modified files: ${
|
|
977
975
|
modifiedFiles.map(file => `'${file.name}'`)
|
|
978
976
|
}`
|
package/src/storage/Storage.js
CHANGED
|
@@ -5,6 +5,7 @@ import { PassThrough } from 'stream'
|
|
|
5
5
|
import { URL } from 'url'
|
|
6
6
|
import { hyphenate, toPromiseCallback } from '@ditojs/utils'
|
|
7
7
|
import { AssetFile } from './AssetFile.js'
|
|
8
|
+
import { matchGlobPattern } from '../utils/glob.js'
|
|
8
9
|
|
|
9
10
|
const storageClasses = {}
|
|
10
11
|
|
|
@@ -57,6 +58,14 @@ export class Storage {
|
|
|
57
58
|
return AssetFile.getUniqueKey(name)
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
isImportSourceAllowed(url) {
|
|
62
|
+
const { allowedImports = [] } = this.config
|
|
63
|
+
for (const pattern of allowedImports) {
|
|
64
|
+
if (matchGlobPattern(url, pattern)) return true
|
|
65
|
+
}
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
convertAssetFile(file) {
|
|
61
70
|
return AssetFile.convert(file, this)
|
|
62
71
|
}
|