diff --git a/.eslintignore b/.eslintignore index 11b3d21e30..cf2ec12bdf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -69,6 +69,9 @@ readme/ packages/app-cli/app/LinkSelector.d.ts packages/app-cli/app/LinkSelector.js packages/app-cli/app/LinkSelector.js.map +packages/app-cli/app/command-e2ee.d.ts +packages/app-cli/app/command-e2ee.js +packages/app-cli/app/command-e2ee.js.map packages/app-cli/app/command-settingschema.d.ts packages/app-cli/app/command-settingschema.js packages/app-cli/app/command-settingschema.js.map @@ -159,6 +162,9 @@ packages/app-desktop/commands/toggleSafeMode.js.map packages/app-desktop/gui/Button/Button.d.ts packages/app-desktop/gui/Button/Button.js packages/app-desktop/gui/Button/Button.js.map +packages/app-desktop/gui/ClipperConfigScreen.d.ts +packages/app-desktop/gui/ClipperConfigScreen.js +packages/app-desktop/gui/ClipperConfigScreen.js.map packages/app-desktop/gui/ConfigScreen/ButtonBar.d.ts packages/app-desktop/gui/ConfigScreen/ButtonBar.js packages/app-desktop/gui/ConfigScreen/ButtonBar.js.map @@ -195,6 +201,9 @@ packages/app-desktop/gui/DialogTitle.js.map packages/app-desktop/gui/DropboxLoginScreen.d.ts packages/app-desktop/gui/DropboxLoginScreen.js packages/app-desktop/gui/DropboxLoginScreen.js.map +packages/app-desktop/gui/EncryptionConfigScreen.d.ts +packages/app-desktop/gui/EncryptionConfigScreen.js +packages/app-desktop/gui/EncryptionConfigScreen.js.map packages/app-desktop/gui/ErrorBoundary.d.ts packages/app-desktop/gui/ErrorBoundary.js packages/app-desktop/gui/ErrorBoundary.js.map @@ -720,6 +729,9 @@ packages/app-mobile/components/screens/Note.js.map packages/app-mobile/components/screens/UpgradeSyncTargetScreen.d.ts packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js.map +packages/app-mobile/components/screens/encryption-config.d.ts +packages/app-mobile/components/screens/encryption-config.js +packages/app-mobile/components/screens/encryption-config.js.map packages/app-mobile/root.d.ts packages/app-mobile/root.js packages/app-mobile/root.js.map @@ -876,6 +888,9 @@ packages/lib/commands/historyForward.js.map packages/lib/commands/synchronize.d.ts packages/lib/commands/synchronize.js packages/lib/commands/synchronize.js.map +packages/lib/components/shared/encryption-config-shared.d.ts +packages/lib/components/shared/encryption-config-shared.js +packages/lib/components/shared/encryption-config-shared.js.map packages/lib/database.d.ts packages/lib/database.js packages/lib/database.js.map @@ -1062,6 +1077,9 @@ packages/lib/services/DecryptionWorker.js.map packages/lib/services/EncryptionService.d.ts packages/lib/services/EncryptionService.js packages/lib/services/EncryptionService.js.map +packages/lib/services/EncryptionService.test.d.ts +packages/lib/services/EncryptionService.test.js +packages/lib/services/EncryptionService.test.js.map packages/lib/services/ExternalEditWatcher.d.ts packages/lib/services/ExternalEditWatcher.js packages/lib/services/ExternalEditWatcher.js.map @@ -1500,6 +1518,9 @@ packages/lib/services/synchronizer/utils/types.js.map packages/lib/shim.d.ts packages/lib/shim.js packages/lib/shim.js.map +packages/lib/testing/syncTargetUtils.d.ts +packages/lib/testing/syncTargetUtils.js +packages/lib/testing/syncTargetUtils.js.map packages/lib/testing/test-utils-synchronizer.d.ts packages/lib/testing/test-utils-synchronizer.js packages/lib/testing/test-utils-synchronizer.js.map diff --git a/.gitignore b/.gitignore index 896fd1ca8c..73aa1cff6f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,9 @@ lerna-debug.log packages/app-cli/app/LinkSelector.d.ts packages/app-cli/app/LinkSelector.js packages/app-cli/app/LinkSelector.js.map +packages/app-cli/app/command-e2ee.d.ts +packages/app-cli/app/command-e2ee.js +packages/app-cli/app/command-e2ee.js.map packages/app-cli/app/command-settingschema.d.ts packages/app-cli/app/command-settingschema.js packages/app-cli/app/command-settingschema.js.map @@ -144,6 +147,9 @@ packages/app-desktop/commands/toggleSafeMode.js.map packages/app-desktop/gui/Button/Button.d.ts packages/app-desktop/gui/Button/Button.js packages/app-desktop/gui/Button/Button.js.map +packages/app-desktop/gui/ClipperConfigScreen.d.ts +packages/app-desktop/gui/ClipperConfigScreen.js +packages/app-desktop/gui/ClipperConfigScreen.js.map packages/app-desktop/gui/ConfigScreen/ButtonBar.d.ts packages/app-desktop/gui/ConfigScreen/ButtonBar.js packages/app-desktop/gui/ConfigScreen/ButtonBar.js.map @@ -180,6 +186,9 @@ packages/app-desktop/gui/DialogTitle.js.map packages/app-desktop/gui/DropboxLoginScreen.d.ts packages/app-desktop/gui/DropboxLoginScreen.js packages/app-desktop/gui/DropboxLoginScreen.js.map +packages/app-desktop/gui/EncryptionConfigScreen.d.ts +packages/app-desktop/gui/EncryptionConfigScreen.js +packages/app-desktop/gui/EncryptionConfigScreen.js.map packages/app-desktop/gui/ErrorBoundary.d.ts packages/app-desktop/gui/ErrorBoundary.js packages/app-desktop/gui/ErrorBoundary.js.map @@ -705,6 +714,9 @@ packages/app-mobile/components/screens/Note.js.map packages/app-mobile/components/screens/UpgradeSyncTargetScreen.d.ts packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js packages/app-mobile/components/screens/UpgradeSyncTargetScreen.js.map +packages/app-mobile/components/screens/encryption-config.d.ts +packages/app-mobile/components/screens/encryption-config.js +packages/app-mobile/components/screens/encryption-config.js.map packages/app-mobile/root.d.ts packages/app-mobile/root.js packages/app-mobile/root.js.map @@ -861,6 +873,9 @@ packages/lib/commands/historyForward.js.map packages/lib/commands/synchronize.d.ts packages/lib/commands/synchronize.js packages/lib/commands/synchronize.js.map +packages/lib/components/shared/encryption-config-shared.d.ts +packages/lib/components/shared/encryption-config-shared.js +packages/lib/components/shared/encryption-config-shared.js.map packages/lib/database.d.ts packages/lib/database.js packages/lib/database.js.map @@ -1047,6 +1062,9 @@ packages/lib/services/DecryptionWorker.js.map packages/lib/services/EncryptionService.d.ts packages/lib/services/EncryptionService.js packages/lib/services/EncryptionService.js.map +packages/lib/services/EncryptionService.test.d.ts +packages/lib/services/EncryptionService.test.js +packages/lib/services/EncryptionService.test.js.map packages/lib/services/ExternalEditWatcher.d.ts packages/lib/services/ExternalEditWatcher.js packages/lib/services/ExternalEditWatcher.js.map @@ -1485,6 +1503,9 @@ packages/lib/services/synchronizer/utils/types.js.map packages/lib/shim.d.ts packages/lib/shim.js packages/lib/shim.js.map +packages/lib/testing/syncTargetUtils.d.ts +packages/lib/testing/syncTargetUtils.js +packages/lib/testing/syncTargetUtils.js.map packages/lib/testing/test-utils-synchronizer.d.ts packages/lib/testing/test-utils-synchronizer.js packages/lib/testing/test-utils-synchronizer.js.map diff --git a/packages/app-cli/app/command-e2ee.js b/packages/app-cli/app/command-e2ee.ts similarity index 91% rename from packages/app-cli/app/command-e2ee.js rename to packages/app-cli/app/command-e2ee.ts index e72d8f7bbc..df75104ef4 100644 --- a/packages/app-cli/app/command-e2ee.js +++ b/packages/app-cli/app/command-e2ee.ts @@ -1,11 +1,11 @@ const { BaseCommand } = require('./base-command.js'); -const { _ } = require('@joplin/lib/locale'); -const EncryptionService = require('@joplin/lib/services/EncryptionService').default; -const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker').default; -const BaseItem = require('@joplin/lib/models/BaseItem').default; -const Setting = require('@joplin/lib/models/Setting').default; -const shim = require('@joplin/lib/shim').default; -const pathUtils = require('@joplin/lib/path-utils'); +import { _ } from '@joplin/lib/locale'; +import EncryptionService from '@joplin/lib/services/EncryptionService'; +import DecryptionWorker from '@joplin/lib/services/DecryptionWorker'; +import BaseItem from '@joplin/lib/models/BaseItem'; +import Setting from '@joplin/lib/models/Setting'; +import shim from '@joplin/lib/shim'; +import * as pathUtils from '@joplin/lib/path-utils'; const imageType = require('image-type'); const readChunk = require('read-chunk'); @@ -28,10 +28,10 @@ class Command extends BaseCommand { ]; } - async action(args) { + async action(args: any) { const options = args.options; - const askForMasterKey = async error => { + const askForMasterKey = async (error: any) => { const masterKeyId = error.masterKeyId; const password = await this.prompt(_('Enter master password:'), { type: 'string', secure: true }); if (!password) { @@ -155,9 +155,9 @@ class Command extends BaseCommand { const targetPath = args.path; if (!targetPath) throw new Error('Please specify the sync target path.'); - const dirPaths = function(targetPath) { - const paths = []; - fs.readdirSync(targetPath).forEach(path => { + const dirPaths = function(targetPath: string) { + const paths: string[] = []; + fs.readdirSync(targetPath).forEach((path: string) => { paths.push(path); }); return paths; diff --git a/packages/app-cli/tests/support/createSyncTargetSnapshot.js b/packages/app-cli/tests/support/createSyncTargetSnapshot.js index d40b81f2af..b1ccb251c8 100644 --- a/packages/app-cli/tests/support/createSyncTargetSnapshot.js +++ b/packages/app-cli/tests/support/createSyncTargetSnapshot.js @@ -1,4 +1,4 @@ -const {main} = require('@joplin/lib/testing/syncTargetUtils'); +const { main } = require('@joplin/lib/testing/syncTargetUtils'); const syncTargetType = process.argv.length <= 2 ? 'normal' : process.argv[2]; diff --git a/packages/app-cli/tests/support/syncTargetSnapshots/2/e2ee/locks/.gitignore b/packages/app-cli/tests/support/syncTargetSnapshots/2/e2ee/locks/.gitignore new file mode 100644 index 0000000000..5e7d2734cf --- /dev/null +++ b/packages/app-cli/tests/support/syncTargetSnapshots/2/e2ee/locks/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/packages/app-cli/tests/support/syncTargetSnapshots/2/e2ee/temp/.gitignore b/packages/app-cli/tests/support/syncTargetSnapshots/2/e2ee/temp/.gitignore new file mode 100644 index 0000000000..5e7d2734cf --- /dev/null +++ b/packages/app-cli/tests/support/syncTargetSnapshots/2/e2ee/temp/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/packages/app-cli/tests/support/syncTargetSnapshots/2/normal/locks/.gitignore b/packages/app-cli/tests/support/syncTargetSnapshots/2/normal/locks/.gitignore new file mode 100644 index 0000000000..5e7d2734cf --- /dev/null +++ b/packages/app-cli/tests/support/syncTargetSnapshots/2/normal/locks/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/packages/app-cli/tests/support/syncTargetSnapshots/2/normal/temp/.gitignore b/packages/app-cli/tests/support/syncTargetSnapshots/2/normal/temp/.gitignore new file mode 100644 index 0000000000..5e7d2734cf --- /dev/null +++ b/packages/app-cli/tests/support/syncTargetSnapshots/2/normal/temp/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/packages/app-desktop/gui/ClipperConfigScreen.jsx b/packages/app-desktop/gui/ClipperConfigScreen.tsx similarity index 91% rename from packages/app-desktop/gui/ClipperConfigScreen.jsx rename to packages/app-desktop/gui/ClipperConfigScreen.tsx index 0b61bd5730..6ed95b1f6a 100644 --- a/packages/app-desktop/gui/ClipperConfigScreen.jsx +++ b/packages/app-desktop/gui/ClipperConfigScreen.tsx @@ -1,13 +1,14 @@ const React = require('react'); const { connect } = require('react-redux'); -const bridge = require('electron').remote.require('./bridge').default; -const { themeStyle } = require('@joplin/lib/theme'); -const { _ } = require('@joplin/lib/locale'); -const ClipperServer = require('@joplin/lib/ClipperServer').default; -const Setting = require('@joplin/lib/models/Setting').default; const { clipboard } = require('electron'); const ExtensionBadge = require('./ExtensionBadge.min'); -const EncryptionService = require('@joplin/lib/services/EncryptionService').default; +import bridge from '../services/bridge'; +import { themeStyle } from '@joplin/lib/theme'; +import { _ } from '@joplin/lib/locale'; +import ClipperServer from '@joplin/lib/ClipperServer'; +import Setting from '@joplin/lib/models/Setting'; +import EncryptionService from '@joplin/lib/services/EncryptionService'; +import { AppState } from '../app'; class ClipperConfigScreenComponent extends React.Component { constructor() { @@ -18,12 +19,12 @@ class ClipperConfigScreenComponent extends React.Component { disableClipperServer_click() { Setting.setValue('clipperServer.autoStart', false); - ClipperServer.instance().stop(); + void ClipperServer.instance().stop(); } enableClipperServer_click() { Setting.setValue('clipperServer.autoStart', true); - ClipperServer.instance().start(); + void ClipperServer.instance().start(); } chromeButton_click() { @@ -165,7 +166,7 @@ class ClipperConfigScreenComponent extends React.Component { } } -const mapStateToProps = state => { +const mapStateToProps = (state: AppState) => { return { themeId: state.settings.theme, clipperServer: state.clipperServer, @@ -176,4 +177,4 @@ const mapStateToProps = state => { const ClipperConfigScreen = connect(mapStateToProps)(ClipperConfigScreenComponent); -module.exports = { ClipperConfigScreen }; +export default ClipperConfigScreen; diff --git a/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx b/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx index 928a7f4db4..96744342f4 100644 --- a/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx +++ b/packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx @@ -6,14 +6,14 @@ import { _ } from '@joplin/lib/locale'; import bridge from '../../services/bridge'; import Setting, { AppType, SyncStartupOperation } from '@joplin/lib/models/Setting'; import control_PluginsStates from './controls/plugins/PluginsStates'; +import EncryptionConfigScreen from '../EncryptionConfigScreen'; const { connect } = require('react-redux'); const { themeStyle } = require('@joplin/lib/theme'); const pathUtils = require('@joplin/lib/path-utils'); const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry'); const shared = require('@joplin/lib/components/shared/config-shared.js'); -const { EncryptionConfigScreen } = require('../EncryptionConfigScreen.min'); -const { ClipperConfigScreen } = require('../ClipperConfigScreen.min'); +import ClipperConfigScreen from '../ClipperConfigScreen'; const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen'); const settingKeyToControl: any = { diff --git a/packages/app-desktop/gui/EncryptionConfigScreen.jsx b/packages/app-desktop/gui/EncryptionConfigScreen.tsx similarity index 91% rename from packages/app-desktop/gui/EncryptionConfigScreen.jsx rename to packages/app-desktop/gui/EncryptionConfigScreen.tsx index fd11e8a870..a09f189867 100644 --- a/packages/app-desktop/gui/EncryptionConfigScreen.jsx +++ b/packages/app-desktop/gui/EncryptionConfigScreen.tsx @@ -1,17 +1,23 @@ const React = require('react'); const { connect } = require('react-redux'); -const Setting = require('@joplin/lib/models/Setting').default; -const EncryptionService = require('@joplin/lib/services/EncryptionService').default; -const { themeStyle } = require('@joplin/lib/theme'); -const { _ } = require('@joplin/lib/locale'); -const time = require('@joplin/lib/time').default; -const shim = require('@joplin/lib/shim').default; -const dialogs = require('./dialogs').default; -const shared = require('@joplin/lib/components/shared/encryption-config-shared.js'); -const bridge = require('electron').remote.require('./bridge').default; +import Setting from '@joplin/lib/models/Setting'; +import EncryptionService from '@joplin/lib/services/EncryptionService'; +import { themeStyle } from '@joplin/lib/theme'; +import { _ } from '@joplin/lib/locale'; +import time from '@joplin/lib/time'; +import { State } from '@joplin/lib/reducer'; +import shim from '@joplin/lib/shim'; +import dialogs from './dialogs'; +import bridge from '../services/bridge'; +import shared from '@joplin/lib/components/shared/encryption-config-shared'; +import { MasterKeyEntity } from '../../lib/services/database/types'; -class EncryptionConfigScreenComponent extends React.Component { - constructor(props) { +interface Props { + +} + +class EncryptionConfigScreenComponent extends React.Component { + constructor(props: Props) { super(props); shared.constructor(this, props); @@ -27,7 +33,7 @@ class EncryptionConfigScreenComponent extends React.Component { shared.componentDidMount(this); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { shared.componentDidUpdate(this, prevProps); } @@ -35,7 +41,7 @@ class EncryptionConfigScreenComponent extends React.Component { return shared.checkPasswords(this); } - renderMasterKey(mk) { + renderMasterKey(mk: MasterKeyEntity) { const theme = themeStyle(this.props.themeId); const passwordStyle = { @@ -49,7 +55,7 @@ class EncryptionConfigScreenComponent extends React.Component { return shared.onSavePasswordClick(this, mk); }; - const onPasswordChange = event => { + const onPasswordChange = (event: any) => { return shared.onPasswordChange(this, mk, event.target.value); }; @@ -188,7 +194,7 @@ class EncryptionConfigScreenComponent extends React.Component { @@ -155,7 +160,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { style={this.styles().normalTextInput} secureTextEntry={true} value={this.state.passwordPromptAnswer} - onChangeText={text => { + onChangeText={(text: string) => { this.setState({ passwordPromptAnswer: text }); }} > @@ -167,7 +172,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { style={this.styles().normalTextInput} secureTextEntry={true} value={this.state.passwordPromptConfirmAnswer} - onChangeText={text => { + onChangeText={(text: string) => { this.setState({ passwordPromptConfirmAnswer: text }); }} > @@ -176,7 +181,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { @@ -286,7 +291,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { { + ref={(dialogbox: any) => { this.dialogbox = dialogbox; }} /> @@ -295,7 +300,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent { } } -const EncryptionConfigScreen = connect(state => { +const EncryptionConfigScreen = connect((state: State) => { return { themeId: state.settings.theme, masterKeys: state.masterKeys, @@ -306,4 +311,4 @@ const EncryptionConfigScreen = connect(state => { }; })(EncryptionConfigScreenComponent); -module.exports = { EncryptionConfigScreen }; +export default EncryptionConfigScreen; diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 5e484d7ec4..1e2eb8a9c6 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -63,7 +63,7 @@ const { LogScreen } = require('./components/screens/log.js'); const { StatusScreen } = require('./components/screens/status.js'); const { SearchScreen } = require('./components/screens/search.js'); const { OneDriveLoginScreen } = require('./components/screens/onedrive-login.js'); -const { EncryptionConfigScreen } = require('./components/screens/encryption-config.js'); +import EncryptionConfigScreen from './components/screens/encryption-config'; const { DropboxLoginScreen } = require('./components/screens/dropbox-login.js'); const { MenuContext } = require('react-native-popup-menu'); const { SideMenu } = require('./components/side-menu.js'); diff --git a/packages/lib/Synchronizer.ts b/packages/lib/Synchronizer.ts index b6bd87b69f..799a1fb943 100644 --- a/packages/lib/Synchronizer.ts +++ b/packages/lib/Synchronizer.ts @@ -21,9 +21,12 @@ import ShareService from './services/share/ShareService'; import TaskQueue from './TaskQueue'; import ItemUploader from './services/synchronizer/ItemUploader'; import { FileApi } from './file-api'; +import JoplinDatabase from './JoplinDatabase'; const { sprintf } = require('sprintf-js'); const { Dirnames } = require('./services/synchronizer/utils/types'); +const logger = Logger.create('Synchronizer'); + interface RemoteItem { id: string; path?: string; @@ -62,7 +65,7 @@ export default class Synchronizer { public static verboseMode: boolean = true; - private db_: any; + private db_: JoplinDatabase; private api_: FileApi; private appType_: string; private logger_: Logger = new Logger(); @@ -87,7 +90,7 @@ export default class Synchronizer { public dispatch: Function; - public constructor(db: any, api: FileApi, appType: string) { + public constructor(db: JoplinDatabase, api: FileApi, appType: string) { this.db_ = db; this.api_ = api; this.appType_ = appType; @@ -214,9 +217,9 @@ export default class Synchronizer { } if (Synchronizer.verboseMode) { - this.logger().info(line.join(': ')); + logger.info(line.join(': ')); } else { - this.logger().debug(line.join(': ')); + logger.debug(line.join(': ')); } if (!this.progressReport_[action]) this.progressReport_[action] = 0; @@ -234,7 +237,7 @@ export default class Synchronizer { } async logSyncSummary(report: any) { - this.logger().info('Operations completed: '); + logger.info('Operations completed: '); for (const n in report) { if (!report.hasOwnProperty(n)) continue; if (n == 'errors') continue; @@ -243,20 +246,20 @@ export default class Synchronizer { if (n == 'state') continue; if (n == 'startTime') continue; if (n == 'completedTime') continue; - this.logger().info(`${n}: ${report[n] ? report[n] : '-'}`); + logger.info(`${n}: ${report[n] ? report[n] : '-'}`); } const folderCount = await Folder.count(); const noteCount = await Note.count(); const resourceCount = await Resource.count(); - this.logger().info(`Total folders: ${folderCount}`); - this.logger().info(`Total notes: ${noteCount}`); - this.logger().info(`Total resources: ${resourceCount}`); + logger.info(`Total folders: ${folderCount}`); + logger.info(`Total notes: ${noteCount}`); + logger.info(`Total resources: ${resourceCount}`); if (Synchronizer.reportHasErrors(report)) { - this.logger().warn('There was some errors:'); + logger.warn('There was some errors:'); for (let i = 0; i < report.errors.length; i++) { const e = report.errors[i]; - this.logger().warn(e); + logger.warn(e); } } } @@ -291,8 +294,8 @@ export default class Synchronizer { for (const r of lastRequests) { const timestamp = time.unixMsToLocalHms(r.timestamp); - this.logger().info(`Req ${timestamp}: ${r.request}`); - this.logger().info(`Res ${timestamp}: ${r.response}`); + logger.info(`Req ${timestamp}: ${r.request}`); + logger.info(`Res ${timestamp}: ${r.response}`); } } @@ -394,11 +397,11 @@ export default class Synchronizer { // plain text. try { if (this.resourceService()) { - this.logger().info('Indexing resources...'); + logger.info('Indexing resources...'); await this.resourceService().indexNoteResources(); } } catch (error) { - this.logger().error('Error indexing resources:', error); + logger.error('Error indexing resources:', error); } // Before synchronising make sure all share_id properties are set @@ -417,10 +420,10 @@ export default class Synchronizer { try { const syncTargetInfo = await this.migrationHandler().checkCanSync(); - this.logger().info('Sync target info:', syncTargetInfo); + logger.info('Sync target info:', syncTargetInfo); if (!syncTargetInfo.version) { - this.logger().info('Sync target is new - setting it up...'); + logger.info('Sync target is new - setting it up...'); await this.migrationHandler().upgrade(Setting.value('syncVersion')); } } catch (error) { @@ -433,7 +436,7 @@ export default class Synchronizer { syncLock = await this.lockHandler().acquireLock(LockType.Sync, this.appType_, this.clientId_); this.lockHandler().startAutoLockRefresh(syncLock, (error: any) => { - this.logger().warn('Could not refresh lock - cancelling sync. Error was:', error); + logger.warn('Could not refresh lock - cancelling sync. Error was:', error); this.syncTargetIsLocked_ = true; void this.cancel(); }); @@ -518,7 +521,7 @@ export default class Synchronizer { } catch (error) { if (error.code === 'rejectedByTarget') { this.progressReport_.errors.push(error); - this.logger().warn(`Rejected by target: ${path}: ${error.message}`); + logger.warn(`Rejected by target: ${path}: ${error.message}`); completeItemProcessing(path); continue; } else { @@ -577,7 +580,7 @@ export default class Synchronizer { // already been done" on the next loop, and sync // will never finish because we'll always end up // here. - this.logger().info(`Need to upload a resource, but blob is not present: ${path}`); + logger.info(`Need to upload a resource, but blob is not present: ${path}`); await handleCannotSyncItem(ItemClass, syncTargetId, local, 'Trying to upload resource, but only metadata is present.'); action = null; } else { @@ -589,7 +592,7 @@ export default class Synchronizer { const localResourceContentPath = result.path; if (resource.size >= 10 * 1000 * 1000) { - this.logger().warn(`Uploading a large resource (resourceId: ${local.id}, size:${resource.size} bytes) which may tie up the sync process.`); + logger.warn(`Uploading a large resource (resourceId: ${local.id}, size:${resource.size} bytes) which may tie up the sync process.`); } await this.apiCall('put', remoteContentPath, null, { path: localResourceContentPath, source: 'file', shareId: resource.share_id }); @@ -750,7 +753,7 @@ export default class Synchronizer { if (this.downloadQueue_) await this.downloadQueue_.stop(); this.downloadQueue_ = new TaskQueue('syncDownload'); - this.downloadQueue_.logger_ = this.logger(); + this.downloadQueue_.logger_ = logger; if (syncSteps.indexOf('delta') >= 0) { // At this point all the local items that have changed have been pushed to remote @@ -779,7 +782,7 @@ export default class Synchronizer { wipeOutFailSafe: Setting.value('sync.wipeOutFailSafe'), - logger: this.logger(), + logger: logger, }); const remotes: RemoteItem[] = listResult.items; @@ -860,7 +863,7 @@ export default class Synchronizer { } catch (error) { if (error.code === 'rejectedByTarget') { this.progressReport_.errors.push(error); - this.logger().warn(`Rejected by target: ${path}: ${error.message}`); + logger.warn(`Rejected by target: ${path}: ${error.message}`); action = null; } else { error.message = `On file ${path}: ${error.message}`; @@ -876,7 +879,7 @@ export default class Synchronizer { if (action == 'createLocal' || action == 'updateLocal') { if (content === null) { - this.logger().warn(`Remote has been deleted between now and the delta() call? In that case it will be handled during the next sync: ${path}`); + logger.warn(`Remote has been deleted between now and the delta() call? In that case it will be handled during the next sync: ${path}`); continue; } content = ItemClass.filter(content); @@ -914,11 +917,11 @@ export default class Synchronizer { if (!hasAutoEnabledEncryption && content.type_ === BaseModel.TYPE_MASTER_KEY && !masterKeysBefore) { hasAutoEnabledEncryption = true; - this.logger().info('One master key was downloaded and none was previously available: automatically enabling encryption'); - this.logger().info('Using master key: ', content.id); + logger.info('One master key was downloaded and none was previously available: automatically enabling encryption'); + logger.info('Using master key: ', content.id); await this.encryptionService().enableEncryption(content); await this.encryptionService().loadMasterKeysFromSettings(); - this.logger().info('Encryption has been enabled with downloaded master key as active key. However, note that no password was initially supplied. It will need to be provided by user.'); + logger.info('Encryption has been enabled with downloaded master key as active key. However, note that no password was initially supplied. It will need to be provided by user.'); } if (content.encryption_applied) this.dispatch({ type: 'SYNC_GOT_ENCRYPTED_ITEM' }); @@ -989,7 +992,7 @@ export default class Synchronizer { // Only log an info statement for this since this is a common condition that is reported // in the application, and needs to be resolved by the user. // Or it's a temporary issue that will be resolved on next sync. - this.logger().info(error.message); + logger.info(error.message); if (error.code === 'failSafe' || error.code === 'lockError') { // Get the message to display on UI, but not in testing to avoid poluting stdout @@ -998,10 +1001,10 @@ export default class Synchronizer { } } else if (error.code === 'unknownItemType') { this.progressReport_.errors.push(_('Unknown item type downloaded - please upgrade Joplin to the latest version')); - this.logger().error(error); + logger.error(error); } else { - this.logger().error(error); - if (error.details) this.logger().error('Details:', error.details); + logger.error(error); + if (error.details) logger.error('Details:', error.details); // Don't save to the report errors that are due to things like temporary network errors or timeout. if (!shim.fetchRequestCanBeRetried(error)) { @@ -1019,7 +1022,7 @@ export default class Synchronizer { this.syncTargetIsLocked_ = false; if (this.cancelling()) { - this.logger().info('Synchronisation was cancelled.'); + logger.info('Synchronisation was cancelled.'); this.cancelling_ = false; } @@ -1029,7 +1032,7 @@ export default class Synchronizer { try { await this.shareService_.maintenance(); } catch (error) { - this.logger().error('Could not run share service maintenance:', error); + logger.error('Could not run share service maintenance:', error); } } diff --git a/packages/lib/components/shared/encryption-config-shared.js b/packages/lib/components/shared/encryption-config-shared.ts similarity index 74% rename from packages/lib/components/shared/encryption-config-shared.js rename to packages/lib/components/shared/encryption-config-shared.ts index bf53693d4e..2e02700e33 100644 --- a/packages/lib/components/shared/encryption-config-shared.js +++ b/packages/lib/components/shared/encryption-config-shared.ts @@ -1,14 +1,15 @@ -const EncryptionService = require('../../services/EncryptionService').default; -const { _ } = require('../../locale'); -const BaseItem = require('../../models/BaseItem').default; -const Setting = require('../../models/Setting').default; -const MasterKey = require('../../models/MasterKey').default; -const { reg } = require('../../registry.js'); -const shim = require('../../shim').default; +import EncryptionService from '../../services/EncryptionService'; +import { _ } from '../../locale'; +import BaseItem from '../../models/BaseItem'; +import Setting from '../../models/Setting'; +import MasterKey from '../../models/MasterKey'; +import { reg } from '../../registry.js'; +import shim from '../../shim'; +import { MasterKeyEntity } from '../../services/database/types'; -const shared = {}; +const shared: any = {}; -shared.constructor = function(comp, props) { +shared.constructor = function(comp: any, props: any) { comp.state = { passwordChecks: {}, stats: { @@ -22,7 +23,7 @@ shared.constructor = function(comp, props) { shared.refreshStatsIID_ = null; }; -shared.refreshStats = async function(comp) { +shared.refreshStats = async function(comp: any) { const stats = await BaseItem.encryptedItemsStats(); comp.setState({ stats: stats, @@ -34,7 +35,7 @@ shared.reencryptData = async function() { if (!ok) return; await BaseItem.forceSyncAll(); - reg.waitForSyncFinishedThenSync(); + void reg.waitForSyncFinishedThenSync(); Setting.setValue('encryption.shouldReencrypt', Setting.SHOULD_REENCRYPT_NO); alert(_('Your data is going to be re-encrypted and synced again.')); }; @@ -43,7 +44,7 @@ shared.dontReencryptData = function() { Setting.setValue('encryption.shouldReencrypt', Setting.SHOULD_REENCRYPT_NO); }; -shared.upgradeMasterKey = async function(comp, masterKey) { +shared.upgradeMasterKey = async function(comp: any, masterKey: MasterKeyEntity) { const passwordCheck = comp.state.passwordChecks[masterKey.id]; if (!passwordCheck) { alert(_('Please enter your password in the master key list below before upgrading the key.')); @@ -54,14 +55,14 @@ shared.upgradeMasterKey = async function(comp, masterKey) { const password = comp.state.passwords[masterKey.id]; const newMasterKey = await EncryptionService.instance().upgradeMasterKey(masterKey, password); await MasterKey.save(newMasterKey); - reg.waitForSyncFinishedThenSync(); + void reg.waitForSyncFinishedThenSync(); alert(_('The master key has been upgraded successfully!')); } catch (error) { alert(_('Could not upgrade master key: %s', error.message)); } }; -shared.componentDidMount = async function(comp) { +shared.componentDidMount = async function(comp: any) { shared.componentDidUpdate(comp); shared.refreshStats(comp); @@ -81,7 +82,7 @@ shared.componentDidMount = async function(comp) { }, 3000); }; -shared.componentDidUpdate = async function(comp, prevProps = null) { +shared.componentDidUpdate = async function(comp: any, prevProps: any = null) { if (prevProps && comp.props.passwords !== prevProps.passwords) { comp.setState({ passwords: Object.assign({}, comp.props.passwords) }); } @@ -98,7 +99,7 @@ shared.componentWillUnmount = function() { } }; -shared.checkPasswords = async function(comp) { +shared.checkPasswords = async function(comp: any) { const passwordChecks = Object.assign({}, comp.state.passwordChecks); for (let i = 0; i < comp.props.masterKeys.length; i++) { const mk = comp.props.masterKeys[i]; @@ -109,14 +110,14 @@ shared.checkPasswords = async function(comp) { comp.setState({ passwordChecks: passwordChecks }); }; -shared.decryptedStatText = function(comp) { +shared.decryptedStatText = function(comp: any) { const stats = comp.state.stats; const doneCount = stats.encrypted !== null ? stats.total - stats.encrypted : '-'; const totalCount = stats.total !== null ? stats.total : '-'; return _('Decrypted items: %s / %s', doneCount, totalCount); }; -shared.onSavePasswordClick = function(comp, mk) { +shared.onSavePasswordClick = function(comp: any, mk: MasterKeyEntity) { const password = comp.state.passwords[mk.id]; if (!password) { Setting.deleteObjectValue('encryption.passwordCache', mk.id); @@ -127,10 +128,10 @@ shared.onSavePasswordClick = function(comp, mk) { comp.checkPasswords(); }; -shared.onPasswordChange = function(comp, mk, password) { +shared.onPasswordChange = function(comp: any, mk: MasterKeyEntity, password: string) { const passwords = Object.assign({}, comp.state.passwords); passwords[mk.id] = password; comp.setState({ passwords: passwords }); }; -module.exports = shared; +export default shared; diff --git a/packages/lib/services/DecryptionWorker.ts b/packages/lib/services/DecryptionWorker.ts index 4a3a27a87e..5fc5885252 100644 --- a/packages/lib/services/DecryptionWorker.ts +++ b/packages/lib/services/DecryptionWorker.ts @@ -9,6 +9,13 @@ import KvStore from './KvStore'; const EventEmitter = require('events'); +interface DecryptionResult { + skippedItemCount?: number; + decryptedItemCounts?: number; + decryptedItemCount?: number; + error: any; +} + export default class DecryptionWorker { public static instance_: DecryptionWorker = null; @@ -107,7 +114,7 @@ export default class DecryptionWorker { this.dispatch(action); } - async start_(options: any = null) { + private async start_(options: any = null): Promise { if (options === null) options = {}; if (!('masterKeyNotLoadedHandler' in options)) options.masterKeyNotLoadedHandler = 'throw'; if (!('errorHandler' in options)) options.errorHandler = 'log'; @@ -260,10 +267,11 @@ export default class DecryptionWorker { let decryptedItemCount = 0; for (const itemType in decryptedItemCounts) decryptedItemCount += decryptedItemCounts[itemType]; - const finalReport = { + const finalReport: DecryptionResult = { skippedItemCount: skippedItemCount, decryptedItemCounts: decryptedItemCounts, decryptedItemCount: decryptedItemCount, + error: null, }; this.dispatchReport(Object.assign({}, finalReport, { state: 'idle' })); @@ -276,7 +284,7 @@ export default class DecryptionWorker { return finalReport; } - async start(options: any) { + public async start(options: any = {}) { this.startCalls_.push(true); let output = null; try { diff --git a/packages/lib/services/EncryptionService.test.js b/packages/lib/services/EncryptionService.test.ts similarity index 92% rename from packages/lib/services/EncryptionService.test.js rename to packages/lib/services/EncryptionService.test.ts index d000a9ac76..90c222c80b 100644 --- a/packages/lib/services/EncryptionService.test.js +++ b/packages/lib/services/EncryptionService.test.ts @@ -1,14 +1,12 @@ -/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars, prefer-const */ +import { fileContentEqual, setupDatabaseAndSynchronizer, supportDir, switchClient, objectsEqual, checkThrowAsync } from '../testing/test-utils'; +import Folder from '../models/Folder'; +import Note from '../models/Note'; +import Setting from '../models/Setting'; +import BaseItem from '../models/BaseItem'; +import MasterKey from '../models/MasterKey'; +import EncryptionService from '../services/EncryptionService'; -const { fileContentEqual, setupDatabaseAndSynchronizer, supportDir, switchClient, objectsEqual, checkThrowAsync } = require('../testing/test-utils.js'); -const Folder = require('../models/Folder').default; -const Note = require('../models/Note').default; -const Setting = require('../models/Setting').default; -const BaseItem = require('../models/BaseItem').default; -const MasterKey = require('../models/MasterKey').default; -const EncryptionService = require('../services/EncryptionService').default; - -let service = null; +let service: EncryptionService = null; describe('services_EncryptionService', function() { @@ -85,7 +83,7 @@ describe('services_EncryptionService', function() { encryptionMethod: EncryptionService.METHOD_SJCL_2, }); - const hasThrown = await checkThrowAsync(async () => await service.upgradeMasterKey(masterKey, '777')); + await checkThrowAsync(async () => await service.upgradeMasterKey(masterKey, '777')); })); it('should require a checksum only for old master keys', (async () => { @@ -128,7 +126,7 @@ describe('services_EncryptionService', function() { encryptionMethod: EncryptionService.METHOD_SJCL, })); - const masterKey3 = await MasterKey.save(await service.generateMasterKey('123456')); + await MasterKey.save(await service.generateMasterKey('123456')); const needUpgrade = service.masterKeysThatNeedUpgrading(await MasterKey.all()); diff --git a/packages/lib/services/EncryptionService.ts b/packages/lib/services/EncryptionService.ts index 674e8fdf0c..5301bb08cf 100644 --- a/packages/lib/services/EncryptionService.ts +++ b/packages/lib/services/EncryptionService.ts @@ -46,7 +46,7 @@ export default class EncryptionService { private chunkSize_ = 5000; private loadedMasterKeys_: Record = {}; private activeMasterKeyId_: string = null; - private defaultEncryptionMethod_ = EncryptionService.METHOD_SJCL_1A; + public defaultEncryptionMethod_ = EncryptionService.METHOD_SJCL_1A; // public because used in tests private defaultMasterKeyEncryptionMethod_ = EncryptionService.METHOD_SJCL_4; private logger_ = new Logger(); diff --git a/packages/lib/services/synchronizer/MigrationHandler.ts b/packages/lib/services/synchronizer/MigrationHandler.ts index 9aaef4ef31..b2207b6551 100644 --- a/packages/lib/services/synchronizer/MigrationHandler.ts +++ b/packages/lib/services/synchronizer/MigrationHandler.ts @@ -16,6 +16,7 @@ const migrations = [ import Setting from '../../models/Setting'; const { sprintf } = require('sprintf-js'); import JoplinError from '../../JoplinError'; +import { FileApi } from '../../file-api'; interface SyncTargetInfo { version: number; @@ -23,12 +24,12 @@ interface SyncTargetInfo { export default class MigrationHandler extends BaseService { - private api_: any = null; + private api_: FileApi = null; private lockHandler_: LockHandler = null; private clientType_: string; private clientId_: string; - constructor(api: any, lockHandler: LockHandler, clientType: string, clientId: string) { + constructor(api: FileApi, lockHandler: LockHandler, clientType: string, clientId: string) { super(); this.api_ = api; this.lockHandler_ = lockHandler; diff --git a/packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.ts b/packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.ts index 2f2b2ace87..1940711a44 100644 --- a/packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.ts +++ b/packages/lib/services/synchronizer/synchronizer_MigrationHandler.test.ts @@ -1,18 +1,16 @@ import LockHandler from '../../services/synchronizer/LockHandler'; import MigrationHandler from '../../services/synchronizer/MigrationHandler'; import { Dirnames } from '../../services/synchronizer/utils/types'; +import { setSyncTargetName, fileApi, synchronizer, decryptionWorker, encryptionService, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } from '../../testing/test-utils'; +import { deploySyncTargetSnapshot, testData, checkTestData } from '../../testing/syncTargetUtils'; +import Setting from '../../models/Setting'; +import MasterKey from '../../models/MasterKey'; // To create a sync target snapshot for the current syncVersion: // - In test-utils, set syncTargetName_ to "filesystem" // - Then run: // gulp buildTests -L && node tests-build/support/createSyncTargetSnapshot.js normal && node tests-build/support/createSyncTargetSnapshot.js e2ee - -const { setSyncTargetName, fileApi, synchronizer, decryptionWorker, encryptionService, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('../../testing/test-utils.js'); -const { deploySyncTargetSnapshot, testData, checkTestData } = require('../../testing/syncTargetUtils'); -import Setting from '../../models/Setting'; -import MasterKey from '../../models/MasterKey'; - const specTimeout = 60000 * 10; // Nextcloud tests can be slow let lockHandler_: LockHandler = null; diff --git a/packages/lib/testing/syncTargetUtils.js b/packages/lib/testing/syncTargetUtils.ts similarity index 78% rename from packages/lib/testing/syncTargetUtils.js rename to packages/lib/testing/syncTargetUtils.ts index 070e274352..abe6adabfb 100644 --- a/packages/lib/testing/syncTargetUtils.js +++ b/packages/lib/testing/syncTargetUtils.ts @@ -1,12 +1,12 @@ -const { syncDir, synchronizer, supportDir, loadEncryptionMasterKey, setupDatabaseAndSynchronizer, switchClient } = require('../testing/test-utils.js'); -const Setting = require('../models/Setting').default; -const Folder = require('../models/Folder').default; -const Note = require('../models/Note').default; -const Tag = require('../models/Tag').default; -const Resource = require('../models/Resource').default; -const markdownUtils = require('../markdownUtils').default; -const shim = require('../shim').default; -const fs = require('fs-extra'); +import { syncDir, synchronizer, supportDir, loadEncryptionMasterKey, setupDatabaseAndSynchronizer, switchClient } from '../testing/test-utils'; +import Setting from '../models/Setting'; +import Folder from '../models/Folder'; +import Note from '../models/Note'; +import Tag from '../models/Tag'; +import Resource from '../models/Resource'; +import markdownUtils from '../markdownUtils'; +import shim from '../shim'; +import * as fs from 'fs-extra'; const snapshotBaseDir = `${supportDir}/syncTargetSnapshots`; @@ -36,8 +36,8 @@ const testData = { }, }; -async function createTestData(data) { - async function recurseStruct(s, parentId = '') { +async function createTestData(data: any) { + async function recurseStruct(s: any, parentId: string = '') { for (const n in s) { if (n.toLowerCase().includes('folder')) { const folder = await Folder.save({ title: n, parent_id: parentId }); @@ -60,8 +60,8 @@ async function createTestData(data) { await recurseStruct(data); } -async function checkTestData(data) { - async function recurseCheck(s) { +async function checkTestData(data: any) { + async function recurseCheck(s: any) { for (const n in s) { const obj = s[n]; @@ -98,13 +98,13 @@ async function checkTestData(data) { await recurseCheck(data); } -async function deploySyncTargetSnapshot(syncTargetType, syncVersion) { +async function deploySyncTargetSnapshot(syncTargetType: string, syncVersion: number) { const sourceDir = `${snapshotBaseDir}/${syncVersion}/${syncTargetType}`; await fs.remove(syncDir); await fs.copy(sourceDir, syncDir); } -async function main(syncTargetType) { +async function main(syncTargetType: string) { const validSyncTargetTypes = ['normal', 'e2ee']; if (!validSyncTargetTypes.includes(syncTargetType)) throw new Error(`Sync target type must be: ${validSyncTargetTypes.join(', ')}`); @@ -129,7 +129,7 @@ async function main(syncTargetType) { console.info(`Sync target snapshot created in: ${destDir}`); } -module.exports = { +export { checkTestData, main, testData,