]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/app/core/users/user.service.ts
Add ability for admins to set default p2p policy
[github/Chocobozzz/PeerTube.git] / client / src / app / core / users / user.service.ts
CommitLineData
5c20a455
C
1import { SortMeta } from 'primeng/api'
2import { from, Observable, of } from 'rxjs'
a9bfa85d 3import { catchError, concatMap, first, map, shareReplay, tap, toArray } from 'rxjs/operators'
74d63469 4import { HttpClient, HttpParams } from '@angular/common/http'
63c4db6d 5import { Injectable } from '@angular/core'
5c20a455 6import { AuthService } from '@app/core/auth'
b4c3c51d 7import { getBytes } from '@root-helpers/bytes'
67ed6552 8import {
f4796856 9 ActorImage,
67ed6552
C
10 ResultList,
11 User as UserServerModel,
12 UserCreate,
13 UserRegister,
14 UserRole,
15 UserUpdate,
16 UserUpdateMe,
17 UserVideoQuota
18} from '@shared/models'
5c20a455 19import { environment } from '../../../environments/environment'
5c20a455 20import { RestExtractor, RestPagination, RestService } from '../rest'
a9bfa85d 21import { UserLocalStorageService } from './'
5c20a455 22import { User } from './user.model'
629d8d6f
C
23
24@Injectable()
e2a2d6c8 25export class UserService {
63c4db6d 26 static BASE_USERS_URL = environment.apiUrl + '/api/v1/users/'
629d8d6f 27
d3217560 28 private userCache: { [ id: number ]: Observable<UserServerModel> } = {}
218b0874 29
bf80903f
C
30 private signupInThisSession = false
31
df98563e 32 constructor (
d592e0a9 33 private authHttp: HttpClient,
5c20a455 34 private authService: AuthService,
e724fa93
C
35 private restExtractor: RestExtractor,
36 private restService: RestService,
a9bfa85d 37 private userLocalStorageService: UserLocalStorageService
9df52d66 38 ) { }
629d8d6f 39
bf80903f
C
40 hasSignupInThisSession () {
41 return this.signupInThisSession
42 }
43
a890d1e0 44 changePassword (currentPassword: string, newPassword: string) {
8094a898
C
45 const url = UserService.BASE_USERS_URL + 'me'
46 const body: UserUpdateMe = {
a890d1e0 47 currentPassword,
629d8d6f 48 password: newPassword
df98563e 49 }
629d8d6f 50
de59c48f 51 return this.authHttp.put(url, body)
db400f44
C
52 .pipe(
53 map(this.restExtractor.extractDataBool),
e4f0e92e 54 catchError(err => this.restExtractor.handleError(err))
db400f44 55 )
629d8d6f 56 }
af5e743b 57
0ba5f5ba
C
58 changeEmail (password: string, newEmail: string) {
59 const url = UserService.BASE_USERS_URL + 'me'
60 const body: UserUpdateMe = {
61 currentPassword: password,
62 email: newEmail
63 }
64
65 return this.authHttp.put(url, body)
66 .pipe(
67 map(this.restExtractor.extractDataBool),
68 catchError(err => this.restExtractor.handleError(err))
69 )
70 }
71
a9bfa85d
C
72 // ---------------------------------------------------------------------------
73
74 updateMyAnonymousProfile (profile: UserUpdateMe) {
75 this.userLocalStorageService.setUserInfo(profile)
76 }
77
78 listenAnonymousUpdate () {
79 return this.userLocalStorageService.listenUserInfoChange()
80 .pipe(map(() => this.getAnonymousUser()))
81 }
82
83 getAnonymousUser () {
84 return new User(this.userLocalStorageService.getUserInfo())
85 }
86
87 // ---------------------------------------------------------------------------
88
ed56ad11 89 updateMyProfile (profile: UserUpdateMe) {
8094a898 90 const url = UserService.BASE_USERS_URL + 'me'
af5e743b 91
ed56ad11 92 return this.authHttp.put(url, profile)
db400f44
C
93 .pipe(
94 map(this.restExtractor.extractDataBool),
e4f0e92e 95 catchError(err => this.restExtractor.handleError(err))
db400f44 96 )
af5e743b 97 }
a184c71b 98
92b9d60c
C
99 deleteMe () {
100 const url = UserService.BASE_USERS_URL + 'me'
101
102 return this.authHttp.delete(url)
103 .pipe(
104 map(this.restExtractor.extractDataBool),
105 catchError(err => this.restExtractor.handleError(err))
106 )
107 }
108
c5911fd3
C
109 changeAvatar (avatarForm: FormData) {
110 const url = UserService.BASE_USERS_URL + 'me/avatar/pick'
111
f4796856 112 return this.authHttp.post<{ avatar: ActorImage }>(url, avatarForm)
e4f0e92e 113 .pipe(catchError(err => this.restExtractor.handleError(err)))
c5911fd3
C
114 }
115
1ea7da81
RK
116 deleteAvatar () {
117 const url = UserService.BASE_USERS_URL + 'me/avatar'
118
119 return this.authHttp.delete(url)
120 .pipe(
121 map(this.restExtractor.extractDataBool),
122 catchError(err => this.restExtractor.handleError(err))
123 )
124 }
125
1d5342ab 126 signup (userCreate: UserRegister) {
d592e0a9 127 return this.authHttp.post(UserService.BASE_USERS_URL + 'register', userCreate)
db400f44
C
128 .pipe(
129 map(this.restExtractor.extractDataBool),
bf80903f 130 tap(() => this.signupInThisSession = true),
e4f0e92e 131 catchError(err => this.restExtractor.handleError(err))
db400f44 132 )
a184c71b 133 }
c5911fd3 134
ce5496d6 135 getMyVideoQuotaUsed () {
bf64ed41 136 const url = UserService.BASE_USERS_URL + 'me/video-quota-used'
c5911fd3 137
5fcbd898 138 return this.authHttp.get<UserVideoQuota>(url)
e4f0e92e 139 .pipe(catchError(err => this.restExtractor.handleError(err)))
c5911fd3 140 }
ecb4e35f
C
141
142 askResetPassword (email: string) {
143 const url = UserService.BASE_USERS_URL + '/ask-reset-password'
144
145 return this.authHttp.post(url, { email })
db400f44
C
146 .pipe(
147 map(this.restExtractor.extractDataBool),
e4f0e92e 148 catchError(err => this.restExtractor.handleError(err))
db400f44 149 )
ecb4e35f
C
150 }
151
152 resetPassword (userId: number, verificationString: string, password: string) {
153 const url = `${UserService.BASE_USERS_URL}/${userId}/reset-password`
154 const body = {
155 verificationString,
156 password
157 }
158
159 return this.authHttp.post(url, body)
db400f44
C
160 .pipe(
161 map(this.restExtractor.extractDataBool),
162 catchError(res => this.restExtractor.handleError(res))
163 )
ecb4e35f 164 }
d9eaee39 165
0ba5f5ba 166 verifyEmail (userId: number, verificationString: string, isPendingEmail: boolean) {
d9eaee39
JM
167 const url = `${UserService.BASE_USERS_URL}/${userId}/verify-email`
168 const body = {
0ba5f5ba
C
169 verificationString,
170 isPendingEmail
d9eaee39
JM
171 }
172
173 return this.authHttp.post(url, body)
174 .pipe(
175 map(this.restExtractor.extractDataBool),
176 catchError(res => this.restExtractor.handleError(res))
177 )
178 }
179
180 askSendVerifyEmail (email: string) {
181 const url = UserService.BASE_USERS_URL + '/ask-send-verify-email'
182
183 return this.authHttp.post(url, { email })
184 .pipe(
185 map(this.restExtractor.extractDataBool),
186 catchError(err => this.restExtractor.handleError(err))
187 )
188 }
74d63469
GR
189
190 autocomplete (search: string): Observable<string[]> {
191 const url = UserService.BASE_USERS_URL + 'autocomplete'
192 const params = new HttpParams().append('search', search)
193
194 return this.authHttp
195 .get<string[]>(url, { params })
196 .pipe(catchError(res => this.restExtractor.handleError(res)))
197 }
e724fa93 198
1f20622f
C
199 getNewUsername (oldDisplayName: string, newDisplayName: string, currentUsername: string) {
200 // Don't update display name, the user seems to have changed it
201 if (this.displayNameToUsername(oldDisplayName) !== currentUsername) return currentUsername
202
203 return this.displayNameToUsername(newDisplayName)
204 }
205
206 displayNameToUsername (displayName: string) {
207 if (!displayName) return ''
208
209 return displayName
210 .toLowerCase()
211 .replace(/\s/g, '_')
212 .replace(/[^a-z0-9_.]/g, '')
213 }
214
e724fa93
C
215 /* ###### Admin methods ###### */
216
217 addUser (userCreate: UserCreate) {
218 return this.authHttp.post(UserService.BASE_USERS_URL, userCreate)
219 .pipe(
220 map(this.restExtractor.extractDataBool),
221 catchError(err => this.restExtractor.handleError(err))
222 )
223 }
224
225 updateUser (userId: number, userUpdate: UserUpdate) {
226 return this.authHttp.put(UserService.BASE_USERS_URL + userId, userUpdate)
227 .pipe(
228 map(this.restExtractor.extractDataBool),
229 catchError(err => this.restExtractor.handleError(err))
230 )
231 }
232
d3217560 233 updateUsers (users: UserServerModel[], userUpdate: UserUpdate) {
fc2ec87a
JM
234 return from(users)
235 .pipe(
236 concatMap(u => this.authHttp.put(UserService.BASE_USERS_URL + u.id, userUpdate)),
237 toArray(),
238 catchError(err => this.restExtractor.handleError(err))
239 )
240 }
241
218b0874
C
242 getUserWithCache (userId: number) {
243 if (!this.userCache[userId]) {
9df52d66 244 this.userCache[userId] = this.getUser(userId).pipe(shareReplay())
218b0874
C
245 }
246
247 return this.userCache[userId]
248 }
249
76314386
RK
250 getUser (userId: number, withStats = false) {
251 const params = new HttpParams().append('withStats', withStats + '')
252 return this.authHttp.get<UserServerModel>(UserService.BASE_USERS_URL + userId, { params })
e724fa93
C
253 .pipe(catchError(err => this.restExtractor.handleError(err)))
254 }
255
8491293b
RK
256 getUsers (parameters: {
257 pagination: RestPagination
258 sort: SortMeta
259 search?: string
260 }): Observable<ResultList<UserServerModel>> {
261 const { pagination, sort, search } = parameters
262
e724fa93
C
263 let params = new HttpParams()
264 params = this.restService.addRestGetParams(params, pagination, sort)
265
8491293b
RK
266 if (search) {
267 const filters = this.restService.parseQueryStringFilter(search, {
268 blocked: {
269 prefix: 'banned:',
1a7d0887 270 isBoolean: true
8491293b
RK
271 }
272 })
273
274 params = this.restService.addObjectParams(params, filters)
275 }
24b9417c 276
d3217560 277 return this.authHttp.get<ResultList<UserServerModel>>(UserService.BASE_USERS_URL, { params })
e724fa93
C
278 .pipe(
279 map(res => this.restExtractor.convertResultListDateToHuman(res)),
280 map(res => this.restExtractor.applyToResultListData(res, this.formatUser.bind(this))),
281 catchError(err => this.restExtractor.handleError(err))
282 )
283 }
284
d3217560 285 removeUser (usersArg: UserServerModel | UserServerModel[]) {
791645e6
C
286 const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
287
288 return from(users)
289 .pipe(
290 concatMap(u => this.authHttp.delete(UserService.BASE_USERS_URL + u.id)),
291 toArray(),
292 catchError(err => this.restExtractor.handleError(err))
293 )
e724fa93
C
294 }
295
d3217560 296 banUsers (usersArg: UserServerModel | UserServerModel[], reason?: string) {
e724fa93 297 const body = reason ? { reason } : {}
791645e6 298 const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
e724fa93 299
791645e6
C
300 return from(users)
301 .pipe(
302 concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/block', body)),
303 toArray(),
304 catchError(err => this.restExtractor.handleError(err))
305 )
e724fa93
C
306 }
307
d3217560 308 unbanUsers (usersArg: UserServerModel | UserServerModel[]) {
791645e6
C
309 const users = Array.isArray(usersArg) ? usersArg : [ usersArg ]
310
311 return from(users)
312 .pipe(
313 concatMap(u => this.authHttp.post(UserService.BASE_USERS_URL + u.id + '/unblock', {})),
314 toArray(),
315 catchError(err => this.restExtractor.handleError(err))
316 )
e724fa93
C
317 }
318
5c20a455
C
319 getAnonymousOrLoggedUser () {
320 if (!this.authService.isLoggedIn()) {
321 return of(this.getAnonymousUser())
322 }
323
324 return this.authService.userInformationLoaded
325 .pipe(
326 first(),
327 map(() => this.authService.getUser())
328 )
329 }
330
d3217560 331 private formatUser (user: UserServerModel) {
e724fa93
C
332 let videoQuota
333 if (user.videoQuota === -1) {
bc99dfe5 334 videoQuota = '∞'
e724fa93 335 } else {
b4c3c51d 336 videoQuota = getBytes(user.videoQuota, 0)
e724fa93
C
337 }
338
b4c3c51d 339 const videoQuotaUsed = getBytes(user.videoQuotaUsed, 0)
e724fa93 340
4f5d0459
RK
341 let videoQuotaDaily: string
342 let videoQuotaUsedDaily: string
bc99dfe5
RK
343 if (user.videoQuotaDaily === -1) {
344 videoQuotaDaily = '∞'
b4c3c51d 345 videoQuotaUsedDaily = getBytes(0, 0) + ''
bc99dfe5 346 } else {
b4c3c51d
C
347 videoQuotaDaily = getBytes(user.videoQuotaDaily, 0) + ''
348 videoQuotaUsedDaily = getBytes(user.videoQuotaUsedDaily || 0, 0) + ''
bc99dfe5
RK
349 }
350
e724fa93 351 const roleLabels: { [ id in UserRole ]: string } = {
66357162
C
352 [UserRole.USER]: $localize`User`,
353 [UserRole.ADMINISTRATOR]: $localize`Administrator`,
354 [UserRole.MODERATOR]: $localize`Moderator`
e724fa93
C
355 }
356
357 return Object.assign(user, {
358 roleLabel: roleLabels[user.role],
359 videoQuota,
bc99dfe5
RK
360 videoQuotaUsed,
361 rawVideoQuota: user.videoQuota,
362 rawVideoQuotaUsed: user.videoQuotaUsed,
363 videoQuotaDaily,
364 videoQuotaUsedDaily,
365 rawVideoQuotaDaily: user.videoQuotaDaily,
366 rawVideoQuotaUsedDaily: user.videoQuotaUsedDaily
e724fa93
C
367 })
368 }
629d8d6f 369}