1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-05 18:13:26 +00:00

Split jslib into multiple modules (#363)

* Split jslib into multiple modules
This commit is contained in:
Oscar Hinton
2021-06-03 18:58:57 +02:00
committed by GitHub
parent b1d9b84eae
commit 1016bbfb9e
509 changed files with 8838 additions and 1887 deletions

View File

@@ -0,0 +1,46 @@
import log from 'electron-log';
import * as path from 'path';
import { isDev } from '../utils';
import { LogLevelType } from 'jslib-common/enums/logLevelType';
import { ConsoleLogService as BaseLogService } from 'jslib-common/services/consoleLog.service';
export class ElectronLogService extends BaseLogService {
constructor(protected filter: (level: LogLevelType) => boolean = null, logDir: string = null) {
super(isDev(), filter);
if (log.transports == null) {
return;
}
log.transports.file.level = 'info';
if (logDir != null) {
log.transports.file.file = path.join(logDir, 'app.log');
}
}
write(level: LogLevelType, message: string) {
if (this.filter != null && this.filter(level)) {
return;
}
switch (level) {
case LogLevelType.Debug:
log.debug(message);
break;
case LogLevelType.Info:
log.info(message);
break;
case LogLevelType.Warning:
log.warn(message);
break;
case LogLevelType.Error:
log.error(message);
break;
default:
break;
}
}
}

View File

@@ -0,0 +1,56 @@
import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme } from 'electron';
import { promises as fs } from 'fs';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { RendererMenuItem } from '../utils';
import { WindowMain } from '../window.main';
export class ElectronMainMessagingService implements MessagingService {
constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) {
ipcMain.handle('appVersion', () => {
return app.getVersion();
});
ipcMain.handle('systemTheme', () => {
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
});
ipcMain.handle('showMessageBox', (event, options) => {
return dialog.showMessageBox(options);
});
ipcMain.handle('openContextMenu', (event, options: {menu: RendererMenuItem[]}) => {
return new Promise(resolve => {
const menu = new Menu();
options.menu.forEach((m, index) => {
menu.append(new MenuItem({
label: m.label,
type: m.type,
click: () => {
resolve(index);
},
}));
});
menu.popup({ window: windowMain.win, callback: () => {
resolve(-1);
}});
});
});
ipcMain.handle('windowVisible', () => {
return windowMain.win?.isVisible();
});
nativeTheme.on('updated', () => {
windowMain.win?.webContents.send('systemThemeUpdated', nativeTheme.shouldUseDarkColors ? 'dark' : 'light');
});
}
send(subscriber: string, arg: any = {}) {
const message = Object.assign({}, { command: subscriber }, arg);
this.onMessage(message);
if (this.windowMain.win != null) {
this.windowMain.win.webContents.send('messagingService', message);
}
}
}

View File

@@ -0,0 +1,211 @@
import {
clipboard,
ipcRenderer,
shell,
} from 'electron';
import {
isDev,
isMacAppStore,
} from '../utils';
import { DeviceType } from 'jslib-common/enums/deviceType';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { ElectronConstants } from '../electronConstants';
export class ElectronPlatformUtilsService implements PlatformUtilsService {
identityClientId: string;
private deviceCache: DeviceType = null;
constructor(protected i18nService: I18nService, private messagingService: MessagingService,
private isDesktopApp: boolean, private storageService: StorageService) {
this.identityClientId = isDesktopApp ? 'desktop' : 'connector';
}
getDevice(): DeviceType {
if (!this.deviceCache) {
switch (process.platform) {
case 'win32':
this.deviceCache = DeviceType.WindowsDesktop;
break;
case 'darwin':
this.deviceCache = DeviceType.MacOsDesktop;
break;
case 'linux':
default:
this.deviceCache = DeviceType.LinuxDesktop;
break;
}
}
return this.deviceCache;
}
getDeviceString(): string {
const device = DeviceType[this.getDevice()].toLowerCase();
return device.replace('desktop', '');
}
isFirefox(): boolean {
return false;
}
isChrome(): boolean {
return true;
}
isEdge(): boolean {
return false;
}
isOpera(): boolean {
return false;
}
isVivaldi(): boolean {
return false;
}
isSafari(): boolean {
return false;
}
isIE(): boolean {
return false;
}
isMacAppStore(): boolean {
return isMacAppStore();
}
isViewOpen(): Promise<boolean> {
return Promise.resolve(false);
}
lockTimeout(): number {
return null;
}
launchUri(uri: string, options?: any): void {
shell.openExternal(uri);
}
saveFile(win: Window, blobData: any, blobOptions: any, fileName: string): void {
const blob = new Blob([blobData], blobOptions);
const a = win.document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = fileName;
win.document.body.appendChild(a);
a.click();
win.document.body.removeChild(a);
}
getApplicationVersion(): Promise<string> {
return ipcRenderer.invoke('appVersion');
}
// Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349
// has been merged and an updated electron build is available.
supportsWebAuthn(win: Window): boolean {
return process.platform === 'win32';
}
supportsDuo(): boolean {
return true;
}
showToast(type: 'error' | 'success' | 'warning' | 'info', title: string, text: string | string[],
options?: any): void {
this.messagingService.send('showToast', {
text: text,
title: title,
type: type,
options: options,
});
}
async showDialog(text: string, title?: string, confirmText?: string, cancelText?: string, type?: string):
Promise<boolean> {
const buttons = [confirmText == null ? this.i18nService.t('ok') : confirmText];
if (cancelText != null) {
buttons.push(cancelText);
}
const result = await ipcRenderer.invoke('showMessageBox', {
type: type,
title: title,
message: title,
detail: text,
buttons: buttons,
cancelId: buttons.length === 2 ? 1 : null,
defaultId: 0,
noLink: true,
});
return Promise.resolve(result.response === 0);
}
async showPasswordDialog(title: string, body: string, passwordValidation: (value: string) => Promise<boolean>):
Promise<boolean> {
throw new Error('Not implemented.');
}
isDev(): boolean {
return isDev();
}
isSelfHost(): boolean {
return false;
}
copyToClipboard(text: string, options?: any): void {
const type = options ? options.type : null;
const clearing = options ? !!options.clearing : false;
const clearMs: number = options && options.clearMs ? options.clearMs : null;
clipboard.writeText(text, type);
if (!clearing) {
this.messagingService.send('copiedToClipboard', {
clipboardValue: text,
clearMs: clearMs,
type: type,
clearing: clearing,
});
}
}
readFromClipboard(options?: any): Promise<string> {
const type = options ? options.type : null;
return Promise.resolve(clipboard.readText(type));
}
supportsBiometric(): Promise<boolean> {
return this.storageService.get(ElectronConstants.enableBiometric);
}
authenticateBiometric(): Promise<boolean> {
return new Promise(resolve => {
const val = ipcRenderer.sendSync('biometric', {
action: 'authenticate',
});
resolve(val);
});
}
getDefaultSystemTheme() {
return ipcRenderer.invoke('systemTheme');
}
onDefaultSystemThemeChange(callback: ((theme: 'light' | 'dark') => unknown)) {
ipcRenderer.on('systemThemeUpdated', (event, theme: 'light' | 'dark') => callback(theme));
}
supportsSecureStorage(): boolean {
return true;
}
}

