@volcanicminds/typeorm 2.0.1 → 2.1.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/README.md +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -147
- package/dist/index.js.map +1 -1
- package/dist/lib/entities/change.d.ts +1 -0
- package/dist/lib/entities/change.d.ts.map +1 -0
- package/dist/lib/entities/change.js +2 -6
- package/dist/lib/entities/change.js.map +1 -1
- package/dist/lib/entities/token.d.ts +1 -0
- package/dist/lib/entities/token.d.ts.map +1 -0
- package/dist/lib/entities/token.js +2 -6
- package/dist/lib/entities/token.js.map +1 -1
- package/dist/lib/entities/user.d.ts +1 -0
- package/dist/lib/entities/user.d.ts.map +1 -0
- package/dist/lib/entities/user.js +2 -6
- package/dist/lib/entities/user.js.map +1 -1
- package/dist/lib/loader/dataBaseManager.d.ts +1 -0
- package/dist/lib/loader/dataBaseManager.d.ts.map +1 -0
- package/dist/lib/loader/dataBaseManager.js +29 -83
- package/dist/lib/loader/dataBaseManager.js.map +1 -1
- package/dist/lib/loader/entities.d.ts +1 -0
- package/dist/lib/loader/entities.d.ts.map +1 -0
- package/dist/lib/loader/entities.js +10 -8
- package/dist/lib/loader/entities.js.map +1 -1
- package/dist/lib/loader/tokenManager.d.ts +1 -0
- package/dist/lib/loader/tokenManager.d.ts.map +1 -0
- package/dist/lib/loader/tokenManager.js +101 -138
- package/dist/lib/loader/tokenManager.js.map +1 -1
- package/dist/lib/loader/userManager.d.ts +1 -0
- package/dist/lib/loader/userManager.d.ts.map +1 -0
- package/dist/lib/loader/userManager.js +234 -321
- package/dist/lib/loader/userManager.js.map +1 -1
- package/dist/lib/query/builder.d.ts +1 -0
- package/dist/lib/query/builder.d.ts.map +1 -0
- package/dist/lib/query/builder.js +4 -8
- package/dist/lib/query/builder.js.map +1 -1
- package/dist/lib/query/parser.d.ts +1 -0
- package/dist/lib/query/parser.d.ts.map +1 -0
- package/dist/lib/query/parser.js +2 -7
- package/dist/lib/query/parser.js.map +1 -1
- package/dist/lib/query.d.ts +1 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +78 -110
- package/dist/lib/query.js.map +1 -1
- package/dist/lib/util/error.d.ts +5 -0
- package/dist/lib/util/error.d.ts.map +1 -0
- package/dist/lib/util/error.js +8 -0
- package/dist/lib/util/error.js.map +1 -0
- package/dist/lib/util/logger.d.ts +1 -0
- package/dist/lib/util/logger.d.ts.map +1 -0
- package/dist/lib/util/logger.js +12 -26
- package/dist/lib/util/logger.js.map +1 -1
- package/dist/lib/util/yn.d.ts +1 -0
- package/dist/lib/util/yn.d.ts.map +1 -0
- package/dist/lib/util/yn.js +1 -3
- package/dist/lib/util/yn.js.map +1 -1
- package/lib/entities/change.ts +11 -0
- package/lib/entities/token.ts +17 -0
- package/lib/entities/user.ts +24 -0
- package/lib/loader/dataBaseManager.ts +37 -0
- package/lib/loader/entities.ts +35 -0
- package/lib/loader/tokenManager.ts +128 -0
- package/lib/loader/userManager.ts +304 -0
- package/lib/query/builder.ts +38 -0
- package/lib/query/parser.ts +29 -0
- package/lib/query.ts +244 -0
- package/lib/util/error.ts +7 -0
- package/lib/util/logger.ts +23 -0
- package/lib/util/yn.ts +19 -0
- package/package.json +18 -26
- package/esm/index.d.ts +0 -12
- package/esm/index.js +0 -140
- package/esm/index.js.map +0 -1
- package/esm/lib/entities/change.d.ts +0 -10
- package/esm/lib/entities/change.js +0 -8
- package/esm/lib/entities/change.js.map +0 -1
- package/esm/lib/entities/token.d.ts +0 -15
- package/esm/lib/entities/token.js +0 -8
- package/esm/lib/entities/token.js.map +0 -1
- package/esm/lib/entities/user.d.ts +0 -22
- package/esm/lib/entities/user.js +0 -8
- package/esm/lib/entities/user.js.map +0 -1
- package/esm/lib/loader/dataBaseManager.d.ts +0 -4
- package/esm/lib/loader/dataBaseManager.js +0 -76
- package/esm/lib/loader/dataBaseManager.js.map +0 -1
- package/esm/lib/loader/entities.d.ts +0 -5
- package/esm/lib/loader/entities.js +0 -28
- package/esm/lib/loader/entities.js.map +0 -1
- package/esm/lib/loader/tokenManager.d.ts +0 -21
- package/esm/lib/loader/tokenManager.js +0 -128
- package/esm/lib/loader/tokenManager.js.map +0 -1
- package/esm/lib/loader/userManager.d.ts +0 -32
- package/esm/lib/loader/userManager.js +0 -341
- package/esm/lib/loader/userManager.js.map +0 -1
- package/esm/lib/query/builder.d.ts +0 -1
- package/esm/lib/query/builder.js +0 -37
- package/esm/lib/query/builder.js.map +0 -1
- package/esm/lib/query/parser.d.ts +0 -1
- package/esm/lib/query/parser.js +0 -32
- package/esm/lib/query/parser.js.map +0 -1
- package/esm/lib/query.d.ts +0 -33
- package/esm/lib/query.js +0 -210
- package/esm/lib/query.js.map +0 -1
- package/esm/lib/util/logger.d.ts +0 -6
- package/esm/lib/util/logger.js +0 -33
- package/esm/lib/util/logger.js.map +0 -1
- package/esm/lib/util/yn.d.ts +0 -1
- package/esm/lib/util/yn.js +0 -17
- package/esm/lib/util/yn.js.map +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseEntity } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
export abstract class Token extends BaseEntity {
|
|
4
|
+
// abstract id: any
|
|
5
|
+
abstract externalId: string
|
|
6
|
+
abstract name: string
|
|
7
|
+
abstract description: string
|
|
8
|
+
abstract blocked: boolean
|
|
9
|
+
abstract blockedReason: string
|
|
10
|
+
abstract blockedAt: Date
|
|
11
|
+
abstract roles: string[]
|
|
12
|
+
abstract createdAt: Date
|
|
13
|
+
abstract updatedAt: Date
|
|
14
|
+
abstract deletedAt: Date
|
|
15
|
+
abstract setId(id: any)
|
|
16
|
+
abstract getId(): any
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseEntity } from 'typeorm'
|
|
2
|
+
|
|
3
|
+
export abstract class User extends BaseEntity {
|
|
4
|
+
// abstract id: any
|
|
5
|
+
abstract externalId: string
|
|
6
|
+
abstract username: string
|
|
7
|
+
abstract email: string
|
|
8
|
+
abstract password: string
|
|
9
|
+
abstract confirmed: boolean
|
|
10
|
+
abstract confirmedAt: Date
|
|
11
|
+
abstract passwordChangedAt: Date
|
|
12
|
+
abstract blocked: boolean
|
|
13
|
+
abstract blockedReason: string
|
|
14
|
+
abstract blockedAt: Date
|
|
15
|
+
abstract resetPasswordToken: string
|
|
16
|
+
abstract confirmationToken: string
|
|
17
|
+
abstract roles: string[]
|
|
18
|
+
abstract version: number
|
|
19
|
+
abstract createdAt: Date
|
|
20
|
+
abstract updatedAt: Date
|
|
21
|
+
abstract deletedAt: Date
|
|
22
|
+
abstract setId(id: any)
|
|
23
|
+
abstract getId(): any
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as log from '../util/logger.js'
|
|
2
|
+
|
|
3
|
+
export function isImplemented() {
|
|
4
|
+
return true
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function synchronizeSchemas() {
|
|
8
|
+
try {
|
|
9
|
+
await global.connection.synchronize()
|
|
10
|
+
return true
|
|
11
|
+
} catch (error) {
|
|
12
|
+
throw error
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function retrieveBy(entityName, entityId) {
|
|
17
|
+
try {
|
|
18
|
+
return await global.entity[entityName].findOneById(entityId)
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (!(entityName in global.entity)) {
|
|
21
|
+
log.error(`${entityName} not found in global.entity`)
|
|
22
|
+
}
|
|
23
|
+
throw error
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function addChange(entityName, entityId, status, userId, contents, changeEntity = 'Change') {
|
|
28
|
+
try {
|
|
29
|
+
const newChange = await global.entity[changeEntity].create({ entityName, entityId, status, userId, contents })
|
|
30
|
+
return global.entity[changeEntity].save(newChange)
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (!(changeEntity in global.entity)) {
|
|
33
|
+
log.error(`${changeEntity} not found in global.entity`)
|
|
34
|
+
}
|
|
35
|
+
throw error
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as path from 'path'
|
|
2
|
+
import { globSync } from 'glob'
|
|
3
|
+
import pluralize from 'pluralize'
|
|
4
|
+
import { createRequire } from 'module'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url)
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = path.dirname(__filename)
|
|
10
|
+
|
|
11
|
+
export function load() {
|
|
12
|
+
const classes: any = {}
|
|
13
|
+
const repositories: any = {}
|
|
14
|
+
const entities: any[] = []
|
|
15
|
+
|
|
16
|
+
const patterns = [
|
|
17
|
+
path.join(__dirname, '..', 'entities', '*.e.{ts,js}'),
|
|
18
|
+
path.join(process.cwd(), 'src', 'entities', '*.e.{ts,js}')
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
patterns.forEach((pattern) => {
|
|
22
|
+
globSync(pattern, { nodir: true, windowsPathsNoEscape: true }).forEach((f: string) => {
|
|
23
|
+
const entityClass = require(f)
|
|
24
|
+
const entityNames = Object.keys(entityClass)
|
|
25
|
+
|
|
26
|
+
entityNames.map((name) => {
|
|
27
|
+
classes[name] = entityClass[name]
|
|
28
|
+
repositories[pluralize(name.toLowerCase())] = entityClass[name]
|
|
29
|
+
entities.push(entityClass[name])
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return { classes, repositories, entities }
|
|
35
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import * as Crypto from 'crypto'
|
|
2
|
+
import { ServiceError } from '../util/error.js'
|
|
3
|
+
import { executeCountQuery, executeFindQuery } from '../query.js'
|
|
4
|
+
|
|
5
|
+
export function isImplemented() {
|
|
6
|
+
return true
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function isValidToken(data: typeof global.entity.Token) {
|
|
10
|
+
return !!data && (!!data._id || !!data.id) && !!data.externalId && !!data.name
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function createToken(data: typeof global.entity.Token) {
|
|
14
|
+
const { name, description } = data
|
|
15
|
+
|
|
16
|
+
if (!name) {
|
|
17
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
let externalId, token
|
|
22
|
+
do {
|
|
23
|
+
externalId = Crypto.randomUUID({ disableEntropyCache: true })
|
|
24
|
+
|
|
25
|
+
token = await global.repository.tokens.findOneBy({ externalId: externalId })
|
|
26
|
+
} while (token != null)
|
|
27
|
+
|
|
28
|
+
token = await global.entity.Token.create({
|
|
29
|
+
...data,
|
|
30
|
+
name: name,
|
|
31
|
+
description: description,
|
|
32
|
+
blocked: false,
|
|
33
|
+
blockedReason: null,
|
|
34
|
+
externalId: externalId
|
|
35
|
+
} as typeof global.entity.Token)
|
|
36
|
+
|
|
37
|
+
return await global.entity.Token.save(token)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
throw error
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function resetExternalId(id: string) {
|
|
44
|
+
if (!id) {
|
|
45
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
let externalId, token
|
|
50
|
+
do {
|
|
51
|
+
externalId = Crypto.randomUUID({ disableEntropyCache: true })
|
|
52
|
+
token = await global.repository.tokens.findOneBy({ externalId: externalId })
|
|
53
|
+
} while (token != null)
|
|
54
|
+
|
|
55
|
+
// TODO: use externalId instead id
|
|
56
|
+
return await updateTokenById(id, { externalId: externalId })
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error?.code == 23505) {
|
|
59
|
+
throw new Error('External ID not changed')
|
|
60
|
+
}
|
|
61
|
+
throw error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function updateTokenById(id: string, token: typeof global.entity.Token) {
|
|
66
|
+
if (!id || !token) {
|
|
67
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const tokenEx = await global.repository.tokens.findOneById(id)
|
|
71
|
+
if (!tokenEx) {
|
|
72
|
+
throw new ServiceError('Token not found', 404)
|
|
73
|
+
}
|
|
74
|
+
const merged = global.repository.tokens.merge(tokenEx, token)
|
|
75
|
+
return await global.entity.Token.save(merged)
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw error
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function retrieveTokenById(id: string) {
|
|
82
|
+
if (!id) {
|
|
83
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return await global.repository.tokens.findOneById(id)
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw error
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function retrieveTokenByExternalId(externalId: string) {
|
|
93
|
+
if (!externalId) {
|
|
94
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return await global.repository.tokens.findOneBy({ externalId: externalId })
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw error
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function blockTokenById(id: string, reason: string) {
|
|
104
|
+
return updateTokenById(id, { blocked: true, blockedAt: new Date(), blockedReason: reason })
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function unblockTokenById(id: string) {
|
|
108
|
+
return updateTokenById(id, { blocked: false, blockedAt: new Date(), blockedReason: null })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function countQuery(data: any) {
|
|
112
|
+
return await executeCountQuery(global.repository.tokens, data, {})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function findQuery(data: any) {
|
|
116
|
+
return await executeFindQuery(global.repository.tokens, {}, data)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function removeTokenById(id: string) {
|
|
120
|
+
if (!id) {
|
|
121
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
return await global.repository.tokens.delete(id)
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw error
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import * as bcrypt from 'bcrypt'
|
|
2
|
+
import * as Crypto from 'crypto'
|
|
3
|
+
import { ServiceError } from '../util/error.js'
|
|
4
|
+
import { executeCountQuery, executeFindQuery } from '../query.js'
|
|
5
|
+
|
|
6
|
+
export function isImplemented() {
|
|
7
|
+
return true
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function isValidUser(data: typeof global.entity.User) {
|
|
11
|
+
return !!data && (!!data._id || !!data.id) && !!data.externalId && !!data.email && !!data.password
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function createUser(data: typeof global.entity.User) {
|
|
15
|
+
const { username, email, password } = data
|
|
16
|
+
|
|
17
|
+
if (!email || !password) {
|
|
18
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const salt = await bcrypt.genSalt(12)
|
|
22
|
+
const hashedPassword = await bcrypt.hash(password, salt)
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
let externalId, user
|
|
26
|
+
do {
|
|
27
|
+
externalId = Crypto.randomUUID({ disableEntropyCache: true })
|
|
28
|
+
|
|
29
|
+
user = await global.repository.users.findOneBy({ externalId: externalId })
|
|
30
|
+
} while (user != null)
|
|
31
|
+
|
|
32
|
+
user = await global.entity.User.create({
|
|
33
|
+
...data,
|
|
34
|
+
passwordChangedAt: new Date(),
|
|
35
|
+
confirmed: false,
|
|
36
|
+
confirmationToken: Crypto.randomBytes(64).toString('hex'),
|
|
37
|
+
blocked: false,
|
|
38
|
+
blockedReason: null,
|
|
39
|
+
externalId: externalId,
|
|
40
|
+
email: email,
|
|
41
|
+
username: username || email,
|
|
42
|
+
password: hashedPassword
|
|
43
|
+
} as typeof global.entity.User)
|
|
44
|
+
|
|
45
|
+
return await global.entity.User.save(user)
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error?.code == 23505) {
|
|
48
|
+
throw new ServiceError('Email or username already registered', 409)
|
|
49
|
+
}
|
|
50
|
+
throw error
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function deleteUser(id: string) {
|
|
55
|
+
if (!id) {
|
|
56
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const userEx = await retrieveUserById(id)
|
|
61
|
+
if (!userEx) {
|
|
62
|
+
throw new ServiceError('User not found', 404)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return global.entity.User.delete(id)
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw error
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function resetExternalId(id: string) {
|
|
72
|
+
if (!id) {
|
|
73
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
let externalId, user
|
|
78
|
+
do {
|
|
79
|
+
externalId = Crypto.randomUUID({ disableEntropyCache: true })
|
|
80
|
+
user = await global.repository.users.findOneBy({ externalId: externalId })
|
|
81
|
+
} while (user != null)
|
|
82
|
+
|
|
83
|
+
// TODO: use externalId instead id
|
|
84
|
+
return await updateUserById(id, { externalId: externalId })
|
|
85
|
+
} catch (error) {
|
|
86
|
+
if (error?.code == 23505) {
|
|
87
|
+
throw new ServiceError('External ID not changed', 409)
|
|
88
|
+
}
|
|
89
|
+
throw error
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function updateUserById(id: string, user: typeof global.entity.User) {
|
|
94
|
+
if (!id || !user) {
|
|
95
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const userEx = await retrieveUserById(id)
|
|
99
|
+
if (!userEx) {
|
|
100
|
+
throw new ServiceError('User not found', 404)
|
|
101
|
+
}
|
|
102
|
+
const merged = global.repository.users.merge(userEx, user)
|
|
103
|
+
return await global.entity.User.save(merged)
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw error
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function retrieveUserById(id: string) {
|
|
110
|
+
if (!id) {
|
|
111
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return await global.repository.users.findOneById(id)
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw error
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function retrieveUserByEmail(email: string) {
|
|
121
|
+
if (!email) {
|
|
122
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
return await global.repository.users.findOneBy({ email: email })
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw error
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function retrieveUserByUsername(username: string) {
|
|
132
|
+
if (!username) {
|
|
133
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
return await global.repository.users.findOneBy({ username })
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw error
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function retrieveUserByConfirmationToken(code: string) {
|
|
143
|
+
if (!code) {
|
|
144
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return await global.repository.users.findOneBy({ confirmationToken: code })
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw error
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function retrieveUserByResetPasswordToken(code: string) {
|
|
154
|
+
if (!code) {
|
|
155
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
return await global.repository.users.findOneBy({ resetPasswordToken: code })
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw error
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function retrieveUserByExternalId(externalId: string) {
|
|
165
|
+
if (!externalId) {
|
|
166
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
return await global.repository.users.findOne({
|
|
170
|
+
where: { externalId: externalId },
|
|
171
|
+
cache: global.cacheTimeout
|
|
172
|
+
})
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw error
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function retrieveUserByPassword(email: string, password: string) {
|
|
179
|
+
if (!email || !password) {
|
|
180
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const user = await global.repository.users.findOneBy({ email: email })
|
|
184
|
+
if (!user) {
|
|
185
|
+
throw new Error('Wrong credentials')
|
|
186
|
+
}
|
|
187
|
+
const match = await bcrypt.compare(password, user.password)
|
|
188
|
+
return match ? user : null
|
|
189
|
+
} catch (error) {
|
|
190
|
+
throw error
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function changePassword(email: string, password: string, oldPassword: string) {
|
|
195
|
+
if (!email || !password || !oldPassword) {
|
|
196
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const user = await global.repository.users.findOneBy({ email: email })
|
|
200
|
+
const match = await bcrypt.compare(oldPassword, user.password)
|
|
201
|
+
if (match) {
|
|
202
|
+
const salt = await bcrypt.genSalt(12)
|
|
203
|
+
const hashedPassword = await bcrypt.hash(password, salt)
|
|
204
|
+
return await global.entity.User.save({ ...user, passwordChangedAt: new Date(), password: hashedPassword })
|
|
205
|
+
}
|
|
206
|
+
throw new ServiceError('Password not changed', 400)
|
|
207
|
+
} catch (error) {
|
|
208
|
+
throw error
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function forgotPassword(email: string) {
|
|
213
|
+
if (!email) {
|
|
214
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const user = await global.repository.users.findOneBy({ email: email })
|
|
218
|
+
|
|
219
|
+
if (user) {
|
|
220
|
+
return await global.entity.User.save({
|
|
221
|
+
...user,
|
|
222
|
+
resetPasswordToken: Crypto.randomBytes(64).toString('hex')
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
throw new ServiceError('Password not changed', 400)
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throw error
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function resetPassword(user: typeof global.entity.User, password: string) {
|
|
232
|
+
if (!user || !password) {
|
|
233
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
const salt = await bcrypt.genSalt(12)
|
|
237
|
+
const hashedPassword = await bcrypt.hash(password, salt)
|
|
238
|
+
return await global.entity.User.save({
|
|
239
|
+
...user,
|
|
240
|
+
passwordChangedAt: new Date(),
|
|
241
|
+
confirmed: true,
|
|
242
|
+
confirmedAt: new Date(),
|
|
243
|
+
resetPasswordToken: null,
|
|
244
|
+
password: hashedPassword
|
|
245
|
+
})
|
|
246
|
+
} catch (error) {
|
|
247
|
+
throw error
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function userConfirmation(user: typeof global.entity.User) {
|
|
252
|
+
if (!user) {
|
|
253
|
+
throw new ServiceError('Invalid parameters', 400)
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
return await global.entity.User.save({ ...user, confirmed: true, confirmedAt: new Date(), confirmationToken: null })
|
|
257
|
+
} catch (error) {
|
|
258
|
+
throw error
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export async function blockUserById(id: string, reason: string) {
|
|
263
|
+
return updateUserById(id, { blocked: true, blockedAt: new Date(), blockedReason: reason })
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function unblockUserById(id: string) {
|
|
267
|
+
return updateUserById(id, { blocked: false, blockedAt: new Date(), blockedReason: null })
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function isPasswordToBeChanged(user: typeof global.entity.User) {
|
|
271
|
+
if (process.env.PASSWORD_EXPIRATION_DAYS != null) {
|
|
272
|
+
let passwordExpirationDays = -1
|
|
273
|
+
try {
|
|
274
|
+
passwordExpirationDays = Number(process.env.PASSWORD_EXPIRATION_DAYS)
|
|
275
|
+
if (passwordExpirationDays <= 0) {
|
|
276
|
+
throw new Error('PASSWORD_EXPIRATION_DAYS_ENV_INVALID')
|
|
277
|
+
}
|
|
278
|
+
} catch (e) {
|
|
279
|
+
throw new Error(e)
|
|
280
|
+
}
|
|
281
|
+
const { passwordChangedAt } = user
|
|
282
|
+
const date1 = new Date(passwordChangedAt)
|
|
283
|
+
const date2 = new Date()
|
|
284
|
+
const differenceInTime = date2.getTime() - date1.getTime()
|
|
285
|
+
const differenceInDays = differenceInTime / (1000 * 3600 * 24)
|
|
286
|
+
|
|
287
|
+
return differenceInDays >= passwordExpirationDays
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return false
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export async function countQuery(data: any) {
|
|
294
|
+
return await executeCountQuery(global.repository.users, data)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function findQuery(data: any) {
|
|
298
|
+
return await executeFindQuery(global.repository.users, {}, data)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export async function disableUserById(id: string) {
|
|
302
|
+
await updateUserById(id, { blocked: true, blockedAt: new Date(), blockedReason: 'User disabled to unregister' })
|
|
303
|
+
return resetExternalId(id)
|
|
304
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const combineSqlAnd = (left, right) => {
|
|
2
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
3
|
+
throw new Error('Combining OR conditions with AND is not supported in this simplified SQL builder.')
|
|
4
|
+
}
|
|
5
|
+
return { ...left, ...right }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const buildWhereFromAst = (ast: any, aliasMap: Map<string, any>, isMongo: boolean) => {
|
|
9
|
+
if (!ast) {
|
|
10
|
+
return {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (ast.type === 'operand') {
|
|
14
|
+
if (!aliasMap.has(ast.value)) {
|
|
15
|
+
throw new Error(`Alias "${ast.value}" used in _logic not found in query parameters.`)
|
|
16
|
+
}
|
|
17
|
+
return aliasMap.get(ast.value)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const left = buildWhereFromAst(ast.left, aliasMap, isMongo)
|
|
21
|
+
const right = buildWhereFromAst(ast.right, aliasMap, isMongo)
|
|
22
|
+
|
|
23
|
+
if (ast.type === 'AND') {
|
|
24
|
+
if (isMongo) {
|
|
25
|
+
return { $and: [left, right].flat() }
|
|
26
|
+
}
|
|
27
|
+
return combineSqlAnd(left, right)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (ast.type === 'OR') {
|
|
31
|
+
if (isMongo) {
|
|
32
|
+
return { $or: [left, right].flat() }
|
|
33
|
+
}
|
|
34
|
+
return [left, right].flat()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {}
|
|
38
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const parseLogicExpression = (logic: string) => {
|
|
2
|
+
const tokens = logic.match(/\(|\)|[A-Za-z0-9_:]+|\s*(AND|OR)\s*/g)?.filter((t) => t.trim()) || []
|
|
3
|
+
let pos = 0
|
|
4
|
+
|
|
5
|
+
const parseExpression = () => {
|
|
6
|
+
let left = parseTerm()
|
|
7
|
+
while (pos < tokens.length && (tokens[pos].trim() === 'OR' || tokens[pos].trim() === 'AND')) {
|
|
8
|
+
const operator = tokens[pos++].trim()
|
|
9
|
+
const right = parseTerm()
|
|
10
|
+
left = { type: operator, left, right }
|
|
11
|
+
}
|
|
12
|
+
return left
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const parseTerm = () => {
|
|
16
|
+
if (tokens[pos] === '(') {
|
|
17
|
+
pos++
|
|
18
|
+
const node = parseExpression()
|
|
19
|
+
if (tokens[pos] !== ')') {
|
|
20
|
+
throw new Error('Mismatched parentheses in _logic expression')
|
|
21
|
+
}
|
|
22
|
+
pos++
|
|
23
|
+
return node
|
|
24
|
+
}
|
|
25
|
+
return { type: 'operand', value: tokens[pos++] }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return parseExpression()
|
|
29
|
+
}
|