diff --git a/client/proxy.config.json b/client/proxy.config.json index e5f0dfd61..4a72f1826 100644 --- a/client/proxy.config.json +++ b/client/proxy.config.json @@ -8,7 +8,8 @@ "secure": false }, "/socket.io": { - "target": "http://localhost:9000", - "secure": false + "target": "ws://localhost:9000", + "secure": false, + "ws": true } } diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/index.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/index.ts new file mode 100644 index 000000000..f42af361e --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/index.ts @@ -0,0 +1 @@ +export * from './my-account-change-email.component' diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.html b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.html new file mode 100644 index 000000000..ebfe3126d --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.html @@ -0,0 +1,36 @@ +
{{ error }}
+
{{ success }}
+ +
+ Your current email is {{ user.email }} +
+ +
+ {{ user.pendingEmail }} is awaiting email verification +
+ +
+ +
+ + +
+ {{ formErrors['new-email'] }} +
+
+ +
+ +
+ {{ formErrors['password'] }} +
+
+ + +
diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.scss b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.scss new file mode 100644 index 000000000..81eba3ec9 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.scss @@ -0,0 +1,24 @@ +@import '_variables'; +@import '_mixins'; + +input[type=password], +input[type=email] { + @include peertube-input-text(340px); + + display: block; +} + +input[type=submit] { + @include peertube-button; + @include orange-button; +} + +.current-email, +.pending-email { + font-size: 16px; + margin: 15px 0; + + .email { + font-weight: $font-semibold; + } +} diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts new file mode 100644 index 000000000..577c5b102 --- /dev/null +++ b/client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts @@ -0,0 +1,73 @@ +import { Component, OnInit } from '@angular/core' +import { AuthService, Notifier, ServerService } from '@app/core' +import { FormReactive, UserService } from '../../../shared' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' +import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service' +import { User } from '../../../../../../shared' +import { switchMap, tap } from 'rxjs/operators' + +@Component({ + selector: 'my-account-change-email', + templateUrl: './my-account-change-email.component.html', + styleUrls: [ './my-account-change-email.component.scss' ] +}) +export class MyAccountChangeEmailComponent extends FormReactive implements OnInit { + error: string = null + success: string = null + user: User = null + + constructor ( + protected formValidatorService: FormValidatorService, + private userValidatorsService: UserValidatorsService, + private notifier: Notifier, + private authService: AuthService, + private userService: UserService, + private serverService: ServerService, + private i18n: I18n + ) { + super() + } + + ngOnInit () { + this.buildForm({ + 'new-email': this.userValidatorsService.USER_EMAIL, + 'password': this.userValidatorsService.USER_PASSWORD + }) + + this.user = this.authService.getUser() + } + + changeEmail () { + this.error = null + this.success = null + + const password = this.form.value[ 'password' ] + const email = this.form.value[ 'new-email' ] + + this.userService.changeEmail(password, email) + .pipe( + tap(() => this.authService.refreshUserInformation()) + ) + .subscribe( + () => { + this.form.reset() + + if (this.serverService.getConfig().signup.requiresEmailVerification) { + this.success = this.i18n('Please check your emails to verify your new email.') + } else { + this.success = this.i18n('Email updated.') + } + }, + + err => { + if (err.status === 401) { + this.error = this.i18n('You current password is invalid.') + return + } + + this.error = err.message + } + ) + } +} diff --git a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html index ae797d1bc..a39061ee3 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html +++ b/client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.html @@ -2,7 +2,7 @@
- + Password +
Email
+ +
Video settings
diff --git a/client/src/app/+my-account/my-account.module.ts b/client/src/app/+my-account/my-account.module.ts index 4a18a9968..ca5b1f7cb 100644 --- a/client/src/app/+my-account/my-account.module.ts +++ b/client/src/app/+my-account/my-account.module.ts @@ -36,6 +36,7 @@ import { MyAccountVideoPlaylistElementsComponent } from '@app/+my-account/my-account-video-playlists/my-account-video-playlist-elements.component' import { DragDropModule } from '@angular/cdk/drag-drop' +import { MyAccountChangeEmailComponent } from '@app/+my-account/my-account-settings/my-account-change-email' @NgModule({ imports: [ @@ -54,7 +55,10 @@ import { DragDropModule } from '@angular/cdk/drag-drop' MyAccountChangePasswordComponent, MyAccountVideoSettingsComponent, MyAccountProfileComponent, + MyAccountChangeEmailComponent, + MyAccountVideosComponent, + VideoChangeOwnershipComponent, MyAccountOwnershipComponent, MyAccountAcceptOwnershipComponent, diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html index 728709ca6..47519c943 100644 --- a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html +++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.html @@ -3,13 +3,16 @@ Verify account email confirmation - + - -
- An error occurred. - Request new verification email. -
-
+
+ Email updated. +
+ +
+ An error occurred. + + Request new verification email. +
diff --git a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts index 3fb2d1cd8..054f04310 100644 --- a/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts +++ b/client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { I18n } from '@ngx-translate/i18n-polyfill' -import { Notifier } from '@app/core' +import { AuthService, Notifier } from '@app/core' import { UserService } from '@app/shared' @Component({ @@ -11,12 +11,15 @@ import { UserService } from '@app/shared' export class VerifyAccountEmailComponent implements OnInit { success = false + failed = false + isPendingEmail = false private userId: number private verificationString: string constructor ( private userService: UserService, + private authService: AuthService, private notifier: Notifier, private router: Router, private route: ActivatedRoute, @@ -25,8 +28,12 @@ export class VerifyAccountEmailComponent implements OnInit { } ngOnInit () { - this.userId = this.route.snapshot.queryParams['userId'] - this.verificationString = this.route.snapshot.queryParams['verificationString'] + const queryParams = this.route.snapshot.queryParams + this.userId = queryParams['userId'] + this.verificationString = queryParams['verificationString'] + this.isPendingEmail = queryParams['isPendingEmail'] === 'true' + + console.log(this.isPendingEmail) if (!this.userId || !this.verificationString) { this.notifier.error(this.i18n('Unable to find user id or verification string.')) @@ -36,13 +43,17 @@ export class VerifyAccountEmailComponent implements OnInit { } verifyEmail () { - this.userService.verifyEmail(this.userId, this.verificationString) + this.userService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail) .subscribe( () => { + this.authService.refreshUserInformation() + this.success = true }, err => { + this.failed = true + this.notifier.error(err.message) } ) diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index e3ed2dfbd..14d13959a 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts @@ -8,6 +8,7 @@ export class User implements UserServerModel { id: number username: string email: string + pendingEmail: string | null emailVerified: boolean nsfwPolicy: NSFWPolicyType diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts index 70ff9a058..41ee87197 100644 --- a/client/src/app/shared/users/user.service.ts +++ b/client/src/app/shared/users/user.service.ts @@ -38,6 +38,20 @@ export class UserService { ) } + changeEmail (password: string, newEmail: string) { + const url = UserService.BASE_USERS_URL + 'me' + const body: UserUpdateMe = { + currentPassword: password, + email: newEmail + } + + return this.authHttp.put(url, body) + .pipe( + map(this.restExtractor.extractDataBool), + catchError(err => this.restExtractor.handleError(err)) + ) + } + updateMyProfile (profile: UserUpdateMe) { const url = UserService.BASE_USERS_URL + 'me' @@ -104,10 +118,11 @@ export class UserService { ) } - verifyEmail (userId: number, verificationString: string) { + verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) { const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email` const body = { - verificationString + verificationString, + isPendingEmail } return this.authHttp.post(url, body) diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index a4d4ae46d..ec70fa0fd 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts @@ -205,7 +205,7 @@ const usersUpdateMeValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking usersUpdateMe parameters', { parameters: omit(req.body, 'password') }) - if (req.body.password) { + if (req.body.password || req.body.email) { if (!req.body.currentPassword) { return res.status(400) .send({ error: 'currentPassword parameter is missing.' })