velocious 1.0.26 → 1.0.28
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 +4 -2
- package/spec/database/record/create-spec.js +15 -0
- package/spec/database/record/query-spec.js +48 -0
- package/src/application.js +5 -5
- package/src/controller.js +5 -4
- package/src/database/drivers/base.js +30 -3
- package/src/database/drivers/mysql/index.js +4 -0
- package/src/database/query/index.js +31 -2
- package/src/database/record/index.js +52 -11
- package/src/database/record/instance-relationships/base.js +7 -0
- package/src/database/record/user-module.js +28 -0
- package/src/http-server/client/index.js +3 -2
- package/src/http-server/client/request-buffer/index.js +4 -5
- package/src/http-server/client/request-runner.js +5 -4
- package/src/http-server/index.js +6 -8
- package/src/http-server/server-client.js +5 -5
- package/src/http-server/worker-handler/index.js +6 -5
- package/src/http-server/worker-handler/worker-thread.js +6 -5
- package/src/logger.js +69 -31
- package/src/testing/test.js +64 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"velocious": "bin/velocious.js"
|
|
4
4
|
},
|
|
5
5
|
"name": "velocious",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.28",
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"test": "jasmine",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"sqlite3": "^5.1.7"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"bcryptjs": "^3.0.2",
|
|
33
34
|
"better-localstorage": "^1.0.7",
|
|
34
35
|
"debounce": "^2.2.0",
|
|
35
36
|
"diggerize": "^1.0.5",
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"inflection": "^3.0.0",
|
|
41
42
|
"sql-escape-string": "^1.1.0",
|
|
42
43
|
"sql.js": "^1.12.0",
|
|
43
|
-
"strftime": "^0.10.2"
|
|
44
|
+
"strftime": "^0.10.2",
|
|
45
|
+
"uuid": "^11.1.0"
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Dummy from "../../dummy/index.js"
|
|
2
|
+
import Project from "../../dummy/src/models/project.js"
|
|
2
3
|
import Task from "../../dummy/src/models/task.js"
|
|
3
4
|
|
|
4
5
|
describe("Record - create", () => {
|
|
@@ -20,4 +21,18 @@ describe("Record - create", () => {
|
|
|
20
21
|
expect(project.nameEn()).toEqual("Test project")
|
|
21
22
|
})
|
|
22
23
|
})
|
|
24
|
+
|
|
25
|
+
it("creates a new task with an existing project", async () => {
|
|
26
|
+
await Dummy.run(async () => {
|
|
27
|
+
const project = await Project.create({name: "Test project"})
|
|
28
|
+
const task = new Task({name: "Test task", project})
|
|
29
|
+
|
|
30
|
+
await task.save()
|
|
31
|
+
|
|
32
|
+
expect(task.id()).not.toBeUndefined()
|
|
33
|
+
expect(task.name()).toEqual("Test task")
|
|
34
|
+
expect(task.project().id()).toEqual(project.id())
|
|
35
|
+
expect(task.project()).toEqual(project)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
23
38
|
})
|
|
@@ -34,4 +34,52 @@ describe("Record - query", () => {
|
|
|
34
34
|
expect(newProject.nameEn()).toEqual("Test project")
|
|
35
35
|
})
|
|
36
36
|
})
|
|
37
|
+
|
|
38
|
+
it("finds the first record", async () => {
|
|
39
|
+
await Dummy.run(async () => {
|
|
40
|
+
const taskIDs = []
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < 5; i++) {
|
|
43
|
+
const task = await Task.create({name: `Task ${i}`})
|
|
44
|
+
|
|
45
|
+
taskIDs.push(task.id())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const lastTask = await Task.first()
|
|
49
|
+
|
|
50
|
+
expect(lastTask.id()).toEqual(taskIDs[0])
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("finds the last record", async () => {
|
|
55
|
+
await Dummy.run(async () => {
|
|
56
|
+
const taskIDs = []
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < 5; i++) {
|
|
59
|
+
const task = await Task.create({name: `Task ${i}`})
|
|
60
|
+
|
|
61
|
+
taskIDs.push(task.id())
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const lastTask = await Task.last()
|
|
65
|
+
|
|
66
|
+
expect(lastTask.id()).toEqual(taskIDs[4])
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it("counts the records", async () => {
|
|
71
|
+
await Dummy.run(async () => {
|
|
72
|
+
const taskIDs = []
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < 5; i++) {
|
|
75
|
+
const task = await Task.create({name: `Task ${i}`})
|
|
76
|
+
|
|
77
|
+
taskIDs.push(task.id())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const tasksCount = await Task.count()
|
|
81
|
+
|
|
82
|
+
expect(tasksCount).toEqual(5)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
37
85
|
})
|
package/src/application.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import AppRoutes from "../src/routes/app-routes.js"
|
|
2
2
|
import {digs} from "diggerize"
|
|
3
|
-
import
|
|
3
|
+
import {Logger} from "./logger.js"
|
|
4
4
|
import HttpServer from "./http-server/index.js"
|
|
5
5
|
|
|
6
6
|
export default class VelociousApplication {
|
|
7
7
|
constructor({configuration, httpServer}) {
|
|
8
8
|
this.configuration = configuration
|
|
9
9
|
this.httpServerConfiguration = httpServer ?? {}
|
|
10
|
+
this.logger = new Logger(this)
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
async initialize() {
|
|
@@ -40,7 +41,7 @@ export default class VelociousApplication {
|
|
|
40
41
|
|
|
41
42
|
const port = httpServerConfiguration.port || 3006
|
|
42
43
|
|
|
43
|
-
logger(
|
|
44
|
+
await this.logger.debug(`Starting server on port ${port}`)
|
|
44
45
|
|
|
45
46
|
this.httpServer = new HttpServer({configuration, port})
|
|
46
47
|
this.httpServer.events.on("close", this.onHttpServerClose)
|
|
@@ -49,13 +50,12 @@ export default class VelociousApplication {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
async stop() {
|
|
52
|
-
logger(
|
|
53
|
-
|
|
53
|
+
await this.logger.debug("Stopping server")
|
|
54
54
|
await this.httpServer.stop()
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
onHttpServerClose = () => {
|
|
58
|
-
logger(
|
|
58
|
+
this.logger.debug("HTTP server closed")
|
|
59
59
|
|
|
60
60
|
if (this.waitResolve) {
|
|
61
61
|
this.waitResolve()
|
package/src/controller.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {digs} from "diggerize"
|
|
2
2
|
import ejs from "ejs"
|
|
3
3
|
import * as inflection from "inflection"
|
|
4
|
-
import
|
|
4
|
+
import {Logger} from "./logger.js"
|
|
5
5
|
import restArgsError from "./utils/rest-args-error.js"
|
|
6
6
|
|
|
7
7
|
export default class VelociousController {
|
|
@@ -23,6 +23,7 @@ export default class VelociousController {
|
|
|
23
23
|
this._action = action
|
|
24
24
|
this._controller = controller
|
|
25
25
|
this._configuration = configuration
|
|
26
|
+
this.logger = new Logger(this)
|
|
26
27
|
this._params = params
|
|
27
28
|
this._request = request
|
|
28
29
|
this._response = response
|
|
@@ -31,12 +32,12 @@ export default class VelociousController {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
async _runBeforeCallbacks() {
|
|
34
|
-
await logger(
|
|
35
|
+
await this.logger.debug("_runBeforeCallbacks")
|
|
35
36
|
|
|
36
37
|
let currentControllerClass = this.constructor
|
|
37
38
|
|
|
38
39
|
while (currentControllerClass) {
|
|
39
|
-
await logger(
|
|
40
|
+
await this.logger.debug(`Running callbacks for ${currentControllerClass.name}`)
|
|
40
41
|
|
|
41
42
|
const beforeActions = currentControllerClass._beforeActions
|
|
42
43
|
|
|
@@ -57,7 +58,7 @@ export default class VelociousController {
|
|
|
57
58
|
if (!currentControllerClass?.name?.endsWith("Controller")) break
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
await logger(
|
|
61
|
+
await this.logger.debug("After runBeforeCallbacks")
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
params = () => this._params
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import Query from "../query/index.js"
|
|
2
2
|
import Handler from "../handler.js"
|
|
3
|
+
import {v4 as uuidv4} from "uuid"
|
|
3
4
|
|
|
4
5
|
export default class VelociousDatabaseDriversBase {
|
|
5
6
|
constructor(config, configuration) {
|
|
6
7
|
this._args = config
|
|
7
8
|
this.configuration = configuration
|
|
9
|
+
this._transactionsCount = 0
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
async createTable(...args) {
|
|
@@ -81,16 +83,41 @@ export default class VelociousDatabaseDriversBase {
|
|
|
81
83
|
return false
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
async startTransaction() {
|
|
87
|
+
return await this.query("BEGIN TRANSACTION")
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
async transaction(callback) {
|
|
85
|
-
|
|
91
|
+
const savePointName = `sp${uuidv4().replaceAll("-", "")}`
|
|
92
|
+
let transactionStarted = false
|
|
93
|
+
|
|
94
|
+
if (this._transactionsCount == 0) {
|
|
95
|
+
await this.startTransaction()
|
|
96
|
+
transactionStarted = true
|
|
97
|
+
this._transactionsCount++
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await this.query(`SAVEPOINT ${savePointName}`)
|
|
86
101
|
|
|
87
102
|
let result
|
|
88
103
|
|
|
89
104
|
try {
|
|
90
105
|
result = await callback()
|
|
91
|
-
|
|
106
|
+
|
|
107
|
+
await this.query(`RELEASE SAVEPOINT ${savePointName}`)
|
|
108
|
+
|
|
109
|
+
if (transactionStarted) {
|
|
110
|
+
await this.query("COMMIT")
|
|
111
|
+
this._transactionsCount--
|
|
112
|
+
}
|
|
92
113
|
} catch (error) {
|
|
93
|
-
this.query(
|
|
114
|
+
await this.query(`ROLLBACK TO SAVEPOINT ${savePointName}`)
|
|
115
|
+
|
|
116
|
+
if (transactionStarted) {
|
|
117
|
+
await this.query("ROLLBACK")
|
|
118
|
+
this._transactionsCount--
|
|
119
|
+
}
|
|
120
|
+
|
|
94
121
|
throw error
|
|
95
122
|
}
|
|
96
123
|
|
|
@@ -140,6 +140,10 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
140
140
|
return this._options
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
async startTransaction() {
|
|
144
|
+
return await this.query("START TRANSACTION")
|
|
145
|
+
}
|
|
146
|
+
|
|
143
147
|
updateSql({conditions, data, tableName}) {
|
|
144
148
|
const update = new Update({conditions, data, driver: this, tableName})
|
|
145
149
|
|
|
@@ -2,6 +2,7 @@ import FromPlain from "./from-plain.js"
|
|
|
2
2
|
import {incorporate} from "incorporator"
|
|
3
3
|
import * as inflection from "inflection"
|
|
4
4
|
import JoinPlain from "./join-plain.js"
|
|
5
|
+
import {Logger} from "../../logger.js"
|
|
5
6
|
import OrderPlain from "./order-plain.js"
|
|
6
7
|
import Preloader from "./preloader.js"
|
|
7
8
|
import RecordNotFoundError from "../record/record-not-found-error.js"
|
|
@@ -16,6 +17,7 @@ export default class VelociousDatabaseQuery {
|
|
|
16
17
|
|
|
17
18
|
this.driver = driver
|
|
18
19
|
this.handler = handler
|
|
20
|
+
this.logger = new Logger(this)
|
|
19
21
|
this.modelClass = modelClass
|
|
20
22
|
this._froms = froms
|
|
21
23
|
this._groups = groups
|
|
@@ -45,6 +47,21 @@ export default class VelociousDatabaseQuery {
|
|
|
45
47
|
return newQuery
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
async count() {
|
|
51
|
+
const countQuery = this.clone()
|
|
52
|
+
|
|
53
|
+
countQuery._selects = []
|
|
54
|
+
countQuery.select("COUNT(id) AS count")
|
|
55
|
+
|
|
56
|
+
const results = await countQuery._executeQuery()
|
|
57
|
+
|
|
58
|
+
if (results.length == 1) {
|
|
59
|
+
return results[0].count
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new Error("count multiple stub")
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
getOptions = () => this.driver.options()
|
|
49
66
|
|
|
50
67
|
async destroyAll() {
|
|
@@ -136,7 +153,11 @@ export default class VelociousDatabaseQuery {
|
|
|
136
153
|
return this
|
|
137
154
|
}
|
|
138
155
|
|
|
139
|
-
last = async () =>
|
|
156
|
+
last = async () => {
|
|
157
|
+
const results = await this.clone().reorder("id DESC").limit(1).toArray()
|
|
158
|
+
|
|
159
|
+
return results[0]
|
|
160
|
+
}
|
|
140
161
|
|
|
141
162
|
limit(value) {
|
|
142
163
|
this._limits.push(value)
|
|
@@ -188,10 +209,18 @@ export default class VelociousDatabaseQuery {
|
|
|
188
209
|
return this
|
|
189
210
|
}
|
|
190
211
|
|
|
191
|
-
async
|
|
212
|
+
async _executeQuery() {
|
|
192
213
|
const sql = this.toSql()
|
|
193
214
|
const results = await this.driver.query(sql)
|
|
215
|
+
|
|
216
|
+
this.logger.debug("SQL: ", sql)
|
|
217
|
+
|
|
218
|
+
return results
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async toArray() {
|
|
194
222
|
const models = []
|
|
223
|
+
const results = await this._executeQuery()
|
|
195
224
|
|
|
196
225
|
for (const result of results) {
|
|
197
226
|
const model = new this.modelClass()
|
|
@@ -41,6 +41,7 @@ export default class VelociousDatabaseRecord {
|
|
|
41
41
|
relationship = new BelongsToRelationship(actualData)
|
|
42
42
|
|
|
43
43
|
const buildMethodName = `build${inflection.camelize(relationshipName)}`
|
|
44
|
+
const setMethodName = `set${inflection.camelize(relationshipName)}`
|
|
44
45
|
|
|
45
46
|
this.prototype[relationshipName] = function () {
|
|
46
47
|
const relationship = this.getRelationshipByName(relationshipName)
|
|
@@ -54,6 +55,13 @@ export default class VelociousDatabaseRecord {
|
|
|
54
55
|
|
|
55
56
|
return record
|
|
56
57
|
}
|
|
58
|
+
|
|
59
|
+
this.prototype[setMethodName] = function (model) {
|
|
60
|
+
const relationship = this.getRelationshipByName(relationshipName)
|
|
61
|
+
|
|
62
|
+
relationship.setLoaded(model)
|
|
63
|
+
relationship.setDirty(true)
|
|
64
|
+
}
|
|
57
65
|
} else if (actualData.type == "hasMany") {
|
|
58
66
|
relationship = new HasManyRelationship(actualData)
|
|
59
67
|
|
|
@@ -276,19 +284,20 @@ export default class VelociousDatabaseRecord {
|
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
async save() {
|
|
279
|
-
let result
|
|
280
|
-
|
|
281
287
|
const isNewRecord = this.isNewRecord()
|
|
288
|
+
let result
|
|
282
289
|
|
|
283
|
-
await this.
|
|
290
|
+
await this.constructor.transaction(async () => {
|
|
291
|
+
await this._autoSaveBelongsToRelationships()
|
|
284
292
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
293
|
+
if (this.isPersisted()) {
|
|
294
|
+
result = await this._updateRecordWithChanges()
|
|
295
|
+
} else {
|
|
296
|
+
result = await this._createNewRecord()
|
|
297
|
+
}
|
|
290
298
|
|
|
291
|
-
|
|
299
|
+
await this._autoSaveHasManyRelationships({isNewRecord})
|
|
300
|
+
})
|
|
292
301
|
|
|
293
302
|
return result
|
|
294
303
|
}
|
|
@@ -310,6 +319,7 @@ export default class VelociousDatabaseRecord {
|
|
|
310
319
|
|
|
311
320
|
this.setAttribute(foreignKey, model.id())
|
|
312
321
|
instanceRelationship.setPreloaded(true)
|
|
322
|
+
instanceRelationship.setDirty(false)
|
|
313
323
|
}
|
|
314
324
|
}
|
|
315
325
|
}
|
|
@@ -350,6 +360,10 @@ export default class VelociousDatabaseRecord {
|
|
|
350
360
|
this._tableName = tableName
|
|
351
361
|
}
|
|
352
362
|
|
|
363
|
+
static async transaction(callback) {
|
|
364
|
+
return await this.connection().transaction(callback)
|
|
365
|
+
}
|
|
366
|
+
|
|
353
367
|
static translates(...names) {
|
|
354
368
|
for (const name of names) {
|
|
355
369
|
if (!this._translations) this._translations = {}
|
|
@@ -457,6 +471,10 @@ export default class VelociousDatabaseRecord {
|
|
|
457
471
|
return this._newQuery()
|
|
458
472
|
}
|
|
459
473
|
|
|
474
|
+
static async count() {
|
|
475
|
+
return this._newQuery().count()
|
|
476
|
+
}
|
|
477
|
+
|
|
460
478
|
static async destroyAll(...args) {
|
|
461
479
|
return this._newQuery().destroyAll(...args)
|
|
462
480
|
}
|
|
@@ -477,6 +495,10 @@ export default class VelociousDatabaseRecord {
|
|
|
477
495
|
return this._newQuery().findOrInitializeBy(...args)
|
|
478
496
|
}
|
|
479
497
|
|
|
498
|
+
static async first() {
|
|
499
|
+
return this._newQuery().first()
|
|
500
|
+
}
|
|
501
|
+
|
|
480
502
|
static joins(...args) {
|
|
481
503
|
return this._newQuery().joins(...args)
|
|
482
504
|
}
|
|
@@ -636,14 +658,31 @@ export default class VelociousDatabaseRecord {
|
|
|
636
658
|
return this._attributes[attributeNameUnderscore]
|
|
637
659
|
}
|
|
638
660
|
|
|
661
|
+
_belongsToChanges() {
|
|
662
|
+
const belongsToChanges = {}
|
|
663
|
+
|
|
664
|
+
if (this._instanceRelationships) {
|
|
665
|
+
for (const relationshipName in this._instanceRelationships) {
|
|
666
|
+
const relationship = this._instanceRelationships[relationshipName]
|
|
667
|
+
|
|
668
|
+
if (relationship.getType() == "belongsTo" && relationship.getDirty()) {
|
|
669
|
+
belongsToChanges[relationship.getForeignKey()] = relationship.loaded()?.id()
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
return belongsToChanges
|
|
675
|
+
}
|
|
676
|
+
|
|
639
677
|
async _createNewRecord() {
|
|
640
678
|
if (!this.constructor.connection()["insertSql"]) {
|
|
641
679
|
throw new Error(`No insertSql on ${this.constructor.connection().constructor.name}`)
|
|
642
680
|
}
|
|
643
681
|
|
|
682
|
+
const data = Object.assign({}, this._belongsToChanges(), this.attributes())
|
|
644
683
|
const sql = this._connection().insertSql({
|
|
645
684
|
tableName: this._tableName(),
|
|
646
|
-
data
|
|
685
|
+
data
|
|
647
686
|
})
|
|
648
687
|
await this._connection().query(sql)
|
|
649
688
|
const id = await this._connection().lastInsertID()
|
|
@@ -666,9 +705,11 @@ export default class VelociousDatabaseRecord {
|
|
|
666
705
|
|
|
667
706
|
conditions[this.constructor.primaryKey()] = this.id()
|
|
668
707
|
|
|
708
|
+
const changes = Object.assign({}, this._belongsToChanges(), this._changes)
|
|
709
|
+
|
|
669
710
|
const sql = this._connection().updateSql({
|
|
670
711
|
tableName: this._tableName(),
|
|
671
|
-
data:
|
|
712
|
+
data: changes,
|
|
672
713
|
conditions
|
|
673
714
|
})
|
|
674
715
|
await this._connection().query(sql)
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
export default class VelociousDatabaseRecordBaseInstanceRelationship {
|
|
2
2
|
constructor({model, relationship}) {
|
|
3
|
+
this._dirty = false
|
|
3
4
|
this.model = model
|
|
4
5
|
this.relationship = relationship
|
|
5
6
|
}
|
|
6
7
|
|
|
8
|
+
setDirty(newValue) {
|
|
9
|
+
this._dirty = newValue
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getDirty = () => this._dirty
|
|
13
|
+
|
|
7
14
|
loaded() {
|
|
8
15
|
if (!this._preloaded && this.model.isPersisted()) {
|
|
9
16
|
throw new Error(`${this.model.constructor.name}#${this.relationship.getRelationshipName()} hasn't been preloaded`)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import bcryptjs from "bcryptjs"
|
|
2
|
+
import restArgsError from "../../utils/rest-args-error.js"
|
|
3
|
+
|
|
4
|
+
export default class UserModule {
|
|
5
|
+
constructor({secretKey, ...restArgs}) {
|
|
6
|
+
restArgsError(restArgs)
|
|
7
|
+
|
|
8
|
+
if (!secretKey) throw new Error(`Invalid secret key given: ${secretKey}`)
|
|
9
|
+
|
|
10
|
+
this.secretKey = secretKey
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
attachTo(UserClass) {
|
|
14
|
+
UserClass.prototype.setPassword = function(newPassword) {
|
|
15
|
+
const salt = bcryptjs.genSaltSync(10)
|
|
16
|
+
const encryptedPassword = bcryptjs.hashSync(newPassword, salt)
|
|
17
|
+
|
|
18
|
+
this.setEncryptedPassword(encryptedPassword)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
UserClass.prototype.setPasswordConfirmation = function(newPasswordConfirmation) {
|
|
22
|
+
const salt = bcryptjs.genSaltSync(10)
|
|
23
|
+
const encryptedPassword = bcryptjs.hashSync(newPasswordConfirmation, salt)
|
|
24
|
+
|
|
25
|
+
this._encryptedPasswordConfirmation = encryptedPassword
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {digg} from "diggerize"
|
|
2
2
|
import {EventEmitter} from "events"
|
|
3
|
-
import
|
|
3
|
+
import {Logger} from "../../logger.js"
|
|
4
4
|
import Request from "./request.js"
|
|
5
5
|
import RequestRunner from "./request-runner.js"
|
|
6
6
|
|
|
@@ -11,6 +11,7 @@ export default class VeoliciousHttpServerClient {
|
|
|
11
11
|
constructor({clientCount, configuration, onExecuteRequest}) {
|
|
12
12
|
if (!configuration) throw new Error("No configuration given")
|
|
13
13
|
|
|
14
|
+
this.logger = new Logger(this)
|
|
14
15
|
this.clientCount = clientCount
|
|
15
16
|
this.configuration = configuration
|
|
16
17
|
this.onExecuteRequest = onExecuteRequest
|
|
@@ -18,7 +19,7 @@ export default class VeoliciousHttpServerClient {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
executeCurrentRequest = () => {
|
|
21
|
-
logger(
|
|
22
|
+
this.logger.debug("executeCurrentRequest")
|
|
22
23
|
|
|
23
24
|
// We are done parsing the given request and can theoretically start parsing a new one, before the current request is done - so reset the state.
|
|
24
25
|
this.state = "initial"
|
|
@@ -2,7 +2,7 @@ import {EventEmitter} from "events"
|
|
|
2
2
|
import FormDataPart from "./form-data-part.js"
|
|
3
3
|
import Header from "./header.js"
|
|
4
4
|
import Incorporator from "incorporator"
|
|
5
|
-
import
|
|
5
|
+
import {Logger} from "../../../logger.js"
|
|
6
6
|
import ParamsToObject from "../params-to-object.js"
|
|
7
7
|
import querystring from "querystring"
|
|
8
8
|
|
|
@@ -20,6 +20,7 @@ export default class RequestBuffer {
|
|
|
20
20
|
|
|
21
21
|
constructor({configuration}) {
|
|
22
22
|
this.configuration = configuration
|
|
23
|
+
this.logger = new Logger(this)
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
feed(data) {
|
|
@@ -185,8 +186,7 @@ export default class RequestBuffer {
|
|
|
185
186
|
this.httpMethod = match[1]
|
|
186
187
|
this.path = match[2]
|
|
187
188
|
this.setState("headers")
|
|
188
|
-
|
|
189
|
-
logger(this, () => ["Parsed status line", {httpMethod: this.httpMethod, path: this.path}])
|
|
189
|
+
this.logger.debug(() => ["Parsed status line", {httpMethod: this.httpMethod, path: this.path}])
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
postRequestDone() {
|
|
@@ -196,8 +196,7 @@ export default class RequestBuffer {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
setState(newState) {
|
|
199
|
-
logger(
|
|
200
|
-
|
|
199
|
+
this.logger.debug(() => [`Changing state from ${this.state} to ${newState}`])
|
|
201
200
|
this.state = newState
|
|
202
201
|
}
|
|
203
202
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import EventEmitter from "events"
|
|
2
|
-
import
|
|
2
|
+
import {Logger} from "../../logger.js"
|
|
3
3
|
import Response from "./response.js"
|
|
4
4
|
import RoutesResolver from "../../routes/resolver.js"
|
|
5
5
|
|
|
@@ -10,6 +10,7 @@ export default class VelociousHttpServerClientRequestRunner {
|
|
|
10
10
|
if (!configuration) throw new Error("No configuration given")
|
|
11
11
|
if (!request) throw new Error("No request given")
|
|
12
12
|
|
|
13
|
+
this.logger = new Logger(this)
|
|
13
14
|
this.configuration = configuration
|
|
14
15
|
this.request = request
|
|
15
16
|
this.response = new Response({configuration})
|
|
@@ -25,7 +26,7 @@ export default class VelociousHttpServerClientRequestRunner {
|
|
|
25
26
|
|
|
26
27
|
try {
|
|
27
28
|
if (request.header("sec-fetch-mode") == "cors") {
|
|
28
|
-
await logger(
|
|
29
|
+
await this.logger.debug(() => ["Run CORS", {httpMethod: request.httpMethod(), secFetchMode: request.header("sec-fetch-mode")}])
|
|
29
30
|
await configuration.cors({request, response})
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -33,13 +34,13 @@ export default class VelociousHttpServerClientRequestRunner {
|
|
|
33
34
|
response.setStatus(200)
|
|
34
35
|
response.setBody("")
|
|
35
36
|
} else {
|
|
36
|
-
await logger(
|
|
37
|
+
await this.logger.debug("Run request")
|
|
37
38
|
const routesResolver = new RoutesResolver({configuration, request, response})
|
|
38
39
|
|
|
39
40
|
await routesResolver.resolve()
|
|
40
41
|
}
|
|
41
42
|
} catch (error) {
|
|
42
|
-
await logger(
|
|
43
|
+
await this.logger.error(() => [`Error while running request: ${error.message}\n\n${error.stack}`])
|
|
43
44
|
|
|
44
45
|
response.setStatus(500)
|
|
45
46
|
response.setErrorBody(error)
|
package/src/http-server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {digg} from "diggerize"
|
|
2
2
|
import EventEmitter from "events"
|
|
3
|
-
import
|
|
3
|
+
import {Logger} from "../logger.js"
|
|
4
4
|
import Net from "net"
|
|
5
5
|
import ServerClient from "./server-client.js"
|
|
6
6
|
import WorkerHandler from "./worker-handler/index.js"
|
|
@@ -14,6 +14,7 @@ export default class VelociousHttpServer {
|
|
|
14
14
|
|
|
15
15
|
constructor({configuration, host, maxWorkers, port}) {
|
|
16
16
|
this.configuration = configuration
|
|
17
|
+
this.logger = new Logger(this)
|
|
17
18
|
this.host = host || "0.0.0.0"
|
|
18
19
|
this.port = port || 3006
|
|
19
20
|
this.maxWorkers = maxWorkers || 16
|
|
@@ -31,7 +32,7 @@ export default class VelociousHttpServer {
|
|
|
31
32
|
return new Promise((resolve, reject) => {
|
|
32
33
|
try {
|
|
33
34
|
this.netServer.listen(this.port, this.host, () => {
|
|
34
|
-
logger(
|
|
35
|
+
this.logger.debug(`Velocious listening on ${this.host}:${this.port}`)
|
|
35
36
|
resolve()
|
|
36
37
|
})
|
|
37
38
|
} catch (error) {
|
|
@@ -82,8 +83,7 @@ export default class VelociousHttpServer {
|
|
|
82
83
|
onConnection = (socket) => {
|
|
83
84
|
const clientCount = this.clientCount
|
|
84
85
|
|
|
85
|
-
logger(
|
|
86
|
-
|
|
86
|
+
this.logger.debug(`New client ${clientCount}`)
|
|
87
87
|
this.clientCount++
|
|
88
88
|
|
|
89
89
|
const workerHandler = this.workerHandlerToUse()
|
|
@@ -95,10 +95,8 @@ export default class VelociousHttpServer {
|
|
|
95
95
|
|
|
96
96
|
client.events.on("close", this.onClientClose)
|
|
97
97
|
|
|
98
|
-
logger(
|
|
99
|
-
|
|
98
|
+
this.logger.debug(`Gave client ${clientCount} to worker ${workerHandler.workerCount}`)
|
|
100
99
|
workerHandler.addSocketConnection(client)
|
|
101
|
-
|
|
102
100
|
this.clients[clientCount] = client
|
|
103
101
|
}
|
|
104
102
|
|
|
@@ -130,7 +128,7 @@ export default class VelociousHttpServer {
|
|
|
130
128
|
}
|
|
131
129
|
|
|
132
130
|
workerHandlerToUse() {
|
|
133
|
-
logger(
|
|
131
|
+
this.logger.debug(`Worker handlers length: ${this.workerHandlers.length}`)
|
|
134
132
|
|
|
135
133
|
const randomWorkerNumber = parseInt(Math.random() * this.workerHandlers.length)
|
|
136
134
|
const workerHandler = this.workerHandlers[randomWorkerNumber]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import EventEmitter from "events"
|
|
2
|
-
import
|
|
2
|
+
import {Logger} from "../logger.js"
|
|
3
3
|
|
|
4
4
|
export default class ServerClient {
|
|
5
5
|
events = new EventEmitter()
|
|
@@ -8,6 +8,7 @@ export default class ServerClient {
|
|
|
8
8
|
if (!configuration) throw new Error("No configuration given")
|
|
9
9
|
|
|
10
10
|
this.configuration = configuration
|
|
11
|
+
this.logger = new Logger(this)
|
|
11
12
|
this.socket = socket
|
|
12
13
|
this.clientCount = clientCount
|
|
13
14
|
|
|
@@ -25,7 +26,7 @@ export default class ServerClient {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
onSocketData = (chunk) => {
|
|
28
|
-
logger(
|
|
29
|
+
this.logger.debug(() => [`Socket ${this.clientCount}: ${chunk}`])
|
|
29
30
|
|
|
30
31
|
this.worker.postMessage({
|
|
31
32
|
command: "clientWrite",
|
|
@@ -35,13 +36,12 @@ export default class ServerClient {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
onSocketEnd = () => {
|
|
38
|
-
logger(
|
|
39
|
+
this.logger.debug(`Socket ${this.clientCount} end`)
|
|
39
40
|
this.events.emit("close", this)
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
send(data) {
|
|
43
|
-
logger(
|
|
44
|
-
|
|
44
|
+
this.logger.debug("Send", data)
|
|
45
45
|
this.socket.write(data)
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {digg, digs} from "diggerize"
|
|
2
2
|
import {dirname} from "path"
|
|
3
3
|
import {fileURLToPath} from "url"
|
|
4
|
-
import
|
|
4
|
+
import {Logger} from "../../logger.js"
|
|
5
5
|
import {Worker} from "worker_threads"
|
|
6
6
|
|
|
7
7
|
export default class VelociousHttpServerWorker {
|
|
8
8
|
constructor({configuration, workerCount}) {
|
|
9
9
|
this.configuration = configuration
|
|
10
10
|
this.clients = {}
|
|
11
|
+
this.logger = new Logger(this)
|
|
11
12
|
this.workerCount = workerCount
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -36,7 +37,7 @@ export default class VelociousHttpServerWorker {
|
|
|
36
37
|
const clientCount = digg(client, "clientCount")
|
|
37
38
|
|
|
38
39
|
client.socket.on("end", () => {
|
|
39
|
-
logger(
|
|
40
|
+
this.logger.debug(`Removing ${clientCount} from clients`)
|
|
40
41
|
delete this.clients[clientCount]
|
|
41
42
|
})
|
|
42
43
|
|
|
@@ -55,12 +56,12 @@ export default class VelociousHttpServerWorker {
|
|
|
55
56
|
if (code !== 0) {
|
|
56
57
|
throw new Error(`Client worker stopped with exit code ${code}`)
|
|
57
58
|
} else {
|
|
58
|
-
logger(
|
|
59
|
+
this.logger.debug(() => [`Client worker stopped with exit code ${code}`])
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
onWorkerMessage = (data) => {
|
|
63
|
-
logger(
|
|
64
|
+
this.logger.debug(`Worker message`, data)
|
|
64
65
|
|
|
65
66
|
const {command} = digs(data, "command")
|
|
66
67
|
|
|
@@ -68,7 +69,7 @@ export default class VelociousHttpServerWorker {
|
|
|
68
69
|
this.onStartCallback()
|
|
69
70
|
this.onStartCallback = null
|
|
70
71
|
} else if (command == "clientOutput") {
|
|
71
|
-
logger(
|
|
72
|
+
this.logger.debug("CLIENT OUTPUT", data)
|
|
72
73
|
|
|
73
74
|
const {clientCount, output} = digs(data, "clientCount", "output")
|
|
74
75
|
|
|
@@ -2,13 +2,14 @@ import Application from "../../application.js"
|
|
|
2
2
|
import Client from "../client/index.js"
|
|
3
3
|
import {digg, digs} from "diggerize"
|
|
4
4
|
import errorLogger from "../../error-logger.js"
|
|
5
|
-
import
|
|
5
|
+
import {Logger} from "../../logger.js"
|
|
6
6
|
|
|
7
7
|
export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
8
8
|
constructor({parentPort, workerData}) {
|
|
9
9
|
const {workerCount} = digs(workerData, "workerCount")
|
|
10
10
|
|
|
11
11
|
this.clients = {}
|
|
12
|
+
this.logger = new Logger(this)
|
|
12
13
|
this.parentPort = parentPort
|
|
13
14
|
this.workerData = workerData
|
|
14
15
|
this.workerCount = workerCount
|
|
@@ -17,7 +18,7 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
17
18
|
|
|
18
19
|
this.initialize().then(() => {
|
|
19
20
|
this.application.initialize().then(() => {
|
|
20
|
-
logger(
|
|
21
|
+
this.logger.debug(`Worker ${workerCount} started`)
|
|
21
22
|
parentPort.postMessage({command: "started"})
|
|
22
23
|
})
|
|
23
24
|
})
|
|
@@ -39,7 +40,7 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
onCommand = async (data) => {
|
|
42
|
-
await logger(
|
|
43
|
+
await this.logger.debug(() => [`Worker ${this.workerCount} received command`, data])
|
|
43
44
|
|
|
44
45
|
const command = data.command
|
|
45
46
|
|
|
@@ -56,12 +57,12 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
56
57
|
|
|
57
58
|
this.clients[clientCount] = client
|
|
58
59
|
} else if (command == "clientWrite") {
|
|
59
|
-
await logger(
|
|
60
|
+
await this.logger.debug("Looking up client")
|
|
60
61
|
|
|
61
62
|
const {chunk, clientCount} = digs(data, "chunk", "clientCount")
|
|
62
63
|
const client = digg(this.clients, clientCount)
|
|
63
64
|
|
|
64
|
-
await logger(
|
|
65
|
+
await this.logger.debug(`Sending to client ${clientCount}`)
|
|
65
66
|
|
|
66
67
|
client.onWrite(chunk)
|
|
67
68
|
} else {
|
package/src/logger.js
CHANGED
|
@@ -2,50 +2,88 @@ import Configuration from "./configuration.js"
|
|
|
2
2
|
|
|
3
3
|
function consoleLog(message) {
|
|
4
4
|
return new Promise((resolve) => {
|
|
5
|
-
process.stdout.write(message
|
|
5
|
+
process.stdout.write(`${message}\n`, "utf8", resolve)
|
|
6
6
|
})
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
function consoleError(message) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
process.stderr.write(`${message}\n`, "utf8", resolve)
|
|
12
|
+
})
|
|
13
|
+
}
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
configuration = Configuration.current()
|
|
15
|
+
function functionOrMessages(messages) {
|
|
16
|
+
if (messages.length === 1 && typeof messages[0] == "function") {
|
|
17
|
+
messages = messages[0]()
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (!object.constructor.name) {
|
|
21
|
-
throw new Error(`No constructor name for object`)
|
|
22
|
-
}
|
|
20
|
+
return messages
|
|
21
|
+
}
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
function messagesToMessage(...messages) {
|
|
24
|
+
let message = ""
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
26
|
+
for (const messagePartIndex in messages) {
|
|
27
|
+
const messagePart = messages[messagePartIndex]
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
if (messagePartIndex > 0) {
|
|
30
|
+
message += " "
|
|
31
|
+
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
if (typeof messagePart == "object") {
|
|
34
|
+
message += JSON.stringify(messagePart)
|
|
35
|
+
} else {
|
|
36
|
+
message += messagePart
|
|
37
|
+
}
|
|
38
|
+
}
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
return message
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class Logger {
|
|
44
|
+
constructor(object) {
|
|
45
|
+
if (typeof object == "string") {
|
|
46
|
+
this._subject = object
|
|
47
|
+
} else {
|
|
48
|
+
this._object = object
|
|
49
|
+
this._subject = object.constructor.name
|
|
50
|
+
}
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
}
|
|
52
|
+
if (!this._subject) {
|
|
53
|
+
throw new Error(`No subject given`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
getConfiguration() {
|
|
58
|
+
if (!this._configuration) {
|
|
59
|
+
this._configuration = this._object?.configuration || Configuration.current()
|
|
49
60
|
}
|
|
61
|
+
|
|
62
|
+
return this._configuration
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async debug(...messages) {
|
|
66
|
+
if (this.getConfiguration()?.debug) {
|
|
67
|
+
await this.log(...messages)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async log(...messages) {
|
|
72
|
+
await consoleLog(messagesToMessage(this._subject, ...functionOrMessages(messages)))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async error(...messages) {
|
|
76
|
+
await consoleError(messagesToMessage(this._subject, ...functionOrMessages(messages)))
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export {Logger}
|
|
81
|
+
|
|
82
|
+
export default async function logger(object, ...messages) {
|
|
83
|
+
const className = object.constructor.name
|
|
84
|
+
const configuration = object.configuration || Configuration.current()
|
|
85
|
+
|
|
86
|
+
if (configuration.debug) {
|
|
87
|
+
await consoleLog(messagesToMessage(className, ...functionOrMessages(messages)))
|
|
50
88
|
}
|
|
51
89
|
}
|
package/src/testing/test.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import restArgsError from "../utils/rest-args-error.js"
|
|
2
|
+
|
|
1
3
|
const tests = {
|
|
2
4
|
args: {},
|
|
3
5
|
subs: {},
|
|
@@ -6,9 +8,53 @@ const tests = {
|
|
|
6
8
|
|
|
7
9
|
let currentPath = [tests]
|
|
8
10
|
|
|
11
|
+
class ExpectToChange {
|
|
12
|
+
constructor({changeCallback, expect, ...restArgs}) {
|
|
13
|
+
restArgsError(restArgs)
|
|
14
|
+
|
|
15
|
+
this.expect = expect
|
|
16
|
+
this.changeCallback = changeCallback
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
by(count) {
|
|
20
|
+
this.count = count
|
|
21
|
+
|
|
22
|
+
return this.expect
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async runBefore() {
|
|
26
|
+
this.oldCount = await this.changeCallback()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async runAfter() {
|
|
30
|
+
this.newCount = await this.changeCallback()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async execute() {
|
|
34
|
+
const difference = this.newCount - this.oldCount
|
|
35
|
+
|
|
36
|
+
if (difference != this.count) {
|
|
37
|
+
throw new Error(`Expected to change by ${this.count} but changed by ${difference}`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
9
42
|
class Expect {
|
|
10
43
|
constructor(object) {
|
|
11
44
|
this._object = object
|
|
45
|
+
this.expectations = []
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
toChange(changeCallback) {
|
|
49
|
+
const expectToChange = new ExpectToChange({changeCallback, expect: this})
|
|
50
|
+
|
|
51
|
+
this.expectations.push(expectToChange)
|
|
52
|
+
|
|
53
|
+
return expectToChange
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
andChange(...args) {
|
|
57
|
+
return this.toChange(...args)
|
|
12
58
|
}
|
|
13
59
|
|
|
14
60
|
toEqual(result) {
|
|
@@ -17,6 +63,24 @@ class Expect {
|
|
|
17
63
|
}
|
|
18
64
|
}
|
|
19
65
|
|
|
66
|
+
async execute() {
|
|
67
|
+
for (const expectation of this.expectations) {
|
|
68
|
+
await expectation.runBefore()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const result = await this._object()
|
|
72
|
+
|
|
73
|
+
for (const expectation of this.expectations) {
|
|
74
|
+
await expectation.runAfter()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (const expectation of this.expectations) {
|
|
78
|
+
await expectation.execute()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
83
|
+
|
|
20
84
|
toHaveAttributes(result) {
|
|
21
85
|
const differences = {}
|
|
22
86
|
|