View File

@@ -0,0 +1,26 @@
import { ipcRenderer } from 'electron';
import { BroadcasterService } from 'jslib-common/abstractions/broadcaster.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
export class ElectronRendererMessagingService implements MessagingService {
constructor(private broadcasterService: BroadcasterService) {
ipcRenderer.on('messagingService', async (event: any, message: any) => {
if (message.command) {
this.sendMessage(message.command, message, false);
}
});
}
send(subscriber: string, arg: any = {}) {
this.sendMessage(subscriber, arg, true);
}
private sendMessage(subscriber: string, arg: any = {}, toMain: boolean) {
const message = Object.assign({}, { command: subscriber }, arg);
this.broadcasterService.send(message);
if (toMain) {
ipcRenderer.send('messagingService', message);
}
}
}

View File

@@ -0,0 +1,30 @@
import { ipcRenderer } from 'electron';
import { StorageService } from 'jslib-common/abstractions/storage.service';
export class ElectronRendererSecureStorageService implements StorageService {
async get<T>(key: string): Promise<T> {
const val = ipcRenderer.sendSync('keytar', {
action: 'getPassword',
key: key,
});
return Promise.resolve(val != null ? JSON.parse(val) as T : null);
}
async save(key: string, obj: any): Promise<any> {
ipcRenderer.sendSync('keytar', {
action: 'setPassword',
key: key,
value: JSON.stringify(obj),
});
return Promise.resolve();
}
async remove(key: string): Promise<any> {
ipcRenderer.sendSync('keytar', {
action: 'deletePassword',
key: key,
});
return Promise.resolve();
}
}

View File

@@ -0,0 +1,27 @@
import { ipcRenderer } from 'electron';
import { StorageService } from 'jslib-common/abstractions/storage.service';
export class ElectronRendererStorageService implements StorageService {
get<T>(key: string): Promise<T> {
return ipcRenderer.invoke('storageService', {
action: 'get',
key: key,
});
}
save(key: string, obj: any): Promise<any> {
return ipcRenderer.invoke('storageService', {
action: 'save',
key: key,
obj: obj,
});
}
remove(key: string): Promise<any> {
return ipcRenderer.invoke('storageService', {
action: 'remove',
key: key,
});
}
}

View File

@@ -0,0 +1,53 @@
import { ipcMain, ipcRenderer } from 'electron';
import * as fs from 'fs';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { NodeUtils } from 'jslib-common/misc/nodeUtils';
// tslint:disable-next-line
const Store = require('electron-store');
export class ElectronStorageService implements StorageService {
private store: any;
constructor(dir: string, defaults = {}) {
if (!fs.existsSync(dir)) {
NodeUtils.mkdirpSync(dir, '700');
}
const storeConfig: any = {
defaults: defaults,
name: 'data',
};
this.store = new Store(storeConfig);
ipcMain.handle('storageService', (event, options) => {
switch (options.action) {
case 'get':
return this.get(options.key);
case 'save':
return this.save(options.key, options.obj);
case 'remove':
return this.remove(options.key);
}
});
}
get<T>(key: string): Promise<T> {
const val = this.store.get(key) as T;
return Promise.resolve(val != null ? val : null);
}
save(key: string, obj: any): Promise<any> {
if (obj instanceof Set) {
obj = Array.from(obj);
}
this.store.set(key, obj);
return Promise.resolve();
}
remove(key: string): Promise<any> {
this.store.delete(key);
return Promise.resolve();
}
}