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