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