mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-22 20:18:48 +00:00
Compare commits
18 Commits
0.25.26
...
0.25.30-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cc70b985a | ||
|
|
0e81ec2586 | ||
|
|
bab66a64d7 | ||
|
|
477913456f | ||
|
|
b0661cdbab | ||
|
|
18f9a842b7 | ||
|
|
5130bc5f2a | ||
|
|
ca8af80a27 | ||
|
|
df273d273b | ||
|
|
23aa0a82ca | ||
|
|
8f488b205b | ||
|
|
893eac5c92 | ||
|
|
cd6946bce2 | ||
|
|
174ca08954 | ||
|
|
4af4d9c4bd | ||
|
|
1b7a25598a | ||
|
|
e2a01c14cc | ||
|
|
b2fbbb38f5 |
128
.github/workflows/release.yml
vendored
128
.github/workflows/release.yml
vendored
@@ -10,19 +10,19 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
|
||||
submodules: recursive
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x' # You might need to adjust this value to your own version
|
||||
node-version: '24.x' # You might need to adjust this value to your own version
|
||||
# Get the version number and put it in a variable
|
||||
- name: Get Version
|
||||
id: version
|
||||
run: |
|
||||
echo "::set-output name=tag::$(git describe --abbrev=0 --tags)"
|
||||
echo "tag=$(git describe --abbrev=0 --tags)" >> $GITHUB_OUTPUT
|
||||
# Build the plugin
|
||||
- name: Build
|
||||
id: build
|
||||
@@ -36,59 +36,69 @@ jobs:
|
||||
cp main.js manifest.json styles.css README.md ${{ github.event.repository.name }}
|
||||
zip -r ${{ github.event.repository.name }}.zip ${{ github.event.repository.name }}
|
||||
# Create the release on github
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ github.ref }}
|
||||
# - name: Create Release
|
||||
# id: create_release
|
||||
# uses: actions/create-release@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# VERSION: ${{ steps.version.outputs.tag }}
|
||||
# with:
|
||||
# tag_name: ${{ steps.version.outputs.tag }}
|
||||
# release_name: ${{ steps.version.outputs.tag }}
|
||||
# draft: true
|
||||
# prerelease: false
|
||||
# # Upload the packaged release file
|
||||
# - name: Upload zip file
|
||||
# id: upload-zip
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./${{ github.event.repository.name }}.zip
|
||||
# asset_name: ${{ github.event.repository.name }}-${{ steps.version.outputs.tag }}.zip
|
||||
# asset_content_type: application/zip
|
||||
# # Upload the main.js
|
||||
# - name: Upload main.js
|
||||
# id: upload-main
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./main.js
|
||||
# asset_name: main.js
|
||||
# asset_content_type: text/javascript
|
||||
# # Upload the manifest.json
|
||||
# - name: Upload manifest.json
|
||||
# id: upload-manifest
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./manifest.json
|
||||
# asset_name: manifest.json
|
||||
# asset_content_type: application/json
|
||||
# # Upload the style.css
|
||||
# - name: Upload styles.css
|
||||
# id: upload-css
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
# asset_path: ./styles.css
|
||||
# asset_name: styles.css
|
||||
# asset_content_type: text/css
|
||||
- name: Create Release and Upload Assets
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
# Upload the packaged release file
|
||||
- name: Upload zip file
|
||||
id: upload-zip
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./${{ github.event.repository.name }}.zip
|
||||
asset_name: ${{ github.event.repository.name }}-${{ steps.version.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
# Upload the main.js
|
||||
- name: Upload main.js
|
||||
id: upload-main
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./main.js
|
||||
asset_name: main.js
|
||||
asset_content_type: text/javascript
|
||||
# Upload the manifest.json
|
||||
- name: Upload manifest.json
|
||||
id: upload-manifest
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./manifest.json
|
||||
asset_name: manifest.json
|
||||
asset_content_type: application/json
|
||||
# Upload the style.css
|
||||
- name: Upload styles.css
|
||||
id: upload-css
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./styles.css
|
||||
asset_name: styles.css
|
||||
asset_content_type: text/css
|
||||
# TODO: release notes???
|
||||
files: |
|
||||
${{ github.event.repository.name }}.zip
|
||||
main.js
|
||||
manifest.json
|
||||
styles.css
|
||||
name: ${{ steps.version.outputs.tag }}
|
||||
tag_name: ${{ steps.version.outputs.tag }}
|
||||
draft: true
|
||||
@@ -28,7 +28,7 @@ openssl ecparam -name secp521r1 -genkey -noout | openssl pkcs8 -topk8 -inform PE
|
||||
openssl ec -in private_key.pem -pubout -outform PEM -out public_key.pem
|
||||
```
|
||||
|
||||
> [!More tip]
|
||||
> [!TIP]
|
||||
> A key generator will be provided again in a future version of the user interface.
|
||||
|
||||
### 2. Configure CouchDB to accept JWT tokens
|
||||
|
||||
@@ -12,7 +12,7 @@ import inlineWorkerPlugin from "esbuild-plugin-inline-worker";
|
||||
import { terserOption } from "./terser.config.mjs";
|
||||
import path from "node:path";
|
||||
|
||||
const prod = process.argv[2] === "production";
|
||||
const prod = process.argv[2] === "production" || process.env?.BUILD_MODE === "production";
|
||||
const keepTest = true; //!prod;
|
||||
|
||||
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json") + "");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.25.26",
|
||||
"version": "0.25.30",
|
||||
"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.",
|
||||
"author": "vorotamoroz",
|
||||
|
||||
30
package-lock.json
generated
30
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.26",
|
||||
"version": "0.25.30",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.26",
|
||||
"version": "0.25.30",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.44",
|
||||
"qrcode-generator": "^2.0.4",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "^0.22.0",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
},
|
||||
@@ -8427,9 +8427,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -10019,9 +10019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode-generator": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-2.0.4.tgz",
|
||||
"integrity": "sha512-mZSiP6RnbHl4xL2Ap5HfkjLnmxfKcPWpWe/c+5XxCuetEenqmNFf1FH/ftXPCtFG5/TDobjsjz6sSNL0Sr8Z9g==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.2.tgz",
|
||||
"integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
@@ -17832,9 +17832,9 @@
|
||||
"integrity": "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -18967,9 +18967,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"qrcode-generator": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-2.0.4.tgz",
|
||||
"integrity": "sha512-mZSiP6RnbHl4xL2Ap5HfkjLnmxfKcPWpWe/c+5XxCuetEenqmNFf1FH/ftXPCtFG5/TDobjsjz6sSNL0Sr8Z9g=="
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.2.tgz",
|
||||
"integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw=="
|
||||
},
|
||||
"querystringify": {
|
||||
"version": "2.2.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.25.26",
|
||||
"version": "0.25.30",
|
||||
"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",
|
||||
"type": "module",
|
||||
@@ -95,7 +95,7 @@
|
||||
"idb": "^8.0.3",
|
||||
"minimatch": "^10.0.2",
|
||||
"octagonal-wheels": "^0.1.44",
|
||||
"qrcode-generator": "^2.0.4",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
"trystero": "^0.22.0",
|
||||
"xxhash-wasm-102": "npm:xxhash-wasm@^1.0.2"
|
||||
}
|
||||
|
||||
@@ -722,6 +722,13 @@ Offline Changed files: ${processFiles.length}`;
|
||||
} else {
|
||||
this._log(`Object merge is not applicable.`, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
// const pat = this.settings.syncInternalFileOverwritePatterns;
|
||||
const regExp = getFileRegExp(this.settings, "syncInternalFileOverwritePatterns");
|
||||
if (regExp.some((r) => r.test(stripAllPrefixes(path)))) {
|
||||
this._log(`Overwrite rule applied for conflicted hidden file: ${path}`, LOG_LEVEL_INFO);
|
||||
await this.resolveByNewerEntry(id, path, doc, revA, revB);
|
||||
return [];
|
||||
}
|
||||
return [{ path, revA, revB, id, doc }];
|
||||
}
|
||||
// When not JSON file, resolve conflicts by choosing a newer one.
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: b8fb5e5e63...86b0a95d56
@@ -1,6 +1,7 @@
|
||||
import { AbstractModule } from "../AbstractModule";
|
||||
import { PouchDB } from "../../lib/src/pouchdb/pouchdb-browser";
|
||||
import type { LiveSyncCore } from "../../main";
|
||||
import { ExtraSuffixIndexedDB } from "../../lib/src/common/types";
|
||||
|
||||
export class ModulePouchDB extends AbstractModule {
|
||||
_createPouchDBInstance<T extends object>(
|
||||
@@ -12,7 +13,7 @@ export class ModulePouchDB extends AbstractModule {
|
||||
optionPass.adapter = "indexeddb";
|
||||
//@ts-ignore :missing def
|
||||
optionPass.purged_infos_limit = 1;
|
||||
return new PouchDB(name + "-indexeddb", optionPass);
|
||||
return new PouchDB(name + ExtraSuffixIndexedDB, optionPass);
|
||||
}
|
||||
return new PouchDB(name, optionPass);
|
||||
}
|
||||
|
||||
@@ -34,12 +34,17 @@ import { serialized } from "octagonal-wheels/concurrency/lock";
|
||||
import { $msg } from "src/lib/src/common/i18n.ts";
|
||||
import { P2PLogCollector } from "../../lib/src/replication/trystero/P2PReplicatorCore.ts";
|
||||
import type { LiveSyncCore } from "../../main.ts";
|
||||
import { LiveSyncError } from "@/lib/src/common/LSError.ts";
|
||||
|
||||
// This module cannot be a core module because it depends on the Obsidian UI.
|
||||
|
||||
// DI the log again.
|
||||
setGlobalLogFunction((message: any, level?: number, key?: string) => {
|
||||
const entry = { message, level, key } as LogEntry;
|
||||
const messageX =
|
||||
message instanceof Error
|
||||
? new LiveSyncError("[Error Logged]: " + message.message, { cause: message })
|
||||
: message;
|
||||
const entry = { message: messageX, level, key } as LogEntry;
|
||||
logStore.enqueue(entry);
|
||||
});
|
||||
let recentLogs = [] as string[];
|
||||
@@ -398,19 +403,29 @@ export class ModuleLog extends AbstractObsidianModule {
|
||||
const vaultName = this.services.vault.getVaultName();
|
||||
const now = new Date();
|
||||
const timestamp = now.toLocaleString();
|
||||
let errorInfo = "";
|
||||
if (message instanceof Error) {
|
||||
if (message instanceof LiveSyncError) {
|
||||
errorInfo = `${message.cause?.name}:${message.cause?.message}\n[StackTrace]: ${message.stack}\n[CausedBy]: ${message.cause?.stack}`;
|
||||
} else {
|
||||
const thisStack = new Error().stack;
|
||||
errorInfo = `${message.name}:${message.message}\n[StackTrace]: ${message.stack}\n[LogCallStack]: ${thisStack}`;
|
||||
}
|
||||
}
|
||||
const messageContent =
|
||||
typeof message == "string"
|
||||
? message
|
||||
: message instanceof Error
|
||||
? `${message.name}:${message.message}`
|
||||
? `${errorInfo}`
|
||||
: JSON.stringify(message, null, 2);
|
||||
if (message instanceof Error) {
|
||||
// debugger;
|
||||
console.dir(message.stack);
|
||||
}
|
||||
const newMessage = timestamp + "->" + messageContent;
|
||||
|
||||
console.log(vaultName + ":" + newMessage);
|
||||
if (message instanceof Error) {
|
||||
console.error(vaultName + ":" + newMessage);
|
||||
} else if (level >= LOG_LEVEL_INFO) {
|
||||
console.log(vaultName + ":" + newMessage);
|
||||
} else {
|
||||
console.debug(vaultName + ":" + newMessage);
|
||||
}
|
||||
if (!this.settings?.showOnlyIconsOnEditor) {
|
||||
this.statusLog.value = messageContent;
|
||||
}
|
||||
|
||||
@@ -316,6 +316,11 @@ export class ModuleObsidianSettings extends AbstractObsidianModule {
|
||||
// this.core.ignoreFiles = this.settings.ignoreFiles.split(",").map(e => e.trim());
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
}
|
||||
|
||||
private _currentSettings(): ObsidianLiveSyncSettings {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
onBindFunction(core: LiveSyncCore, services: typeof core.services): void {
|
||||
super.onBindFunction(core, services);
|
||||
services.appLifecycle.handleLayoutReady(this._everyOnLayoutReady.bind(this));
|
||||
@@ -323,6 +328,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule {
|
||||
services.setting.handleDecryptSettings(this._decryptSettings.bind(this));
|
||||
services.setting.handleAdjustSettings(this._adjustSettings.bind(this));
|
||||
services.setting.handleLoadSettings(this._loadSettings.bind(this));
|
||||
services.setting.handleCurrentSettings(this._currentSettings.bind(this));
|
||||
services.setting.handleSaveDeviceAndVaultName(this._saveDeviceAndVaultName.bind(this));
|
||||
services.setting.handleSaveSettingData(this._saveSettingData.bind(this));
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@ import {
|
||||
E2EEAlgorithms,
|
||||
type HashAlgorithm,
|
||||
LOG_LEVEL_NOTICE,
|
||||
SuffixDatabaseName,
|
||||
} from "../../../lib/src/common/types.ts";
|
||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
||||
import type { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab.ts";
|
||||
import type { PageFunctions } from "./SettingPane.ts";
|
||||
import { visibleOnly } from "./SettingPane.ts";
|
||||
import { PouchDB } from "../../../lib/src/pouchdb/pouchdb-browser";
|
||||
import { ExtraSuffixIndexedDB } from "../../../lib/src/common/types.ts";
|
||||
import { migrateDatabases } from "./settingUtils.ts";
|
||||
|
||||
export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElement, { addPanel }: PageFunctions): void {
|
||||
void addPanel(paneEl, "Compatibility (Metadata)").then((paneEl) => {
|
||||
@@ -26,17 +30,88 @@ export function panePatches(this: ObsidianLiveSyncSettingTab, paneEl: HTMLElemen
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Compatibility (Database structure)").then((paneEl) => {
|
||||
new Setting(paneEl).autoWireToggle("useIndexedDBAdapter", { invert: true, holdValue: true });
|
||||
|
||||
// new Setting(paneEl)
|
||||
// .autoWireToggle("doNotUseFixedRevisionForChunks", { holdValue: true })
|
||||
// .setClass("wizardHidden");
|
||||
const migrateAllToIndexedDB = async () => {
|
||||
const dbToName = this.plugin.localDatabase.dbname + SuffixDatabaseName + ExtraSuffixIndexedDB;
|
||||
const options = {
|
||||
adapter: "indexeddb",
|
||||
//@ts-ignore :missing def
|
||||
purged_infos_limit: 1,
|
||||
auto_compaction: false,
|
||||
deterministic_revs: true,
|
||||
};
|
||||
const openTo = () => {
|
||||
return new PouchDB(dbToName, options);
|
||||
};
|
||||
if (await migrateDatabases("to IndexedDB", this.plugin.localDatabase.localDatabase, openTo)) {
|
||||
Logger(
|
||||
"Migration to IndexedDB completed. Obsidian will be restarted with new configuration immediately.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.plugin.settings.useIndexedDBAdapter = true;
|
||||
await this.services.setting.saveSettingData();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
};
|
||||
const migrateAllToIDB = async () => {
|
||||
const dbToName = this.plugin.localDatabase.dbname + SuffixDatabaseName;
|
||||
const options = {
|
||||
adapter: "idb",
|
||||
auto_compaction: false,
|
||||
deterministic_revs: true,
|
||||
};
|
||||
const openTo = () => {
|
||||
return new PouchDB(dbToName, options);
|
||||
};
|
||||
if (await migrateDatabases("to IDB", this.plugin.localDatabase.localDatabase, openTo)) {
|
||||
Logger(
|
||||
"Migration to IDB completed. Obsidian will be restarted with new configuration immediately.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
this.plugin.settings.useIndexedDBAdapter = false;
|
||||
await this.services.setting.saveSettingData();
|
||||
this.services.appLifecycle.performRestart();
|
||||
}
|
||||
};
|
||||
{
|
||||
const infoClass = this.editingSettings.useIndexedDBAdapter ? "op-warn" : "op-warn-info";
|
||||
paneEl.createDiv({
|
||||
text: "The IndexedDB adapter often offers superior performance in certain scenarios, but it has been found to cause memory leaks when used with LiveSync mode. When using LiveSync mode, please use IDB adapter instead.",
|
||||
cls: infoClass,
|
||||
});
|
||||
paneEl.createDiv({
|
||||
text: "Changing this setting requires migrating existing data (a bit time may be taken) and restarting Obsidian. Please make sure to back up your data before proceeding.",
|
||||
cls: "op-warn-info",
|
||||
});
|
||||
const setting = new Setting(paneEl)
|
||||
.setName("Database Adapter")
|
||||
.setDesc("Select the database adapter to use. ");
|
||||
const el = setting.controlEl.createDiv({});
|
||||
el.setText(`Current adapter: ${this.editingSettings.useIndexedDBAdapter ? "IndexedDB" : "IDB"}`);
|
||||
if (!this.editingSettings.useIndexedDBAdapter) {
|
||||
setting.addButton((button) => {
|
||||
button.setButtonText("Switch to IndexedDB").onClick(async () => {
|
||||
Logger("Migrating all data to IndexedDB...", LOG_LEVEL_NOTICE);
|
||||
await migrateAllToIndexedDB();
|
||||
Logger(
|
||||
"Migration to IndexedDB completed. Please switch the adapter and restart Obsidian.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setting.addButton((button) => {
|
||||
button.setButtonText("Switch to IDB").onClick(async () => {
|
||||
Logger("Migrating all data to IDB...", LOG_LEVEL_NOTICE);
|
||||
await migrateAllToIDB();
|
||||
Logger(
|
||||
"Migration to IDB completed. Please switch the adapter and restart Obsidian.",
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
new Setting(paneEl).autoWireToggle("handleFilenameCaseSensitive", { holdValue: true }).setClass("wizardHidden");
|
||||
|
||||
this.addOnSaved("useIndexedDBAdapter", async () => {
|
||||
await this.saveAllDirtySettings();
|
||||
await this.rebuildDB("localOnly");
|
||||
});
|
||||
});
|
||||
|
||||
void addPanel(paneEl, "Compatibility (Internal API Usage)").then((paneEl) => {
|
||||
|
||||
@@ -117,5 +117,26 @@ export function paneSelector(this: ObsidianLiveSyncSettingTab, paneEl: HTMLEleme
|
||||
await addDefaultPatterns(defaultSkipPatternXPlat);
|
||||
});
|
||||
});
|
||||
|
||||
const overwritePatterns = new Setting(paneEl)
|
||||
.setName("Overwrite patterns")
|
||||
.setClass("wizardHidden")
|
||||
.setDesc("Patterns to match files for overwriting instead of merging");
|
||||
const patTarget2 = splitCustomRegExpList(this.editingSettings.syncInternalFileOverwritePatterns, ",");
|
||||
mount(MultipleRegExpControl, {
|
||||
target: overwritePatterns.controlEl,
|
||||
props: {
|
||||
patterns: patTarget2,
|
||||
originals: [...patTarget2],
|
||||
apply: async (newPatterns: CustomRegExpSource[]) => {
|
||||
this.editingSettings.syncInternalFileOverwritePatterns = constructCustomRegExpList(
|
||||
newPatterns,
|
||||
","
|
||||
);
|
||||
await this.saveAllDirtySettings();
|
||||
this.display();
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { escapeStringToHTML } from "octagonal-wheels/string";
|
||||
import { E2EEAlgorithmNames, type ObsidianLiveSyncSettings } from "../../../lib/src/common/types";
|
||||
import {
|
||||
E2EEAlgorithmNames,
|
||||
MILESTONE_DOCID,
|
||||
NODEINFO_DOCID,
|
||||
type ObsidianLiveSyncSettings,
|
||||
} from "../../../lib/src/common/types";
|
||||
import {
|
||||
pickCouchDBSyncSettings,
|
||||
pickBucketSyncSettings,
|
||||
@@ -7,6 +12,7 @@ import {
|
||||
pickEncryptionSettings,
|
||||
} from "../../../lib/src/common/utils";
|
||||
import { getConfig, type AllSettingItemKey } from "./settingConstants";
|
||||
import { LOG_LEVEL_NOTICE, Logger } from "octagonal-wheels/common/logger";
|
||||
|
||||
/**
|
||||
* Generates a summary of P2P configuration settings
|
||||
@@ -76,3 +82,73 @@ export function getSummaryFromPartialSettings(setting: Partial<ObsidianLiveSyncS
|
||||
}
|
||||
return outputSummary;
|
||||
}
|
||||
|
||||
// Migration or de-migration helper functions
|
||||
|
||||
/**
|
||||
* Copy document from one database to another for migration purposes
|
||||
* @param docName document ID
|
||||
* @param dbFrom source database
|
||||
* @param dbTo destination database
|
||||
* @returns
|
||||
*/
|
||||
export async function copyMigrationDocs(docName: string, dbFrom: PouchDB.Database, dbTo: PouchDB.Database) {
|
||||
try {
|
||||
const doc = await dbFrom.get(docName);
|
||||
delete (doc as any)._rev;
|
||||
await dbTo.put(doc);
|
||||
} catch (e) {
|
||||
if ((e as any).status === 404) {
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
type PouchDBOpenFunction = () => Promise<PouchDB.Database> | PouchDB.Database;
|
||||
|
||||
/**
|
||||
* Migrate databases from one to another
|
||||
* @param operationName Name of the migration operation
|
||||
* @param from source database
|
||||
* @param openTo function to open destination database
|
||||
* @returns True if migration succeeded
|
||||
*/
|
||||
export async function migrateDatabases(operationName: string, from: PouchDB.Database, openTo: PouchDBOpenFunction) {
|
||||
const dbTo = await openTo();
|
||||
await dbTo.info(); // ensure created
|
||||
Logger(`Opening destination database for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
// destroy existing data
|
||||
await dbTo.destroy();
|
||||
Logger(`Destroyed existing destination database for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
|
||||
const dbTo2 = await openTo();
|
||||
const info2 = await dbTo2.info(); // ensure created
|
||||
console.log(info2);
|
||||
Logger(`Re-created destination database for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
|
||||
const info = await from.info();
|
||||
const totalDocs = info.doc_count || 0;
|
||||
const result = await from.replicate
|
||||
.to(dbTo2, {
|
||||
//@ts-ignore Missing in typedefs
|
||||
style: "all_docs",
|
||||
})
|
||||
.on("change", (info) => {
|
||||
Logger(
|
||||
`Replicating... Docs replicated: ${info.docs_written} / ${totalDocs}`,
|
||||
LOG_LEVEL_NOTICE,
|
||||
"migration"
|
||||
);
|
||||
});
|
||||
if (result.ok) {
|
||||
Logger(`Replication completed for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
} else {
|
||||
throw new Error(`Replication failed for migration: ${operationName}.`);
|
||||
}
|
||||
await copyMigrationDocs(MILESTONE_DOCID, from, dbTo2);
|
||||
await copyMigrationDocs(NODEINFO_DOCID, from, dbTo2);
|
||||
Logger(`Copied migration documents for migration: ${operationName}.`, LOG_LEVEL_NOTICE, "migration");
|
||||
await dbTo2.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,12 +62,19 @@ export class ModuleLiveSyncMain extends AbstractModule {
|
||||
|
||||
_wireUpEvents() {
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
this.localDatabase.settings = settings;
|
||||
setLang(settings.displayLanguage);
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
});
|
||||
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||
fireAndForget(() => this.core.services.setting.realiseSetting());
|
||||
fireAndForget(async () => {
|
||||
try {
|
||||
await this.core.services.setting.realiseSetting();
|
||||
const lang = this.core.services.setting.currentSettings()?.displayLanguage ?? undefined;
|
||||
if (lang !== undefined) {
|
||||
setLang(this.core.services.setting.currentSettings()?.displayLanguage);
|
||||
}
|
||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||
} catch (e) {
|
||||
this._log(`Error in Setting Save Event`, LOG_LEVEL_NOTICE);
|
||||
this._log(e, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
});
|
||||
});
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
114
updates.md
114
updates.md
@@ -4,6 +4,50 @@ Since 19th July, 2025 (beta1 in 0.25.0-beta1, 13th July, 2025)
|
||||
|
||||
The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md). Because 0.25 got a lot of updates, thankfully, compatibility is kept and we do not need breaking changes! In other words, when get enough stabled. The next version will be v1.0.0. Even though it my hope.
|
||||
|
||||
## 0.25.30
|
||||
|
||||
17th November, 2025
|
||||
|
||||
So sorry for the quick follow-up release, due to a humble mistake in a quick causing a matter.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now we can save settings correctly again (#756).
|
||||
|
||||
|
||||
## ~~0.25.28~~ 0.25.29
|
||||
(0.25.28 was skipped due to a packaging issue.)
|
||||
|
||||
17th November, 2025
|
||||
|
||||
### New feature
|
||||
- We can now configure hidden file synchronisation to always overwrite with the latest version (#579).
|
||||
|
||||
### Fixed
|
||||
- Timing dependency issues during initialisation have been mitigated (#714)
|
||||
|
||||
### Improved
|
||||
- Error logs now contain stack-traces for better inspection.
|
||||
|
||||
## 0.25.27
|
||||
|
||||
12th November, 2025
|
||||
|
||||
### Improved
|
||||
|
||||
- Now we can switch the database adapter between IndexedDB and IDB without rebuilding (#747).
|
||||
- Just a local migration will be required, but faster than a full rebuild.
|
||||
- No longer checking for the adapter by `Doctor`.
|
||||
|
||||
### Changes
|
||||
|
||||
- The default adapter is reverted to IDB to avoid memory leaks (#747).
|
||||
|
||||
### Fixed (?)
|
||||
|
||||
- Reverted QR code library to v1.4.4 (To make sure #752).
|
||||
|
||||
|
||||
## 0.25.26
|
||||
|
||||
07th November, 2025
|
||||
@@ -56,75 +100,5 @@ And, tips about JWT Authentication on CouchDB have been added to the documentati
|
||||
- The notification area is no longer imposing, distracting, and overwhelming.
|
||||
- With a pale background, but bordered and with icons.
|
||||
|
||||
## 0.25.24
|
||||
|
||||
04th November, 2025
|
||||
|
||||
(Beta release notes have been consolidated to this note).
|
||||
|
||||
### Guidance and UI improvements!
|
||||
|
||||
Since several issues were pointed out, our setup procedure had been quite `system-oriented`. This is not good for users. Therefore, I have changed the procedure to be more `goal-oriented`. I have made extensive use of Svelte, resulting in a very straightforward setup.
|
||||
While I would like to accelerate documentation and i18n adoption, I do not want to confuse everyone who's already working on it. Therefore, I have decided to release a Beta version at this stage. Significant changes are not expected from this point onward, so I will proceed to stabilise the codebase. (However, this is significant).
|
||||
|
||||
### TURN server support and important notice
|
||||
|
||||
TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P
|
||||
connections. In most cases, you do not need to set up a TURN server.
|
||||
|
||||
Using public TURN servers may have privacy implications, as your data will be relayed through third-party
|
||||
servers. Even if your data are encrypted, your existence may be known to them. Please ensure you trust the TURN
|
||||
server provider before using their services. Also your `network administrator` too. You should consider setting
|
||||
up your own TURN server for your FQDN, if possible.
|
||||
|
||||
### New features
|
||||
|
||||
- We can use the TURN server for P2P connections now.
|
||||
|
||||
### Fixed
|
||||
|
||||
- P2P Replication got more robust and stable.
|
||||
- Update [Trystero](https://github.com/dmotz/trystero) to the official v0.22.0!
|
||||
- Fixed a bug that caused P2P connections to drop or (unwanted reconnection to the relay server) unexpectedly in some environments.
|
||||
- Now, the connection status is more accurately reported.
|
||||
- While in the background, the connection to the signalling server is now disconnected to save resources.
|
||||
- When returning to the foreground, it will not reconnect automatically for safety. Please reconnect manually.
|
||||
- All connection configurations should be edited in each dedicated dialogue now.
|
||||
- No longer will larger files create chunks during preparing `Reset Synchronisation on This Device`.
|
||||
- Now hidden file synchronisation respects the filters correctly (#631, #735)
|
||||
- And `ignore-files` settings are also respected and surely read during the start-up.
|
||||
|
||||
### Behaviour changes
|
||||
|
||||
- The setup wizard is now more `goal-oriented`. Brand-new screens are introduced.
|
||||
- `Fetch everything` and `Rebuild everything` are now `Reset Synchronisation on This Device` and `Overwrite Server Data with This Device's Files`.
|
||||
- Remote configuration and E2EE settings are now separated into each modal dialogue.
|
||||
- Remote configuration is now more straightforward. And if we need the rebuild (No... `Overwrite Server Data with This Device's Files`), it is now clearly indicated.
|
||||
- Peer-to-Peer settings are also separated into their own modal dialogue (still in progress, and we need to open a P2P pane, still).
|
||||
- Setup-URI, and Report for the Issue are now not copied to the clipboard automatically. Instead, there are copy-dialogue and buttons to copy them explicitly.
|
||||
- This is to avoid confusion for users who do not want to use these features.
|
||||
- No longer optional features are introduced during the setup, or `Reset Synchronisation on This Device`, `Overwrite Server Data with This Device's Files`.
|
||||
- This is to avoid confusion for users who do not want to use these features. Instead, we will be informed that optional features are available after the setup is completed.
|
||||
- We cannot perform `Fetch everything` and `Rebuild everything` (Removed, so the old name) without restarting Obsidian now.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Setup QR Code generation is separated into a src/lib/src/API/processSetting.ts file. Please use it as a subrepository if you want to generate QR codes in your own application.
|
||||
- Setup-URI is also separated into a src/lib/src/API/processSetting.ts
|
||||
- Some direct access to web APIs is now wrapped into the services layer.
|
||||
|
||||
### Dependency updates
|
||||
|
||||
- Many dependencies are updated. Please see `package.json`.
|
||||
- This is the hardest part of this update. I read most of the changes in the dependencies. If you find any extra information, please let me know.
|
||||
- As upgrading TypeScript, Fixed many UInt8Array<ArrayBuffer> and Uint8Array type mismatches.
|
||||
-
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Sending configuration via Peer-to-Peer connection is not compatible with older versions.
|
||||
- Please upgrade all devices to v0.25.24.beta1 or later to use this feature again.
|
||||
- This is due to security improvements in the encryption scheme.
|
||||
|
||||
Older notes are in
|
||||
[updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||
|
||||
@@ -9,6 +9,75 @@ I have now rewritten the E2EE code to be more robust and easier to understand. I
|
||||
As a result, this is the first time in a while that forward compatibility has been broken. We have also taken the opportunity to change all metadata to use encryption rather than obfuscation. Furthermore, the `Dynamic Iteration Count` setting is now redundant and has been moved to the `Patches` pane in the settings. Thanks to Rabin-Karp, the eden setting is also no longer necessary and has been relocated accordingly. Therefore, v0.25.0 represents a legitimate and correct evolution.
|
||||
|
||||
---
|
||||
## 0.25.24
|
||||
|
||||
04th November, 2025
|
||||
|
||||
(Beta release notes have been consolidated to this note).
|
||||
|
||||
### Guidance and UI improvements!
|
||||
|
||||
Since several issues were pointed out, our setup procedure had been quite `system-oriented`. This is not good for users. Therefore, I have changed the procedure to be more `goal-oriented`. I have made extensive use of Svelte, resulting in a very straightforward setup.
|
||||
While I would like to accelerate documentation and i18n adoption, I do not want to confuse everyone who's already working on it. Therefore, I have decided to release a Beta version at this stage. Significant changes are not expected from this point onward, so I will proceed to stabilise the codebase. (However, this is significant).
|
||||
|
||||
### TURN server support and important notice
|
||||
|
||||
TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P
|
||||
connections. In most cases, you do not need to set up a TURN server.
|
||||
|
||||
Using public TURN servers may have privacy implications, as your data will be relayed through third-party
|
||||
servers. Even if your data are encrypted, your existence may be known to them. Please ensure you trust the TURN
|
||||
server provider before using their services. Also your `network administrator` too. You should consider setting
|
||||
up your own TURN server for your FQDN, if possible.
|
||||
|
||||
### New features
|
||||
|
||||
- We can use the TURN server for P2P connections now.
|
||||
|
||||
### Fixed
|
||||
|
||||
- P2P Replication got more robust and stable.
|
||||
- Update [Trystero](https://github.com/dmotz/trystero) to the official v0.22.0!
|
||||
- Fixed a bug that caused P2P connections to drop or (unwanted reconnection to the relay server) unexpectedly in some environments.
|
||||
- Now, the connection status is more accurately reported.
|
||||
- While in the background, the connection to the signalling server is now disconnected to save resources.
|
||||
- When returning to the foreground, it will not reconnect automatically for safety. Please reconnect manually.
|
||||
- All connection configurations should be edited in each dedicated dialogue now.
|
||||
- No longer will larger files create chunks during preparing `Reset Synchronisation on This Device`.
|
||||
- Now hidden file synchronisation respects the filters correctly (#631, #735)
|
||||
- And `ignore-files` settings are also respected and surely read during the start-up.
|
||||
|
||||
### Behaviour changes
|
||||
|
||||
- The setup wizard is now more `goal-oriented`. Brand-new screens are introduced.
|
||||
- `Fetch everything` and `Rebuild everything` are now `Reset Synchronisation on This Device` and `Overwrite Server Data with This Device's Files`.
|
||||
- Remote configuration and E2EE settings are now separated into each modal dialogue.
|
||||
- Remote configuration is now more straightforward. And if we need the rebuild (No... `Overwrite Server Data with This Device's Files`), it is now clearly indicated.
|
||||
- Peer-to-Peer settings are also separated into their own modal dialogue (still in progress, and we need to open a P2P pane, still).
|
||||
- Setup-URI, and Report for the Issue are now not copied to the clipboard automatically. Instead, there are copy-dialogue and buttons to copy them explicitly.
|
||||
- This is to avoid confusion for users who do not want to use these features.
|
||||
- No longer optional features are introduced during the setup, or `Reset Synchronisation on This Device`, `Overwrite Server Data with This Device's Files`.
|
||||
- This is to avoid confusion for users who do not want to use these features. Instead, we will be informed that optional features are available after the setup is completed.
|
||||
- We cannot perform `Fetch everything` and `Rebuild everything` (Removed, so the old name) without restarting Obsidian now.
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Setup QR Code generation is separated into a src/lib/src/API/processSetting.ts file. Please use it as a subrepository if you want to generate QR codes in your own application.
|
||||
- Setup-URI is also separated into a src/lib/src/API/processSetting.ts
|
||||
- Some direct access to web APIs is now wrapped into the services layer.
|
||||
|
||||
### Dependency updates
|
||||
|
||||
- Many dependencies are updated. Please see `package.json`.
|
||||
- This is the hardest part of this update. I read most of the changes in the dependencies. If you find any extra information, please let me know.
|
||||
- As upgrading TypeScript, Fixed many UInt8Array<ArrayBuffer> and Uint8Array type mismatches.
|
||||
-
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Sending configuration via Peer-to-Peer connection is not compatible with older versions.
|
||||
- Please upgrade all devices to v0.25.24.beta1 or later to use this feature again.
|
||||
- This is due to security improvements in the encryption scheme.
|
||||
|
||||
## 0.25.23
|
||||
|
||||
|
||||
Reference in New Issue
Block a user