diff --git a/CHANGELOG.md b/CHANGELOG.md index dbfe219b55..36baaed49a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency * Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) +* Reorder the closing of services, prevent sagas running multiple times and close backend server properly [2.1.2] diff --git a/packages/backend/ipfs-pubsub-peer-monitor.patch b/packages/backend/ipfs-pubsub-peer-monitor.patch new file mode 100644 index 0000000000..3ee35dc77d --- /dev/null +++ b/packages/backend/ipfs-pubsub-peer-monitor.patch @@ -0,0 +1,11 @@ +--- node_modules/ipfs-pubsub-peer-monitor/src/ipfs-pubsub-peer-monitor.js 2024-05-08 12:44:48 ++++ node_modules/ipfs-pubsub-peer-monitor/src/ipfs-pubsub-peer-monitor.backup.js 2024-05-08 12:44:25 +@@ -55,7 +55,7 @@ + async _pollPeers () { + try { + const peers = await this._pubsub.peers(this._topic) +- IpfsPubsubPeerMonitor._emitJoinsAndLeaves(new Set(this._peers), new Set(peers), this) ++ IpfsPubsubPeerMonitor._emitJoinsAndLeaves(new Set(this._peers.map(p => p.toString())), new Set(peers.map(p => p.toString())), this) + this._peers = peers + } catch (err) { + clearInterval(this._interval) \ No newline at end of file diff --git a/packages/backend/package.json b/packages/backend/package.json index 87312a5a6e..e663a57080 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -12,7 +12,7 @@ "build": "tsc -p tsconfig.build.json", "webpack": "webpack --env mode=development && cp ./lib/bundle.cjs ../backend-bundle/bundle.cjs", "webpack:prod": "webpack --env mode=production && cp ./lib/bundle.cjs ../backend-bundle/bundle.cjs", - "applyPatches": "patch -f -p0 < ./electron-fetch.patch || true && patch -f -p0 --forward --binary < ./parse-duration.patch || true && patch -f -p0 --forward --binary < ./parse-duration-esm.patch || true", + "applyPatches": "patch -f -p0 < ./electron-fetch.patch || true && patch -f -p0 --forward --binary < ./parse-duration.patch || true && patch -f -p0 --forward --binary < ./parse-duration-esm.patch || true && patch -f -p0 < ./ipfs-pubsub-peer-monitor.patch || true", "prepare": "npm run applyPatches && npm run webpack", "version": "git add -A src", "lint:no-fix": "eslint --ext .jsx,.js,.ts,.tsx ./src/", diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 63fd7449be..d76d655a9b 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -109,12 +109,12 @@ export const runBackendMobile = async () => { { logger: ['warn', 'error', 'log', 'debug', 'verbose'] } ) - rn_bridge.channel.on('close', async () => { + rn_bridge.channel.on('close', () => { const connectionsManager = app.get(ConnectionsManagerService) - await connectionsManager.pause() + connectionsManager.pause() }) - rn_bridge.channel.on('open', async (msg: OpenServices) => { + rn_bridge.channel.on('open', (msg: OpenServices) => { const connectionsManager = app.get(ConnectionsManagerService) const torControl = app.get(TorControl) const proxyAgent = app.get<{ proxy: { port: string } }>(SOCKS_PROXY_AGENT) @@ -123,7 +123,7 @@ export const runBackendMobile = async () => { torControl.torControlParams.auth.value = msg.authCookie proxyAgent.proxy.port = msg.httpTunnelPort - await connectionsManager.resume() + connectionsManager.resume() }) } diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index 586bc941a4..a030206be7 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -224,6 +224,10 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } public async closeAllServices(options: { saveTor: boolean } = { saveTor: false }) { + this.logger('Closing services') + + await this.closeSocket() + if (this.tor && !options.saveTor) { this.logger('Killing tor') await this.tor.kill() @@ -231,31 +235,26 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.logger('Saving tor') } if (this.storageService) { - this.logger('Stopping orbitdb') + this.logger('Stopping OrbitDB') await this.storageService?.stopOrbitDb() } - if (this.serverIoProvider?.io) { - this.logger('Closing socket server') - this.serverIoProvider.io.close() - } - if (this.localDbService) { - this.logger('Closing local storage') - await this.localDbService.close() - } if (this.libp2pService) { this.logger('Stopping libp2p') await this.libp2pService.close() } + if (this.localDbService) { + this.logger('Closing local DB') + await this.localDbService.close() + } } - public closeSocket() { - this.serverIoProvider.io.close() + public async closeSocket() { + await this.socketService.close() } public async pause() { this.logger('Pausing!') - this.logger('Closing socket!') - this.closeSocket() + await this.closeSocket() this.logger('Pausing libp2pService!') this.peerInfo = await this.libp2pService?.pause() this.logger('Found the following peer info on pause: ', this.peerInfo) @@ -263,7 +262,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public async resume() { this.logger('Resuming!') - this.logger('Reopening socket!') await this.openSocket() this.logger('Attempting to redial peers!') if (this.peerInfo && (this.peerInfo?.connected.length !== 0 || this.peerInfo?.dialed.length !== 0)) { @@ -289,21 +287,14 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public async leaveCommunity(): Promise { this.logger('Running leaveCommunity') - this.logger('Resetting tor') - this.tor.resetHiddenServices() - - this.logger('Closing the socket') - this.closeSocket() - - this.logger('Purging local DB') - await this.localDbService.purge() - - this.logger('Closing services') await this.closeAllServices({ saveTor: true }) this.logger('Purging data') await this.purgeData() + this.logger('Resetting Tor') + this.tor.resetHiddenServices() + this.logger('Resetting state') await this.resetState() diff --git a/packages/backend/src/nest/libp2p/libp2p.module.ts b/packages/backend/src/nest/libp2p/libp2p.module.ts index d75edc799e..e010bb8b55 100644 --- a/packages/backend/src/nest/libp2p/libp2p.module.ts +++ b/packages/backend/src/nest/libp2p/libp2p.module.ts @@ -1,10 +1,8 @@ import { Module } from '@nestjs/common' -import { SocketModule } from '../socket/socket.module' import { Libp2pService } from './libp2p.service' import { ProcessInChunksService } from './process-in-chunks.service' @Module({ - imports: [SocketModule], providers: [Libp2pService, ProcessInChunksService], exports: [Libp2pService], }) diff --git a/packages/backend/src/nest/registration/registration.service.ts b/packages/backend/src/nest/registration/registration.service.ts index 17cd8ff838..5e6057c91b 100644 --- a/packages/backend/src/nest/registration/registration.service.ts +++ b/packages/backend/src/nest/registration/registration.service.ts @@ -51,7 +51,7 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { // Get the next event. const event = this.registrationEvents.shift() if (event) { - this.logger('Processing registration event', event) + this.logger('Processing registration event') // Event processing in progress this.registrationEventInProgress = true diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index e6f4b17c00..9d34f37dd6 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -27,6 +27,7 @@ import { CONFIG_OPTIONS, SERVER_IO_PROVIDER } from '../const' import { ConfigOptions, ServerIoProviderTypes } from '../types' import { suspendableSocketEvents } from './suspendable.events' import Logger from '../common/logger' +import type net from 'node:net' @Injectable() export class SocketService extends EventEmitter implements OnModuleInit { @@ -34,6 +35,7 @@ export class SocketService extends EventEmitter implements OnModuleInit { public resolveReadyness: (value: void | PromiseLike) => void public readyness: Promise + private sockets: Set constructor( @Inject(SERVER_IO_PROVIDER) public readonly serverIoProvider: ServerIoProviderTypes, @@ -44,14 +46,15 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.readyness = new Promise(resolve => { this.resolveReadyness = resolve }) + + this.sockets = new Set() + + this.attachListeners() } async onModuleInit() { this.logger('init: Started') - - this.attachListeners() await this.init() - this.logger('init: Finished') } @@ -71,7 +74,9 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.logger('init: Frontend connected') } - private readonly attachListeners = (): void => { + private readonly attachListeners = () => { + this.logger('Attaching listeners') + // Attach listeners here this.serverIoProvider.io.on(SocketActionTypes.CONNECTION, socket => { this.logger('Socket connection') @@ -173,7 +178,7 @@ export class SocketService extends EventEmitter implements OnModuleInit { } ) - socket.on(SocketActionTypes.LEAVE_COMMUNITY, async (callback: (closed: boolean) => void) => { + socket.on(SocketActionTypes.LEAVE_COMMUNITY, (callback: (closed: boolean) => void) => { this.logger('Leaving community') this.emit(SocketActionTypes.LEAVE_COMMUNITY, callback) }) @@ -195,11 +200,53 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.emit(SocketActionTypes.LOAD_MIGRATION_DATA, data) }) }) + + // Ensure the underlying connections get closed. See: + // https://github.com/socketio/socket.io/issues/1602 + this.serverIoProvider.server.on('connection', conn => { + this.sockets.add(conn) + conn.on('close', () => { + this.sockets.delete(conn) + }) + }) } - public listen = async (port = this.configOptions.socketIOPort): Promise => { - return await new Promise(resolve => { - if (this.serverIoProvider.server.listening) resolve() + public getConnections = (): Promise => { + return new Promise(resolve => { + this.serverIoProvider.server.getConnections((err, count) => { + if (err) throw new Error(err.message) + resolve(count) + }) + }) + } + + // Ensure the underlying connections get closed. See: + // https://github.com/socketio/socket.io/issues/1602 + // + // I also tried `this.serverIoProvider.io.disconnectSockets(true)` + // which didn't work for me, but we still call it. + public closeSockets = () => { + this.logger('Disconnecting sockets') + this.serverIoProvider.io.disconnectSockets(true) + this.sockets.forEach(s => s.destroy()) + } + + public listen = async (): Promise => { + this.logger(`Opening data server on port ${this.configOptions.socketIOPort}`) + + if (this.serverIoProvider.server.listening) { + this.logger('Failed to listen. Server already listening.') + return + } + + const numConnections = await this.getConnections() + + if (numConnections > 0) { + this.logger('Failed to listen. Connections still open:', numConnections) + return + } + + return new Promise(resolve => { this.serverIoProvider.server.listen(this.configOptions.socketIOPort, '127.0.0.1', () => { this.logger(`Data server running on port ${this.configOptions.socketIOPort}`) resolve() @@ -207,13 +254,23 @@ export class SocketService extends EventEmitter implements OnModuleInit { }) } - public close = async (): Promise => { - this.logger(`Closing data server on port ${this.configOptions.socketIOPort}`) - return await new Promise(resolve => { - this.serverIoProvider.server.close(err => { + public close = (): Promise => { + return new Promise(resolve => { + this.logger(`Closing data server on port ${this.configOptions.socketIOPort}`) + + if (!this.serverIoProvider.server.listening) { + this.logger('Data server is not running.') + resolve() + return + } + + this.serverIoProvider.io.close(err => { if (err) throw new Error(err.message) + this.logger('Data server closed') resolve() }) + + this.closeSockets() }) } } diff --git a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts index be2d6c54b5..0b23a48cf0 100644 --- a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts +++ b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts @@ -29,6 +29,7 @@ export class CertificatesRequestsStore extends EventEmitter { write: ['*'], }, }) + await this.store.load() this.store.events.on('write', async (_address, entry) => { this.logger('Added CSR to database') @@ -40,8 +41,6 @@ export class CertificatesRequestsStore extends EventEmitter { this.loadedCertificateRequests() }) - // @ts-ignore - await this.store.load({ fetchEntryTimeout: 15000 }) this.logger('Initialized') } @@ -52,9 +51,9 @@ export class CertificatesRequestsStore extends EventEmitter { } public async close() { - this.logger('Closing...') + this.logger('Closing certificate requests DB') await this.store?.close() - this.logger('Closed') + this.logger('Closed certificate requests DB') } public getAddress() { @@ -91,8 +90,6 @@ export class CertificatesRequestsStore extends EventEmitter { public async getCsrs() { const filteredCsrsMap: Map = new Map() - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) const allEntries = this.store .iterator({ limit: -1 }) .collect() diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.ts b/packages/backend/src/nest/storage/certificates/certificates.store.ts index c262e96a07..f8699c0651 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.ts @@ -41,7 +41,6 @@ export class CertificatesStore extends EventEmitter { write: ['*'], }, }) - await this.store.load() this.store.events.on('ready', async () => { this.logger('Loaded certificates to memory') @@ -59,8 +58,7 @@ export class CertificatesStore extends EventEmitter { await this.loadedCertificates() }) - // // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - // await this.store.load({ fetchEntryTimeout: 15000 }) + await this.store.load() this.logger('Initialized') } @@ -72,7 +70,9 @@ export class CertificatesStore extends EventEmitter { } public async close() { + this.logger('Closing certificates DB') await this.store?.close() + this.logger('Closed certificates DB') } public getAddress() { @@ -154,8 +154,6 @@ export class CertificatesStore extends EventEmitter { return [] } - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) const allCertificates = this.store .iterator({ limit: -1 }) .collect() diff --git a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts index 9acbb661be..1b9ceca296 100644 --- a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts +++ b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts @@ -67,17 +67,13 @@ export class CommunityMetadataStore extends EventEmitter { this.store.events.on('replicated', async () => { logger('Replicated community metadata') - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - // TODO: Is this necessary here? - await this.store.load({ fetchEntryTimeout: 15000 }) const meta = this.getCommunityMetadata() if (meta) { this.emit(StorageEvents.COMMUNITY_METADATA_STORED, meta) } }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) + await this.store.load() const meta = this.getCommunityMetadata() if (meta) { this.emit(StorageEvents.COMMUNITY_METADATA_STORED, meta) @@ -90,7 +86,9 @@ export class CommunityMetadataStore extends EventEmitter { } public async close() { + logger('Closing community metadata DB') await this.store?.close() + logger('Closed community metadata DB') } public async updateCommunityMetadata(newMeta: CommunityMetadata): Promise { diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index 7a8c1d95fb..9673dfb9c2 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -251,7 +251,9 @@ export class StorageService extends EventEmitter { public async stopOrbitDb() { try { + this.logger('Closing channels DB') await this.channels?.close() + this.logger('Closed channels DB') } catch (e) { this.logger.error('Error closing channels db', e) } @@ -352,8 +354,6 @@ export class StorageService extends EventEmitter { public async loadAllChannels() { this.logger('Getting all channels') - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 2000 }) this.emit(StorageEvents.CHANNELS_STORED, { channels: this.channels.all as unknown as { [key: string]: PublicChannel }, }) @@ -376,8 +376,6 @@ export class StorageService extends EventEmitter { this.channels.events.on('replicated', async () => { this.logger('REPLICATED: Channels') this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CHANNELS_STORED) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 2000 }) const channels = Object.values(this.channels.all) @@ -398,8 +396,7 @@ export class StorageService extends EventEmitter { }) }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 1000 }) + await this.channels.load() this.logger('Channels count:', Object.keys(this.channels.all).length) this.logger('Channels names:', Object.keys(this.channels.all)) Object.values(this.channels.all).forEach(async (channel: PublicChannel) => { @@ -544,6 +541,7 @@ export class StorageService extends EventEmitter { } }) + // FIXME: load is called twice for channel stores await db.load() repo.eventsAttached = true } @@ -601,8 +599,7 @@ export class StorageService extends EventEmitter { this.publicChannelsRepos.set(channelId, { db, eventsAttached: false }) this.logger(`Set ${channelId} to local channels`) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await db.load({ fetchEntryTimeout: 2000 }) + await db.load() this.logger(`Created channel ${channelId}`) await this.subscribeToPubSub([StorageService.dbAddress(db.address)]) @@ -612,8 +609,6 @@ export class StorageService extends EventEmitter { public async deleteChannel(payload: { channelId: string; ownerPeerId: string }) { console.log('deleting channel storage', payload) const { channelId, ownerPeerId } = payload - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 15000 }) const channel = this.channels.get(channelId) if (!this.peerId) { this.logger('deleteChannel - peerId is null') @@ -636,18 +631,8 @@ export class StorageService extends EventEmitter { eventsAttached: false, } } - await repo.db.load() - // const allEntries = this.getAllEventLogRawEntries(repo.db) await repo.db.close() await repo.db.drop() - // const hashes = allEntries.map(e => CID.parse(e.hash)) - // const files = allEntries - // .map(e => { - // return e.payload.value.media - // }) - // .filter(isDefined) - // await this.deleteChannelFiles(files) - // await this.deleteChannelMessages(hashes) this.publicChannelsRepos.delete(channelId) return { channelId: payload.channelId } } @@ -689,6 +674,7 @@ export class StorageService extends EventEmitter { return } try { + this.logger('Sending message:', message.id) await repo.db.add(message) } catch (e) { this.logger.error( diff --git a/packages/backend/src/nest/storage/userProfile/userProfile.store.ts b/packages/backend/src/nest/storage/userProfile/userProfile.store.ts index aace9f027a..503baf45af 100644 --- a/packages/backend/src/nest/storage/userProfile/userProfile.store.ts +++ b/packages/backend/src/nest/storage/userProfile/userProfile.store.ts @@ -77,8 +77,7 @@ export class UserProfileStore extends EventEmitter { }) }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) + await this.store.load() } public getAddress() { @@ -86,7 +85,9 @@ export class UserProfileStore extends EventEmitter { } public async close() { + logger('Closing user profile DB') await this.store?.close() + logger('Closed user profile DB') } public async addUserProfile(userProfile: UserProfile) { diff --git a/packages/mobile/src/store/init/init.slice.ts b/packages/mobile/src/store/init/init.slice.ts index f9106199d6..92abfb4a45 100644 --- a/packages/mobile/src/store/init/init.slice.ts +++ b/packages/mobile/src/store/init/init.slice.ts @@ -44,9 +44,6 @@ export const initSlice = createSlice({ setStoreReady: state => { state.ready = true }, - setCryptoEngineInitialized: (state, action: PayloadAction) => { - state.isCryptoEngineInitialized = action.payload - }, updateInitDescription: (state, action: PayloadAction) => { state.initDescription = action.payload }, diff --git a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts b/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts deleted file mode 100644 index efd5b18bb6..0000000000 --- a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { combineReducers } from '@reduxjs/toolkit' -import { expectSaga } from 'redux-saga-test-plan' -import { call } from 'redux-saga-test-plan/matchers' -import { StoreKeys } from '../../store.keys' -import { initActions, initReducer, InitState } from '../init.slice' -import { initCryptoEngine, setupCryptoSaga } from './setupCrypto.saga' - -describe('setupCryptoSaga', () => { - test('should be defined', async () => { - await expectSaga(setupCryptoSaga) - .withReducer(combineReducers({ [StoreKeys.Init]: initReducer }), { - [StoreKeys.Init]: { - ...new InitState(), - }, - }) - .provide([[call.fn(initCryptoEngine), null]]) - .call(initCryptoEngine) - .put(initActions.setCryptoEngineInitialized(true)) - .hasFinalState({ - [StoreKeys.Init]: { - ...new InitState(), - isCryptoEngineInitialized: true, - }, - }) - .run() - }) -}) diff --git a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts b/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts deleted file mode 100644 index 61320653a7..0000000000 --- a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { setEngine, CryptoEngine } from 'pkijs' - -import { select, call, put } from 'typed-redux-saga' -import { initSelectors } from '../init.selectors' -import { initActions } from '../init.slice' - -export function* setupCryptoSaga(): Generator { - const isCryptoEngineInitialized = yield* select(initSelectors.isCryptoEngineInitialized) - if (!isCryptoEngineInitialized) { - yield* call(initCryptoEngine) - yield* put(initActions.setCryptoEngineInitialized(true)) - } -} - -export const initCryptoEngine = () => { - setEngine( - 'newEngine', - new CryptoEngine({ - name: '', - crypto, - subtle: crypto.subtle, - }) - ) -} diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index 44a48cfbc6..4f77f32552 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -1,5 +1,17 @@ import { io } from 'socket.io-client' -import { select, put, call, cancel, fork, takeEvery, FixedTask, delay, apply, putResolve } from 'typed-redux-saga' +import { + select, + put, + putResolve, + call, + cancel, + fork, + take, + takeLeading, + takeEvery, + FixedTask, + apply, +} from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' import { socket as stateManager, Socket } from '@quiet/state-manager' import { encodeSecret } from '@quiet/common' @@ -14,16 +26,6 @@ export function* startConnectionSaga( const isAlreadyConnected = yield* select(initSelectors.isWebsocketConnected) if (isAlreadyConnected) return - while (true) { - const isCryptoEngineInitialized = yield* select(initSelectors.isCryptoEngineInitialized) - console.log('WEBSOCKET', 'Waiting for crypto engine to initialize') - if (!isCryptoEngineInitialized) { - yield* delay(500) - } else { - break - } - } - const { dataPort, socketIOSecret } = action.payload console.log('WEBSOCKET', 'Entered start connection saga', dataPort) @@ -49,17 +51,20 @@ export function* startConnectionSaga( }) yield* fork(handleSocketLifecycleActions, socket, action.payload) // Handle opening/restoring connection - yield* takeEvery(initActions.setWebsocketConnected, setConnectedSaga, socket) + yield* takeLeading(initActions.setWebsocketConnected, setConnectedSaga, socket) } function* setConnectedSaga(socket: Socket): Generator { + console.log('Frontend is ready. Forking state-manager sagas and starting backend...') + const task = yield* fork(stateManager.useIO, socket) - console.log('WEBSOCKET', 'Forking state-manager sagas', task) - // Handle suspending current connection - yield* takeEvery(initActions.suspendWebsocketConnection, cancelRootTaskSaga, task) - console.log('Frontend is ready. Starting backend...') + // @ts-ignore - Why is this broken? yield* apply(socket, socket.emit, [SocketActionTypes.START]) + + // Handle suspending current connection + yield* take(initActions.suspendWebsocketConnection) + yield* call(cancelRootTaskSaga, task) } function* handleSocketLifecycleActions(socket: Socket, socketIOData: WebsocketConnectionPayload): Generator { diff --git a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts index 23ab6b8ba5..fba6757ce7 100644 --- a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts +++ b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts @@ -1,5 +1,5 @@ import { eventChannel } from 'redux-saga' -import { call, put, take } from 'typed-redux-saga' +import { call, put, take, cancelled } from 'typed-redux-saga' import { app, publicChannels, WEBSOCKET_CONNECTION_CHANNEL, INIT_CHECK_CHANNEL, network } from '@quiet/state-manager' import { initActions, InitCheckPayload, WebsocketConnectionPayload } from '../../init/init.slice' import { ScreenNames } from '../../../const/ScreenNames.enum' @@ -9,10 +9,18 @@ import { navigationActions } from '../../navigation/navigation.slice' import { nativeServicesActions } from '../nativeServices.slice' export function* nativeServicesCallbacksSaga(): Generator { - const channel = yield* call(deviceEvents) - while (true) { - const action = yield* take(channel) - yield put(action) + console.log('nativeServicesCallbacksSaga starting') + try { + const channel = yield* call(deviceEvents) + while (true) { + const action = yield* take(channel) + yield put(action) + } + } finally { + console.log('nativeServicesCallbacksSaga stopping') + if (yield cancelled()) { + console.log('nativeServicesCallbacksSaga cancelled') + } } } diff --git a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts index 56ec0c77f7..3d363a82ae 100644 --- a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts +++ b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts @@ -1,4 +1,4 @@ -import { select, call, takeLeading, putResolve } from 'typed-redux-saga' +import { select, call, putResolve } from 'typed-redux-saga' import { app } from '@quiet/state-manager' import { persistor } from '../../store' import { nativeServicesActions } from '../nativeServices.slice' @@ -9,11 +9,8 @@ import { ScreenNames } from '../../../../src/const/ScreenNames.enum' export function* leaveCommunitySaga(): Generator { console.log('Leaving community') - // Restart backend yield* putResolve(app.actions.closeServices()) - - yield takeLeading(initActions.canceledRootTask.type, clearReduxStore) } export function* clearReduxStore(): Generator { diff --git a/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts b/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts index 13f1fe441a..8544fbc563 100644 --- a/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts +++ b/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts @@ -1,13 +1,21 @@ -import { all, fork, takeEvery } from 'typed-redux-saga' +import { all, fork, takeEvery, cancelled } from 'typed-redux-saga' import { nativeServicesCallbacksSaga } from './events/nativeServicesCallbacks' import { leaveCommunitySaga } from './leaveCommunity/leaveCommunity.saga' import { flushPersistorSaga } from './flushPersistor/flushPersistor.saga' import { nativeServicesActions } from './nativeServices.slice' export function* nativeServicesMasterSaga(): Generator { - yield all([ - fork(nativeServicesCallbacksSaga), - takeEvery(nativeServicesActions.leaveCommunity.type, leaveCommunitySaga), - takeEvery(nativeServicesActions.flushPersistor.type, flushPersistorSaga), - ]) + console.log('nativeServicesMasterSaga starting') + try { + yield all([ + fork(nativeServicesCallbacksSaga), + takeEvery(nativeServicesActions.leaveCommunity.type, leaveCommunitySaga), + takeEvery(nativeServicesActions.flushPersistor.type, flushPersistorSaga), + ]) + } finally { + console.log('nativeServicesMasterSaga stopping') + if (yield cancelled()) { + console.log('nativeServicesMasterSaga cancelled') + } + } } diff --git a/packages/mobile/src/store/root.saga.ts b/packages/mobile/src/store/root.saga.ts index 9b2fffff52..e3978f3cf4 100644 --- a/packages/mobile/src/store/root.saga.ts +++ b/packages/mobile/src/store/root.saga.ts @@ -1,19 +1,62 @@ -import { all, takeEvery } from 'typed-redux-saga' +import { all, call, take, takeEvery, takeLeading, fork, cancelled } from 'typed-redux-saga' import { nativeServicesMasterSaga } from './nativeServices/nativeServices.master.saga' import { navigationMasterSaga } from './navigation/navigation.master.saga' import { initMasterSaga } from './init/init.master.saga' import { initActions } from './init/init.slice' -import { setupCryptoSaga } from './init/setupCrypto/setupCrypto.saga' import { publicChannels } from '@quiet/state-manager' import { showNotificationSaga } from './nativeServices/showNotification/showNotification.saga' +import { clearReduxStore } from './nativeServices/leaveCommunity/leaveCommunity.saga' +import { setEngine, CryptoEngine } from 'pkijs' + +const initCryptoEngine = () => { + setEngine( + 'newEngine', + new CryptoEngine({ + name: '', + crypto, + subtle: crypto.subtle, + }) + ) +} export function* rootSaga(): Generator { - yield all([ - takeEvery(initActions.setStoreReady.type, setupCryptoSaga), - takeEvery(initActions.setStoreReady.type, initMasterSaga), - takeEvery(initActions.setStoreReady.type, navigationMasterSaga), - takeEvery(initActions.setStoreReady.type, nativeServicesMasterSaga), - // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one - takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), - ]) + console.log('rootSaga starting') + try { + console.log('Initializing crypto engine') + yield* call(initCryptoEngine) + // We don't want to start any sagas until the store is ready in + // case they use the store. Currently, we run these sagas once per + // application lifecycle. However, when we leave the community and + // clear the Redux store, if the Redux store is cleared while a + // saga is running, I suppose there is a possibility of corrupted + // state. Perhaps, it would make more sense to stop this saga, + // clear the store and then restart it, but that requires some + // refactoring. + yield* take(initActions.setStoreReady) + yield* call(storeReadySaga) + } finally { + console.log('rootSaga stopping') + if (yield cancelled()) { + console.log('rootSaga cancelled') + } + } +} + +function* storeReadySaga(): Generator { + console.log('storeReadySaga starting') + try { + yield all([ + fork(initMasterSaga), + fork(navigationMasterSaga), + fork(nativeServicesMasterSaga), + // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one + takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), + takeLeading(initActions.canceledRootTask.type, clearReduxStore), + ]) + } finally { + console.log('storeReadySaga stopping') + if (yield cancelled()) { + console.log('storeReadySaga cancelled') + } + } } diff --git a/packages/mobile/src/store/store.ts b/packages/mobile/src/store/store.ts index 106867a42f..62cfcab17e 100644 --- a/packages/mobile/src/store/store.ts +++ b/packages/mobile/src/store/store.ts @@ -71,5 +71,6 @@ export const store = configureStore({ }) export const persistor = persistStore(store, {}, () => { + console.log('Redux store is ready!') store.dispatch(initActions.setStoreReady()) }) diff --git a/packages/state-manager/src/sagas/app/app.master.saga.ts b/packages/state-manager/src/sagas/app/app.master.saga.ts index 07dca44027..9775a83256 100644 --- a/packages/state-manager/src/sagas/app/app.master.saga.ts +++ b/packages/state-manager/src/sagas/app/app.master.saga.ts @@ -1,14 +1,22 @@ import { Socket } from '../../types' -import { all, takeEvery, takeLeading } from 'typed-redux-saga' +import { all, takeEvery, takeLeading, cancelled } from 'typed-redux-saga' import { appActions } from './app.slice' import { closeServicesSaga } from './closeServices.saga' import { stopBackendSaga } from './stopBackend/stopBackend.saga' import { loadMigrationDataSaga } from './loadMigrationData/loadMigrationData.saga' export function* appMasterSaga(socket: Socket): Generator { - yield* all([ - takeLeading(appActions.closeServices.type, closeServicesSaga, socket), - takeEvery(appActions.stopBackend.type, stopBackendSaga, socket), - takeEvery(appActions.loadMigrationData.type, loadMigrationDataSaga, socket), - ]) + console.log('appMasterSaga starting') + try { + yield* all([ + takeLeading(appActions.closeServices.type, closeServicesSaga, socket), + takeEvery(appActions.stopBackend.type, stopBackendSaga, socket), + takeEvery(appActions.loadMigrationData.type, loadMigrationDataSaga, socket), + ]) + } finally { + console.log('appMasterSaga stopping') + if (yield cancelled()) { + console.log('appMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts b/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts index bd2f054422..b0da74eb5b 100644 --- a/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts +++ b/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts @@ -1,6 +1,14 @@ -import { all, fork } from 'typed-redux-saga' +import { all, fork, cancelled } from 'typed-redux-saga' import { uptimeSaga } from './uptime/uptime.saga' export function* connectionMasterSaga(): Generator { - yield all([fork(uptimeSaga)]) + console.log('connectionMasterSaga starting') + try { + yield all([fork(uptimeSaga)]) + } finally { + console.log('connectionMasterSaga stopping') + if (yield cancelled()) { + console.log('connectionMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/communities/communities.master.saga.ts b/packages/state-manager/src/sagas/communities/communities.master.saga.ts index c5e1e1a275..a19dd3f20d 100644 --- a/packages/state-manager/src/sagas/communities/communities.master.saga.ts +++ b/packages/state-manager/src/sagas/communities/communities.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { communitiesActions } from './communities.slice' import { connectionActions } from '../appConnection/connection.slice' import { createCommunitySaga } from './createCommunity/createCommunity.saga' @@ -7,10 +7,18 @@ import { initCommunities, launchCommunitySaga } from './launchCommunity/launchCo import { createNetworkSaga } from './createNetwork/createNetwork.saga' export function* communitiesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), - takeEvery(connectionActions.torBootstrapped.type, initCommunities), - takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), - takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), - ]) + console.log('communitiesMasterSaga starting') + try { + yield all([ + takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), + takeEvery(connectionActions.torBootstrapped.type, initCommunities), + takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), + takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), + ]) + } finally { + console.log('communitiesMasterSaga stopping') + if (yield cancelled()) { + console.log('communitiesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts index 8308e764f1..8ab3953d56 100644 --- a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts +++ b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts @@ -12,6 +12,8 @@ export function* createCommunitySaga( socket: Socket, action: PayloadAction['payload']> ): Generator { + console.log('Creating community') + let communityId: string = action.payload if (!communityId) { diff --git a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts index 33003d6df5..a75d7fb9cf 100644 --- a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts +++ b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts @@ -12,11 +12,13 @@ export function* createNetworkSaga( socket: Socket, action: PayloadAction['payload']> ) { - console.log('create network saga') + console.log('Creating network') // Community IDs are only local identifiers + console.log('Generating community ID') const id = yield* call(generateId) + console.log('Emitting CREATE_NETWORK') const network = yield* apply(socket, socket.emitWithAck, applyEmitParams(SocketActionTypes.CREATE_NETWORK, id)) // TODO: Move CA generation to backend when creating Community @@ -29,6 +31,7 @@ export function* createNetworkSaga( const notBeforeDate = new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) const notAfterDate = new Date(Date.UTC(2030, 11, 28, 10, 10, 10)) + console.log('Generating CA') CA = yield* call( createRootCA, new Time({ type: 0, value: notBeforeDate }), @@ -46,6 +49,7 @@ export function* createNetworkSaga( ownerOrbitDbIdentity: action.payload.ownerOrbitDbIdentity, } + console.log('Adding new community', id) yield* put(communitiesActions.addNewCommunity(community)) yield* put(communitiesActions.setCurrentCommunity(id)) @@ -65,5 +69,8 @@ export function* createNetworkSaga( joinTimestamp: null, } + console.log('Adding new identity', identity.id) yield* put(identityActions.addNewIdentity(identity)) + + console.log('Network created') } diff --git a/packages/state-manager/src/sagas/errors/errors.master.saga.ts b/packages/state-manager/src/sagas/errors/errors.master.saga.ts index 88b09d53b7..acaf09e588 100644 --- a/packages/state-manager/src/sagas/errors/errors.master.saga.ts +++ b/packages/state-manager/src/sagas/errors/errors.master.saga.ts @@ -1,7 +1,15 @@ -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { errorsActions } from './errors.slice' import { handleErrorsSaga } from './handleErrors/handleErrors.saga' export function* errorsMasterSaga(): Generator { - yield all([takeEvery(errorsActions.handleError.type, handleErrorsSaga)]) + console.log('errorsMasterSaga starting') + try { + yield all([takeEvery(errorsActions.handleError.type, handleErrorsSaga)]) + } finally { + console.log('errorsMasterSaga stopping') + if (yield cancelled()) { + console.log('errorsMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/files/files.master.saga.ts b/packages/state-manager/src/sagas/files/files.master.saga.ts index 094e856eab..7b0a373084 100644 --- a/packages/state-manager/src/sagas/files/files.master.saga.ts +++ b/packages/state-manager/src/sagas/files/files.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { checkForMissingFilesSaga } from './checkForMissingFiles/checkForMissingFiles.saga' import { resetTransferSpeedSaga } from './resetTransferSpeed/resetTransferSpeed.saga' import { updateMessageMediaSaga } from './updateMessageMedia/updateMessageMedia' @@ -14,15 +14,23 @@ import { messagesActions } from '../messages/messages.slice' import { sendFileMessageSaga } from './uploadFile/sendFileMessage.saga' export function* filesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(networkActions.addInitializedCommunity.type, resetTransferSpeedSaga), - takeEvery(filesActions.checkForMissingFiles.type, checkForMissingFilesSaga, socket), - takeEvery(filesActions.uploadFile.type, sendFileMessageSaga), - takeEvery(messagesActions.addMessagesSendingStatus.type, uploadFileSaga, socket), - takeEvery(filesActions.cancelDownload.type, cancelDownloadSaga, socket), - takeEvery(filesActions.updateMessageMedia.type, updateMessageMediaSaga), - takeEvery(filesActions.downloadFile.type, downloadFileSaga, socket), - takeEvery(filesActions.broadcastHostedFile.type, broadcastHostedFileSaga, socket), - takeEvery(filesActions.deleteFilesFromChannel.type, deleteFilesFromChannelSaga, socket), - ]) + console.log('filesMasterSaga starting') + try { + yield all([ + takeEvery(networkActions.addInitializedCommunity.type, resetTransferSpeedSaga), + takeEvery(filesActions.checkForMissingFiles.type, checkForMissingFilesSaga, socket), + takeEvery(filesActions.uploadFile.type, sendFileMessageSaga), + takeEvery(messagesActions.addMessagesSendingStatus.type, uploadFileSaga, socket), + takeEvery(filesActions.cancelDownload.type, cancelDownloadSaga, socket), + takeEvery(filesActions.updateMessageMedia.type, updateMessageMediaSaga), + takeEvery(filesActions.downloadFile.type, downloadFileSaga, socket), + takeEvery(filesActions.broadcastHostedFile.type, broadcastHostedFileSaga, socket), + takeEvery(filesActions.deleteFilesFromChannel.type, deleteFilesFromChannelSaga, socket), + ]) + } finally { + console.log('filesMasterSaga stopping') + if (yield cancelled()) { + console.log('filesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/identity/identity.master.saga.ts b/packages/state-manager/src/sagas/identity/identity.master.saga.ts index cf3c116db7..4a4ddbbd03 100644 --- a/packages/state-manager/src/sagas/identity/identity.master.saga.ts +++ b/packages/state-manager/src/sagas/identity/identity.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { identityActions } from './identity.slice' import { registerUsernameSaga } from './registerUsername/registerUsername.saga' import { verifyJoinTimestampSaga } from './verifyJoinTimestamp/verifyJoinTimestamp.saga' @@ -8,10 +8,18 @@ import { usersActions } from '../users/users.slice' import { updateCertificateSaga } from './updateCertificate/updateCertificate.saga' export function* identityMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), - takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), - takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), - takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), - ]) + console.log('identityMasterSaga starting') + try { + yield all([ + takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), + takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), + takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), + takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), + ]) + } finally { + console.log('identityMasterSaga stopping') + if (yield cancelled()) { + console.log('identityMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts index e6714f430c..0623bdc65e 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts @@ -29,7 +29,7 @@ export function* registerUsernameSaga( console.error('Could not register username, no community data') return } - console.log('Found community') + console.log('Found community', community.id) let identity = yield* select(identitySelectors.currentIdentity) if (!identity) { @@ -41,7 +41,7 @@ export function* registerUsernameSaga( console.error('Could not register username, no identity') return } - console.log('Found identity') + console.log('Found identity', identity.id) let userCsr = identity.userCsr @@ -66,6 +66,7 @@ export function* registerUsernameSaga( existingKeyPair, } + console.log('Recreating user CSR') userCsr = yield* call(createUserCsr, payload) } catch (e) { console.error(e) @@ -80,6 +81,8 @@ export function* registerUsernameSaga( signAlg: config.signAlg, hashAlg: config.hashAlg, } + + console.log('Creating user CSR') userCsr = yield* call(createUserCsr, payload) } catch (e) { console.error(e) @@ -96,6 +99,7 @@ export function* registerUsernameSaga( isUsernameTaken, } + console.log('Adding user CSR to Redux', payload.communityId) yield* put(identityActions.addCsr(payload)) if (community.CA?.rootCertString) { diff --git a/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts b/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts index 05c371bef0..0cc07fd1c7 100644 --- a/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts +++ b/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts @@ -7,8 +7,14 @@ export function* saveUserCsrSaga(socket: Socket): Generator { console.log('Saving user CSR') const identity = yield* select(identitySelectors.currentIdentity) - if (!identity?.userCsr) { - console.error('Cannot save user csr to backend, no userCsr') + + if (!identity) { + console.error('Cannot save user CSR to backend, no identity') + return + } + + if (!identity.userCsr) { + console.error('Cannot save user CSR to backend, no userCsr', identity) return } @@ -16,6 +22,6 @@ export function* saveUserCsrSaga(socket: Socket): Generator { csr: identity.userCsr?.userCsr, } - console.log(`Send ${SocketActionTypes.ADD_CSR}`) + console.log('Emitting ADD_CSR') yield* apply(socket, socket.emit, applyEmitParams(SocketActionTypes.ADD_CSR, payload)) } diff --git a/packages/state-manager/src/sagas/messages/messages.master.saga.ts b/packages/state-manager/src/sagas/messages/messages.master.saga.ts index 6f7f7e70c6..2235f985de 100644 --- a/packages/state-manager/src/sagas/messages/messages.master.saga.ts +++ b/packages/state-manager/src/sagas/messages/messages.master.saga.ts @@ -1,5 +1,4 @@ -import { takeEvery } from 'redux-saga/effects' -import { all } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { type Socket } from '../../types' import { messagesActions } from './messages.slice' import { sendMessageSaga } from './sendMessage/sendMessage.saga' @@ -16,18 +15,26 @@ import { autoDownloadFilesSaga } from '../files/autoDownloadFiles/autoDownloadFi import { sendDeletionMessageSaga } from './sendDeletionMessage/sendDeletionMessage.saga' export function* messagesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(messagesActions.sendMessage.type, sendMessageSaga, socket), - takeEvery(messagesActions.addMessages.type, autoDownloadFilesSaga, socket), - takeEvery(messagesActions.addMessages.type, addMessagesSaga), - takeEvery(messagesActions.addMessages.type, verifyMessagesSaga), - takeEvery(messagesActions.addMessages.type, markUnreadChannelsSaga), - takeEvery(messagesActions.addMessages.type, updateNewestMessageSaga), - takeEvery(messagesActions.lazyLoading.type, lazyLoadingSaga), - takeEvery(messagesActions.extendCurrentPublicChannelCache.type, extendCurrentPublicChannelCacheSaga), - takeEvery(messagesActions.resetCurrentPublicChannelCache.type, resetCurrentPublicChannelCacheSaga), - takeEvery(messagesActions.checkForMessages.type, checkForMessagesSaga), - takeEvery(messagesActions.getMessages.type, getMessagesSaga, socket), - takeEvery(messagesActions.sendDeletionMessage.type, sendDeletionMessageSaga), - ]) + console.log('messagesMasterSaga starting') + try { + yield all([ + takeEvery(messagesActions.sendMessage.type, sendMessageSaga, socket), + takeEvery(messagesActions.addMessages.type, autoDownloadFilesSaga, socket), + takeEvery(messagesActions.addMessages.type, addMessagesSaga), + takeEvery(messagesActions.addMessages.type, verifyMessagesSaga), + takeEvery(messagesActions.addMessages.type, markUnreadChannelsSaga), + takeEvery(messagesActions.addMessages.type, updateNewestMessageSaga), + takeEvery(messagesActions.lazyLoading.type, lazyLoadingSaga), + takeEvery(messagesActions.extendCurrentPublicChannelCache.type, extendCurrentPublicChannelCacheSaga), + takeEvery(messagesActions.resetCurrentPublicChannelCache.type, resetCurrentPublicChannelCacheSaga), + takeEvery(messagesActions.checkForMessages.type, checkForMessagesSaga), + takeEvery(messagesActions.getMessages.type, getMessagesSaga, socket), + takeEvery(messagesActions.sendDeletionMessage.type, sendDeletionMessageSaga), + ]) + } finally { + console.log('messagesMasterSaga stopping') + if (yield cancelled()) { + console.log('messagesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts index fee94b4d50..21e7fbccd0 100644 --- a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts +++ b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts @@ -99,6 +99,7 @@ export function* sendMessageSaga( yield* take(publicChannelsActions.setChannelSubscribed) } + console.log('Emitting SEND_MESSAGE', id) yield* apply( socket, socket.emit, diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts index d97db92bf4..675146bcff 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { publicChannelsActions } from './publicChannels.slice' import { createChannelSaga } from './createChannel/createChannel.saga' import { deleteChannelSaga } from './deleteChannel/deleteChannel.saga' @@ -11,14 +11,22 @@ import { channelDeletionResponseSaga } from './channelDeletionResponse/channelDe import { sendIntroductionMessageSaga } from './sendIntroductionMessage/sendIntroductionMessage.saga' export function* publicChannelsMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(publicChannelsActions.createChannel.type, createChannelSaga, socket), - takeEvery(publicChannelsActions.deleteChannel.type, deleteChannelSaga, socket), - takeEvery(publicChannelsActions.channelDeletionResponse.type, channelDeletionResponseSaga), - takeEvery(publicChannelsActions.createGeneralChannel.type, createGeneralChannelSaga), - takeEvery(publicChannelsActions.sendInitialChannelMessage.type, sendInitialChannelMessageSaga), - takeEvery(publicChannelsActions.channelsReplicated.type, channelsReplicatedSaga), - takeEvery(publicChannelsActions.setCurrentChannel.type, clearUnreadChannelsSaga), - takeEvery(publicChannelsActions.sendIntroductionMessage.type, sendIntroductionMessageSaga), - ]) + console.log('publicChannelsMasterSaga starting') + try { + yield all([ + takeEvery(publicChannelsActions.createChannel.type, createChannelSaga, socket), + takeEvery(publicChannelsActions.deleteChannel.type, deleteChannelSaga, socket), + takeEvery(publicChannelsActions.channelDeletionResponse.type, channelDeletionResponseSaga), + takeEvery(publicChannelsActions.createGeneralChannel.type, createGeneralChannelSaga), + takeEvery(publicChannelsActions.sendInitialChannelMessage.type, sendInitialChannelMessageSaga), + takeEvery(publicChannelsActions.channelsReplicated.type, channelsReplicatedSaga), + takeEvery(publicChannelsActions.setCurrentChannel.type, clearUnreadChannelsSaga), + takeEvery(publicChannelsActions.sendIntroductionMessage.type, sendIntroductionMessageSaga), + ]) + } finally { + console.log('publicChannelsMasterSaga stopping') + if (yield cancelled()) { + console.log('publicChannelsMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts index a64a65224c..9e324ef158 100644 --- a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts +++ b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts @@ -1,6 +1,6 @@ import { eventChannel } from 'redux-saga' import { type Socket } from '../../../types' -import { all, call, fork, put, takeEvery } from 'typed-redux-saga' +import { all, call, fork, put, takeEvery, cancelled } from 'typed-redux-saga' import logger from '../../../utils/logger' import { appActions } from '../../app/app.slice' import { appMasterSaga } from '../../app/app.master.saga' @@ -176,23 +176,39 @@ export function subscribe(socket: Socket) { } export function* handleActions(socket: Socket): Generator { - const socketChannel = yield* call(subscribe, socket) - yield takeEvery(socketChannel, function* (action) { - yield put(action) - }) + console.log('handleActions starting') + try { + const socketChannel = yield* call(subscribe, socket) + yield takeEvery(socketChannel, function* (action) { + yield put(action) + }) + } finally { + console.log('handleActions stopping') + if (yield cancelled()) { + console.log('handleActions cancelled') + } + } } export function* useIO(socket: Socket): Generator { - yield all([ - fork(handleActions, socket), - fork(publicChannelsMasterSaga, socket), - fork(messagesMasterSaga, socket), - fork(filesMasterSaga, socket), - fork(identityMasterSaga, socket), - fork(communitiesMasterSaga, socket), - fork(usersMasterSaga, socket), - fork(appMasterSaga, socket), - fork(connectionMasterSaga), - fork(errorsMasterSaga), - ]) + console.log('useIO starting') + try { + yield all([ + fork(handleActions, socket), + fork(publicChannelsMasterSaga, socket), + fork(messagesMasterSaga, socket), + fork(filesMasterSaga, socket), + fork(identityMasterSaga, socket), + fork(communitiesMasterSaga, socket), + fork(usersMasterSaga, socket), + fork(appMasterSaga, socket), + fork(connectionMasterSaga), + fork(errorsMasterSaga), + ]) + } finally { + console.log('useIO stopping') + if (yield cancelled()) { + console.log('useIO cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/users/users.master.saga.ts b/packages/state-manager/src/sagas/users/users.master.saga.ts index 72c7383ef3..947b95615a 100644 --- a/packages/state-manager/src/sagas/users/users.master.saga.ts +++ b/packages/state-manager/src/sagas/users/users.master.saga.ts @@ -1,9 +1,17 @@ -import { takeEvery } from 'redux-saga/effects' +import { takeEvery, cancelled } from 'redux-saga/effects' import { all } from 'typed-redux-saga' import { type Socket } from '../../types' import { usersActions } from './users.slice' import { saveUserProfileSaga } from './userProfile/saveUserProfile.saga' export function* usersMasterSaga(socket: Socket): Generator { - yield all([takeEvery(usersActions.saveUserProfile.type, saveUserProfileSaga, socket)]) + console.log('usersMasterSaga starting') + try { + yield all([takeEvery(usersActions.saveUserProfile.type, saveUserProfileSaga, socket)]) + } finally { + console.log('usersMasterSaga stopping') + if (yield cancelled()) { + console.log('usersMasterSaga cancelled') + } + } }