user-registration.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import { FindOptions, Op, WhereOptions } from 'sequelize'
  2. import {
  3. AllowNull,
  4. BeforeCreate,
  5. BelongsTo,
  6. Column,
  7. CreatedAt,
  8. DataType,
  9. ForeignKey,
  10. Is,
  11. IsEmail,
  12. Model,
  13. Table,
  14. UpdatedAt
  15. } from 'sequelize-typescript'
  16. import {
  17. isRegistrationModerationResponseValid,
  18. isRegistrationReasonValid,
  19. isRegistrationStateValid
  20. } from '@server/helpers/custom-validators/user-registration'
  21. import { isVideoChannelDisplayNameValid } from '@server/helpers/custom-validators/video-channels'
  22. import { cryptPassword } from '@server/helpers/peertube-crypto'
  23. import { USER_REGISTRATION_STATES } from '@server/initializers/constants'
  24. import { MRegistration, MRegistrationFormattable } from '@server/types/models'
  25. import { UserRegistration, UserRegistrationState } from '@shared/models'
  26. import { AttributesOnly } from '@shared/typescript-utils'
  27. import { isUserDisplayNameValid, isUserEmailVerifiedValid, isUserPasswordValid } from '../../helpers/custom-validators/users'
  28. import { getSort, throwIfNotValid } from '../shared'
  29. import { UserModel } from './user'
  30. @Table({
  31. tableName: 'userRegistration',
  32. indexes: [
  33. {
  34. fields: [ 'username' ],
  35. unique: true
  36. },
  37. {
  38. fields: [ 'email' ],
  39. unique: true
  40. },
  41. {
  42. fields: [ 'channelHandle' ],
  43. unique: true
  44. },
  45. {
  46. fields: [ 'userId' ],
  47. unique: true
  48. }
  49. ]
  50. })
  51. export class UserRegistrationModel extends Model<Partial<AttributesOnly<UserRegistrationModel>>> {
  52. @AllowNull(false)
  53. @Is('RegistrationState', value => throwIfNotValid(value, isRegistrationStateValid, 'state'))
  54. @Column
  55. state: UserRegistrationState
  56. @AllowNull(false)
  57. @Is('RegistrationReason', value => throwIfNotValid(value, isRegistrationReasonValid, 'registration reason'))
  58. @Column(DataType.TEXT)
  59. registrationReason: string
  60. @AllowNull(true)
  61. @Is('RegistrationModerationResponse', value => throwIfNotValid(value, isRegistrationModerationResponseValid, 'moderation response', true))
  62. @Column(DataType.TEXT)
  63. moderationResponse: string
  64. @AllowNull(true)
  65. @Is('RegistrationPassword', value => throwIfNotValid(value, isUserPasswordValid, 'registration password', true))
  66. @Column
  67. password: string
  68. @AllowNull(false)
  69. @Column
  70. username: string
  71. @AllowNull(false)
  72. @IsEmail
  73. @Column(DataType.STRING(400))
  74. email: string
  75. @AllowNull(true)
  76. @Is('RegistrationEmailVerified', value => throwIfNotValid(value, isUserEmailVerifiedValid, 'email verified boolean', true))
  77. @Column
  78. emailVerified: boolean
  79. @AllowNull(true)
  80. @Is('RegistrationAccountDisplayName', value => throwIfNotValid(value, isUserDisplayNameValid, 'account display name', true))
  81. @Column
  82. accountDisplayName: string
  83. @AllowNull(true)
  84. @Is('ChannelHandle', value => throwIfNotValid(value, isVideoChannelDisplayNameValid, 'channel handle', true))
  85. @Column
  86. channelHandle: string
  87. @AllowNull(true)
  88. @Is('ChannelDisplayName', value => throwIfNotValid(value, isVideoChannelDisplayNameValid, 'channel display name', true))
  89. @Column
  90. channelDisplayName: string
  91. @CreatedAt
  92. createdAt: Date
  93. @UpdatedAt
  94. updatedAt: Date
  95. @ForeignKey(() => UserModel)
  96. @Column
  97. userId: number
  98. @BelongsTo(() => UserModel, {
  99. foreignKey: {
  100. allowNull: true
  101. },
  102. onDelete: 'SET NULL'
  103. })
  104. User: UserModel
  105. @BeforeCreate
  106. static async cryptPasswordIfNeeded (instance: UserRegistrationModel) {
  107. instance.password = await cryptPassword(instance.password)
  108. }
  109. static load (id: number): Promise<MRegistration> {
  110. return UserRegistrationModel.findByPk(id)
  111. }
  112. static loadByEmail (email: string): Promise<MRegistration> {
  113. const query = {
  114. where: { email }
  115. }
  116. return UserRegistrationModel.findOne(query)
  117. }
  118. static loadByEmailOrUsername (emailOrUsername: string): Promise<MRegistration> {
  119. const query = {
  120. where: {
  121. [Op.or]: [
  122. { email: emailOrUsername },
  123. { username: emailOrUsername }
  124. ]
  125. }
  126. }
  127. return UserRegistrationModel.findOne(query)
  128. }
  129. static loadByEmailOrHandle (options: {
  130. email: string
  131. username: string
  132. channelHandle?: string
  133. }): Promise<MRegistration> {
  134. const { email, username, channelHandle } = options
  135. let or: WhereOptions = [
  136. { email },
  137. { channelHandle: username },
  138. { username }
  139. ]
  140. if (channelHandle) {
  141. or = or.concat([
  142. { username: channelHandle },
  143. { channelHandle }
  144. ])
  145. }
  146. const query = {
  147. where: {
  148. [Op.or]: or
  149. }
  150. }
  151. return UserRegistrationModel.findOne(query)
  152. }
  153. // ---------------------------------------------------------------------------
  154. static listForApi (options: {
  155. start: number
  156. count: number
  157. sort: string
  158. search?: string
  159. }) {
  160. const { start, count, sort, search } = options
  161. const where: WhereOptions = {}
  162. if (search) {
  163. Object.assign(where, {
  164. [Op.or]: [
  165. {
  166. email: {
  167. [Op.iLike]: '%' + search + '%'
  168. }
  169. },
  170. {
  171. username: {
  172. [Op.iLike]: '%' + search + '%'
  173. }
  174. }
  175. ]
  176. })
  177. }
  178. const query: FindOptions = {
  179. offset: start,
  180. limit: count,
  181. order: getSort(sort),
  182. where,
  183. include: [
  184. {
  185. model: UserModel.unscoped(),
  186. required: false
  187. }
  188. ]
  189. }
  190. return Promise.all([
  191. UserRegistrationModel.count(query),
  192. UserRegistrationModel.findAll<MRegistrationFormattable>(query)
  193. ]).then(([ total, data ]) => ({ total, data }))
  194. }
  195. // ---------------------------------------------------------------------------
  196. toFormattedJSON (this: MRegistrationFormattable): UserRegistration {
  197. return {
  198. id: this.id,
  199. state: {
  200. id: this.state,
  201. label: USER_REGISTRATION_STATES[this.state]
  202. },
  203. registrationReason: this.registrationReason,
  204. moderationResponse: this.moderationResponse,
  205. username: this.username,
  206. email: this.email,
  207. emailVerified: this.emailVerified,
  208. accountDisplayName: this.accountDisplayName,
  209. channelHandle: this.channelHandle,
  210. channelDisplayName: this.channelDisplayName,
  211. createdAt: this.createdAt,
  212. updatedAt: this.updatedAt,
  213. user: this.User
  214. ? { id: this.User.id }
  215. : null
  216. }
  217. }
  218. }