@ditojs/server 1.5.0 → 1.5.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 +5 -4
- package/src/app/Application.js +49 -38
- package/src/controllers/AdminController.js +28 -0
- package/src/controllers/Controller.js +6 -6
- package/src/middleware/ensureRunning.js +13 -0
- package/src/middleware/index.js +1 -0
- package/src/services/Service.js +2 -0
- package/src/storage/Storage.js +3 -7
- package/src/utils/glob.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.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",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
|
31
31
|
"ajv": "^8.11.0",
|
|
32
32
|
"ajv-formats": "^2.1.1",
|
|
33
|
-
"aws-sdk": "^2.
|
|
33
|
+
"aws-sdk": "^2.1108.0",
|
|
34
34
|
"axios": "^0.26.1",
|
|
35
35
|
"bcryptjs": "^2.4.3",
|
|
36
36
|
"bytes": "^3.1.2",
|
|
@@ -60,6 +60,7 @@
|
|
|
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",
|
|
63
64
|
"pino": "^7.9.2",
|
|
64
65
|
"pino-pretty": "^7.6.0",
|
|
65
66
|
"pluralize": "^8.0.0",
|
|
@@ -75,10 +76,10 @@
|
|
|
75
76
|
"objection": "^3.0.1"
|
|
76
77
|
},
|
|
77
78
|
"devDependencies": {
|
|
78
|
-
"knex": "^1.0.
|
|
79
|
+
"knex": "^1.0.5",
|
|
79
80
|
"objection": "^3.0.1",
|
|
80
81
|
"pg": "^8.7.3",
|
|
81
82
|
"sqlite3": "^5.0.2"
|
|
82
83
|
},
|
|
83
|
-
"gitHead": "
|
|
84
|
+
"gitHead": "ec436ef535d96b1653914df6194cc865dfe16ead"
|
|
84
85
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -22,8 +22,8 @@ import helmet from 'koa-helmet'
|
|
|
22
22
|
import responseTime from 'koa-response-time'
|
|
23
23
|
import Router from '@ditojs/router'
|
|
24
24
|
import {
|
|
25
|
-
isArray, isObject, isString, asArray, isPlainObject,
|
|
26
|
-
parseDataPath, normalizeDataPath,
|
|
25
|
+
isArray, isObject, isString, asArray, isPlainObject, isModule,
|
|
26
|
+
hyphenate, clone, merge, parseDataPath, normalizeDataPath, toPromiseCallback
|
|
27
27
|
} from '@ditojs/utils'
|
|
28
28
|
import SessionStore from './SessionStore.js'
|
|
29
29
|
import { Validator } from './Validator.js'
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
import {
|
|
43
43
|
attachLogger,
|
|
44
44
|
createTransaction,
|
|
45
|
+
ensureRunning,
|
|
45
46
|
findRoute,
|
|
46
47
|
handleError,
|
|
47
48
|
handleRoute,
|
|
@@ -88,6 +89,9 @@ export class Application extends Koa {
|
|
|
88
89
|
this.models = Object.create(null)
|
|
89
90
|
this.services = Object.create(null)
|
|
90
91
|
this.controllers = Object.create(null)
|
|
92
|
+
this.server = null
|
|
93
|
+
this.isRunning = false
|
|
94
|
+
|
|
91
95
|
this.setupLogger()
|
|
92
96
|
this.setupKnex()
|
|
93
97
|
this.setupMiddleware(middleware)
|
|
@@ -283,10 +287,6 @@ export class Application extends Koa {
|
|
|
283
287
|
return Object.values(this.services).find(callback)
|
|
284
288
|
}
|
|
285
289
|
|
|
286
|
-
forEachService(callback) {
|
|
287
|
-
return Promise.all(Object.values(this.services).map(callback))
|
|
288
|
-
}
|
|
289
|
-
|
|
290
290
|
addControllers(controllers, namespace) {
|
|
291
291
|
for (const [key, value] of Object.entries(controllers)) {
|
|
292
292
|
if (isModule(value) || isPlainObject(value)) {
|
|
@@ -508,6 +508,7 @@ export class Application extends Koa {
|
|
|
508
508
|
|
|
509
509
|
// Setup global middleware
|
|
510
510
|
|
|
511
|
+
this.use(ensureRunning(this))
|
|
511
512
|
this.use(attachLogger(this.logger))
|
|
512
513
|
if (app.responseTime !== false) {
|
|
513
514
|
this.use(responseTime(getOptions(app.responseTime)))
|
|
@@ -718,46 +719,56 @@ export class Application extends Koa {
|
|
|
718
719
|
this.on('error', this.logError)
|
|
719
720
|
}
|
|
720
721
|
await this.emit('before:start')
|
|
721
|
-
await
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
const server = this.listen(port, host, () => {
|
|
725
|
-
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()
|
|
726
725
|
console.info(
|
|
727
|
-
`Dito server started at http://${
|
|
726
|
+
`Dito.js server started at http://${address}:${port}`
|
|
728
727
|
)
|
|
729
728
|
resolve(server)
|
|
730
729
|
})
|
|
731
|
-
if (!server) {
|
|
732
|
-
reject(new Error(`Unable to start server at http://${host}:${port}`))
|
|
733
|
-
}
|
|
734
730
|
})
|
|
731
|
+
if (!this.server) {
|
|
732
|
+
throw new Error('Unable to start Dito.js server')
|
|
733
|
+
}
|
|
734
|
+
this.isRunning = true
|
|
735
735
|
await this.emit('after:start')
|
|
736
736
|
}
|
|
737
737
|
|
|
738
|
-
async stop() {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
738
|
+
async stop(timeout = 0) {
|
|
739
|
+
if (!this.server) {
|
|
740
|
+
throw new Error('Dito.js server is not running')
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const promise = (async () => {
|
|
744
|
+
await this.emit('before:stop')
|
|
745
|
+
this.isRunning = false
|
|
746
|
+
await new Promise((resolve, reject) => {
|
|
747
|
+
this.server.close(toPromiseCallback(resolve, reject))
|
|
748
|
+
})
|
|
749
|
+
// Hack to make sure that the server is closed, even if sockets are still
|
|
750
|
+
// open after `server.close()`, see: https://stackoverflow.com/a/36830072
|
|
751
|
+
this.server.emit('close')
|
|
752
|
+
this.server = null
|
|
753
|
+
await this.emit('after:stop')
|
|
754
|
+
})()
|
|
755
|
+
|
|
756
|
+
if (timeout > 0) {
|
|
757
|
+
await Promise.race([
|
|
758
|
+
promise,
|
|
759
|
+
new Promise((resolve, reject) =>
|
|
760
|
+
setTimeout(reject,
|
|
761
|
+
timeout,
|
|
762
|
+
new Error(
|
|
763
|
+
`Timeout reached while stopping Dito.js server (${timeout}ms)`
|
|
764
|
+
)
|
|
765
|
+
)
|
|
766
|
+
)
|
|
767
|
+
])
|
|
768
|
+
} else {
|
|
769
|
+
await promise
|
|
770
|
+
}
|
|
771
|
+
|
|
761
772
|
if (this.config.log.errors !== false) {
|
|
762
773
|
this.off('error', this.logError)
|
|
763
774
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
+
import { exit } from 'process'
|
|
2
3
|
import Koa from 'koa'
|
|
3
4
|
import serve from 'koa-static'
|
|
4
5
|
import { defineConfig, createServer } from 'vite'
|
|
@@ -90,6 +91,7 @@ export class AdminController extends Controller {
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
// @override
|
|
93
95
|
compose() {
|
|
94
96
|
this.koa = new Koa()
|
|
95
97
|
this.koa.use(this.middleware())
|
|
@@ -128,6 +130,32 @@ export class AdminController extends Controller {
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
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
|
+
if (!closed) {
|
|
154
|
+
closed = true
|
|
155
|
+
return server.close()
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
|
|
131
159
|
this.koa.use(handleConnectMiddleware(server.middlewares, {
|
|
132
160
|
expandMountPath: true
|
|
133
161
|
}))
|
|
@@ -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
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function ensureRunning(app) {
|
|
2
|
+
return async (ctx, next) => {
|
|
3
|
+
if (app.isRunning) {
|
|
4
|
+
await next()
|
|
5
|
+
} else {
|
|
6
|
+
// When the app isn't running, e.g. while stopping, we don't want to send
|
|
7
|
+
// content back anymore even if the controllers would still respond. This
|
|
8
|
+
// is to avoid strange behavior during shut-down of the vite dev server.
|
|
9
|
+
// For that scenario, sending back an empty 205 response seems to work.
|
|
10
|
+
ctx.status = 205 // HTTP_RESET_CONTENT
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/middleware/index.js
CHANGED
package/src/services/Service.js
CHANGED
package/src/storage/Storage.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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
|
-
import { matchGlobPattern } from '../utils/glob.js'
|
|
9
9
|
|
|
10
10
|
const storageClasses = {}
|
|
11
11
|
|
|
@@ -59,11 +59,7 @@ export class Storage {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
isImportSourceAllowed(url) {
|
|
62
|
-
|
|
63
|
-
for (const pattern of allowedImports) {
|
|
64
|
-
if (matchGlobPattern(url, pattern)) return true
|
|
65
|
-
}
|
|
66
|
-
return false
|
|
62
|
+
return picomatch.isMatch(url, this.config.allowedImports || [])
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
convertAssetFile(file) {
|
package/src/utils/glob.js
DELETED