mirror of
https://github.com/bitwarden/browser
synced 2026-01-03 00:53:23 +00:00
sync folders and ciphers. fix dates
This commit is contained in:
@@ -286,6 +286,11 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
|
||||
// Folder APIs
|
||||
|
||||
async getFolder(id: string): Promise<FolderResponse> {
|
||||
const r = await this.send('GET', '/folders/' + id, null, true, true);
|
||||
return new FolderResponse(r);
|
||||
}
|
||||
|
||||
async postFolder(request: FolderRequest): Promise<FolderResponse> {
|
||||
const r = await this.send('POST', '/folders', request, true, true);
|
||||
return new FolderResponse(r);
|
||||
|
||||
@@ -644,7 +644,9 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
if (typeof id === 'string') {
|
||||
const i = id as string;
|
||||
if (ciphers[id] == null) {
|
||||
return;
|
||||
}
|
||||
delete ciphers[id];
|
||||
} else {
|
||||
(id as string[]).forEach((i) => {
|
||||
|
||||
@@ -152,7 +152,9 @@ export class FolderService implements FolderServiceAbstraction {
|
||||
}
|
||||
|
||||
if (typeof id === 'string') {
|
||||
const i = id as string;
|
||||
if (folders[id] == null) {
|
||||
return;
|
||||
}
|
||||
delete folders[id];
|
||||
} else {
|
||||
(id as string[]).forEach((i) => {
|
||||
|
||||
@@ -2,23 +2,24 @@ import * as signalR from '@aspnet/signalr';
|
||||
|
||||
import { NotificationType } from '../enums/notificationType';
|
||||
|
||||
import { CipherService } from '../abstractions/cipher.service';
|
||||
import { CollectionService } from '../abstractions/collection.service';
|
||||
import { AppIdService } from '../abstractions/appId.service';
|
||||
import { EnvironmentService } from '../abstractions/environment.service';
|
||||
import { FolderService } from '../abstractions/folder.service';
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from '../abstractions/notifications.service';
|
||||
import { SettingsService } from '../abstractions/settings.service';
|
||||
import { SyncService } from '../abstractions/sync.service';
|
||||
import { TokenService } from '../abstractions/token.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
|
||||
import { NotificationResponse } from '../models/response/notificationResponse';
|
||||
import {
|
||||
NotificationResponse,
|
||||
SyncCipherNotification,
|
||||
SyncFolderNotification,
|
||||
} from '../models/response/notificationResponse';
|
||||
|
||||
export class NotificationsService implements NotificationsServiceAbstraction {
|
||||
private signalrConnection: signalR.HubConnection;
|
||||
|
||||
constructor(private userService: UserService, private tokenService: TokenService,
|
||||
private syncService: SyncService) { }
|
||||
private syncService: SyncService, private appIdService: AppIdService) { }
|
||||
|
||||
async init(environmentService: EnvironmentService): Promise<void> {
|
||||
let url = 'https://notifications.bitwarden.com';
|
||||
@@ -61,21 +62,26 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
||||
}
|
||||
|
||||
private async processNotification(notification: NotificationResponse) {
|
||||
if (notification == null) {
|
||||
const appId = await this.appIdService.getAppId();
|
||||
if (notification == null || notification.contextId === appId) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (notification.type) {
|
||||
case NotificationType.SyncCipherCreate:
|
||||
case NotificationType.SyncCipherDelete:
|
||||
case NotificationType.SyncCipherUpdate:
|
||||
this.syncService.syncUpsertCipher(notification.payload as SyncCipherNotification);
|
||||
break;
|
||||
case NotificationType.SyncCipherDelete:
|
||||
case NotificationType.SyncLoginDelete:
|
||||
this.syncService.fullSync(false);
|
||||
this.syncService.syncDeleteCipher(notification.payload as SyncCipherNotification);
|
||||
break;
|
||||
case NotificationType.SyncFolderCreate:
|
||||
case NotificationType.SyncFolderDelete:
|
||||
case NotificationType.SyncFolderUpdate:
|
||||
this.syncService.fullSync(false);
|
||||
this.syncService.syncUpsertFolder(notification.payload as SyncFolderNotification);
|
||||
break;
|
||||
case NotificationType.SyncFolderDelete:
|
||||
this.syncService.syncDeleteFolder(notification.payload as SyncFolderNotification);
|
||||
break;
|
||||
case NotificationType.SyncVault:
|
||||
case NotificationType.SyncCiphers:
|
||||
|
||||
@@ -18,6 +18,10 @@ import { CipherResponse } from '../models/response/cipherResponse';
|
||||
import { CollectionDetailsResponse } from '../models/response/collectionResponse';
|
||||
import { DomainsResponse } from '../models/response/domainsResponse';
|
||||
import { FolderResponse } from '../models/response/folderResponse';
|
||||
import {
|
||||
SyncCipherNotification,
|
||||
SyncFolderNotification,
|
||||
} from '../models/response/notificationResponse';
|
||||
import { ProfileResponse } from '../models/response/profileResponse';
|
||||
|
||||
const Keys = {
|
||||
@@ -57,22 +61,11 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
await this.storageService.save(Keys.lastSyncPrefix + userId, date.toJSON());
|
||||
}
|
||||
|
||||
syncStarted() {
|
||||
this.syncInProgress = true;
|
||||
this.messagingService.send('syncStarted');
|
||||
}
|
||||
|
||||
syncCompleted(successfully: boolean) {
|
||||
this.syncInProgress = false;
|
||||
this.messagingService.send('syncCompleted', { successfully: successfully });
|
||||
}
|
||||
|
||||
async fullSync(forceSync: boolean): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
const isAuthenticated = await this.userService.isAuthenticated();
|
||||
if (!isAuthenticated) {
|
||||
this.syncCompleted(false);
|
||||
return false;
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
@@ -81,14 +74,12 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
const skipped = needsSyncResult[1];
|
||||
|
||||
if (skipped) {
|
||||
this.syncCompleted(false);
|
||||
return false;
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
if (!needsSync) {
|
||||
await this.setLastSync(now);
|
||||
this.syncCompleted(false);
|
||||
return false;
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
const userId = await this.userService.getUserId();
|
||||
@@ -102,16 +93,82 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
await this.syncSettings(userId, response.domains);
|
||||
|
||||
await this.setLastSync(now);
|
||||
this.syncCompleted(true);
|
||||
return true;
|
||||
return this.syncCompleted(true);
|
||||
} catch (e) {
|
||||
this.syncCompleted(false);
|
||||
return false;
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
}
|
||||
|
||||
async syncUpsertFolder(notification: SyncFolderNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
try {
|
||||
const remoteFolder = await this.apiService.getFolder(notification.id);
|
||||
const localFolder = await this.folderService.get(notification.id);
|
||||
if (remoteFolder != null &&
|
||||
(localFolder == null || localFolder.revisionDate < notification.revisionDate)) {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.folderService.upsert(new FolderData(remoteFolder, userId));
|
||||
this.messagingService.send('syncedUpsertedFolder', { folderId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
async syncDeleteFolder(notification: SyncFolderNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
await this.folderService.delete(notification.id);
|
||||
this.messagingService.send('syncedDeletedFolder', { folderId: notification.id });
|
||||
this.syncCompleted(true);
|
||||
return true;
|
||||
}
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
async syncUpsertCipher(notification: SyncCipherNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
try {
|
||||
const remoteCipher = await this.apiService.getCipher(notification.id);
|
||||
const localCipher = await this.cipherService.get(notification.id);
|
||||
if (remoteCipher != null &&
|
||||
(localCipher == null || localCipher.revisionDate < notification.revisionDate)) {
|
||||
const userId = await this.userService.getUserId();
|
||||
await this.cipherService.upsert(new CipherData(remoteCipher, userId));
|
||||
this.messagingService.send('syncedUpsertedCipher', { cipherId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
async syncDeleteCipher(notification: SyncCipherNotification): Promise<boolean> {
|
||||
this.syncStarted();
|
||||
if (await this.userService.isAuthenticated()) {
|
||||
await this.cipherService.delete(notification.id);
|
||||
this.messagingService.send('syncedDeletedCipher', { cipherId: notification.id });
|
||||
return this.syncCompleted(true);
|
||||
}
|
||||
return this.syncCompleted(false);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private syncStarted() {
|
||||
this.syncInProgress = true;
|
||||
this.messagingService.send('syncStarted');
|
||||
}
|
||||
|
||||
private syncCompleted(successfully: boolean): boolean {
|
||||
this.syncInProgress = false;
|
||||
this.messagingService.send('syncCompleted', { successfully: successfully });
|
||||
return successfully;
|
||||
}
|
||||
|
||||
private async needsSyncing(forceSync: boolean) {
|
||||
if (forceSync) {
|
||||
return [true, false];
|
||||
|
||||
Reference in New Issue
Block a user