mirror of
https://github.com/standardnotes/server
synced 2026-08-02 03:16:09 -04:00
235 lines
8.5 KiB
TypeScript
235 lines
8.5 KiB
TypeScript
import 'reflect-metadata'
|
|
|
|
import 'newrelic'
|
|
|
|
import { Stream } from 'stream'
|
|
|
|
import { Logger } from 'winston'
|
|
import * as dayjs from 'dayjs'
|
|
import * as utc from 'dayjs/plugin/utc'
|
|
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
|
|
|
|
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
|
import TYPES from '../src/Bootstrap/Types'
|
|
import { Env } from '../src/Bootstrap/Env'
|
|
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
|
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
|
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
|
import { MuteFailedBackupsEmailsOption, MuteFailedCloudBackupsEmailsOption, SettingName } from '@standardnotes/settings'
|
|
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
|
import { PermissionName } from '@standardnotes/features'
|
|
import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface'
|
|
import { AnalyticsEntityRepositoryInterface } from '../src/Domain/Analytics/AnalyticsEntityRepositoryInterface'
|
|
|
|
const inputArgs = process.argv.slice(2)
|
|
const backupProvider = inputArgs[0]
|
|
const backupFrequency = inputArgs[1]
|
|
|
|
const shouldEmailBackupBeTriggered = async (
|
|
analyticsId: number,
|
|
analyticsStore: AnalyticsStoreInterface,
|
|
): Promise<boolean> => {
|
|
let periods = [Period.Today, Period.Yesterday]
|
|
if (backupFrequency === 'weekly') {
|
|
periods = [Period.ThisWeek, Period.LastWeek]
|
|
}
|
|
|
|
for (const period of periods) {
|
|
const wasUnBackedUpDataCreatedInPeriod = await analyticsStore.wasActivityDone(
|
|
AnalyticsActivity.EmailUnbackedUpData,
|
|
analyticsId,
|
|
period,
|
|
)
|
|
if (wasUnBackedUpDataCreatedInPeriod) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
const requestBackups = async (
|
|
settingRepository: SettingRepositoryInterface,
|
|
roleService: RoleServiceInterface,
|
|
settingService: SettingServiceInterface,
|
|
domainEventFactory: DomainEventFactoryInterface,
|
|
domainEventPublisher: DomainEventPublisherInterface,
|
|
analyticsEntityRepository: AnalyticsEntityRepositoryInterface,
|
|
analyticsStore: AnalyticsStoreInterface,
|
|
logger: Logger,
|
|
): Promise<void> => {
|
|
let settingName: SettingName,
|
|
permissionName: PermissionName,
|
|
muteEmailsSettingName: SettingName,
|
|
muteEmailsSettingValue: string,
|
|
providerTokenSettingName: SettingName
|
|
switch (backupProvider) {
|
|
case 'email':
|
|
settingName = SettingName.EmailBackupFrequency
|
|
permissionName = PermissionName.DailyEmailBackup
|
|
muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
|
muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
|
break
|
|
case 'dropbox':
|
|
settingName = SettingName.DropboxBackupFrequency
|
|
permissionName = PermissionName.DailyDropboxBackup
|
|
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
|
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
|
providerTokenSettingName = SettingName.DropboxBackupToken
|
|
break
|
|
case 'one_drive':
|
|
settingName = SettingName.OneDriveBackupFrequency
|
|
permissionName = PermissionName.DailyOneDriveBackup
|
|
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
|
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
|
providerTokenSettingName = SettingName.OneDriveBackupToken
|
|
break
|
|
case 'google_drive':
|
|
settingName = SettingName.GoogleDriveBackupFrequency
|
|
permissionName = PermissionName.DailyGDriveBackup
|
|
muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails
|
|
muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted
|
|
providerTokenSettingName = SettingName.GoogleDriveBackupToken
|
|
break
|
|
default:
|
|
throw new Error(`Not handled backup provider: ${backupProvider}`)
|
|
}
|
|
|
|
const stream = await settingRepository.streamAllByNameAndValue(settingName, backupFrequency)
|
|
|
|
return new Promise((resolve, reject) => {
|
|
stream
|
|
.pipe(
|
|
new Stream.Transform({
|
|
objectMode: true,
|
|
transform: async (setting, _encoding, callback) => {
|
|
const userIsPermittedForEmailBackups = await roleService.userHasPermission(
|
|
setting.setting_user_uuid,
|
|
permissionName,
|
|
)
|
|
if (!userIsPermittedForEmailBackups) {
|
|
callback()
|
|
|
|
return
|
|
}
|
|
|
|
let userHasEmailsMuted = false
|
|
const emailsMutedSetting = await settingRepository.findOneByNameAndUserUuid(
|
|
muteEmailsSettingName,
|
|
setting.setting_user_uuid,
|
|
)
|
|
if (emailsMutedSetting !== null && emailsMutedSetting.value !== null) {
|
|
userHasEmailsMuted = emailsMutedSetting.value === muteEmailsSettingValue
|
|
}
|
|
|
|
if (backupProvider === 'email') {
|
|
const analyticsEntity = await analyticsEntityRepository.findOneByUserUuid(setting.setting_user_uuid)
|
|
if (analyticsEntity === null) {
|
|
callback()
|
|
|
|
return
|
|
}
|
|
|
|
const emailBackupsShouldBeTriggered = await shouldEmailBackupBeTriggered(
|
|
analyticsEntity.id,
|
|
analyticsStore,
|
|
)
|
|
if (!emailBackupsShouldBeTriggered) {
|
|
logger.info(
|
|
`Email backup for user ${setting.setting_user_uuid} should not be triggered due to inactivity. It will be triggered until further changes.`,
|
|
)
|
|
}
|
|
|
|
await domainEventPublisher.publish(
|
|
domainEventFactory.createEmailBackupRequestedEvent(
|
|
setting.setting_user_uuid,
|
|
emailsMutedSetting?.uuid as string,
|
|
userHasEmailsMuted,
|
|
),
|
|
)
|
|
|
|
await analyticsStore.markActivity([AnalyticsActivity.EmailBackup], analyticsEntity.id, [
|
|
Period.Today,
|
|
Period.ThisWeek,
|
|
])
|
|
await analyticsStore.unmarkActivity([AnalyticsActivity.EmailUnbackedUpData], analyticsEntity.id, [
|
|
Period.Today,
|
|
Period.ThisWeek,
|
|
])
|
|
|
|
callback()
|
|
|
|
return
|
|
}
|
|
|
|
const cloudBackupProviderToken = await settingService.findSettingWithDecryptedValue({
|
|
settingName: providerTokenSettingName,
|
|
userUuid: setting.setting_user_uuid,
|
|
})
|
|
if (cloudBackupProviderToken === null || cloudBackupProviderToken.value === null) {
|
|
callback()
|
|
|
|
return
|
|
}
|
|
|
|
await domainEventPublisher.publish(
|
|
domainEventFactory.createCloudBackupRequestedEvent(
|
|
backupProvider.toUpperCase() as 'DROPBOX' | 'ONE_DRIVE' | 'GOOGLE_DRIVE',
|
|
cloudBackupProviderToken.value,
|
|
setting.setting_user_uuid,
|
|
emailsMutedSetting?.uuid as string,
|
|
userHasEmailsMuted,
|
|
),
|
|
)
|
|
callback()
|
|
},
|
|
}),
|
|
)
|
|
.on('finish', resolve)
|
|
.on('error', reject)
|
|
})
|
|
}
|
|
|
|
const container = new ContainerConfigLoader()
|
|
void container.load().then((container) => {
|
|
dayjs.extend(utc)
|
|
|
|
const env: Env = new Env()
|
|
env.load()
|
|
|
|
const logger: Logger = container.get(TYPES.Logger)
|
|
|
|
logger.info(`Starting ${backupFrequency} ${backupProvider} backup requesting...`)
|
|
|
|
const settingRepository: SettingRepositoryInterface = container.get(TYPES.SettingRepository)
|
|
const roleService: RoleServiceInterface = container.get(TYPES.RoleService)
|
|
const settingService: SettingServiceInterface = container.get(TYPES.SettingService)
|
|
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory)
|
|
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher)
|
|
const analyticsEntityRepository: AnalyticsEntityRepositoryInterface = container.get(TYPES.AnalyticsEntityRepository)
|
|
const analyticsStore: AnalyticsStoreInterface = container.get(TYPES.AnalyticsStore)
|
|
|
|
Promise.resolve(
|
|
requestBackups(
|
|
settingRepository,
|
|
roleService,
|
|
settingService,
|
|
domainEventFactory,
|
|
domainEventPublisher,
|
|
analyticsEntityRepository,
|
|
analyticsStore,
|
|
logger,
|
|
),
|
|
)
|
|
.then(() => {
|
|
logger.info(`${backupFrequency} ${backupProvider} backup requesting complete`)
|
|
|
|
process.exit(0)
|
|
})
|
|
.catch((error) => {
|
|
logger.error(`Could not finish ${backupFrequency} ${backupProvider} backup requesting: ${error.message}`)
|
|
|
|
process.exit(1)
|
|
})
|
|
})
|