chore: rely on websockets for autosync if a websocket connection is open (#2672)
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,4 +3,5 @@ export enum WebSocketsServiceEvent {
|
|||||||
NotificationAddedForUser = 'NotificationAddedForUser',
|
NotificationAddedForUser = 'NotificationAddedForUser',
|
||||||
MessageSentToUser = 'MessageSentToUser',
|
MessageSentToUser = 'MessageSentToUser',
|
||||||
UserInvitedToSharedVault = 'UserInvitedToSharedVault',
|
UserInvitedToSharedVault = 'UserInvitedToSharedVault',
|
||||||
|
ItemsChangedOnServer = 'ItemsChangedOnServer',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ export class WebSocketsService extends AbstractService<WebSocketsServiceEvent, D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isWebSocketConnectionOpen(): boolean {
|
||||||
|
return this.webSocket?.readyState === WebSocket.OPEN
|
||||||
|
}
|
||||||
|
|
||||||
public closeWebSocketConnection(): void {
|
public closeWebSocketConnection(): void {
|
||||||
this.webSocket?.close()
|
this.webSocket?.close()
|
||||||
}
|
}
|
||||||
@@ -62,6 +66,9 @@ export class WebSocketsService extends AbstractService<WebSocketsServiceEvent, D
|
|||||||
private onWebSocketMessage(messageEvent: MessageEvent) {
|
private onWebSocketMessage(messageEvent: MessageEvent) {
|
||||||
const eventData = JSON.parse(messageEvent.data)
|
const eventData = JSON.parse(messageEvent.data)
|
||||||
switch (eventData.type) {
|
switch (eventData.type) {
|
||||||
|
case 'ITEMS_CHANGED_ON_SERVER':
|
||||||
|
void this.notifyEvent(WebSocketsServiceEvent.ItemsChangedOnServer, eventData)
|
||||||
|
break
|
||||||
case 'USER_ROLES_CHANGED':
|
case 'USER_ROLES_CHANGED':
|
||||||
void this.notifyEvent(WebSocketsServiceEvent.UserRoleMessageReceived, eventData)
|
void this.notifyEvent(WebSocketsServiceEvent.UserRoleMessageReceived, eventData)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export interface SyncServiceInterface extends AbstractService<SyncEvent> {
|
|||||||
isDatabaseLoaded(): boolean
|
isDatabaseLoaded(): boolean
|
||||||
onNewDatabaseCreated(): Promise<void>
|
onNewDatabaseCreated(): Promise<void>
|
||||||
loadDatabasePayloads(): Promise<void>
|
loadDatabasePayloads(): Promise<void>
|
||||||
|
beginAutoSyncTimer(): void
|
||||||
resetSyncState(): void
|
resetSyncState(): void
|
||||||
markAllItemsAsNeedingSyncAndPersist(): Promise<void>
|
markAllItemsAsNeedingSyncAndPersist(): Promise<void>
|
||||||
downloadFirstSync(waitTimeOnFailureMs: number, otherSyncOptions?: Partial<SyncOptions>): Promise<void>
|
downloadFirstSync(waitTimeOnFailureMs: number, otherSyncOptions?: Partial<SyncOptions>): Promise<void>
|
||||||
|
|||||||
@@ -137,9 +137,6 @@ import { TYPES } from './Dependencies/Types'
|
|||||||
import { RegisterApplicationServicesEvents } from './Dependencies/DependencyEvents'
|
import { RegisterApplicationServicesEvents } from './Dependencies/DependencyEvents'
|
||||||
import { Result } from '@standardnotes/domain-core'
|
import { Result } from '@standardnotes/domain-core'
|
||||||
|
|
||||||
/** How often to automatically sync, in milliseconds */
|
|
||||||
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
|
|
||||||
|
|
||||||
type LaunchCallback = {
|
type LaunchCallback = {
|
||||||
receiveChallenge: (challenge: Challenge) => void
|
receiveChallenge: (challenge: Challenge) => void
|
||||||
}
|
}
|
||||||
@@ -165,7 +162,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
|
|
||||||
private serviceObservers: ObserverRemover[] = []
|
private serviceObservers: ObserverRemover[] = []
|
||||||
private managedSubscribers: ObserverRemover[] = []
|
private managedSubscribers: ObserverRemover[] = []
|
||||||
private autoSyncInterval!: ReturnType<typeof setInterval>
|
|
||||||
|
|
||||||
/** True if the result of deviceInterface.openDatabase yields a new database being created */
|
/** True if the result of deviceInterface.openDatabase yields a new database being created */
|
||||||
private createdNewDatabase = false
|
private createdNewDatabase = false
|
||||||
@@ -463,7 +459,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
throw 'Application has been destroyed.'
|
throw 'Application has been destroyed.'
|
||||||
}
|
}
|
||||||
await this.handleStage(ApplicationStage.LoadedDatabase_12)
|
await this.handleStage(ApplicationStage.LoadedDatabase_12)
|
||||||
this.beginAutoSyncTimer()
|
this.sync.beginAutoSyncTimer()
|
||||||
await this.sync.sync({
|
await this.sync.sync({
|
||||||
mode: SyncMode.DownloadFirst,
|
mode: SyncMode.DownloadFirst,
|
||||||
source: SyncSource.External,
|
source: SyncSource.External,
|
||||||
@@ -503,14 +499,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private beginAutoSyncTimer() {
|
|
||||||
this.autoSyncInterval = setInterval(() => {
|
|
||||||
const logger = this.dependencies.get<LoggerInterface>(TYPES.Logger)
|
|
||||||
logger.info('Syncing from autosync')
|
|
||||||
void this.sync.sync({ sourceDescription: 'Auto Sync' })
|
|
||||||
}, DEFAULT_AUTO_SYNC_INTERVAL)
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleStage(stage: ApplicationStage) {
|
private async handleStage(stage: ApplicationStage) {
|
||||||
await this.events.publishSync(
|
await this.events.publishSync(
|
||||||
{
|
{
|
||||||
@@ -743,9 +731,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
public deinit(mode: DeinitMode, source: DeinitSource): void {
|
public deinit(mode: DeinitMode, source: DeinitSource): void {
|
||||||
this.dealloced = true
|
this.dealloced = true
|
||||||
|
|
||||||
clearInterval(this.autoSyncInterval)
|
|
||||||
;(this.autoSyncInterval as unknown) = undefined
|
|
||||||
|
|
||||||
for (const uninstallObserver of this.serviceObservers) {
|
for (const uninstallObserver of this.serviceObservers) {
|
||||||
uninstallObserver()
|
uninstallObserver()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1331,6 +1331,15 @@ export class Dependencies {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.factory.set(TYPES.WebSocketsService, () => {
|
||||||
|
return new WebSocketsService(
|
||||||
|
this.get<DiskStorageService>(TYPES.DiskStorageService),
|
||||||
|
this.options.webSocketUrl,
|
||||||
|
this.get<WebSocketApiService>(TYPES.WebSocketApiService),
|
||||||
|
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
this.factory.set(TYPES.SyncService, () => {
|
this.factory.set(TYPES.SyncService, () => {
|
||||||
return new SyncService(
|
return new SyncService(
|
||||||
this.get<ItemManager>(TYPES.ItemManager),
|
this.get<ItemManager>(TYPES.ItemManager),
|
||||||
@@ -1347,6 +1356,7 @@ export class Dependencies {
|
|||||||
sleepBetweenBatches: this.options.sleepBetweenBatches,
|
sleepBetweenBatches: this.options.sleepBetweenBatches,
|
||||||
},
|
},
|
||||||
this.get<Logger>(TYPES.Logger),
|
this.get<Logger>(TYPES.Logger),
|
||||||
|
this.get<WebSocketsService>(TYPES.WebSocketsService),
|
||||||
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1390,15 +1400,6 @@ export class Dependencies {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.factory.set(TYPES.WebSocketsService, () => {
|
|
||||||
return new WebSocketsService(
|
|
||||||
this.get<DiskStorageService>(TYPES.DiskStorageService),
|
|
||||||
this.options.webSocketUrl,
|
|
||||||
this.get<WebSocketApiService>(TYPES.WebSocketApiService),
|
|
||||||
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.factory.set(TYPES.WebSocketApiService, () => {
|
this.factory.set(TYPES.WebSocketApiService, () => {
|
||||||
return new WebSocketApiService(this.get<WebSocketServer>(TYPES.WebSocketServer))
|
return new WebSocketApiService(this.get<WebSocketServer>(TYPES.WebSocketServer))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export function RegisterApplicationServicesEvents(container: Dependencies, event
|
|||||||
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.UserRolesChanged)
|
events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.UserRolesChanged)
|
||||||
events.addEventHandler(container.get(TYPES.SubscriptionManager), SessionEvent.Restored)
|
events.addEventHandler(container.get(TYPES.SubscriptionManager), SessionEvent.Restored)
|
||||||
events.addEventHandler(container.get(TYPES.SyncService), IntegrityEvent.IntegrityCheckCompleted)
|
events.addEventHandler(container.get(TYPES.SyncService), IntegrityEvent.IntegrityCheckCompleted)
|
||||||
|
events.addEventHandler(container.get(TYPES.SyncService), WebSocketsServiceEvent.ItemsChangedOnServer)
|
||||||
events.addEventHandler(container.get(TYPES.UserService), AccountEvent.SignedInOrRegistered)
|
events.addEventHandler(container.get(TYPES.UserService), AccountEvent.SignedInOrRegistered)
|
||||||
events.addEventHandler(container.get(TYPES.VaultInviteService), ApplicationEvent.Launched)
|
events.addEventHandler(container.get(TYPES.VaultInviteService), ApplicationEvent.Launched)
|
||||||
events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites)
|
events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites)
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ import {
|
|||||||
SyncEventReceivedAsymmetricMessagesData,
|
SyncEventReceivedAsymmetricMessagesData,
|
||||||
SyncOpStatus,
|
SyncOpStatus,
|
||||||
ApplicationSyncOptions,
|
ApplicationSyncOptions,
|
||||||
|
WebSocketsServiceEvent,
|
||||||
|
WebSocketsService,
|
||||||
} from '@standardnotes/services'
|
} from '@standardnotes/services'
|
||||||
import { OfflineSyncResponse } from './Offline/Response'
|
import { OfflineSyncResponse } from './Offline/Response'
|
||||||
import {
|
import {
|
||||||
@@ -98,6 +100,7 @@ import { ContentType } from '@standardnotes/domain-core'
|
|||||||
|
|
||||||
const DEFAULT_MAJOR_CHANGE_THRESHOLD = 15
|
const DEFAULT_MAJOR_CHANGE_THRESHOLD = 15
|
||||||
const INVALID_SESSION_RESPONSE_STATUS = 401
|
const INVALID_SESSION_RESPONSE_STATUS = 401
|
||||||
|
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
|
||||||
|
|
||||||
/** Content types appearing first are always mapped first */
|
/** Content types appearing first are always mapped first */
|
||||||
const ContentTypeLocalLoadPriorty = [
|
const ContentTypeLocalLoadPriorty = [
|
||||||
@@ -149,6 +152,9 @@ export class SyncService
|
|||||||
public lastSyncInvokationPromise?: Promise<unknown>
|
public lastSyncInvokationPromise?: Promise<unknown>
|
||||||
public currentSyncRequestPromise?: Promise<void>
|
public currentSyncRequestPromise?: Promise<void>
|
||||||
|
|
||||||
|
private autoSyncInterval?: NodeJS.Timer
|
||||||
|
private wasNotifiedOfItemsChangeOnServer = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private itemManager: ItemManager,
|
private itemManager: ItemManager,
|
||||||
private sessionManager: SessionManager,
|
private sessionManager: SessionManager,
|
||||||
@@ -161,6 +167,7 @@ export class SyncService
|
|||||||
private identifier: string,
|
private identifier: string,
|
||||||
private readonly options: ApplicationSyncOptions,
|
private readonly options: ApplicationSyncOptions,
|
||||||
private logger: LoggerInterface,
|
private logger: LoggerInterface,
|
||||||
|
private sockets: WebSocketsService,
|
||||||
protected override internalEventBus: InternalEventBusInterface,
|
protected override internalEventBus: InternalEventBusInterface,
|
||||||
) {
|
) {
|
||||||
super(internalEventBus)
|
super(internalEventBus)
|
||||||
@@ -187,6 +194,10 @@ export class SyncService
|
|||||||
|
|
||||||
public override deinit(): void {
|
public override deinit(): void {
|
||||||
this.dealloced = true
|
this.dealloced = true
|
||||||
|
if (this.autoSyncInterval) {
|
||||||
|
clearInterval(this.autoSyncInterval)
|
||||||
|
}
|
||||||
|
;(this.autoSyncInterval as unknown) = undefined
|
||||||
;(this.sessionManager as unknown) = undefined
|
;(this.sessionManager as unknown) = undefined
|
||||||
;(this.itemManager as unknown) = undefined
|
;(this.itemManager as unknown) = undefined
|
||||||
;(this.encryptionService as unknown) = undefined
|
;(this.encryptionService as unknown) = undefined
|
||||||
@@ -349,6 +360,28 @@ export class SyncService
|
|||||||
this.opStatus.setDatabaseLoadStatus(0, 0, true)
|
this.opStatus.setDatabaseLoadStatus(0, 0, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beginAutoSyncTimer(): void {
|
||||||
|
this.autoSyncInterval = setInterval(this.autoSync.bind(this), DEFAULT_AUTO_SYNC_INTERVAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
private autoSync(): void {
|
||||||
|
if (!this.sockets.isWebSocketConnectionOpen()) {
|
||||||
|
this.logger.debug('WebSocket connection is closed, doing autosync')
|
||||||
|
|
||||||
|
void this.sync({ sourceDescription: 'Auto Sync' })
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.wasNotifiedOfItemsChangeOnServer) {
|
||||||
|
this.logger.debug('Was notified of items changed on server, doing autosync')
|
||||||
|
|
||||||
|
this.wasNotifiedOfItemsChangeOnServer = false
|
||||||
|
|
||||||
|
void this.sync({ sourceDescription: 'WebSockets Event - Items Changed On Server' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async processPayloadBatch(
|
private async processPayloadBatch(
|
||||||
batch: FullyFormedPayloadInterface<ItemContent>[],
|
batch: FullyFormedPayloadInterface<ItemContent>[],
|
||||||
currentPosition?: number,
|
currentPosition?: number,
|
||||||
@@ -1400,8 +1433,15 @@ export class SyncService
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||||
if (event.type === IntegrityEvent.IntegrityCheckCompleted) {
|
switch (event.type) {
|
||||||
await this.handleIntegrityCheckEventResponse(event.payload as IntegrityEventPayload)
|
case IntegrityEvent.IntegrityCheckCompleted:
|
||||||
|
await this.handleIntegrityCheckEventResponse(event.payload as IntegrityEventPayload)
|
||||||
|
break
|
||||||
|
case WebSocketsServiceEvent.ItemsChangedOnServer:
|
||||||
|
this.wasNotifiedOfItemsChangeOnServer = true
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
"@standardnotes/api": "workspace:*",
|
"@standardnotes/api": "workspace:*",
|
||||||
"@standardnotes/common": "^1.50.0",
|
"@standardnotes/common": "^1.50.0",
|
||||||
"@standardnotes/domain-core": "^1.40.0",
|
"@standardnotes/domain-core": "^1.40.0",
|
||||||
"@standardnotes/domain-events": "^2.122.0",
|
"@standardnotes/domain-events": "^2.138.1",
|
||||||
"@standardnotes/encryption": "workspace:*",
|
"@standardnotes/encryption": "workspace:*",
|
||||||
"@standardnotes/features": "workspace:*",
|
"@standardnotes/features": "workspace:*",
|
||||||
"@standardnotes/files": "workspace:*",
|
"@standardnotes/files": "workspace:*",
|
||||||
|
|||||||
31
yarn.lock
31
yarn.lock
@@ -6610,7 +6610,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@standardnotes/domain-events@npm:2.138.1":
|
"@standardnotes/domain-events@npm:2.138.1, @standardnotes/domain-events@npm:^2.138.1":
|
||||||
version: 2.138.1
|
version: 2.138.1
|
||||||
resolution: "@standardnotes/domain-events@npm:2.138.1"
|
resolution: "@standardnotes/domain-events@npm:2.138.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -6620,16 +6620,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@standardnotes/domain-events@npm:^2.122.0":
|
|
||||||
version: 2.122.0
|
|
||||||
resolution: "@standardnotes/domain-events@npm:2.122.0"
|
|
||||||
dependencies:
|
|
||||||
"@standardnotes/predicates": 1.6.9
|
|
||||||
"@standardnotes/security": 1.12.0
|
|
||||||
checksum: 9dd2f02f1c9c91b3380d0db88dba3e504aaa14b7e58dcb519611dd8b15edb27c111805eb0e34f1612751fb59428296a216e39b5711892f6c8f1fc1f057e61c50
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@standardnotes/dynamic-theme@npm:^1.2.8":
|
"@standardnotes/dynamic-theme@npm:^1.2.8":
|
||||||
version: 1.2.8
|
version: 1.2.8
|
||||||
resolution: "@standardnotes/dynamic-theme@npm:1.2.8"
|
resolution: "@standardnotes/dynamic-theme@npm:1.2.8"
|
||||||
@@ -6992,13 +6982,6 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@standardnotes/predicates@npm:1.6.9":
|
|
||||||
version: 1.6.9
|
|
||||||
resolution: "@standardnotes/predicates@npm:1.6.9"
|
|
||||||
checksum: 3b931aaccd763c98d043487e90624507265a350d5656ef55a3355b69fbdae2f718ecd2bde1e402d8fc49355404f91220df99b0a80c081e8da0d515eea1def9e7
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@standardnotes/predicates@npm:1.8.1":
|
"@standardnotes/predicates@npm:1.8.1":
|
||||||
version: 1.8.1
|
version: 1.8.1
|
||||||
resolution: "@standardnotes/predicates@npm:1.8.1"
|
resolution: "@standardnotes/predicates@npm:1.8.1"
|
||||||
@@ -7081,16 +7064,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@standardnotes/security@npm:1.12.0":
|
|
||||||
version: 1.12.0
|
|
||||||
resolution: "@standardnotes/security@npm:1.12.0"
|
|
||||||
dependencies:
|
|
||||||
jsonwebtoken: ^9.0.0
|
|
||||||
reflect-metadata: ^0.1.13
|
|
||||||
checksum: 96d42255e79fc2cf4c52f3d370c04ae00296673993e1c0cea2356ca4a6c934d22d6100ba95f9ad602ffb72ee686367ef4a263dc6a6c51795d9b61dc835e635cd
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@standardnotes/security@npm:1.17.2, @standardnotes/security@npm:^1.17.2":
|
"@standardnotes/security@npm:1.17.2, @standardnotes/security@npm:^1.17.2":
|
||||||
version: 1.17.2
|
version: 1.17.2
|
||||||
resolution: "@standardnotes/security@npm:1.17.2"
|
resolution: "@standardnotes/security@npm:1.17.2"
|
||||||
@@ -7239,7 +7212,7 @@ __metadata:
|
|||||||
"@standardnotes/api": "workspace:*"
|
"@standardnotes/api": "workspace:*"
|
||||||
"@standardnotes/common": ^1.50.0
|
"@standardnotes/common": ^1.50.0
|
||||||
"@standardnotes/domain-core": ^1.40.0
|
"@standardnotes/domain-core": ^1.40.0
|
||||||
"@standardnotes/domain-events": ^2.122.0
|
"@standardnotes/domain-events": ^2.138.1
|
||||||
"@standardnotes/encryption": "workspace:*"
|
"@standardnotes/encryption": "workspace:*"
|
||||||
"@standardnotes/features": "workspace:*"
|
"@standardnotes/features": "workspace:*"
|
||||||
"@standardnotes/files": "workspace:*"
|
"@standardnotes/files": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user