crazy-odds-bet-shared 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/models/index.js +4 -0
- package/models/permission.js +21 -0
- package/models/role.js +21 -0
- package/models/user.js +40 -6
- package/package.json +2 -1
package/models/index.js
CHANGED
|
@@ -6,6 +6,8 @@ const { Team } = require('./team');
|
|
|
6
6
|
const { Player } = require('./player');
|
|
7
7
|
const { Fixture, FixtureStatus } = require('./fixture');
|
|
8
8
|
const { User } = require('./user');
|
|
9
|
+
const { Permission } = require('./permission');
|
|
10
|
+
const { Role } = require('./role');
|
|
9
11
|
const { MarketTemplate } = require('./marketTemplate');
|
|
10
12
|
const { Market } = require('./market');
|
|
11
13
|
const { Odd } = require('./odd');
|
|
@@ -23,6 +25,8 @@ module.exports = {
|
|
|
23
25
|
Fixture,
|
|
24
26
|
FixtureStatus,
|
|
25
27
|
User,
|
|
28
|
+
Permission,
|
|
29
|
+
Role,
|
|
26
30
|
MarketTemplate,
|
|
27
31
|
Market,
|
|
28
32
|
Odd,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { Schema, model } = require('mongoose');
|
|
2
|
+
const { createBaseSchema, baseSchemaOptions } = require('./base');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Permission schema - granular permission codes (e.g. "users:read", "roles:write")
|
|
6
|
+
*/
|
|
7
|
+
const permissionSchema = new Schema(
|
|
8
|
+
{
|
|
9
|
+
...createBaseSchema(),
|
|
10
|
+
code: { type: String, required: true, unique: true, trim: true },
|
|
11
|
+
name: { type: String, required: true, trim: true },
|
|
12
|
+
description: { type: String, default: '' }
|
|
13
|
+
},
|
|
14
|
+
baseSchemaOptions
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
permissionSchema.index({ code: 1 });
|
|
18
|
+
|
|
19
|
+
const Permission = model('Permission', permissionSchema);
|
|
20
|
+
|
|
21
|
+
module.exports = { Permission };
|
package/models/role.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { Schema, model } = require('mongoose');
|
|
2
|
+
const { createBaseSchema, baseSchemaOptions } = require('./base');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Role schema - named roles with a set of permission IDs (inherited by users assigned this role)
|
|
6
|
+
*/
|
|
7
|
+
const roleSchema = new Schema(
|
|
8
|
+
{
|
|
9
|
+
...createBaseSchema(),
|
|
10
|
+
name: { type: String, required: true, unique: true, trim: true },
|
|
11
|
+
description: { type: String, default: '' },
|
|
12
|
+
permissionIds: [{ type: String, ref: 'Permission' }]
|
|
13
|
+
},
|
|
14
|
+
baseSchemaOptions
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
roleSchema.index({ name: 1 });
|
|
18
|
+
|
|
19
|
+
const Role = model('Role', roleSchema);
|
|
20
|
+
|
|
21
|
+
module.exports = { Role };
|
package/models/user.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
const bcrypt = require('bcrypt');
|
|
1
2
|
const { Schema, model } = require('mongoose');
|
|
2
3
|
const { createBaseSchema, baseSchemaOptions } = require('./base');
|
|
3
4
|
|
|
5
|
+
const BCRYPT_ROUNDS = 10;
|
|
6
|
+
const BCRYPT_PREFIX = '$2';
|
|
7
|
+
|
|
4
8
|
/**
|
|
5
9
|
* User schema
|
|
6
10
|
*/
|
|
@@ -10,15 +14,32 @@ const userSchema = new Schema(
|
|
|
10
14
|
email: { type: String, required: true, unique: true, lowercase: true, trim: true },
|
|
11
15
|
password: { type: String, required: true },
|
|
12
16
|
name: { type: String, required: true },
|
|
13
|
-
|
|
17
|
+
roleId: { type: String, ref: 'Role', default: null },
|
|
18
|
+
/** Custom permission IDs added on top of role permissions */
|
|
19
|
+
permissionIds: [{ type: String, ref: 'Permission' }],
|
|
20
|
+
config: { type: Schema.Types.Mixed, default: {} },
|
|
14
21
|
isActive: { type: Boolean, default: true }
|
|
15
22
|
},
|
|
16
23
|
baseSchemaOptions
|
|
17
24
|
);
|
|
18
25
|
|
|
26
|
+
// Hash password before saving (only when password was modified and not already a hash)
|
|
27
|
+
userSchema.pre('save', async function (next) {
|
|
28
|
+
if (!this.isModified('password')) return next();
|
|
29
|
+
if (typeof this.password !== 'string') return next();
|
|
30
|
+
if (this.password.startsWith(BCRYPT_PREFIX)) return next();
|
|
31
|
+
try {
|
|
32
|
+
this.password = await bcrypt.hash(this.password, BCRYPT_ROUNDS);
|
|
33
|
+
next();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
next(err);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
19
39
|
// Indexes
|
|
20
40
|
userSchema.index({ isActive: 1 });
|
|
21
|
-
userSchema.index({
|
|
41
|
+
userSchema.index({ roleId: 1 });
|
|
42
|
+
userSchema.index({ email: 1, isActive: 1 });
|
|
22
43
|
|
|
23
44
|
// Static methods
|
|
24
45
|
userSchema.static('findByEmail', function (email) {
|
|
@@ -29,8 +50,11 @@ userSchema.static('getActive', function () {
|
|
|
29
50
|
return this.find({ isActive: true }).select('-password');
|
|
30
51
|
});
|
|
31
52
|
|
|
32
|
-
userSchema.static('findAdmins', function () {
|
|
33
|
-
|
|
53
|
+
userSchema.static('findAdmins', async function () {
|
|
54
|
+
const Role = this.db.model('Role');
|
|
55
|
+
const adminRole = await Role.findOne({ name: 'Admin' }).lean();
|
|
56
|
+
if (!adminRole) return this.find({ _id: { $in: [] } }).select('-password');
|
|
57
|
+
return this.find({ roleId: adminRole._id, isActive: true }).select('-password');
|
|
34
58
|
});
|
|
35
59
|
|
|
36
60
|
// Instance methods
|
|
@@ -45,8 +69,18 @@ userSchema.method('deactivate', async function () {
|
|
|
45
69
|
});
|
|
46
70
|
|
|
47
71
|
userSchema.method('comparePassword', async function (password) {
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
if (!this.password) return false;
|
|
73
|
+
if (this.password.startsWith(BCRYPT_PREFIX)) {
|
|
74
|
+
const match = await bcrypt.compare(password, this.password);
|
|
75
|
+
if (match) return true;
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const plainMatch = this.password === password;
|
|
79
|
+
if (plainMatch) {
|
|
80
|
+
this.password = await bcrypt.hash(password, BCRYPT_ROUNDS);
|
|
81
|
+
await this.save({ validateBeforeSave: false });
|
|
82
|
+
}
|
|
83
|
+
return plainMatch;
|
|
50
84
|
});
|
|
51
85
|
|
|
52
86
|
// Middleware to exclude password from toJSON
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crazy-odds-bet-shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
4
|
"description": "Shared MongoDB models and utilities for odds project",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"exports": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"author": "",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"bcrypt": "^5.1.1",
|
|
32
33
|
"dotenv": "^16.3.1",
|
|
33
34
|
"mongoose": "^8.0.0",
|
|
34
35
|
"uuid": "^9.0.1"
|