Split user service

pull/4750/head
Chocobozzz 2022-01-21 11:03:25 +01:00
parent cce70ae5ca
commit d92d070c91
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
21 changed files with 342 additions and 276 deletions

View File

@ -10,6 +10,7 @@ import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedMainModule } from '@app/shared/shared-main'
import { SharedModerationModule } from '@app/shared/shared-moderation'
import { SharedTablesModule } from '@app/shared/shared-tables'
import { SharedUsersModule } from '@app/shared/shared-users'
import { SharedVideoCommentModule } from '@app/shared/shared-video-comment'
import { SharedVideoMiniatureModule } from '@app/shared/shared-video-miniature'
import { AdminRoutingModule } from './admin-routing.module'
@ -67,6 +68,7 @@ import { JobsComponent } from './system/jobs/jobs.component'
SharedCustomMarkupModule,
SharedVideoMiniatureModule,
SharedTablesModule,
SharedUsersModule,
TableModule,
ChartModule

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router'
import { ConfigService } from '@app/+admin/config/shared/config.service'
import { AuthService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
import { AuthService, Notifier, ScreenService, ServerService } from '@app/core'
import {
USER_CHANNEL_NAME_VALIDATOR,
USER_EMAIL_VALIDATOR,
@ -13,6 +13,7 @@ import {
USER_VIDEO_QUOTA_VALIDATOR
} from '@app/shared/form-validators/user-validators'
import { FormValidatorService } from '@app/shared/shared-forms'
import { UserAdminService } from '@app/shared/shared-users'
import { UserCreate, UserRole } from '@shared/models'
import { UserEdit } from './user-edit'
@ -32,7 +33,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
protected auth: AuthService,
private router: Router,
private notifier: Notifier,
private userService: UserService
private userAdminService: UserAdminService
) {
super()
@ -71,7 +72,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
userCreate.videoQuota = parseInt(this.form.value['videoQuota'], 10)
userCreate.videoQuotaDaily = parseInt(this.form.value['videoQuotaDaily'], 10)
this.userService.addUser(userCreate)
this.userAdminService.addUser(userCreate)
.subscribe({
next: () => {
this.notifier.success($localize`User ${userCreate.username} created.`)

View File

@ -1,7 +1,8 @@
import { Component, Input, OnInit } from '@angular/core'
import { Notifier, UserService } from '@app/core'
import { Notifier } from '@app/core'
import { USER_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { UserAdminService } from '@app/shared/shared-users'
import { UserUpdate } from '@shared/models'
@Component({
@ -19,7 +20,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
constructor (
protected formValidatorService: FormValidatorService,
private notifier: Notifier,
private userService: UserService
private userAdminService: UserAdminService
) {
super()
}
@ -35,7 +36,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
const userUpdate: UserUpdate = this.form.value
this.userService.updateUser(this.userId, userUpdate)
this.userAdminService.updateUser(this.userId, userUpdate)
.subscribe({
next: () => this.notifier.success($localize`Password changed for user ${this.username}.`),

View File

@ -10,6 +10,7 @@ import {
USER_VIDEO_QUOTA_VALIDATOR
} from '@app/shared/form-validators/user-validators'
import { FormValidatorService } from '@app/shared/shared-forms'
import { UserAdminService } from '@app/shared/shared-users'
import { User as UserType, UserAdminFlag, UserRole, UserUpdate } from '@shared/models'
import { UserEdit } from './user-edit'
@ -32,7 +33,8 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
private route: ActivatedRoute,
private router: Router,
private notifier: Notifier,
private userService: UserService
private userService: UserService,
private userAdminService: UserAdminService
) {
super()
@ -86,7 +88,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
if (userUpdate.pluginAuth === 'null') userUpdate.pluginAuth = null
this.userService.updateUser(this.user.id, userUpdate)
this.userAdminService.updateUser(this.user.id, userUpdate)
.subscribe({
next: () => {
this.notifier.success($localize`User ${this.user.username} updated.`)

View File

@ -1,10 +1,11 @@
import { SortMeta } from 'primeng/api'
import { Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService, UserService } from '@app/core'
import { AuthService, ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
import { AdvancedInputFilter } from '@app/shared/shared-forms'
import { DropdownAction } from '@app/shared/shared-main'
import { UserBanModalComponent } from '@app/shared/shared-moderation'
import { UserAdminService } from '@app/shared/shared-users'
import { User, UserRole } from '@shared/models'
type UserForList = User & {
@ -57,7 +58,7 @@ export class UserListComponent extends RestTable implements OnInit {
private confirmService: ConfirmService,
private serverService: ServerService,
private auth: AuthService,
private userService: UserService
private userAdminService: UserAdminService
) {
super()
}
@ -177,7 +178,7 @@ export class UserListComponent extends RestTable implements OnInit {
const res = await this.confirmService.confirm($localize`Do you really want to unban ${users.length} users?`, $localize`Unban`)
if (res === false) return
this.userService.unbanUsers(users)
this.userAdminService.unbanUsers(users)
.subscribe({
next: () => {
this.notifier.success($localize`${users.length} users unbanned.`)
@ -200,7 +201,7 @@ export class UserListComponent extends RestTable implements OnInit {
const res = await this.confirmService.confirm(message, $localize`Delete`)
if (res === false) return
this.userService.removeUser(users)
this.userAdminService.removeUser(users)
.subscribe({
next: () => {
this.notifier.success($localize`${users.length} users deleted.`)
@ -212,7 +213,7 @@ export class UserListComponent extends RestTable implements OnInit {
}
setEmailsAsVerified (users: User[]) {
this.userService.updateUsers(users, { emailVerified: true })
this.userAdminService.updateUsers(users, { emailVerified: true })
.subscribe({
next: () => {
this.notifier.success($localize`${users.length} users email set as verified.`)
@ -230,7 +231,7 @@ export class UserListComponent extends RestTable implements OnInit {
protected reloadData () {
this.selectedUsers = []
this.userService.getUsers({
this.userAdminService.getUsers({
pagination: this.pagination,
sort: this.sort,
search: this.search

View File

@ -2,9 +2,9 @@ import { concat, of } from 'rxjs'
import { pairwise } from 'rxjs/operators'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormGroup } from '@angular/forms'
import { UserService } from '@app/core'
import { VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR, VIDEO_CHANNEL_NAME_VALIDATOR } from '@app/shared/form-validators/video-channel-validators'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { UserSignupService } from '@app/shared/shared-users'
@Component({
selector: 'my-register-step-channel',
@ -17,7 +17,7 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit
constructor (
protected formValidatorService: FormValidatorService,
private userService: UserService
private userSignupService: UserSignupService
) {
super()
}
@ -48,7 +48,7 @@ export class RegisterStepChannelComponent extends FormReactive implements OnInit
private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
const name = this.form.value['name'] || ''
const newName = this.userService.getNewUsername(oldDisplayName, newDisplayName, name)
const newName = this.userSignupService.getNewUsername(oldDisplayName, newDisplayName, name)
this.form.patchValue({ name: newName })
}
}

View File

@ -2,7 +2,6 @@ import { concat, of } from 'rxjs'
import { pairwise } from 'rxjs/operators'
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { FormGroup } from '@angular/forms'
import { UserService } from '@app/core'
import {
USER_DISPLAY_NAME_REQUIRED_VALIDATOR,
USER_EMAIL_VALIDATOR,
@ -10,6 +9,7 @@ import {
USER_USERNAME_VALIDATOR
} from '@app/shared/form-validators/user-validators'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { UserSignupService } from '@app/shared/shared-users'
@Component({
selector: 'my-register-step-user',
@ -23,7 +23,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit {
constructor (
protected formValidatorService: FormValidatorService,
private userService: UserService
private userSignupService: UserSignupService
) {
super()
}
@ -52,7 +52,7 @@ export class RegisterStepUserComponent extends FormReactive implements OnInit {
private onDisplayNameChange (oldDisplayName: string, newDisplayName: string) {
const username = this.form.value['username'] || ''
const newUsername = this.userService.getNewUsername(oldDisplayName, newDisplayName, username)
const newUsername = this.userSignupService.getNewUsername(oldDisplayName, newDisplayName, username)
this.form.patchValue({ username: newUsername })
}
}

View File

@ -1,12 +1,13 @@
import { Component, OnInit } from '@angular/core'
import { FormGroup } from '@angular/forms'
import { ActivatedRoute } from '@angular/router'
import { AuthService, UserService } from '@app/core'
import { AuthService } from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance'
import { UserSignupService } from '@app/shared/shared-users'
import { NgbAccordion } from '@ng-bootstrap/ng-bootstrap'
import { UserRegister } from '@shared/models'
import { ServerConfig } from '@shared/models/server'
import { InstanceAboutAccordionComponent } from '@app/shared/shared-instance'
@Component({
selector: 'my-register',
@ -49,7 +50,7 @@ export class RegisterComponent implements OnInit {
constructor (
private route: ActivatedRoute,
private authService: AuthService,
private userService: UserService,
private userSignupService: UserSignupService,
private hooks: HooksService
) { }
@ -128,7 +129,7 @@ export class RegisterComponent implements OnInit {
'filter:api.signup.registration.create.params'
)
this.userService.signup(body).subscribe({
this.userSignupService.signup(body).subscribe({
next: () => {
this.signupDone = true

View File

@ -1,6 +1,6 @@
import { CdkStepperModule } from '@angular/cdk/stepper'
import { NgModule } from '@angular/core'
import { SignupSharedModule } from '@app/+signup/shared/signup-shared.module'
import { SharedSignupModule } from '@app/+signup/shared/shared-signup.module'
import { SharedInstanceModule } from '@app/shared/shared-instance'
import { CustomStepperComponent } from './custom-stepper.component'
import { RegisterRoutingModule } from './register-routing.module'
@ -15,7 +15,7 @@ import { RegisterComponent } from './register.component'
CdkStepperModule,
SignupSharedModule,
SharedSignupModule,
SharedInstanceModule
],

View File

@ -1,7 +1,8 @@
import { Component, OnInit } from '@angular/core'
import { Notifier, RedirectService, ServerService, UserService } from '@app/core'
import { Notifier, RedirectService, ServerService } from '@app/core'
import { USER_EMAIL_VALIDATOR } from '@app/shared/form-validators/user-validators'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { UserSignupService } from '@app/shared/shared-users'
@Component({
selector: 'my-verify-account-ask-send-email',
@ -14,7 +15,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
constructor (
protected formValidatorService: FormValidatorService,
private userService: UserService,
private userSignupService: UserSignupService,
private serverService: ServerService,
private notifier: Notifier,
private redirectService: RedirectService
@ -33,7 +34,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
askSendVerifyEmail () {
const email = this.form.value['verify-email-email']
this.userService.askSendVerifyEmail(email)
this.userSignupService.askSendVerifyEmail(email)
.subscribe({
next: () => {
this.notifier.success($localize`An email with verification link will be sent to ${email}.`)

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { AuthService, Notifier, UserService } from '@app/core'
import { AuthService, Notifier } from '@app/core'
import { UserSignupService } from '@app/shared/shared-users'
@Component({
selector: 'my-verify-account-email',
@ -16,7 +17,7 @@ export class VerifyAccountEmailComponent implements OnInit {
private verificationString: string
constructor (
private userService: UserService,
private userSignupService: UserSignupService,
private authService: AuthService,
private notifier: Notifier,
private route: ActivatedRoute
@ -37,7 +38,7 @@ export class VerifyAccountEmailComponent implements OnInit {
}
verifyEmail () {
this.userService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail)
this.userSignupService.verifyEmail(this.userId, this.verificationString, this.isPendingEmail)
.subscribe({
next: () => {
if (this.authService.isLoggedIn()) {

View File

@ -1,5 +1,5 @@
import { NgModule } from '@angular/core'
import { SignupSharedModule } from '../shared/signup-shared.module'
import { SharedSignupModule } from '../shared/shared-signup.module'
import { VerifyAccountAskSendEmailComponent } from './verify-account-ask-send-email/verify-account-ask-send-email.component'
import { VerifyAccountEmailComponent } from './verify-account-email/verify-account-email.component'
import { VerifyAccountRoutingModule } from './verify-account-routing.module'
@ -8,7 +8,7 @@ import { VerifyAccountRoutingModule } from './verify-account-routing.module'
imports: [
VerifyAccountRoutingModule,
SignupSharedModule
SharedSignupModule
],
declarations: [

View File

@ -1,14 +1,16 @@
import { NgModule } from '@angular/core'
import { SharedMainModule } from '@app/shared/shared-main'
import { SignupSuccessComponent } from './signup-success.component'
import { SharedFormModule } from '@app/shared/shared-forms'
import { SharedGlobalIconModule } from '@app/shared/shared-icons'
import { SharedMainModule } from '@app/shared/shared-main'
import { SharedUsersModule } from '@app/shared/shared-users'
import { SignupSuccessComponent } from './signup-success.component'
@NgModule({
imports: [
SharedMainModule,
SharedFormModule,
SharedGlobalIconModule
SharedGlobalIconModule,
SharedUsersModule
],
declarations: [
@ -26,4 +28,4 @@ import { SharedGlobalIconModule } from '@app/shared/shared-icons'
providers: [
]
})
export class SignupSharedModule { }
export class SharedSignupModule { }

View File

@ -1,24 +1,12 @@
import { SortMeta } from 'primeng/api'
import { from, Observable, of } from 'rxjs'
import { catchError, concatMap, first, map, shareReplay, tap, toArray } from 'rxjs/operators'
import { Observable, of } from 'rxjs'
import { catchError, first, map, shareReplay } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { AuthService } from '@app/core/auth'
import { getBytes } from '@root-helpers/bytes'
import {
ActorImage,
ResultList,
User as UserServerModel,
UserCreate,
UserRegister,
UserRole,
UserUpdate,
UserUpdateMe,
UserVideoQuota
} from '@shared/models'
import { ActorImage, User as UserServerModel, UserUpdateMe, UserVideoQuota } from '@shared/models'
import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest'
import { UserLocalStorageService } from './'
import { RestExtractor } from '../rest'
import { UserLocalStorageService } from './user-local-storage.service'
import { User } from './user.model'
@Injectable()
@ -26,21 +14,71 @@ export class UserService {
static BASE_USERS_URL = environment.apiUrl + '/api/v1/users/'
private userCache: { [ id: number ]: Observable<UserServerModel> } = {}
private signupInThisSession = false
constructor (
private authHttp: HttpClient,
private authService: AuthService,
private restExtractor: RestExtractor,
private restService: RestService,
private userLocalStorageService: UserLocalStorageService
) { }
// ---------------------------------------------------------------------------
getUserWithCache (userId: number) {
if (!this.userCache[userId]) {
this.userCache[userId] = this.getUser(userId).pipe(shareReplay())
}
return this.userCache[userId]
}
getUser (userId: number, withStats = false) {
const params = new HttpParams().append('withStats', withStats + '')
return this.authHttp.get<UserServerModel>(UserService.BASE_USERS_URL + userId, { params })
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
// ---------------------------------------------------------------------------
setSignupInThisSession (value: boolean) {
this.signupInThisSession = value
}
hasSignupInThisSession () {
return this.signupInThisSession
}
// ---------------------------------------------------------------------------
updateMyAnonymousProfile (profile: UserUpdateMe) {
this.userLocalStorageService.setUserInfo(profile)
}
listenAnonymousUpdate () {
return this.userLocalStorageService.listenUserInfoChange()
.pipe(map(() => this.getAnonymousUser()))
}
getAnonymousUser () {
return new User(this.userLocalStorageService.getUserInfo())
}
getAnonymousOrLoggedUser () {
if (!this.authService.isLoggedIn()) {
return of(this.getAnonymousUser())
}
return this.authService.userInformationLoaded
.pipe(
first(),
map(() => this.authService.getUser())
)
}
// ---------------------------------------------------------------------------
changePassword (currentPassword: string, newPassword: string) {
const url = UserService.BASE_USERS_URL + 'me'
const body: UserUpdateMe = {
@ -63,23 +101,6 @@ export class UserService {
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
// ---------------------------------------------------------------------------
updateMyAnonymousProfile (profile: UserUpdateMe) {
this.userLocalStorageService.setUserInfo(profile)
}
listenAnonymousUpdate () {
return this.userLocalStorageService.listenUserInfoChange()
.pipe(map(() => this.getAnonymousUser()))
}
getAnonymousUser () {
return new User(this.userLocalStorageService.getUserInfo())
}
// ---------------------------------------------------------------------------
updateMyProfile (profile: UserUpdateMe) {
const url = UserService.BASE_USERS_URL + 'me'
@ -108,14 +129,6 @@ export class UserService {
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
signup (userCreate: UserRegister) {
return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate)
.pipe(
tap(() => this.signupInThisSession = true),
catchError(err => this.restExtractor.handleError(err))
)
}
getMyVideoQuotaUsed () {
const url = UserService.BASE_USERS_URL + 'me/video-quota-used'
@ -141,24 +154,6 @@ export class UserService {
.pipe(catchError(res => this.restExtractor.handleError(res)))
}
verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) {
const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email`
const body = {
verificationString,
isPendingEmail
}
return this.authHttp.post(url, body)
.pipe(catchError(res => this.restExtractor.handleError(res)))
}
askSendVerifyEmail (email: string) {
const url = UserService.BASE_USERS_URL + '/ask-send-verify-email'
return this.authHttp.post(url, { email })
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
autocomplete (search: string): Observable<string[]> {
const url = UserService.BASE_USERS_URL + 'autocomplete'
const params = new HttpParams().append('search', search)
@ -167,169 +162,4 @@ export class UserService {
.get<string[]>(url, { params })
.pipe(catchError(res => this.restExtractor.handleError(res)))
}
getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) {
// Don't update display name, the user seems to have changed it
if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername
return this.displayNameToUsername(newDisplayName)
}
displayNameToUsername (displayName: string) {
if (!displayName) return ''
return displayName
.toLowerCase()
.replace(/\s/g, '_')
.replace(/[^a-z0-9_.]/g, '')
}
/* ###### Admin methods ###### */
addUser (userCreate: UserCreate) {
return this.authHttp.post(UserService.BASE_USERS_URL, userCreate)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
updateUser (userId: number, userUpdate: UserUpdate) {
return this.authHttp.put(UserService.BASE_USERS_URL + userId, userUpdate)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
updateUsers (users: UserServerModel[], userUpdate: UserUpdate) {
return from(users)
.pipe(
concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
getUserWithCache (userId: number) {
if (!this.userCache[userId]) {
this.userCache[userId] = this.getUser(userId).pipe(shareReplay())
}
return this.userCache[userId]
}
getUser (userId: number, withStats = false) {
const params = new HttpParams().append('withStats', withStats + '')
return this.authHttp.get<UserServerModel>(UserService.BASE_USERS_URL + userId, { params })
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
getUsers (parameters: {
pagination: RestPagination
sort: SortMeta
search?: string
}): Observable<ResultList<UserServerModel>> {
const { pagination, sort, search } = parameters
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
if (search) {
const filters = this.restService.parseQueryStringFilter(search, {
blocked: {
prefix: 'banned:',
isBoolean: true
}
})
params = this.restService.addObjectParams(params, filters)
}
return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params })
.pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)),
map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))),
catchError(err => this.restExtractor.handleError(err))
)
}
removeUser (usersArg: UserServerModel | UserServerModel[]) {
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
return from(users)
.pipe(
concatMap(u => this.authHttp.delete(UserService.BASE_USERS_URL + u.id)),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) {
const body = reason ? { reason } : {}
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
return from(users)
.pipe(
concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/block', body)),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
unbanUsers (usersArg: UserServerModel | UserServerModel[]) {
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
return from(users)
.pipe(
concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/unblock', {})),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
getAnonymousOrLoggedUser () {
if (!this.authService.isLoggedIn()) {
return of(this.getAnonymousUser())
}
return this.authService.userInformationLoaded
.pipe(
first(),
map(() => this.authService.getUser())
)
}
private formatUser (user: UserServerModel) {
let videoQuota
if (user.videoQuota === -1) {
videoQuota = '∞'
} else {
videoQuota = getBytes(user.videoQuota, 0)
}
const videoQuotaUsed = getBytes(user.videoQuotaUsed, 0)
let videoQuotaDaily: string
let videoQuotaUsedDaily: string
if (user.videoQuotaDaily === -1) {
videoQuotaDaily = '∞'
videoQuotaUsedDaily = getBytes(0, 0) + ''
} else {
videoQuotaDaily = getBytes(user.videoQuotaDaily, 0) + ''
videoQuotaUsedDaily = getBytes(user.videoQuotaUsedDaily || 0, 0) + ''
}
const roleLabels: { [ id in UserRole ]: string } = {
[UserRole.USER]: $localize`User`,
[UserRole.ADMINISTRATOR]: $localize`Administrator`,
[UserRole.MODERATOR]: $localize`Moderator`
}
return Object.assign(user, {
roleLabel: roleLabels[user.role],
videoQuota,
videoQuotaUsed,
rawVideoQuota: user.videoQuota,
rawVideoQuotaUsed: user.videoQuotaUsed,
videoQuotaDaily,
videoQuotaUsedDaily,
rawVideoQuotaDaily: user.videoQuotaDaily,
rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily
})
}
}

View File

@ -1,10 +1,13 @@
import { NgModule } from '@angular/core'
import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
import { SharedFormModule } from '../shared-forms/shared-form.module'
import { SharedGlobalIconModule } from '../shared-icons'
import { SharedMainModule } from '../shared-main/shared-main.module'
import { SharedUsersModule } from '../shared-users'
import { SharedVideoCommentModule } from '../shared-video-comment'
import { AbuseService } from './abuse.service'
import { AccountBlockBadgesComponent } from './account-block-badges.component'
import { BatchDomainsModalComponent } from './batch-domains-modal.component'
import { BlocklistService } from './blocklist.service'
import { BulkService } from './bulk.service'
@ -13,8 +16,6 @@ import { UserBanModalComponent } from './user-ban-modal.component'
import { UserModerationDropdownComponent } from './user-moderation-dropdown.component'
import { VideoBlockComponent } from './video-block.component'
import { VideoBlockService } from './video-block.service'
import { AccountBlockBadgesComponent } from './account-block-badges.component'
import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image.module'
@NgModule({
imports: [
@ -22,7 +23,8 @@ import { SharedActorImageModule } from '../shared-actor-image/shared-actor-image
SharedFormModule,
SharedGlobalIconModule,
SharedVideoCommentModule,
SharedActorImageModule
SharedActorImageModule,
SharedUsersModule
],
declarations: [

View File

@ -1,10 +1,11 @@
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
import { Notifier, UserService } from '@app/core'
import { Notifier } from '@app/core'
import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
import { User } from '@shared/models'
import { USER_BAN_REASON_VALIDATOR } from '../form-validators/user-validators'
import { UserAdminService } from '../shared-users'
@Component({
selector: 'my-user-ban-modal',
@ -23,7 +24,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
protected formValidatorService: FormValidatorService,
private modalService: NgbModal,
private notifier: Notifier,
private userService: UserService
private userAdminService: UserAdminService
) {
super()
}
@ -50,7 +51,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit {
banUser () {
const reason = this.form.value['reason'] || undefined
this.userService.banUsers(this.usersToBan, reason)
this.userAdminService.banUsers(this.usersToBan, reason)
.subscribe({
next: () => {
const message = Array.isArray(this.usersToBan)

View File

@ -1,7 +1,8 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'
import { AuthService, ConfirmService, Notifier, ServerService, UserService } from '@app/core'
import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core'
import { Account, DropdownAction } from '@app/shared/shared-main'
import { BulkRemoveCommentsOfBody, User, UserRight } from '@shared/models'
import { UserAdminService } from '../shared-users'
import { BlocklistService } from './blocklist.service'
import { BulkService } from './bulk.service'
import { UserBanModalComponent } from './user-ban-modal.component'
@ -35,7 +36,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
private notifier: Notifier,
private confirmService: ConfirmService,
private serverService: ServerService,
private userService: UserService,
private userAdminService: UserAdminService,
private blocklistService: BlocklistService,
private bulkService: BulkService
) { }
@ -66,7 +67,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
const res = await this.confirmService.confirm($localize`Do you really want to unban ${user.username}?`, $localize`Unban`)
if (res === false) return
this.userService.unbanUsers(user)
this.userAdminService.unbanUsers(user)
.subscribe({
next: () => {
this.notifier.success($localize`User ${user.username} unbanned.`)
@ -87,7 +88,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
const res = await this.confirmService.confirm(message, $localize`Delete ${user.username}`)
if (res === false) return
this.userService.removeUser(user)
this.userAdminService.removeUser(user)
.subscribe({
next: () => {
this.notifier.success($localize`User ${user.username} deleted.`)
@ -99,7 +100,7 @@ export class UserModerationDropdownComponent implements OnInit, OnChanges {
}
setEmailAsVerified (user: User) {
this.userService.updateUser(user.id, { emailVerified: true })
this.userAdminService.updateUser(user.id, { emailVerified: true })
.subscribe({
next: () => {
this.notifier.success($localize`User ${user.username} email set as verified`)

View File

@ -0,0 +1,4 @@
export * from './user-admin.service'
export * from './user-signup.service'
export * from './shared-users.module'

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core'
import { SharedMainModule } from '../shared-main/shared-main.module'
import { UserAdminService } from './user-admin.service'
import { UserSignupService } from './user-signup.service'
@NgModule({
imports: [
SharedMainModule
],
declarations: [ ],
exports: [],
providers: [
UserSignupService,
UserAdminService
]
})
export class SharedUsersModule { }

View File

@ -0,0 +1,139 @@
import { SortMeta } from 'primeng/api'
import { from, Observable } from 'rxjs'
import { catchError, concatMap, map, toArray } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { RestExtractor, RestPagination, RestService, UserService } from '@app/core'
import { getBytes } from '@root-helpers/bytes'
import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate } from '@shared/models'
@Injectable()
export class UserAdminService {
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private restService: RestService
) { }
addUser (userCreate: UserCreate) {
return this.authHttp.post(UserService.BASE_USERS_URL, userCreate)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
updateUser (userId: number, userUpdate: UserUpdate) {
return this.authHttp.put(UserService.BASE_USERS_URL + userId, userUpdate)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
updateUsers (users: UserServerModel[], userUpdate: UserUpdate) {
return from(users)
.pipe(
concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
getUsers (parameters: {
pagination: RestPagination
sort: SortMeta
search?: string
}): Observable<ResultList<UserServerModel>> {
const { pagination, sort, search } = parameters
let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort)
if (search) {
const filters = this.restService.parseQueryStringFilter(search, {
blocked: {
prefix: 'banned:',
isBoolean: true
}
})
params = this.restService.addObjectParams(params, filters)
}
return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params })
.pipe(
map(res => this.restExtractor.convertResultListDateToHuman(res)),
map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))),
catchError(err => this.restExtractor.handleError(err))
)
}
removeUser (usersArg: UserServerModel | UserServerModel[]) {
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
return from(users)
.pipe(
concatMap(u => this.authHttp.delete(UserService.BASE_USERS_URL + u.id)),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) {
const body = reason ? { reason } : {}
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
return from(users)
.pipe(
concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/block', body)),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
unbanUsers (usersArg: UserServerModel | UserServerModel[]) {
const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
return from(users)
.pipe(
concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/unblock', {})),
toArray(),
catchError(err => this.restExtractor.handleError(err))
)
}
private formatUser (user: UserServerModel) {
let videoQuota
if (user.videoQuota === -1) {
videoQuota = '∞'
} else {
videoQuota = getBytes(user.videoQuota, 0)
}
const videoQuotaUsed = getBytes(user.videoQuotaUsed, 0)
let videoQuotaDaily: string
let videoQuotaUsedDaily: string
if (user.videoQuotaDaily === -1) {
videoQuotaDaily = '∞'
videoQuotaUsedDaily = getBytes(0, 0) + ''
} else {
videoQuotaDaily = getBytes(user.videoQuotaDaily, 0) + ''
videoQuotaUsedDaily = getBytes(user.videoQuotaUsedDaily || 0, 0) + ''
}
const roleLabels: { [ id in UserRole ]: string } = {
[UserRole.USER]: $localize`User`,
[UserRole.ADMINISTRATOR]: $localize`Administrator`,
[UserRole.MODERATOR]: $localize`Moderator`
}
return Object.assign(user, {
roleLabel: roleLabels[user.role],
videoQuota,
videoQuotaUsed,
rawVideoQuota: user.videoQuota,
rawVideoQuotaUsed: user.videoQuotaUsed,
videoQuotaDaily,
videoQuotaUsedDaily,
rawVideoQuotaDaily: user.videoQuotaDaily,
rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily
})
}
}

View File

@ -0,0 +1,56 @@
import { catchError, tap } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { RestExtractor, UserService } from '@app/core'
import { UserRegister } from '@shared/models'
@Injectable()
export class UserSignupService {
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private userService: UserService
) { }
signup (userCreate: UserRegister) {
return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate)
.pipe(
tap(() => this.userService.setSignupInThisSession(true)),
catchError(err => this.restExtractor.handleError(err))
)
}
verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) {
const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email`
const body = {
verificationString,
isPendingEmail
}
return this.authHttp.post(url, body)
.pipe(catchError(res => this.restExtractor.handleError(res)))
}
askSendVerifyEmail (email: string) {
const url = UserService.BASE_USERS_URL + '/ask-send-verify-email'
return this.authHttp.post(url, { email })
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) {
// Don't update display name, the user seems to have changed it
if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername
return this.displayNameToUsername(newDisplayName)
}
private displayNameToUsername (displayName: string) {
if (!displayName) return ''
return displayName
.toLowerCase()
.replace(/\s/g, '_')
.replace(/[^a-z0-9_.]/g, '')
}
}