Simplify API
This commit is contained in:
parent
010b3d61e9
commit
eb1ad9921a
5 changed files with 203 additions and 53 deletions
|
|
@ -1,19 +1,6 @@
|
||||||
export { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Database,
|
|
||||||
type RelativePath,
|
|
||||||
type DocumentId,
|
|
||||||
type VaultUpdateId,
|
|
||||||
type DocumentMetadata
|
|
||||||
} from "./persistence/database";
|
|
||||||
|
|
||||||
export { Settings, type SyncSettings } from "./persistence/settings";
|
export { Settings, type SyncSettings } from "./persistence/settings";
|
||||||
|
|
||||||
export {
|
export { type CheckConnectionResult } from "./services/sync-service";
|
||||||
SyncService,
|
|
||||||
type CheckConnectionResult
|
|
||||||
} from "./services/sync-service";
|
|
||||||
|
|
||||||
export { Syncer } from "./sync-operations/syncer";
|
export { Syncer } from "./sync-operations/syncer";
|
||||||
|
|
||||||
|
|
@ -25,26 +12,17 @@ export {
|
||||||
type HistoryStats,
|
type HistoryStats,
|
||||||
type HistoryEntry
|
type HistoryEntry
|
||||||
} from "./tracing/sync-history";
|
} from "./tracing/sync-history";
|
||||||
|
|
||||||
export { Logger, LogLevel } from "./tracing/logger";
|
export { Logger, LogLevel } from "./tracing/logger";
|
||||||
|
|
||||||
|
export { SyncClient } from "./sync-client";
|
||||||
export { type FileOperations } from "./file-operations";
|
export { type FileOperations } from "./file-operations";
|
||||||
|
export { type RelativePath } from "./persistence/database";
|
||||||
import init from "sync_lib";
|
export type { PersistenceProvider } from "./persistence/persistence";
|
||||||
import wasmBin from "sync_lib/sync_lib_bg.wasm";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isFileTypeMergable,
|
isFileTypeMergable,
|
||||||
mergeText,
|
mergeText,
|
||||||
bytesToBase64,
|
bytesToBase64,
|
||||||
base64ToBytes,
|
base64ToBytes,
|
||||||
merge,
|
merge
|
||||||
isBinary
|
|
||||||
} from "sync_lib";
|
} from "sync_lib";
|
||||||
|
|
||||||
export const initialize = async (): Promise<void> => {
|
|
||||||
await init(
|
|
||||||
// eslint-disable-next-line
|
|
||||||
(wasmBin as any).default // it is loaded as a base64 string by webpack
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
4
frontend/sync-client/src/persistence/persistence.ts
Normal file
4
frontend/sync-client/src/persistence/persistence.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface PersistenceProvider {
|
||||||
|
load: () => Promise<unknown>;
|
||||||
|
save: (data: unknown) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Client } from "openapi-fetch";
|
import type { Client } from "openapi-fetch";
|
||||||
import createClient from "openapi-fetch";
|
import createClient from "openapi-fetch";
|
||||||
import type { components, paths } from "./types"; // Generated by openapi-typescript
|
import type { components, paths } from "./types"; // generated by openapi-typescript
|
||||||
import type {
|
import type {
|
||||||
DocumentId,
|
DocumentId,
|
||||||
RelativePath,
|
RelativePath,
|
||||||
|
|
@ -16,8 +16,8 @@ export interface CheckConnectionResult {
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
export class SyncService {
|
export class SyncService {
|
||||||
private client: Client<paths>;
|
private client!: Client<paths>;
|
||||||
private clientWithoutRetries: Client<paths>;
|
private clientWithoutRetries!: Client<paths>;
|
||||||
|
|
||||||
public constructor(private readonly settings: Settings) {
|
public constructor(private readonly settings: Settings) {
|
||||||
this.createClient(settings.getSettings());
|
this.createClient(settings.getSettings());
|
||||||
|
|
@ -39,28 +39,6 @@ export class SyncService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ping(): Promise<components["schemas"]["PingResponse"]> {
|
|
||||||
const response = await this.clientWithoutRetries.GET("/ping", {
|
|
||||||
params: {
|
|
||||||
header: {
|
|
||||||
authorization: `Bearer ${this.settings.getSettings().token}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Logger.getInstance().debug(
|
|
||||||
`Ping response: ${JSON.stringify(response.data)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.data) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to ping server: ${SyncService.formatError(response.error)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async create({
|
public async create({
|
||||||
relativePath,
|
relativePath,
|
||||||
contentBytes,
|
contentBytes,
|
||||||
|
|
@ -282,6 +260,28 @@ export class SyncService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async ping(): Promise<components["schemas"]["PingResponse"]> {
|
||||||
|
const response = await this.clientWithoutRetries.GET("/ping", {
|
||||||
|
params: {
|
||||||
|
header: {
|
||||||
|
authorization: `Bearer ${this.settings.getSettings().token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Logger.getInstance().debug(
|
||||||
|
`Ping response: ${JSON.stringify(response.data)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.data) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to ping server: ${SyncService.formatError(response.error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
private createClient(settings: SyncSettings): void {
|
private createClient(settings: SyncSettings): void {
|
||||||
this.client = createClient<paths>({
|
this.client = createClient<paths>({
|
||||||
baseUrl: settings.remoteUri,
|
baseUrl: settings.remoteUri,
|
||||||
|
|
|
||||||
168
frontend/sync-client/src/sync-client.ts
Normal file
168
frontend/sync-client/src/sync-client.ts
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
import init from "sync_lib";
|
||||||
|
import wasmBin from "sync_lib/sync_lib_bg.wasm";
|
||||||
|
import type { PersistenceProvider } from "./persistence/persistence";
|
||||||
|
import { SyncHistory } from "./tracing/sync-history";
|
||||||
|
import type { FileOperations } from "./file-operations";
|
||||||
|
import { Logger } from "./tracing/logger";
|
||||||
|
import { Database } from "./persistence/database";
|
||||||
|
import { Settings } from "./persistence/settings";
|
||||||
|
import type { CheckConnectionResult } from "./services/sync-service";
|
||||||
|
import { SyncService } from "./services/sync-service";
|
||||||
|
import { Syncer } from "./sync-operations/syncer";
|
||||||
|
import { applyRemoteChangesLocally } from "./sync-operations/apply-remote-changes-locally";
|
||||||
|
|
||||||
|
export class SyncClient {
|
||||||
|
private remoteListenerIntervalId: number | null = null;
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
private readonly _history: SyncHistory,
|
||||||
|
private readonly _settings: Settings,
|
||||||
|
private readonly _database: Database,
|
||||||
|
private readonly _syncer: Syncer,
|
||||||
|
private readonly _syncService: SyncService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public get history(): SyncHistory {
|
||||||
|
return this._history;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get settings(): Settings {
|
||||||
|
return this._settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get syncer(): Syncer {
|
||||||
|
return this._syncer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async create(
|
||||||
|
operations: FileOperations,
|
||||||
|
persistence: PersistenceProvider
|
||||||
|
): Promise<SyncClient> {
|
||||||
|
const history = new SyncHistory();
|
||||||
|
Logger.getInstance().info("Starting SyncClient");
|
||||||
|
|
||||||
|
await init(
|
||||||
|
// eslint-disable-next-line
|
||||||
|
(wasmBin as any).default // it is loaded as a base64 string by webpack
|
||||||
|
);
|
||||||
|
|
||||||
|
let state: Partial<{
|
||||||
|
settings: any;
|
||||||
|
database: any;
|
||||||
|
}> = (await persistence.load()) ?? {
|
||||||
|
settings: undefined,
|
||||||
|
database: undefined
|
||||||
|
};
|
||||||
|
const database = new Database(
|
||||||
|
state.database,
|
||||||
|
async (data: unknown): Promise<void> => {
|
||||||
|
state = { ...state, database: data };
|
||||||
|
return persistence.save(state);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const settings = new Settings(
|
||||||
|
state.settings,
|
||||||
|
async (data: unknown): Promise<void> => {
|
||||||
|
state = { ...state, settings: data };
|
||||||
|
return persistence.save(state);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const syncService = new SyncService(settings);
|
||||||
|
|
||||||
|
const syncer = new Syncer(
|
||||||
|
database,
|
||||||
|
settings,
|
||||||
|
syncService,
|
||||||
|
operations,
|
||||||
|
history
|
||||||
|
);
|
||||||
|
|
||||||
|
const client = new SyncClient(
|
||||||
|
history,
|
||||||
|
settings,
|
||||||
|
database,
|
||||||
|
syncer,
|
||||||
|
syncService
|
||||||
|
);
|
||||||
|
|
||||||
|
void syncer.scheduleSyncForOfflineChanges();
|
||||||
|
|
||||||
|
client.registerRemoteEventListener(
|
||||||
|
settings,
|
||||||
|
database,
|
||||||
|
syncService,
|
||||||
|
syncer,
|
||||||
|
settings.getSettings().fetchChangesUpdateIntervalMs
|
||||||
|
);
|
||||||
|
|
||||||
|
settings.addOnSettingsChangeHandlers((newSettings, oldSettings) => {
|
||||||
|
client.registerRemoteEventListener(
|
||||||
|
settings,
|
||||||
|
database,
|
||||||
|
syncService,
|
||||||
|
syncer,
|
||||||
|
newSettings.fetchChangesUpdateIntervalMs
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!oldSettings.isSyncEnabled && newSettings.isSyncEnabled) {
|
||||||
|
syncer
|
||||||
|
.scheduleSyncForOfflineChanges()
|
||||||
|
.catch((_error: unknown) => {
|
||||||
|
Logger.getInstance().error(
|
||||||
|
"Failed to schedule sync for offline changes"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Logger.getInstance().info("SyncClient loaded");
|
||||||
|
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get documentCount(): number {
|
||||||
|
return this._database.getDocuments().size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkConnection(): Promise<CheckConnectionResult> {
|
||||||
|
return this._syncService.checkConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async reset(): Promise<void> {
|
||||||
|
await this._syncer.reset();
|
||||||
|
this._history.reset();
|
||||||
|
Logger.getInstance().reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public onunload(): void {
|
||||||
|
if (this.remoteListenerIntervalId !== null) {
|
||||||
|
window.clearInterval(this.remoteListenerIntervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerRemoteEventListener(
|
||||||
|
settings: Settings,
|
||||||
|
database: Database,
|
||||||
|
syncService: SyncService,
|
||||||
|
syncer: Syncer,
|
||||||
|
intervalMs: number
|
||||||
|
): void {
|
||||||
|
if (this.remoteListenerIntervalId !== null) {
|
||||||
|
window.clearInterval(this.remoteListenerIntervalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.remoteListenerIntervalId = window.setInterval(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
async () =>
|
||||||
|
applyRemoteChangesLocally({
|
||||||
|
settings,
|
||||||
|
database,
|
||||||
|
syncService,
|
||||||
|
syncer
|
||||||
|
}),
|
||||||
|
intervalMs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { RelativePath } from "src/persistence/database";
|
import type { RelativePath } from "src/persistence/database";
|
||||||
import { Logger } from "./logger";
|
import { Logger } from "./logger";
|
||||||
|
|
||||||
export interface CommonHistoryEntry {
|
export interface CommonHistoryEntry {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue