From 5c5d9c866c5046afe2b92ca3478da8528220b487 Mon Sep 17 00:00:00 2001 From: breadhunter <6099829+EmiM@users.noreply.github.com> Date: Thu, 16 May 2024 14:14:40 +0200 Subject: [PATCH] refactor: add base class for Stores (#2493) * refactor: add base abstract classes for kv and event stores * fix: revert filename change for macos e2e github action --- .github/workflows/e2e-mac.yml | 4 +- CHANGELOG.md | 1 + packages/backend/src/nest/common/types.ts | 5 -- .../registration/registration.service.spec.ts | 6 +- .../backend/src/nest/storage/base.store.ts | 43 ++++++++++++ .../certificatesRequestsStore.spec.ts | 8 +-- .../certificatesRequestsStore.ts | 38 ++++------- .../certificates/certificates.store.spec.ts | 14 ++-- .../certificates/certificates.store.ts | 46 ++++--------- .../communityMetadata.store.spec.ts | 12 ++-- .../communityMetadata.store.ts | 65 +++++++------------ .../src/nest/storage/storage.service.spec.ts | 4 +- .../src/nest/storage/storage.service.ts | 30 ++++----- .../storage/userProfile/userProfile.store.ts | 39 +++++------ 14 files changed, 146 insertions(+), 169 deletions(-) create mode 100644 packages/backend/src/nest/storage/base.store.ts diff --git a/.github/workflows/e2e-mac.yml b/.github/workflows/e2e-mac.yml index b8749fc7a2..ff9002b06b 100644 --- a/.github/workflows/e2e-mac.yml +++ b/.github/workflows/e2e-mac.yml @@ -35,7 +35,7 @@ jobs: - name: FILE_NAME env working-directory: ./packages/desktop/dist - run: echo "FILE_NAME="Quiet-$VERSION-arm64.dmg"" >> $GITHUB_ENV + run: echo "FILE_NAME="Quiet-$VERSION.dmg"" >> $GITHUB_ENV - name: List dist dir content working-directory: ./packages/desktop/dist @@ -50,7 +50,7 @@ jobs: run: hdiutil mount $FILE_NAME - name: Add App file to applications - run: cd ~ && cp -R "/Volumes/Quiet $VERSION-arm64/Quiet.app" /Applications + run: cd ~ && cp -R "/Volumes/Quiet $VERSION/Quiet.app" /Applications - name: Run invitation link test - Includes 2 separate application clients uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e89ca1cb2..bed72c3a3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Cleanup data directory at end of e2e tests * Update github workflows for PR gating ([#2487](https://github.com/TryQuiet/quiet/issues/2487)) * Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) +* Add abstract base classes for stores ([#2407](https://github.com/TryQuiet/quiet/issues/2407)) [2.2.0] diff --git a/packages/backend/src/nest/common/types.ts b/packages/backend/src/nest/common/types.ts index 0337d54875..afe79bdb8d 100644 --- a/packages/backend/src/nest/common/types.ts +++ b/packages/backend/src/nest/common/types.ts @@ -6,11 +6,6 @@ export interface PublicChannelsRepo { eventsAttached: boolean } -export interface DirectMessagesRepo { - db: EventStore - eventsAttached: boolean -} - export type ChannelInfoResponse = Record export class StorageOptions { diff --git a/packages/backend/src/nest/registration/registration.service.spec.ts b/packages/backend/src/nest/registration/registration.service.spec.ts index 1ccbcf4dd1..66fcabc05f 100644 --- a/packages/backend/src/nest/registration/registration.service.spec.ts +++ b/packages/backend/src/nest/registration/registration.service.spec.ts @@ -158,10 +158,10 @@ describe('RegistrationService', () => { const peerId = await PeerId.create() const ipfs = await create() const loadAllCertificates = async () => { - return await certificatesStore.loadAllCertificates() + return await certificatesStore.getEntries() } const saveCertificate = async (payload: SaveCertificatePayload) => { - await certificatesStore.addCertificate(payload.certificate) + await certificatesStore.addEntry(payload.certificate) } await orbitDb.create(peerId, ipfs) @@ -202,7 +202,7 @@ describe('RegistrationService', () => { await new Promise(r => setTimeout(r, 2000)) - expect((await certificatesStore.loadAllCertificates()).length).toEqual(1) + expect((await certificatesStore.getEntries()).length).toEqual(1) await orbitDb.stop() await ipfs.stop() diff --git a/packages/backend/src/nest/storage/base.store.ts b/packages/backend/src/nest/storage/base.store.ts new file mode 100644 index 0000000000..741f7df8fd --- /dev/null +++ b/packages/backend/src/nest/storage/base.store.ts @@ -0,0 +1,43 @@ +import KeyValueStore from 'orbit-db-kvstore' +import Store from 'orbit-db-store' +import EventStore from 'orbit-db-eventstore' +import { EventEmitter } from 'events' +import { createLogger } from '../common/logger' + +const logger = createLogger('store') + +abstract class StoreBase | EventStore> extends EventEmitter { + protected abstract store: S | undefined + + getStore() { + if (!this.store) { + throw new Error('Store not initialized') + } + return this.store + } + + getAddress(): Store['address'] { + return this.getStore().address + } + + async close(): Promise { + logger.info('Closing', this.getAddress().path) + await this.store?.close() + logger.info('Closed', this.getAddress().path) + } + + abstract init(): Promise + abstract clean(): void +} + +export abstract class KeyValueStoreBase extends StoreBase> { + protected store: KeyValueStore | undefined + abstract setEntry(key: string, value: V): Promise + abstract getEntry(key?: string): V | null +} + +export abstract class EventStoreBase extends StoreBase> { + protected store: EventStore | undefined + abstract addEntry(value: V): Promise + abstract getEntries(): Promise +} diff --git a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.spec.ts b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.spec.ts index d15308b289..611c0d8438 100644 --- a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.spec.ts +++ b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.spec.ts @@ -76,15 +76,15 @@ describe('CertificatesRequestsStore', () => { ] for (const csr of allCsrs) { - await certificatesRequestsStore.addUserCsr(csr) + await certificatesRequestsStore.addEntry(csr) // This should not be there, there's bug in orbitdb, it breaks if we add entries without artificial sleep, tho it's awaited. // https://github.com/TryQuiet/quiet/issues/2121 await new Promise(resolve => setTimeout(() => resolve(), 500)) } - await certificatesRequestsStore.getCsrs() + await certificatesRequestsStore.getEntries() - const filteredCsrs = await certificatesRequestsStore.getCsrs() + const filteredCsrs = await certificatesRequestsStore.getEntries() expect(filteredCsrs.length).toEqual(allCsrs.length - 1) expect(filteredCsrs).toEqual([ @@ -103,7 +103,7 @@ describe('CertificatesRequestsStore', () => { const spy = jest.fn() certificatesRequestsStore.on(StorageEvents.CSRS_STORED, spy) - await replicatedEvent(certificatesRequestsStore.store) + await replicatedEvent(certificatesRequestsStore.getStore()) expect(spy).toBeCalledTimes(1) }) diff --git a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts index afcfe13032..74b9df611a 100644 --- a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts +++ b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts @@ -1,20 +1,17 @@ import { getCrypto } from 'pkijs' -import { EventEmitter } from 'events' -import EventStore from 'orbit-db-eventstore' import { NoCryptoEngineError } from '@quiet/types' import { loadCSR, keyFromCertificate } from '@quiet/identity' -import { CsrReplicatedPromiseValues, StorageEvents } from '../storage.types' +import { StorageEvents } from '../storage.types' import { validate } from 'class-validator' import { UserCsrData } from '../../registration/registration.functions' import { Injectable } from '@nestjs/common' import { OrbitDb } from '../orbitDb/orbitDb.service' import { createLogger } from '../../common/logger' +import { EventStoreBase } from '../base.store' @Injectable() -export class CertificatesRequestsStore extends EventEmitter { - public store: EventStore - - private readonly logger = createLogger(CertificatesRequestsStore.name) +export class CertificatesRequestsStore extends EventStoreBase { + protected readonly logger = createLogger(CertificatesRequestsStore.name) constructor(private readonly orbitDbService: OrbitDb) { super() @@ -46,23 +43,14 @@ export class CertificatesRequestsStore extends EventEmitter { public async loadedCertificateRequests() { this.emit(StorageEvents.CSRS_STORED, { - csrs: await this.getCsrs(), + csrs: await this.getEntries(), }) } - public async close() { - this.logger.info('Closing certificate requests DB') - await this.store?.close() - this.logger.info('Closed certificate requests DB') - } - - public getAddress() { - return this.store?.address - } - - public async addUserCsr(csr: string) { - await this.store.add(csr) - return true + public async addEntry(csr: string): Promise { + this.logger.info('Adding CSR to database') + await this.store?.add(csr) + return csr } public async validateUserCsr(csr: string) { @@ -88,9 +76,9 @@ export class CertificatesRequestsStore extends EventEmitter { return validationErrors } - public async getCsrs() { + public async getEntries() { const filteredCsrsMap: Map = new Map() - const allEntries = this.store + const allEntries = this.getStore() .iterator({ limit: -1 }) .collect() .map(e => { @@ -122,9 +110,7 @@ export class CertificatesRequestsStore extends EventEmitter { } public clean() { - // FIXME: Add correct typings on object fields. - - // @ts-ignore + this.logger.info('Cleaning certificates requests store') this.store = undefined } } diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts b/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts index bf0a1b1d54..f373b3e845 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts @@ -133,9 +133,9 @@ describe('CertificatesStore', () => { certificatesStore.updateMetadata(communityMetadata) - await certificatesStore.addCertificate(certificate) + await certificatesStore.addEntry(certificate) - const certificates = await certificatesStore.getCertificates() + const certificates = await certificatesStore.getEntries() expect(certificates).toContain(certificate) }) @@ -146,9 +146,9 @@ describe('CertificatesStore', () => { certificatesStore.updateMetadata(communityMetadata) - await certificatesStore.addCertificate(certificate) + await certificatesStore.addEntry(certificate) - const certificates = await certificatesStore.getCertificates() + const certificates = await certificatesStore.getEntries() expect(certificates).not.toContain(certificate) }) @@ -159,9 +159,9 @@ describe('CertificatesStore', () => { certificatesStore.updateMetadata(communityMetadata) - jest.spyOn(certificatesStore, 'getCertificates').mockResolvedValue([certificate1, certificate2]) + jest.spyOn(certificatesStore, 'getEntries').mockResolvedValue([certificate1, certificate2]) - const certificates = await certificatesStore.loadAllCertificates() + const certificates = await certificatesStore.getEntries() expect(certificates).toContain(certificate1) expect(certificates).toContain(certificate2) @@ -172,7 +172,7 @@ describe('CertificatesStore', () => { certificatesStore.updateMetadata(communityMetadata) - await certificatesStore.addCertificate(certificate) + await certificatesStore.addEntry(certificate) const result = await certificatesStore.getCertificateUsername(pubkey) expect(result).toBe(username) diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.ts b/packages/backend/src/nest/storage/certificates/certificates.store.ts index 7a0cf6ed81..0f345bb2e6 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.ts @@ -1,7 +1,5 @@ import { getCrypto } from 'pkijs' -import { EventEmitter } from 'events' import { StorageEvents } from '../storage.types' -import EventStore from 'orbit-db-eventstore' import { CommunityMetadata, NoCryptoEngineError } from '@quiet/types' import { keyFromCertificate, @@ -16,16 +14,16 @@ import { CertificateData } from '../../registration/registration.functions' import { OrbitDb } from '../orbitDb/orbitDb.service' import { Injectable } from '@nestjs/common' import { createLogger } from '../../common/logger' +import { EventStoreBase } from '../base.store' @Injectable() -export class CertificatesStore extends EventEmitter { - public store: EventStore +export class CertificatesStore extends EventStoreBase { + protected readonly logger = createLogger(CertificatesStore.name) + private metadata: CommunityMetadata | undefined private filteredCertificatesMapping: Map> private usernameMapping: Map - private readonly logger = createLogger(CertificatesStore.name) - constructor(private readonly orbitDbService: OrbitDb) { super() this.filteredCertificatesMapping = new Map() @@ -65,29 +63,14 @@ export class CertificatesStore extends EventEmitter { public async loadedCertificates() { this.emit(StorageEvents.CERTIFICATES_STORED, { - certificates: await this.getCertificates(), + certificates: await this.getEntries(), }) } - public async close() { - this.logger.info('Closing certificates DB') - await this.store?.close() - this.logger.info('Closed certificates DB') - } - - public getAddress() { - return this.store?.address - } - - public async addCertificate(certificate: string) { + public async addEntry(certificate: string): Promise { this.logger.info('Adding user certificate') await this.store?.add(certificate) - return true - } - - public async loadAllCertificates() { - const certificates = await this.getCertificates() - return certificates + return certificate } public updateMetadata(metadata: CommunityMetadata) { @@ -147,14 +130,9 @@ export class CertificatesStore extends EventEmitter { * as specified in the comment section of * https://github.com/TryQuiet/quiet/issues/1899 */ - public async getCertificates(): Promise { + public async getEntries(): Promise { this.logger.info('Getting certificates') - if (!this.store) { - this.logger.warn('No store found!') - return [] - } - - const allCertificates = this.store + const allCertificates = this.getStore() .iterator({ limit: -1 }) .collect() .map(e => e.payload.value) @@ -199,16 +177,14 @@ export class CertificatesStore extends EventEmitter { if (cache) return cache // Perform cryptographic operations and populate cache - await this.getCertificates() + await this.getEntries() // Return desired data from updated cache return this.usernameMapping.get(pubkey) } public clean() { - // FIXME: Add correct typings on object fields. - - // @ts-ignore + this.logger.info('Cleaning certificates store') this.store = undefined this.metadata = undefined this.filteredCertificatesMapping = new Map() diff --git a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.spec.ts b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.spec.ts index 6c49a5604d..36b88ffc2b 100644 --- a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.spec.ts +++ b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.spec.ts @@ -109,8 +109,8 @@ describe('CommmunityMetadataStore', () => { describe('updateCommunityMetadata', () => { test('updates community metadata if the metadata is valid', async () => { - const ret = await communityMetadataStore.updateCommunityMetadata(metaValid) - const meta = communityMetadataStore.getCommunityMetadata() + const ret = await communityMetadataStore.setEntry(metaValid.id, metaValid) + const meta = communityMetadataStore.getEntry() expect(ret).toStrictEqual(metaValidWithOwnerId) expect(meta).toStrictEqual(metaValidWithOwnerId) @@ -121,11 +121,9 @@ describe('CommmunityMetadataStore', () => { ...metaValid, rootCa: 'Something invalid!', } - const ret = await communityMetadataStore.updateCommunityMetadata(metaInvalid) - const meta = communityMetadataStore.getCommunityMetadata() - - expect(ret).toStrictEqual(undefined) - expect(meta).toEqual(undefined) + expect(communityMetadataStore.setEntry(metaInvalid.id, metaInvalid)).rejects.toThrow() + const meta = communityMetadataStore.getEntry() + expect(meta).toEqual(null) }) }) diff --git a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts index 14e0b785df..d40e62daaa 100644 --- a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts +++ b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts @@ -1,5 +1,3 @@ -import { EventEmitter } from 'events' -import KeyValueStore from 'orbit-db-kvstore' import { IdentityProvider } from 'orbit-db-identity-provider' // @ts-ignore Hacking around ipfs-log not exporting Entry import Entry from '../../../../node_modules/ipfs-log/src/entry' @@ -12,13 +10,12 @@ import { OrbitDb } from '../orbitDb/orbitDb.service' import { Injectable } from '@nestjs/common' import { createLogger } from '../../common/logger' import { constructPartial } from '@quiet/common' +import { KeyValueStoreBase } from '../base.store' -const logger = createLogger('CommunityMetadataStore') +const logger = createLogger('communityMetadataStore') @Injectable() -export class CommunityMetadataStore extends EventEmitter { - public store: KeyValueStore - +export class CommunityMetadataStore extends KeyValueStoreBase { constructor( private readonly orbitDbService: OrbitDb, private readonly localDbService: LocalDbService @@ -67,47 +64,37 @@ export class CommunityMetadataStore extends EventEmitter { this.store.events.on('replicated', async () => { logger.info('Replicated community metadata') - const meta = this.getCommunityMetadata() + const meta = this.getEntry() if (meta) { this.emit(StorageEvents.COMMUNITY_METADATA_STORED, meta) } }) await this.store.load() - const meta = this.getCommunityMetadata() + const meta = this.getEntry() if (meta) { this.emit(StorageEvents.COMMUNITY_METADATA_STORED, meta) } logger.info('Loaded community metadata to memory') } - public getAddress() { - return this.store?.address - } - - public async close() { - logger.info('Closing community metadata DB') - await this.store?.close() - logger.info('Closed community metadata DB') - } - - public async updateCommunityMetadata(newMeta: CommunityMetadata): Promise { + public async setEntry(key: string, value: CommunityMetadata): Promise { try { // TODO: Also check OrbitDB identity when updating community metadata - const valid = await CommunityMetadataStore.validateCommunityMetadata(newMeta) + const valid = await CommunityMetadataStore.validateCommunityMetadata(value) if (!valid) { // TODO: Send validation errors to frontend or replicate // validation on frontend? - logger.error('Failed to update community metadata') - return + logger.error('Failed to set community metadata. Metadata is invalid') + throw new Error('Failed to set community metadata') } - logger.info(`About to update community metadata`, newMeta?.id) - if (!newMeta.id) return + logger.info(`About to update community metadata`, value?.id) + if (!value.id) throw new Error('Community metadata id is missing') // FIXME: update community metadata if it has changed (so that // we can migrate community metadata easily) - const oldMeta = this.store.get(newMeta.id) + const oldMeta = this.getStore().get(key) if (oldMeta?.ownerCertificate && oldMeta?.rootCa) { return oldMeta } @@ -115,9 +102,9 @@ export class CommunityMetadataStore extends EventEmitter { logger.info(`Updating community metadata`) // @ts-expect-error - OrbitDB's type declaration of OrbitDB lacks identity const ownerOrbitDbIdentity = this.orbitDbService.orbitDb.identity.id - const meta = { + const meta: CommunityMetadata = { ...oldMeta, - ...newMeta, + ...value, ownerOrbitDbIdentity, } @@ -135,14 +122,22 @@ export class CommunityMetadataStore extends EventEmitter { // validateCommunityMetadataEntry and so validation may pass in // this method, but still the entry is not added to the internal // index. How can we detect that? - await this.store.put(meta.id, meta) + await this.getStore().put(key, meta) return meta } catch (err) { - logger.error('Failed to add community metadata', err) + logger.error('Failed to add community metadata', key, err) + throw new Error('Failed to add community metadata') } } + public getEntry(_key?: string): CommunityMetadata | null { + const metadata = Object.values(this.getStore().all) + if (metadata.length === 0) return null + + return metadata[0] + } + public static async validateCommunityMetadata(communityMetadata: CommunityMetadata): Promise { // FIXME: Add additional validation to verify communityMetadata // contains required fields. @@ -205,18 +200,8 @@ export class CommunityMetadataStore extends EventEmitter { } } - public getCommunityMetadata(): CommunityMetadata | undefined { - const metadata = Object.values(this.store.all) - - if (metadata.length > 0) { - return metadata[0] - } - } - public clean() { - // FIXME: Add correct typings on object fields. - - // @ts-ignore + logger.info('Cleaning metadata store') this.store = undefined } } diff --git a/packages/backend/src/nest/storage/storage.service.spec.ts b/packages/backend/src/nest/storage/storage.service.spec.ts index 9324a7390d..f0e733b466 100644 --- a/packages/backend/src/nest/storage/storage.service.spec.ts +++ b/packages/backend/src/nest/storage/storage.service.spec.ts @@ -539,13 +539,13 @@ describe('StorageService', () => { await storageService.init(peerId) // @ts-ignore storageService.certificatesRequestsStore = { - getCsrs: jest.fn(() => { + getEntries: jest.fn(() => { return csrs }), } // @ts-ignore storageService.certificatesStore = { - getCertificates: jest.fn(() => { + getEntries: jest.fn(() => { return certs }), } diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index a8d4afa60b..2098c98c17 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -12,7 +12,6 @@ import { import type { IPFS } from 'ipfs-core' import EventStore from 'orbit-db-eventstore' import KeyValueStore from 'orbit-db-kvstore' -import path from 'path' import { EventEmitter } from 'events' import PeerId from 'peer-id' import { getCrypto } from 'pkijs' @@ -37,16 +36,15 @@ import { type UserProfile, type UserProfilesStoredEvent, } from '@quiet/types' -import { createLibp2pAddress, isDefined } from '@quiet/common' +import { createLibp2pAddress } from '@quiet/common' import fs from 'fs' import { IpfsFileManagerService } from '../ipfs-file-manager/ipfs-file-manager.service' import { IPFS_REPO_PATCH, ORBIT_DB_DIR, QUIET_DIR } from '../const' import { IpfsFilesManagerEvents } from '../ipfs-file-manager/ipfs-file-manager.types' -import { LocalDBKeys } from '../local-db/local-db.types' import { LocalDbService } from '../local-db/local-db.service' import { LazyModuleLoader } from '@nestjs/core' import { createLogger } from '../common/logger' -import { DirectMessagesRepo, PublicChannelsRepo } from '../common/types' +import { PublicChannelsRepo } from '../common/types' import { removeFiles, removeDirs, createPaths } from '../common/utils' import { DBOptions, StorageEvents } from './storage.types' import { CertificatesStore } from './certificates/certificates.store' @@ -58,7 +56,6 @@ import { UserProfileStore } from './userProfile/userProfile.store' @Injectable() export class StorageService extends EventEmitter { public publicChannelsRepos: Map = new Map() - public directMessagesRepos: Map = new Map() private publicKeysMap: Map = new Map() public certificates: EventStore @@ -311,7 +308,7 @@ export class StorageService extends EventEmitter { } public async updateCommunityMetadata(communityMetadata: CommunityMetadata): Promise { - const meta = await this.communityMetadataStore?.updateCommunityMetadata(communityMetadata) + const meta = await this.communityMetadataStore?.setEntry(communityMetadata.id, communityMetadata) if (meta) { this.certificatesStore.updateMetadata(meta) } @@ -348,7 +345,7 @@ export class StorageService extends EventEmitter { public async loadAllCertificates() { this.logger.info('Loading all certificates') - return await this.certificatesStore.loadAllCertificates() + return await this.certificatesStore.getEntries() } public async loadAllChannels() { @@ -722,21 +719,20 @@ export class StorageService extends EventEmitter { return false } this.logger.info('Saving certificate...') - const result = await this.certificatesStore.addCertificate(payload.certificate) - return result + await this.certificatesStore.addEntry(payload.certificate) + return true } - public async saveCSR(payload: SaveCSRPayload): Promise { - const result = await this.certificatesRequestsStore.addUserCsr(payload.csr) - return result + public async saveCSR(payload: SaveCSRPayload): Promise { + await this.certificatesRequestsStore.addEntry(payload.csr) } /** * Retrieve all users (using certificates and CSRs to determine users) */ public async getAllUsers(): Promise { - const csrs = await this.certificatesRequestsStore.getCsrs() - const certs = await this.certificatesStore.getCertificates() + const csrs = await this.certificatesRequestsStore.getEntries() + const certs = await this.certificatesStore.getEntries() const allUsersByKey: Record = {} this.logger.info(`Retrieving all users. CSRs count: ${csrs.length} Certificates count: ${certs.length}`) @@ -788,7 +784,7 @@ export class StorageService extends EventEmitter { /** * Check if given username is already in use */ - const certificates = this.getAllEventLogEntries(this.certificatesStore.store) + const certificates = this.getAllEventLogEntries(this.certificatesStore.getStore()) for (const cert of certificates) { const parsedCert = parseCertificate(cert) const certUsername = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) @@ -822,7 +818,7 @@ export class StorageService extends EventEmitter { } public async addUserProfile(profile: UserProfile) { - await this.userProfileStore.addUserProfile(profile) + await this.userProfileStore.setEntry(profile.pubKey, profile) } public async checkIfFileExist(filepath: string): Promise { @@ -840,7 +836,6 @@ export class StorageService extends EventEmitter { this.messageThreads = undefined // @ts-ignore this.publicChannelsRepos = new Map() - this.directMessagesRepos = new Map() this.publicKeysMap = new Map() // @ts-ignore this.ipfs = null @@ -851,5 +846,6 @@ export class StorageService extends EventEmitter { this.certificatesRequestsStore.clean() this.certificatesStore.clean() this.communityMetadataStore.clean() + this.userProfileStore.clean() } } diff --git a/packages/backend/src/nest/storage/userProfile/userProfile.store.ts b/packages/backend/src/nest/storage/userProfile/userProfile.store.ts index a8f344e331..aa2a6e4d12 100644 --- a/packages/backend/src/nest/storage/userProfile/userProfile.store.ts +++ b/packages/backend/src/nest/storage/userProfile/userProfile.store.ts @@ -1,13 +1,10 @@ import { Injectable } from '@nestjs/common' -import { EventEmitter } from 'events' -import KeyValueStore from 'orbit-db-kvstore' import { IdentityProvider } from 'orbit-db-identity-provider' -import { getCrypto, ICryptoEngine } from 'pkijs' +import { getCrypto } from 'pkijs' import { sha256 } from 'multiformats/hashes/sha2' import * as Block from 'multiformats/block' import * as dagCbor from '@ipld/dag-cbor' import { stringToArrayBuffer } from 'pvutils' - import { NoCryptoEngineError, UserProfile } from '@quiet/types' import { keyObjectFromString, verifySignature } from '@quiet/identity' import { constructPartial } from '@quiet/common' @@ -17,13 +14,12 @@ import { OrbitDb } from '../orbitDb/orbitDb.service' import { StorageEvents } from '../storage.types' import { KeyValueIndex } from '../orbitDb/keyValueIndex' import { validatePhoto } from './userProfile.utils' +import { KeyValueStoreBase } from '../base.store' const logger = createLogger('UserProfileStore') @Injectable() -export class UserProfileStore extends EventEmitter { - public store: KeyValueStore - +export class UserProfileStore extends KeyValueStoreBase { // Copying OrbitDB by using dag-cbor/sha256 for converting the // profile to a byte array for signing: // https://github.com/orbitdb/orbitdb/blob/3eee148510110a7b698036488c70c5c78f868cd9/src/oplog/entry.js#L75-L76 @@ -79,29 +75,25 @@ export class UserProfileStore extends EventEmitter { await this.store.load() } - public getAddress() { - return this.store?.address - } - - public async close() { - logger.info('Closing user profile DB') - await this.store?.close() - logger.info('Closed user profile DB') + public getEntry(key: string): UserProfile { + throw new Error('Method not implemented.') } - public async addUserProfile(userProfile: UserProfile) { + public async setEntry(key: string, userProfile: UserProfile) { logger.info('Adding user profile') try { if (!UserProfileStore.validateUserProfile(userProfile)) { // TODO: Send validation errors to frontend or replicate // validation on frontend? - logger.error('Failed to add user profile') - return + logger.error('Failed to add user profile, profile is invalid', userProfile.pubKey) + throw new Error('Failed to add user profile') } - await this.store.put(userProfile.pubKey, userProfile) + await this.getStore().put(key, userProfile) } catch (err) { - logger.error('Failed to add user profile', err) + logger.error('Failed to add user profile', userProfile.pubKey, err) + throw new Error('Failed to add user profile') } + return userProfile } public static async validateUserProfile(userProfile: UserProfile) { @@ -157,7 +149,12 @@ export class UserProfileStore extends EventEmitter { } public getUserProfiles(): UserProfile[] { - return Object.values(this.store.all) + return Object.values(this.getStore().all) + } + + clean(): void { + logger.info('Cleaning user profiles store') + this.store = undefined } }