diff --git a/frontend/sync-client/src/consts.ts b/frontend/sync-client/src/consts.ts index dbab3de0..8fa50f47 100644 --- a/frontend/sync-client/src/consts.ts +++ b/frontend/sync-client/src/consts.ts @@ -2,3 +2,4 @@ export const TIMEOUT_FOR_MERGING_HISTORY_ENTRIES_IN_SECONDS = 60; export const DIFF_CACHE_SIZE_MB = 2; export const MAX_LOG_MESSAGE_COUNT = 100000; export const MAX_HISTORY_ENTRY_COUNT = 5000; +export const SUPPORTED_API_VERSION = 1; diff --git a/frontend/sync-client/src/index.ts b/frontend/sync-client/src/index.ts index 81b7f7ff..f09d339c 100644 --- a/frontend/sync-client/src/index.ts +++ b/frontend/sync-client/src/index.ts @@ -25,6 +25,8 @@ export type { PersistenceProvider } from "./persistence/persistence"; export type { CursorSpan } from "./services/types/CursorSpan"; export type { ClientCursors } from "./services/types/ClientCursors"; export type { NetworkConnectionStatus } from "./types/network-connection-status"; +export type { ServerVersionMismatchError } from "./services/server-version-mismatch-error"; +export type { AuthenticationError } from "./services/authentication-error"; export type { MaybeOutdatedClientCursors } from "./types/maybe-outdated-client-cursors"; export { DocumentSyncStatus } from "./types/document-sync-status"; export { SyncClient } from "./sync-client"; diff --git a/frontend/sync-client/src/services/authentication-error.ts b/frontend/sync-client/src/services/authentication-error.ts new file mode 100644 index 00000000..9daa1937 --- /dev/null +++ b/frontend/sync-client/src/services/authentication-error.ts @@ -0,0 +1,6 @@ +export class AuthenticationError extends Error { + public constructor(message: string) { + super(message); + this.name = "AuthenticationError"; + } +} diff --git a/frontend/sync-client/src/services/server-config.ts b/frontend/sync-client/src/services/server-config.ts index b5ba5b15..b3107d10 100644 --- a/frontend/sync-client/src/services/server-config.ts +++ b/frontend/sync-client/src/services/server-config.ts @@ -1,9 +1,13 @@ -import { createPromise } from "../utils/create-promise"; +import { SUPPORTED_API_VERSION } from "../consts"; +import { AuthenticationError } from "./authentication-error"; +import { ServerVersionMismatchError } from "./server-version-mismatch-error"; import type { SyncService } from "./sync-service"; import type { PingResponse } from "./types/PingResponse"; export interface ServerConfigData { mergeableFileExtensions: string[]; + supportedApiVersion: number; + isAuthenticated: boolean; } export class ServerConfig { @@ -15,6 +19,22 @@ export class ServerConfig { public async initialize(): Promise { this.response = this.syncService.ping(); this.config = await this.response; + + if (this.config.supportedApiVersion !== SUPPORTED_API_VERSION) { + const shouldUpgradeClient = + this.config.supportedApiVersion > SUPPORTED_API_VERSION; + throw new ServerVersionMismatchError( + `Unsupported API version: ${this.config.supportedApiVersion}. Consider upgrading the ${ + shouldUpgradeClient ? "client" : "sync-server" + } to ensure compatibility.` + ); + } + + if (!this.config.isAuthenticated) { + throw new AuthenticationError( + "Failed to authenticate with the sync-server." + ); + } } public async checkConnection(forceUpdate = false): Promise<{ diff --git a/frontend/sync-client/src/services/server-version-mismatch-error.ts b/frontend/sync-client/src/services/server-version-mismatch-error.ts new file mode 100644 index 00000000..0f37fc6f --- /dev/null +++ b/frontend/sync-client/src/services/server-version-mismatch-error.ts @@ -0,0 +1,6 @@ +export class ServerVersionMismatchError extends Error { + public constructor(message: string) { + super(message); + this.name = "ServerVersionMismatchError"; + } +} diff --git a/frontend/sync-client/src/services/types/PingResponse.ts b/frontend/sync-client/src/services/types/PingResponse.ts index ea691a93..cc7370e7 100644 --- a/frontend/sync-client/src/services/types/PingResponse.ts +++ b/frontend/sync-client/src/services/types/PingResponse.ts @@ -17,4 +17,9 @@ export interface PingResponse { * List of file extensions that are allowed to be merged. */ mergeableFileExtensions: string[]; + /** + * API version ensuring backwards & forwards compatibility between the client + * and server. + */ + supportedApiVersion: number; } diff --git a/frontend/sync-client/src/sync-client.ts b/frontend/sync-client/src/sync-client.ts index d0af6bfe..06c839c9 100644 --- a/frontend/sync-client/src/sync-client.ts +++ b/frontend/sync-client/src/sync-client.ts @@ -220,8 +220,6 @@ export class SyncClient { } this.hasStarted = true; - await this.serverConfig.initialize(); - if ( !this.unloadTelemetry && this.settings.getSettings().enableTelemetry @@ -311,6 +309,7 @@ export class SyncClient { this.resetInMemoryState(); this.hasStartedOfflineSync = false; this.hasFinishedOfflineSync = false; + this.serverConfig.reset(); // restart syncing this.fetchController.finishReset(); @@ -457,6 +456,8 @@ export class SyncClient { private async startSyncing(): Promise { this.checkIfDestroyed(); + await this.serverConfig.initialize(); + if (!this.hasStartedOfflineSync) { this.hasStartedOfflineSync = true; await this.syncer.scheduleSyncForOfflineChanges(); diff --git a/sync-server/src/consts.rs b/sync-server/src/consts.rs index 881bd727..3c672520 100644 --- a/sync-server/src/consts.rs +++ b/sync-server/src/consts.rs @@ -16,3 +16,5 @@ pub const DEFAULT_LOG_DIRECTORY: &str = "logs"; pub const DEFAULT_LOG_ROTATION_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24); // 1 day pub const DEFAULT_MERGEABLE_FILE_EXTENSIONS: &[&str] = &["md", "txt"]; + +pub const SUPPORTED_API_VERSION: u32 = 1; diff --git a/sync-server/src/server/ping.rs b/sync-server/src/server/ping.rs index ec019a1d..82eefff7 100644 --- a/sync-server/src/server/ping.rs +++ b/sync-server/src/server/ping.rs @@ -11,6 +11,7 @@ use serde::Deserialize; use super::{auth::auth, responses::PingResponse}; use crate::{ app_state::{AppState, database::models::VaultId}, + consts::SUPPORTED_API_VERSION, errors::SyncServerError, utils::normalize::normalize, }; @@ -34,5 +35,6 @@ pub async fn ping( server_version: env!("CARGO_PKG_VERSION").to_owned(), is_authenticated, mergeable_file_extensions: state.config.server.mergeable_file_extensions.clone(), + supported_api_version: SUPPORTED_API_VERSION, })) } diff --git a/sync-server/src/server/responses.rs b/sync-server/src/server/responses.rs index 22918106..a8b3fcd7 100644 --- a/sync-server/src/server/responses.rs +++ b/sync-server/src/server/responses.rs @@ -19,6 +19,10 @@ pub struct PingResponse { /// List of file extensions that are allowed to be merged. pub mergeable_file_extensions: Vec, + + /// API version ensuring backwards & forwards compatibility between the client + /// and server. + pub supported_api_version: u32, } /// Response to a fetch latest documents request.