mirror of
https://github.com/standardnotes/server
synced 2026-07-14 09:01:33 -04:00
193 lines
7.2 KiB
TypeScript
193 lines
7.2 KiB
TypeScript
import { inject, injectable } from 'inversify'
|
|
import { Logger } from 'winston'
|
|
|
|
import TYPES from '../../Bootstrap/Types'
|
|
import { UserSubscription } from '../Subscription/UserSubscription'
|
|
|
|
import { SettingDecrypterInterface } from './SettingDecrypterInterface'
|
|
import { SettingDescription } from './SettingDescription'
|
|
import { SubscriptionSettingServiceInterface } from './SubscriptionSettingServiceInterface'
|
|
import { CreateOrReplaceSubscriptionSettingDTO } from './CreateOrReplaceSubscriptionSettingDTO'
|
|
import { CreateOrReplaceSubscriptionSettingResponse } from './CreateOrReplaceSubscriptionSettingResponse'
|
|
import { SubscriptionSetting } from './SubscriptionSetting'
|
|
import { FindSubscriptionSettingDTO } from './FindSubscriptionSettingDTO'
|
|
import { SubscriptionSettingRepositoryInterface } from './SubscriptionSettingRepositoryInterface'
|
|
import { SettingFactoryInterface } from './SettingFactoryInterface'
|
|
import { SubscriptionSettingsAssociationServiceInterface } from './SubscriptionSettingsAssociationServiceInterface'
|
|
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
|
import { SettingName } from '@standardnotes/settings'
|
|
import { SettingInterpreterInterface } from './SettingInterpreterInterface'
|
|
|
|
@injectable()
|
|
export class SubscriptionSettingService implements SubscriptionSettingServiceInterface {
|
|
constructor(
|
|
@inject(TYPES.Auth_SettingFactory) private factory: SettingFactoryInterface,
|
|
@inject(TYPES.Auth_SubscriptionSettingRepository)
|
|
private subscriptionSettingRepository: SubscriptionSettingRepositoryInterface,
|
|
@inject(TYPES.Auth_SubscriptionSettingsAssociationService)
|
|
private subscriptionSettingAssociationService: SubscriptionSettingsAssociationServiceInterface,
|
|
@inject(TYPES.Auth_SettingInterpreter) private settingInterpreter: SettingInterpreterInterface,
|
|
@inject(TYPES.Auth_SettingDecrypter) private settingDecrypter: SettingDecrypterInterface,
|
|
@inject(TYPES.Auth_UserSubscriptionRepository)
|
|
private userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
|
@inject(TYPES.Auth_Logger) private logger: Logger,
|
|
) {}
|
|
|
|
async applyDefaultSubscriptionSettingsForSubscription(
|
|
userSubscription: UserSubscription,
|
|
overrides?: Map<string, string>,
|
|
): Promise<void> {
|
|
const defaultSettingsWithValues =
|
|
await this.subscriptionSettingAssociationService.getDefaultSettingsAndValuesForSubscriptionName(
|
|
userSubscription.planName,
|
|
)
|
|
if (defaultSettingsWithValues === undefined) {
|
|
this.logger.warn(`Could not find settings for subscription: ${userSubscription.planName}`)
|
|
|
|
return
|
|
}
|
|
|
|
const user = await userSubscription.user
|
|
|
|
for (const settingNameString of defaultSettingsWithValues.keys()) {
|
|
const settingNameOrError = SettingName.create(settingNameString)
|
|
if (settingNameOrError.isFailed()) {
|
|
throw new Error(settingNameOrError.getError())
|
|
}
|
|
const settingName = settingNameOrError.getValue()
|
|
if (!settingName.isASubscriptionSetting()) {
|
|
throw new Error(`Setting ${settingName.value} is not a subscription setting`)
|
|
}
|
|
|
|
const setting = defaultSettingsWithValues.get(settingName.value) as SettingDescription
|
|
if (!setting.replaceable) {
|
|
const existingSetting = await this.findPreviousSubscriptionSetting(
|
|
settingName,
|
|
userSubscription.uuid,
|
|
user.uuid,
|
|
)
|
|
if (existingSetting !== null) {
|
|
existingSetting.userSubscription = Promise.resolve(userSubscription)
|
|
await this.subscriptionSettingRepository.save(existingSetting)
|
|
|
|
continue
|
|
}
|
|
}
|
|
|
|
let unencryptedValue = setting.value
|
|
if (overrides && overrides.has(settingName.value)) {
|
|
unencryptedValue = overrides.get(settingName.value) as string
|
|
}
|
|
|
|
await this.createOrReplace({
|
|
userSubscription,
|
|
user,
|
|
props: {
|
|
name: settingName.value,
|
|
unencryptedValue,
|
|
serverEncryptionVersion: setting.serverEncryptionVersion,
|
|
sensitive: setting.sensitive,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
async findSubscriptionSettingWithDecryptedValue(
|
|
dto: FindSubscriptionSettingDTO,
|
|
): Promise<SubscriptionSetting | null> {
|
|
if (!dto.subscriptionSettingName.isASubscriptionSetting()) {
|
|
throw new Error(`Setting ${dto.subscriptionSettingName.value} is not a subscription setting`)
|
|
}
|
|
|
|
let setting: SubscriptionSetting | null
|
|
if (dto.settingUuid !== undefined) {
|
|
setting = await this.subscriptionSettingRepository.findOneByUuid(dto.settingUuid)
|
|
} else {
|
|
setting = await this.subscriptionSettingRepository.findLastByNameAndUserSubscriptionUuid(
|
|
dto.subscriptionSettingName.value,
|
|
dto.userSubscriptionUuid,
|
|
)
|
|
}
|
|
|
|
if (setting === null) {
|
|
return null
|
|
}
|
|
|
|
setting.value = await this.settingDecrypter.decryptSettingValue(setting, dto.userUuid)
|
|
|
|
return setting
|
|
}
|
|
|
|
async createOrReplace(
|
|
dto: CreateOrReplaceSubscriptionSettingDTO,
|
|
): Promise<CreateOrReplaceSubscriptionSettingResponse> {
|
|
const { userSubscription, user, props } = dto
|
|
|
|
const settingNameOrError = SettingName.create(props.name)
|
|
if (settingNameOrError.isFailed()) {
|
|
throw new Error(settingNameOrError.getError())
|
|
}
|
|
const settingName = settingNameOrError.getValue()
|
|
|
|
if (!settingName.isASubscriptionSetting()) {
|
|
throw new Error(`Setting ${settingName.value} is not a subscription setting`)
|
|
}
|
|
|
|
const existing = await this.findSubscriptionSettingWithDecryptedValue({
|
|
userUuid: user.uuid,
|
|
userSubscriptionUuid: userSubscription.uuid,
|
|
subscriptionSettingName: settingName,
|
|
settingUuid: props.uuid,
|
|
})
|
|
|
|
if (existing === null) {
|
|
const subscriptionSetting = await this.subscriptionSettingRepository.save(
|
|
await this.factory.createSubscriptionSetting(props, userSubscription),
|
|
)
|
|
|
|
this.logger.debug('Created subscription setting %s: %O', props.name, subscriptionSetting)
|
|
|
|
await this.settingInterpreter.interpretSettingUpdated(settingName.value, user, props.unencryptedValue)
|
|
|
|
return {
|
|
status: 'created',
|
|
subscriptionSetting,
|
|
}
|
|
}
|
|
|
|
const subscriptionSetting = await this.subscriptionSettingRepository.save(
|
|
await this.factory.createSubscriptionSettingReplacement(existing, props),
|
|
)
|
|
|
|
this.logger.debug('Replaced existing subscription setting %s with: %O', props.name, subscriptionSetting)
|
|
|
|
await this.settingInterpreter.interpretSettingUpdated(settingName.value, user, props.unencryptedValue)
|
|
|
|
return {
|
|
status: 'replaced',
|
|
subscriptionSetting,
|
|
}
|
|
}
|
|
|
|
private async findPreviousSubscriptionSetting(
|
|
settingName: SettingName,
|
|
currentUserSubscriptionUuid: string,
|
|
userUuid: string,
|
|
): Promise<SubscriptionSetting | null> {
|
|
const userSubscriptions = await this.userSubscriptionRepository.findByUserUuid(userUuid)
|
|
const previousSubscriptions = userSubscriptions.filter(
|
|
(subscription) => subscription.uuid !== currentUserSubscriptionUuid,
|
|
)
|
|
const lastSubscription = previousSubscriptions.shift()
|
|
|
|
if (!lastSubscription) {
|
|
return null
|
|
}
|
|
|
|
return this.subscriptionSettingRepository.findLastByNameAndUserSubscriptionUuid(
|
|
settingName.value,
|
|
lastSubscription.uuid,
|
|
)
|
|
}
|
|
}
|