user.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import { Transaction } from 'sequelize/types'
  2. import { v4 as uuidv4 } from 'uuid'
  3. import { UserModel } from '@server/models/account/user'
  4. import { ActivityPubActorType } from '../../shared/models/activitypub'
  5. import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
  6. import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants'
  7. import { sequelizeTypescript } from '../initializers/database'
  8. import { AccountModel } from '../models/account/account'
  9. import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
  10. import { ActorModel } from '../models/activitypub/actor'
  11. import { MAccountDefault, MActorDefault, MChannelActor } from '../types/models'
  12. import { MUser, MUserDefault, MUserId } from '../types/models/user'
  13. import { buildActorInstance, setAsyncActorKeys } from './activitypub/actor'
  14. import { getLocalAccountActivityPubUrl } from './activitypub/url'
  15. import { Emailer } from './emailer'
  16. import { LiveManager } from './live-manager'
  17. import { Redis } from './redis'
  18. import { createLocalVideoChannel } from './video-channel'
  19. import { createWatchLaterPlaylist } from './video-playlist'
  20. type ChannelNames = { name: string, displayName: string }
  21. async function createUserAccountAndChannelAndPlaylist (parameters: {
  22. userToCreate: MUser
  23. userDisplayName?: string
  24. channelNames?: ChannelNames
  25. validateUser?: boolean
  26. }): Promise<{ user: MUserDefault, account: MAccountDefault, videoChannel: MChannelActor }> {
  27. const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
  28. const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
  29. const userOptions = {
  30. transaction: t,
  31. validate: validateUser
  32. }
  33. const userCreated: MUserDefault = await userToCreate.save(userOptions)
  34. userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
  35. const accountCreated = await createLocalAccountWithoutKeys({
  36. name: userCreated.username,
  37. displayName: userDisplayName,
  38. userId: userCreated.id,
  39. applicationId: null,
  40. t: t
  41. })
  42. userCreated.Account = accountCreated
  43. const channelAttributes = await buildChannelAttributes(userCreated, channelNames)
  44. const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t)
  45. const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t)
  46. return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
  47. })
  48. const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([
  49. setAsyncActorKeys(account.Actor),
  50. setAsyncActorKeys(videoChannel.Actor)
  51. ])
  52. account.Actor = accountActorWithKeys
  53. videoChannel.Actor = channelActorWithKeys
  54. return { user, account, videoChannel }
  55. }
  56. async function createLocalAccountWithoutKeys (parameters: {
  57. name: string
  58. displayName?: string
  59. userId: number | null
  60. applicationId: number | null
  61. t: Transaction | undefined
  62. type?: ActivityPubActorType
  63. }) {
  64. const { name, displayName, userId, applicationId, t, type = 'Person' } = parameters
  65. const url = getLocalAccountActivityPubUrl(name)
  66. const actorInstance = buildActorInstance(type, url, name)
  67. const actorInstanceCreated: MActorDefault = await actorInstance.save({ transaction: t })
  68. const accountInstance = new AccountModel({
  69. name: displayName || name,
  70. userId,
  71. applicationId,
  72. actorId: actorInstanceCreated.id
  73. })
  74. const accountInstanceCreated: MAccountDefault = await accountInstance.save({ transaction: t })
  75. accountInstanceCreated.Actor = actorInstanceCreated
  76. return accountInstanceCreated
  77. }
  78. async function createApplicationActor (applicationId: number) {
  79. const accountCreated = await createLocalAccountWithoutKeys({
  80. name: SERVER_ACTOR_NAME,
  81. userId: null,
  82. applicationId: applicationId,
  83. t: undefined,
  84. type: 'Application'
  85. })
  86. accountCreated.Actor = await setAsyncActorKeys(accountCreated.Actor)
  87. return accountCreated
  88. }
  89. async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
  90. const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id)
  91. let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString
  92. if (isPendingEmail) url += '&isPendingEmail=true'
  93. const email = isPendingEmail ? user.pendingEmail : user.email
  94. const username = user.username
  95. await Emailer.Instance.addVerifyEmailJob(username, email, url)
  96. }
  97. async function getOriginalVideoFileTotalFromUser (user: MUserId) {
  98. // Don't use sequelize because we need to use a sub query
  99. const query = UserModel.generateUserQuotaBaseSQL({
  100. withSelect: true,
  101. whereUserId: '$userId'
  102. })
  103. const base = await UserModel.getTotalRawQuery(query, user.id)
  104. return base + LiveManager.Instance.getLiveQuotaUsedByUser(user.id)
  105. }
  106. // Returns cumulative size of all video files uploaded in the last 24 hours.
  107. async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
  108. // Don't use sequelize because we need to use a sub query
  109. const query = UserModel.generateUserQuotaBaseSQL({
  110. withSelect: true,
  111. whereUserId: '$userId',
  112. where: '"video"."createdAt" > now() - interval \'24 hours\''
  113. })
  114. const base = await UserModel.getTotalRawQuery(query, user.id)
  115. return base + LiveManager.Instance.getLiveQuotaUsedByUser(user.id)
  116. }
  117. async function isAbleToUploadVideo (userId: number, size: number) {
  118. const user = await UserModel.loadById(userId)
  119. if (user.videoQuota === -1 && user.videoQuotaDaily === -1) return Promise.resolve(true)
  120. const [ totalBytes, totalBytesDaily ] = await Promise.all([
  121. getOriginalVideoFileTotalFromUser(user),
  122. getOriginalVideoFileTotalDailyFromUser(user)
  123. ])
  124. const uploadedTotal = size + totalBytes
  125. const uploadedDaily = size + totalBytesDaily
  126. if (user.videoQuotaDaily === -1) return uploadedTotal < user.videoQuota
  127. if (user.videoQuota === -1) return uploadedDaily < user.videoQuotaDaily
  128. return uploadedTotal < user.videoQuota && uploadedDaily < user.videoQuotaDaily
  129. }
  130. // ---------------------------------------------------------------------------
  131. export {
  132. getOriginalVideoFileTotalFromUser,
  133. getOriginalVideoFileTotalDailyFromUser,
  134. createApplicationActor,
  135. createUserAccountAndChannelAndPlaylist,
  136. createLocalAccountWithoutKeys,
  137. sendVerifyUserEmail,
  138. isAbleToUploadVideo
  139. }
  140. // ---------------------------------------------------------------------------
  141. function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) {
  142. const values: UserNotificationSetting & { userId: number } = {
  143. userId: user.id,
  144. newVideoFromSubscription: UserNotificationSettingValue.WEB,
  145. newCommentOnMyVideo: UserNotificationSettingValue.WEB,
  146. myVideoImportFinished: UserNotificationSettingValue.WEB,
  147. myVideoPublished: UserNotificationSettingValue.WEB,
  148. abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  149. videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  150. blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  151. newUserRegistration: UserNotificationSettingValue.WEB,
  152. commentMention: UserNotificationSettingValue.WEB,
  153. newFollow: UserNotificationSettingValue.WEB,
  154. newInstanceFollower: UserNotificationSettingValue.WEB,
  155. abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  156. abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  157. autoInstanceFollowing: UserNotificationSettingValue.WEB
  158. }
  159. return UserNotificationSettingModel.create(values, { transaction: t })
  160. }
  161. async function buildChannelAttributes (user: MUser, channelNames?: ChannelNames) {
  162. if (channelNames) return channelNames
  163. let channelName = user.username + '_channel'
  164. // Conflict, generate uuid instead
  165. const actor = await ActorModel.loadLocalByName(channelName)
  166. if (actor) channelName = uuidv4()
  167. const videoChannelDisplayName = `Main ${user.username} channel`
  168. return {
  169. name: channelName,
  170. displayName: videoChannelDisplayName
  171. }
  172. }