Files
Picsur/frontend/src/app/api/user.service.ts

124 lines
3.0 KiB
TypeScript
Raw Normal View History

2022-03-06 12:34:33 +01:00
import { Injectable } from '@angular/core';
2022-03-03 13:38:39 +01:00
import { plainToClass } from 'class-transformer';
2022-03-06 12:34:33 +01:00
import { validate } from 'class-validator';
import jwt_decode from 'jwt-decode';
2022-03-03 13:38:39 +01:00
import {
2022-03-12 20:15:48 +01:00
UserLoginRequest,
UserLoginResponse,
UserMeResponse
} from 'picsur-shared/dist/dto/api/user.dto';
import { JwtDataDto } from 'picsur-shared/dist/dto/jwt.dto';
2022-03-06 12:34:33 +01:00
import { EUser } from 'picsur-shared/dist/entities/user.entity';
2022-03-12 20:15:48 +01:00
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
2022-03-06 12:34:33 +01:00
import { BehaviorSubject } from 'rxjs';
2022-03-03 13:38:39 +01:00
import { ApiService } from './api.service';
import { KeyService } from './key.service';
@Injectable({
providedIn: 'root',
})
export class UserService {
2022-03-12 15:10:22 +01:00
private readonly logger = console;
2022-03-03 15:44:22 +01:00
public get liveUser() {
2022-03-03 13:38:39 +01:00
return this.userSubject;
}
2022-03-03 15:44:22 +01:00
public get user() {
return this.userSubject.getValue();
}
public get isLoggedIn() {
return this.userSubject.getValue() !== null;
}
2022-03-03 13:38:39 +01:00
private userSubject = new BehaviorSubject<EUser | null>(null);
constructor(private api: ApiService, private key: KeyService) {
2022-03-12 20:15:48 +01:00
this.init().catch(this.logger.error);
2022-03-03 13:38:39 +01:00
}
public async login(username: string, password: string): AsyncFailable<EUser> {
2022-03-12 20:15:48 +01:00
const request: UserLoginRequest = {
2022-03-03 13:38:39 +01:00
username,
password,
};
const response = await this.api.post(
2022-03-12 20:15:48 +01:00
UserLoginRequest,
UserLoginResponse,
'/api/user/login',
2022-03-03 13:38:39 +01:00
request
);
if (HasFailed(response)) return response;
this.key.set(response.jwt_token);
const user = await this.fetchUser();
if (HasFailed(user)) return user;
this.userSubject.next(user);
return user;
}
public async logout(): AsyncFailable<EUser> {
const value = this.userSubject.getValue();
this.key.clear();
this.userSubject.next(null);
if (value === null) {
return Fail('Not logged in');
} else {
return value;
}
}
private async init() {
const apikey = await this.key.get();
if (!apikey) return;
const user = await this.extractUser(apikey);
if (HasFailed(user)) {
2022-03-12 15:10:22 +01:00
this.logger.warn(user.getReason());
2022-03-03 15:44:22 +01:00
await this.logout();
2022-03-03 13:38:39 +01:00
return;
}
this.userSubject.next(user);
const fetchedUser = await this.fetchUser();
if (HasFailed(fetchedUser)) {
2022-03-12 15:10:22 +01:00
this.logger.warn(fetchedUser.getReason());
2022-03-03 15:44:22 +01:00
await this.logout();
2022-03-03 13:38:39 +01:00
return;
}
this.userSubject.next(fetchedUser);
}
private async extractUser(token: string): AsyncFailable<EUser> {
let decoded: any;
try {
decoded = jwt_decode(token);
} catch (e) {
return Fail('Invalid token');
}
const jwtData = plainToClass(JwtDataDto, decoded);
const errors = await validate(jwtData);
if (errors.length > 0) {
2022-03-12 15:10:22 +01:00
this.logger.warn(errors);
2022-03-03 13:38:39 +01:00
return Fail('Invalid token data');
}
return jwtData.user;
}
private async fetchUser(): AsyncFailable<EUser> {
2022-03-12 20:15:48 +01:00
const got = await this.api.get(UserMeResponse, '/api/user/me');
2022-03-03 13:38:39 +01:00
if (HasFailed(got)) return got;
this.key.set(got.newJwtToken);
return got.user;
}
}