From e364e31e25bd1d4b8d801c845a96d6be708f0a18 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 19 Jan 2023 09:27:16 +0100 Subject: Implement signup approval in server --- server/lib/auth/oauth.ts | 29 ++++++++++-- server/lib/emailer.ts | 54 +++++++++++++++++++--- server/lib/emails/common/base.pug | 12 +---- .../user-registration-request-accepted/html.pug | 10 ++++ .../user-registration-request-rejected/html.pug | 9 ++++ .../lib/emails/user-registration-request/html.pug | 9 ++++ server/lib/emails/verify-email/html.pug | 26 ++++++----- server/lib/notifier/notifier.ts | 19 ++++++-- .../instance/direct-registration-for-moderators.ts | 49 ++++++++++++++++++++ server/lib/notifier/shared/instance/index.ts | 3 +- .../shared/instance/registration-for-moderators.ts | 49 -------------------- .../registration-request-for-moderators.ts | 48 +++++++++++++++++++ server/lib/redis.ts | 30 +++++++++--- server/lib/server-config-manager.ts | 12 ++++- server/lib/signup.ts | 15 +++++- server/lib/user.ts | 38 +++++++++++---- 16 files changed, 305 insertions(+), 107 deletions(-) create mode 100644 server/lib/emails/user-registration-request-accepted/html.pug create mode 100644 server/lib/emails/user-registration-request-rejected/html.pug create mode 100644 server/lib/emails/user-registration-request/html.pug create mode 100644 server/lib/notifier/shared/instance/direct-registration-for-moderators.ts delete mode 100644 server/lib/notifier/shared/instance/registration-for-moderators.ts create mode 100644 server/lib/notifier/shared/instance/registration-request-for-moderators.ts (limited to 'server/lib') diff --git a/server/lib/auth/oauth.ts b/server/lib/auth/oauth.ts index 2905c79a2..887c4f7c9 100644 --- a/server/lib/auth/oauth.ts +++ b/server/lib/auth/oauth.ts @@ -11,20 +11,31 @@ import OAuth2Server, { import { randomBytesPromise } from '@server/helpers/core-utils' import { isOTPValid } from '@server/helpers/otp' import { CONFIG } from '@server/initializers/config' +import { UserRegistrationModel } from '@server/models/user/user-registration' import { MOAuthClient } from '@server/types/models' import { sha1 } from '@shared/extra-utils' -import { HttpStatusCode } from '@shared/models' +import { HttpStatusCode, ServerErrorCode, UserRegistrationState } from '@shared/models' import { OTP } from '../../initializers/constants' import { BypassLogin, getClient, getRefreshToken, getUser, revokeToken, saveToken } from './oauth-model' class MissingTwoFactorError extends Error { code = HttpStatusCode.UNAUTHORIZED_401 - name = 'missing_two_factor' + name = ServerErrorCode.MISSING_TWO_FACTOR } class InvalidTwoFactorError extends Error { code = HttpStatusCode.BAD_REQUEST_400 - name = 'invalid_two_factor' + name = ServerErrorCode.INVALID_TWO_FACTOR +} + +class RegistrationWaitingForApproval extends Error { + code = HttpStatusCode.BAD_REQUEST_400 + name = ServerErrorCode.ACCOUNT_WAITING_FOR_APPROVAL +} + +class RegistrationApprovalRejected extends Error { + code = HttpStatusCode.BAD_REQUEST_400 + name = ServerErrorCode.ACCOUNT_APPROVAL_REJECTED } /** @@ -128,7 +139,17 @@ async function handlePasswordGrant (options: { } const user = await getUser(request.body.username, request.body.password, bypassLogin) - if (!user) throw new InvalidGrantError('Invalid grant: user credentials are invalid') + if (!user) { + const registration = await UserRegistrationModel.loadByEmailOrUsername(request.body.username) + + if (registration?.state === UserRegistrationState.REJECTED) { + throw new RegistrationApprovalRejected('Registration approval for this account has been rejected') + } else if (registration?.state === UserRegistrationState.PENDING) { + throw new RegistrationWaitingForApproval('Registration for this account is awaiting approval') + } + + throw new InvalidGrantError('Invalid grant: user credentials are invalid') + } if (user.otpSecret) { if (!request.headers[OTP.HEADER_NAME]) { diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 39b662eb2..f5c3e4745 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts @@ -3,13 +3,13 @@ import { merge } from 'lodash' import { createTransport, Transporter } from 'nodemailer' import { join } from 'path' import { arrayify, root } from '@shared/core-utils' -import { EmailPayload } from '@shared/models' +import { EmailPayload, UserRegistrationState } from '@shared/models' import { SendEmailDefaultOptions } from '../../shared/models/server/emailer.model' import { isTestOrDevInstance } from '../helpers/core-utils' import { bunyanLogger, logger } from '../helpers/logger' import { CONFIG, isEmailEnabled } from '../initializers/config' import { WEBSERVER } from '../initializers/constants' -import { MUser } from '../types/models' +import { MRegistration, MUser } from '../types/models' import { JobQueue } from './job-queue' const Email = require('email-templates') @@ -62,7 +62,9 @@ class Emailer { subject: 'Reset your account password', locals: { username, - resetPasswordUrl + resetPasswordUrl, + + hideNotificationPreferencesLink: true } } @@ -76,21 +78,33 @@ class Emailer { subject: 'Create your account password', locals: { username, - createPasswordUrl + createPasswordUrl, + + hideNotificationPreferencesLink: true } } return JobQueue.Instance.createJobAsync({ type: 'email', payload: emailPayload }) } - addVerifyEmailJob (username: string, to: string, verifyEmailUrl: string) { + addVerifyEmailJob (options: { + username: string + isRegistrationRequest: boolean + to: string + verifyEmailUrl: string + }) { + const { username, isRegistrationRequest, to, verifyEmailUrl } = options + const emailPayload: EmailPayload = { template: 'verify-email', to: [ to ], subject: `Verify your email on ${CONFIG.INSTANCE.NAME}`, locals: { username, - verifyEmailUrl + verifyEmailUrl, + isRegistrationRequest, + + hideNotificationPreferencesLink: true } } @@ -123,7 +137,33 @@ class Emailer { body, // There are not notification preferences for the contact form - hideNotificationPreferences: true + hideNotificationPreferencesLink: true + } + } + + return JobQueue.Instance.createJobAsync({ type: 'email', payload: emailPayload }) + } + + addUserRegistrationRequestProcessedJob (registration: MRegistration) { + let template: string + let subject: string + if (registration.state === UserRegistrationState.ACCEPTED) { + template = 'user-registration-request-accepted' + subject = `Your registration request for ${registration.username} has been accepted` + } else { + template = 'user-registration-request-rejected' + subject = `Your registration request for ${registration.username} has been rejected` + } + + const to = registration.email + const emailPayload: EmailPayload = { + to: [ to ], + template, + subject, + locals: { + username: registration.username, + moderationResponse: registration.moderationResponse, + loginLink: WEBSERVER.URL + '/login' } } diff --git a/server/lib/emails/common/base.pug b/server/lib/emails/common/base.pug index 6da5648e4..41e94564d 100644 --- a/server/lib/emails/common/base.pug +++ b/server/lib/emails/common/base.pug @@ -222,19 +222,9 @@ body(width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: td(aria-hidden='true' height='20' style='font-size: 0px; line-height: 0px;') br //- Clear Spacer : END - //- 1 Column Text : BEGIN - if username - tr - td(style='background-color: #cccccc;') - table(role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%') - tr - td(style='padding: 20px; font-family: sans-serif; font-size: 15px; line-height: 20px; color: #555555;') - p(style='margin: 0;') - | You are receiving this email as part of your notification settings on #{instanceName} for your account #{username}. - //- 1 Column Text : END //- Email Body : END //- Email Footer : BEGIN - unless hideNotificationPreferences + unless hideNotificationPreferencesLink table(align='center' role='presentation' cellspacing='0' cellpadding='0' border='0' width='100%' style='margin: auto;') tr td(style='padding: 20px; padding-bottom: 0px; font-family: sans-serif; font-size: 12px; line-height: 15px; text-align: center; color: #888888;') diff --git a/server/lib/emails/user-registration-request-accepted/html.pug b/server/lib/emails/user-registration-request-accepted/html.pug new file mode 100644 index 000000000..7a52c3fe1 --- /dev/null +++ b/server/lib/emails/user-registration-request-accepted/html.pug @@ -0,0 +1,10 @@ +extends ../common/greetings + +block title + | Congratulation #{username}, your registration request has been accepted! + +block content + p Your registration request has been accepted. + p Moderators sent you the following message: + blockquote(style='white-space: pre-wrap') #{moderationResponse} + p Your account has been created and you can login on #[a(href=loginLink) #{loginLink}] diff --git a/server/lib/emails/user-registration-request-rejected/html.pug b/server/lib/emails/user-registration-request-rejected/html.pug new file mode 100644 index 000000000..ec0aa8dfe --- /dev/null +++ b/server/lib/emails/user-registration-request-rejected/html.pug @@ -0,0 +1,9 @@ +extends ../common/greetings + +block title + | Registration request of your account #{username} has rejected + +block content + p Your registration request has been rejected. + p Moderators sent you the following message: + blockquote(style='white-space: pre-wrap') #{moderationResponse} diff --git a/server/lib/emails/user-registration-request/html.pug b/server/lib/emails/user-registration-request/html.pug new file mode 100644 index 000000000..64898f3f2 --- /dev/null +++ b/server/lib/emails/user-registration-request/html.pug @@ -0,0 +1,9 @@ +extends ../common/greetings + +block title + | A new user wants to register + +block content + p User #{registration.username} wants to register on your PeerTube instance with the following reason: + blockquote(style='white-space: pre-wrap') #{registration.registrationReason} + p You can accept or reject the registration request in the #[a(href=`${WEBSERVER.URL}/admin/moderation/registrations/list`) administration]. diff --git a/server/lib/emails/verify-email/html.pug b/server/lib/emails/verify-email/html.pug index be9dde21b..19ef65f75 100644 --- a/server/lib/emails/verify-email/html.pug +++ b/server/lib/emails/verify-email/html.pug @@ -1,17 +1,19 @@ extends ../common/greetings block title - | Account verification + | Email verification block content - p Welcome to #{instanceName}! - p. - You just created an account at #[a(href=WEBSERVER.URL) #{instanceName}]. - Your username there is: #{username}. - p. - To start using your account you must verify your email first! - Please follow #[a(href=verifyEmailUrl) this link] to verify this email belongs to you. - p. - If you can't see the verification link above you can use the following link #[a(href=verifyEmailUrl) #{verifyEmailUrl}] - p. - If you are not the person who initiated this request, please ignore this email. + if isRegistrationRequest + p You just requested an account on #[a(href=WEBSERVER.URL) #{instanceName}]. + else + p You just created an account on #[a(href=WEBSERVER.URL) #{instanceName}]. + + if isRegistrationRequest + p To complete your registration request you must verify your email first! + else + p To start using your account you must verify your email first! + + p Please follow #[a(href=verifyEmailUrl) this link] to verify this email belongs to you. + p If you can't see the verification link above you can use the following link #[a(href=verifyEmailUrl) #{verifyEmailUrl}] + p If you are not the person who initiated this request, please ignore this email. diff --git a/server/lib/notifier/notifier.ts b/server/lib/notifier/notifier.ts index 66cfc31c4..920c55df0 100644 --- a/server/lib/notifier/notifier.ts +++ b/server/lib/notifier/notifier.ts @@ -1,4 +1,4 @@ -import { MUser, MUserDefault } from '@server/types/models/user' +import { MRegistration, MUser, MUserDefault } from '@server/types/models/user' import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' import { UserNotificationSettingValue } from '../../../shared/models/users' import { logger } from '../../helpers/logger' @@ -13,6 +13,7 @@ import { AbuseStateChangeForReporter, AutoFollowForInstance, CommentMention, + DirectRegistrationForModerators, FollowForInstance, FollowForUser, ImportFinishedForOwner, @@ -30,7 +31,7 @@ import { OwnedPublicationAfterAutoUnblacklist, OwnedPublicationAfterScheduleUpdate, OwnedPublicationAfterTranscoding, - RegistrationForModerators, + RegistrationRequestForModerators, StudioEditionFinishedForOwner, UnblacklistForOwner } from './shared' @@ -47,7 +48,8 @@ class Notifier { newBlacklist: [ NewBlacklistForOwner ], unblacklist: [ UnblacklistForOwner ], importFinished: [ ImportFinishedForOwner ], - userRegistration: [ RegistrationForModerators ], + directRegistration: [ DirectRegistrationForModerators ], + registrationRequest: [ RegistrationRequestForModerators ], userFollow: [ FollowForUser ], instanceFollow: [ FollowForInstance ], autoInstanceFollow: [ AutoFollowForInstance ], @@ -138,13 +140,20 @@ class Notifier { }) } - notifyOnNewUserRegistration (user: MUserDefault): void { - const models = this.notificationModels.userRegistration + notifyOnNewDirectRegistration (user: MUserDefault): void { + const models = this.notificationModels.directRegistration this.sendNotifications(models, user) .catch(err => logger.error('Cannot notify moderators of new user registration (%s).', user.username, { err })) } + notifyOnNewRegistrationRequest (registration: MRegistration): void { + const models = this.notificationModels.registrationRequest + + this.sendNotifications(models, registration) + .catch(err => logger.error('Cannot notify moderators of new registration request (%s).', registration.username, { err })) + } + notifyOfNewUserFollow (actorFollow: MActorFollowFull): void { const models = this.notificationModels.userFollow diff --git a/server/lib/notifier/shared/instance/direct-registration-for-moderators.ts b/server/lib/notifier/shared/instance/direct-registration-for-moderators.ts new file mode 100644 index 000000000..5044f2068 --- /dev/null +++ b/server/lib/notifier/shared/instance/direct-registration-for-moderators.ts @@ -0,0 +1,49 @@ +import { logger } from '@server/helpers/logger' +import { CONFIG } from '@server/initializers/config' +import { UserModel } from '@server/models/user/user' +import { UserNotificationModel } from '@server/models/user/user-notification' +import { MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' +import { UserNotificationType, UserRight } from '@shared/models' +import { AbstractNotification } from '../common/abstract-notification' + +export class DirectRegistrationForModerators extends AbstractNotification { + private moderators: MUserDefault[] + + async prepare () { + this.moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) + } + + log () { + logger.info('Notifying %s moderators of new user registration of %s.', this.moderators.length, this.payload.username) + } + + getSetting (user: MUserWithNotificationSetting) { + return user.NotificationSetting.newUserRegistration + } + + getTargetUsers () { + return this.moderators + } + + createNotification (user: MUserWithNotificationSetting) { + const notification = UserNotificationModel.build({ + type: UserNotificationType.NEW_USER_REGISTRATION, + userId: user.id, + accountId: this.payload.Account.id + }) + notification.Account = this.payload.Account + + return notification + } + + createEmail (to: string) { + return { + template: 'user-registered', + to, + subject: `A new user registered on ${CONFIG.INSTANCE.NAME}: ${this.payload.username}`, + locals: { + user: this.payload + } + } + } +} diff --git a/server/lib/notifier/shared/instance/index.ts b/server/lib/notifier/shared/instance/index.ts index c3bb22aec..8c75a8ee9 100644 --- a/server/lib/notifier/shared/instance/index.ts +++ b/server/lib/notifier/shared/instance/index.ts @@ -1,3 +1,4 @@ export * from './new-peertube-version-for-admins' export * from './new-plugin-version-for-admins' -export * from './registration-for-moderators' +export * from './direct-registration-for-moderators' +export * from './registration-request-for-moderators' diff --git a/server/lib/notifier/shared/instance/registration-for-moderators.ts b/server/lib/notifier/shared/instance/registration-for-moderators.ts deleted file mode 100644 index e92467424..000000000 --- a/server/lib/notifier/shared/instance/registration-for-moderators.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { logger } from '@server/helpers/logger' -import { CONFIG } from '@server/initializers/config' -import { UserModel } from '@server/models/user/user' -import { UserNotificationModel } from '@server/models/user/user-notification' -import { MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' -import { UserNotificationType, UserRight } from '@shared/models' -import { AbstractNotification } from '../common/abstract-notification' - -export class RegistrationForModerators extends AbstractNotification { - private moderators: MUserDefault[] - - async prepare () { - this.moderators = await UserModel.listWithRight(UserRight.MANAGE_USERS) - } - - log () { - logger.info('Notifying %s moderators of new user registration of %s.', this.moderators.length, this.payload.username) - } - - getSetting (user: MUserWithNotificationSetting) { - return user.NotificationSetting.newUserRegistration - } - - getTargetUsers () { - return this.moderators - } - - createNotification (user: MUserWithNotificationSetting) { - const notification = UserNotificationModel.build({ - type: UserNotificationType.NEW_USER_REGISTRATION, - userId: user.id, - accountId: this.payload.Account.id - }) - notification.Account = this.payload.Account - - return notification - } - - createEmail (to: string) { - return { - template: 'user-registered', - to, - subject: `a new user registered on ${CONFIG.INSTANCE.NAME}: ${this.payload.username}`, - locals: { - user: this.payload - } - } - } -} diff --git a/server/lib/notifier/shared/instance/registration-request-for-moderators.ts b/server/lib/notifier/shared/instance/registration-request-for-moderators.ts new file mode 100644 index 000000000..79920245a --- /dev/null +++ b/server/lib/notifier/shared/instance/registration-request-for-moderators.ts @@ -0,0 +1,48 @@ +import { logger } from '@server/helpers/logger' +import { UserModel } from '@server/models/user/user' +import { UserNotificationModel } from '@server/models/user/user-notification' +import { MRegistration, MUserDefault, MUserWithNotificationSetting, UserNotificationModelForApi } from '@server/types/models' +import { UserNotificationType, UserRight } from '@shared/models' +import { AbstractNotification } from '../common/abstract-notification' + +export class RegistrationRequestForModerators extends AbstractNotification { + private moderators: MUserDefault[] + + async prepare () { + this.moderators = await UserModel.listWithRight(UserRight.MANAGE_REGISTRATIONS) + } + + log () { + logger.info('Notifying %s moderators of new user registration request of %s.', this.moderators.length, this.payload.username) + } + + getSetting (user: MUserWithNotificationSetting) { + return user.NotificationSetting.newUserRegistration + } + + getTargetUsers () { + return this.moderators + } + + createNotification (user: MUserWithNotificationSetting) { + const notification = UserNotificationModel.build({ + type: UserNotificationType.NEW_USER_REGISTRATION_REQUEST, + userId: user.id, + userRegistrationId: this.payload.id + }) + notification.UserRegistration = this.payload + + return notification + } + + createEmail (to: string) { + return { + template: 'user-registration-request', + to, + subject: `A new user wants to register: ${this.payload.username}`, + locals: { + registration: this.payload + } + } + } +} diff --git a/server/lib/redis.ts b/server/lib/redis.ts index 451ddd0b6..3706d2228 100644 --- a/server/lib/redis.ts +++ b/server/lib/redis.ts @@ -9,7 +9,7 @@ import { CONTACT_FORM_LIFETIME, RESUMABLE_UPLOAD_SESSION_LIFETIME, TWO_FACTOR_AUTH_REQUEST_TOKEN_LIFETIME, - USER_EMAIL_VERIFY_LIFETIME, + EMAIL_VERIFY_LIFETIME, USER_PASSWORD_CREATE_LIFETIME, USER_PASSWORD_RESET_LIFETIME, VIEW_LIFETIME, @@ -124,16 +124,28 @@ class Redis { /* ************ Email verification ************ */ - async setVerifyEmailVerificationString (userId: number) { + async setUserVerifyEmailVerificationString (userId: number) { const generatedString = await generateRandomString(32) - await this.setValue(this.generateVerifyEmailKey(userId), generatedString, USER_EMAIL_VERIFY_LIFETIME) + await this.setValue(this.generateUserVerifyEmailKey(userId), generatedString, EMAIL_VERIFY_LIFETIME) return generatedString } - async getVerifyEmailLink (userId: number) { - return this.getValue(this.generateVerifyEmailKey(userId)) + async getUserVerifyEmailLink (userId: number) { + return this.getValue(this.generateUserVerifyEmailKey(userId)) + } + + async setRegistrationVerifyEmailVerificationString (registrationId: number) { + const generatedString = await generateRandomString(32) + + await this.setValue(this.generateRegistrationVerifyEmailKey(registrationId), generatedString, EMAIL_VERIFY_LIFETIME) + + return generatedString + } + + async getRegistrationVerifyEmailLink (registrationId: number) { + return this.getValue(this.generateRegistrationVerifyEmailKey(registrationId)) } /* ************ Contact form per IP ************ */ @@ -346,8 +358,12 @@ class Redis { return 'two-factor-request-' + userId + '-' + token } - private generateVerifyEmailKey (userId: number) { - return 'verify-email-' + userId + private generateUserVerifyEmailKey (userId: number) { + return 'verify-email-user-' + userId + } + + private generateRegistrationVerifyEmailKey (registrationId: number) { + return 'verify-email-registration-' + registrationId } private generateIPViewKey (ip: string, videoUUID: string) { diff --git a/server/lib/server-config-manager.ts b/server/lib/server-config-manager.ts index 78a9546ae..e87e2854f 100644 --- a/server/lib/server-config-manager.ts +++ b/server/lib/server-config-manager.ts @@ -261,10 +261,17 @@ class ServerConfigManager { async getServerConfig (ip?: string): Promise { const { allowed } = await Hooks.wrapPromiseFun( isSignupAllowed, + { - ip + ip, + signupMode: CONFIG.SIGNUP.REQUIRES_APPROVAL + ? 'request-registration' + : 'direct-registration' }, - 'filter:api.user.signup.allowed.result' + + CONFIG.SIGNUP.REQUIRES_APPROVAL + ? 'filter:api.user.request-signup.allowed.result' + : 'filter:api.user.signup.allowed.result' ) const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip) @@ -273,6 +280,7 @@ class ServerConfigManager { allowed, allowedForCurrentIP, minimumAge: CONFIG.SIGNUP.MINIMUM_AGE, + requiresApproval: CONFIG.SIGNUP.REQUIRES_APPROVAL, requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION } diff --git a/server/lib/signup.ts b/server/lib/signup.ts index f094531eb..f19232621 100644 --- a/server/lib/signup.ts +++ b/server/lib/signup.ts @@ -4,11 +4,24 @@ import { UserModel } from '../models/user/user' const isCidr = require('is-cidr') -async function isSignupAllowed (): Promise<{ allowed: boolean, errorMessage?: string }> { +export type SignupMode = 'direct-registration' | 'request-registration' + +async function isSignupAllowed (options: { + signupMode: SignupMode + + ip: string // For plugins + body?: any +}): Promise<{ allowed: boolean, errorMessage?: string }> { + const { signupMode } = options + if (CONFIG.SIGNUP.ENABLED === false) { return { allowed: false } } + if (signupMode === 'direct-registration' && CONFIG.SIGNUP.REQUIRES_APPROVAL === true) { + return { allowed: false } + } + // No limit and signup is enabled if (CONFIG.SIGNUP.LIMIT === -1) { return { allowed: true } diff --git a/server/lib/user.ts b/server/lib/user.ts index 2e433da04..ffb57944a 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts @@ -10,7 +10,7 @@ import { sequelizeTypescript } from '../initializers/database' import { AccountModel } from '../models/account/account' import { UserNotificationSettingModel } from '../models/user/user-notification-setting' import { MAccountDefault, MChannelActor } from '../types/models' -import { MUser, MUserDefault, MUserId } from '../types/models/user' +import { MRegistration, MUser, MUserDefault, MUserId } from '../types/models/user' import { generateAndSaveActorKeys } from './activitypub/actors' import { getLocalAccountActivityPubUrl } from './activitypub/url' import { Emailer } from './emailer' @@ -97,7 +97,7 @@ async function createUserAccountAndChannelAndPlaylist (parameters: { }) userCreated.Account = accountCreated - const channelAttributes = await buildChannelAttributes(userCreated, t, channelNames) + const channelAttributes = await buildChannelAttributes({ user: userCreated, transaction: t, channelNames }) const videoChannel = await createLocalVideoChannel(channelAttributes, accountCreated, t) const videoPlaylist = await createWatchLaterPlaylist(accountCreated, t) @@ -160,15 +160,28 @@ async function createApplicationActor (applicationId: number) { // --------------------------------------------------------------------------- async function sendVerifyUserEmail (user: MUser, isPendingEmail = false) { - const verificationString = await Redis.Instance.setVerifyEmailVerificationString(user.id) - let url = WEBSERVER.URL + '/verify-account/email?userId=' + user.id + '&verificationString=' + verificationString + const verificationString = await Redis.Instance.setUserVerifyEmailVerificationString(user.id) + let verifyEmailUrl = `${WEBSERVER.URL}/verify-account/email?userId=${user.id}&verificationString=${verificationString}` - if (isPendingEmail) url += '&isPendingEmail=true' + if (isPendingEmail) verifyEmailUrl += '&isPendingEmail=true' + + const to = isPendingEmail + ? user.pendingEmail + : user.email - const email = isPendingEmail ? user.pendingEmail : user.email const username = user.username - Emailer.Instance.addVerifyEmailJob(username, email, url) + Emailer.Instance.addVerifyEmailJob({ username, to, verifyEmailUrl, isRegistrationRequest: false }) +} + +async function sendVerifyRegistrationEmail (registration: MRegistration) { + const verificationString = await Redis.Instance.setRegistrationVerifyEmailVerificationString(registration.id) + const verifyEmailUrl = `${WEBSERVER.URL}/verify-account/email?registrationId=${registration.id}&verificationString=${verificationString}` + + const to = registration.email + const username = registration.username + + Emailer.Instance.addVerifyEmailJob({ username, to, verifyEmailUrl, isRegistrationRequest: true }) } // --------------------------------------------------------------------------- @@ -232,7 +245,10 @@ export { createApplicationActor, createUserAccountAndChannelAndPlaylist, createLocalAccountWithoutKeys, + sendVerifyUserEmail, + sendVerifyRegistrationEmail, + isAbleToUploadVideo, buildUser } @@ -264,7 +280,13 @@ function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | return UserNotificationSettingModel.create(values, { transaction: t }) } -async function buildChannelAttributes (user: MUser, transaction?: Transaction, channelNames?: ChannelNames) { +async function buildChannelAttributes (options: { + user: MUser + transaction?: Transaction + channelNames?: ChannelNames +}) { + const { user, transaction, channelNames } = options + if (channelNames) return channelNames const channelName = await findAvailableLocalActorName(user.username + '_channel', transaction) -- cgit v1.2.3