mirror of
https://github.com/CaramelFur/Picsur.git
synced 2026-01-24 16:09:05 +01:00
refactor services
This commit is contained in:
@@ -6,10 +6,10 @@ import {
|
||||
Router,
|
||||
RouterStateSnapshot
|
||||
} from '@angular/router';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { isPermissionsArray } from 'picsur-shared/dist/validators/permissions.validator';
|
||||
import { PRouteData } from '../models/dto/picsur-routes.dto';
|
||||
import { PermissionService } from '../services/api/permission.service';
|
||||
import { StaticInfoService } from '../services/api/static-info.service';
|
||||
import { Logger } from '../services/logger/logger.service';
|
||||
|
||||
@Injectable({
|
||||
@@ -21,18 +21,14 @@ export class PermissionGuard implements CanActivate, CanActivateChild {
|
||||
|
||||
constructor(
|
||||
private permissionService: PermissionService,
|
||||
private staticInfo: StaticInfoService,
|
||||
private router: Router
|
||||
) {
|
||||
this.setupAllPermissions().catch(this.logger.error);
|
||||
}
|
||||
|
||||
private async setupAllPermissions() {
|
||||
const permissions = await this.permissionService.fetchAllPermission();
|
||||
if (HasFailed(permissions)) {
|
||||
return this.logger.error(`Could not fetch all permissions`);
|
||||
}
|
||||
|
||||
this.allPermissionsArray = permissions;
|
||||
this.allPermissionsArray = await this.staticInfo.getAllPermissions();
|
||||
}
|
||||
|
||||
async canActivateChild(
|
||||
@@ -61,7 +57,7 @@ export class PermissionGuard implements CanActivate, CanActivateChild {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ourPermissions = await this.permissionService.loadedSnapshot();
|
||||
const ourPermissions = await this.permissionService.getLoadedSnapshot();
|
||||
const weHavePermission = requiredPermissions.every((permission) =>
|
||||
ourPermissions.includes(permission)
|
||||
);
|
||||
|
||||
@@ -3,8 +3,8 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
|
||||
import { UpdateRoleControl } from 'src/app/models/forms/updaterole.control';
|
||||
import { PermissionService } from 'src/app/services/api/permission.service';
|
||||
import { RolesService } from 'src/app/services/api/roles.service';
|
||||
import { StaticInfoService } from 'src/app/services/api/static-info.service';
|
||||
import { UtilService } from 'src/app/util/util.service';
|
||||
|
||||
enum EditMode {
|
||||
@@ -34,7 +34,7 @@ export class SettingsRolesEditComponent implements OnInit {
|
||||
private router: Router,
|
||||
private utilService: UtilService,
|
||||
private rolesService: RolesService,
|
||||
private permissionsService: PermissionService
|
||||
private staticInfo: StaticInfoService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -65,16 +65,7 @@ export class SettingsRolesEditComponent implements OnInit {
|
||||
|
||||
private async initPermissions() {
|
||||
// Get a list of all permissions so that we can select them
|
||||
const allPermissions = await this.permissionsService.fetchAllPermission();
|
||||
if (HasFailed(allPermissions)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to fetch permissions',
|
||||
SnackBarType.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.allPermissions = allPermissions;
|
||||
this.allPermissions = await this.staticInfo.getAllPermissions();
|
||||
}
|
||||
|
||||
async updateRole() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
|
||||
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
|
||||
import { RolesService } from 'src/app/services/api/roles.service';
|
||||
import { StaticInfoService } from 'src/app/services/api/static-info.service';
|
||||
import { UtilService } from 'src/app/util/util.service';
|
||||
|
||||
@Component({
|
||||
@@ -34,6 +35,7 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
|
||||
constructor(
|
||||
private rolesService: RolesService,
|
||||
private utilService: UtilService,
|
||||
private staticInfo: StaticInfoService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
@@ -101,23 +103,15 @@ export class SettingsRolesComponent implements OnInit, AfterViewInit {
|
||||
private async loadRoles() {
|
||||
const [roles, specialRoles] = await Promise.all([
|
||||
this.rolesService.getRoles(),
|
||||
this.rolesService.getSpecialRoles(),
|
||||
this.staticInfo.getSpecialRoles(),
|
||||
]);
|
||||
this.UndeletableRolesList = specialRoles.UndeletableRoles;
|
||||
this.ImmutableRolesList = specialRoles.ImmutableRoles;
|
||||
|
||||
if (HasFailed(roles)) {
|
||||
this.utilService.showSnackBar('Failed to load roles', SnackBarType.Error);
|
||||
return;
|
||||
}
|
||||
this.dataSource.data = roles;
|
||||
|
||||
if (HasFailed(specialRoles)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to load special roles',
|
||||
SnackBarType.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.UndeletableRolesList = specialRoles.UndeletableRoles;
|
||||
this.ImmutableRolesList = specialRoles.ImmutableRoles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/pref.dto';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SysprefService as SysPrefService } from 'src/app/services/api/syspref.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './settings-syspref.component.html',
|
||||
})
|
||||
export class SettingsSysprefComponent {
|
||||
preferences: Subject<SysPreferenceBaseResponse[]>;
|
||||
preferences: Observable<SysPreferenceBaseResponse[]>;
|
||||
|
||||
constructor(sysprefService: SysPrefService) {
|
||||
this.preferences = sysprefService.live;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { UIFriendlyPermissions } from 'src/app/i18n/permissions.i18n';
|
||||
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
|
||||
import { UpdateUserControl } from 'src/app/models/forms/updateuser.control';
|
||||
import { RolesService } from 'src/app/services/api/roles.service';
|
||||
import { StaticInfoService } from 'src/app/services/api/static-info.service';
|
||||
import { UserManageService } from 'src/app/services/api/usermanage.service';
|
||||
import { UtilService } from 'src/app/util/util.service';
|
||||
|
||||
@@ -43,7 +44,8 @@ export class SettingsUsersEditComponent implements OnInit {
|
||||
private router: Router,
|
||||
private userManageService: UserManageService,
|
||||
private utilService: UtilService,
|
||||
private rolesService: RolesService
|
||||
private rolesService: RolesService,
|
||||
private staticInfo: StaticInfoService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -58,14 +60,7 @@ export class SettingsUsersEditComponent implements OnInit {
|
||||
const username = this.route.snapshot.paramMap.get('username');
|
||||
|
||||
// Get special roles
|
||||
const SpecialRoles = await this.rolesService.getSpecialRoles();
|
||||
if (HasFailed(SpecialRoles)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to get special roles',
|
||||
SnackBarType.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
const SpecialRoles = await this.staticInfo.getSpecialRoles();
|
||||
this.soulBoundRoles = SpecialRoles.SoulBoundRoles;
|
||||
|
||||
// Check if edit or add
|
||||
@@ -93,15 +88,7 @@ export class SettingsUsersEditComponent implements OnInit {
|
||||
}
|
||||
|
||||
private async initImmutableUsersList() {
|
||||
const SpecialUsers = await this.userManageService.getSpecialUsers();
|
||||
if (HasFailed(SpecialUsers)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to get special users',
|
||||
SnackBarType.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const SpecialUsers = await this.staticInfo.getSpecialUsers();
|
||||
this.ImmutableUsersList = SpecialUsers.ImmutableUsersList;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { BehaviorSubject, Subject, throttleTime } from 'rxjs';
|
||||
import { SnackBarType } from "src/app/models/dto/snack-bar-type.dto";
|
||||
import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto';
|
||||
import { StaticInfoService } from 'src/app/services/api/static-info.service';
|
||||
import { UserManageService } from 'src/app/services/api/usermanage.service';
|
||||
import { UtilService } from 'src/app/util/util.service';
|
||||
|
||||
@@ -29,6 +30,7 @@ export class SettingsUsersComponent implements OnInit {
|
||||
constructor(
|
||||
private userManageService: UserManageService,
|
||||
private utilService: UtilService,
|
||||
private staticInfo: StaticInfoService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
@@ -129,15 +131,7 @@ export class SettingsUsersComponent implements OnInit {
|
||||
}
|
||||
|
||||
private async initSpecialUsers() {
|
||||
const specialUsers = await this.userManageService.getSpecialUsers();
|
||||
if (HasFailed(specialUsers)) {
|
||||
this.utilService.showSnackBar(
|
||||
'Failed to fetch special users',
|
||||
SnackBarType.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const specialUsers = await this.staticInfo.getSpecialUsers();
|
||||
this.UndeletableUsersList = specialUsers.UndeletableUsersList;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ClassConstructor, plainToClass } from 'class-transformer';
|
||||
import { ApiResponse, ApiSuccessResponse } from 'picsur-shared/dist/dto/api/api.dto';
|
||||
import {
|
||||
ApiResponse,
|
||||
ApiSuccessResponse
|
||||
} from 'picsur-shared/dist/dto/api/api.dto';
|
||||
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { strictValidate } from 'picsur-shared/dist/util/validate';
|
||||
import { Subject } from 'rxjs';
|
||||
import { ApiError } from 'src/app/models/dto/api-error.dto';
|
||||
import { MultiPartRequest } from '../../models/dto/multi-part-request.dto';
|
||||
import { KeyService } from './key.service';
|
||||
import { KeyService } from '../storage/key.service';
|
||||
|
||||
/*
|
||||
Proud of this, it works so smoooth
|
||||
*/
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
|
||||
import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/info.dto';
|
||||
import { UserMePermissionsResponse } from 'picsur-shared/dist/dto/api/user.dto';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { BehaviorSubject, filter, map, Observable, take } from 'rxjs';
|
||||
import { ApiService } from './api.service';
|
||||
import { StaticInfoService } from './static-info.service';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PermissionService {
|
||||
private readonly logger = console;
|
||||
|
||||
constructor(private userService: UserService, private api: ApiService) {
|
||||
this.onUser();
|
||||
}
|
||||
private allPermissions: string[] = [];
|
||||
private permissionsSubject = new BehaviorSubject<string[] | null>(null);
|
||||
|
||||
// TODO: add full permission list as default
|
||||
public get live(): Observable<string[]> {
|
||||
return this.permissionsSubject.pipe(
|
||||
map((permissions) => permissions ?? [])
|
||||
map((permissions) => permissions ?? this.allPermissions)
|
||||
);
|
||||
}
|
||||
|
||||
public get snapshot(): string[] {
|
||||
return this.permissionsSubject.getValue() ?? [];
|
||||
return this.permissionsSubject.getValue() ?? this.allPermissions;
|
||||
}
|
||||
|
||||
// This will not be optimistic, it will instead wait for correct data
|
||||
public loadedSnapshot(): Promise<string[]> {
|
||||
public getLoadedSnapshot(): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
const filtered = this.permissionsSubject.pipe(
|
||||
filter((permissions) => permissions !== null),
|
||||
@@ -37,38 +35,42 @@ export class PermissionService {
|
||||
});
|
||||
}
|
||||
|
||||
private permissionsSubject = new BehaviorSubject<string[] | null>(null);
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private api: ApiService,
|
||||
private staticInfo: StaticInfoService
|
||||
) {
|
||||
this.subscribeUser();
|
||||
this.loadAllPermissions().catch(this.logger.error);
|
||||
}
|
||||
|
||||
private async loadAllPermissions() {
|
||||
this.allPermissions = await this.staticInfo.getAllPermissions();
|
||||
|
||||
if (this.snapshot === null) {
|
||||
this.permissionsSubject.next(null);
|
||||
}
|
||||
}
|
||||
|
||||
@AutoUnsubscribe()
|
||||
private onUser() {
|
||||
private subscribeUser() {
|
||||
return this.userService.live.subscribe(async (user) => {
|
||||
const permissions = await this.fetchPermissions();
|
||||
const permissions = await this.updatePermissions();
|
||||
if (HasFailed(permissions)) {
|
||||
this.logger.warn(permissions.getReason());
|
||||
return;
|
||||
}
|
||||
this.permissionsSubject.next(permissions);
|
||||
});
|
||||
}
|
||||
|
||||
private async fetchPermissions(): AsyncFailable<string[]> {
|
||||
private async updatePermissions(): AsyncFailable<true> {
|
||||
const got = await this.api.get(
|
||||
UserMePermissionsResponse,
|
||||
'/api/user/me/permissions'
|
||||
);
|
||||
if (HasFailed(got)) return got;
|
||||
|
||||
return got.permissions;
|
||||
}
|
||||
|
||||
public async fetchAllPermission(): AsyncFailable<string[]> {
|
||||
const result = await this.api.get(
|
||||
AllPermissionsResponse,
|
||||
'/api/info/permissions'
|
||||
);
|
||||
|
||||
if (HasFailed(result)) return result;
|
||||
|
||||
return result.Permissions;
|
||||
this.permissionsSubject.next(got.permissions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,21 @@ import {
|
||||
RoleInfoResponse,
|
||||
RoleListResponse,
|
||||
RoleUpdateRequest,
|
||||
RoleUpdateResponse,
|
||||
SpecialRolesResponse
|
||||
RoleUpdateResponse
|
||||
} from 'picsur-shared/dist/dto/api/roles.dto';
|
||||
import { ERole } from 'picsur-shared/dist/entities/role.entity';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { RoleModel } from 'src/app/models/forms-dto/role.dto';
|
||||
import { ApiService } from './api.service';
|
||||
import { CacheService } from './cache.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RolesService {
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private cacheService: CacheService
|
||||
) {}
|
||||
constructor(private api: ApiService) {}
|
||||
|
||||
public async getRoles(): AsyncFailable<ERole[]> {
|
||||
const result = await this.apiService.get(
|
||||
const result = await this.api.get(
|
||||
RoleListResponse,
|
||||
'/api/roles/list'
|
||||
);
|
||||
@@ -44,7 +39,7 @@ export class RolesService {
|
||||
name,
|
||||
};
|
||||
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
RoleInfoRequest,
|
||||
RoleInfoResponse,
|
||||
'/api/roles/info',
|
||||
@@ -55,7 +50,7 @@ export class RolesService {
|
||||
}
|
||||
|
||||
public async createRole(role: RoleModel): AsyncFailable<ERole> {
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
RoleCreateRequest,
|
||||
RoleCreateResponse,
|
||||
'/api/roles/create',
|
||||
@@ -66,7 +61,7 @@ export class RolesService {
|
||||
}
|
||||
|
||||
public async updateRole(role: RoleModel): AsyncFailable<ERole> {
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
RoleUpdateRequest,
|
||||
RoleUpdateResponse,
|
||||
'/api/roles/update',
|
||||
@@ -81,7 +76,7 @@ export class RolesService {
|
||||
name,
|
||||
};
|
||||
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
RoleDeleteRequest,
|
||||
RoleDeleteResponse,
|
||||
'/api/roles/delete',
|
||||
@@ -90,38 +85,4 @@ export class RolesService {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async getSpecialRoles(): AsyncFailable<SpecialRolesResponse> {
|
||||
const cached = this.cacheService.get<SpecialRolesResponse>('specialRoles');
|
||||
if (cached !== null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const result = await this.apiService.get(
|
||||
SpecialRolesResponse,
|
||||
'/api/roles/special'
|
||||
);
|
||||
|
||||
if (HasFailed(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
this.cacheService.set('specialRoles', result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async getSpecialRolesOptimistic(): Promise<SpecialRolesResponse> {
|
||||
const result = await this.getSpecialRoles();
|
||||
if (HasFailed(result)) {
|
||||
return {
|
||||
DefaultRoles: [],
|
||||
ImmutableRoles: [],
|
||||
SoulBoundRoles: [],
|
||||
UndeletableRoles: [],
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
55
frontend/src/app/services/api/static-info.service.ts
Normal file
55
frontend/src/app/services/api/static-info.service.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/info.dto';
|
||||
import { SpecialRolesResponse } from 'picsur-shared/dist/dto/api/roles.dto';
|
||||
import { GetSpecialUsersResponse } from 'picsur-shared/dist/dto/api/usermanage.dto';
|
||||
import { HasFailed } from 'picsur-shared/dist/types';
|
||||
import { CacheService } from '../storage/cache.service';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class StaticInfoService {
|
||||
constructor(private api: ApiService, private cache: CacheService) {}
|
||||
|
||||
public async getSpecialRoles(): Promise<SpecialRolesResponse> {
|
||||
return this.cache.getFallback(
|
||||
'specialRoles',
|
||||
{
|
||||
DefaultRoles: [],
|
||||
ImmutableRoles: [],
|
||||
SoulBoundRoles: [],
|
||||
UndeletableRoles: [],
|
||||
},
|
||||
() => this.api.get(SpecialRolesResponse, '/api/roles/special')
|
||||
);
|
||||
}
|
||||
|
||||
public async getSpecialUsers(): Promise<GetSpecialUsersResponse> {
|
||||
return this.cache.getFallback(
|
||||
'specialUsers',
|
||||
{
|
||||
ImmutableUsersList: [],
|
||||
LockedLoginUsersList: [],
|
||||
UndeletableUsersList: [],
|
||||
},
|
||||
() => this.api.get(GetSpecialUsersResponse, '/api/user/special')
|
||||
);
|
||||
}
|
||||
|
||||
public async getAllPermissions(): Promise<string[]> {
|
||||
return this.cache.getFallback(
|
||||
'allPermissions',
|
||||
// "error" works fine, it is not a valid permission, but will display where necessary and will otherwise be ignored
|
||||
['error'] as string[],
|
||||
async () => {
|
||||
const res = await this.api.get(
|
||||
AllPermissionsResponse,
|
||||
'/api/info/permissions'
|
||||
);
|
||||
if (HasFailed(res)) return res;
|
||||
return res.Permissions;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -22,18 +22,18 @@ import { PermissionService } from './permission.service';
|
||||
export class SysprefService {
|
||||
private hasPermission = false;
|
||||
|
||||
private sysprefObservable = new BehaviorSubject<SysPreferenceBaseResponse[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
public get snapshot() {
|
||||
return this.sysprefObservable.getValue();
|
||||
}
|
||||
|
||||
public get live() {
|
||||
return this.sysprefObservable;
|
||||
return this.sysprefObservable.asObservable();
|
||||
}
|
||||
|
||||
private sysprefObservable = new BehaviorSubject<SysPreferenceBaseResponse[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private permissionsService: PermissionService,
|
||||
@@ -62,10 +62,7 @@ export class SysprefService {
|
||||
MultipleSysPreferencesResponse,
|
||||
'/api/pref/sys'
|
||||
);
|
||||
if (HasFailed(response)) {
|
||||
this.sync();
|
||||
return response;
|
||||
}
|
||||
if (HasFailed(response)) return response;
|
||||
|
||||
this.sysprefObservable.next(response.preferences);
|
||||
return response.preferences;
|
||||
@@ -81,12 +78,8 @@ export class SysprefService {
|
||||
GetSyspreferenceResponse,
|
||||
`/api/pref/sys/${key}`
|
||||
);
|
||||
if (HasFailed(response)) {
|
||||
this.sync();
|
||||
return response;
|
||||
}
|
||||
|
||||
this.updatePrefArray(response);
|
||||
if (!HasFailed(response)) this.updatePrefArray(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -103,12 +96,8 @@ export class SysprefService {
|
||||
`/api/pref/sys/${key}`,
|
||||
{ value }
|
||||
);
|
||||
if (HasFailed(response)) {
|
||||
this.sync();
|
||||
return response;
|
||||
}
|
||||
|
||||
this.updatePrefArray(response);
|
||||
if (!HasFailed(response)) this.updatePrefArray(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -126,16 +115,11 @@ export class SysprefService {
|
||||
}
|
||||
}
|
||||
|
||||
private sync() {
|
||||
this.sysprefObservable.next(
|
||||
([] as SysPreferenceBaseResponse[]).concat(this.snapshot)
|
||||
);
|
||||
}
|
||||
|
||||
private flush() {
|
||||
this.sysprefObservable.next([]);
|
||||
}
|
||||
|
||||
// We want to flush on logout, because the syspreferences can contain sensitive information
|
||||
@AutoUnsubscribe()
|
||||
private subscribePermissions() {
|
||||
return this.permissionsService.live.subscribe((permissions) => {
|
||||
|
||||
@@ -13,17 +13,18 @@ import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { strictValidate } from 'picsur-shared/dist/util/validate';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { KeyService } from '../storage/key.service';
|
||||
import { ApiService } from './api.service';
|
||||
import { KeyService } from './key.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
private readonly logger = console;
|
||||
private userSubject = new BehaviorSubject<EUser | null>(null);
|
||||
|
||||
public get live() {
|
||||
return this.userSubject;
|
||||
return this.userSubject.asObservable();
|
||||
}
|
||||
|
||||
public get snapshot() {
|
||||
@@ -34,12 +35,33 @@ export class UserService {
|
||||
return this.userSubject.getValue() !== null;
|
||||
}
|
||||
|
||||
private userSubject = new BehaviorSubject<EUser | null>(null);
|
||||
|
||||
constructor(private api: ApiService, private key: KeyService) {
|
||||
this.init().catch(this.logger.error);
|
||||
}
|
||||
|
||||
private async init() {
|
||||
const apikey = await this.key.get();
|
||||
if (!apikey) return;
|
||||
|
||||
const user = await this.extractUser(apikey);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
await this.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
this.userSubject.next(user);
|
||||
|
||||
const fetchedUser = await this.fetchUser();
|
||||
if (HasFailed(fetchedUser)) {
|
||||
this.logger.warn(fetchedUser.getReason());
|
||||
await this.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
this.userSubject.next(fetchedUser);
|
||||
}
|
||||
|
||||
public async login(username: string, password: string): AsyncFailable<EUser> {
|
||||
const request: UserLoginRequest = {
|
||||
username,
|
||||
@@ -52,8 +74,9 @@ export class UserService {
|
||||
'/api/user/login',
|
||||
request
|
||||
);
|
||||
|
||||
if (HasFailed(response)) return response;
|
||||
|
||||
// Set the key so the apiservice can use it
|
||||
this.key.set(response.jwt_token);
|
||||
|
||||
const user = await this.fetchUser();
|
||||
@@ -83,10 +106,11 @@ export class UserService {
|
||||
}
|
||||
|
||||
public async logout(): AsyncFailable<EUser> {
|
||||
console.log('logging out');
|
||||
const value = this.userSubject.getValue();
|
||||
const value = this.snapshot;
|
||||
|
||||
this.key.clear();
|
||||
this.userSubject.next(null);
|
||||
|
||||
if (value === null) {
|
||||
return Fail('Not logged in');
|
||||
} else {
|
||||
@@ -94,29 +118,7 @@ export class UserService {
|
||||
}
|
||||
}
|
||||
|
||||
private async init() {
|
||||
const apikey = await this.key.get();
|
||||
if (!apikey) return;
|
||||
|
||||
const user = await this.extractUser(apikey);
|
||||
if (HasFailed(user)) {
|
||||
this.logger.warn(user.getReason());
|
||||
await this.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
this.userSubject.next(user);
|
||||
|
||||
const fetchedUser = await this.fetchUser();
|
||||
if (HasFailed(fetchedUser)) {
|
||||
this.logger.warn(fetchedUser.getReason());
|
||||
await this.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
this.userSubject.next(fetchedUser);
|
||||
}
|
||||
|
||||
// This extracts the available userdata from the jwt token
|
||||
private async extractUser(token: string): AsyncFailable<EUser> {
|
||||
let decoded: any;
|
||||
try {
|
||||
@@ -135,6 +137,7 @@ export class UserService {
|
||||
return jwtData.user;
|
||||
}
|
||||
|
||||
// This actually fetches up to date information from the server
|
||||
private async fetchUser(): AsyncFailable<EUser> {
|
||||
const got = await this.api.get(UserMeResponse, '/api/user/me');
|
||||
if (HasFailed(got)) return got;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
GetSpecialUsersResponse,
|
||||
UserCreateRequest,
|
||||
UserCreateResponse,
|
||||
UserDeleteRequest,
|
||||
@@ -16,23 +15,19 @@ import { EUser } from 'picsur-shared/dist/entities/user.entity';
|
||||
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
|
||||
import { FullUserModel } from 'src/app/models/forms-dto/fulluser.dto';
|
||||
import { ApiService } from './api.service';
|
||||
import { CacheService } from './cache.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserManageService {
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private cacheService: CacheService
|
||||
) {}
|
||||
constructor(private api: ApiService) {}
|
||||
|
||||
public async getUser(username: string): AsyncFailable<EUser> {
|
||||
const body = {
|
||||
username,
|
||||
};
|
||||
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
UserInfoRequest,
|
||||
UserInfoResponse,
|
||||
'api/user/info',
|
||||
@@ -48,7 +43,7 @@ export class UserManageService {
|
||||
page,
|
||||
};
|
||||
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
UserListRequest,
|
||||
UserListResponse,
|
||||
'/api/user/list',
|
||||
@@ -63,7 +58,7 @@ export class UserManageService {
|
||||
}
|
||||
|
||||
public async createUser(user: FullUserModel): AsyncFailable<EUser> {
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
UserCreateRequest,
|
||||
UserCreateResponse,
|
||||
'/api/user/create',
|
||||
@@ -74,7 +69,7 @@ export class UserManageService {
|
||||
}
|
||||
|
||||
public async updateUser(user: FullUserModel): AsyncFailable<EUser> {
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
UserUpdateRequest,
|
||||
UserUpdateResponse,
|
||||
'/api/user/update',
|
||||
@@ -89,7 +84,7 @@ export class UserManageService {
|
||||
username,
|
||||
};
|
||||
|
||||
const result = await this.apiService.post(
|
||||
const result = await this.api.post(
|
||||
UserDeleteRequest,
|
||||
UserDeleteResponse,
|
||||
'/api/user/delete',
|
||||
@@ -98,38 +93,4 @@ export class UserManageService {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async getSpecialUsers(): AsyncFailable<GetSpecialUsersResponse> {
|
||||
const cached =
|
||||
this.cacheService.get<GetSpecialUsersResponse>('specialUsers');
|
||||
if (cached !== null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const result = await this.apiService.get(
|
||||
GetSpecialUsersResponse,
|
||||
'/api/user/special'
|
||||
);
|
||||
|
||||
if (HasFailed(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
this.cacheService.set('specialUsers', result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async getSpecialUsersOptimistic(): Promise<GetSpecialUsersResponse> {
|
||||
const result = await this.getSpecialUsers();
|
||||
if (HasFailed(result)) {
|
||||
return {
|
||||
ImmutableUsersList: [],
|
||||
LockedLoginUsersList: [],
|
||||
UndeletableUsersList: [],
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ import { Injectable, Injector } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
|
||||
import { PRouteData } from 'src/app/models/dto/picsur-routes.dto';
|
||||
|
||||
// This service makes sure that any sidebar components are getting dependency injection
|
||||
// from their correct module. Instead of getting it from the module where it is being
|
||||
// sent via the portal.
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'any',
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AsyncFailable, Failable, HasFailed } from 'picsur-shared/dist/types';
|
||||
|
||||
interface dataWrapper<T> {
|
||||
data: T;
|
||||
@@ -21,6 +22,15 @@ export class CacheService {
|
||||
}
|
||||
}
|
||||
|
||||
public set<T>(key: string, value: T): void {
|
||||
const data: dataWrapper<T> = {
|
||||
data: value,
|
||||
expires: Date.now() + this.cacheExpiresMS,
|
||||
};
|
||||
|
||||
this.storage.setItem(key, JSON.stringify(data));
|
||||
}
|
||||
|
||||
public get<T>(key: string): T | null {
|
||||
try {
|
||||
const data: dataWrapper<T> = JSON.parse(this.storage.getItem(key) ?? '');
|
||||
@@ -33,12 +43,26 @@ export class CacheService {
|
||||
}
|
||||
}
|
||||
|
||||
public set<T>(key: string, value: T): void {
|
||||
const data: dataWrapper<T> = {
|
||||
data: value,
|
||||
expires: Date.now() + this.cacheExpiresMS,
|
||||
};
|
||||
public async getFallback<T>(
|
||||
key: string,
|
||||
finalFallback: T,
|
||||
...fallbacks: Array<(key: string) => AsyncFailable<T> | Failable<T>>
|
||||
): Promise<T> {
|
||||
const cached = this.get<T>(key);
|
||||
if (cached !== null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
this.storage.setItem(key, JSON.stringify(data));
|
||||
for (const fallback of fallbacks) {
|
||||
const result = await fallback(key);
|
||||
if (HasFailed(result)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.set(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return finalFallback;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user