user.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { Transaction } from 'sequelize'
  2. import {
  3. ActivityPubActorType,
  4. UserAdminFlag,
  5. UserAdminFlagType,
  6. UserNotificationSetting,
  7. UserNotificationSettingValue,
  8. UserRole,
  9. UserRoleType
  10. } from '@peertube/peertube-models'
  11. import { logger } from '@server/helpers/logger.js'
  12. import { CONFIG } from '@server/initializers/config.js'
  13. import { UserModel } from '@server/models/user/user.js'
  14. import { MActorDefault } from '@server/types/models/actor/index.js'
  15. import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants.js'
  16. import { sequelizeTypescript } from '../initializers/database.js'
  17. import { AccountModel } from '../models/account/account.js'
  18. import { UserNotificationSettingModel } from '../models/user/user-notification-setting.js'
  19. import { MAccountDefault, MChannelActor } from '../types/models/index.js'
  20. import { MRegistration, MUser, MUserDefault, MUserId } from '../types/models/user/index.js'
  21. import { generateAndSaveActorKeys } from './activitypub/actors/index.js'
  22. import { getLocalAccountActivityPubUrl } from './activitypub/url.js'
  23. import { Emailer } from './emailer.js'
  24. import { LiveQuotaStore } from './live/live-quota-store.js'
  25. import { buildActorInstance, findAvailableLocalActorName } from './local-actor.js'
  26. import { Redis } from './redis.js'
  27. import { createLocalVideoChannelWithoutKeys } from './video-channel.js'
  28. import { createWatchLaterPlaylist } from './video-playlist.js'
  29. type ChannelNames = { name: string, displayName: string }
  30. function buildUser (options: {
  31. username: string
  32. password: string
  33. email: string
  34. role?: UserRoleType // Default to UserRole.User
  35. adminFlags?: UserAdminFlagType // Default to UserAdminFlag.NONE
  36. emailVerified: boolean | null
  37. videoQuota?: number // Default to CONFIG.USER.VIDEO_QUOTA
  38. videoQuotaDaily?: number // Default to CONFIG.USER.VIDEO_QUOTA_DAILY
  39. pluginAuth?: string
  40. }): MUser {
  41. const {
  42. username,
  43. password,
  44. email,
  45. role = UserRole.USER,
  46. emailVerified,
  47. videoQuota = CONFIG.USER.VIDEO_QUOTA,
  48. videoQuotaDaily = CONFIG.USER.VIDEO_QUOTA_DAILY,
  49. adminFlags = UserAdminFlag.NONE,
  50. pluginAuth
  51. } = options
  52. return new UserModel({
  53. username,
  54. password,
  55. email,
  56. nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
  57. p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
  58. videosHistoryEnabled: CONFIG.USER.HISTORY.VIDEOS.ENABLED,
  59. autoPlayVideo: true,
  60. role,
  61. emailVerified,
  62. adminFlags,
  63. videoQuota,
  64. videoQuotaDaily,
  65. pluginAuth
  66. })
  67. }
  68. // ---------------------------------------------------------------------------
  69. async function createUserAccountAndChannelAndPlaylist (parameters: {
  70. userToCreate: MUser
  71. userDisplayName?: string
  72. channelNames?: ChannelNames
  73. validateUser?: boolean
  74. }): Promise<{ user: MUserDefault, account: MAccountDefault, videoChannel: MChannelActor }> {
  75. const { userToCreate, userDisplayName, channelNames, validateUser = true } = parameters
  76. const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
  77. const userOptions = {
  78. transaction: t,
  79. validate: validateUser
  80. }
  81. const userCreated: MUserDefault = await userToCreate.save(userOptions)
  82. userCreated.NotificationSetting = await createDefaultUserNotificationSettings(userCreated, t)
  83. const accountCreated = await createLocalAccountWithoutKeys({
  84. name: userCreated.username,
  85. displayName: userDisplayName,
  86. userId: userCreated.id,
  87. applicationId: null,
  88. t
  89. })
  90. userCreated.Account = accountCreated
  91. const channelAttributes = await buildChannelAttributes({ user: userCreated, transaction: t, channelNames })
  92. const videoChannel = await createLocalVideoChannelWithoutKeys(channelAttributes, accountCreated, t)
  93. const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t)
  94. return { user: userCreated, account: accountCreated, videoChannel, videoPlaylist }
  95. })
  96. const [ accountActorWithKeys, channelActorWithKeys ] = await Promise.all([
  97. generateAndSaveActorKeys(account.Actor),
  98. generateAndSaveActorKeys(videoChannel.Actor)
  99. ])
  100. account.Actor = accountActorWithKeys
  101. videoChannel.Actor = channelActorWithKeys
  102. return { user, account, videoChannel }
  103. }
  104. async function createLocalAccountWithoutKeys (parameters: {
  105. name: string
  106. displayName?: string
  107. userId: number | null
  108. applicationId: number | null
  109. t: Transaction | undefined
  110. type?: ActivityPubActorType
  111. }) {
  112. const { name, displayName, userId, applicationId, t, type = 'Person' } = parameters
  113. const url = getLocalAccountActivityPubUrl(name)
  114. const actorInstance = buildActorInstance(type, url, name)
  115. const actorInstanceCreated: MActorDefault = await actorInstance.save({ transaction: t })
  116. const accountInstance = new AccountModel({
  117. name: displayName || name,
  118. userId,
  119. applicationId,
  120. actorId: actorInstanceCreated.id
  121. })
  122. const accountInstanceCreated: MAccountDefault = await accountInstance.save({ transaction: t })
  123. accountInstanceCreated.Actor = actorInstanceCreated
  124. return accountInstanceCreated
  125. }
  126. async function createApplicationActor (applicationId: number) {
  127. const accountCreated = await createLocalAccountWithoutKeys({
  128. name: SERVER_ACTOR_NAME,
  129. userId: null,
  130. applicationId,
  131. t: undefined,
  132. type: 'Application'
  133. })
  134. accountCreated.Actor = await generateAndSaveActorKeys(accountCreated.Actor)
  135. return accountCreated
  136. }
  137. // ---------------------------------------------------------------------------
  138. async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
  139. const verificationString = await Redis.Instance.setUserVerifyEmailVerificationString(user.id)
  140. let verifyEmailUrl = `${WEBSERVER.URL}/verify-account/email?userId=${user.id}&verificationString=${verificationString}`
  141. if (isPendingEmail) verifyEmailUrl += '&isPendingEmail=true'
  142. const to = isPendingEmail
  143. ? user.pendingEmail
  144. : user.email
  145. const username = user.username
  146. Emailer.Instance.addVerifyEmailJob({ username, to, verifyEmailUrl, isRegistrationRequest: false })
  147. }
  148. async function sendVerifyRegistrationEmail (registration: MRegistration) {
  149. const verificationString = await Redis.Instance.setRegistrationVerifyEmailVerificationString(registration.id)
  150. const verifyEmailUrl = `${WEBSERVER.URL}/verify-account/email?registrationId=${registration.id}&verificationString=${verificationString}`
  151. const to = registration.email
  152. const username = registration.username
  153. Emailer.Instance.addVerifyEmailJob({ username, to, verifyEmailUrl, isRegistrationRequest: true })
  154. }
  155. // ---------------------------------------------------------------------------
  156. async function getOriginalVideoFileTotalFromUser (user: MUserId) {
  157. const base = await UserModel.getUserQuota({ userId: user.id, daily: false })
  158. return base + LiveQuotaStore.Instance.getLiveQuotaOfUser(user.id)
  159. }
  160. // Returns cumulative size of all video files uploaded in the last 24 hours.
  161. async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
  162. const base = await UserModel.getUserQuota({ userId: user.id, daily: true })
  163. return base + LiveQuotaStore.Instance.getLiveQuotaOfUser(user.id)
  164. }
  165. async function isUserQuotaValid (options: {
  166. userId: number
  167. uploadSize: number
  168. checkDaily?: boolean // default true
  169. }) {
  170. const { userId, uploadSize, checkDaily = true } = options
  171. const user = await UserModel.loadById(userId)
  172. if (user.videoQuota === -1 && user.videoQuotaDaily === -1) return Promise.resolve(true)
  173. const [ totalBytes, totalBytesDaily ] = await Promise.all([
  174. getOriginalVideoFileTotalFromUser(user),
  175. getOriginalVideoFileTotalDailyFromUser(user)
  176. ])
  177. const uploadedTotal = uploadSize + totalBytes
  178. const uploadedDaily = uploadSize + totalBytesDaily
  179. logger.debug(
  180. 'Check user %d quota to upload content.', userId,
  181. { totalBytes, totalBytesDaily, videoQuota: user.videoQuota, videoQuotaDaily: user.videoQuotaDaily, uploadSize }
  182. )
  183. if (checkDaily && user.videoQuotaDaily !== -1 && uploadedDaily >= user.videoQuotaDaily) return false
  184. if (user.videoQuota !== -1 && uploadedTotal >= user.videoQuota) return false
  185. return true
  186. }
  187. // ---------------------------------------------------------------------------
  188. export {
  189. getOriginalVideoFileTotalFromUser,
  190. getOriginalVideoFileTotalDailyFromUser,
  191. createApplicationActor,
  192. createUserAccountAndChannelAndPlaylist,
  193. createLocalAccountWithoutKeys,
  194. sendVerifyUserEmail,
  195. sendVerifyRegistrationEmail,
  196. isUserQuotaValid,
  197. buildUser
  198. }
  199. // ---------------------------------------------------------------------------
  200. function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | undefined) {
  201. const values: UserNotificationSetting & { userId: number } = {
  202. userId: user.id,
  203. newVideoFromSubscription: UserNotificationSettingValue.WEB,
  204. newCommentOnMyVideo: UserNotificationSettingValue.WEB,
  205. myVideoImportFinished: UserNotificationSettingValue.WEB,
  206. myVideoPublished: UserNotificationSettingValue.WEB,
  207. abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  208. videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  209. blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  210. newUserRegistration: UserNotificationSettingValue.WEB,
  211. commentMention: UserNotificationSettingValue.WEB,
  212. newFollow: UserNotificationSettingValue.WEB,
  213. newInstanceFollower: UserNotificationSettingValue.WEB,
  214. abuseNewMessage: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  215. abuseStateChange: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  216. autoInstanceFollowing: UserNotificationSettingValue.WEB,
  217. newPeerTubeVersion: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL,
  218. newPluginVersion: UserNotificationSettingValue.WEB,
  219. myVideoStudioEditionFinished: UserNotificationSettingValue.WEB
  220. }
  221. return UserNotificationSettingModel.create(values, { transaction: t })
  222. }
  223. async function buildChannelAttributes (options: {
  224. user: MUser
  225. transaction?: Transaction
  226. channelNames?: ChannelNames
  227. }) {
  228. const { user, transaction, channelNames } = options
  229. if (channelNames) return channelNames
  230. const channelName = await findAvailableLocalActorName(user.username + '_channel', transaction)
  231. const videoChannelDisplayName = CONFIG.USER.DEFAULT_CHANNEL_NAME.replace('$1', user.username)
  232. return {
  233. name: channelName,
  234. displayName: videoChannelDisplayName
  235. }
  236. }