@ditojs/server 1.25.0 → 1.25.1
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 +3 -3
- package/src/app/Application.js +45 -28
- package/src/utils/date.js +15 -0
- package/src/utils/index.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "1.25.
|
|
3
|
+
"version": "1.25.1",
|
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@ditojs/admin": "^1.25.0",
|
|
26
26
|
"@ditojs/build": "^1.25.0",
|
|
27
|
-
"@ditojs/router": "^1.25.
|
|
27
|
+
"@ditojs/router": "^1.25.1",
|
|
28
28
|
"@ditojs/utils": "^1.25.0",
|
|
29
29
|
"@koa/cors": "^4.0.0",
|
|
30
30
|
"@koa/multer": "^3.0.2",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"typescript": "^4.9.5"
|
|
91
91
|
},
|
|
92
92
|
"types": "types",
|
|
93
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "bebbf44ddf15a565e50ed8b2cbe0bf8a463c276d",
|
|
94
94
|
"scripts": {
|
|
95
95
|
"types": "tsc --noEmit ./src/index.d.ts"
|
|
96
96
|
},
|
package/src/app/Application.js
CHANGED
|
@@ -6,7 +6,6 @@ import Koa from 'koa'
|
|
|
6
6
|
import Knex from 'knex'
|
|
7
7
|
import pico from 'picocolors'
|
|
8
8
|
import pino from 'pino'
|
|
9
|
-
import parseDuration from 'parse-duration'
|
|
10
9
|
import bodyParser from 'koa-bodyparser'
|
|
11
10
|
import cors from '@koa/cors'
|
|
12
11
|
import compose from 'koa-compose'
|
|
@@ -21,9 +20,9 @@ import responseTime from 'koa-response-time'
|
|
|
21
20
|
import { Model, knexSnakeCaseMappers, ref } from 'objection'
|
|
22
21
|
import Router from '@ditojs/router'
|
|
23
22
|
import {
|
|
24
|
-
isArray, isObject,
|
|
25
|
-
hyphenate, clone, merge, parseDataPath, normalizeDataPath,
|
|
26
|
-
mapConcurrently
|
|
23
|
+
isArray, isObject, asArray, isPlainObject, isModule,
|
|
24
|
+
hyphenate, clone, merge, parseDataPath, normalizeDataPath,
|
|
25
|
+
toPromiseCallback, mapConcurrently
|
|
27
26
|
} from '@ditojs/utils'
|
|
28
27
|
import SessionStore from './SessionStore.js'
|
|
29
28
|
import { Validator } from './Validator.js'
|
|
@@ -32,7 +31,7 @@ import { Controller, AdminController } from '../controllers/index.js'
|
|
|
32
31
|
import { Service } from '../services/index.js'
|
|
33
32
|
import { Storage } from '../storage/index.js'
|
|
34
33
|
import { convertSchema } from '../schema/index.js'
|
|
35
|
-
import { deprecate } from '../utils/index.js'
|
|
34
|
+
import { getDuration, subtractDuration, deprecate } from '../utils/index.js'
|
|
36
35
|
import {
|
|
37
36
|
ResponseError,
|
|
38
37
|
ValidationError,
|
|
@@ -66,6 +65,7 @@ export class Application extends Koa {
|
|
|
66
65
|
// Pluck keys out of `config.app` to keep them secret
|
|
67
66
|
app: { keys, ...app } = {},
|
|
68
67
|
log,
|
|
68
|
+
assets,
|
|
69
69
|
logger,
|
|
70
70
|
...rest
|
|
71
71
|
} = config
|
|
@@ -74,6 +74,7 @@ export class Application extends Koa {
|
|
|
74
74
|
log: log === false || log?.silent || process.env.DITO_SILENT
|
|
75
75
|
? {}
|
|
76
76
|
: getOptions(log),
|
|
77
|
+
assets: merge(defaultAssetOptions, getOptions(assets)),
|
|
77
78
|
logger: merge(defaultLoggerOptions, getOptions(logger)),
|
|
78
79
|
...rest
|
|
79
80
|
}
|
|
@@ -805,16 +806,6 @@ export class Application extends Koa {
|
|
|
805
806
|
removedFiles,
|
|
806
807
|
trx = null
|
|
807
808
|
) {
|
|
808
|
-
const {
|
|
809
|
-
assets: {
|
|
810
|
-
cleanupTimeThreshold = 0
|
|
811
|
-
} = {}
|
|
812
|
-
} = this.config
|
|
813
|
-
// Only remove unused assets that haven't seen changes for given time frame.
|
|
814
|
-
const timeThreshold = isString(cleanupTimeThreshold)
|
|
815
|
-
? parseDuration(cleanupTimeThreshold)
|
|
816
|
-
: cleanupTimeThreshold
|
|
817
|
-
|
|
818
809
|
let importedFiles = []
|
|
819
810
|
const AssetModel = this.getModel('Asset')
|
|
820
811
|
if (AssetModel) {
|
|
@@ -837,18 +828,20 @@ export class Application extends Koa {
|
|
|
837
828
|
changeCount(addedFiles, 1),
|
|
838
829
|
changeCount(removedFiles, -1)
|
|
839
830
|
])
|
|
840
|
-
|
|
831
|
+
const cleanupTimeThreshold =
|
|
832
|
+
getDuration(this.config.assets.cleanupTimeThreshold)
|
|
833
|
+
if (cleanupTimeThreshold > 0) {
|
|
841
834
|
setTimeout(
|
|
842
835
|
// Don't pass `trx` here, as we want this delayed execution to
|
|
843
836
|
// create its own transaction.
|
|
844
|
-
() => this.releaseUnusedAssets(
|
|
845
|
-
|
|
837
|
+
() => this.releaseUnusedAssets(),
|
|
838
|
+
cleanupTimeThreshold
|
|
846
839
|
)
|
|
847
840
|
}
|
|
848
841
|
}
|
|
849
842
|
// Also execute releaseUnusedAssets() immediately in the same
|
|
850
843
|
// transaction, to potentially clean up other pending assets.
|
|
851
|
-
await this.releaseUnusedAssets(
|
|
844
|
+
await this.releaseUnusedAssets(null, trx)
|
|
852
845
|
return importedFiles
|
|
853
846
|
}
|
|
854
847
|
}
|
|
@@ -951,21 +944,34 @@ export class Application extends Koa {
|
|
|
951
944
|
return modifiedFiles
|
|
952
945
|
}
|
|
953
946
|
|
|
954
|
-
async releaseUnusedAssets(timeThreshold =
|
|
947
|
+
async releaseUnusedAssets(timeThreshold = null, trx = null) {
|
|
955
948
|
const AssetModel = this.getModel('Asset')
|
|
956
949
|
if (AssetModel) {
|
|
950
|
+
const { assets } = this.config
|
|
951
|
+
const cleanupTimeThreshold =
|
|
952
|
+
getDuration(timeThreshold ?? assets.cleanupTimeThreshold)
|
|
953
|
+
const danglingTimeThreshold =
|
|
954
|
+
getDuration(timeThreshold ?? assets.danglingTimeThreshold)
|
|
957
955
|
return AssetModel.transaction(trx, async trx => {
|
|
958
|
-
//
|
|
959
|
-
//
|
|
960
|
-
const
|
|
961
|
-
|
|
956
|
+
// Calculate the date math in JS instead of SQL, as there is no easy
|
|
957
|
+
// cross-SQL way to do `now() - interval X hours`:
|
|
958
|
+
const now = new Date()
|
|
959
|
+
const cleanupDate = subtractDuration(now, cleanupTimeThreshold)
|
|
960
|
+
const danglingDate = subtractDuration(now, danglingTimeThreshold)
|
|
962
961
|
const orphanedAssets = await AssetModel
|
|
963
962
|
.query(trx)
|
|
964
963
|
.where('count', 0)
|
|
965
|
-
.andWhere(
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
964
|
+
.andWhere(
|
|
965
|
+
query => query
|
|
966
|
+
.where('updatedAt', '<=', cleanupDate)
|
|
967
|
+
.orWhere(
|
|
968
|
+
// Protect freshly created assets from being deleted again right
|
|
969
|
+
// away, .e.g. when `config.assets.cleanupTimeThreshold = 0`
|
|
970
|
+
query => query
|
|
971
|
+
.where('updatedAt', '=', ref('createdAt'))
|
|
972
|
+
.andWhere('updatedAt', '<=', danglingDate)
|
|
973
|
+
)
|
|
974
|
+
)
|
|
969
975
|
if (orphanedAssets.length > 0) {
|
|
970
976
|
const orphanedKeys = await mapConcurrently(
|
|
971
977
|
orphanedAssets,
|
|
@@ -998,6 +1004,17 @@ function getOptions(options) {
|
|
|
998
1004
|
return isObject(options) ? options : {}
|
|
999
1005
|
}
|
|
1000
1006
|
|
|
1007
|
+
const defaultAssetOptions = {
|
|
1008
|
+
// Only remove unused or dangling assets that haven't seen changes for
|
|
1009
|
+
// these given time frames. Set to `0` to clean up instantly.
|
|
1010
|
+
cleanupTimeThreshold: '24h',
|
|
1011
|
+
// Dangling assets are those that got uploaded but never actually persisted in
|
|
1012
|
+
// the model. This can happen when the admin uploads a file but doesn't store
|
|
1013
|
+
// the associated form. This cannot be set to 0 or else the the file would be
|
|
1014
|
+
// deleted immediately after upload.
|
|
1015
|
+
danglingTimeThreshold: '24h'
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1001
1018
|
const { err, req, res } = pino.stdSerializers
|
|
1002
1019
|
const defaultLoggerOptions = {
|
|
1003
1020
|
level: 'info',
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isNumber } from '@ditojs/utils'
|
|
2
|
+
import parseDuration from 'parse-duration'
|
|
3
|
+
|
|
4
|
+
export function getDuration(duration) {
|
|
5
|
+
return isNumber(duration) ? duration : parseDuration(duration)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function addDuration(date, duration) {
|
|
9
|
+
date.setMilliseconds(date.getMilliseconds() + getDuration(duration))
|
|
10
|
+
return date
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function subtractDuration(date, duration) {
|
|
14
|
+
return addDuration(date, -getDuration(duration))
|
|
15
|
+
}
|
package/src/utils/index.js
CHANGED