refactor: application dependency management (#2363)
This commit is contained in:
File diff suppressed because it is too large
Load Diff
1211
packages/snjs/lib/Application/Dependencies/Dependencies.ts
Normal file
1211
packages/snjs/lib/Application/Dependencies/Dependencies.ts
Normal file
File diff suppressed because it is too large
Load Diff
147
packages/snjs/lib/Application/Dependencies/Types.ts
Normal file
147
packages/snjs/lib/Application/Dependencies/Types.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
export const TYPES = {
|
||||
// System
|
||||
DeviceInterface: Symbol.for('DeviceInterface'),
|
||||
AlertService: Symbol.for('AlertService'),
|
||||
Crypto: Symbol.for('Crypto'),
|
||||
|
||||
// Services
|
||||
InternalEventBus: Symbol.for('InternalEventBus'),
|
||||
PayloadManager: Symbol.for('PayloadManager'),
|
||||
ItemManager: Symbol.for('ItemManager'),
|
||||
MutatorService: Symbol.for('MutatorService'),
|
||||
DiskStorageService: Symbol.for('DiskStorageService'),
|
||||
UserEventService: Symbol.for('UserEventService'),
|
||||
InMemoryStore: Symbol.for('InMemoryStore'),
|
||||
KeySystemKeyManager: Symbol.for('KeySystemKeyManager'),
|
||||
EncryptionService: Symbol.for('EncryptionService'),
|
||||
ChallengeService: Symbol.for('ChallengeService'),
|
||||
DeprecatedHttpService: Symbol.for('DeprecatedHttpService'),
|
||||
HttpService: Symbol.for('HttpService'),
|
||||
LegacyApiService: Symbol.for('LegacyApiService'),
|
||||
UserServer: Symbol.for('UserServer'),
|
||||
UserRequestServer: Symbol.for('UserRequestServer'),
|
||||
UserApiService: Symbol.for('UserApiService'),
|
||||
SubscriptionServer: Symbol.for('SubscriptionServer'),
|
||||
SubscriptionApiService: Symbol.for('SubscriptionApiService'),
|
||||
WebSocketServer: Symbol.for('WebSocketServer'),
|
||||
WebSocketApiService: Symbol.for('WebSocketApiService'),
|
||||
WebSocketsService: Symbol.for('WebSocketsService'),
|
||||
SessionManager: Symbol.for('SessionManager'),
|
||||
SubscriptionManager: Symbol.for('SubscriptionManager'),
|
||||
HistoryManager: Symbol.for('HistoryManager'),
|
||||
SyncService: Symbol.for('SyncService'),
|
||||
ProtectionService: Symbol.for('ProtectionService'),
|
||||
UserService: Symbol.for('UserService'),
|
||||
KeyRecoveryService: Symbol.for('KeyRecoveryService'),
|
||||
SingletonManager: Symbol.for('SingletonManager'),
|
||||
PreferencesService: Symbol.for('PreferencesService'),
|
||||
SettingsService: Symbol.for('SettingsService'),
|
||||
FeaturesService: Symbol.for('FeaturesService'),
|
||||
ComponentManager: Symbol.for('ComponentManager'),
|
||||
MfaService: Symbol.for('MfaService'),
|
||||
StatusService: Symbol.for('StatusService'),
|
||||
MigrationService: Symbol.for('MigrationService'),
|
||||
FileService: Symbol.for('FileService'),
|
||||
IntegrityService: Symbol.for('IntegrityService'),
|
||||
ListedService: Symbol.for('ListedService'),
|
||||
ActionsService: Symbol.for('ActionsService'),
|
||||
AuthenticatorApiService: Symbol.for('AuthenticatorApiService'),
|
||||
AuthenticatorManager: Symbol.for('AuthenticatorManager'),
|
||||
AuthApiService: Symbol.for('AuthApiService'),
|
||||
AuthManager: Symbol.for('AuthManager'),
|
||||
RevisionApiService: Symbol.for('RevisionApiService'),
|
||||
RevisionManager: Symbol.for('RevisionManager'),
|
||||
ContactService: Symbol.for('ContactService'),
|
||||
VaultService: Symbol.for('VaultService'),
|
||||
SharedVaultService: Symbol.for('SharedVaultService'),
|
||||
AsymmetricMessageService: Symbol.for('AsymmetricMessageService'),
|
||||
SelfContactManager: Symbol.for('SelfContactManager'),
|
||||
EncryptionOperators: Symbol.for('EncryptionOperators'),
|
||||
RootKeyManager: Symbol.for('RootKeyManager'),
|
||||
ItemsEncryptionService: Symbol.for('ItemsEncryptionService'),
|
||||
|
||||
// Servers
|
||||
RevisionServer: Symbol.for('RevisionServer'),
|
||||
AuthenticatorServer: Symbol.for('AuthenticatorServer'),
|
||||
AuthServer: Symbol.for('AuthServer'),
|
||||
SharedVaultInvitesServer: Symbol.for('SharedVaultInvitesServer'),
|
||||
SharedVaultServer: Symbol.for('SharedVaultServer'),
|
||||
SharedVaultUsersServer: Symbol.for('SharedVaultUsersServer'),
|
||||
AsymmetricMessageServer: Symbol.for('AsymmetricMessageServer'),
|
||||
|
||||
// Desktop Services
|
||||
FilesBackupService: Symbol.for('FilesBackupService'),
|
||||
HomeServerService: Symbol.for('HomeServerService'),
|
||||
|
||||
// Usecases
|
||||
SignInWithRecoveryCodes: Symbol.for('SignInWithRecoveryCodes'),
|
||||
GetRecoveryCodes: Symbol.for('GetRecoveryCodes'),
|
||||
AddAuthenticator: Symbol.for('AddAuthenticator'),
|
||||
ListAuthenticators: Symbol.for('ListAuthenticators'),
|
||||
DeleteAuthenticator: Symbol.for('DeleteAuthenticator'),
|
||||
GetAuthenticatorAuthenticationOptions: Symbol.for('GetAuthenticatorAuthenticationOptions'),
|
||||
GetAuthenticatorAuthenticationResponse: Symbol.for('GetAuthenticatorAuthenticationResponse'),
|
||||
ListRevisions: Symbol.for('ListRevisions'),
|
||||
GetRevision: Symbol.for('GetRevision'),
|
||||
DeleteRevision: Symbol.for('DeleteRevision'),
|
||||
ImportDataUseCase: Symbol.for('ImportDataUseCase'),
|
||||
RemoveItemsLocally: Symbol.for('RemoveItemsLocally'),
|
||||
FindContact: Symbol.for('FindContact'),
|
||||
GetAllContacts: Symbol.for('GetAllContacts'),
|
||||
CreateOrEditContact: Symbol.for('CreateOrEditContact'),
|
||||
EditContact: Symbol.for('EditContact'),
|
||||
ValidateItemSigner: Symbol.for('ValidateItemSigner'),
|
||||
GetVault: Symbol.for('GetVault'),
|
||||
ChangeVaultKeyOptions: Symbol.for('ChangeVaultKeyOptions'),
|
||||
MoveItemsToVault: Symbol.for('MoveItemsToVault'),
|
||||
CreateVault: Symbol.for('CreateVault'),
|
||||
RemoveItemFromVault: Symbol.for('RemoveItemFromVault'),
|
||||
DeleteVault: Symbol.for('DeleteVault'),
|
||||
RotateVaultKey: Symbol.for('RotateVaultKey'),
|
||||
CreateSharedVault: Symbol.for('CreateSharedVault'),
|
||||
HandleKeyPairChange: Symbol.for('HandleKeyPairChange'),
|
||||
NotifyVaultUsersOfKeyRotation: Symbol.for('NotifyVaultUsersOfKeyRotation'),
|
||||
SendVaultDataChangedMessage: Symbol.for('SendVaultDataChangedMessage'),
|
||||
SendVaultKeyChangedMessage: Symbol.for('SendVaultKeyChangedMessage'),
|
||||
GetTrustedPayload: Symbol.for('GetTrustedPayload'),
|
||||
GetUntrustedPayload: Symbol.for('GetUntrustedPayload'),
|
||||
GetVaultContacts: Symbol.for('GetVaultContacts'),
|
||||
AcceptVaultInvite: Symbol.for('AcceptVaultInvite'),
|
||||
InviteToVault: Symbol.for('InviteToVault'),
|
||||
LeaveVault: Symbol.for('LeaveVault'),
|
||||
DeleteThirdPartyVault: Symbol.for('DeleteThirdPartyVault'),
|
||||
ShareContactWithVault: Symbol.for('ShareContactWithVault'),
|
||||
ConvertToSharedVault: Symbol.for('ConvertToSharedVault'),
|
||||
DeleteSharedVault: Symbol.for('DeleteSharedVault'),
|
||||
RemoveVaultMember: Symbol.for('RemoveVaultMember'),
|
||||
GetVaultUsers: Symbol.for('GetSharedVaultUsers'),
|
||||
ResendAllMessages: Symbol.for('ResendAllMessages'),
|
||||
ReuploadAllInvites: Symbol.for('ReuploadAllInvites'),
|
||||
ReuploadInvite: Symbol.for('ReuploadInvite'),
|
||||
GetInboundMessages: Symbol.for('GetInboundMessages'),
|
||||
GetOutboundMessages: Symbol.for('GetOutboundMessages'),
|
||||
HandleRootKeyChangedMessage: Symbol.for('HandleRootKeyChangedMessage'),
|
||||
ProcessAcceptedVaultInvite: Symbol.for('ProcessAcceptedVaultInvite'),
|
||||
ResendMessage: Symbol.for('ResendMessage'),
|
||||
SendMessage: Symbol.for('SendMessage'),
|
||||
SendOwnContactChangeMessage: Symbol.for('SendOwnContactChangeMessage'),
|
||||
DecryptMessage: Symbol.for('DecryptMessage'),
|
||||
DecryptOwnMessage: Symbol.for('DecryptOwnMessage'),
|
||||
EncryptMessage: Symbol.for('EncryptMessage'),
|
||||
GetMessageAdditionalData: Symbol.for('GetMessageAdditionalData'),
|
||||
SendVaultInvite: Symbol.for('SendVaultInvite'),
|
||||
ReplaceContactData: Symbol.for('ReplaceContactData'),
|
||||
CreateNewDefaultItemsKey: Symbol.for('CreateNewDefaultItemsKey'),
|
||||
CreateNewItemsKeyWithRollback: Symbol.for('CreateNewItemsKeyWithRollback'),
|
||||
FindDefaultItemsKey: Symbol.for('FindDefaultItemsKey'),
|
||||
DecryptErroredTypeAPayloads: Symbol.for('DecryptErroredTypeAPayloads'),
|
||||
DecryptTypeAPayload: Symbol.for('DecryptTypeAPayload'),
|
||||
DecryptTypeAPayloadWithKeyLookup: Symbol.for('DecryptTypeAPayloadWithKeyLookup'),
|
||||
EncryptTypeAPayload: Symbol.for('EncryptTypeAPayload'),
|
||||
EncryptTypeAPayloadWithKeyLookup: Symbol.for('EncryptTypeAPayloadWithKeyLookup'),
|
||||
DecryptBackupFile: Symbol.for('DecryptBackupFile'),
|
||||
|
||||
// Mappers
|
||||
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
|
||||
LegacySessionStorageMapper: Symbol.for('LegacySessionStorageMapper'),
|
||||
}
|
||||
14
packages/snjs/lib/Application/Dependencies/isDeinitable.ts
Normal file
14
packages/snjs/lib/Application/Dependencies/isDeinitable.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export function isDeinitable(service: unknown): service is { deinit(): void } {
|
||||
if (!service) {
|
||||
throw new Error('Service is undefined')
|
||||
}
|
||||
return typeof (service as { deinit(): void }).deinit === 'function'
|
||||
}
|
||||
|
||||
export function canBlockDeinit(service: unknown): service is { blockDeinit(): Promise<void> } {
|
||||
if (!service) {
|
||||
throw new Error('Service is undefined')
|
||||
}
|
||||
|
||||
return typeof (service as { blockDeinit(): Promise<void> }).blockDeinit === 'function'
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { EncryptedPayloadInterface, HistoryEntry } from '@standardnotes/models'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { RevisionClientInterface } from '@standardnotes/services'
|
||||
import { EncryptionProviderInterface, RevisionClientInterface } from '@standardnotes/services'
|
||||
jest.mock('@standardnotes/models', () => {
|
||||
const original = jest.requireActual('@standardnotes/models')
|
||||
|
||||
@@ -32,11 +31,13 @@ describe('GetRevision', () => {
|
||||
enc_item_key: 'foobar',
|
||||
auth_hash: 'foobar',
|
||||
created_at: '2021-01-01T00:00:00.000Z',
|
||||
updated_at: '2021-01-01T00:00:00.000Z'
|
||||
updated_at: '2021-01-01T00:00:00.000Z',
|
||||
} as jest.Mocked<Revision>)
|
||||
|
||||
encryptionService = {} as jest.Mocked<EncryptionProviderInterface>
|
||||
encryptionService.getEmbeddedPayloadAuthenticatedData = jest.fn().mockReturnValue({ u: '00000000-0000-0000-0000-000000000000' })
|
||||
encryptionService.getEmbeddedPayloadAuthenticatedData = jest
|
||||
.fn()
|
||||
.mockReturnValue({ u: '00000000-0000-0000-0000-000000000000' })
|
||||
const encryptedPayload = {
|
||||
content: 'foobar',
|
||||
} as jest.Mocked<EncryptedPayloadInterface>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ServerItemResponse } from '@standardnotes/responses'
|
||||
import { RevisionClientInterface } from '@standardnotes/services'
|
||||
import { EncryptionProviderInterface, RevisionClientInterface } from '@standardnotes/services'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import {
|
||||
EncryptedPayload,
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
NoteContent,
|
||||
PayloadTimestampDefaults,
|
||||
} from '@standardnotes/models'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
|
||||
import { GetRevisionDTO } from './GetRevisionDTO'
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {
|
||||
AuthClientInterface,
|
||||
EncryptionProviderInterface,
|
||||
InternalEventBusInterface,
|
||||
KeyValueStoreInterface,
|
||||
SessionsClientInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AnyKeyParamsContent } from '@standardnotes/common'
|
||||
import { DecryptedPayloadInterface, RootKeyContent, RootKeyInterface } from '@standardnotes/models'
|
||||
@@ -20,14 +20,8 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
let sessionManager: SessionsClientInterface
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createUseCase = () => new SignInWithRecoveryCodes(
|
||||
authManager,
|
||||
encryptionService,
|
||||
inMemoryStore,
|
||||
crypto,
|
||||
sessionManager,
|
||||
internalEventBus,
|
||||
)
|
||||
const createUseCase = () =>
|
||||
new SignInWithRecoveryCodes(authManager, encryptionService, inMemoryStore, crypto, sessionManager, internalEventBus)
|
||||
|
||||
beforeEach(() => {
|
||||
authManager = {} as jest.Mocked<AuthClientInterface>
|
||||
@@ -54,12 +48,7 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
encryptionService.hasAccount = jest.fn()
|
||||
encryptionService.computeRootKey = jest.fn().mockReturnValue(rootKey)
|
||||
encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(true)
|
||||
encryptionService.supportedVersions = jest.fn().mockReturnValue([
|
||||
'001',
|
||||
'002',
|
||||
'003',
|
||||
'004',
|
||||
])
|
||||
encryptionService.supportedVersions = jest.fn().mockReturnValue(['001', '002', '003', '004'])
|
||||
encryptionService.isVersionNewerThanLibraryVersion = jest.fn()
|
||||
|
||||
inMemoryStore = {} as jest.Mocked<KeyValueStoreInterface<string>>
|
||||
@@ -82,7 +71,11 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
encryptionService.hasAccount = jest.fn().mockReturnValue(true)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toEqual('Tried to sign in when an account already exists.')
|
||||
@@ -92,7 +85,11 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
authManager.recoveryKeyParams = jest.fn().mockReturnValue(false)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toEqual('Could not retrieve recovery key params')
|
||||
@@ -102,10 +99,16 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toEqual('Your account was created on a platform with higher security capabilities than this browser supports. If we attempted to generate your login keys here, it would take hours. Please use a browser with more up to date security capabilities, like Google Chrome or Firefox, to log in.')
|
||||
expect(result.getError()).toEqual(
|
||||
'Your account was created on a platform with higher security capabilities than this browser supports. If we attempted to generate your login keys here, it would take hours. Please use a browser with more up to date security capabilities, like Google Chrome or Firefox, to log in.',
|
||||
)
|
||||
})
|
||||
|
||||
it('should fail if key params has unsupported version', async () => {
|
||||
@@ -123,10 +126,16 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toEqual('This version of the application does not support your newer account type. Please upgrade to the latest version of Standard Notes to sign in.')
|
||||
expect(result.getError()).toEqual(
|
||||
'This version of the application does not support your newer account type. Please upgrade to the latest version of Standard Notes to sign in.',
|
||||
)
|
||||
})
|
||||
|
||||
it('should fail if key params has expired version', async () => {
|
||||
@@ -144,17 +153,27 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
encryptionService.platformSupportsKeyDerivation = jest.fn().mockReturnValue(false)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toEqual('The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.com/help/security for more information.')
|
||||
expect(result.getError()).toEqual(
|
||||
'The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.com/help/security for more information.',
|
||||
)
|
||||
})
|
||||
|
||||
it('should fail if the sign in with recovery code fails', async () => {
|
||||
authManager.signInWithRecoveryCodes = jest.fn().mockReturnValue(false)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toEqual('Could not sign in with recovery code')
|
||||
@@ -168,11 +187,15 @@ describe('SignInWithRecoveryCodes', () => {
|
||||
uuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
protocolVersion: '004',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({ recoveryCodes: 'recovery-codes', password: 'foobar', username: 'test@test.te' })
|
||||
const result = await useCase.execute({
|
||||
recoveryCodes: 'recovery-codes',
|
||||
password: 'foobar',
|
||||
username: 'test@test.te',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
AccountEvent,
|
||||
AuthClientInterface,
|
||||
EXPIRED_PROTOCOL_VERSION,
|
||||
EncryptionProviderInterface,
|
||||
InternalEventBusInterface,
|
||||
InternalEventPublishStrategy,
|
||||
KeyValueStoreInterface,
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
UNSUPPORTED_KEY_DERIVATION,
|
||||
UNSUPPORTED_PROTOCOL_VERSION,
|
||||
} from '@standardnotes/services'
|
||||
import { CreateAnyKeyParams, EncryptionProviderInterface, SNRootKey } from '@standardnotes/encryption'
|
||||
import { CreateAnyKeyParams, SNRootKey } from '@standardnotes/encryption'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
|
||||
import { SignInWithRecoveryCodesDTO } from './SignInWithRecoveryCodesDTO'
|
||||
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { SNSessionManager } from '../Services/Session/SessionManager'
|
||||
import { SessionManager } from '../Services/Session/SessionManager'
|
||||
import { ApplicationIdentifier } from '@standardnotes/common'
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import { ChallengeService, SNSingletonManager, SNFeaturesService, DiskStorageService } from '@Lib/Services'
|
||||
import { ChallengeService, SingletonManager, FeaturesService, DiskStorageService } from '@Lib/Services'
|
||||
import { LegacySession, MapperInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export type MigrationServices = {
|
||||
@@ -18,12 +18,12 @@ export type MigrationServices = {
|
||||
deviceInterface: DeviceInterface
|
||||
storageService: DiskStorageService
|
||||
challengeService: ChallengeService
|
||||
sessionManager: SNSessionManager
|
||||
sessionManager: SessionManager
|
||||
backups?: BackupServiceInterface
|
||||
itemManager: ItemManager
|
||||
mutator: MutatorClientInterface
|
||||
singletonManager: SNSingletonManager
|
||||
featuresService: SNFeaturesService
|
||||
singletonManager: SingletonManager
|
||||
featuresService: FeaturesService
|
||||
preferences: PreferenceServiceInterface
|
||||
environment: Environment
|
||||
platform: Platform
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { SNRootKey } from '@standardnotes/encryption'
|
||||
import { ChallengeService } from '../Challenge'
|
||||
import { ListedService } from '../Listed/ListedService'
|
||||
import { ActionResponse, DeprecatedHttpResponse } from '@standardnotes/responses'
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import {
|
||||
@@ -21,8 +20,6 @@ import {
|
||||
TransferPayload,
|
||||
ItemContent,
|
||||
} from '@standardnotes/models'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { PayloadManager } from '../Payloads/PayloadManager'
|
||||
import { DeprecatedHttpService } from '../Api/DeprecatedHttpService'
|
||||
import {
|
||||
AbstractService,
|
||||
@@ -53,20 +50,17 @@ type PayloadRequestHandler = (uuid: string) => TransferPayload | undefined
|
||||
* `post`: sends an item's data to a remote service. This is used for example by Listed
|
||||
* to allow publishing a note to a user's blog.
|
||||
*/
|
||||
export class SNActionsService extends AbstractService {
|
||||
export class ActionsService extends AbstractService {
|
||||
private previousPasswords: string[] = []
|
||||
private payloadRequestHandlers: PayloadRequestHandler[] = []
|
||||
|
||||
constructor(
|
||||
private itemManager: ItemManager,
|
||||
private alertService: AlertService,
|
||||
public deviceInterface: DeviceInterface,
|
||||
private device: DeviceInterface,
|
||||
private httpService: DeprecatedHttpService,
|
||||
private payloadManager: PayloadManager,
|
||||
private encryptionService: EncryptionService,
|
||||
private syncService: SNSyncService,
|
||||
private challengeService: ChallengeService,
|
||||
private listedService: ListedService,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
@@ -76,13 +70,10 @@ export class SNActionsService extends AbstractService {
|
||||
public override deinit(): void {
|
||||
;(this.itemManager as unknown) = undefined
|
||||
;(this.alertService as unknown) = undefined
|
||||
;(this.deviceInterface as unknown) = undefined
|
||||
;(this.device as unknown) = undefined
|
||||
;(this.httpService as unknown) = undefined
|
||||
;(this.payloadManager as unknown) = undefined
|
||||
;(this.listedService as unknown) = undefined
|
||||
;(this.challengeService as unknown) = undefined
|
||||
;(this.encryptionService as unknown) = undefined
|
||||
;(this.syncService as unknown) = undefined
|
||||
this.payloadRequestHandlers.length = 0
|
||||
this.previousPasswords.length = 0
|
||||
super.deinit()
|
||||
@@ -323,7 +314,7 @@ export class SNActionsService extends AbstractService {
|
||||
}
|
||||
|
||||
private handleShowAction(action: Action) {
|
||||
void this.deviceInterface.openUrl(action.url)
|
||||
void this.device.openUrl(action.url)
|
||||
return {} as ActionResponse
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { joinPaths } from '@standardnotes/utils'
|
||||
import {
|
||||
AbstractService,
|
||||
ApiServiceInterface,
|
||||
LegacyApiServiceInterface,
|
||||
InternalEventBusInterface,
|
||||
IntegrityApiInterface,
|
||||
ItemsServerInterface,
|
||||
@@ -86,10 +86,10 @@ const V0_API_VERSION = '20200115'
|
||||
|
||||
type InvalidSessionObserver = (revoked: boolean) => void
|
||||
|
||||
export class SNApiService
|
||||
export class LegacyApiService
|
||||
extends AbstractService<ApiServiceEvent, ApiServiceEventData>
|
||||
implements
|
||||
ApiServiceInterface,
|
||||
LegacyApiServiceInterface,
|
||||
FilesApiInterface,
|
||||
IntegrityApiInterface,
|
||||
ItemsServerInterface,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { WebSocketApiServiceInterface } from '@standardnotes/api'
|
||||
|
||||
import { StorageKey, DiskStorageService } from '@Lib/index'
|
||||
import { SNWebSocketsService } from './WebsocketsService'
|
||||
import { WebSocketsService } from './WebsocketsService'
|
||||
|
||||
describe('webSocketsService', () => {
|
||||
const webSocketUrl = ''
|
||||
@@ -12,7 +12,7 @@ describe('webSocketsService', () => {
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () => {
|
||||
return new SNWebSocketsService(storageService, webSocketUrl, webSocketApiService, internalEventBus)
|
||||
return new WebSocketsService(storageService, webSocketUrl, webSocketApiService, internalEventBus)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { WebSocketApiServiceInterface } from '@standardnotes/api'
|
||||
import { WebSocketsServiceEvent } from './WebSocketsServiceEvent'
|
||||
|
||||
export class SNWebSocketsService extends AbstractService<WebSocketsServiceEvent, UserRolesChangedEvent> {
|
||||
export class WebSocketsService extends AbstractService<WebSocketsServiceEvent, UserRolesChangedEvent> {
|
||||
private webSocket?: WebSocket
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -158,7 +158,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
|
||||
return { wrappingKey }
|
||||
}
|
||||
|
||||
public isPasscodeLocked() {
|
||||
public isPasscodeLocked(): Promise<boolean> {
|
||||
return this.encryptionService.isPasscodeLocked()
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
|
||||
delete this.challengeOperations[challenge.id]
|
||||
}
|
||||
|
||||
public cancelChallenge(challenge: Challenge) {
|
||||
public cancelChallenge(challenge: Challenge): void {
|
||||
const operation = this.challengeOperations[challenge.id]
|
||||
operation.cancel()
|
||||
|
||||
@@ -274,7 +274,7 @@ export class ChallengeService extends AbstractService implements ChallengeServic
|
||||
this.deleteChallengeOperation(operation)
|
||||
}
|
||||
|
||||
public async submitValuesForChallenge(challenge: Challenge, values: ChallengeValue[]) {
|
||||
public async submitValuesForChallenge(challenge: Challenge, values: ChallengeValue[]): Promise<void> {
|
||||
if (values.length === 0) {
|
||||
throw Error('Attempting to submit 0 values for challenge')
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ import {
|
||||
PreferenceServiceInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import { SNFeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
import { FeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
import { SNComponentManager } from './ComponentManager'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { SyncService } from '../Sync/SyncService'
|
||||
|
||||
describe('featuresService', () => {
|
||||
let items: ItemManagerInterface
|
||||
let mutator: MutatorClientInterface
|
||||
let features: SNFeaturesService
|
||||
let features: FeaturesService
|
||||
let alerts: AlertService
|
||||
let sync: SyncServiceInterface
|
||||
let prefs: PreferenceServiceInterface
|
||||
@@ -47,7 +47,7 @@ describe('featuresService', () => {
|
||||
attachEvent: jest.fn(),
|
||||
} as unknown as Window & typeof globalThis
|
||||
|
||||
sync = {} as jest.Mocked<SNSyncService>
|
||||
sync = {} as jest.Mocked<SyncService>
|
||||
sync.sync = jest.fn()
|
||||
|
||||
items = {} as jest.Mocked<ItemManager>
|
||||
@@ -61,7 +61,7 @@ describe('featuresService', () => {
|
||||
mutator.changeItem = jest.fn()
|
||||
mutator.changeFeatureRepo = jest.fn()
|
||||
|
||||
features = {} as jest.Mocked<SNFeaturesService>
|
||||
features = {} as jest.Mocked<FeaturesService>
|
||||
|
||||
prefs = {} as jest.Mocked<SNPreferencesService>
|
||||
prefs.addEventObserver = jest.fn()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SNFeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
import { FeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import {
|
||||
ActionObserver,
|
||||
@@ -94,7 +94,7 @@ export class SNComponentManager
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private features: SNFeaturesService,
|
||||
private features: FeaturesService,
|
||||
private preferences: PreferenceServiceInterface,
|
||||
protected alerts: AlertService,
|
||||
private environment: Environment,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
ItemManagerInterface,
|
||||
SyncServiceInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { SNFeaturesService } from '@Lib/Services'
|
||||
import { FeaturesService } from '@Lib/Services'
|
||||
import {
|
||||
ActionObserver,
|
||||
ComponentEventObserver,
|
||||
@@ -100,7 +100,7 @@ export class ComponentViewer implements ComponentViewerInterface {
|
||||
sync: SyncServiceInterface
|
||||
alerts: AlertService
|
||||
preferences: PreferenceServiceInterface
|
||||
features: SNFeaturesService
|
||||
features: FeaturesService
|
||||
},
|
||||
private options: {
|
||||
item: ComponentViewerItem
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { ItemInterface, SNFeatureRepo } from '@standardnotes/models'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { SyncService } from '../Sync/SyncService'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SNFeaturesService } from '@Lib/Services/Features'
|
||||
import { FeaturesService } from '@Lib/Services/Features'
|
||||
import { RoleName, ContentType } from '@standardnotes/domain-core'
|
||||
import { FeatureIdentifier, GetFeatures } from '@standardnotes/features'
|
||||
import { SNWebSocketsService } from '../Api/WebsocketsService'
|
||||
import { SNSettingsService } from '../Settings'
|
||||
import { WebSocketsService } from '../Api/WebsocketsService'
|
||||
import { SettingsService } from '../Settings'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import {
|
||||
AlertService,
|
||||
ApiServiceInterface,
|
||||
LegacyApiServiceInterface,
|
||||
FeaturesEvent,
|
||||
FeatureStatus,
|
||||
InternalEventBusInterface,
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
UserClientInterface,
|
||||
UserService,
|
||||
} from '@standardnotes/services'
|
||||
import { SNApiService, SNSessionManager } from '../Api'
|
||||
import { LegacyApiService, SessionManager } from '../Api'
|
||||
import { ItemManager } from '../Items'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { SettingsClientInterface } from '../Settings/SettingsClientInterface'
|
||||
@@ -33,8 +33,8 @@ describe('FeaturesService', () => {
|
||||
let itemManager: ItemManagerInterface
|
||||
let mutator: MutatorClientInterface
|
||||
let subscriptions: SubscriptionManagerInterface
|
||||
let apiService: ApiServiceInterface
|
||||
let webSocketsService: SNWebSocketsService
|
||||
let apiService: LegacyApiServiceInterface
|
||||
let webSocketsService: WebSocketsService
|
||||
let settingsService: SettingsClientInterface
|
||||
let userService: UserClientInterface
|
||||
let syncService: SyncServiceInterface
|
||||
@@ -46,7 +46,7 @@ describe('FeaturesService', () => {
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () => {
|
||||
return new SNFeaturesService(
|
||||
return new FeaturesService(
|
||||
storageService,
|
||||
itemManager,
|
||||
mutator,
|
||||
@@ -72,7 +72,7 @@ describe('FeaturesService', () => {
|
||||
storageService.setValue = jest.fn()
|
||||
storageService.getValue = jest.fn()
|
||||
|
||||
apiService = {} as jest.Mocked<SNApiService>
|
||||
apiService = {} as jest.Mocked<LegacyApiService>
|
||||
apiService.addEventObserver = jest.fn()
|
||||
apiService.isThirdPartyHostUsed = jest.fn().mockReturnValue(false)
|
||||
|
||||
@@ -92,23 +92,23 @@ describe('FeaturesService', () => {
|
||||
subscriptions.getOnlineSubscription = jest.fn()
|
||||
subscriptions.addEventObserver = jest.fn()
|
||||
|
||||
webSocketsService = {} as jest.Mocked<SNWebSocketsService>
|
||||
webSocketsService = {} as jest.Mocked<WebSocketsService>
|
||||
webSocketsService.addEventObserver = jest.fn()
|
||||
|
||||
settingsService = {} as jest.Mocked<SNSettingsService>
|
||||
settingsService = {} as jest.Mocked<SettingsService>
|
||||
settingsService.updateSetting = jest.fn()
|
||||
|
||||
userService = {} as jest.Mocked<UserService>
|
||||
userService.addEventObserver = jest.fn()
|
||||
|
||||
syncService = {} as jest.Mocked<SNSyncService>
|
||||
syncService = {} as jest.Mocked<SyncService>
|
||||
syncService.sync = jest.fn()
|
||||
|
||||
alertService = {} as jest.Mocked<AlertService>
|
||||
alertService.confirm = jest.fn().mockReturnValue(true)
|
||||
alertService.alert = jest.fn()
|
||||
|
||||
sessionManager = {} as jest.Mocked<SNSessionManager>
|
||||
sessionManager = {} as jest.Mocked<SessionManager>
|
||||
sessionManager.isSignedIntoFirstPartyServer = jest.fn()
|
||||
sessionManager.getUser = jest.fn()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { RoleName, ContentType } from '@standardnotes/domain-core'
|
||||
import { PROD_OFFLINE_FEATURES_URL } from '../../Hosts'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { SNWebSocketsService } from '../Api/WebsocketsService'
|
||||
import { WebSocketsService } from '../Api/WebsocketsService'
|
||||
import { WebSocketsServiceEvent } from '../Api/WebSocketsServiceEvent'
|
||||
import { TRUSTED_CUSTOM_EXTENSIONS_HOSTS, TRUSTED_FEATURE_HOSTS } from '@Lib/Hosts'
|
||||
import { UserRolesChangedEvent } from '@standardnotes/domain-events'
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
StorageKey,
|
||||
MutatorClientInterface,
|
||||
StorageServiceInterface,
|
||||
ApiServiceInterface,
|
||||
LegacyApiServiceInterface,
|
||||
ItemManagerInterface,
|
||||
SyncServiceInterface,
|
||||
SessionsClientInterface,
|
||||
@@ -47,6 +47,8 @@ import {
|
||||
SubscriptionManagerInterface,
|
||||
AccountEvent,
|
||||
SubscriptionManagerEvent,
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
} from '@standardnotes/services'
|
||||
|
||||
import { DownloadRemoteThirdPartyFeatureUseCase } from './UseCase/DownloadRemoteThirdPartyFeature'
|
||||
@@ -56,7 +58,7 @@ import { SettingsClientInterface } from '../Settings/SettingsClientInterface'
|
||||
|
||||
type GetOfflineSubscriptionDetailsResponse = OfflineSubscriptionEntitlements | ClientDisplayableError
|
||||
|
||||
export class SNFeaturesService
|
||||
export class FeaturesService
|
||||
extends AbstractService<FeaturesEvent>
|
||||
implements FeaturesClientInterface, InternalEventHandlerInterface
|
||||
{
|
||||
@@ -71,8 +73,8 @@ export class SNFeaturesService
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private subscriptions: SubscriptionManagerInterface,
|
||||
private api: ApiServiceInterface,
|
||||
sockets: SNWebSocketsService,
|
||||
private api: LegacyApiServiceInterface,
|
||||
sockets: WebSocketsService,
|
||||
private settings: SettingsClientInterface,
|
||||
private user: UserClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
@@ -152,20 +154,19 @@ export class SNFeaturesService
|
||||
const { userRoles } = event.payload as MetaReceivedData
|
||||
void this.updateOnlineRolesWithNewValues(userRoles.map((role) => role.name))
|
||||
}
|
||||
}
|
||||
|
||||
override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
if (stage === ApplicationStage.FullSyncCompleted_13) {
|
||||
if (!this.hasFirstPartyOnlineSubscription()) {
|
||||
const offlineRepo = this.getOfflineRepo()
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
if (stage === ApplicationStage.FullSyncCompleted_13) {
|
||||
if (!this.hasFirstPartyOnlineSubscription()) {
|
||||
const offlineRepo = this.getOfflineRepo()
|
||||
|
||||
if (offlineRepo) {
|
||||
void this.downloadOfflineRoles(offlineRepo)
|
||||
if (offlineRepo) {
|
||||
void this.downloadOfflineRoles(offlineRepo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.handleApplicationStage(stage)
|
||||
}
|
||||
|
||||
public enableExperimentalFeature(identifier: FeatureIdentifier): void {
|
||||
|
||||
@@ -9,13 +9,17 @@ import {
|
||||
import {
|
||||
AlertService,
|
||||
API_MESSAGE_FAILED_DOWNLOADING_EXTENSION,
|
||||
ApiServiceInterface,
|
||||
LegacyApiServiceInterface,
|
||||
ItemManagerInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { isString } from '@standardnotes/utils'
|
||||
|
||||
export class DownloadRemoteThirdPartyFeatureUseCase {
|
||||
constructor(private api: ApiServiceInterface, private items: ItemManagerInterface, private alerts: AlertService) {}
|
||||
constructor(
|
||||
private api: LegacyApiServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private alerts: AlertService,
|
||||
) {}
|
||||
|
||||
async execute(url: string): Promise<ComponentInterface | undefined> {
|
||||
const response = await this.api.downloadFeatureUrl(url)
|
||||
|
||||
@@ -30,7 +30,7 @@ const LargeEntryDeltaThreshold = 25
|
||||
* 2. Remote server history. Entries are automatically added by the server and must be
|
||||
* retrieved per item via an API call.
|
||||
*/
|
||||
export class SNHistoryManager extends AbstractService implements HistoryServiceInterface {
|
||||
export class HistoryManager extends AbstractService implements HistoryServiceInterface {
|
||||
private removeChangeObserver: () => void
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { ItemsKeyInterface } from '@standardnotes/models'
|
||||
import { dateSorted } from '@standardnotes/utils'
|
||||
import { SNRootKeyParams, EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { SNRootKeyParams } from '@standardnotes/encryption'
|
||||
import { DecryptionQueueItem, KeyRecoveryOperationResult } from './Types'
|
||||
import { serverKeyParamsAreSafe } from './Utils'
|
||||
import { ChallengeServiceInterface, DecryptItemsKeyByPromptingUser } from '@standardnotes/services'
|
||||
import {
|
||||
ChallengeServiceInterface,
|
||||
DecryptItemsKeyByPromptingUser,
|
||||
EncryptionProviderInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '../Items'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import {
|
||||
getIncrementedDirtyIndex,
|
||||
ContentTypeUsesRootKeyEncryption,
|
||||
} from '@standardnotes/models'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { SyncService } from '../Sync/SyncService'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { PayloadManager } from '../Payloads/PayloadManager'
|
||||
import { ChallengeService } from '../Challenge'
|
||||
import { SNApiService } from '@Lib/Services/Api/ApiService'
|
||||
import { LegacyApiService } from '@Lib/Services/Api/ApiService'
|
||||
import { ItemManager } from '../Items/ItemManager'
|
||||
import { removeFromArray, Uuids } from '@standardnotes/utils'
|
||||
import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses'
|
||||
@@ -33,6 +33,10 @@ import {
|
||||
EncryptionService,
|
||||
Challenge,
|
||||
UserService,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
} from '@standardnotes/services'
|
||||
import {
|
||||
UndecryptableItemsStorage,
|
||||
@@ -79,7 +83,10 @@ import { ContentType } from '@standardnotes/domain-core'
|
||||
* but our current copy is not, we will ignore the incoming value until we can properly
|
||||
* decrypt it.
|
||||
*/
|
||||
export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, DecryptedPayloadInterface[]> {
|
||||
export class KeyRecoveryService
|
||||
extends AbstractService<KeyRecoveryEvent, DecryptedPayloadInterface[]>
|
||||
implements InternalEventHandlerInterface
|
||||
{
|
||||
private removeItemObserver: () => void
|
||||
private decryptionQueue: DecryptionQueueItem[] = []
|
||||
private isProcessingQueue = false
|
||||
@@ -87,12 +94,12 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
|
||||
constructor(
|
||||
private itemManager: ItemManager,
|
||||
private payloadManager: PayloadManager,
|
||||
private apiService: SNApiService,
|
||||
private apiService: LegacyApiService,
|
||||
private encryptionService: EncryptionService,
|
||||
private challengeService: ChallengeService,
|
||||
private alertService: AlertService,
|
||||
private storageService: DiskStorageService,
|
||||
private syncService: SNSyncService,
|
||||
private sync: SyncService,
|
||||
private userService: UserService,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
@@ -126,7 +133,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
|
||||
;(this.challengeService as unknown) = undefined
|
||||
;(this.alertService as unknown) = undefined
|
||||
;(this.storageService as unknown) = undefined
|
||||
;(this.syncService as unknown) = undefined
|
||||
;(this.sync as unknown) = undefined
|
||||
;(this.userService as unknown) = undefined
|
||||
|
||||
this.removeItemObserver()
|
||||
@@ -135,11 +142,12 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
void super.handleApplicationStage(stage)
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
void this.processPersistedUndecryptables()
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
void this.processPersistedUndecryptables()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,8 +391,8 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
|
||||
await this.potentiallyPerformFallbackSignInToUpdateOutdatedLocalRootKey(serverParams)
|
||||
}
|
||||
|
||||
if (this.syncService.isOutOfSync()) {
|
||||
void this.syncService.sync({ checkIntegrity: true })
|
||||
if (this.sync.isOutOfSync()) {
|
||||
void this.sync.sync({ checkIntegrity: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +508,7 @@ export class SNKeyRecoveryService extends AbstractService<KeyRecoveryEvent, Decr
|
||||
}
|
||||
|
||||
if (decryptedMatching.some((p) => p.dirty)) {
|
||||
await this.syncService.sync()
|
||||
await this.sync.sync()
|
||||
}
|
||||
|
||||
await this.notifyEvent(KeyRecoveryEvent.KeysRecovered, decryptedMatching)
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
import { SyncClientInterface } from './../Sync/SyncClientInterface'
|
||||
import { isString, lastElement, sleep } from '@standardnotes/utils'
|
||||
import { UuidString } from '@Lib/Types/UuidString'
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import { DeprecatedHttpService } from '../Api/DeprecatedHttpService'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SNSettingsService } from '../Settings/SNSettingsService'
|
||||
import { SettingsService } from '../Settings/SNSettingsService'
|
||||
import { ListedClientInterface } from './ListedClientInterface'
|
||||
import { SNApiService } from '../Api/ApiService'
|
||||
import { LegacyApiService } from '../Api/ApiService'
|
||||
import { isErrorResponse, ListedAccount, ListedAccountInfo, ListedAccountInfoResponse } from '@standardnotes/responses'
|
||||
import { NoteMutator, SNActionsExtension, SNNote } from '@standardnotes/models'
|
||||
import { AbstractService, InternalEventBusInterface, MutatorClientInterface } from '@standardnotes/services'
|
||||
import {
|
||||
AbstractService,
|
||||
InternalEventBusInterface,
|
||||
MutatorClientInterface,
|
||||
SyncServiceInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { SNProtectionService } from '../Protection'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
export class ListedService extends AbstractService implements ListedClientInterface {
|
||||
constructor(
|
||||
private apiService: SNApiService,
|
||||
private apiService: LegacyApiService,
|
||||
private itemManager: ItemManager,
|
||||
private settingsService: SNSettingsService,
|
||||
private settingsService: SettingsService,
|
||||
private httpSerivce: DeprecatedHttpService,
|
||||
private protectionService: SNProtectionService,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
|
||||
import { SNSettingsService } from '../Settings'
|
||||
import { SettingsService } from '../Settings'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { SNFeaturesService } from '../Features/FeaturesService'
|
||||
import { FeaturesService } from '../Features/FeaturesService'
|
||||
import { AbstractService, InternalEventBusInterface, SignInStrings } from '@standardnotes/services'
|
||||
|
||||
export class SNMfaService extends AbstractService {
|
||||
constructor(
|
||||
private settingsService: SNSettingsService,
|
||||
private settingsService: SettingsService,
|
||||
private crypto: PureCryptoInterface,
|
||||
private featuresService: SNFeaturesService,
|
||||
private featuresService: FeaturesService,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
|
||||
@@ -10,6 +10,9 @@ import {
|
||||
ApplicationStage,
|
||||
AbstractService,
|
||||
DiagnosticInfo,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
ApplicationStageChangedEventPayload,
|
||||
} from '@standardnotes/services'
|
||||
import { SnjsVersion, isRightVersionGreaterThanLeft } from '../../Version'
|
||||
import { SNLog } from '@Lib/Log'
|
||||
@@ -23,7 +26,7 @@ import { MigrationClasses } from '@Lib/Migrations/Versions'
|
||||
* first launches, and also other steps after the application is unlocked, or after the
|
||||
* first sync completes. Migrations live under /migrations and inherit from the base Migration class.
|
||||
*/
|
||||
export class SNMigrationService extends AbstractService {
|
||||
export class MigrationService extends AbstractService implements InternalEventHandlerInterface {
|
||||
private activeMigrations?: Migration[]
|
||||
private baseMigration!: BaseMigration
|
||||
|
||||
@@ -44,7 +47,7 @@ export class SNMigrationService extends AbstractService {
|
||||
public async initialize(): Promise<void> {
|
||||
await this.runBaseMigrationPreRun()
|
||||
|
||||
const requiredMigrations = SNMigrationService.getRequiredMigrations(await this.getStoredSnjsVersion())
|
||||
const requiredMigrations = MigrationService.getRequiredMigrations(await this.getStoredSnjsVersion())
|
||||
|
||||
this.activeMigrations = this.instantiateMigrationClasses(requiredMigrations)
|
||||
|
||||
@@ -70,13 +73,11 @@ export class SNMigrationService extends AbstractService {
|
||||
await this.baseMigration.preRun()
|
||||
}
|
||||
|
||||
/**
|
||||
* Application instances will call this function directly when they arrive
|
||||
* at a certain migratory state.
|
||||
*/
|
||||
public override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
await super.handleApplicationStage(stage)
|
||||
await this.handleStage(stage)
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
await this.handleStage(stage)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +90,7 @@ export class SNMigrationService extends AbstractService {
|
||||
}
|
||||
|
||||
public async hasPendingMigrations(): Promise<boolean> {
|
||||
const requiredMigrations = SNMigrationService.getRequiredMigrations(await this.getStoredSnjsVersion())
|
||||
const requiredMigrations = MigrationService.getRequiredMigrations(await this.getStoredSnjsVersion())
|
||||
return requiredMigrations.length > 0 || (await this.baseMigration.needsKeychainRepair())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SNUserPrefs, PrefKey, PrefValue, UserPrefsMutator, ItemContent, FillItemContent } from '@standardnotes/models'
|
||||
import { ItemManager } from '../Items/ItemManager'
|
||||
import { SNSingletonManager } from '../Singleton/SingletonManager'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { SingletonManager } from '../Singleton/SingletonManager'
|
||||
import { SyncService } from '../Sync/SyncService'
|
||||
import {
|
||||
AbstractService,
|
||||
InternalEventBusInterface,
|
||||
@@ -10,12 +10,16 @@ import {
|
||||
PreferenceServiceInterface,
|
||||
PreferencesServiceEvent,
|
||||
MutatorClientInterface,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
} from '@standardnotes/services'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
export class SNPreferencesService
|
||||
extends AbstractService<PreferencesServiceEvent>
|
||||
implements PreferenceServiceInterface
|
||||
implements PreferenceServiceInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private shouldReload = true
|
||||
private reloading = false
|
||||
@@ -24,10 +28,10 @@ export class SNPreferencesService
|
||||
private removeSyncObserver?: () => void
|
||||
|
||||
constructor(
|
||||
private singletons: SNSingletonManager,
|
||||
private singletons: SingletonManager,
|
||||
items: ItemManager,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SNSyncService,
|
||||
private sync: SyncService,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
@@ -52,18 +56,19 @@ export class SNPreferencesService
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
public override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
await super.handleApplicationStage(stage)
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
/** Try to read preferences singleton from storage */
|
||||
this.preferences = this.singletons.findSingleton<SNUserPrefs>(
|
||||
ContentType.TYPES.UserPrefs,
|
||||
SNUserPrefs.singletonPredicate,
|
||||
)
|
||||
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
/** Try to read preferences singleton from storage */
|
||||
this.preferences = this.singletons.findSingleton<SNUserPrefs>(
|
||||
ContentType.TYPES.UserPrefs,
|
||||
SNUserPrefs.singletonPredicate,
|
||||
)
|
||||
|
||||
if (this.preferences) {
|
||||
void this.notifyEvent(PreferencesServiceEvent.PreferencesChanged)
|
||||
if (this.preferences) {
|
||||
void this.notifyEvent(PreferencesServiceEvent.PreferencesChanged)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ import {
|
||||
TimingDisplayOption,
|
||||
ProtectionsClientInterface,
|
||||
MutatorClientInterface,
|
||||
InternalEventHandlerInterface,
|
||||
InternalEventInterface,
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
} from '@standardnotes/services'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
@@ -70,7 +74,10 @@ export const ProtectionSessionDurations = [
|
||||
* like viewing a protected note, as well as managing how long that
|
||||
* authentication should be valid for.
|
||||
*/
|
||||
export class SNProtectionService extends AbstractService<ProtectionEvent> implements ProtectionsClientInterface {
|
||||
export class SNProtectionService
|
||||
extends AbstractService<ProtectionEvent>
|
||||
implements ProtectionsClientInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private sessionExpiryTimeout = -1
|
||||
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
|
||||
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
|
||||
@@ -93,13 +100,15 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
override handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.updateSessionExpiryTimer(this.getSessionExpiryDate())
|
||||
this.mobilePasscodeTiming = this.getMobilePasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getMobileBiometricsTiming()
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.updateSessionExpiryTimer(this.getSessionExpiryDate())
|
||||
this.mobilePasscodeTiming = this.getMobilePasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getMobileBiometricsTiming()
|
||||
}
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
public hasProtectionSources(): boolean {
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
InternalFeatureService,
|
||||
InternalFeature,
|
||||
} from '@standardnotes/services'
|
||||
import { Base64String, PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { Base64String, PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import {
|
||||
SessionBody,
|
||||
ErrorTag,
|
||||
@@ -52,9 +52,9 @@ import * as Common from '@standardnotes/common'
|
||||
|
||||
import { RawStorageValue } from './Sessions/Types'
|
||||
import { ShareToken } from './ShareToken'
|
||||
import { SNApiService } from '../Api/ApiService'
|
||||
import { LegacyApiService } from '../Api/ApiService'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { SNWebSocketsService } from '../Api/WebsocketsService'
|
||||
import { WebSocketsService } from '../Api/WebsocketsService'
|
||||
import { Strings } from '@Lib/Strings'
|
||||
import { UuidString } from '@Lib/Types/UuidString'
|
||||
import { ChallengeResponse, ChallengeService } from '../Challenge'
|
||||
@@ -78,7 +78,7 @@ const cleanedEmailString = (email: string) => {
|
||||
* server credentials, such as the session token. It also exposes methods for registering
|
||||
* for a new account, signing into an existing one, or changing an account password.
|
||||
*/
|
||||
export class SNSessionManager
|
||||
export class SessionManager
|
||||
extends AbstractService<SessionEvent>
|
||||
implements SessionsClientInterface, InternalEventHandlerInterface
|
||||
{
|
||||
@@ -87,13 +87,14 @@ export class SNSessionManager
|
||||
private session?: Session | LegacySession
|
||||
|
||||
constructor(
|
||||
private diskStorageService: DiskStorageService,
|
||||
private apiService: SNApiService,
|
||||
private storage: DiskStorageService,
|
||||
private apiService: LegacyApiService,
|
||||
private userApiService: UserApiServiceInterface,
|
||||
private alertService: AlertService,
|
||||
private encryptionService: EncryptionService,
|
||||
private crypto: PureCryptoInterface,
|
||||
private challengeService: ChallengeService,
|
||||
private webSocketsService: SNWebSocketsService,
|
||||
private webSocketsService: WebSocketsService,
|
||||
private httpService: HttpServiceInterface,
|
||||
private sessionStorageMapper: MapperInterface<Session, Record<string, unknown>>,
|
||||
private legacySessionStorageMapper: MapperInterface<LegacySession, Record<string, unknown>>,
|
||||
@@ -118,7 +119,7 @@ export class SNSessionManager
|
||||
|
||||
override deinit(): void {
|
||||
;(this.encryptionService as unknown) = undefined
|
||||
;(this.diskStorageService as unknown) = undefined
|
||||
;(this.storage as unknown) = undefined
|
||||
;(this.apiService as unknown) = undefined
|
||||
;(this.alertService as unknown) = undefined
|
||||
;(this.challengeService as unknown) = undefined
|
||||
@@ -141,17 +142,17 @@ export class SNSessionManager
|
||||
this.apiService.setUser(user)
|
||||
}
|
||||
|
||||
async initializeFromDisk() {
|
||||
this.memoizeUser(this.diskStorageService.getValue(StorageKey.User))
|
||||
async initializeFromDisk(): Promise<void> {
|
||||
this.memoizeUser(this.storage.getValue(StorageKey.User))
|
||||
|
||||
if (!this.user) {
|
||||
const legacyUuidLookup = this.diskStorageService.getValue<string>(StorageKey.LegacyUuid)
|
||||
const legacyUuidLookup = this.storage.getValue<string>(StorageKey.LegacyUuid)
|
||||
if (legacyUuidLookup) {
|
||||
this.memoizeUser({ uuid: legacyUuidLookup, email: legacyUuidLookup })
|
||||
}
|
||||
}
|
||||
|
||||
const rawSession = this.diskStorageService.getValue<RawStorageValue>(StorageKey.Session)
|
||||
const rawSession = this.storage.getValue<RawStorageValue>(StorageKey.Session)
|
||||
if (rawSession) {
|
||||
try {
|
||||
const session =
|
||||
@@ -286,7 +287,7 @@ export class SNSessionManager
|
||||
email,
|
||||
password,
|
||||
false,
|
||||
this.diskStorageService.isEphemeralSession(),
|
||||
this.storage.isEphemeralSession(),
|
||||
currentKeyParams?.version,
|
||||
)
|
||||
if (isErrorResponse(response)) {
|
||||
@@ -630,10 +631,17 @@ export class SNSessionManager
|
||||
if (!isErrorResponse(rawResponse)) {
|
||||
if (InternalFeatureService.get().isFeatureEnabled(InternalFeature.Vaults)) {
|
||||
const eventData: UserKeyPairChangedEventData = {
|
||||
oldKeyPair,
|
||||
oldSigningKeyPair,
|
||||
newKeyPair: parameters.newRootKey.encryptionKeyPair,
|
||||
newSigningKeyPair: parameters.newRootKey.signingKeyPair,
|
||||
previous:
|
||||
oldKeyPair && oldSigningKeyPair
|
||||
? {
|
||||
encryption: oldKeyPair,
|
||||
signing: oldSigningKeyPair,
|
||||
}
|
||||
: undefined,
|
||||
current: {
|
||||
encryption: parameters.newRootKey.encryptionKeyPair,
|
||||
signing: parameters.newRootKey.signingKeyPair,
|
||||
},
|
||||
}
|
||||
|
||||
void this.notifyEvent(SessionEvent.UserKeyPairChanged, eventData)
|
||||
@@ -692,7 +700,7 @@ export class SNSessionManager
|
||||
}
|
||||
|
||||
private decodeDemoShareToken(token: Base64String): ShareToken {
|
||||
const jsonString = this.encryptionService.crypto.base64Decode(token)
|
||||
const jsonString = this.crypto.base64Decode(token)
|
||||
return JSON.parse(jsonString)
|
||||
}
|
||||
|
||||
@@ -712,7 +720,7 @@ export class SNSessionManager
|
||||
await this.encryptionService.setRootKey(rootKey, wrappingKey)
|
||||
|
||||
this.memoizeUser(user)
|
||||
this.diskStorageService.setValue(StorageKey.User, user)
|
||||
this.storage.setValue(StorageKey.User, user)
|
||||
|
||||
void this.apiService.setHost(host)
|
||||
this.httpService.setHost(host)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { SNApiService } from '../Api/ApiService'
|
||||
import { LegacyApiService } from '../Api/ApiService'
|
||||
import { SettingsGateway } from './SettingsGateway'
|
||||
import { SNSessionManager } from '../Session/SessionManager'
|
||||
import { SessionManager } from '../Session/SessionManager'
|
||||
import { EmailBackupFrequency, SettingName } from '@standardnotes/settings'
|
||||
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { SettingsClientInterface } from './SettingsClientInterface'
|
||||
|
||||
export class SNSettingsService extends AbstractService implements SettingsClientInterface {
|
||||
export class SettingsService extends AbstractService implements SettingsClientInterface {
|
||||
private provider!: SettingsGateway
|
||||
private frequencyOptionsLabels = {
|
||||
[EmailBackupFrequency.Disabled]: 'No email backups',
|
||||
@@ -14,8 +14,8 @@ export class SNSettingsService extends AbstractService implements SettingsClient
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly sessionManager: SNSessionManager,
|
||||
private readonly apiService: SNApiService,
|
||||
private readonly sessionManager: SessionManager,
|
||||
private readonly apiService: LegacyApiService,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
Predicate,
|
||||
} from '@standardnotes/models'
|
||||
import { arrayByRemovingFromIndex, extendArray, UuidGenerator } from '@standardnotes/utils'
|
||||
import { SNSyncService } from '../Sync/SyncService'
|
||||
import { SyncService } from '../Sync/SyncService'
|
||||
import {
|
||||
AbstractService,
|
||||
InternalEventBusInterface,
|
||||
@@ -33,7 +33,7 @@ import { ContentType } from '@standardnotes/domain-core'
|
||||
* 2. Items can override isSingleton, singletonPredicate, and strategyWhenConflictingWithItem (optional)
|
||||
* to automatically gain singleton resolution.
|
||||
*/
|
||||
export class SNSingletonManager extends AbstractService implements SingletonManagerInterface {
|
||||
export class SingletonManager extends AbstractService implements SingletonManagerInterface {
|
||||
private resolveQueue: DecryptedItemInterface[] = []
|
||||
|
||||
private removeItemObserver!: () => void
|
||||
@@ -43,7 +43,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
private itemManager: ItemManager,
|
||||
private mutator: MutatorClientInterface,
|
||||
private payloadManager: PayloadManager,
|
||||
private syncService: SNSyncService,
|
||||
private sync: SyncService,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
@@ -51,7 +51,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
}
|
||||
|
||||
public override deinit(): void {
|
||||
;(this.syncService as unknown) = undefined
|
||||
;(this.sync as unknown) = undefined
|
||||
;(this.mutator as unknown) = undefined
|
||||
;(this.itemManager as unknown) = undefined
|
||||
;(this.payloadManager as unknown) = undefined
|
||||
@@ -93,7 +93,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
}
|
||||
})
|
||||
|
||||
this.removeSyncObserver = this.syncService.addEventObserver(async (eventName) => {
|
||||
this.removeSyncObserver = this.sync.addEventObserver(async (eventName) => {
|
||||
if (
|
||||
eventName === SyncEvent.DownloadFirstSyncCompleted ||
|
||||
eventName === SyncEvent.SyncCompletedWithAllItemsUploaded
|
||||
@@ -142,7 +142,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
* of a download-first request.
|
||||
*/
|
||||
if (handled.length > 0 && eventSource === SyncEvent.SyncCompletedWithAllItemsUploaded) {
|
||||
await this.syncService?.sync({ sourceDescription: 'Resolve singletons for items' })
|
||||
await this.sync?.sync({ sourceDescription: 'Resolve singletons for items' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
}
|
||||
|
||||
/** Item not found, safe to create after full sync has completed */
|
||||
if (!this.syncService.getLastSyncDate()) {
|
||||
if (!this.sync.getLastSyncDate()) {
|
||||
/**
|
||||
* Add a temporary observer in case of long-running sync request, where
|
||||
* the item we're looking for ends up resolving early or in the middle.
|
||||
@@ -199,7 +199,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
}
|
||||
})
|
||||
|
||||
await this.syncService.sync({ sourceDescription: 'Find or create singleton, before any sync has completed' })
|
||||
await this.sync.sync({ sourceDescription: 'Find or create singleton, before any sync has completed' })
|
||||
|
||||
removeObserver()
|
||||
|
||||
@@ -233,7 +233,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
|
||||
const item = await this.mutator.emitItemFromPayload(dirtyPayload, PayloadEmitSource.LocalInserted)
|
||||
|
||||
void this.syncService.sync({ sourceDescription: 'After find or create singleton' })
|
||||
void this.sync.sync({ sourceDescription: 'After find or create singleton' })
|
||||
|
||||
return item as T
|
||||
}
|
||||
@@ -248,7 +248,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
}
|
||||
|
||||
/** Item not found, safe to create after full sync has completed */
|
||||
if (!this.syncService.getLastSyncDate()) {
|
||||
if (!this.sync.getLastSyncDate()) {
|
||||
/**
|
||||
* Add a temporary observer in case of long-running sync request, where
|
||||
* the item we're looking for ends up resolving early or in the middle.
|
||||
@@ -265,7 +265,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
}
|
||||
})
|
||||
|
||||
await this.syncService.sync({ sourceDescription: 'Find or create singleton, before any sync has completed' })
|
||||
await this.sync.sync({ sourceDescription: 'Find or create singleton, before any sync has completed' })
|
||||
|
||||
removeObserver()
|
||||
|
||||
@@ -292,7 +292,7 @@ export class SNSingletonManager extends AbstractService implements SingletonMana
|
||||
|
||||
const item = await this.mutator.emitItemFromPayload(dirtyPayload, PayloadEmitSource.LocalInserted)
|
||||
|
||||
void this.syncService.sync({ sourceDescription: 'After find or create singleton' })
|
||||
void this.sync.sync({ sourceDescription: 'After find or create singleton' })
|
||||
|
||||
return item as T
|
||||
}
|
||||
|
||||
@@ -1,8 +1,33 @@
|
||||
import { Copy, extendArray, UuidGenerator, Uuids } from '@standardnotes/utils'
|
||||
import { SNLog } from '../../Log'
|
||||
import { isErrorDecryptingParameters, SNRootKey } from '@standardnotes/encryption'
|
||||
import * as Encryption from '@standardnotes/encryption'
|
||||
import * as Services from '@standardnotes/services'
|
||||
import {
|
||||
KeyedDecryptionSplit,
|
||||
KeyedEncryptionSplit,
|
||||
SplitPayloadsByEncryptionType,
|
||||
CreateEncryptionSplitWithKeyLookup,
|
||||
isErrorDecryptingParameters,
|
||||
SNRootKey,
|
||||
} from '@standardnotes/encryption'
|
||||
import {
|
||||
AbstractService,
|
||||
StorageServiceInterface,
|
||||
InternalEventHandlerInterface,
|
||||
StoragePersistencePolicies,
|
||||
StorageValuesObject,
|
||||
DeviceInterface,
|
||||
InternalEventBusInterface,
|
||||
InternalEventInterface,
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
ApplicationStage,
|
||||
ValueModesKeys,
|
||||
StorageValueModes,
|
||||
namespacedKey,
|
||||
RawStorageKey,
|
||||
WrappedStorageValue,
|
||||
ValuesObjectRecord,
|
||||
EncryptionProviderInterface,
|
||||
} from '@standardnotes/services'
|
||||
import {
|
||||
CreateDecryptedLocalStorageContextPayload,
|
||||
CreateDeletedLocalStorageContextPayload,
|
||||
@@ -31,25 +56,28 @@ import { ContentType } from '@standardnotes/domain-core'
|
||||
* decrypt the persisted key/values, and also a method to determine whether a particular
|
||||
* key can decrypt wrapped storage.
|
||||
*/
|
||||
export class DiskStorageService extends Services.AbstractService implements Services.StorageServiceInterface {
|
||||
private encryptionProvider!: Encryption.EncryptionProviderInterface
|
||||
export class DiskStorageService
|
||||
extends AbstractService
|
||||
implements StorageServiceInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private encryptionProvider!: EncryptionProviderInterface
|
||||
private storagePersistable = false
|
||||
private persistencePolicy!: Services.StoragePersistencePolicies
|
||||
private persistencePolicy!: StoragePersistencePolicies
|
||||
private needsPersist = false
|
||||
private currentPersistPromise?: Promise<Services.StorageValuesObject>
|
||||
private currentPersistPromise?: Promise<StorageValuesObject>
|
||||
|
||||
private values!: Services.StorageValuesObject
|
||||
private values!: StorageValuesObject
|
||||
|
||||
constructor(
|
||||
private deviceInterface: Services.DeviceInterface,
|
||||
private deviceInterface: DeviceInterface,
|
||||
private identifier: string,
|
||||
protected override internalEventBus: Services.InternalEventBusInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
void this.setPersistencePolicy(Services.StoragePersistencePolicies.Default)
|
||||
void this.setPersistencePolicy(StoragePersistencePolicies.Default)
|
||||
}
|
||||
|
||||
public provideEncryptionProvider(provider: Encryption.EncryptionProviderInterface): void {
|
||||
public provideEncryptionProvider(provider: EncryptionProviderInterface): void {
|
||||
this.encryptionProvider = provider
|
||||
}
|
||||
|
||||
@@ -60,21 +88,22 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
super.deinit()
|
||||
}
|
||||
|
||||
override async handleApplicationStage(stage: Services.ApplicationStage) {
|
||||
await super.handleApplicationStage(stage)
|
||||
|
||||
if (stage === Services.ApplicationStage.Launched_10) {
|
||||
this.storagePersistable = true
|
||||
if (this.needsPersist) {
|
||||
void this.persistValuesToDisk()
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
if (stage === ApplicationStage.Launched_10) {
|
||||
this.storagePersistable = true
|
||||
if (this.needsPersist) {
|
||||
void this.persistValuesToDisk()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async setPersistencePolicy(persistencePolicy: Services.StoragePersistencePolicies) {
|
||||
public async setPersistencePolicy(persistencePolicy: StoragePersistencePolicies) {
|
||||
this.persistencePolicy = persistencePolicy
|
||||
|
||||
if (this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral) {
|
||||
if (this.persistencePolicy === StoragePersistencePolicies.Ephemeral) {
|
||||
await this.deviceInterface.clearNamespacedKeychainValue(this.identifier)
|
||||
await this.deviceInterface.removeAllDatabaseEntries(this.identifier)
|
||||
await this.deviceInterface.removeRawStorageValuesForIdentifier(this.identifier)
|
||||
@@ -82,42 +111,42 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
}
|
||||
}
|
||||
|
||||
public isEphemeralSession() {
|
||||
return this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral
|
||||
public isEphemeralSession(): boolean {
|
||||
return this.persistencePolicy === StoragePersistencePolicies.Ephemeral
|
||||
}
|
||||
|
||||
public async initializeFromDisk() {
|
||||
public async initializeFromDisk(): Promise<void> {
|
||||
const value = await this.deviceInterface.getRawStorageValue(this.getPersistenceKey())
|
||||
const values = value ? JSON.parse(value as string) : undefined
|
||||
|
||||
await this.setInitialValues(values)
|
||||
}
|
||||
|
||||
private async setInitialValues(values?: Services.StorageValuesObject) {
|
||||
private async setInitialValues(values?: StorageValuesObject) {
|
||||
const sureValues = values || this.defaultValuesObject()
|
||||
|
||||
if (!sureValues[Services.ValueModesKeys.Unwrapped]) {
|
||||
sureValues[Services.ValueModesKeys.Unwrapped] = {}
|
||||
if (!sureValues[ValueModesKeys.Unwrapped]) {
|
||||
sureValues[ValueModesKeys.Unwrapped] = {}
|
||||
}
|
||||
|
||||
this.values = sureValues
|
||||
|
||||
if (!this.isStorageWrapped()) {
|
||||
this.values[Services.ValueModesKeys.Unwrapped] = {
|
||||
...(this.values[Services.ValueModesKeys.Wrapped].content as object),
|
||||
...this.values[Services.ValueModesKeys.Unwrapped],
|
||||
this.values[ValueModesKeys.Unwrapped] = {
|
||||
...(this.values[ValueModesKeys.Wrapped].content as object),
|
||||
...this.values[ValueModesKeys.Unwrapped],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public isStorageWrapped(): boolean {
|
||||
const wrappedValue = this.values[Services.ValueModesKeys.Wrapped]
|
||||
const wrappedValue = this.values[ValueModesKeys.Wrapped]
|
||||
|
||||
return wrappedValue != undefined && isEncryptedLocalStoragePayload(wrappedValue)
|
||||
}
|
||||
|
||||
public async canDecryptWithKey(key: SNRootKey): Promise<boolean> {
|
||||
const wrappedValue = this.values[Services.ValueModesKeys.Wrapped]
|
||||
const wrappedValue = this.values[ValueModesKeys.Wrapped]
|
||||
|
||||
if (!isEncryptedLocalStoragePayload(wrappedValue)) {
|
||||
throw Error('Attempting to decrypt non decrypted storage value')
|
||||
@@ -143,7 +172,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
content_type: ContentType.TYPES.EncryptedStorage,
|
||||
})
|
||||
|
||||
const split: Encryption.KeyedDecryptionSplit = key
|
||||
const split: KeyedDecryptionSplit = key
|
||||
? {
|
||||
usesRootKey: {
|
||||
items: [payload],
|
||||
@@ -161,8 +190,8 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
return decryptedPayload
|
||||
}
|
||||
|
||||
public async decryptStorage() {
|
||||
const wrappedValue = this.values[Services.ValueModesKeys.Wrapped]
|
||||
public async decryptStorage(): Promise<void> {
|
||||
const wrappedValue = this.values[ValueModesKeys.Wrapped]
|
||||
|
||||
if (!isEncryptedLocalStoragePayload(wrappedValue)) {
|
||||
throw Error('Attempting to decrypt already decrypted storage')
|
||||
@@ -174,7 +203,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
throw SNLog.error(Error('Unable to decrypt storage.'))
|
||||
}
|
||||
|
||||
this.values[Services.ValueModesKeys.Unwrapped] = Copy(decryptedPayload.content)
|
||||
this.values[ValueModesKeys.Unwrapped] = Copy(decryptedPayload.content)
|
||||
}
|
||||
|
||||
/** @todo This function should be debounced. */
|
||||
@@ -184,7 +213,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
return
|
||||
}
|
||||
|
||||
if (this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral) {
|
||||
if (this.persistencePolicy === StoragePersistencePolicies.Ephemeral) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -195,18 +224,18 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
const values = await this.immediatelyPersistValuesToDisk()
|
||||
|
||||
/** Save the persisted value so we have access to it in memory (for unit tests afawk) */
|
||||
this.values[Services.ValueModesKeys.Wrapped] = values[Services.ValueModesKeys.Wrapped]
|
||||
this.values[ValueModesKeys.Wrapped] = values[ValueModesKeys.Wrapped]
|
||||
}
|
||||
|
||||
public async awaitPersist(): Promise<void> {
|
||||
await this.currentPersistPromise
|
||||
}
|
||||
|
||||
private async immediatelyPersistValuesToDisk(): Promise<Services.StorageValuesObject> {
|
||||
private async immediatelyPersistValuesToDisk(): Promise<StorageValuesObject> {
|
||||
this.currentPersistPromise = this.executeCriticalFunction(async () => {
|
||||
const values = await this.generatePersistableValues()
|
||||
|
||||
const persistencePolicySuddenlyChanged = this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral
|
||||
const persistencePolicySuddenlyChanged = this.persistencePolicy === StoragePersistencePolicies.Ephemeral
|
||||
if (persistencePolicySuddenlyChanged) {
|
||||
return values
|
||||
}
|
||||
@@ -224,10 +253,10 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
* either as a plain object, or an encrypted item.
|
||||
*/
|
||||
private async generatePersistableValues() {
|
||||
const rawContent = <Partial<Services.StorageValuesObject>>Copy(this.values)
|
||||
const rawContent = <Partial<StorageValuesObject>>Copy(this.values)
|
||||
|
||||
const valuesToWrap = rawContent[Services.ValueModesKeys.Unwrapped]
|
||||
rawContent[Services.ValueModesKeys.Unwrapped] = undefined
|
||||
const valuesToWrap = rawContent[ValueModesKeys.Unwrapped]
|
||||
rawContent[ValueModesKeys.Unwrapped] = undefined
|
||||
|
||||
const payload = new DecryptedPayload({
|
||||
uuid: UuidGenerator.GenerateUuid(),
|
||||
@@ -237,7 +266,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
})
|
||||
|
||||
if (this.encryptionProvider.hasRootKeyEncryptionSource()) {
|
||||
const split: Encryption.KeyedEncryptionSplit = {
|
||||
const split: KeyedEncryptionSplit = {
|
||||
usesRootKeyWithKeyLookup: {
|
||||
items: [payload],
|
||||
},
|
||||
@@ -245,31 +274,27 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
|
||||
const encryptedPayload = await this.encryptionProvider.encryptSplitSingle(split)
|
||||
|
||||
rawContent[Services.ValueModesKeys.Wrapped] = CreateEncryptedLocalStorageContextPayload(encryptedPayload)
|
||||
rawContent[ValueModesKeys.Wrapped] = CreateEncryptedLocalStorageContextPayload(encryptedPayload)
|
||||
} else {
|
||||
rawContent[Services.ValueModesKeys.Wrapped] = CreateDecryptedLocalStorageContextPayload(payload)
|
||||
rawContent[ValueModesKeys.Wrapped] = CreateDecryptedLocalStorageContextPayload(payload)
|
||||
}
|
||||
|
||||
return rawContent as Services.StorageValuesObject
|
||||
return rawContent as StorageValuesObject
|
||||
}
|
||||
|
||||
public setValue<T>(key: string, value: T, mode = Services.StorageValueModes.Default): void {
|
||||
public setValue<T>(key: string, value: T, mode = StorageValueModes.Default): void {
|
||||
this.setValueWithNoPersist(key, value, mode)
|
||||
|
||||
void this.persistValuesToDisk()
|
||||
}
|
||||
|
||||
public async setValueAndAwaitPersist(
|
||||
key: string,
|
||||
value: unknown,
|
||||
mode = Services.StorageValueModes.Default,
|
||||
): Promise<void> {
|
||||
public async setValueAndAwaitPersist(key: string, value: unknown, mode = StorageValueModes.Default): Promise<void> {
|
||||
this.setValueWithNoPersist(key, value, mode)
|
||||
|
||||
await this.persistValuesToDisk()
|
||||
}
|
||||
|
||||
private setValueWithNoPersist(key: string, value: unknown, mode = Services.StorageValueModes.Default): void {
|
||||
private setValueWithNoPersist(key: string, value: unknown, mode = StorageValueModes.Default): void {
|
||||
if (!this.values) {
|
||||
throw Error(`Attempting to set storage key ${key} before loading local storage.`)
|
||||
}
|
||||
@@ -279,7 +304,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
domainStorage[key] = value
|
||||
}
|
||||
|
||||
public getValue<T>(key: string, mode = Services.StorageValueModes.Default, defaultValue?: T): T {
|
||||
public getValue<T>(key: string, mode = StorageValueModes.Default, defaultValue?: T): T {
|
||||
if (!this.values) {
|
||||
throw Error(`Attempting to get storage key ${key} before loading local storage.`)
|
||||
}
|
||||
@@ -293,7 +318,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
return value != undefined ? (value as T) : (defaultValue as T)
|
||||
}
|
||||
|
||||
public getAllKeys(mode = Services.StorageValueModes.Default): string[] {
|
||||
public getAllKeys(mode = StorageValueModes.Default): string[] {
|
||||
if (!this.values) {
|
||||
throw Error('Attempting to get all keys before loading local storage.')
|
||||
}
|
||||
@@ -301,7 +326,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
return Object.keys(this.values[this.domainKeyForMode(mode)])
|
||||
}
|
||||
|
||||
public async removeValue(key: string, mode = Services.StorageValueModes.Default): Promise<void> {
|
||||
public async removeValue(key: string, mode = StorageValueModes.Default): Promise<void> {
|
||||
if (!this.values) {
|
||||
throw Error(`Attempting to remove storage key ${key} before loading local storage.`)
|
||||
}
|
||||
@@ -318,34 +343,34 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
* Default persistence key. Platforms can override as needed.
|
||||
*/
|
||||
private getPersistenceKey() {
|
||||
return Services.namespacedKey(this.identifier, Services.RawStorageKey.StorageObject)
|
||||
return namespacedKey(this.identifier, RawStorageKey.StorageObject)
|
||||
}
|
||||
|
||||
private defaultValuesObject(
|
||||
wrapped?: Services.WrappedStorageValue,
|
||||
unwrapped?: Services.ValuesObjectRecord,
|
||||
nonwrapped?: Services.ValuesObjectRecord,
|
||||
wrapped?: WrappedStorageValue,
|
||||
unwrapped?: ValuesObjectRecord,
|
||||
nonwrapped?: ValuesObjectRecord,
|
||||
) {
|
||||
return DiskStorageService.DefaultValuesObject(wrapped, unwrapped, nonwrapped)
|
||||
}
|
||||
|
||||
public static DefaultValuesObject(
|
||||
wrapped: Services.WrappedStorageValue = {} as Services.WrappedStorageValue,
|
||||
unwrapped: Services.ValuesObjectRecord = {},
|
||||
nonwrapped: Services.ValuesObjectRecord = {},
|
||||
wrapped: WrappedStorageValue = {} as WrappedStorageValue,
|
||||
unwrapped: ValuesObjectRecord = {},
|
||||
nonwrapped: ValuesObjectRecord = {},
|
||||
) {
|
||||
return {
|
||||
[Services.ValueModesKeys.Wrapped]: wrapped,
|
||||
[Services.ValueModesKeys.Unwrapped]: unwrapped,
|
||||
[Services.ValueModesKeys.Nonwrapped]: nonwrapped,
|
||||
} as Services.StorageValuesObject
|
||||
[ValueModesKeys.Wrapped]: wrapped,
|
||||
[ValueModesKeys.Unwrapped]: unwrapped,
|
||||
[ValueModesKeys.Nonwrapped]: nonwrapped,
|
||||
} as StorageValuesObject
|
||||
}
|
||||
|
||||
private domainKeyForMode(mode: Services.StorageValueModes) {
|
||||
if (mode === Services.StorageValueModes.Default) {
|
||||
return Services.ValueModesKeys.Unwrapped
|
||||
} else if (mode === Services.StorageValueModes.Nonwrapped) {
|
||||
return Services.ValueModesKeys.Nonwrapped
|
||||
private domainKeyForMode(mode: StorageValueModes) {
|
||||
if (mode === StorageValueModes.Default) {
|
||||
return ValueModesKeys.Unwrapped
|
||||
} else if (mode === StorageValueModes.Nonwrapped) {
|
||||
return ValueModesKeys.Nonwrapped
|
||||
} else {
|
||||
throw Error('Invalid mode')
|
||||
}
|
||||
@@ -368,7 +393,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
}
|
||||
|
||||
public async savePayloads(payloads: FullyFormedPayloadInterface[]): Promise<void> {
|
||||
if (this.persistencePolicy === Services.StoragePersistencePolicies.Ephemeral) {
|
||||
if (this.persistencePolicy === StoragePersistencePolicies.Ephemeral) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -380,7 +405,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
const unencryptable: DecryptedPayloadInterface[] = []
|
||||
|
||||
const { rootKeyEncryption, keySystemRootKeyEncryption, itemsKeyEncryption } =
|
||||
Encryption.SplitPayloadsByEncryptionType(decrypted)
|
||||
SplitPayloadsByEncryptionType(decrypted)
|
||||
|
||||
if (itemsKeyEncryption) {
|
||||
extendArray(encryptable, itemsKeyEncryption)
|
||||
@@ -402,9 +427,9 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
await this.deletePayloads(discardable)
|
||||
}
|
||||
|
||||
const encryptableSplit = Encryption.SplitPayloadsByEncryptionType(encryptable)
|
||||
const encryptableSplit = SplitPayloadsByEncryptionType(encryptable)
|
||||
|
||||
const keyLookupSplit = Encryption.CreateEncryptionSplitWithKeyLookup(encryptableSplit)
|
||||
const keyLookupSplit = CreateEncryptionSplitWithKeyLookup(encryptableSplit)
|
||||
|
||||
const encryptedResults = await this.encryptionProvider.encryptSplit(keyLookupSplit)
|
||||
|
||||
@@ -449,9 +474,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
await this.clearValues()
|
||||
await this.clearAllPayloads()
|
||||
|
||||
await this.deviceInterface.removeRawStorageValue(
|
||||
Services.namespacedKey(this.identifier, Services.RawStorageKey.SnjsVersion),
|
||||
)
|
||||
await this.deviceInterface.removeRawStorageValue(namespacedKey(this.identifier, RawStorageKey.SnjsVersion))
|
||||
|
||||
await this.deviceInterface.removeRawStorageValue(this.getPersistenceKey())
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ServerSyncPushContextualPayload } from '@standardnotes/models'
|
||||
import { arrayByDifference, nonSecureRandomIdentifier, subtractFromArray } from '@standardnotes/utils'
|
||||
import { ServerSyncResponse } from '@Lib/Services/Sync/Account/Response'
|
||||
import { ResponseSignalReceiver, SyncSignal } from '@Lib/Services/Sync/Signals'
|
||||
import { SNApiService } from '../../Api/ApiService'
|
||||
import { LegacyApiService } from '../../Api/ApiService'
|
||||
|
||||
export const SyncUpDownLimit = 150
|
||||
|
||||
@@ -23,7 +23,7 @@ export class AccountSyncOperation {
|
||||
constructor(
|
||||
public readonly payloads: ServerSyncPushContextualPayload[],
|
||||
private receiver: ResponseSignalReceiver<ServerSyncResponse>,
|
||||
private apiService: SNApiService,
|
||||
private apiService: LegacyApiService,
|
||||
public readonly options: {
|
||||
syncToken?: string
|
||||
paginationToken?: string
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { SyncOpStatus } from './SyncOpStatus'
|
||||
import { AbstractService, SyncEvent, SyncOptions } from '@standardnotes/services'
|
||||
|
||||
export interface SyncClientInterface extends AbstractService<SyncEvent> {
|
||||
setLaunchPriorityUuids(launchPriorityUuids: string[]): void
|
||||
sync(options?: Partial<SyncOptions>): Promise<unknown>
|
||||
isOutOfSync(): boolean
|
||||
getLastSyncDate(): Date | undefined
|
||||
getSyncStatus(): SyncOpStatus
|
||||
lockSyncing(): void
|
||||
unlockSyncing(): void
|
||||
|
||||
completedOnlineDownloadFirstSync: boolean
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import { SyncEvent, SyncEventReceiver } from '@standardnotes/services'
|
||||
|
||||
const HEALTHY_SYNC_DURATION_THRESHOLD_S = 5
|
||||
const TIMING_MONITOR_POLL_FREQUENCY_MS = 500
|
||||
|
||||
export class SyncOpStatus {
|
||||
error?: any
|
||||
private interval: any
|
||||
private receiver: SyncEventReceiver
|
||||
private completedUpload = 0
|
||||
private totalUpload = 0
|
||||
private downloaded = 0
|
||||
private databaseLoadCurrent = 0
|
||||
private databaseLoadTotal = 0
|
||||
private databaseLoadDone = false
|
||||
private syncing = false
|
||||
private syncStart!: Date
|
||||
private timingMonitor?: any
|
||||
|
||||
constructor(interval: any, receiver: SyncEventReceiver) {
|
||||
this.interval = interval
|
||||
this.receiver = receiver
|
||||
}
|
||||
|
||||
public deinit() {
|
||||
this.stopTimingMonitor()
|
||||
}
|
||||
|
||||
public setUploadStatus(completed: number, total: number) {
|
||||
this.completedUpload = completed
|
||||
this.totalUpload = total
|
||||
this.receiver(SyncEvent.StatusChanged)
|
||||
}
|
||||
|
||||
public setDownloadStatus(downloaded: number) {
|
||||
this.downloaded += downloaded
|
||||
this.receiver(SyncEvent.StatusChanged)
|
||||
}
|
||||
|
||||
public setDatabaseLoadStatus(current: number, total: number, done: boolean) {
|
||||
this.databaseLoadCurrent = current
|
||||
this.databaseLoadTotal = total
|
||||
this.databaseLoadDone = done
|
||||
if (done) {
|
||||
this.receiver(SyncEvent.LocalDataLoaded)
|
||||
} else {
|
||||
this.receiver(SyncEvent.LocalDataIncrementalLoad)
|
||||
}
|
||||
}
|
||||
|
||||
public getStats() {
|
||||
return {
|
||||
uploadCompletionCount: this.completedUpload,
|
||||
uploadTotalCount: this.totalUpload,
|
||||
downloadCount: this.downloaded,
|
||||
localDataDone: this.databaseLoadDone,
|
||||
localDataCurrent: this.databaseLoadCurrent,
|
||||
localDataTotal: this.databaseLoadTotal,
|
||||
}
|
||||
}
|
||||
|
||||
public setDidBegin() {
|
||||
this.syncing = true
|
||||
this.syncStart = new Date()
|
||||
}
|
||||
|
||||
public setDidEnd() {
|
||||
this.syncing = false
|
||||
}
|
||||
|
||||
get syncInProgress() {
|
||||
return this.syncing === true
|
||||
}
|
||||
|
||||
get secondsSinceSyncStart() {
|
||||
return (new Date().getTime() - this.syncStart.getTime()) / 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies receiver if current sync request is taking too long to complete.
|
||||
*/
|
||||
startTimingMonitor(): void {
|
||||
if (this.timingMonitor) {
|
||||
this.stopTimingMonitor()
|
||||
}
|
||||
|
||||
this.timingMonitor = this.interval(() => {
|
||||
if (this.secondsSinceSyncStart > HEALTHY_SYNC_DURATION_THRESHOLD_S) {
|
||||
this.receiver(SyncEvent.SyncTakingTooLong)
|
||||
this.stopTimingMonitor()
|
||||
}
|
||||
}, TIMING_MONITOR_POLL_FREQUENCY_MS)
|
||||
}
|
||||
|
||||
stopTimingMonitor(): void {
|
||||
if (Object.prototype.hasOwnProperty.call(this.interval, 'cancel')) {
|
||||
this.interval.cancel(this.timingMonitor)
|
||||
} else {
|
||||
clearInterval(this.timingMonitor)
|
||||
}
|
||||
this.timingMonitor = null
|
||||
}
|
||||
|
||||
hasError(): boolean {
|
||||
return !!this.error
|
||||
}
|
||||
|
||||
setError(error: any): void {
|
||||
this.error = error
|
||||
}
|
||||
|
||||
clearError() {
|
||||
this.error = null
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.downloaded = 0
|
||||
this.completedUpload = 0
|
||||
this.totalUpload = 0
|
||||
this.syncing = false
|
||||
this.error = null
|
||||
this.stopTimingMonitor()
|
||||
this.receiver(SyncEvent.StatusChanged)
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,12 @@ import {
|
||||
import { ItemManager } from '@Lib/Services/Items/ItemManager'
|
||||
import { OfflineSyncOperation } from '@Lib/Services/Sync/Offline/Operation'
|
||||
import { PayloadManager } from '../Payloads/PayloadManager'
|
||||
import { SNApiService } from '../Api/ApiService'
|
||||
import { SNHistoryManager } from '../History/HistoryManager'
|
||||
import { LegacyApiService } from '../Api/ApiService'
|
||||
import { HistoryManager } from '../History/HistoryManager'
|
||||
import { SNLog } from '@Lib/Log'
|
||||
import { SNSessionManager } from '../Session/SessionManager'
|
||||
import { SessionManager } from '../Session/SessionManager'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { SyncClientInterface } from './SyncClientInterface'
|
||||
import { SyncPromise } from './Types'
|
||||
import { SyncOpStatus } from '@Lib/Services/Sync/SyncOpStatus'
|
||||
import { ServerSyncResponse } from '@Lib/Services/Sync/Account/Response'
|
||||
import { ServerSyncResponseResolver } from '@Lib/Services/Sync/Account/ResponseResolver'
|
||||
import { SyncSignal, SyncStats } from '@Lib/Services/Sync/Signals'
|
||||
@@ -84,6 +82,7 @@ import {
|
||||
SyncEventReceivedRemoteSharedVaultsData,
|
||||
SyncEventReceivedUserEventsData,
|
||||
SyncEventReceivedAsymmetricMessagesData,
|
||||
SyncOpStatus,
|
||||
} from '@standardnotes/services'
|
||||
import { OfflineSyncResponse } from './Offline/Response'
|
||||
import {
|
||||
@@ -121,9 +120,9 @@ const ContentTypeLocalLoadPriorty = [
|
||||
* After each sync request, any changes made or retrieved are also persisted locally.
|
||||
* The sync service largely does not perform any task unless it is called upon.
|
||||
*/
|
||||
export class SNSyncService
|
||||
export class SyncService
|
||||
extends AbstractService<SyncEvent>
|
||||
implements SyncServiceInterface, InternalEventHandlerInterface, SyncClientInterface
|
||||
implements SyncServiceInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private dirtyIndexAtLastPresyncSave?: number
|
||||
private lastSyncDate?: Date
|
||||
@@ -152,12 +151,12 @@ export class SNSyncService
|
||||
|
||||
constructor(
|
||||
private itemManager: ItemManager,
|
||||
private sessionManager: SNSessionManager,
|
||||
private sessionManager: SessionManager,
|
||||
private encryptionService: EncryptionService,
|
||||
private storageService: DiskStorageService,
|
||||
private payloadManager: PayloadManager,
|
||||
private apiService: SNApiService,
|
||||
private historyService: SNHistoryManager,
|
||||
private apiService: LegacyApiService,
|
||||
private historyService: HistoryManager,
|
||||
private device: DeviceInterface,
|
||||
private identifier: string,
|
||||
private readonly options: ApplicationSyncOptions,
|
||||
@@ -968,25 +967,25 @@ export class SNSyncService
|
||||
|
||||
const historyMap = this.historyService.getHistoryMapCopy()
|
||||
|
||||
if (response.userEvents) {
|
||||
if (response.userEvents && response.userEvents.length > 0) {
|
||||
await this.notifyEventSync(SyncEvent.ReceivedUserEvents, response.userEvents as SyncEventReceivedUserEventsData)
|
||||
}
|
||||
|
||||
if (response.asymmetricMessages) {
|
||||
if (response.asymmetricMessages && response.asymmetricMessages.length > 0) {
|
||||
await this.notifyEventSync(
|
||||
SyncEvent.ReceivedAsymmetricMessages,
|
||||
response.asymmetricMessages as SyncEventReceivedAsymmetricMessagesData,
|
||||
)
|
||||
}
|
||||
|
||||
if (response.vaults) {
|
||||
if (response.vaults && response.vaults.length > 0) {
|
||||
await this.notifyEventSync(
|
||||
SyncEvent.ReceivedRemoteSharedVaults,
|
||||
response.vaults as SyncEventReceivedRemoteSharedVaultsData,
|
||||
)
|
||||
}
|
||||
|
||||
if (response.vaultInvites) {
|
||||
if (response.vaultInvites && response.vaultInvites.length > 0) {
|
||||
await this.notifyEventSync(
|
||||
SyncEvent.ReceivedSharedVaultInvites,
|
||||
response.vaultInvites as SyncEventReceivedSharedVaultInvitesData,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
export * from './SyncService'
|
||||
export * from './Types'
|
||||
export * from './SyncOpStatus'
|
||||
export * from './SyncClientInterface'
|
||||
export * from './Account/Operation'
|
||||
export * from './Account/ResponseResolver'
|
||||
export * from './Offline/Operation'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './Application'
|
||||
export * from './Application/Dependencies/Types'
|
||||
export * from './ApplicationGroup'
|
||||
export * from './Client'
|
||||
export * from './Domain'
|
||||
|
||||
Reference in New Issue
Block a user