diff --git a/backend/src/collections/syspreferencesdb/syspreferencedb.service.ts b/backend/src/collections/syspreferencesdb/syspreferencedb.service.ts index 8f0198e..00e3545 100644 --- a/backend/src/collections/syspreferencesdb/syspreferencedb.service.ts +++ b/backend/src/collections/syspreferencesdb/syspreferencedb.service.ts @@ -1,10 +1,9 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { PrefValueType, PrefValueTypeStrings } from 'picsur-shared/dist/dto/preferences.dto'; import { - InternalSysprefRepresentation, - SysPreference, - SysPrefValueType, - SysPrefValueTypeStrings + InternalSysPrefRepresentation, + SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto'; import { AsyncFailable, @@ -33,8 +32,8 @@ export class SysPreferenceService { public async setPreference( key: string, - value: SysPrefValueType, - ): AsyncFailable { + value: PrefValueType, + ): AsyncFailable { // Validate let sysPreference = await this.validatePref(key, value); if (HasFailed(sysPreference)) return sysPreference; @@ -61,7 +60,7 @@ export class SysPreferenceService { public async getPreference( key: string, - ): AsyncFailable { + ): AsyncFailable { // Validate let validatedKey = this.validatePrefKey(key); if (HasFailed(validatedKey)) return validatedKey; @@ -108,8 +107,8 @@ export class SysPreferenceService { private async getPreferencePinned( key: string, - type: SysPrefValueTypeStrings, - ): AsyncFailable { + type: PrefValueTypeStrings, + ): AsyncFailable { let pref = await this.getPreference(key); if (HasFailed(pref)) return pref; if (pref.type !== type) return Fail('Invalid preference type'); @@ -118,7 +117,7 @@ export class SysPreferenceService { } public async getAllPreferences(): AsyncFailable< - InternalSysprefRepresentation[] + InternalSysPrefRepresentation[] > { // TODO: We are fetching each value invidually, we should fetch all at once let internalSysPrefs = await Promise.all( @@ -128,21 +127,21 @@ export class SysPreferenceService { return Fail('Could not get all preferences'); } - return internalSysPrefs as InternalSysprefRepresentation[]; + return internalSysPrefs as InternalSysPrefRepresentation[]; } // Private private async saveDefault( key: SysPreference, // Force enum here because we dont validate - ): AsyncFailable { + ): AsyncFailable { return this.setPreference(key, this.defaultsService.defaults[key]()); } // This converts the raw string representation of the value to the correct type private retrieveConvertedValue( preference: ESysPreferenceBackend, - ): Failable { + ): Failable { const key = this.validatePrefKey(preference.key); if (HasFailed(key)) return key; @@ -173,7 +172,7 @@ export class SysPreferenceService { private async validatePref( key: string, - value: SysPrefValueType, + value: PrefValueType, ): AsyncFailable { const validatedKey = this.validatePrefKey(key); if (HasFailed(validatedKey)) return validatedKey; @@ -204,7 +203,7 @@ export class SysPreferenceService { private validatePrefValue( // Key is required, because the type of the value depends on the key key: SysPreference, - value: SysPrefValueType, + value: PrefValueType, ): Failable { const expectedType = SysPreferenceValueTypes[key]; diff --git a/backend/src/collections/syspreferencesdb/syspreferencedefaults.service.ts b/backend/src/collections/syspreferencesdb/syspreferencedefaults.service.ts index 0a75fb5..c2ebb02 100644 --- a/backend/src/collections/syspreferencesdb/syspreferencedefaults.service.ts +++ b/backend/src/collections/syspreferencesdb/syspreferencedefaults.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; +import { PrefValueType } from 'picsur-shared/dist/dto/preferences.dto'; import { - SysPreference, - SysPrefValueType + SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto'; import { generateRandomString } from 'picsur-shared/dist/util/random'; import { EarlyJwtConfigService } from '../../config/early/earlyjwt.config.service'; @@ -16,7 +16,7 @@ export class SysPreferenceDefaultsService { constructor(private jwtConfigService: EarlyJwtConfigService) {} public readonly defaults: { - [key in SysPreference]: () => SysPrefValueType; + [key in SysPreference]: () => PrefValueType; } = { [SysPreference.JwtSecret]: () => { const envSecret = this.jwtConfigService.getJwtSecret(); diff --git a/backend/src/models/dto/syspreferences.dto.ts b/backend/src/models/dto/syspreferences.dto.ts index 045ee18..867ecec 100644 --- a/backend/src/models/dto/syspreferences.dto.ts +++ b/backend/src/models/dto/syspreferences.dto.ts @@ -1,14 +1,12 @@ -import { - SysPreference, - SysPrefValueTypeStrings -} from 'picsur-shared/dist/dto/syspreferences.dto'; +import { PrefValueTypeStrings } from 'picsur-shared/dist/dto/preferences.dto'; +import { SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto'; export type SysPreferences = SysPreference[]; export const SysPreferenceList: string[] = Object.values(SysPreference); // Syspref Value types export const SysPreferenceValueTypes: { - [key in SysPreference]: SysPrefValueTypeStrings; + [key in SysPreference]: PrefValueTypeStrings; } = { [SysPreference.JwtSecret]: 'string', [SysPreference.JwtExpiresIn]: 'string', diff --git a/backend/src/models/entities/preference.entity.ts b/backend/src/models/entities/preference.entity.ts new file mode 100644 index 0000000..32e1476 --- /dev/null +++ b/backend/src/models/entities/preference.entity.ts @@ -0,0 +1,18 @@ +import { EUsrPreference } from 'picsur-shared/dist/entities/usrpreference'; +import { Column, Index, PrimaryGeneratedColumn } from 'typeorm'; + +export class EUsrPreferenceBackend extends EUsrPreference { + @PrimaryGeneratedColumn() + override id?: number; + + @Index() + @Column({ nullable: false, unique: true }) + override key: string; + + @Column({ nullable: false }) + override value: string; + + @Index() + @Column({ nullable: false }) + override userId: number; +} diff --git a/backend/src/routes/api/pref/pref.controller.ts b/backend/src/routes/api/pref/pref.controller.ts index c2bbc26..2e36fd6 100644 --- a/backend/src/routes/api/pref/pref.controller.ts +++ b/backend/src/routes/api/pref/pref.controller.ts @@ -11,7 +11,7 @@ import { GetSyspreferenceResponse, MultipleSysPreferencesResponse, UpdateSysPreferenceRequest, UpdateSysPreferenceResponse -} from 'picsur-shared/dist/dto/api/pref.dto'; +} from 'picsur-shared/dist/dto/api/syspref.dto'; import { HasFailed } from 'picsur-shared/dist/types'; import { SysPreferenceService } from '../../../collections/syspreferencesdb/syspreferencedb.service'; import { RequiredPermissions } from '../../../decorators/permissions.decorator'; diff --git a/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts b/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts index 15439fc..e8f69ae 100644 --- a/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts +++ b/frontend/src/app/routes/settings/syspref/settings-syspref.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/pref.dto'; +import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/syspref.dto'; import { Observable } from 'rxjs'; import { SysprefService as SysPrefService } from 'src/app/services/api/syspref.service'; diff --git a/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts b/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts index dad5c8a..0633c0d 100644 --- a/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts +++ b/frontend/src/app/routes/settings/syspref/syspref-option/settings-syspref-option.component.ts @@ -1,10 +1,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe-decorator'; -import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/pref.dto'; -import { - SysPreference, - SysPrefValueType -} from 'picsur-shared/dist/dto/syspreferences.dto'; +import { SysPreferenceBaseResponse } from 'picsur-shared/dist/dto/api/syspref.dto'; +import { PrefValueType } from 'picsur-shared/dist/dto/preferences.dto'; +import { SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto'; import { HasFailed } from 'picsur-shared/dist/types'; import { Subject, throttleTime } from 'rxjs'; import { SysPreferenceFriendlyNames } from 'src/app/i18n/syspref.i18n'; @@ -20,7 +18,7 @@ import { UtilService } from 'src/app/util/util.service'; export class SettingsSysprefOptionComponent implements OnInit { @Input() pref: SysPreferenceBaseResponse; - private updateSubject = new Subject(); + private updateSubject = new Subject(); constructor( private sysprefService: SysprefService, @@ -71,7 +69,7 @@ export class SettingsSysprefOptionComponent implements OnInit { this.update((e.target as HTMLInputElement).valueAsNumber); } - private async updatePreference(value: SysPrefValueType) { + private async updatePreference(value: PrefValueType) { const result = await this.sysprefService.setPreference( this.pref.key, value diff --git a/frontend/src/app/services/api/info.service.ts b/frontend/src/app/services/api/info.service.ts index 068e0a4..2d02897 100644 --- a/frontend/src/app/services/api/info.service.ts +++ b/frontend/src/app/services/api/info.service.ts @@ -28,11 +28,12 @@ export class InfoService { private infoSubject = new BehaviorSubject(new ServerInfo()); constructor(private api: ApiService, private utilService: UtilService) { - this.pollInfo() - .then(() => { - this.checkCompatibility(); - }) - .catch(this.logger.error); + this.init().catch(this.logger.error); + } + + private async init() { + await this.pollInfo(); + this.checkCompatibility(); } public async pollInfo(): AsyncFailable { diff --git a/frontend/src/app/services/api/syspref.service.ts b/frontend/src/app/services/api/syspref.service.ts index d464413..8057082 100644 --- a/frontend/src/app/services/api/syspref.service.ts +++ b/frontend/src/app/services/api/syspref.service.ts @@ -6,9 +6,9 @@ import { SysPreferenceBaseResponse, UpdateSysPreferenceRequest, UpdateSysPreferenceResponse -} from 'picsur-shared/dist/dto/api/pref.dto'; +} from 'picsur-shared/dist/dto/api/syspref.dto'; import { Permission } from 'picsur-shared/dist/dto/permissions.dto'; -import { SysPrefValueType } from 'picsur-shared/dist/dto/syspreferences.dto'; +import { PrefValueType } from 'picsur-shared/dist/dto/preferences.dto'; import { AsyncFailable, Fail, HasFailed, Map } from 'picsur-shared/dist/types'; import { BehaviorSubject } from 'rxjs'; import { SnackBarType } from 'src/app/models/dto/snack-bar-type.dto'; @@ -67,7 +67,7 @@ export class SysprefService { ); return Map(response, (pref) => { - this.sysprefObservable.next(pref.preferences) + this.sysprefObservable.next(pref.preferences); return pref.preferences; }); } @@ -89,7 +89,7 @@ export class SysprefService { public async setPreference( key: string, - value: SysPrefValueType + value: PrefValueType ): AsyncFailable { if (!this.hasPermission) return Fail('You do not have permission to edit system preferences'); diff --git a/shared/src/dto/api/pref.dto.ts b/shared/src/dto/api/syspref.dto.ts similarity index 81% rename from shared/src/dto/api/pref.dto.ts rename to shared/src/dto/api/syspref.dto.ts index d6c0b6f..f6b899a 100644 --- a/shared/src/dto/api/pref.dto.ts +++ b/shared/src/dto/api/syspref.dto.ts @@ -4,7 +4,7 @@ import { } from 'class-validator'; import { IsPosInt } from '../../validators/positive-int.validator'; import { IsSysPrefValue } from '../../validators/syspref.validator'; -import { SysPrefValueType, SysPrefValueTypes, SysPrefValueTypeStrings } from '../syspreferences.dto'; +import { PrefValueType, PrefValueTypes, PrefValueTypeStrings } from '../preferences.dto'; export class SysPreferenceBaseResponse { @IsNotEmpty() @@ -13,11 +13,11 @@ export class SysPreferenceBaseResponse { @IsNotEmpty() @IsSysPrefValue() - value: SysPrefValueType; + value: PrefValueType; @IsNotEmpty() - @IsEnum(SysPrefValueTypes) - type: SysPrefValueTypeStrings; + @IsEnum(PrefValueTypes) + type: PrefValueTypeStrings; } // Get Syspreference @@ -40,7 +40,7 @@ export class MultipleSysPreferencesResponse { export class UpdateSysPreferenceRequest { @IsNotEmpty() @IsSysPrefValue() - value: SysPrefValueType; + value: PrefValueType; } export class UpdateSysPreferenceResponse extends SysPreferenceBaseResponse {} diff --git a/shared/src/dto/preferences.dto.ts b/shared/src/dto/preferences.dto.ts new file mode 100644 index 0000000..ea84f14 --- /dev/null +++ b/shared/src/dto/preferences.dto.ts @@ -0,0 +1,4 @@ +// Variable value type +export type PrefValueType = string | number | boolean; +export type PrefValueTypeStrings = 'string' | 'number' | 'boolean'; +export const PrefValueTypes = ['string', 'number', 'boolean']; diff --git a/shared/src/dto/syspreferences.dto.ts b/shared/src/dto/syspreferences.dto.ts index 7166883..53e8443 100644 --- a/shared/src/dto/syspreferences.dto.ts +++ b/shared/src/dto/syspreferences.dto.ts @@ -1,3 +1,5 @@ +import { PrefValueType, PrefValueTypeStrings } from './preferences.dto'; + // This enum is only here to make accessing the values easier, and type checking in the backend export enum SysPreference { JwtSecret = 'jwt_secret', @@ -8,14 +10,9 @@ export enum SysPreference { TestBoolean = 'test_boolean', } -// Variable value type -export type SysPrefValueType = string | number | boolean; -export type SysPrefValueTypeStrings = 'string' | 'number' | 'boolean'; -export const SysPrefValueTypes = ['string', 'number', 'boolean']; - // Interfaces -export interface InternalSysprefRepresentation { +export interface InternalSysPrefRepresentation { key: string; - value: SysPrefValueType; - type: SysPrefValueTypeStrings; + value: PrefValueType; + type: PrefValueTypeStrings; } diff --git a/shared/src/dto/usrpreferences.dto.ts b/shared/src/dto/usrpreferences.dto.ts new file mode 100644 index 0000000..9fa47cc --- /dev/null +++ b/shared/src/dto/usrpreferences.dto.ts @@ -0,0 +1,19 @@ +import { PrefValueType, PrefValueTypeStrings } from './preferences.dto'; + +// This enum is only here to make accessing the values easier, and type checking in the backend +export enum UsrPreference { + JwtSecret = 'jwt_secret', + JwtExpiresIn = 'jwt_expires_in', + BCryptStrength = 'bcrypt_strength', + TestString = 'test_string', + TestNumber = 'test_number', + TestBoolean = 'test_boolean', +} + +// Interfaces +export interface InternalUsrPrefRepresentation { + key: string; + value: PrefValueType; + type: PrefValueTypeStrings; + user: number; +} diff --git a/shared/src/entities/usrpreference.ts b/shared/src/entities/usrpreference.ts new file mode 100644 index 0000000..63e30f3 --- /dev/null +++ b/shared/src/entities/usrpreference.ts @@ -0,0 +1,20 @@ +import { IsDefined, IsNotEmpty, IsString } from 'class-validator'; +import { EntityID } from '../validators/entity-id.validator'; +import { IsPosInt } from '../validators/positive-int.validator'; + +export class EUsrPreference { + @EntityID() + id?: number; + + @IsNotEmpty() + @IsString() + key: string; + + @IsNotEmpty() + @IsString() + value: string; + + @IsDefined() + @IsPosInt() + userId: number; +} diff --git a/shared/src/validators/syspref.validator.ts b/shared/src/validators/syspref.validator.ts index 213e52a..a9016d8 100644 --- a/shared/src/validators/syspref.validator.ts +++ b/shared/src/validators/syspref.validator.ts @@ -1,9 +1,9 @@ import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator'; -import { SysPrefValueTypes } from '../dto/syspreferences.dto'; +import { PrefValueTypes } from '../dto/preferences.dto'; export function isSysPrefValue(value: any, args: ValidationArguments) { const type = typeof value; - return SysPrefValueTypes.includes(type); + return PrefValueTypes.includes(type); } export function IsSysPrefValue(validationOptions?: ValidationOptions) {