]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Refactor user build and express file middlewares
authorChocobozzz <me@florianbigard.com>
Fri, 4 Mar 2022 09:57:36 +0000 (10:57 +0100)
committerChocobozzz <me@florianbigard.com>
Fri, 4 Mar 2022 09:57:36 +0000 (10:57 +0100)
14 files changed:
server/controllers/api/users/index.ts
server/controllers/api/users/me.ts
server/controllers/api/video-channel.ts
server/controllers/api/video-playlist.ts
server/controllers/api/videos/captions.ts
server/controllers/api/videos/editor.ts
server/controllers/api/videos/import.ts
server/controllers/api/videos/live.ts
server/controllers/api/videos/update.ts
server/controllers/api/videos/upload.ts
server/helpers/express-utils.ts
server/initializers/installer.ts
server/lib/auth/oauth-model.ts
server/lib/user.ts

index 7efc3a13773dfc47165a1fb58b9378babe253877..8a06bfe93234956dec6198403fb788a3a5263510 100644 (file)
@@ -3,8 +3,9 @@ import RateLimit from 'express-rate-limit'
 import { tokensRouter } from '@server/controllers/api/users/token'
 import { Hooks } from '@server/lib/plugins/hooks'
 import { OAuthTokenModel } from '@server/models/oauth/oauth-token'
-import { MUser, MUserAccountDefault } from '@server/types/models'
-import { HttpStatusCode, UserAdminFlag, UserCreate, UserCreateResult, UserRegister, UserRight, UserRole, UserUpdate } from '@shared/models'
+import { MUserAccountDefault } from '@server/types/models'
+import { pick } from '@shared/core-utils'
+import { HttpStatusCode, UserCreate, UserCreateResult, UserRegister, UserRight, UserUpdate } from '@shared/models'
 import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger'
 import { logger } from '../../../helpers/logger'
 import { generateRandomString, getFormattedObjects } from '../../../helpers/utils'
@@ -14,7 +15,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
 import { Emailer } from '../../../lib/emailer'
 import { Notifier } from '../../../lib/notifier'
 import { Redis } from '../../../lib/redis'
