diff --git a/src/abstractions/event.service.ts b/src/abstractions/event.service.ts new file mode 100644 index 0000000000..d677722512 --- /dev/null +++ b/src/abstractions/event.service.ts @@ -0,0 +1,6 @@ +import { EventType } from '../enums/eventType'; + +export abstract class EventService { + collect: (eventType: EventType, cipherId?: string, uploadImmediately?: boolean) => Promise; + uploadEvents: () => Promise; +} diff --git a/src/models/data/eventData.ts b/src/models/data/eventData.ts new file mode 100644 index 0000000000..232fb8235c --- /dev/null +++ b/src/models/data/eventData.ts @@ -0,0 +1,6 @@ +import { EventType } from '../../enums/eventType'; + +export class EventData { + type: EventType; + cipherId: string; +} diff --git a/src/services/constants.service.ts b/src/services/constants.service.ts index 4ec0fc0e08..648c85ff54 100644 --- a/src/services/constants.service.ts +++ b/src/services/constants.service.ts @@ -21,6 +21,7 @@ export class ConstantsService { static readonly pinProtectedKey: string = 'pinProtectedKey'; static readonly protectedPin: string = 'protectedPin'; static readonly clearClipboardKey: string = 'clearClipboardKey'; + static readonly eventCollectionKey: string = 'eventCollection'; readonly environmentUrlsKey: string = ConstantsService.environmentUrlsKey; readonly disableGaKey: string = ConstantsService.disableGaKey; @@ -43,4 +44,5 @@ export class ConstantsService { readonly pinProtectedKey: string = ConstantsService.pinProtectedKey; readonly protectedPin: string = ConstantsService.protectedPin; readonly clearClipboardKey: string = ConstantsService.clearClipboardKey; + readonly eventCollectionKey: string = ConstantsService.eventCollectionKey; } diff --git a/src/services/eventService.ts b/src/services/eventService.ts new file mode 100644 index 0000000000..86ce3f94db --- /dev/null +++ b/src/services/eventService.ts @@ -0,0 +1,78 @@ +import { EventType } from '../enums/eventType'; + +import { EventData } from '../models/data/eventData'; + +import { EventRequest } from '../models/request/eventRequest'; + +import { ApiService } from '../abstractions/api.service'; +import { CipherService } from '../abstractions/cipher.service'; +import { EventService as EventServiceAbstraction } from '../abstractions/event.service'; +import { StorageService } from '../abstractions/storage.service'; +import { UserService } from '../abstractions/user.service'; + +import { ConstantsService } from './constants.service'; + +export class EventService implements EventServiceAbstraction { + private inited = false; + + constructor(private storageService: StorageService, private apiService: ApiService, + private userService: UserService, private cipherService: CipherService) { } + + init(checkOnInterval: boolean) { + if (this.inited) { + return; + } + + this.inited = true; + if (checkOnInterval) { + this.uploadEvents(); + setInterval(() => this.uploadEvents(), 60 * 1000); // check every 60 seconds + } + } + + async collect(eventType: EventType, cipherId: string = null, uploadImmediately = false): Promise { + const organizations = await this.userService.getAllOrganizations(); + if (organizations == null) { + return; + } + const orgIds = new Set(organizations.filter((o) => o.useEvents).map((o) => o.id)); + if (orgIds.size === 0) { + return; + } + if (cipherId != null) { + const cipher = await this.cipherService.get(cipherId); + if (cipher == null || cipher.organizationId == null || !orgIds.has(cipher.organizationId)) { + return; + } + } + let eventCollection = await this.storageService.get(ConstantsService.eventCollectionKey); + if (eventCollection == null) { + eventCollection = []; + } + const event = new EventData(); + event.type = eventType; + event.cipherId = cipherId; + eventCollection.push(event); + await this.storageService.save(ConstantsService.eventCollectionKey, eventCollection); + if (uploadImmediately) { + await this.uploadEvents(); + } + } + + async uploadEvents(): Promise { + const eventCollection = await this.storageService.get(ConstantsService.eventCollectionKey); + if (eventCollection == null || eventCollection.length === 0) { + return; + } + const request = eventCollection.map((e) => { + const req = new EventRequest(); + req.type = e.type; + req.cipherId = e.cipherId; + return req; + }); + try { + await this.apiService.postEventsCollectMany(request); + await this.storageService.remove(ConstantsService.eventCollectionKey); + } catch { } + } +}