@ditojs/server 1.4.3 → 1.5.2
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 -15
- package/src/app/Application.js +77 -52
- package/src/controllers/AdminController.js +35 -31
- package/src/controllers/CollectionController.js +12 -11
- package/src/controllers/Controller.js +6 -6
- package/src/models/Model.js +3 -5
- package/src/schema/relations.test.js +1 -1
- package/src/services/Service.js +2 -0
- package/src/storage/Storage.js +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.2",
|
|
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.1108.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,31 @@
|
|
|
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
63
|
"picomatch": "^2.3.1",
|
|
64
64
|
"pino": "^7.9.2",
|
|
65
|
-
"pino-pretty": "^7.
|
|
65
|
+
"pino-pretty": "^7.6.0",
|
|
66
66
|
"pluralize": "^8.0.0",
|
|
67
67
|
"repl": "^0.1.3",
|
|
68
68
|
"uuid": "^8.3.2",
|
|
69
|
-
"vite": "^2.
|
|
69
|
+
"vite": "^2.9.1",
|
|
70
70
|
"vite-plugin-vue2": "^1.9.3",
|
|
71
71
|
"vue": "^2.6.14",
|
|
72
72
|
"vue-template-compiler": "^2.6.14"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
|
-
"knex": "^0.
|
|
76
|
-
"objection": "^
|
|
75
|
+
"knex": "^1.0.4",
|
|
76
|
+
"objection": "^3.0.1"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"knex": "^0.
|
|
80
|
-
"objection": "^
|
|
79
|
+
"knex": "^1.0.5",
|
|
80
|
+
"objection": "^3.0.1",
|
|
81
81
|
"pg": "^8.7.3",
|
|
82
82
|
"sqlite3": "^5.0.2"
|
|
83
83
|
},
|
|
84
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "b7861d7cea5426a8a487ce4c3b9d9f1ff3e908ce"
|
|
85
85
|
}
|
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'
|
|
@@ -20,8 +22,8 @@ import helmet from 'koa-helmet'
|
|
|
20
22
|
import responseTime from 'koa-response-time'
|
|
21
23
|
import Router from '@ditojs/router'
|
|
22
24
|
import {
|
|
23
|
-
isArray, isObject, isString, asArray, isPlainObject,
|
|
24
|
-
parseDataPath, normalizeDataPath,
|
|
25
|
+
isArray, isObject, isString, asArray, isPlainObject, isModule,
|
|
26
|
+
hyphenate, clone, merge, parseDataPath, normalizeDataPath, toPromiseCallback
|
|
25
27
|
} from '@ditojs/utils'
|
|
26
28
|
import SessionStore from './SessionStore.js'
|
|
27
29
|
import { Validator } from './Validator.js'
|
|
@@ -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
|
|
|
@@ -73,6 +74,7 @@ export class Application extends Koa {
|
|
|
73
74
|
log = {},
|
|
74
75
|
...rest
|
|
75
76
|
} = config
|
|
77
|
+
this.server = null
|
|
76
78
|
this.config = {
|
|
77
79
|
app,
|
|
78
80
|
log: log.silent || process.env.DITO_SILENT ? {} : log,
|
|
@@ -105,6 +107,10 @@ export class Application extends Koa {
|
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
|
|
110
|
+
get isRunning() {
|
|
111
|
+
return !!this.server
|
|
112
|
+
}
|
|
113
|
+
|
|
108
114
|
addRoute(
|
|
109
115
|
method, path, transacted, middlewares, controller = null, action = null
|
|
110
116
|
) {
|
|
@@ -282,10 +288,6 @@ export class Application extends Koa {
|
|
|
282
288
|
return Object.values(this.services).find(callback)
|
|
283
289
|
}
|
|
284
290
|
|
|
285
|
-
forEachService(callback) {
|
|
286
|
-
return Promise.all(Object.values(this.services).map(callback))
|
|
287
|
-
}
|
|
288
|
-
|
|
289
291
|
addControllers(controllers, namespace) {
|
|
290
292
|
for (const [key, value] of Object.entries(controllers)) {
|
|
291
293
|
if (isModule(value) || isPlainObject(value)) {
|
|
@@ -626,7 +628,7 @@ export class Application extends Koa {
|
|
|
626
628
|
if (snakeCaseOptions) {
|
|
627
629
|
knex = {
|
|
628
630
|
...knex,
|
|
629
|
-
...
|
|
631
|
+
...knexSnakeCaseMappers(snakeCaseOptions)
|
|
630
632
|
}
|
|
631
633
|
}
|
|
632
634
|
this.knex = Knex(knex)
|
|
@@ -717,46 +719,54 @@ export class Application extends Koa {
|
|
|
717
719
|
this.on('error', this.logError)
|
|
718
720
|
}
|
|
719
721
|
await this.emit('before:start')
|
|
720
|
-
await
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
const server = this.listen(port, host, () => {
|
|
724
|
-
const { port } = server.address()
|
|
722
|
+
this.server = await new Promise(resolve => {
|
|
723
|
+
const server = this.listen(this.config.server, () => {
|
|
724
|
+
const { address, port } = server.address()
|
|
725
725
|
console.info(
|
|
726
|
-
`Dito server started at http://${
|
|
726
|
+
`Dito.js server started at http://${address}:${port}`
|
|
727
727
|
)
|
|
728
728
|
resolve(server)
|
|
729
729
|
})
|
|
730
|
-
if (!server) {
|
|
731
|
-
reject(new Error(`Unable to start server at http://${host}:${port}`))
|
|
732
|
-
}
|
|
733
730
|
})
|
|
731
|
+
if (!this.server) {
|
|
732
|
+
throw new Error('Unable to start Dito.js server')
|
|
733
|
+
}
|
|
734
734
|
await this.emit('after:start')
|
|
735
735
|
}
|
|
736
736
|
|
|
737
|
-
async stop() {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
737
|
+
async stop(timeout = 0) {
|
|
738
|
+
if (!this.server) {
|
|
739
|
+
throw new Error('Dito.js server is not running')
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const promise = (async () => {
|
|
743
|
+
await this.emit('before:stop')
|
|
744
|
+
await new Promise((resolve, reject) => {
|
|
745
|
+
this.server.close(toPromiseCallback(resolve, reject))
|
|
746
|
+
})
|
|
747
|
+
// Hack to make sure that the server is closed, even if sockets are still
|
|
748
|
+
// open after `server.close()`, see: https://stackoverflow.com/a/36830072
|
|
749
|
+
this.server.emit('close')
|
|
750
|
+
this.server = null
|
|
751
|
+
await this.emit('after:stop')
|
|
752
|
+
})()
|
|
753
|
+
|
|
754
|
+
if (timeout > 0) {
|
|
755
|
+
await Promise.race([
|
|
756
|
+
promise,
|
|
757
|
+
new Promise((resolve, reject) =>
|
|
758
|
+
setTimeout(reject,
|
|
759
|
+
timeout,
|
|
760
|
+
new Error(
|
|
761
|
+
`Timeout reached while stopping Dito.js server (${timeout}ms)`
|
|
762
|
+
)
|
|
763
|
+
)
|
|
764
|
+
)
|
|
765
|
+
])
|
|
766
|
+
} else {
|
|
767
|
+
await promise
|
|
768
|
+
}
|
|
769
|
+
|
|
760
770
|
if (this.config.log.errors !== false) {
|
|
761
771
|
this.off('error', this.logError)
|
|
762
772
|
}
|
|
@@ -853,23 +863,38 @@ export class Application extends Koa {
|
|
|
853
863
|
if (file.data || file.url) {
|
|
854
864
|
let { data } = file
|
|
855
865
|
if (!data) {
|
|
866
|
+
const { url } = file
|
|
867
|
+
if (!storage.isImportSourceAllowed(url)) {
|
|
868
|
+
throw new AssetError(
|
|
869
|
+
`Unable to import asset from foreign source: '${
|
|
870
|
+
file.name
|
|
871
|
+
}' ('${
|
|
872
|
+
url
|
|
873
|
+
}'): The source needs to be explicitly allowed.`
|
|
874
|
+
)
|
|
875
|
+
}
|
|
856
876
|
console.info(
|
|
857
877
|
`${
|
|
858
878
|
pico.red('INFO:')
|
|
859
879
|
} Asset ${
|
|
860
880
|
pico.green(`'${file.name}'`)
|
|
861
881
|
} is from a foreign source, fetching from ${
|
|
862
|
-
pico.green(`'${
|
|
882
|
+
pico.green(`'${url}'`)
|
|
863
883
|
} and adding to storage ${
|
|
864
884
|
pico.green(`'${storage.name}'`)
|
|
865
885
|
}...`
|
|
866
886
|
)
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
887
|
+
if (url.startsWith('file://')) {
|
|
888
|
+
const filepath = path.resolve(url.substring(7))
|
|
889
|
+
data = await fs.readFile(filepath)
|
|
890
|
+
} else {
|
|
891
|
+
const response = await axios.request({
|
|
892
|
+
method: 'get',
|
|
893
|
+
responseType: 'arraybuffer',
|
|
894
|
+
url
|
|
895
|
+
})
|
|
896
|
+
data = response.data
|
|
897
|
+
}
|
|
873
898
|
}
|
|
874
899
|
const importedFile = await storage.addFile(file, data)
|
|
875
900
|
await this.createAssets(storage, [importedFile], 0, trx)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import
|
|
2
|
+
import { exit } from 'process'
|
|
3
3
|
import Koa from 'koa'
|
|
4
4
|
import serve from 'koa-static'
|
|
5
5
|
import { defineConfig, createServer } from 'vite'
|
|
@@ -7,8 +7,10 @@ import { createVuePlugin } from 'vite-plugin-vue2'
|
|
|
7
7
|
import {
|
|
8
8
|
viteCommonjs as createCommonJsPlugin
|
|
9
9
|
} from '@originjs/vite-plugin-commonjs'
|
|
10
|
-
import
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
createRollupImportsResolver,
|
|
12
|
+
testModuleIdentifier
|
|
13
|
+
} from '@ditojs/build'
|
|
12
14
|
import { merge } from '@ditojs/utils'
|
|
13
15
|
import { Controller } from './Controller.js'
|
|
14
16
|
import { handleConnectMiddleware } from '../middleware/index.js'
|
|
@@ -89,6 +91,7 @@ export class AdminController extends Controller {
|
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
// @override
|
|
92
95
|
compose() {
|
|
93
96
|
this.koa = new Koa()
|
|
94
97
|
this.koa.use(this.middleware())
|
|
@@ -127,6 +130,32 @@ export class AdminController extends Controller {
|
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
})
|
|
133
|
+
|
|
134
|
+
let closed = false
|
|
135
|
+
|
|
136
|
+
// Monkey-patch `process.exit()` to filter out the calls caused by vite's
|
|
137
|
+
// handling of SIGTERM, see: https://github.com/vitejs/vite/issues/7627
|
|
138
|
+
process.exit = code => {
|
|
139
|
+
// Filter out calls from inside vite by looking at the stack trace.
|
|
140
|
+
if (new Error().stack.includes('/vite/dist/')) {
|
|
141
|
+
// vite's own `exitProcess()` just called `process.exit(), and this
|
|
142
|
+
// means it has already called `server.close()` internally.
|
|
143
|
+
closed = true
|
|
144
|
+
process.exit = exit
|
|
145
|
+
} else {
|
|
146
|
+
exit(code)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.app.once('before:stop', () => {
|
|
151
|
+
// For good timing it seems crucial to not add more ticks with async
|
|
152
|
+
// signature, so we directly return the `server.close()` promise instead.
|
|
153
|
+
process.exit = exit
|
|
154
|
+
if (!closed) {
|
|
155
|
+
closed = true
|
|
156
|
+
return server.close()
|
|
157
|
+
}
|
|
158
|
+
})
|
|
130
159
|
this.koa.use(handleConnectMiddleware(server.middlewares, {
|
|
131
160
|
expandMountPath: true
|
|
132
161
|
}))
|
|
@@ -135,16 +164,11 @@ export class AdminController extends Controller {
|
|
|
135
164
|
getViteConfig(config = {}) {
|
|
136
165
|
const development = this.mode === 'development'
|
|
137
166
|
|
|
138
|
-
const cwd =
|
|
167
|
+
const cwd = process.cwd()
|
|
139
168
|
const root = this.getPath('root')
|
|
140
169
|
const base = `${this.url}/`
|
|
141
170
|
const views = path.join(root, 'views')
|
|
142
171
|
|
|
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
172
|
return defineConfig(merge({
|
|
149
173
|
root,
|
|
150
174
|
base,
|
|
@@ -185,7 +209,7 @@ export class AdminController extends Controller {
|
|
|
185
209
|
return 'common'
|
|
186
210
|
} else {
|
|
187
211
|
const module = id.match(/node_modules\/([^/$]*)/)?.[1] || ''
|
|
188
|
-
return
|
|
212
|
+
return testModuleIdentifier(module, CORE_DEPENDENCIES)
|
|
189
213
|
? 'core'
|
|
190
214
|
: 'vendor'
|
|
191
215
|
}
|
|
@@ -210,27 +234,7 @@ export class AdminController extends Controller {
|
|
|
210
234
|
find: '@',
|
|
211
235
|
replacement: root
|
|
212
236
|
},
|
|
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
|
-
}
|
|
237
|
+
createRollupImportsResolver({ cwd: root })
|
|
234
238
|
]
|
|
235
239
|
}
|
|
236
240
|
}, 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
|
},
|
|
@@ -26,6 +26,12 @@ export class Controller {
|
|
|
26
26
|
initialize() {
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// @return {Application|Function} [app or function]
|
|
30
|
+
compose() {
|
|
31
|
+
// To be overridden in sub-classes, if the controller needs to install
|
|
32
|
+
// middleware. For normal routes, use `this.app.addRoute()` instead.
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
setup(isRoot = true, setupActionsObject = true) {
|
|
30
36
|
this._setupEmitter(this.hooks, {
|
|
31
37
|
// Support wildcard hooks only on controllers:
|
|
@@ -246,12 +252,6 @@ export class Controller {
|
|
|
246
252
|
])
|
|
247
253
|
}
|
|
248
254
|
|
|
249
|
-
// @return {Application|Function} [app or function]
|
|
250
|
-
compose() {
|
|
251
|
-
// To be overridden in sub-classes, if the controller needs to install
|
|
252
|
-
// middleware. For normal routes, use `this.app.addRoute()` instead.
|
|
253
|
-
}
|
|
254
|
-
|
|
255
255
|
getPath(type, path) {
|
|
256
256
|
// To be overridden by sub-classes.
|
|
257
257
|
return path
|
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/services/Service.js
CHANGED
package/src/storage/Storage.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
+
import { URL } from 'url'
|
|
2
3
|
import multer from '@koa/multer'
|
|
4
|
+
import picomatch from 'picomatch'
|
|
3
5
|
import imageSize from 'image-size'
|
|
4
6
|
import { PassThrough } from 'stream'
|
|
5
|
-
import { URL } from 'url'
|
|
6
7
|
import { hyphenate, toPromiseCallback } from '@ditojs/utils'
|
|
7
8
|
import { AssetFile } from './AssetFile.js'
|
|
8
9
|
|
|
@@ -57,6 +58,10 @@ export class Storage {
|
|
|
57
58
|
return AssetFile.getUniqueKey(name)
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
isImportSourceAllowed(url) {
|
|
62
|
+
return picomatch.isMatch(url, this.config.allowedImports || [])
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
convertAssetFile(file) {
|
|
61
66
|
return AssetFile.convert(file, this)
|
|
62
67
|
}
|