-import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user'
+import { buildUser, createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
@@ -175,18 +176,11 @@ export {
 async function createUser (req: express.Request, res: express.Response) {
   const body: UserCreate = req.body
 
-  const userToCreate = new UserModel({
-    username: body.username,
-    password: body.password,
-    email: body.email,
-    nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
-    p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
-    autoPlayVideo: true,
-    role: body.role,
-    videoQuota: body.videoQuota,
-    videoQuotaDaily: body.videoQuotaDaily,
-    adminFlags: body.adminFlags || UserAdminFlag.NONE
-  }) as MUser
+  const userToCreate = buildUser({
+    ...pick(body, [ 'username', 'password', 'email', 'role', 'videoQuota', 'videoQuotaDaily', 'adminFlags' ]),
+
+    emailVerified: null
+  })
 
   // NB: due to the validator usersAddValidator, password==='' can only be true if we can send the mail.
   const createPassword = userToCreate.password === ''
@@ -225,16 +219,9 @@ async function createUser (req: express.Request, res: express.Response) {
 async function registerUser (req: express.Request, res: express.Response) {
   const body: UserRegister = req.body
 
-  const userToCreate = new UserModel({
-    username: body.username,
-    password: body.password,
-    email: body.email,
-    nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
-    p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
-    autoPlayVideo: true,
-    role: UserRole.USER,
-    videoQuota: CONFIG.USER.VIDEO_QUOTA,
-    videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
+  const userToCreate = buildUser({
+    ...pick(body, [ 'username', 'password', 'email' ]),
+
     emailVerified: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION ? false : null
   })
 
index a1d6211525799324312d88a1cbb8c9ea40ab3a57..595abcf959bd9ca4dcda8309cac27c3967d96161 100644 (file)
@@ -35,7 +35,7 @@ import { VideoImportModel } from '../../../models/video/video-import'
 
 const auditLogger = auditLoggerFactory('users')
 
-const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR })
+const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
 
 const meRouter = express.Router()
 
index 2f869d9b3d84e3cf1b47dec08e14282c71846b51..2454b1ec9342cb945fb4742588deb763f5e5b9fd 100644 (file)
@@ -12,7 +12,6 @@ import { resetSequelizeInstance } from '../../helpers/database-utils'
 import { buildNSFWFilter, createReqFiles, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
 import { logger } from '../../helpers/logger'
 import { getFormattedObjects } from '../../helpers/utils'
-import { CONFIG } from '../../initializers/config'
 import { MIMETYPES } from '../../initializers/constants'
 import { sequelizeTypescript } from '../../initializers/database'
 import { sendUpdateActor } from '../../lib/activitypub/send'
@@ -51,8 +50,8 @@ import { VideoChannelModel } from '../../models/video/video-channel'
 import { VideoPlaylistModel } from '../../models/video/video-playlist'
 
 const auditLogger = auditLoggerFactory('channels')
-const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR })
-const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { bannerfile: CONFIG.STORAGE.TMP_DIR })
+const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
+const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
 
 const videoChannelRouter = express.Router()
 
index ee6c738557bc03c072cdfaa1e81c1c85ddf79619..1255d14c67c80aa71ea74679228a81b6d6d9a1af 100644 (file)
@@ -47,7 +47,7 @@ import { AccountModel } from '../../models/account/account'
 import { VideoPlaylistModel } from '../../models/video/video-playlist'
 import { VideoPlaylistElementModel } from '../../models/video/video-playlist-element'
 
-const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR })
+const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
 
 const videoPlaylistRouter = express.Router()
 
index 2a9a9d233a73b1dcb45b73d357349d7e2b7c36e2..2b511a39834c8380b59d043f9b2a2ad9b087f59a 100644 (file)
@@ -1,26 +1,19 @@
 import express from 'express'
+import { Hooks } from '@server/lib/plugins/hooks'
 import { MVideoCaption } from '@server/types/models'
 import { HttpStatusCode } from '../../../../shared/models/http/http-error-codes'
 import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
 import { createReqFiles } from '../../../helpers/express-utils'
 import { logger } from '../../../helpers/logger'
 import { getFormattedObjects } from '../../../helpers/utils'
-import { CONFIG } from '../../../initializers/config'
 import { MIMETYPES } from '../../../initializers/constants'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { federateVideoIfNeeded } from '../../../lib/activitypub/videos'
 import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares'
 import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators'
 import { VideoCaptionModel } from '../../../models/video/video-caption'
-import { Hooks } from '@server/lib/plugins/hooks'
 
-const reqVideoCaptionAdd = createReqFiles(
-  [ 'captionfile' ],
-  MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT,
-  {
-    captionfile: CONFIG.STORAGE.CAPTIONS_DIR
-  }
-)
+const reqVideoCaptionAdd = createReqFiles([ 'captionfile' ], MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT)
 
 const videoCaptionsRouter = express.Router()
 
index 61e2eb5da6d3afc22ccb949d0923423e34b1ab0d..588cc1a8ca55ef6827aabef1bc509325195e36c7 100644 (file)
@@ -1,6 +1,5 @@
 import express from 'express'
 import { createAnyReqFiles } from '@server/helpers/express-utils'
-import { CONFIG } from '@server/initializers/config'
 import { MIMETYPES } from '@server/initializers/constants'
 import { JobQueue } from '@server/lib/job-queue'
 import { buildTaskFileFieldname, getTaskFile } from '@server/lib/video-editor'
@@ -21,7 +20,6 @@ const editorRouter = express.Router()
 
 const tasksFiles = createAnyReqFiles(
   MIMETYPES.VIDEO.MIMETYPE_EXT,
-  CONFIG.STORAGE.TMP_DIR,
   (req: express.Request, file: Express.Multer.File, cb: (err: Error, result?: boolean) => void) => {
     const body = req.body as VideoEditorCreateEdition
 
index b54fa822c29736dfef3fccf7a49c5b95e3246566..44283e26674e4525127013127b37dcd8cf6c1734 100644 (file)
@@ -61,12 +61,7 @@ const videoImportsRouter = express.Router()
 
 const reqVideoFileImport = createReqFiles(
   [ 'thumbnailfile', 'previewfile', 'torrentfile' ],
-  Object.assign({}, MIMETYPES.TORRENT.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT),
-  {
-    thumbnailfile: CONFIG.STORAGE.TMP_DIR,
-    previewfile: CONFIG.STORAGE.TMP_DIR,
-    torrentfile: CONFIG.STORAGE.TMP_DIR
-  }
+  { ...MIMETYPES.TORRENT.MIMETYPE_EXT, ...MIMETYPES.IMAGE.MIMETYPE_EXT }
 )
 
 videoImportsRouter.post('/imports',
index 8b8cacff99c8304621c01326cb8b81bdb1a46550..49cabb6f3a256d045d4c1cd5efb0d1ca83311f1e 100644 (file)
@@ -1,6 +1,5 @@
 import express from 'express'
 import { createReqFiles } from '@server/helpers/express-utils'
-import { CONFIG } from '@server/initializers/config'
 import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants'
 import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
 import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
@@ -19,14 +18,7 @@ import { VideoModel } from '../../../models/video/video'
 
 const liveRouter = express.Router()
 
-const reqVideoFileLive = createReqFiles(
-  [ 'thumbnailfile', 'previewfile' ],
-  MIMETYPES.IMAGE.MIMETYPE_EXT,
-  {
-    thumbnailfile: CONFIG.STORAGE.TMP_DIR,
-    previewfile: CONFIG.STORAGE.TMP_DIR
-  }
-)
+const reqVideoFileLive = createReqFiles([ 'thumbnailfile', 'previewfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
 
 liveRouter.post('/live',
   authenticate,
index f600847d4da9736f5d29de061506d492257b3b94..8906003fc6a1ad573934e9f232ecd4ce37a801ed 100644 (file)
@@ -11,7 +11,6 @@ import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../
 import { resetSequelizeInstance } from '../../../helpers/database-utils'
 import { createReqFiles } from '../../../helpers/express-utils'
 import { logger, loggerTagsFactory } from '../../../helpers/logger'
-import { CONFIG } from '../../../initializers/config'
 import { MIMETYPES } from '../../../initializers/constants'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { federateVideoIfNeeded } from '../../../lib/activitypub/videos'
@@ -26,14 +25,7 @@ const lTags = loggerTagsFactory('api', 'video')
 const auditLogger = auditLoggerFactory('videos')
 const updateRouter = express.Router()
 
-const reqVideoFileUpdate = createReqFiles(
-  [ 'thumbnailfile', 'previewfile' ],
-  MIMETYPES.IMAGE.MIMETYPE_EXT,
-  {
-    thumbnailfile: CONFIG.STORAGE.TMP_DIR,
-    previewfile: CONFIG.STORAGE.TMP_DIR
-  }
-)
+const reqVideoFileUpdate = createReqFiles([ 'thumbnailfile', 'previewfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT)
 
 updateRouter.put('/:id',
   openapiOperationDoc({ operationId: 'putVideo' }),
index 3c026ad1f07393589426ce5e024807b36d38143e..dd69cf2385403bc5684c0c9e77b69a1cacf91a5c 100644 (file)
@@ -24,9 +24,8 @@ import { HttpStatusCode, VideoCreate, VideoResolution, VideoState } from '@share
 import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { createReqFiles } from '../../../helpers/express-utils'
-import { ffprobePromise, buildFileMetadata, getVideoStreamFPS, getVideoStreamDimensionsInfo } from '../../../helpers/ffmpeg'
+import { buildFileMetadata, ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamFPS } from '../../../helpers/ffmpeg'
 import { logger, loggerTagsFactory } from '../../../helpers/logger'
-import { CONFIG } from '../../../initializers/config'
 import { MIMETYPES } from '../../../initializers/constants'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { federateVideoIfNeeded } from '../../../lib/activitypub/videos'
@@ -52,21 +51,13 @@ const uploadRouter = express.Router()
 
 const reqVideoFileAdd = createReqFiles(
   [ 'videofile', 'thumbnailfile', 'previewfile' ],
-  Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT),
-  {
-    videofile: CONFIG.STORAGE.TMP_DIR,
-    thumbnailfile: CONFIG.STORAGE.TMP_DIR,
-    previewfile: CONFIG.STORAGE.TMP_DIR
-  }
+  { ...MIMETYPES.VIDEO.MIMETYPE_EXT, ...MIMETYPES.IMAGE.MIMETYPE_EXT }
 )
 
 const reqVideoFileAddResumable = createReqFiles(
   [ 'thumbnailfile', 'previewfile' ],
   MIMETYPES.IMAGE.MIMETYPE_EXT,
-  {
-    thumbnailfile: getResumableUploadPath(),
-    previewfile: getResumableUploadPath()
-  }
+  getResumableUploadPath()
 )
 
 uploadRouter.post('/upload',
index 08f77966f29293a503e5589c90af571a612a9059..82dd4c17807006dce47cdfa884974d62082a58f5 100644 (file)
@@ -68,11 +68,11 @@ function badRequest (_req: express.Request, res: express.Response) {
 function createReqFiles (
   fieldNames: string[],
   mimeTypes: { [id: string]: string | string[] },
-  destinations: { [fieldName: string]: string }
+  destination = CONFIG.STORAGE.TMP_DIR
 ): RequestHandler {
   const storage = diskStorage({
     destination: (req, file, cb) => {
-      cb(null, destinations[file.fieldname])
+      cb(null, destination)
     },
 
     filename: (req, file, cb) => {
@@ -93,12 +93,11 @@ function createReqFiles (
 
 function createAnyReqFiles (
   mimeTypes: { [id: string]: string | string[] },
-  destinationDirectory: string,
   fileFilter: (req: express.Request, file: Express.Multer.File, cb: (err: Error, result: boolean) => void) => void
 ): RequestHandler {
   const storage = diskStorage({
     destination: (req, file, cb) => {
-      cb(null, destinationDirectory)
+      cb(null, CONFIG.STORAGE.TMP_DIR)
     },
 
     filename: (req, file, cb) => {
index 7e321fb76e8c9f30c3e34eed1db3ab10b5ba3009..0517e00840725ad33926d58b5db3aedff4949746 100644 (file)
@@ -2,10 +2,9 @@ import { ensureDir, remove } from 'fs-extra'
 import passwordGenerator from 'password-generator'
 import { UserRole } from '@shared/models'
 import { logger } from '../helpers/logger'
-import { createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user'
+import { buildUser, createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user'
 import { ApplicationModel } from '../models/application/application'
 import { OAuthClientModel } from '../models/oauth/oauth-client'
-import { UserModel } from '../models/user/user'
 import { applicationExist, clientsExist, usersExist } from './checker-after-init'
 import { CONFIG } from './config'
 import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION, RESUMABLE_UPLOAD_DIRECTORY } from './constants'
@@ -137,18 +136,15 @@ async function createOAuthAdminIfNotExist () {
     password = passwordGenerator(16, true)
   }
 
-  const userData = {
+  const user = buildUser({
     username,
     email,
     password,
     role,
-    verified: true,
-    nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
-    p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
+    emailVerified: true,
     videoQuota: -1,
     videoQuotaDaily: -1
-  }
-  const user = new UserModel(userData)
+  })
 
   await createUserAccountAndChannelAndPlaylist({ userToCreate: user, channelNames: undefined, validateUser: validatePassword })
   logger.info('Username: ' + username)
index 5d68f44e9c50a4e6dc62725dbd25f012601451f8..910fdeec1099d4e38e04515ca19089baaafa75f0 100644 (file)
@@ -5,14 +5,14 @@ import { ActorModel } from '@server/models/actor/actor'
 import { MOAuthClient } from '@server/types/models'
 import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token'
 import { MUser } from '@server/types/models/user/user'
-import { UserAdminFlag } from '@shared/models/users/user-flag.model'
+import { pick } from '@shared/core-utils'
 import { UserRole } from '@shared/models/users/user-role'
 import { logger } from '../../helpers/logger'
 import { CONFIG } from '../../initializers/config'
 import { OAuthClientModel } from '../../models/oauth/oauth-client'
 import { OAuthTokenModel } from '../../models/oauth/oauth-token'
 import { UserModel } from '../../models/user/user'
-import { createUserAccountAndChannelAndPlaylist } from '../user'
+import { buildUser, createUserAccountAndChannelAndPlaylist } from '../user'
 import { TokensCache } from './tokens-cache'
 
 type TokenInfo = {
@@ -229,19 +229,13 @@ async function createUserFromExternal (pluginAuth: string, options: {
   const actor = await ActorModel.loadLocalByName(options.username)
   if (actor) return null
 
-  const userToCreate = new UserModel({
-    username: options.username,
+  const userToCreate = buildUser({
+    ...pick(options, [ 'username', 'email', 'role' ]),
+
+    emailVerified: null,
     password: null,
-    email: options.email,
-    nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
-    p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
-    autoPlayVideo: true,
-    role: options.role,
-    videoQuota: CONFIG.USER.VIDEO_QUOTA,
-    videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
-    adminFlags: UserAdminFlag.NONE,
     pluginAuth
-  }) as MUser
+  })
 
   const { user } = await createUserAccountAndChannelAndPlaylist({
     userToCreate,
index 3f749929675db1bad4e6951f289471cef6460bf6..ea755f4beab4dee1a92bb863224a9939126ab492 100644 (file)
@@ -1,9 +1,11 @@
 import { Transaction } from 'sequelize/types'
+import { logger } from '@server/helpers/logger'
+import { CONFIG } from '@server/initializers/config'
 import { UserModel } from '@server/models/user/user'
 import { MActorDefault } from '@server/types/models/actor'
 import { buildUUID } from '@shared/extra-utils'
 import { ActivityPubActorType } from '../../shared/models/activitypub'
-import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users'
+import { UserAdminFlag, UserNotificationSetting, UserNotificationSettingValue, UserRole } from '../../shared/models/users'
 import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants'
 import { sequelizeTypescript } from '../initializers/database'
 import { AccountModel } from '../models/account/account'
@@ -19,10 +21,56 @@ import { buildActorInstance } from './local-actor'
 import { Redis } from './redis'
 import { createLocalVideoChannel } from './video-channel'
 import { createWatchLaterPlaylist } from './video-playlist'
-import { logger } from '@server/helpers/logger'
 
 type ChannelNames = { name: string, displayName: string }
 
+function buildUser (options: {
+  username: string
+  password: string
+  email: string
+
+  role?: UserRole // Default to UserRole.User
+  adminFlags?: UserAdminFlag // Default to UserAdminFlag.NONE
+
+  emailVerified: boolean | null
+
+  videoQuota?: number // Default to CONFIG.USER.VIDEO_QUOTA
+  videoQuotaDaily?: number // Default to CONFIG.USER.VIDEO_QUOTA_DAILY
+
+  pluginAuth?: string
+}): MUser {
+  const {
+    username,
+    password,
+    email,
+    role = UserRole.USER,
+    emailVerified,
+    videoQuota = CONFIG.USER.VIDEO_QUOTA,
+    videoQuotaDaily = CONFIG.USER.VIDEO_QUOTA_DAILY,
+    adminFlags = UserAdminFlag.NONE,
+    pluginAuth
+  } = options
+
+  return new UserModel({
+    username,
+    password,
+    email,
+
+    nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
+    p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
+    autoPlayVideo: true,
+
+    role,
+    emailVerified,
+    adminFlags,
+
+    videoQuota: videoQuota,
+    videoQuotaDaily: videoQuotaDaily,
+
+    pluginAuth
+  })
+}
+
 async function createUserAccountAndChannelAndPlaylist (parameters: {
   userToCreate: MUser
   userDisplayName?: string
@@ -118,7 +166,7 @@ async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) {
   const email = isPendingEmail ? user.pendingEmail : user.email
   const username = user.username
 
-  await Emailer.Instance.addVerifyEmailJob(username, email, url)
+  Emailer.Instance.addVerifyEmailJob(username, email, url)
 }
 
 async function getOriginalVideoFileTotalFromUser (user: MUserId) {
@@ -180,7 +228,8 @@ export {
   createUserAccountAndChannelAndPlaylist,
   createLocalAccountWithoutKeys,
   sendVerifyUserEmail,
-  isAbleToUploadVideo
+  isAbleToUploadVideo,
+  buildUser
 }
 
 // ---------------------------------------------------------------------------