diff --git a/backend/src/collections/preferencesdb/preferencedefaults.service.ts b/backend/src/collections/preferencesdb/preferencedefaults.service.ts index 6892684..0c6cb72 100644 --- a/backend/src/collections/preferencesdb/preferencedefaults.service.ts +++ b/backend/src/collections/preferencesdb/preferencedefaults.service.ts @@ -30,7 +30,6 @@ export class PreferenceDefaultsService { if (envSecret) { return envSecret; } else { - console.trace(`what`); this.logger.warn( 'Since no JWT secret was provided, a random one will be generated and saved', ); diff --git a/backend/src/collections/preferencesdb/syspreferencedb.service.ts b/backend/src/collections/preferencesdb/syspreferencedb.service.ts index 15e65bc..e8be4bd 100644 --- a/backend/src/collections/preferencesdb/syspreferencedb.service.ts +++ b/backend/src/collections/preferencesdb/syspreferencedb.service.ts @@ -3,16 +3,20 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DecodedSysPref, PrefValueType, - PrefValueTypeStrings + PrefValueTypeStrings, } from 'picsur-shared/dist/dto/preferences.dto'; import { SysPreference } from 'picsur-shared/dist/dto/syspreferences.dto'; import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types'; import { Repository } from 'typeorm'; import { SysPreferenceList, - SysPreferenceValueTypes + SysPreferenceValueTypes, } from '../../models/dto/syspreferences.dto'; -import { ESysPreferenceBackend, ESysPreferenceSchema } from '../../models/entities/syspreference.entity'; +import { + ESysPreferenceBackend, + ESysPreferenceSchema, +} from '../../models/entities/syspreference.entity'; +import { FallBackMutex } from '../../models/util/FallBackMutex'; import { PreferenceCommonService } from './preferencecommon.service'; import { PreferenceDefaultsService } from './preferencedefaults.service'; @@ -57,29 +61,25 @@ export class SysPreferenceService { public async getPreference(key: string): AsyncFailable { // Validate - let validatedKey = this.prefCommon.validatePrefKey( - key, - SysPreference, - ); + let validatedKey = this.prefCommon.validatePrefKey(key, SysPreference); if (HasFailed(validatedKey)) return validatedKey; - // Fetch - let foundSysPreference: ESysPreferenceBackend | null; + let foundSysPreference: ESysPreferenceBackend; try { - foundSysPreference = await this.sysPreferenceRepository.findOne({ - where: { key: validatedKey }, - cache: 60000, - }); + foundSysPreference = await FallBackMutex( + 'fetchSysPrefrence', + () => + this.sysPreferenceRepository.findOne({ + where: { key: validatedKey as SysPreference }, + cache: 60000, + }), + () => this.saveDefault(validatedKey as SysPreference), + ); } catch (e: any) { this.logger.warn(e); return Fail('Could not get preference'); } - // Fallback - if (!foundSysPreference) { - return this.saveDefault(validatedKey); - } - // Validate const result = ESysPreferenceSchema.safeParse(foundSysPreference); if (!result.success) { diff --git a/backend/src/collections/preferencesdb/usrpreferencedb.service.ts b/backend/src/collections/preferencesdb/usrpreferencedb.service.ts index 1356a6e..bcd535e 100644 --- a/backend/src/collections/preferencesdb/usrpreferencedb.service.ts +++ b/backend/src/collections/preferencesdb/usrpreferencedb.service.ts @@ -3,19 +3,20 @@ import { InjectRepository } from '@nestjs/typeorm'; import { DecodedUsrPref, PrefValueType, - PrefValueTypeStrings + PrefValueTypeStrings, } from 'picsur-shared/dist/dto/preferences.dto'; import { UsrPreference } from 'picsur-shared/dist/dto/usrpreferences.dto'; import { AsyncFailable, Fail, HasFailed } from 'picsur-shared/dist/types'; import { Repository } from 'typeorm'; import { UsrPreferenceList, - UsrPreferenceValueTypes + UsrPreferenceValueTypes, } from '../../models/dto/usrpreferences.dto'; import { EUsrPreferenceBackend, - EUsrPreferenceSchema + EUsrPreferenceSchema, } from '../../models/entities/usrpreference.entity'; +import { FallBackMutex } from '../../models/util/FallBackMutex'; import { PreferenceCommonService } from './preferencecommon.service'; import { PreferenceDefaultsService } from './preferencedefaults.service'; @@ -68,23 +69,22 @@ export class UsrPreferenceService { let validatedKey = this.prefCommon.validatePrefKey(key, UsrPreference); if (HasFailed(validatedKey)) return validatedKey; - // Fetch - let foundUsrPreference: EUsrPreferenceBackend | null; + let foundUsrPreference: EUsrPreferenceBackend; try { - foundUsrPreference = await this.usrPreferenceRepository.findOne({ - where: { key: validatedKey, userId: userid }, - cache: 60000, - }); + foundUsrPreference = await FallBackMutex( + 'fetchUsrPrefrence', + () => + this.usrPreferenceRepository.findOne({ + where: { key: validatedKey as UsrPreference, userId: userid }, + cache: 60000, + }), + () => this.saveDefault(userid, validatedKey as UsrPreference), + ); } catch (e: any) { this.logger.warn(e); return Fail('Could not get preference'); } - // Fallback - if (!foundUsrPreference) { - return this.saveDefault(userid, validatedKey); - } - // Validate const result = EUsrPreferenceSchema.safeParse(foundUsrPreference); if (!result.success) { diff --git a/backend/src/models/util/FallBackMutex.ts b/backend/src/models/util/FallBackMutex.ts index d61db64..e6df309 100644 --- a/backend/src/models/util/FallBackMutex.ts +++ b/backend/src/models/util/FallBackMutex.ts @@ -1,14 +1,24 @@ -import { AsyncFailable, HasSuccess } from 'picsur-shared/dist/types'; +/* +This function is necessary to make sure that a default isnt generated multiple times at the same time. +Doing that will cause errors. + +An example is the jwt secret value, its value is requested aroun 3 times at the same time while starting. +So when the program was started for the first time, each request returned a different secret. + +This function will first try and see if its first function returns a value, if it does, it will return that value. +If not it will execute a fallback function, which usually is a function that populates a default value. +After that is done it will retry the first function again. +*/ const fallBackMap: Record> = {}; export async function FallBackMutex< - MF extends () => AsyncFailable, - FF extends () => AsyncFailable, + MF extends () => Promise, + FF extends () => Promise, O, ->(key: string, mainFunc: MF, fallBackFunc: FF): AsyncFailable { +>(key: string, mainFunc: MF, fallBackFunc: FF): Promise { const try_it = await mainFunc(); - if (HasSuccess(try_it)) return try_it; + if (try_it !== undefined && try_it !== null) return try_it; if (fallBackMap[key] !== undefined) { await fallBackMap[key]; diff --git a/backend/src/routes/api/user/usermanage.controller.ts b/backend/src/routes/api/user/usermanage.controller.ts index 70268a2..840909f 100644 --- a/backend/src/routes/api/user/usermanage.controller.ts +++ b/backend/src/routes/api/user/usermanage.controller.ts @@ -4,7 +4,7 @@ import { Get, InternalServerErrorException, Logger, - Post + Post, } from '@nestjs/common'; import { GetSpecialUsersResponse, @@ -17,7 +17,7 @@ import { UserListRequest, UserListResponse, UserUpdateRequest, - UserUpdateResponse + UserUpdateResponse, } from 'picsur-shared/dist/dto/api/usermanage.dto'; import { HasFailed } from 'picsur-shared/dist/types'; import { UsersService } from '../../../collections/userdb/userdb.service'; @@ -27,7 +27,7 @@ import { Permission } from '../../../models/dto/permissions.dto'; import { ImmutableUsersList, LockedLoginUsersList, - UndeletableUsersList + UndeletableUsersList, } from '../../../models/dto/specialusers.dto'; import { EUserBackend2EUser } from '../../../models/transformers/user.transformer';