diff options
Diffstat (limited to 'server/models/user')
-rw-r--r-- | server/models/user/index.ts | 2 | ||||
-rw-r--r-- | server/models/user/user-interface.ts | 63 | ||||
-rw-r--r-- | server/models/user/user-video-rate-interface.ts | 22 | ||||
-rw-r--r-- | server/models/user/user-video-rate.ts | 80 | ||||
-rw-r--r-- | server/models/user/user.ts | 221 |
5 files changed, 388 insertions, 0 deletions
diff --git a/server/models/user/index.ts b/server/models/user/index.ts new file mode 100644 index 000000000..ed3689518 --- /dev/null +++ b/server/models/user/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './user-video-rate-interface' | ||
2 | export * from './user-interface' | ||
diff --git a/server/models/user/user-interface.ts b/server/models/user/user-interface.ts new file mode 100644 index 000000000..1ba4bd800 --- /dev/null +++ b/server/models/user/user-interface.ts | |||
@@ -0,0 +1,63 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import * as Bluebird from 'bluebird' | ||
3 | |||
4 | // Don't use barrel, import just what we need | ||
5 | import { User as FormatedUser } from '../../../shared/models/user.model' | ||
6 | |||
7 | export namespace UserMethods { | ||
8 | export type IsPasswordMatchCallback = (err: Error, same: boolean) => void | ||
9 | export type IsPasswordMatch = (password: string, callback: IsPasswordMatchCallback) => void | ||
10 | |||
11 | export type ToFormatedJSON = () => FormatedUser | ||
12 | export type IsAdmin = () => boolean | ||
13 | |||
14 | export type CountTotalCallback = (err: Error, total: number) => void | ||
15 | export type CountTotal = (callback: CountTotalCallback) => void | ||
16 | |||
17 | export type GetByUsername = (username: string) => Bluebird<UserInstance> | ||
18 | |||
19 | export type ListCallback = (err: Error, userInstances: UserInstance[]) => void | ||
20 | export type List = (callback: ListCallback) => void | ||
21 | |||
22 | export type ListForApiCallback = (err: Error, userInstances?: UserInstance[], total?: number) => void | ||
23 | export type ListForApi = (start: number, count: number, sort: string, callback: ListForApiCallback) => void | ||
24 | |||
25 | export type LoadByIdCallback = (err: Error, userInstance: UserInstance) => void | ||
26 | export type LoadById = (id: number, callback: LoadByIdCallback) => void | ||
27 | |||
28 | export type LoadByUsernameCallback = (err: Error, userInstance: UserInstance) => void | ||
29 | export type LoadByUsername = (username: string, callback: LoadByUsernameCallback) => void | ||
30 | |||
31 | export type LoadByUsernameOrEmailCallback = (err: Error, userInstance: UserInstance) => void | ||
32 | export type LoadByUsernameOrEmail = (username: string, email: string, callback: LoadByUsernameOrEmailCallback) => void | ||
33 | } | ||
34 | |||
35 | export interface UserClass { | ||
36 | isPasswordMatch: UserMethods.IsPasswordMatch, | ||
37 | toFormatedJSON: UserMethods.ToFormatedJSON, | ||
38 | isAdmin: UserMethods.IsAdmin, | ||
39 | |||
40 | countTotal: UserMethods.CountTotal, | ||
41 | getByUsername: UserMethods.GetByUsername, | ||
42 | list: UserMethods.List, | ||
43 | listForApi: UserMethods.ListForApi, | ||
44 | loadById: UserMethods.LoadById, | ||
45 | loadByUsername: UserMethods.LoadByUsername, | ||
46 | loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail | ||
47 | } | ||
48 | |||
49 | export interface UserAttributes { | ||
50 | password: string | ||
51 | username: string | ||
52 | email: string | ||
53 | displayNSFW?: boolean | ||
54 | role: string | ||
55 | } | ||
56 | |||
57 | export interface UserInstance extends UserClass, UserAttributes, Sequelize.Instance<UserAttributes> { | ||
58 | id: number | ||
59 | createdAt: Date | ||
60 | updatedAt: Date | ||
61 | } | ||
62 | |||
63 | export interface UserModel extends UserClass, Sequelize.Model<UserInstance, UserAttributes> {} | ||
diff --git a/server/models/user/user-video-rate-interface.ts b/server/models/user/user-video-rate-interface.ts new file mode 100644 index 000000000..e48869fd2 --- /dev/null +++ b/server/models/user/user-video-rate-interface.ts | |||
@@ -0,0 +1,22 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | export namespace UserVideoRateMethods { | ||
4 | export type LoadCallback = (err: Error, userVideoRateInstance: UserVideoRateInstance) => void | ||
5 | export type Load = (userId, videoId, transaction, callback) => void | ||
6 | } | ||
7 | |||
8 | export interface UserVideoRateClass { | ||
9 | load: UserVideoRateMethods.Load | ||
10 | } | ||
11 | |||
12 | export interface UserVideoRateAttributes { | ||
13 | type: string | ||
14 | } | ||
15 | |||
16 | export interface UserVideoRateInstance extends UserVideoRateClass, UserVideoRateAttributes, Sequelize.Instance<UserVideoRateAttributes> { | ||
17 | id: number | ||
18 | createdAt: Date | ||
19 | updatedAt: Date | ||
20 | } | ||
21 | |||
22 | export interface UserVideoRateModel extends UserVideoRateClass, Sequelize.Model<UserVideoRateInstance, UserVideoRateAttributes> {} | ||
diff --git a/server/models/user/user-video-rate.ts b/server/models/user/user-video-rate.ts new file mode 100644 index 000000000..68be62fc2 --- /dev/null +++ b/server/models/user/user-video-rate.ts | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | User rates per video. | ||
3 | |||
4 | */ | ||
5 | import { values } from 'lodash' | ||
6 | import * as Sequelize from 'sequelize' | ||
7 | |||
8 | import { VIDEO_RATE_TYPES } from '../../initializers' | ||
9 | |||
10 | import { addMethodsToModel } from '../utils' | ||
11 | import { | ||
12 | UserVideoRateClass, | ||
13 | UserVideoRateInstance, | ||
14 | UserVideoRateAttributes, | ||
15 | |||
16 | UserVideoRateMethods | ||
17 | } from './user-video-rate-interface' | ||
18 | |||
19 | let UserVideoRate: Sequelize.Model<UserVideoRateInstance, UserVideoRateAttributes> | ||
20 | let load: UserVideoRateMethods.Load | ||
21 | |||
22 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | ||
23 | UserVideoRate = sequelize.define<UserVideoRateInstance, UserVideoRateAttributes>('UserVideoRate', | ||
24 | { | ||
25 | type: { | ||
26 | type: DataTypes.ENUM(values(VIDEO_RATE_TYPES)), | ||
27 | allowNull: false | ||
28 | } | ||
29 | }, | ||
30 | { | ||
31 | indexes: [ | ||
32 | { | ||
33 | fields: [ 'videoId', 'userId', 'type' ], | ||
34 | unique: true | ||
35 | } | ||
36 | ] | ||
37 | } | ||
38 | ) | ||
39 | |||
40 | const classMethods = [ | ||
41 | associate, | ||
42 | |||
43 | load | ||
44 | ] | ||
45 | addMethodsToModel(UserVideoRate, classMethods) | ||
46 | |||
47 | return UserVideoRate | ||
48 | } | ||
49 | |||
50 | // ------------------------------ STATICS ------------------------------ | ||
51 | |||
52 | function associate (models) { | ||
53 | UserVideoRate.belongsTo(models.Video, { | ||
54 | foreignKey: { | ||
55 | name: 'videoId', | ||
56 | allowNull: false | ||
57 | }, | ||
58 | onDelete: 'CASCADE' | ||
59 | }) | ||
60 | |||
61 | UserVideoRate.belongsTo(models.User, { | ||
62 | foreignKey: { | ||
63 | name: 'userId', | ||
64 | allowNull: false | ||
65 | }, | ||
66 | onDelete: 'CASCADE' | ||
67 | }) | ||
68 | } | ||
69 | |||
70 | load = function (userId: number, videoId: number, transaction: Sequelize.Transaction, callback: UserVideoRateMethods.LoadCallback) { | ||
71 | const options: Sequelize.FindOptions = { | ||
72 | where: { | ||
73 | userId, | ||
74 | videoId | ||
75 | } | ||
76 | } | ||
77 | if (transaction) options.transaction = transaction | ||
78 | |||
79 | return UserVideoRate.findOne(options).asCallback(callback) | ||
80 | } | ||
diff --git a/server/models/user/user.ts b/server/models/user/user.ts new file mode 100644 index 000000000..d78f5f845 --- /dev/null +++ b/server/models/user/user.ts | |||
@@ -0,0 +1,221 @@ | |||
1 | import { values } from 'lodash' | ||
2 | import * as Sequelize from 'sequelize' | ||
3 | |||
4 | import { getSort } from '../utils' | ||
5 | import { USER_ROLES } from '../../initializers' | ||
6 | import { | ||
7 | cryptPassword, | ||
8 | comparePassword, | ||
9 | isUserPasswordValid, | ||
10 | isUserUsernameValid, | ||
11 | isUserDisplayNSFWValid | ||
12 | } from '../../helpers' | ||
13 | |||
14 | import { addMethodsToModel } from '../utils' | ||
15 | import { | ||
16 | UserClass, | ||
17 | UserInstance, | ||
18 | UserAttributes, | ||
19 | |||
20 | UserMethods | ||
21 | } from './user-interface' | ||
22 | |||
23 | let User: Sequelize.Model<UserInstance, UserAttributes> | ||
24 | let isPasswordMatch: UserMethods.IsPasswordMatch | ||
25 | let toFormatedJSON: UserMethods.ToFormatedJSON | ||
26 | let isAdmin: UserMethods.IsAdmin | ||
27 | let countTotal: UserMethods.CountTotal | ||
28 | let getByUsername: UserMethods.GetByUsername | ||
29 | let list: UserMethods.List | ||
30 | let listForApi: UserMethods.ListForApi | ||
31 | let loadById: UserMethods.LoadById | ||
32 | let loadByUsername: UserMethods.LoadByUsername | ||
33 | let loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail | ||
34 | |||
35 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | ||
36 | User = sequelize.define<UserInstance, UserAttributes>('User', | ||
37 | { | ||
38 | password: { | ||
39 | type: DataTypes.STRING, | ||
40 | allowNull: false, | ||
41 | validate: { | ||
42 | passwordValid: function (value) { | ||
43 | const res = isUserPasswordValid(value) | ||
44 | if (res === false) throw new Error('Password not valid.') | ||
45 | } | ||
46 | } | ||
47 | }, | ||
48 | username: { | ||
49 | type: DataTypes.STRING, | ||
50 | allowNull: false, | ||
51 | validate: { | ||
52 | usernameValid: function (value) { | ||
53 | const res = isUserUsernameValid(value) | ||
54 | if (res === false) throw new Error('Username not valid.') | ||
55 | } | ||
56 | } | ||
57 | }, | ||
58 | email: { | ||
59 | type: DataTypes.STRING(400), | ||
60 | allowNull: false, | ||
61 | validate: { | ||
62 | isEmail: true | ||
63 | } | ||
64 | }, | ||
65 | displayNSFW: { | ||
66 | type: DataTypes.BOOLEAN, | ||
67 | allowNull: false, | ||
68 | defaultValue: false, | ||
69 | validate: { | ||
70 | nsfwValid: function (value) { | ||
71 | const res = isUserDisplayNSFWValid(value) | ||
72 | if (res === false) throw new Error('Display NSFW is not valid.') | ||
73 | } | ||
74 | } | ||
75 | }, | ||
76 | role: { | ||
77 | type: DataTypes.ENUM(values(USER_ROLES)), | ||
78 | allowNull: false | ||
79 | } | ||
80 | }, | ||
81 | { | ||
82 | indexes: [ | ||
83 | { | ||
84 | fields: [ 'username' ], | ||
85 | unique: true | ||
86 | }, | ||
87 | { | ||
88 | fields: [ 'email' ], | ||
89 | unique: true | ||
90 | } | ||
91 | ], | ||
92 | hooks: { | ||
93 | beforeCreate: beforeCreateOrUpdate, | ||
94 | beforeUpdate: beforeCreateOrUpdate | ||
95 | } | ||
96 | } | ||
97 | ) | ||
98 | |||
99 | const classMethods = [ | ||
100 | associate, | ||
101 | |||
102 | countTotal, | ||
103 | getByUsername, | ||
104 | list, | ||
105 | listForApi, | ||
106 | loadById, | ||
107 | loadByUsername, | ||
108 | loadByUsernameOrEmail | ||
109 | ] | ||
110 | const instanceMethods = [ | ||
111 | isPasswordMatch, | ||
112 | toFormatedJSON, | ||
113 | isAdmin | ||
114 | ] | ||
115 | addMethodsToModel(User, classMethods, instanceMethods) | ||
116 | |||
117 | return User | ||
118 | } | ||
119 | |||
120 | function beforeCreateOrUpdate (user: UserInstance) { | ||
121 | return new Promise(function (resolve, reject) { | ||
122 | cryptPassword(user.password, function (err, hash) { | ||
123 | if (err) return reject(err) | ||
124 | |||
125 | user.password = hash | ||
126 | |||
127 | return resolve() | ||
128 | }) | ||
129 | }) | ||
130 | } | ||
131 | |||
132 | // ------------------------------ METHODS ------------------------------ | ||
133 | |||
134 | isPasswordMatch = function (password: string, callback: UserMethods.IsPasswordMatchCallback) { | ||
135 | return comparePassword(password, this.password, callback) | ||
136 | } | ||
137 | |||
138 | toFormatedJSON = function (this: UserInstance) { | ||
139 | return { | ||
140 | id: this.id, | ||
141 | username: this.username, | ||
142 | email: this.email, | ||
143 | displayNSFW: this.displayNSFW, | ||
144 | role: this.role, | ||
145 | createdAt: this.createdAt | ||
146 | } | ||
147 | } | ||
148 | |||
149 | isAdmin = function () { | ||
150 | return this.role === USER_ROLES.ADMIN | ||
151 | } | ||
152 | |||
153 | // ------------------------------ STATICS ------------------------------ | ||
154 | |||
155 | function associate (models) { | ||
156 | User.hasOne(models.Author, { | ||
157 | foreignKey: 'userId', | ||
158 | onDelete: 'cascade' | ||
159 | }) | ||
160 | |||
161 | User.hasMany(models.OAuthToken, { | ||
162 | foreignKey: 'userId', | ||
163 | onDelete: 'cascade' | ||
164 | }) | ||
165 | } | ||
166 | |||
167 | countTotal = function (callback: UserMethods.CountTotalCallback) { | ||
168 | return this.count().asCallback(callback) | ||
169 | } | ||
170 | |||
171 | getByUsername = function (username: string) { | ||
172 | const query = { | ||
173 | where: { | ||
174 | username: username | ||
175 | } | ||
176 | } | ||
177 | |||
178 | return User.findOne(query) | ||
179 | } | ||
180 | |||
181 | list = function (callback: UserMethods.ListCallback) { | ||
182 | return User.find().asCallback(callback) | ||
183 | } | ||
184 | |||
185 | listForApi = function (start: number, count: number, sort: string, callback: UserMethods.ListForApiCallback) { | ||
186 | const query = { | ||
187 | offset: start, | ||
188 | limit: count, | ||
189 | order: [ getSort(sort) ] | ||
190 | } | ||
191 | |||
192 | return User.findAndCountAll(query).asCallback(function (err, result) { | ||
193 | if (err) return callback(err) | ||
194 | |||
195 | return callback(null, result.rows, result.count) | ||
196 | }) | ||
197 | } | ||
198 | |||
199 | loadById = function (id: number, callback: UserMethods.LoadByIdCallback) { | ||
200 | return User.findById(id).asCallback(callback) | ||
201 | } | ||
202 | |||
203 | loadByUsername = function (username: string, callback: UserMethods.LoadByUsernameCallback) { | ||
204 | const query = { | ||
205 | where: { | ||
206 | username: username | ||
207 | } | ||
208 | } | ||
209 | |||
210 | return User.findOne(query).asCallback(callback) | ||
211 | } | ||
212 | |||
213 | loadByUsernameOrEmail = function (username: string, email: string, callback: UserMethods.LoadByUsernameOrEmailCallback) { | ||
214 | const query = { | ||
215 | where: { | ||
216 | $or: [ { username }, { email } ] | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return User.findOne(query).asCallback(callback) | ||
221 | } | ||