mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-19 22:11:23 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fec203a751 | ||
|
|
1a06837769 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.25.10",
|
"version": "0.25.11",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.10",
|
"version": "0.25.11",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.10",
|
"version": "0.25.11",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.808.0",
|
"@aws-sdk/client-s3": "^3.808.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.25.10",
|
"version": "0.25.11",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: e488bca9fc...f21001fcb2
@@ -17,6 +17,7 @@ import {
|
|||||||
type EntryLeaf,
|
type EntryLeaf,
|
||||||
type LoadedEntry,
|
type LoadedEntry,
|
||||||
type MetaEntry,
|
type MetaEntry,
|
||||||
|
type RemoteType,
|
||||||
} from "../../lib/src/common/types";
|
} from "../../lib/src/common/types";
|
||||||
import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
|
import { QueueProcessor } from "octagonal-wheels/concurrency/processor";
|
||||||
import {
|
import {
|
||||||
@@ -38,7 +39,8 @@ const KEY_REPLICATION_ON_EVENT = "replicationOnEvent";
|
|||||||
const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000;
|
const REPLICATION_ON_EVENT_FORECASTED_TIME = 5000;
|
||||||
|
|
||||||
export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||||
_replicatorType?: string;
|
_replicatorType?: RemoteType;
|
||||||
|
|
||||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||||
eventHub.onEvent(EVENT_FILE_SAVED, () => {
|
eventHub.onEvent(EVENT_FILE_SAVED, () => {
|
||||||
if (this.settings.syncOnSave && !this.core.$$isSuspended()) {
|
if (this.settings.syncOnSave && !this.core.$$isSuspended()) {
|
||||||
@@ -91,6 +93,10 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
|||||||
|
|
||||||
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
async $everyBeforeReplicate(showMessage: boolean): Promise<boolean> {
|
||||||
// Checking salt
|
// Checking salt
|
||||||
|
if (!this.core.managers.networkManager.isOnline) {
|
||||||
|
this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Showing message is false: that because be shown here. (And it is a fatal error, no way to hide it).
|
// Showing message is false: that because be shown here. (And it is a fatal error, no way to hide it).
|
||||||
if (!(await this.ensureReplicatorPBKDF2Salt(false))) {
|
if (!(await this.ensureReplicatorPBKDF2Salt(false))) {
|
||||||
Logger("Failed to initialise the encryption key, preventing replication.", LOG_LEVEL_NOTICE);
|
Logger("Failed to initialise the encryption key, preventing replication.", LOG_LEVEL_NOTICE);
|
||||||
@@ -182,6 +188,11 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
|||||||
Logger($msg("Replicator.Message.Pending"), LOG_LEVEL_NOTICE);
|
Logger($msg("Replicator.Message.Pending"), LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.core.managers.networkManager.isOnline) {
|
||||||
|
this._log("Network is offline", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!(await this.core.$everyBeforeReplicate(showMessage))) {
|
if (!(await this.core.$everyBeforeReplicate(showMessage))) {
|
||||||
Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
|
Logger($msg("Replicator.Message.SomeModuleFailed"), LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ export class ModuleReplicatorCouchDB extends AbstractModule implements ICoreModu
|
|||||||
if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) {
|
if (this.settings.remoteType != REMOTE_MINIO && this.settings.remoteType != REMOTE_P2P) {
|
||||||
// If LiveSync enabled, open replication
|
// If LiveSync enabled, open replication
|
||||||
if (this.settings.liveSync) {
|
if (this.settings.liveSync) {
|
||||||
fireAndForget(() => this.core.replicator.openReplication(this.settings, true, false, false));
|
fireAndForget(() => this.core.$$replicate(false));
|
||||||
}
|
}
|
||||||
// If sync on start enabled, open replication
|
// If sync on start enabled, open replication
|
||||||
if (!this.settings.liveSync && this.settings.syncOnStart) {
|
if (!this.settings.liveSync && this.settings.syncOnStart) {
|
||||||
// Possibly ok as if only share the result
|
// Possibly ok as if only share the result
|
||||||
fireAndForget(() => this.core.replicator.openReplication(this.settings, false, false, false));
|
fireAndForget(() => this.core.$$replicate(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import { $msg } from "src/lib/src/common/i18n.ts";
|
|||||||
|
|
||||||
export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule {
|
export class ModuleCheckRemoteSize extends AbstractModule implements ICoreModule {
|
||||||
async $allScanStat(): Promise<boolean> {
|
async $allScanStat(): Promise<boolean> {
|
||||||
|
if (this.core.managers.networkManager.isOnline === false) {
|
||||||
|
this._log("Network is offline, skipping remote size check.", LOG_LEVEL_INFO);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
this._log($msg("moduleCheckRemoteSize.logCheckingStorageSizes"), LOG_LEVEL_VERBOSE);
|
this._log($msg("moduleCheckRemoteSize.logCheckingStorageSizes"), LOG_LEVEL_VERBOSE);
|
||||||
if (this.settings.notifyThresholdOfRemoteStorageSize < 0) {
|
if (this.settings.notifyThresholdOfRemoteStorageSize < 0) {
|
||||||
const message = $msg("moduleCheckRemoteSize.msgSetDBCapacity");
|
const message = $msg("moduleCheckRemoteSize.msgSetDBCapacity");
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { type UXFileInfo } from "../../../lib/src/common/types.ts";
|
|||||||
function getFileLockKey(file: TFile | TFolder | string | UXFileInfo) {
|
function getFileLockKey(file: TFile | TFolder | string | UXFileInfo) {
|
||||||
return `fl:${typeof file == "string" ? file : file.path}`;
|
return `fl:${typeof file == "string" ? file : file.path}`;
|
||||||
}
|
}
|
||||||
function toArrayBuffer(arr: Uint8Array | ArrayBuffer | DataView): ArrayBufferLike {
|
function toArrayBuffer(arr: Uint8Array<ArrayBuffer> | ArrayBuffer | DataView<ArrayBuffer>): ArrayBuffer {
|
||||||
if (arr instanceof Uint8Array) {
|
if (arr instanceof Uint8Array) {
|
||||||
return arr.buffer;
|
return arr.buffer;
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,11 @@ export class SerializedFileAccess {
|
|||||||
return await processReadFile(file, () => this.app.vault.adapter.readBinary(path));
|
return await processReadFile(file, () => this.app.vault.adapter.readBinary(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
async adapterWrite(file: TFile | string, data: string | ArrayBuffer | Uint8Array, options?: DataWriteOptions) {
|
async adapterWrite(
|
||||||
|
file: TFile | string,
|
||||||
|
data: string | ArrayBuffer | Uint8Array<ArrayBuffer>,
|
||||||
|
options?: DataWriteOptions
|
||||||
|
) {
|
||||||
const path = file instanceof TFile ? file.path : file;
|
const path = file instanceof TFile ? file.path : file;
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
return await processWriteFile(file, () => this.app.vault.adapter.write(path, data, options));
|
return await processWriteFile(file, () => this.app.vault.adapter.write(path, data, options));
|
||||||
@@ -106,7 +110,7 @@ export class SerializedFileAccess {
|
|||||||
return await processReadFile(file, () => this.app.vault.readBinary(file));
|
return await processReadFile(file, () => this.app.vault.readBinary(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
async vaultModify(file: TFile, data: string | ArrayBuffer | Uint8Array, options?: DataWriteOptions) {
|
async vaultModify(file: TFile, data: string | ArrayBuffer | Uint8Array<ArrayBuffer>, options?: DataWriteOptions) {
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
return await processWriteFile(file, async () => {
|
return await processWriteFile(file, async () => {
|
||||||
const oldData = await this.app.vault.read(file);
|
const oldData = await this.app.vault.read(file);
|
||||||
@@ -131,7 +135,7 @@ export class SerializedFileAccess {
|
|||||||
}
|
}
|
||||||
async vaultCreate(
|
async vaultCreate(
|
||||||
path: string,
|
path: string,
|
||||||
data: string | ArrayBuffer | Uint8Array,
|
data: string | ArrayBuffer | Uint8Array<ArrayBuffer>,
|
||||||
options?: DataWriteOptions
|
options?: DataWriteOptions
|
||||||
): Promise<TFile> {
|
): Promise<TFile> {
|
||||||
if (typeof data === "string") {
|
if (typeof data === "string") {
|
||||||
|
|||||||
@@ -228,7 +228,9 @@ export class ModuleMigration extends AbstractModule implements ICoreModule {
|
|||||||
// Check local database for compromised chunks
|
// Check local database for compromised chunks
|
||||||
const localCompromised = await countCompromisedChunks(this.localDatabase.localDatabase);
|
const localCompromised = await countCompromisedChunks(this.localDatabase.localDatabase);
|
||||||
const remote = this.core.$$getReplicator();
|
const remote = this.core.$$getReplicator();
|
||||||
const remoteCompromised = await remote.countCompromisedChunks();
|
const remoteCompromised = this.core.managers.networkManager.isOnline
|
||||||
|
? await remote.countCompromisedChunks()
|
||||||
|
: 0;
|
||||||
if (localCompromised === false) {
|
if (localCompromised === false) {
|
||||||
Logger(`Failed to count compromised chunks in local database`, LOG_LEVEL_NOTICE);
|
Logger(`Failed to count compromised chunks in local database`, LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
||||||
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
if (uri.toLowerCase() != uri) return "Remote URI and database name could not contain capital letters.";
|
||||||
if (uri.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
if (uri.indexOf(" ") !== -1) return "Remote URI and database name could not contain spaces.";
|
||||||
|
if (!this.core.managers.networkManager.isOnline) {
|
||||||
|
return "Network is offline";
|
||||||
|
}
|
||||||
// let authHeader = await this._authHeader.getAuthorizationHeader(auth);
|
// let authHeader = await this._authHeader.getAuthorizationHeader(auth);
|
||||||
|
|
||||||
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
const conf: PouchDB.HttpAdapter.HttpAdapterConfiguration = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
import { type IObsidianModule, AbstractObsidianModule } from "../AbstractObsidianModule.ts";
|
||||||
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
// import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||||
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events";
|
import { EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts";
|
||||||
import {
|
import {
|
||||||
type BucketSyncSetting,
|
type BucketSyncSetting,
|
||||||
ChunkAlgorithmNames,
|
ChunkAlgorithmNames,
|
||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
SALT_OF_PASSPHRASE,
|
SALT_OF_PASSPHRASE,
|
||||||
} from "../../lib/src/common/types";
|
} from "../../lib/src/common/types";
|
||||||
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
import { LOG_LEVEL_NOTICE, LOG_LEVEL_URGENT } from "octagonal-wheels/common/logger";
|
||||||
import { $msg, setLang } from "../../lib/src/common/i18n";
|
import { $msg, setLang } from "../../lib/src/common/i18n.ts";
|
||||||
import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb";
|
import { isCloudantURI } from "../../lib/src/pouchdb/utils_couchdb.ts";
|
||||||
import { getLanguage } from "obsidian";
|
import { getLanguage } from "obsidian";
|
||||||
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts";
|
import { SUPPORTED_I18N_LANGS, type I18N_LANGS } from "../../lib/src/common/rosetta.ts";
|
||||||
import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts";
|
import { decryptString, encryptString } from "@/lib/src/encryption/stringEncryption.ts";
|
||||||
@@ -23,8 +23,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
const obsidianLanguage = getLanguage();
|
const obsidianLanguage = getLanguage();
|
||||||
if (
|
if (
|
||||||
SUPPORTED_I18N_LANGS.indexOf(obsidianLanguage) !== -1 && // Check if the language is supported
|
SUPPORTED_I18N_LANGS.indexOf(obsidianLanguage) !== -1 && // Check if the language is supported
|
||||||
obsidianLanguage != this.settings.displayLanguage && // Check if the language is different from the current setting
|
obsidianLanguage != this.settings.displayLanguage // Check if the language is different from the current setting
|
||||||
this.settings.displayLanguage != ""
|
|
||||||
) {
|
) {
|
||||||
// Check if the current setting is not empty (Means migrated or installed).
|
// Check if the current setting is not empty (Means migrated or installed).
|
||||||
this.settings.displayLanguage = obsidianLanguage as I18N_LANGS;
|
this.settings.displayLanguage = obsidianLanguage as I18N_LANGS;
|
||||||
|
|||||||
10
updates.md
10
updates.md
@@ -1,3 +1,13 @@
|
|||||||
|
## 0.25.11
|
||||||
|
|
||||||
|
28th August, 2025
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Automatic translation detection on the first launch now works correctly (#630).
|
||||||
|
- No errors are shown during synchronisations in offline (if not explicitly requested) (#699).
|
||||||
|
- Missing some checking during automatic-synchronisation now works correctly.
|
||||||
|
|
||||||
## 0.25.10
|
## 0.25.10
|
||||||
|
|
||||||
26th August, 2025
|
26th August, 2025
|
||||||
|
|||||||
Reference in New Issue
Block a user