Stop Logger being a singleton
This commit is contained in:
parent
d965265709
commit
3471a9c498
12 changed files with 147 additions and 138 deletions
|
|
@ -1,47 +1,45 @@
|
|||
import type { Syncer } from "sync-client";
|
||||
import type { SyncClient, Syncer } from "sync-client";
|
||||
import { Logger } from "sync-client";
|
||||
import type { TAbstractFile } from "obsidian";
|
||||
import { TFile } from "obsidian";
|
||||
|
||||
export class ObsidianFileEventHandler {
|
||||
public constructor(private readonly syncer: Syncer) {}
|
||||
public constructor(private readonly client: SyncClient) {}
|
||||
|
||||
public async onCreate(file: TAbstractFile): Promise<void> {
|
||||
if (file instanceof TFile) {
|
||||
Logger.getInstance().info(`File created: ${file.path}`);
|
||||
this.client.logger.info(`File created: ${file.path}`);
|
||||
|
||||
await this.syncer.syncLocallyCreatedFile(
|
||||
await this.client.syncer.syncLocallyCreatedFile(
|
||||
file.path,
|
||||
new Date(file.stat.ctime)
|
||||
);
|
||||
} else {
|
||||
Logger.getInstance().debug(`Folder created: ${file.path}, ignored`);
|
||||
this.client.logger.debug(`Folder created: ${file.path}, ignored`);
|
||||
}
|
||||
}
|
||||
|
||||
public async onDelete(file: TAbstractFile): Promise<void> {
|
||||
if (file instanceof TFile) {
|
||||
Logger.getInstance().info(`File deleted: ${file.path}`);
|
||||
this.client.logger.info(`File deleted: ${file.path}`);
|
||||
|
||||
await this.syncer.syncLocallyDeletedFile(file.path);
|
||||
await this.client.syncer.syncLocallyDeletedFile(file.path);
|
||||
} else {
|
||||
Logger.getInstance().debug(`Folder deleted: ${file.path}, ignored`);
|
||||
this.client.logger.debug(`Folder deleted: ${file.path}, ignored`);
|
||||
}
|
||||
}
|
||||
|
||||
public async onRename(file: TAbstractFile, oldPath: string): Promise<void> {
|
||||
if (file instanceof TFile) {
|
||||
Logger.getInstance().info(
|
||||
`File renamed: ${oldPath} -> ${file.path}`
|
||||
);
|
||||
this.client.logger.info(`File renamed: ${oldPath} -> ${file.path}`);
|
||||
|
||||
await this.syncer.syncLocallyUpdatedFile({
|
||||
await this.client.syncer.syncLocallyUpdatedFile({
|
||||
oldPath,
|
||||
relativePath: file.path,
|
||||
updateTime: new Date(file.stat.ctime)
|
||||
});
|
||||
} else {
|
||||
Logger.getInstance().debug(
|
||||
this.client.logger.debug(
|
||||
`Folder renamed: ${oldPath} -> ${file.path}, ignored`
|
||||
);
|
||||
}
|
||||
|
|
@ -53,16 +51,14 @@ export class ObsidianFileEventHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
Logger.getInstance().info(`File modified: ${file.path}`);
|
||||
this.client.logger.info(`File modified: ${file.path}`);
|
||||
|
||||
await this.syncer.syncLocallyUpdatedFile({
|
||||
await this.client.syncer.syncLocallyUpdatedFile({
|
||||
relativePath: file.path,
|
||||
updateTime: new Date(file.stat.ctime)
|
||||
});
|
||||
} else {
|
||||
Logger.getInstance().debug(
|
||||
`Folder modified: ${file.path}, ignored`
|
||||
);
|
||||
this.client.logger.debug(`Folder modified: ${file.path}, ignored`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { StatusBar } from "./views/status-bar";
|
|||
|
||||
import { LogsView } from "./views/logs-view";
|
||||
import { StatusDescription } from "./views/status-description";
|
||||
import { Logger, SyncClient } from "sync-client";
|
||||
import { SyncClient } from "sync-client";
|
||||
import { ObsidianFileSystemOperations } from "./obsidian-file-system";
|
||||
|
||||
export default class VaultLinkPlugin extends Plugin {
|
||||
|
|
@ -18,8 +18,6 @@ export default class VaultLinkPlugin extends Plugin {
|
|||
private client!: SyncClient;
|
||||
|
||||
public async onload(): Promise<void> {
|
||||
Logger.getInstance().info("Starting plugin");
|
||||
|
||||
this.client = await SyncClient.create(
|
||||
new ObsidianFileSystemOperations(this.app.vault),
|
||||
{
|
||||
|
|
@ -28,6 +26,8 @@ export default class VaultLinkPlugin extends Plugin {
|
|||
}
|
||||
);
|
||||
|
||||
this.client.logger.info("Starting plugin");
|
||||
|
||||
const statusDescription = new StatusDescription(this.client);
|
||||
|
||||
this.settingsTab = new SyncSettingsTab({
|
||||
|
|
@ -42,12 +42,11 @@ export default class VaultLinkPlugin extends Plugin {
|
|||
|
||||
this.registerView(
|
||||
HistoryView.TYPE,
|
||||
(leaf) =>
|
||||
new HistoryView(leaf, this.client.settings, this.client.history)
|
||||
(leaf) => new HistoryView(leaf, this.client)
|
||||
);
|
||||
this.registerView(
|
||||
LogsView.TYPE,
|
||||
(leaf) => new LogsView(this, this.client.settings, leaf)
|
||||
(leaf) => new LogsView(this, this.client, leaf)
|
||||
);
|
||||
|
||||
this.addRibbonIcon(
|
||||
|
|
@ -61,11 +60,10 @@ export default class VaultLinkPlugin extends Plugin {
|
|||
async (_: MouseEvent) => this.activateView(LogsView.TYPE)
|
||||
);
|
||||
|
||||
const eventHandler = new ObsidianFileEventHandler(this.client.syncer);
|
||||
const eventHandler = new ObsidianFileEventHandler(this.client);
|
||||
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
Logger.getInstance().info("Initialising sync handlers");
|
||||
|
||||
this.client.logger.info("Initialising sync handlers");
|
||||
[
|
||||
this.app.vault.on(
|
||||
"create",
|
||||
|
|
@ -87,7 +85,7 @@ export default class VaultLinkPlugin extends Plugin {
|
|||
this.registerEvent(event);
|
||||
});
|
||||
|
||||
Logger.getInstance().info("Sync handlers initialised");
|
||||
this.client.logger.info("Sync handlers initialised");
|
||||
|
||||
void this.client.syncer.scheduleSyncForOfflineChanges();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { IconName, WorkspaceLeaf } from "obsidian";
|
|||
import { ItemView, setIcon } from "obsidian";
|
||||
|
||||
import { intlFormatDistance } from "date-fns";
|
||||
import type { SyncHistory, HistoryEntry, Settings } from "sync-client";
|
||||
import type { HistoryEntry, SyncClient } from "sync-client";
|
||||
import { SyncType, SyncSource, SyncStatus, Logger } from "sync-client";
|
||||
|
||||
export class HistoryView extends ItemView {
|
||||
|
|
@ -12,15 +12,14 @@ export class HistoryView extends ItemView {
|
|||
|
||||
public constructor(
|
||||
leaf: WorkspaceLeaf,
|
||||
private readonly settings: Settings,
|
||||
private readonly history: SyncHistory
|
||||
private readonly client: SyncClient
|
||||
) {
|
||||
super(leaf);
|
||||
this.icon = HistoryView.ICON;
|
||||
|
||||
history.addSyncHistoryUpdateListener(() => {
|
||||
this.client.history.addSyncHistoryUpdateListener(() => {
|
||||
this.updateView().catch((_error: unknown) => {
|
||||
Logger.getInstance().error("Failed to update history view");
|
||||
this.client.logger.error("Failed to update history view");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -94,13 +93,13 @@ export class HistoryView extends ItemView {
|
|||
container.empty();
|
||||
container.createEl("h4", { text: "VaultLink History" });
|
||||
|
||||
const entries = this.history
|
||||
const entries = this.client.history
|
||||
.getEntries()
|
||||
.reverse()
|
||||
.filter(
|
||||
(entry) =>
|
||||
entry.status !== SyncStatus.NO_OP ||
|
||||
this.settings.getSettings().displayNoopSyncEvents
|
||||
this.client.settings.getSettings().displayNoopSyncEvents
|
||||
);
|
||||
|
||||
entries.forEach((entry) => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import type { WorkspaceLeaf } from "obsidian";
|
||||
import { ItemView } from "obsidian";
|
||||
import type VaultLinkPlugin from "src/vault-link-plugin";
|
||||
import type { Settings } from "sync-client";
|
||||
import { Logger } from "sync-client";
|
||||
import type { SyncClient } from "sync-client";
|
||||
|
||||
export class LogsView extends ItemView {
|
||||
public static readonly TYPE = "logs-view";
|
||||
|
|
@ -10,20 +9,24 @@ export class LogsView extends ItemView {
|
|||
|
||||
public constructor(
|
||||
private readonly plugin: VaultLinkPlugin,
|
||||
private readonly settings: Settings,
|
||||
private readonly client: SyncClient,
|
||||
leaf: WorkspaceLeaf
|
||||
) {
|
||||
super(leaf);
|
||||
this.icon = LogsView.ICON;
|
||||
Logger.getInstance().addOnMessageListener(() => {
|
||||
this.client.logger.addOnMessageListener(() => {
|
||||
this.updateView();
|
||||
});
|
||||
|
||||
settings.addOnSettingsChangeHandlers((newSettings, oldSettings) => {
|
||||
if (newSettings.minimumLogLevel !== oldSettings.minimumLogLevel) {
|
||||
this.updateView();
|
||||
this.client.settings.addOnSettingsChangeHandlers(
|
||||
(newSettings, oldSettings) => {
|
||||
if (
|
||||
newSettings.minimumLogLevel !== oldSettings.minimumLogLevel
|
||||
) {
|
||||
this.updateView();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
private static formatTimestamp(timestamp: Date): string {
|
||||
|
|
@ -78,8 +81,8 @@ export class LogsView extends ItemView {
|
|||
}
|
||||
);
|
||||
|
||||
const logs = Logger.getInstance().getMessages(
|
||||
this.settings.getSettings().minimumLogLevel
|
||||
const logs = this.client.logger.getMessages(
|
||||
this.client.settings.getSettings().minimumLogLevel
|
||||
);
|
||||
|
||||
if (logs.length === 0) {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,19 @@ import { RelativePath } from "src/persistence/database";
|
|||
import { isBinary, isFileTypeMergable, mergeText } from "sync_lib";
|
||||
|
||||
export class FileOperations {
|
||||
public constructor(private readonly fs: FileSystemOperations) {}
|
||||
public constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly fs: FileSystemOperations
|
||||
) {}
|
||||
|
||||
public async listAllFiles(): Promise<RelativePath[]> {
|
||||
const files = await this.fs.listAllFiles();
|
||||
Logger.getInstance().debug(`Listing all files, found ${files.length}`);
|
||||
this.logger.debug(`Listing all files, found ${files.length}`);
|
||||
return files;
|
||||
}
|
||||
|
||||
public async read(path: RelativePath): Promise<Uint8Array> {
|
||||
Logger.getInstance().debug(`Reading file: ${path}`);
|
||||
this.logger.debug(`Reading file: ${path}`);
|
||||
const content = await this.fs.read(path);
|
||||
|
||||
if (isBinary(content)) {
|
||||
|
|
@ -30,17 +33,17 @@ export class FileOperations {
|
|||
}
|
||||
|
||||
public async getFileSize(path: RelativePath): Promise<number> {
|
||||
Logger.getInstance().debug(`Getting file size: ${path}`);
|
||||
this.logger.debug(`Getting file size: ${path}`);
|
||||
return this.fs.getFileSize(path);
|
||||
}
|
||||
|
||||
public async getModificationTime(path: RelativePath): Promise<Date> {
|
||||
Logger.getInstance().debug(`Getting modification time: ${path}`);
|
||||
this.logger.debug(`Getting modification time: ${path}`);
|
||||
return this.fs.getModificationTime(path);
|
||||
}
|
||||
|
||||
public async exists(path: RelativePath): Promise<boolean> {
|
||||
Logger.getInstance().debug(`Checking existance of ${path}`);
|
||||
this.logger.debug(`Checking existance of ${path}`);
|
||||
return this.fs.exists(path);
|
||||
}
|
||||
|
||||
|
|
@ -50,9 +53,9 @@ export class FileOperations {
|
|||
path: RelativePath,
|
||||
newContent: Uint8Array
|
||||
): Promise<void> {
|
||||
Logger.getInstance().debug(`Creating file: ${path}`);
|
||||
this.logger.debug(`Creating file: ${path}`);
|
||||
if (await this.fs.exists(path)) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Didn't expect ${path} to exist, when trying to create it, merging instead`
|
||||
);
|
||||
await this.write(path, new Uint8Array(0), newContent);
|
||||
|
|
@ -71,9 +74,9 @@ export class FileOperations {
|
|||
expectedContent: Uint8Array,
|
||||
newContent: Uint8Array
|
||||
): Promise<Uint8Array> {
|
||||
Logger.getInstance().debug(`Writing file: ${path}`);
|
||||
this.logger.debug(`Writing file: ${path}`);
|
||||
if (!(await this.fs.exists(path))) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`The caller assumed ${path} exists, but it no longer, so we wont recreate it`
|
||||
);
|
||||
return new Uint8Array(0);
|
||||
|
|
@ -84,7 +87,7 @@ export class FileOperations {
|
|||
isBinary(expectedContent) ||
|
||||
isBinary(newContent)
|
||||
) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`The expected content is not mergable, so we won't perform a 3-way merge, just overwrite it`
|
||||
);
|
||||
await this.fs.write(path, newContent);
|
||||
|
|
@ -99,14 +102,14 @@ export class FileOperations {
|
|||
(currentText) => {
|
||||
currentText = currentText.replace(/\r\n/g, "\n");
|
||||
if (currentText !== expectedText) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Performing a 3-way merge for ${path} with the expected content`
|
||||
);
|
||||
|
||||
return mergeText(expectedText, currentText, newText);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`The current content of ${path} is the same as the expected content, so we will just write the new content`
|
||||
);
|
||||
|
||||
|
|
@ -117,7 +120,7 @@ export class FileOperations {
|
|||
}
|
||||
|
||||
public async remove(path: RelativePath): Promise<void> {
|
||||
Logger.getInstance().debug(`Removing file: ${path}`);
|
||||
this.logger.debug(`Removing file: ${path}`);
|
||||
return this.fs.delete(path);
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +128,7 @@ export class FileOperations {
|
|||
oldPath: RelativePath,
|
||||
newPath: RelativePath
|
||||
): Promise<void> {
|
||||
Logger.getInstance().debug(`Moving file: ${oldPath} -> ${newPath}`);
|
||||
this.logger.debug(`Moving file: ${oldPath} -> ${newPath}`);
|
||||
|
||||
if (oldPath === newPath) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export class Database {
|
|||
private lastSeenUpdateId: VaultUpdateId | undefined;
|
||||
|
||||
public constructor(
|
||||
private readonly logger: Logger,
|
||||
initialState: Partial<StoredDatabase> | undefined,
|
||||
private readonly saveData: (data: unknown) => Promise<void>
|
||||
) {
|
||||
|
|
@ -32,10 +33,10 @@ export class Database {
|
|||
this.documents.set(relativePath, metadata as DocumentMetadata);
|
||||
}
|
||||
}
|
||||
Logger.getInstance().debug(`Loaded ${this.documents.size} documents`);
|
||||
this.logger.debug(`Loaded ${this.documents.size} documents`);
|
||||
|
||||
this.lastSeenUpdateId = initialState.lastSeenUpdateId;
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Loaded last seen update id: ${this.lastSeenUpdateId}`
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export class Settings {
|
|||
) => void)[] = [];
|
||||
|
||||
public constructor(
|
||||
private readonly logger: Logger,
|
||||
initialState: Partial<SyncSettings> | undefined,
|
||||
private readonly saveData: (data: unknown) => Promise<void>
|
||||
) {
|
||||
|
|
@ -41,7 +42,7 @@ export class Settings {
|
|||
...(initialState ?? {})
|
||||
};
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Loaded settings: ${JSON.stringify(this.settings, null, 2)}`
|
||||
);
|
||||
}
|
||||
|
|
@ -70,7 +71,7 @@ export class Settings {
|
|||
value: SyncSettings[T]
|
||||
): Promise<void> {
|
||||
const newSettings = { ...this.settings, [key]: value };
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Setting ${key} to ${value}, new settings: ${JSON.stringify(
|
||||
newSettings,
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type {
|
|||
VaultUpdateId
|
||||
} from "../persistence/database";
|
||||
import { Logger } from "src/tracing/logger";
|
||||
import { retriedFetch } from "src/utils/retried-fetch";
|
||||
import { retriedFetchFactory } from "src/utils/retried-fetch";
|
||||
import type { SyncSettings } from "dist/types";
|
||||
import type { Settings } from "src/persistence/settings";
|
||||
|
||||
|
|
@ -19,7 +19,10 @@ export class SyncService {
|
|||
private client!: Client<paths>;
|
||||
private clientWithoutRetries!: Client<paths>;
|
||||
|
||||
public constructor(private readonly settings: Settings) {
|
||||
public constructor(
|
||||
private readonly settings: Settings,
|
||||
private readonly logger: Logger
|
||||
) {
|
||||
this.createClient(settings.getSettings());
|
||||
|
||||
settings.addOnSettingsChangeHandlers(this.createClient.bind(this));
|
||||
|
|
@ -73,7 +76,7 @@ export class SyncService {
|
|||
);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Created document ${JSON.stringify(response.data)} with id ${
|
||||
response.data.documentId
|
||||
}`
|
||||
|
|
@ -124,7 +127,7 @@ export class SyncService {
|
|||
);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Updated document ${JSON.stringify(response.data)} with id ${
|
||||
response.data.documentId
|
||||
}`
|
||||
|
|
@ -165,7 +168,7 @@ export class SyncService {
|
|||
throw new Error(`Failed to delete document`);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Deleted document ${relativePath} with id ${documentId}`
|
||||
);
|
||||
|
||||
|
|
@ -198,7 +201,7 @@ export class SyncService {
|
|||
);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Get document ${response.data.relativePath} with id ${response.data.documentId}`
|
||||
);
|
||||
|
||||
|
|
@ -229,7 +232,7 @@ export class SyncService {
|
|||
);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Got ${response.data.latestDocuments.length} document metadata`
|
||||
);
|
||||
|
||||
|
|
@ -267,9 +270,7 @@ export class SyncService {
|
|||
}
|
||||
});
|
||||
|
||||
Logger.getInstance().debug(
|
||||
`Ping response: ${JSON.stringify(response.data)}`
|
||||
);
|
||||
this.logger.debug(`Ping response: ${JSON.stringify(response.data)}`);
|
||||
|
||||
if (!response.data) {
|
||||
throw new Error(
|
||||
|
|
@ -283,7 +284,7 @@ export class SyncService {
|
|||
private createClient(settings: SyncSettings): void {
|
||||
this.client = createClient<paths>({
|
||||
baseUrl: settings.remoteUri,
|
||||
fetch: retriedFetch
|
||||
fetch: retriedFetchFactory(this.logger)
|
||||
});
|
||||
|
||||
this.clientWithoutRetries = createClient<paths>({
|
||||
|
|
|
|||
|
|
@ -12,14 +12,15 @@ import { FileSystemOperations } from "./file-operations/filesystem-operations";
|
|||
import { FileOperations } from "./file-operations/file-operations";
|
||||
|
||||
export class SyncClient {
|
||||
private remoteListenerIntervalId: number | null = null;
|
||||
private remoteListenerIntervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
private constructor(
|
||||
private readonly _history: SyncHistory,
|
||||
private readonly _settings: Settings,
|
||||
private readonly _database: Database,
|
||||
private readonly _syncer: Syncer,
|
||||
private readonly _syncService: SyncService
|
||||
private readonly _syncService: SyncService,
|
||||
private readonly _logger: Logger
|
||||
) {}
|
||||
|
||||
public get history(): SyncHistory {
|
||||
|
|
@ -34,12 +35,17 @@ export class SyncClient {
|
|||
return this._syncer;
|
||||
}
|
||||
|
||||
public get logger(): Logger {
|
||||
return this._logger;
|
||||
}
|
||||
|
||||
public static async create(
|
||||
fs: FileSystemOperations,
|
||||
persistence: PersistenceProvider
|
||||
): Promise<SyncClient> {
|
||||
const history = new SyncHistory();
|
||||
Logger.getInstance().info("Starting SyncClient");
|
||||
const logger = new Logger();
|
||||
const history = new SyncHistory(logger);
|
||||
logger.info("Starting SyncClient");
|
||||
|
||||
await init(
|
||||
// eslint-disable-next-line
|
||||
|
|
@ -54,6 +60,7 @@ export class SyncClient {
|
|||
database: undefined
|
||||
};
|
||||
const database = new Database(
|
||||
logger,
|
||||
state.database,
|
||||
async (data: unknown): Promise<void> => {
|
||||
state = { ...state, database: data };
|
||||
|
|
@ -62,6 +69,7 @@ export class SyncClient {
|
|||
);
|
||||
|
||||
const settings = new Settings(
|
||||
logger,
|
||||
state.settings,
|
||||
async (data: unknown): Promise<void> => {
|
||||
state = { ...state, settings: data };
|
||||
|
|
@ -69,13 +77,14 @@ export class SyncClient {
|
|||
}
|
||||
);
|
||||
|
||||
const syncService = new SyncService(settings);
|
||||
const syncService = new SyncService(settings, logger);
|
||||
|
||||
const syncer = new Syncer(
|
||||
logger,
|
||||
database,
|
||||
settings,
|
||||
syncService,
|
||||
new FileOperations(fs),
|
||||
new FileOperations(logger, fs),
|
||||
history
|
||||
);
|
||||
|
||||
|
|
@ -84,7 +93,8 @@ export class SyncClient {
|
|||
settings,
|
||||
database,
|
||||
syncer,
|
||||
syncService
|
||||
syncService,
|
||||
logger
|
||||
);
|
||||
|
||||
void syncer.scheduleSyncForOfflineChanges();
|
||||
|
|
@ -102,14 +112,14 @@ export class SyncClient {
|
|||
syncer
|
||||
.scheduleSyncForOfflineChanges()
|
||||
.catch((_error: unknown) => {
|
||||
Logger.getInstance().error(
|
||||
logger.error(
|
||||
"Failed to schedule sync for offline changes"
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Logger.getInstance().info("SyncClient loaded");
|
||||
logger.info("SyncClient loaded");
|
||||
|
||||
return client;
|
||||
}
|
||||
|
|
@ -125,21 +135,21 @@ export class SyncClient {
|
|||
public async reset(): Promise<void> {
|
||||
await this._syncer.reset();
|
||||
this._history.reset();
|
||||
Logger.getInstance().reset();
|
||||
this.logger.reset();
|
||||
}
|
||||
|
||||
public onunload(): void {
|
||||
if (this.remoteListenerIntervalId !== null) {
|
||||
window.clearInterval(this.remoteListenerIntervalId);
|
||||
clearInterval(this.remoteListenerIntervalId);
|
||||
}
|
||||
}
|
||||
|
||||
private registerRemoteEventListener(intervalMs: number): void {
|
||||
if (this.remoteListenerIntervalId !== null) {
|
||||
window.clearInterval(this.remoteListenerIntervalId);
|
||||
clearInterval(this.remoteListenerIntervalId);
|
||||
}
|
||||
|
||||
this.remoteListenerIntervalId = window.setInterval(
|
||||
this.remoteListenerIntervalId = setInterval(
|
||||
() => void this._syncer.applyRemoteChangesLocally(),
|
||||
intervalMs
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export class Syncer {
|
|||
undefined;
|
||||
|
||||
public constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly database: Database,
|
||||
private readonly settings: Settings,
|
||||
private readonly syncService: SyncService,
|
||||
|
|
@ -91,16 +92,14 @@ export class Syncer {
|
|||
|
||||
public async scheduleSyncForOfflineChanges(): Promise<void> {
|
||||
if (!this.settings.getSettings().isSyncEnabled) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Syncing is disabled, not uploading local changes`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.runningScheduleSyncForOfflineChanges != null) {
|
||||
Logger.getInstance().debug(
|
||||
"Uploading local changes is already in progress"
|
||||
);
|
||||
this.logger.debug("Uploading local changes is already in progress");
|
||||
return this.runningScheduleSyncForOfflineChanges;
|
||||
}
|
||||
|
||||
|
|
@ -108,11 +107,9 @@ export class Syncer {
|
|||
this.runningScheduleSyncForOfflineChanges =
|
||||
this.internalScheduleSyncForOfflineChanges();
|
||||
await this.runningScheduleSyncForOfflineChanges;
|
||||
Logger.getInstance().info(
|
||||
`All local changes have been applied remotely`
|
||||
);
|
||||
this.logger.info(`All local changes have been applied remotely`);
|
||||
} catch (e) {
|
||||
Logger.getInstance().error(
|
||||
this.logger.error(
|
||||
`Not all local changes have been applied remotely: ${e}`
|
||||
);
|
||||
throw e;
|
||||
|
|
@ -150,7 +147,7 @@ export class Syncer {
|
|||
(item) => item != originalFile
|
||||
);
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document ${relativePath} was not found under its current path in the database but was found under a different path ${originalFile[0]}, scheduling sync to move it`
|
||||
);
|
||||
return this.internalSyncLocallyUpdatedFile({
|
||||
|
|
@ -167,7 +164,7 @@ export class Syncer {
|
|||
});
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document ${relativePath} not found in database, scheduling sync to create it`
|
||||
);
|
||||
return this.internalSyncLocallyCreatedFile(
|
||||
|
|
@ -178,7 +175,7 @@ export class Syncer {
|
|||
);
|
||||
}
|
||||
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document ${relativePath} has been updated locally, scheduling sync to update it`
|
||||
);
|
||||
return this.internalSyncLocallyUpdatedFile({
|
||||
|
|
@ -194,12 +191,12 @@ export class Syncer {
|
|||
|
||||
await Promise.all(
|
||||
locallyDeletedFiles.map(async ([relativePath, _]) => {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document ${relativePath} has been deleted locally, scheduling sync to delete it`
|
||||
);
|
||||
|
||||
if (await this.operations.exists(relativePath)) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document ${relativePath} actually exists locally, skipping`
|
||||
);
|
||||
return Promise.resolve();
|
||||
|
|
@ -212,14 +209,14 @@ export class Syncer {
|
|||
|
||||
public async applyRemoteChangesLocally(): Promise<void> {
|
||||
if (!this.settings.getSettings().isSyncEnabled) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Syncing is disabled, not fetching remote changes`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.runningApplyRemoteChangesLocally != null) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
"Applying remote changes locally is already in progress"
|
||||
);
|
||||
return this.runningApplyRemoteChangesLocally;
|
||||
|
|
@ -229,13 +226,9 @@ export class Syncer {
|
|||
this.runningApplyRemoteChangesLocally =
|
||||
this.internalApplyRemoteChangesLocally();
|
||||
await this.runningApplyRemoteChangesLocally;
|
||||
Logger.getInstance().info(
|
||||
"All remote changes have been applied locally"
|
||||
);
|
||||
this.logger.info("All remote changes have been applied locally");
|
||||
} catch (e) {
|
||||
Logger.getInstance().error(
|
||||
`Failed to apply remote changes locally: ${e}`
|
||||
);
|
||||
this.logger.error(`Failed to apply remote changes locally: ${e}`);
|
||||
throw e;
|
||||
} finally {
|
||||
this.runningApplyRemoteChangesLocally = undefined;
|
||||
|
|
@ -248,11 +241,11 @@ export class Syncer {
|
|||
);
|
||||
|
||||
if (remote.latestDocuments.length === 0) {
|
||||
Logger.getInstance().debug("No remote changes to apply");
|
||||
this.logger.debug("No remote changes to apply");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.getInstance().info("Applying remote changes locally");
|
||||
this.logger.info("Applying remote changes locally");
|
||||
|
||||
await Promise.all(
|
||||
remote.latestDocuments.map(async (remoteDocument) =>
|
||||
|
|
@ -317,7 +310,7 @@ export class Syncer {
|
|||
|
||||
const localMetadata = this.database.getDocument(relativePath);
|
||||
if (localMetadata) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document metadata already exists for ${relativePath}, it must have been downloaded from the server`
|
||||
);
|
||||
|
||||
|
|
@ -631,7 +624,7 @@ export class Syncer {
|
|||
|
||||
const [relativePath, metadata] = localMetadata;
|
||||
if (metadata.parentVersionId === remoteVersion.vaultUpdateId) {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`Document ${relativePath} is already up to date`
|
||||
);
|
||||
return;
|
||||
|
|
@ -658,7 +651,7 @@ export class Syncer {
|
|||
const currentHash = hash(currentContent);
|
||||
|
||||
if (currentHash !== metadata.hash) {
|
||||
Logger.getInstance().info(
|
||||
this.logger.info(
|
||||
`Document ${relativePath} has been updated both remotely and locally, letting the local file update event handle it`
|
||||
);
|
||||
return;
|
||||
|
|
@ -716,18 +709,18 @@ export class Syncer {
|
|||
fn: () => Promise<void>
|
||||
): Promise<void> {
|
||||
if (!this.settings.getSettings().isSyncEnabled) {
|
||||
Logger.getInstance().info(
|
||||
this.logger.info(
|
||||
`Syncing is disabled, not syncing ${relativePath}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!this.operations.isFileEligibleForSync(relativePath)) {
|
||||
Logger.getInstance().info(
|
||||
this.logger.info(
|
||||
`File ${relativePath} is not eligible for syncing`
|
||||
);
|
||||
return;
|
||||
}
|
||||
Logger.getInstance().debug(`Syncing ${relativePath}`);
|
||||
this.logger.debug(`Syncing ${relativePath}`);
|
||||
|
||||
await waitForDocumentLock(relativePath);
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ export class SyncHistory {
|
|||
error: 0
|
||||
};
|
||||
|
||||
public constructor(private logger: Logger) {}
|
||||
|
||||
public getEntries(): HistoryEntry[] {
|
||||
return [...this.entries];
|
||||
}
|
||||
|
|
@ -78,16 +80,16 @@ export class SyncHistory {
|
|||
|
||||
if (entry.status === SyncStatus.SUCCESS) {
|
||||
this.status.success++;
|
||||
Logger.getInstance().info(
|
||||
this.logger.info(
|
||||
`History entry: ${entry.relativePath} - ${entry.message}`
|
||||
);
|
||||
} else if (entry.status === SyncStatus.ERROR) {
|
||||
this.status.error++;
|
||||
Logger.getInstance().error(
|
||||
this.logger.error(
|
||||
`Error syncing file: ${entry.relativePath} - ${entry.message}`
|
||||
);
|
||||
} else {
|
||||
Logger.getInstance().debug(
|
||||
this.logger.debug(
|
||||
`No-op syncing file: ${entry.relativePath} - ${entry.message}`
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,23 +14,25 @@ function getUrlFromInput(input: RequestInfo | URL): string {
|
|||
return input.url;
|
||||
}
|
||||
|
||||
export async function retriedFetch(
|
||||
input: RequestInfo | URL,
|
||||
init: RequestInitRetryParams<typeof fetch> = {}
|
||||
): Promise<Response> {
|
||||
return fetchWithRetry(input, {
|
||||
retryOn: function (attempt, error, response) {
|
||||
if (error !== null || !response || response.status >= 500) {
|
||||
Logger.getInstance().warn(
|
||||
`Retrying fetch for ${getUrlFromInput(input)}, attempt ${attempt}`
|
||||
);
|
||||
export function retriedFetchFactory(logger: Logger) {
|
||||
return (
|
||||
input: RequestInfo | URL,
|
||||
init: RequestInitRetryParams<typeof fetch> = {}
|
||||
): Promise<Response> => {
|
||||
return fetchWithRetry(input, {
|
||||
retryOn: function (attempt, error, response) {
|
||||
if (error !== null || !response || response.status >= 500) {
|
||||
logger.warn(
|
||||
`Retrying fetch for ${getUrlFromInput(input)}, attempt ${attempt}`
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
retries: 6,
|
||||
retryDelay: (attempt) => Math.pow(1.5, attempt) * 500,
|
||||
...init
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
retries: 6,
|
||||
retryDelay: (attempt) => Math.pow(1.5, attempt) * 500,
|
||||
...init
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue