diff options
Diffstat (limited to 'client/src/app/shared/shared-users')
4 files changed, 220 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-users/index.ts b/client/src/app/shared/shared-users/index.ts new file mode 100644 index 000000000..8f90f2515 --- /dev/null +++ b/client/src/app/shared/shared-users/index.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export * from './user-admin.service' | ||
2 | export * from './user-signup.service' | ||
3 | |||
4 | export * from './shared-users.module' | ||
diff --git a/client/src/app/shared/shared-users/shared-users.module.ts b/client/src/app/shared/shared-users/shared-users.module.ts new file mode 100644 index 000000000..2a1dadf20 --- /dev/null +++ b/client/src/app/shared/shared-users/shared-users.module.ts | |||
@@ -0,0 +1,21 @@ | |||
1 | |||
2 | import { NgModule } from '@angular/core' | ||
3 | import { SharedMainModule } from '../shared-main/shared-main.module' | ||
4 | import { UserAdminService } from './user-admin.service' | ||
5 | import { UserSignupService } from './user-signup.service' | ||
6 | |||
7 | @NgModule({ | ||
8 | imports: [ | ||
9 | SharedMainModule | ||
10 | ], | ||
11 | |||
12 | declarations: [ ], | ||
13 | |||
14 | exports: [], | ||
15 | |||
16 | providers: [ | ||
17 | UserSignupService, | ||
18 | UserAdminService | ||
19 | ] | ||
20 | }) | ||
21 | export class SharedUsersModule { } | ||
diff --git a/client/src/app/shared/shared-users/user-admin.service.ts b/client/src/app/shared/shared-users/user-admin.service.ts new file mode 100644 index 000000000..3db271c4a --- /dev/null +++ b/client/src/app/shared/shared-users/user-admin.service.ts | |||
@@ -0,0 +1,139 @@ | |||
1 | import { SortMeta } from 'primeng/api' | ||
2 | import { from, Observable } from 'rxjs' | ||
3 | import { catchError, concatMap, map, toArray } from 'rxjs/operators' | ||
4 | import { HttpClient, HttpParams } from '@angular/common/http' | ||
5 | import { Injectable } from '@angular/core' | ||
6 | import { RestExtractor, RestPagination, RestService, UserService } from '@app/core' | ||
7 | import { getBytes } from '@root-helpers/bytes' | ||
8 | import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate } from '@shared/models' | ||
9 | |||
10 | @Injectable() | ||
11 | export class UserAdminService { | ||
12 | |||
13 | constructor ( | ||
14 | private authHttp: HttpClient, | ||
15 | private restExtractor: RestExtractor, | ||
16 | private restService: RestService | ||
17 | ) { } | ||
18 | |||
19 | addUser (userCreate: UserCreate) { | ||
20 | return this.authHttp.post(UserService.BASE_USERS_URL, userCreate) | ||
21 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
22 | } | ||
23 | |||
24 | updateUser (userId: number, userUpdate: UserUpdate) { | ||
25 | return this.authHttp.put(UserService.BASE_USERS_URL + userId, userUpdate) | ||
26 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
27 | } | ||
28 | |||
29 | updateUsers (users: UserServerModel[], userUpdate: UserUpdate) { | ||
30 | return from(users) | ||
31 | .pipe( | ||
32 | concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)), | ||
33 | toArray(), | ||
34 | catchError(err => this.restExtractor.handleError(err)) | ||
35 | ) | ||
36 | } | ||
37 | |||
38 | getUsers (parameters: { | ||
39 | pagination: RestPagination | ||
40 | sort: SortMeta | ||
41 | search?: string | ||
42 | }): Observable<ResultList<UserServerModel>> { | ||
43 | const { pagination, sort, search } = parameters | ||
44 | |||
45 | let params = new HttpParams() | ||
46 | params = this.restService.addRestGetParams(params, pagination, sort) | ||
47 | |||
48 | if (search) { | ||
49 | const filters = this.restService.parseQueryStringFilter(search, { | ||
50 | blocked: { | ||
51 | prefix: 'banned:', | ||
52 | isBoolean: true | ||
53 | } | ||
54 | }) | ||
55 | |||
56 | params = this.restService.addObjectParams(params, filters) | ||
57 | } | ||
58 | |||
59 | return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params }) | ||
60 | .pipe( | ||
61 | map(res => this.restExtractor.convertResultListDateToHuman(res)), | ||
62 | map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))), | ||
63 | catchError(err => this.restExtractor.handleError(err)) | ||
64 | ) | ||
65 | } | ||
66 | |||
67 | removeUser (usersArg: UserServerModel | UserServerModel[]) { | ||
68 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
69 | |||
70 | return from(users) | ||
71 | .pipe( | ||
72 | concatMap(u => this.authHttp.delete(UserService.BASE_USERS_URL + u.id)), | ||
73 | toArray(), | ||
74 | catchError(err => this.restExtractor.handleError(err)) | ||
75 | ) | ||
76 | } | ||
77 | |||
78 | banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) { | ||
79 | const body = reason ? { reason } : {} | ||
80 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
81 | |||
82 | return from(users) | ||
83 | .pipe( | ||
84 | concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/block', body)), | ||
85 | toArray(), | ||
86 | catchError(err => this.restExtractor.handleError(err)) | ||
87 | ) | ||
88 | } | ||
89 | |||
90 | unbanUsers (usersArg: UserServerModel | UserServerModel[]) { | ||
91 | const users = Array.isArray(usersArg) ? usersArg : [ usersArg ] | ||
92 | |||
93 | return from(users) | ||
94 | .pipe( | ||
95 | concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/unblock', {})), | ||
96 | toArray(), | ||
97 | catchError(err => this.restExtractor.handleError(err)) | ||
98 | ) | ||
99 | } | ||
100 | |||
101 | private formatUser (user: UserServerModel) { | ||
102 | let videoQuota | ||
103 | if (user.videoQuota === -1) { | ||
104 | videoQuota = '∞' | ||
105 | } else { | ||
106 | videoQuota = getBytes(user.videoQuota, 0) | ||
107 | } | ||
108 | |||
109 | const videoQuotaUsed = getBytes(user.videoQuotaUsed, 0) | ||
110 | |||
111 | let videoQuotaDaily: string | ||
112 | let videoQuotaUsedDaily: string | ||
113 | if (user.videoQuotaDaily === -1) { | ||
114 | videoQuotaDaily = '∞' | ||
115 | videoQuotaUsedDaily = getBytes(0, 0) + '' | ||
116 | } else { | ||
117 | videoQuotaDaily = getBytes(user.videoQuotaDaily, 0) + '' | ||
118 | videoQuotaUsedDaily = getBytes(user.videoQuotaUsedDaily || 0, 0) + '' | ||
119 | } | ||
120 | |||
121 | const roleLabels: { [ id in UserRole ]: string } = { | ||
122 | [UserRole.USER]: $localize`User`, | ||
123 | [UserRole.ADMINISTRATOR]: $localize`Administrator`, | ||
124 | [UserRole.MODERATOR]: $localize`Moderator` | ||
125 | } | ||
126 | |||
127 | return Object.assign(user, { | ||
128 | roleLabel: roleLabels[user.role], | ||
129 | videoQuota, | ||
130 | videoQuotaUsed, | ||
131 | rawVideoQuota: user.videoQuota, | ||
132 | rawVideoQuotaUsed: user.videoQuotaUsed, | ||
133 | videoQuotaDaily, | ||
134 | videoQuotaUsedDaily, | ||
135 | rawVideoQuotaDaily: user.videoQuotaDaily, | ||
136 | rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily | ||
137 | }) | ||
138 | } | ||
139 | } | ||
diff --git a/client/src/app/shared/shared-users/user-signup.service.ts b/client/src/app/shared/shared-users/user-signup.service.ts new file mode 100644 index 000000000..46fe34af1 --- /dev/null +++ b/client/src/app/shared/shared-users/user-signup.service.ts | |||
@@ -0,0 +1,56 @@ | |||
1 | import { catchError, tap } from 'rxjs/operators' | ||
2 | import { HttpClient } from '@angular/common/http' | ||
3 | import { Injectable } from '@angular/core' | ||
4 | import { RestExtractor, UserService } from '@app/core' | ||
5 | import { UserRegister } from '@shared/models' | ||
6 | |||
7 | @Injectable() | ||
8 | export class UserSignupService { | ||
9 | constructor ( | ||
10 | private authHttp: HttpClient, | ||
11 | private restExtractor: RestExtractor, | ||
12 | private userService: UserService | ||
13 | ) { } | ||
14 | |||
15 | signup (userCreate: UserRegister) { | ||
16 | return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate) | ||
17 | .pipe( | ||
18 | tap(() => this.userService.setSignupInThisSession(true)), | ||
19 | catchError(err => this.restExtractor.handleError(err)) | ||
20 | ) | ||
21 | } | ||
22 | |||
23 | verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) { | ||
24 | const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email` | ||
25 | const body = { | ||
26 | verificationString, | ||
27 | isPendingEmail | ||
28 | } | ||
29 | |||
30 | return this.authHttp.post(url, body) | ||
31 | .pipe(catchError(res => this.restExtractor.handleError(res))) | ||
32 | } | ||
33 | |||
34 | askSendVerifyEmail (email: string) { | ||
35 | const url = UserService.BASE_USERS_URL + '/ask-send-verify-email' | ||
36 | |||
37 | return this.authHttp.post(url, { email }) | ||
38 | .pipe(catchError(err => this.restExtractor.handleError(err))) | ||
39 | } | ||
40 | |||
41 | getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) { | ||
42 | // Don't update display name, the user seems to have changed it | ||
43 | if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername | ||
44 | |||
45 | return this.displayNameToUsername(newDisplayName) | ||
46 | } | ||
47 | |||
48 | private displayNameToUsername (displayName: string) { | ||
49 | if (!displayName) return '' | ||
50 | |||
51 | return displayName | ||
52 | .toLowerCase() | ||
53 | .replace(/\s/g, '_') | ||
54 | .replace(/[^a-z0-9_.]/g, '') | ||
55 | } | ||
56 | } | ||