mvoe syspref to backend

This commit is contained in:
rubikscraft
2022-03-27 22:33:13 +02:00
parent 2c150c3027
commit ac98db10df
17 changed files with 105 additions and 114 deletions

View File

@@ -3,8 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { plainToClass } from 'class-transformer'; import { plainToClass } from 'class-transformer';
import { import {
InternalSysprefRepresentation, InternalSysprefRepresentation,
SysPreferences, SysPreference,
SysPreferenceValueTypes,
SysPrefValueType SysPrefValueType
} from 'picsur-shared/dist/dto/syspreferences.dto'; } from 'picsur-shared/dist/dto/syspreferences.dto';
import { import {
@@ -15,6 +14,10 @@ import {
} from 'picsur-shared/dist/types'; } from 'picsur-shared/dist/types';
import { strictValidate } from 'picsur-shared/dist/util/validate'; import { strictValidate } from 'picsur-shared/dist/util/validate';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import {
SysPreferenceList,
SysPreferenceValueTypes
} from '../../models/dto/syspreferences.dto';
import { ESysPreferenceBackend } from '../../models/entities/syspreference.entity'; import { ESysPreferenceBackend } from '../../models/entities/syspreference.entity';
import { SysPreferenceDefaultsService } from './syspreferencedefaults.service'; import { SysPreferenceDefaultsService } from './syspreferencedefaults.service';
@@ -29,7 +32,7 @@ export class SysPreferenceService {
) {} ) {}
public async setPreference( public async setPreference(
key: SysPreferences, key: string,
value: SysPrefValueType, value: SysPrefValueType,
): AsyncFailable<InternalSysprefRepresentation> { ): AsyncFailable<InternalSysprefRepresentation> {
// Validate // Validate
@@ -50,12 +53,13 @@ export class SysPreferenceService {
return { return {
key: sysPreference.key, key: sysPreference.key,
value, value,
type: SysPreferenceValueTypes[key], // key has to be valid here, we validated it
type: SysPreferenceValueTypes[key as SysPreference],
}; };
} }
public async getPreference( public async getPreference(
key: SysPreferences, key: string,
): AsyncFailable<InternalSysprefRepresentation> { ): AsyncFailable<InternalSysprefRepresentation> {
// Validate // Validate
let validatedKey = this.validatePrefKey(key); let validatedKey = this.validatePrefKey(key);
@@ -92,7 +96,7 @@ export class SysPreferenceService {
return this.retrieveConvertedValue(foundSysPreference); return this.retrieveConvertedValue(foundSysPreference);
} }
public async getStringPreference(key: SysPreferences): AsyncFailable<string> { public async getStringPreference(key: string): AsyncFailable<string> {
const pref = await this.getPreference(key); const pref = await this.getPreference(key);
if (HasFailed(pref)) return pref; if (HasFailed(pref)) return pref;
if (pref.type !== 'string') return Fail('Invalid preference type'); if (pref.type !== 'string') return Fail('Invalid preference type');
@@ -100,7 +104,7 @@ export class SysPreferenceService {
return pref.value as string; return pref.value as string;
} }
public async getNumberPreference(key: SysPreferences): AsyncFailable<number> { public async getNumberPreference(key: string): AsyncFailable<number> {
const pref = await this.getPreference(key); const pref = await this.getPreference(key);
if (HasFailed(pref)) return pref; if (HasFailed(pref)) return pref;
if (pref.type !== 'number') return Fail('Invalid preference type'); if (pref.type !== 'number') return Fail('Invalid preference type');
@@ -108,9 +112,7 @@ export class SysPreferenceService {
return pref.value as number; return pref.value as number;
} }
public async getBooleanPreference( public async getBooleanPreference(key: string): AsyncFailable<boolean> {
key: SysPreferences,
): AsyncFailable<boolean> {
const pref = await this.getPreference(key); const pref = await this.getPreference(key);
if (HasFailed(pref)) return pref; if (HasFailed(pref)) return pref;
if (pref.type !== 'boolean') return Fail('Invalid preference type'); if (pref.type !== 'boolean') return Fail('Invalid preference type');
@@ -122,7 +124,7 @@ export class SysPreferenceService {
InternalSysprefRepresentation[] InternalSysprefRepresentation[]
> { > {
let internalSysPrefs = await Promise.all( let internalSysPrefs = await Promise.all(
SysPreferences.map((key) => this.getPreference(key as SysPreferences)), SysPreferenceList.map((key) => this.getPreference(key)),
); );
if (internalSysPrefs.some((pref) => HasFailed(pref))) { if (internalSysPrefs.some((pref) => HasFailed(pref))) {
return Fail('Could not get all preferences'); return Fail('Could not get all preferences');
@@ -133,7 +135,7 @@ export class SysPreferenceService {
// Private // Private
private async saveDefault( private async saveDefault(
key: SysPreferences, key: SysPreference, // Force enum here because we dont validate
): AsyncFailable<InternalSysprefRepresentation> { ): AsyncFailable<InternalSysprefRepresentation> {
return this.setPreference(key, this.defaultsService.defaults[key]()); return this.setPreference(key, this.defaultsService.defaults[key]());
} }
@@ -141,7 +143,10 @@ export class SysPreferenceService {
private retrieveConvertedValue( private retrieveConvertedValue(
preference: ESysPreferenceBackend, preference: ESysPreferenceBackend,
): Failable<InternalSysprefRepresentation> { ): Failable<InternalSysprefRepresentation> {
const type = SysPreferenceValueTypes[preference.key]; const key = this.validatePrefKey(preference.key);
if (HasFailed(key)) return key;
const type = SysPreferenceValueTypes[key];
switch (type) { switch (type) {
case 'string': case 'string':
return { return {
@@ -190,16 +195,16 @@ export class SysPreferenceService {
return verifySysPreference; return verifySysPreference;
} }
private validatePrefKey(key: string): Failable<SysPreferences> { private validatePrefKey(key: string): Failable<SysPreference> {
if (!SysPreferences.includes(key)) { if (!SysPreferenceList.includes(key)) {
return Fail('Invalid preference key'); return Fail('Invalid preference key');
} }
return key as SysPreferences; return key as SysPreference;
} }
private validatePrefValue( private validatePrefValue(
key: SysPreferences, key: SysPreference,
value: SysPrefValueType, value: SysPrefValueType,
): Failable<string> { ): Failable<string> {
const expectedType = SysPreferenceValueTypes[key]; const expectedType = SysPreferenceValueTypes[key];

View File

@@ -1,6 +1,6 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { import {
SysPreferences, SysPreference,
SysPrefValueType SysPrefValueType
} from 'picsur-shared/dist/dto/syspreferences.dto'; } from 'picsur-shared/dist/dto/syspreferences.dto';
import { generateRandomString } from 'picsur-shared/dist/util/random'; import { generateRandomString } from 'picsur-shared/dist/util/random';
@@ -13,9 +13,9 @@ export class SysPreferenceDefaultsService {
constructor(private jwtConfigService: EnvJwtConfigService) {} constructor(private jwtConfigService: EnvJwtConfigService) {}
public readonly defaults: { public readonly defaults: {
[key in SysPreferences]: () => SysPrefValueType; [key in SysPreference]: () => SysPrefValueType;
} = { } = {
jwt_secret: () => { [SysPreference.JwtSecret]: () => {
const envSecret = this.jwtConfigService.getJwtSecret(); const envSecret = this.jwtConfigService.getJwtSecret();
if (envSecret) { if (envSecret) {
return envSecret; return envSecret;
@@ -26,10 +26,10 @@ export class SysPreferenceDefaultsService {
return generateRandomString(64); return generateRandomString(64);
} }
}, },
jwt_expires_in: () => this.jwtConfigService.getJwtExpiresIn() ?? '7d', [SysPreference.JwtExpiresIn]: () => this.jwtConfigService.getJwtExpiresIn() ?? '7d',
test_string: () => 'test_string', [SysPreference.TestString]: () => 'test_string',
test_number: () => 123, [SysPreference.TestNumber]: () => 123,
test_boolean: () => true, [SysPreference.TestBoolean]: () => true,
}; };
} }

View File

@@ -6,5 +6,4 @@ export { Permission } from 'picsur-shared/dist/dto/permissions.dto';
// Derivatives // Derivatives
export const PermissionsList: Permission[] = Object.values(Permission); export const PermissionsList: Permission[] = Object.values(Permission);
export type Permissions = Permission[]; export type Permissions = Permission[];

View File

@@ -0,0 +1,19 @@
import {
SysPreference,
SysPrefValueTypeStrings
} from 'picsur-shared/dist/dto/syspreferences.dto';
export type SysPreferences = SysPreference[];
export const SysPreferenceList: string[] = Object.values(SysPreference);
// Syspref Values
export const SysPreferenceValueTypes: {
[key in SysPreference]: SysPrefValueTypeStrings;
} = {
[SysPreference.JwtSecret]: 'string',
[SysPreference.JwtExpiresIn]: 'string',
[SysPreference.TestString]: 'string',
[SysPreference.TestNumber]: 'number',
[SysPreference.TestBoolean]: 'boolean',
};

View File

@@ -1,4 +1,3 @@
import { SysPreferences } from 'picsur-shared/dist/dto/syspreferences.dto';
import { ESysPreference } from 'picsur-shared/dist/entities/syspreference.entity'; import { ESysPreference } from 'picsur-shared/dist/entities/syspreference.entity';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@@ -8,7 +7,7 @@ export class ESysPreferenceBackend extends ESysPreference {
override id?: number; override id?: number;
@Column({ nullable: false, unique: true }) @Column({ nullable: false, unique: true })
override key: SysPreferences; override key: string;
@Column({ nullable: false }) @Column({ nullable: false })
override value: string; override value: string;

View File

@@ -1,7 +1,6 @@
import { Controller, Get } from '@nestjs/common'; import { Controller, Get } from '@nestjs/common';
import { plainToClass } from 'class-transformer'; import { plainToClass } from 'class-transformer';
import { InfoResponse } from 'picsur-shared/dist/dto/api/info.dto'; import { AllPermissionsResponse, InfoResponse } from 'picsur-shared/dist/dto/api/info.dto';
import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/roles.dto';
import { HostConfigService } from '../../../config/host.config.service'; import { HostConfigService } from '../../../config/host.config.service';
import { NoPermissions } from '../../../decorators/permissions.decorator'; import { NoPermissions } from '../../../decorators/permissions.decorator';
import { PermissionsList } from '../../../models/dto/permissions.dto'; import { PermissionsList } from '../../../models/dto/permissions.dto';

View File

@@ -15,7 +15,6 @@ import {
UpdateSysPreferenceRequest, UpdateSysPreferenceRequest,
UpdateSysPreferenceResponse UpdateSysPreferenceResponse
} from 'picsur-shared/dist/dto/api/pref.dto'; } from 'picsur-shared/dist/dto/api/pref.dto';
import { SysPreferences } from 'picsur-shared/dist/dto/syspreferences.dto';
import { HasFailed } from 'picsur-shared/dist/types'; import { HasFailed } from 'picsur-shared/dist/types';
import { SysPreferenceService } from '../../../collections/syspreferencesdb/syspreferencedb.service'; import { SysPreferenceService } from '../../../collections/syspreferencesdb/syspreferencedb.service';
import { RequiredPermissions } from '../../../decorators/permissions.decorator'; import { RequiredPermissions } from '../../../decorators/permissions.decorator';
@@ -50,7 +49,7 @@ export class PrefController {
async getSysPref( async getSysPref(
@Param('key') key: string, @Param('key') key: string,
): Promise<GetSyspreferenceResponse> { ): Promise<GetSyspreferenceResponse> {
const pref = await this.prefService.getPreference(key as SysPreferences); const pref = await this.prefService.getPreference(key);
if (HasFailed(pref)) { if (HasFailed(pref)) {
this.logger.warn(pref.getReason()); this.logger.warn(pref.getReason());
throw new InternalServerErrorException('Could not get preference'); throw new InternalServerErrorException('Could not get preference');
@@ -66,20 +65,18 @@ export class PrefController {
): Promise<UpdateSysPreferenceResponse> { ): Promise<UpdateSysPreferenceResponse> {
const value = body.value; const value = body.value;
const pref = await this.prefService.setPreference( const pref = await this.prefService.setPreference(key, value);
key as SysPreferences,
value,
);
if (HasFailed(pref)) { if (HasFailed(pref)) {
this.logger.warn(pref.getReason()); this.logger.warn(pref.getReason());
throw new InternalServerErrorException('Could not set preference'); throw new InternalServerErrorException('Could not set preference');
} }
const returned = new UpdateSysPreferenceResponse(); const returned = {
returned.key = key as SysPreferences; key,
returned.value = pref.value; value: pref.value,
returned.type = pref.type; type: pref.type,
};
return returned; return plainToClass(UpdateSysPreferenceResponse, returned);
} }
} }

View File

@@ -0,0 +1,11 @@
import { SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto';
export const SysPreferenceFriendlyNames: {
[key in SysPreference]: string;
} = {
[SysPreference.JwtSecret]: 'JWT Secret',
[SysPreference.JwtExpiresIn]: 'JWT Expiry Time',
[SysPreference.TestString]: 'Test String',
[SysPreference.TestNumber]: 'Test Number',
[SysPreference.TestBoolean]: 'Test Boolean',
};

View File

@@ -1,12 +1,10 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/pref.dto'; import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/pref.dto';
import { import { SysPreference, SysPrefValueType } from 'picsur-shared/dist/dto/syspreferences.dto';
SysPreferenceFriendlyNames,
SysPrefValueType
} from 'picsur-shared/dist/dto/syspreferences.dto';
import { HasFailed } from 'picsur-shared/dist/types'; import { HasFailed } from 'picsur-shared/dist/types';
import { Subject, throttleTime } from 'rxjs'; import { Subject, throttleTime } from 'rxjs';
import { SysPreferenceFriendlyNames } from 'src/app/i18n/syspref.i18n';
import { SnackBarType } from 'src/app/models/snack-bar-type'; import { SnackBarType } from 'src/app/models/snack-bar-type';
import { SysprefService } from 'src/app/services/api/syspref.service'; import { SysprefService } from 'src/app/services/api/syspref.service';
import { UtilService } from 'src/app/util/util.service'; import { UtilService } from 'src/app/util/util.service';
@@ -31,7 +29,7 @@ export class SettingsSysprefOptionComponent implements OnInit {
} }
get name(): string { get name(): string {
return SysPreferenceFriendlyNames[this.pref.key]; return SysPreferenceFriendlyNames[this.pref.key as SysPreference] ?? this.pref.key;
} }
get valString(): string { get valString(): string {

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator';
import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/roles.dto'; import { AllPermissionsResponse } from 'picsur-shared/dist/dto/api/info.dto';
import { UserMePermissionsResponse } from 'picsur-shared/dist/dto/api/user.dto'; import { UserMePermissionsResponse } from 'picsur-shared/dist/dto/api/user.dto';
import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types'; import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject, filter, map, Observable, take } from 'rxjs'; import { BehaviorSubject, filter, map, Observable, take } from 'rxjs';

View File

@@ -8,15 +8,13 @@ import {
UpdateSysPreferenceResponse UpdateSysPreferenceResponse
} from 'picsur-shared/dist/dto/api/pref.dto'; } from 'picsur-shared/dist/dto/api/pref.dto';
import { Permission } from 'picsur-shared/dist/dto/permissions.dto'; import { Permission } from 'picsur-shared/dist/dto/permissions.dto';
import { import { SysPrefValueType } from 'picsur-shared/dist/dto/syspreferences.dto';
SysPreferences,
SysPrefValueType
} from 'picsur-shared/dist/dto/syspreferences.dto';
import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types'; import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
import { PermissionService } from './permission.service'; import { PermissionService } from './permission.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
@@ -60,7 +58,7 @@ export class SysprefService {
} }
public async getPreference( public async getPreference(
key: SysPreferences key: string,
): AsyncFailable<GetSyspreferenceResponse> { ): AsyncFailable<GetSyspreferenceResponse> {
if (!this.hasPermission) if (!this.hasPermission)
return Fail('You do not have permission to edit system preferences'); return Fail('You do not have permission to edit system preferences');
@@ -79,7 +77,7 @@ export class SysprefService {
} }
public async setPreference( public async setPreference(
key: SysPreferences, key: string,
value: SysPrefValueType value: SysPrefValueType
): AsyncFailable<UpdateSysPreferenceResponse> { ): AsyncFailable<UpdateSysPreferenceResponse> {
if (!this.hasPermission) if (!this.hasPermission)

View File

@@ -1,4 +1,5 @@
import { IsBoolean, IsDefined, IsSemVer, IsString } from 'class-validator'; import { IsBoolean, IsDefined, IsSemVer, IsString } from 'class-validator';
import { IsStringList } from '../../validators/string-list.validator';
export class InfoResponse { export class InfoResponse {
@IsBoolean() @IsBoolean()
@@ -14,3 +15,10 @@ export class InfoResponse {
@IsSemVer() @IsSemVer()
version: string; version: string;
} }
// AllPermissions
export class AllPermissionsResponse {
@IsDefined()
@IsStringList()
Permissions: string[];
}

View File

@@ -1,20 +1,15 @@
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { import {
IsArray, IsEnum, IsNotEmpty, ValidateNested IsArray, IsEnum, IsNotEmpty, IsString, ValidateNested
} from 'class-validator'; } from 'class-validator';
import { IsPosInt } from '../../validators/positive-int.validator'; import { IsPosInt } from '../../validators/positive-int.validator';
import { IsSysPrefValue } from '../../validators/syspref.validator'; import { IsSysPrefValue } from '../../validators/syspref.validator';
import { import { SysPrefValueType, SysPrefValueTypes, SysPrefValueTypeStrings } from '../syspreferences.dto';
SysPreferences,
SysPrefValueType,
SysPrefValueTypes,
SysPrefValueTypeStrings
} from '../syspreferences.dto';
export class SysPreferenceBaseResponse { export class SysPreferenceBaseResponse {
@IsNotEmpty() @IsNotEmpty()
@IsEnum(SysPreferences) @IsString()
key: SysPreferences; key: string;
@IsNotEmpty() @IsNotEmpty()
@IsSysPrefValue() @IsSysPrefValue()

View File

@@ -54,10 +54,3 @@ export class SpecialRolesResponse {
@IsStringList() @IsStringList()
DefaultRoles: string[]; DefaultRoles: string[];
} }
// AllPermissions
export class AllPermissionsResponse {
@IsDefined()
@IsStringList()
Permissions: string[];
}

View File

@@ -1,4 +1,7 @@
// Only add no rename // Only add no rename
// This enum only makes permissions easier to program,
// This does not have to be a complete list of all permissions
// -> the frontend and backend can be somewhat out of sync
export enum Permission { export enum Permission {
ImageView = 'image-view', ImageView = 'image-view',
ImageUpload = 'image-upload', ImageUpload = 'image-upload',

View File

@@ -1,51 +1,19 @@
import tuple from '../types/tuple'; export enum SysPreference {
JwtSecret = 'jwt_secret',
// Syspref keys JwtExpiresIn = 'jwt_expires_in',
TestString = 'test_string',
const SysPreferencesTuple = tuple( TestNumber = 'test_number',
'jwt_secret', TestBoolean = 'test_boolean',
'jwt_expires_in', }
'test_string',
'test_number',
'test_boolean',
);
export const SysPreferences: string[] = SysPreferencesTuple;
export type SysPreferences = typeof SysPreferencesTuple[number];
export const SysPreferenceFriendlyNames: {
[key in SysPreferences]: string;
} = {
jwt_secret: 'JWT Secret',
jwt_expires_in: 'JWT Expiry Time',
test_string: 'Test String',
test_number: 'Test Number',
test_boolean: 'Test Boolean',
};
// Syspref Values
// Variable value type
export type SysPrefValueType = string | number | boolean; export type SysPrefValueType = string | number | boolean;
export type SysPrefValueTypeStrings = 'string' | 'number' | 'boolean'; export type SysPrefValueTypeStrings = 'string' | 'number' | 'boolean';
export const SysPrefValueTypes = ['string', 'number', 'boolean']; export const SysPrefValueTypes = ['string', 'number', 'boolean'];
export const SysPreferenceValueTypes: { // Interfaces
[key in SysPreferences]: SysPrefValueTypeStrings;
} = {
jwt_secret: 'string',
jwt_expires_in: 'string',
test_string: 'string',
test_number: 'number',
test_boolean: 'boolean',
};
// Validators
// interfaces
export interface InternalSysprefRepresentation { export interface InternalSysprefRepresentation {
key: SysPreferences; key: string;
value: SysPrefValueType; value: SysPrefValueType;
type: SysPrefValueTypeStrings; type: SysPrefValueTypeStrings;
} }

View File

@@ -1,5 +1,4 @@
import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; import { IsNotEmpty, IsString } from 'class-validator';
import { SysPreferences } from '../dto/syspreferences.dto';
import { EntityID } from '../validators/entity-id.validator'; import { EntityID } from '../validators/entity-id.validator';
export class ESysPreference { export class ESysPreference {
@@ -7,8 +6,8 @@ export class ESysPreference {
id?: number; id?: number;
@IsNotEmpty() @IsNotEmpty()
@IsEnum(SysPreferences) @IsString()
key: SysPreferences; key: string;
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()