chore: handle notifications from websockets (#2472)
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { MfaService } from './../Services/Mfa/MfaService'
|
||||
import { KeyRecoveryService } from './../Services/KeyRecovery/KeyRecoveryService'
|
||||
import { WebSocketsService } from './../Services/Api/WebsocketsService'
|
||||
import { MigrationService } from './../Services/Migration/MigrationService'
|
||||
import { LegacyApiService } from './../Services/Api/ApiService'
|
||||
import { FeaturesService } from '@Lib/Services/Features/FeaturesService'
|
||||
@@ -83,6 +82,7 @@ import {
|
||||
CreateEncryptedBackupFile,
|
||||
GetTransitionStatus,
|
||||
StartTransition,
|
||||
WebSocketsService,
|
||||
} from '@standardnotes/services'
|
||||
import {
|
||||
SNNote,
|
||||
|
||||
@@ -22,7 +22,6 @@ import { ProtectionService } from '../../Services/Protection/ProtectionService'
|
||||
import { SyncService } from '../../Services/Sync/SyncService'
|
||||
import { HistoryManager } from '../../Services/History/HistoryManager'
|
||||
import { SessionManager } from '../../Services/Session/SessionManager'
|
||||
import { WebSocketsService } from '../../Services/Api/WebsocketsService'
|
||||
import { LegacyApiService } from '../../Services/Api/ApiService'
|
||||
import { SnjsVersion } from '../../Version'
|
||||
import { DeprecatedHttpService } from '../../Services/Api/DeprecatedHttpService'
|
||||
@@ -143,6 +142,7 @@ import {
|
||||
SyncLocalVaultsWithRemoteSharedVaults,
|
||||
GetTransitionStatus,
|
||||
StartTransition,
|
||||
WebSocketsService,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '../../Services/Items/ItemManager'
|
||||
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export enum WebSocketsServiceEvent {
|
||||
UserRoleMessageReceived = 'WebSocketMessageReceived',
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { WebSocketApiServiceInterface } from '@standardnotes/api'
|
||||
|
||||
import { StorageKey, DiskStorageService } from '@Lib/index'
|
||||
import { WebSocketsService } from './WebsocketsService'
|
||||
|
||||
describe('webSocketsService', () => {
|
||||
const webSocketUrl = ''
|
||||
|
||||
let storageService: DiskStorageService
|
||||
let webSocketApiService: WebSocketApiServiceInterface
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () => {
|
||||
return new WebSocketsService(storageService, webSocketUrl, webSocketApiService, internalEventBus)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
storageService = {} as jest.Mocked<DiskStorageService>
|
||||
storageService.setValue = jest.fn()
|
||||
|
||||
internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||
internalEventBus.publish = jest.fn()
|
||||
|
||||
webSocketApiService = {} as jest.Mocked<WebSocketApiServiceInterface>
|
||||
webSocketApiService.createConnectionToken = jest.fn().mockReturnValue({ token: 'foobar' })
|
||||
})
|
||||
|
||||
describe('setWebSocketUrl()', () => {
|
||||
it('saves url in local storage', async () => {
|
||||
const webSocketUrl = 'wss://test-websocket'
|
||||
await createService().setWebSocketUrl(webSocketUrl)
|
||||
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.WebSocketUrl, webSocketUrl)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,96 +0,0 @@
|
||||
import { isErrorResponse } from '@standardnotes/responses'
|
||||
import { UserRolesChangedEvent } from '@standardnotes/domain-events'
|
||||
import {
|
||||
AbstractService,
|
||||
InternalEventBusInterface,
|
||||
StorageKey,
|
||||
StorageServiceInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { WebSocketApiServiceInterface } from '@standardnotes/api'
|
||||
import { WebSocketsServiceEvent } from './WebSocketsServiceEvent'
|
||||
|
||||
export class WebSocketsService extends AbstractService<WebSocketsServiceEvent, UserRolesChangedEvent> {
|
||||
private webSocket?: WebSocket
|
||||
|
||||
constructor(
|
||||
private storageService: StorageServiceInterface,
|
||||
private webSocketUrl: string | undefined,
|
||||
private webSocketApiService: WebSocketApiServiceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
}
|
||||
|
||||
public setWebSocketUrl(url: string | undefined): void {
|
||||
this.webSocketUrl = url
|
||||
this.storageService.setValue(StorageKey.WebSocketUrl, url)
|
||||
}
|
||||
|
||||
public loadWebSocketUrl(): void {
|
||||
const storedValue = this.storageService.getValue<string | undefined>(StorageKey.WebSocketUrl)
|
||||
this.webSocketUrl =
|
||||
storedValue ||
|
||||
this.webSocketUrl ||
|
||||
(
|
||||
window as {
|
||||
_websocket_url?: string
|
||||
}
|
||||
)._websocket_url
|
||||
}
|
||||
|
||||
async startWebSocketConnection(): Promise<void> {
|
||||
if (!this.webSocketUrl) {
|
||||
return
|
||||
}
|
||||
|
||||
const webSocketConectionToken = await this.createWebSocketConnectionToken()
|
||||
if (webSocketConectionToken === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
this.webSocket = new WebSocket(`${this.webSocketUrl}?authToken=${webSocketConectionToken}`)
|
||||
this.webSocket.onmessage = this.onWebSocketMessage.bind(this)
|
||||
this.webSocket.onclose = this.onWebSocketClose.bind(this)
|
||||
} catch (e) {
|
||||
console.error('Error starting WebSocket connection', e)
|
||||
}
|
||||
}
|
||||
|
||||
public closeWebSocketConnection(): void {
|
||||
this.webSocket?.close()
|
||||
}
|
||||
|
||||
private onWebSocketMessage(event: MessageEvent) {
|
||||
const eventData: UserRolesChangedEvent = JSON.parse(event.data)
|
||||
void this.notifyEvent(WebSocketsServiceEvent.UserRoleMessageReceived, eventData)
|
||||
}
|
||||
|
||||
private onWebSocketClose() {
|
||||
this.webSocket = undefined
|
||||
}
|
||||
|
||||
private async createWebSocketConnectionToken(): Promise<string | undefined> {
|
||||
try {
|
||||
const response = await this.webSocketApiService.createConnectionToken()
|
||||
if (isErrorResponse(response)) {
|
||||
console.error(response.data.error)
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
return response.data.token
|
||||
} catch (error) {
|
||||
console.error('Caught error:', (error as Error).message)
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
override deinit(): void {
|
||||
super.deinit()
|
||||
;(this.storageService as unknown) = undefined
|
||||
;(this.webSocketApiService as unknown) = undefined
|
||||
this.closeWebSocketConnection()
|
||||
}
|
||||
}
|
||||
@@ -2,4 +2,3 @@ export * from './ApiService'
|
||||
export * from './DeprecatedHttpService'
|
||||
export * from './Paths'
|
||||
export * from '../Session/SessionManager'
|
||||
export * from './WebsocketsService'
|
||||
|
||||
@@ -4,7 +4,6 @@ import { SettingName } from '@standardnotes/settings'
|
||||
import { FeaturesService } from '@Lib/Services/Features'
|
||||
import { RoleName, ContentType, Uuid, Result } from '@standardnotes/domain-core'
|
||||
import { NativeFeatureIdentifier, GetFeatures } from '@standardnotes/features'
|
||||
import { WebSocketsService } from '../Api/WebsocketsService'
|
||||
import { SettingsService } from '../Settings'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import {
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
UserServiceInterface,
|
||||
UserService,
|
||||
IsApplicationUsingThirdPartyHost,
|
||||
WebSocketsService,
|
||||
} from '@standardnotes/services'
|
||||
import { LegacyApiService, SessionManager } from '../Api'
|
||||
import { ItemManager } from '../Items'
|
||||
|
||||
@@ -3,8 +3,6 @@ import { arraysEqual, removeFromArray, lastElement, LoggerInterface } from '@sta
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { RoleName, ContentType, Uuid } from '@standardnotes/domain-core'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { WebSocketsService } from '../Api/WebsocketsService'
|
||||
import { WebSocketsServiceEvent } from '../Api/WebSocketsServiceEvent'
|
||||
import { UserRolesChangedEvent } from '@standardnotes/domain-events'
|
||||
import { ExperimentalFeatures, FindNativeFeature, NativeFeatureIdentifier } from '@standardnotes/features'
|
||||
import {
|
||||
@@ -47,6 +45,8 @@ import {
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
IsApplicationUsingThirdPartyHost,
|
||||
WebSocketsServiceEvent,
|
||||
WebSocketsService,
|
||||
} from '@standardnotes/services'
|
||||
|
||||
import { DownloadRemoteThirdPartyFeatureUseCase } from './UseCase/DownloadRemoteThirdPartyFeature'
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
ApplicationStage,
|
||||
GetKeyPairs,
|
||||
IsApplicationUsingThirdPartyHost,
|
||||
WebSocketsService,
|
||||
} from '@standardnotes/services'
|
||||
import { Base64String, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import {
|
||||
@@ -59,7 +60,6 @@ import { RawStorageValue } from './Sessions/Types'
|
||||
import { ShareToken } from './ShareToken'
|
||||
import { LegacyApiService } from '../Api/ApiService'
|
||||
import { DiskStorageService } from '../Storage/DiskStorageService'
|
||||
import { WebSocketsService } from '../Api/WebsocketsService'
|
||||
import { Strings } from '@Lib/Strings'
|
||||
import { UuidString } from '@Lib/Types/UuidString'
|
||||
import { ChallengeResponse, ChallengeService } from '../Challenge'
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"@standardnotes/api": "workspace:*",
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.25.0",
|
||||
"@standardnotes/domain-events": "^2.108.1",
|
||||
"@standardnotes/domain-events": "^2.120.0",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/files": "workspace:*",
|
||||
|
||||
Reference in New Issue
Block a user